void furn_t::load( JsonObject &jo ) { mandatory( jo, was_loaded, "name", name, translated_string_reader ); mandatory( jo, was_loaded, "move_cost_mod", movecost ); mandatory( jo, was_loaded, "required_str", move_str_req ); optional( jo, was_loaded, "max_volume", max_volume, legacy_volume_reader, DEFAULT_MAX_VOLUME_IN_SQUARE ); optional( jo, was_loaded, "crafting_pseudo_item", crafting_pseudo_item, "" ); load_symbol( jo ); transparent = false; for( auto & flag : jo.get_string_array( "flags" ) ) { set_flag( flag ); } if( jo.has_member( "examine_action" ) ) { examine = iexamine_function_from_string( jo.get_string( "examine_action" ) ); } else { examine = iexamine_function_from_string( "none" ); } optional( jo, was_loaded, "open", open, string_id_reader<furn_t> {}, NULL_ID ); optional( jo, was_loaded, "close", close, string_id_reader<furn_t> {}, NULL_ID ); bash.load( jo, "bash", true ); deconstruct.load( jo, "deconstruct", true ); }
void martialart::load( JsonObject &jo, const std::string & ) { JsonArray jsarr; mandatory( jo, was_loaded, "name", name, translated_string_reader ); mandatory( jo, was_loaded, "description", description, translated_string_reader ); optional( jo, was_loaded, "static_buffs", static_buffs, ma_buff_reader{} ); optional( jo, was_loaded, "onmove_buffs", onmove_buffs, ma_buff_reader{} ); optional( jo, was_loaded, "onhit_buffs", onhit_buffs, ma_buff_reader{} ); optional( jo, was_loaded, "onattack_buffs", onattack_buffs, ma_buff_reader{} ); optional( jo, was_loaded, "ondodge_buffs", ondodge_buffs, ma_buff_reader{} ); optional( jo, was_loaded, "onblock_buffs", onblock_buffs, ma_buff_reader{} ); optional( jo, was_loaded, "ongethit_buffs", ongethit_buffs, ma_buff_reader{} ); optional( jo, was_loaded, "techniques", techniques, auto_flags_reader<matec_id>{} ); optional( jo, was_loaded, "weapons", weapons, auto_flags_reader<itype_id>{} ); optional( jo, was_loaded, "strictly_unarmed", strictly_unarmed, false ); optional( jo, was_loaded, "force_unarmed", force_unarmed, false ); optional( jo, was_loaded, "leg_block", leg_block, 99 ); optional( jo, was_loaded, "arm_block", arm_block, 99 ); optional( jo, was_loaded, "arm_block_with_bio_armor_arms", arm_block_with_bio_armor_arms, false ); optional( jo, was_loaded, "leg_block_with_bio_armor_legs", leg_block_with_bio_armor_legs, false ); }
void ter_t::load( JsonObject &jo, const std::string &src ) { map_data_common_t::load( jo, src ); mandatory( jo, was_loaded, "name", name_ ); mandatory( jo, was_loaded, "move_cost", movecost ); optional( jo, was_loaded, "max_volume", max_volume, legacy_volume_reader, DEFAULT_MAX_VOLUME_IN_SQUARE ); optional( jo, was_loaded, "trap", trap_id_str ); load_symbol( jo ); trap = tr_null; transparent = false; connect_group = TERCONN_NONE; for( auto &flag : jo.get_string_array( "flags" ) ) { set_flag( flag ); } // connect_group is initialized to none, then terrain flags are set, then finally // connections from JSON are set. This is so that wall flags can set wall connections // but can be overridden by explicit connections in JSON. if( jo.has_member( "connects_to" ) ) { set_connects( jo.get_string( "connects_to" ) ); } optional( jo, was_loaded, "open", open, ter_str_id::NULL_ID() ); optional( jo, was_loaded, "close", close, ter_str_id::NULL_ID() ); optional( jo, was_loaded, "transforms_into", transforms_into, ter_str_id::NULL_ID() ); optional( jo, was_loaded, "roof", roof, ter_str_id::NULL_ID() ); bash.load( jo, "bash", false ); deconstruct.load( jo, "deconstruct", false ); }
void morale_type_data::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "id", id ); mandatory( jo, was_loaded, "text", text, translated_string_reader ); optional( jo, was_loaded, "permanent", permanent, false ); needs_item = text.find( "%s" ) != std::string::npos; }
void overmap_connection::subtype::load( JsonObject &jo ) { static const typed_flag_reader<decltype( connection_subtype_flag_map )> flag_reader{ connection_subtype_flag_map, "invalid connection subtype flag" }; mandatory( jo, false, "terrain", terrain ); mandatory( jo, false, "locations", locations ); optional( jo, false, "basic_cost", basic_cost, 0 ); optional( jo, false, "flags", flags, flag_reader ); }
void ter_t::load( JsonObject &jo ) { mandatory( jo, was_loaded, "name", name, translated_string_reader ); mandatory( jo, was_loaded, "move_cost", movecost ); optional( jo, was_loaded, "max_volume", max_volume, MAX_VOLUME_IN_SQUARE ); optional( jo, was_loaded, "trap", trap_id_str ); load_symbol( jo ); trap = tr_null; transparent = false; connect_group = TERCONN_NONE; for( auto &flag : jo.get_string_array( "flags" ) ) { set_flag( flag ); } // connect_group is initialised to none, then terrain flags are set, then finally // connections from JSON are set. This is so that wall flags can set wall connections // but can be overridden by explicit connections in JSON. if( jo.has_member( "connects_to" ) ) { set_connects( jo.get_string( "connects_to" ) ); } if( jo.has_member( "examine_action" ) ) { examine = iexamine_function_from_string( jo.get_string( "examine_action" ) ); } else { examine = iexamine_function_from_string( "none" ); } optional( jo, was_loaded, "harvestable", harvestable ); optional( jo, was_loaded, "open", open, NULL_ID ); optional( jo, was_loaded, "close", close, NULL_ID ); optional( jo, was_loaded, "transforms_into", transforms_into, NULL_ID ); optional( jo, was_loaded, "roof", roof, NULL_ID ); if( jo.has_member("harvest_season") ) { const std::string season = jo.get_string( "harvest_season" ); if( season == "SPRING" ) { harvest_season = season_type::SPRING; } else if( season == "SUMMER" ) { harvest_season = season_type::SUMMER; } else if( season == "AUTUMN" ) { harvest_season = season_type::AUTUMN; } else if( season == "WINTER" ) { harvest_season = season_type::WINTER; } else { harvest_season = season_type::AUTUMN; debugmsg( "Invalid harvest season \"%s\" in \"%s\".", season.c_str(), id.c_str() ); } } bash.load( jo, "bash", false ); deconstruct.load( jo, "deconstruct", false ); }
void material_type::load( JsonObject &jsobj ) { mandatory( jsobj, was_loaded, "name", _name, translated_string_reader ); mandatory( jsobj, was_loaded, "bash_resist", _bash_resist ); mandatory( jsobj, was_loaded, "cut_resist", _cut_resist ); mandatory( jsobj, was_loaded, "acid_resist", _acid_resist ); mandatory( jsobj, was_loaded, "elec_resist", _elec_resist ); mandatory( jsobj, was_loaded, "fire_resist", _fire_resist ); mandatory( jsobj, was_loaded, "chip_resist", _chip_resist ); mandatory( jsobj, was_loaded, "density", _density ); optional( jsobj, was_loaded, "salvaged_into", _salvaged_into, "null" ); optional( jsobj, was_loaded, "repaired_with", _repaired_with, "null" ); optional( jsobj, was_loaded, "edible", _edible, false ); optional( jsobj, was_loaded, "soft", _soft, false ); auto arr = jsobj.get_array( "vitamins" ); while( arr.has_more() ) { auto pair = arr.next_array(); _vitamins.emplace( vitamin_id( pair.get_string( 0 ) ), pair.get_float( 1 ) ); } mandatory( jsobj, was_loaded, "bash_dmg_verb", _bash_dmg_verb, translated_string_reader ); mandatory( jsobj, was_loaded, "cut_dmg_verb", _cut_dmg_verb, translated_string_reader ); JsonArray jsarr = jsobj.get_array( "dmg_adj" ); while( jsarr.has_more() ) { _dmg_adj.push_back( _( jsarr.next_string().c_str() ) ); } JsonArray burn_data_array = jsobj.get_array( "burn_data" ); for( size_t intensity = 0; intensity < MAX_FIELD_DENSITY; intensity++ ) { if( burn_data_array.has_more() ) { JsonObject brn = burn_data_array.next_object(); _burn_data[ intensity ] = load_mat_burn_data( brn ); } else { // If not specified, supply default bool flammable = _fire_resist <= ( int )intensity; mat_burn_data mbd; if( flammable ) { mbd.burn = 1; } _burn_data[ intensity ] = mbd; } } }
void add_if_exists( JsonObject &jo, Container &cont, bool was_loaded, const std::string &json_key, const typename Container::key_type &id ) { if( jo.has_member( json_key ) ) { mandatory( jo, was_loaded, json_key, cont[id] ); } }
void profession::load( JsonObject &jo, const std::string & ) { //If the "name" is an object then we have to deal with gender-specific titles, if( jo.has_object( "name" ) ) { JsonObject name_obj = jo.get_object( "name" ); _name_male = pgettext( "profession_male", name_obj.get_string( "male" ).c_str() ); _name_female = pgettext( "profession_female", name_obj.get_string( "female" ).c_str() ); } else if( jo.has_string( "name" ) ) { // Same profession names for male and female in English. // Still need to different names in other languages. const std::string name = jo.get_string( "name" ); _name_female = pgettext( "profession_female", name.c_str() ); _name_male = pgettext( "profession_male", name.c_str() ); } else if( !was_loaded ) { jo.throw_error( "missing mandatory member \"name\"" ); } if( !was_loaded || jo.has_member( "description" ) ) { const std::string desc = jo.get_string( "description" ); // These also may differ depending on the language settings! _description_male = pgettext( "prof_desc_male", desc.c_str() ); _description_female = pgettext( "prof_desc_female", desc.c_str() ); } mandatory( jo, was_loaded, "points", _point_cost ); if( !was_loaded || jo.has_member( "items" ) ) { JsonObject items_obj = jo.get_object( "items" ); if( items_obj.has_array( "both" ) ) { optional( items_obj, was_loaded, "both", legacy_starting_items, item_reader {} ); } if( items_obj.has_object( "both" ) ) { _starting_items = item_group::load_item_group( *items_obj.get_raw( "both" ), "collection" ); } if( items_obj.has_array( "male" ) ) { optional( items_obj, was_loaded, "male", legacy_starting_items_male, item_reader {} ); } if( items_obj.has_object( "male" ) ) { _starting_items_male = item_group::load_item_group( *items_obj.get_raw( "male" ), "collection" ); } if( items_obj.has_array( "female" ) ) { optional( items_obj, was_loaded, "female", legacy_starting_items_female, item_reader {} ); } if( items_obj.has_object( "female" ) ) { _starting_items_female = item_group::load_item_group( *items_obj.get_raw( "female" ), "collection" ); } } optional( jo, was_loaded, "no_bonus", no_bonus ); optional( jo, was_loaded, "skills", _starting_skills, skilllevel_reader {} ); optional( jo, was_loaded, "addictions", _starting_addictions, addiction_reader {} ); // TODO: use string_id<bionic_type> or so optional( jo, was_loaded, "CBMs", _starting_CBMs, auto_flags_reader<bionic_id> {} ); // TODO: use string_id<mutation_branch> or so optional( jo, was_loaded, "traits", _starting_traits, auto_flags_reader<trait_id> {} ); optional( jo, was_loaded, "flags", flags, auto_flags_reader<> {} ); }
void ma_buff::load( JsonObject &jo, const std::string &src ) { mandatory( jo, was_loaded, "name", name, translated_string_reader ); mandatory( jo, was_loaded, "description", description, translated_string_reader ); optional( jo, was_loaded, "buff_duration", buff_duration, 2 ); optional( jo, was_loaded, "max_stacks", max_stacks, 1 ); optional( jo, was_loaded, "bonus_dodges", dodges_bonus, 0 ); optional( jo, was_loaded, "bonus_blocks", blocks_bonus, 0 ); optional( jo, was_loaded, "quiet", quiet, false ); optional( jo, was_loaded, "throw_immune", throw_immune, false ); reqs.load( jo, src ); bonuses.load( jo ); }
void overmap_location::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "terrains", terrains ); if( terrains.empty() ) { jo.throw_error( "At least one terrain must be specified." ); } }
void gate_data::load( JsonObject &jo ) { mandatory( jo, was_loaded, "door", door ); mandatory( jo, was_loaded, "floor", floor ); mandatory( jo, was_loaded, "walls", walls, string_id_reader<ter_t> {} ); if( !was_loaded || jo.has_member( "messages" ) ) { JsonObject messages_obj = jo.get_object( "messages" ); optional( messages_obj, was_loaded, "pull", pull_message, translated_string_reader ); optional( messages_obj, was_loaded, "open", open_message, translated_string_reader ); optional( messages_obj, was_loaded, "close", close_message, translated_string_reader ); optional( messages_obj, was_loaded, "fail", fail_message, translated_string_reader ); } optional( jo, was_loaded, "moves", moves, 0 ); optional( jo, was_loaded, "bashing_damage", bash_dmg, 0 ); }
void npc_class::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "name", name, translated_string_reader ); mandatory( jo, was_loaded, "job_description", job_description, translated_string_reader ); optional( jo, was_loaded, "common", common, true ); bonus_str = load_distribution( jo, "bonus_str" ); bonus_dex = load_distribution( jo, "bonus_dex" ); bonus_int = load_distribution( jo, "bonus_int" ); bonus_per = load_distribution( jo, "bonus_per" ); optional( jo, was_loaded, "shopkeeper_item_group", shopkeeper_item_group, "EMPTY_GROUP" ); optional( jo, was_loaded, "worn_override", worn_override ); optional( jo, was_loaded, "carry_override", carry_override ); optional( jo, was_loaded, "weapon_override", weapon_override ); if( jo.has_array( "traits" ) ) { JsonArray jarr = jo.get_array( "traits" ); while( jarr.has_more() ) { JsonArray jarr_in = jarr.next_array(); traits[ trait_id( jarr_in.get_string( 0 ) ) ] = jarr_in.get_int( 1 ); } } if( jo.has_array( "skills" ) ) { JsonArray jarr = jo.get_array( "skills" ); while( jarr.has_more() ) { JsonObject skill_obj = jarr.next_object(); auto skill_ids = skill_obj.get_tags( "skill" ); if( skill_obj.has_object( "level" ) ) { distribution dis = load_distribution( skill_obj, "level" ); for( const auto &sid : skill_ids ) { skills[ skill_id( sid ) ] = dis; } } else { distribution dis = load_distribution( skill_obj, "bonus" ); for( const auto &sid : skill_ids ) { bonus_skills[ skill_id( sid ) ] = dis; } } } } }
void quality::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "name", name, translated_string_reader ); JsonArray arr = jo.get_array( "usages" ); while( arr.has_more() ) { auto lvl = arr.next_array(); auto funcs = lvl.get_array( 1 ); while( funcs.has_more() ) { usages.emplace_back( lvl.get_int( 0 ), funcs.next_string() ); } } }
void furn_t::load( JsonObject &jo, const std::string &src ) { map_data_common_t::load( jo, src ); mandatory( jo, was_loaded, "name", name_ ); mandatory( jo, was_loaded, "move_cost_mod", movecost ); mandatory( jo, was_loaded, "required_str", move_str_req ); optional( jo, was_loaded, "max_volume", max_volume, legacy_volume_reader, DEFAULT_MAX_VOLUME_IN_SQUARE ); optional( jo, was_loaded, "crafting_pseudo_item", crafting_pseudo_item, "" ); optional( jo, was_loaded, "deployed_item", deployed_item ); load_symbol( jo ); transparent = false; for( auto & flag : jo.get_string_array( "flags" ) ) { set_flag( flag ); } optional( jo, was_loaded, "open", open, string_id_reader<furn_t> {}, furn_str_id::NULL_ID() ); optional( jo, was_loaded, "close", close, string_id_reader<furn_t> {}, furn_str_id::NULL_ID() ); bash.load( jo, "bash", true ); deconstruct.load( jo, "deconstruct", true ); }
void load( JsonObject &jo, const std::string &src ) { const std::string tuple_key = "tuple"; const bool strict = src == "dda"; std::vector<long> tuple; mandatory( jo, false, tuple_key, tuple ); if( tuple.size() != 2 && tuple.size() != 4 ) { jo.throw_error( "Invalid size. Must be either 2 or 4.", tuple_key ); } rotatable_symbol temp_entry; for( auto iter = tuple.cbegin(); iter != tuple.cend(); ++iter ) { const auto entry_iter = std::lower_bound( symbols.begin(), symbols.end(), *iter ); const bool found = entry_iter != symbols.end() && entry_iter->sym == *iter; if( strict && found ) { jo.throw_error( string_format( "Symbol %ld was already defined.", *iter ), tuple_key ); } rotatable_symbol &entry = found ? *entry_iter : temp_entry; entry.sym = *iter; auto rotation_iter = iter; for( auto &element : entry.rotated_sym ) { if( ++rotation_iter == tuple.cend() ) { rotation_iter = tuple.cbegin(); } element = *rotation_iter; } if( !found ) { symbols.insert( entry_iter, entry ); } } }
void trap::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "id", id ); mandatory( jo, was_loaded, "name", name_ ); if( !assign( jo, "color", color ) ) { jo.throw_error( "missing mandatory member \"color\"" ); } mandatory( jo, was_loaded, "symbol", sym, one_char_symbol_reader ); mandatory( jo, was_loaded, "visibility", visibility ); mandatory( jo, was_loaded, "avoidance", avoidance ); mandatory( jo, was_loaded, "difficulty", difficulty ); // @todo Is there a generic_factory version of this? act = trap_function_from_string( jo.get_string( "action" ) ); optional( jo, was_loaded, "benign", benign, false ); optional( jo, was_loaded, "funnel_radius", funnel_radius_mm, 0 ); assign( jo, "trigger_weight", trigger_weight ); optional( jo, was_loaded, "drops", components ); }
void mtype::load( JsonObject &jo ) { MonsterGenerator &gen = MonsterGenerator::generator(); // Name and name plural are not translated here, but when needed in // combination with the actual count in `mtype::nname`. mandatory( jo, was_loaded, "name", name ); // default behaviour: Assume the regular plural form (appending an “s”) optional( jo, was_loaded, "name_plural", name_plural, name + "s" ); mandatory( jo, was_loaded, "description", description, translated_string_reader ); optional( jo, was_loaded, "material", mat, auto_flags_reader<material_id> {} ); optional( jo, was_loaded, "species", species, auto_flags_reader<species_id> {} ); optional( jo, was_loaded, "categories", categories, auto_flags_reader<> {} ); // See monfaction.cpp if( !was_loaded || jo.has_member( "default_faction" ) ) { const auto faction = mfaction_str_id( jo.get_string( "default_faction" ) ); default_faction = monfactions::get_or_add_faction( faction ); } if( !was_loaded || jo.has_member( "symbol" ) ) { sym = jo.get_string( "symbol" ); if( utf8_wrapper( sym ).display_width() != 1 ) { jo.throw_error( "monster symbol should be exactly one console cell width", "symbol" ); } } mandatory( jo, was_loaded, "color", color, color_reader{} ); const typed_flag_reader<decltype( Creature::size_map )> size_reader{ Creature::size_map, "invalid creature size" }; optional( jo, was_loaded, "size", size, size_reader, MS_MEDIUM ); const typed_flag_reader<decltype( gen.phase_map )> phase_reader{ gen.phase_map, "invalid phase id" }; optional( jo, was_loaded, "phase", phase, phase_reader, SOLID ); optional( jo, was_loaded, "diff", difficulty, 0 ); optional( jo, was_loaded, "aggression", agro, 0 ); optional( jo, was_loaded, "morale", morale, 0 ); optional( jo, was_loaded, "speed", speed, 0 ); optional( jo, was_loaded, "attack_cost", attack_cost, 100 ); optional( jo, was_loaded, "melee_skill", melee_skill, 0 ); optional( jo, was_loaded, "melee_dice", melee_dice, 0 ); optional( jo, was_loaded, "melee_dice_sides", melee_sides, 0 ); optional( jo, was_loaded, "dodge", sk_dodge, 0 ); optional( jo, was_loaded, "armor_bash", armor_bash, 0 ); optional( jo, was_loaded, "armor_cut", armor_cut, 0 ); optional( jo, was_loaded, "armor_acid", armor_acid, armor_cut / 2 ); optional( jo, was_loaded, "armor_fire", armor_fire, 0 ); optional( jo, was_loaded, "hp", hp, 0 ); optional( jo, was_loaded, "starting_ammo", starting_ammo ); optional( jo, was_loaded, "luminance", luminance, 0 ); optional( jo, was_loaded, "revert_to_itype", revert_to_itype, "" ); optional( jo, was_loaded, "vision_day", vision_day, 40 ); optional( jo, was_loaded, "vision_night", vision_night, 1 ); optional( jo, was_loaded, "armor_stab", armor_stab, 0.8f * armor_cut ); optional( jo, was_loaded, "attack_effs", atk_effs, mon_attack_effect_reader{} ); // TODO: make this work with `was_loaded` if( jo.has_array( "melee_damage" ) ) { JsonArray arr = jo.get_array( "melee_damage" ); melee_damage = load_damage_instance( arr ); } else if( jo.has_object( "melee_damage" ) ) { melee_damage = load_damage_instance( jo ); } if( jo.has_int( "melee_cut" ) ) { int bonus_cut = jo.get_int( "melee_cut" ); melee_damage.add_damage( DT_CUT, bonus_cut ); } if( jo.has_member( "death_drops" ) ) { JsonIn &stream = *jo.get_raw( "death_drops" ); death_drops = item_group::load_item_group( stream, "distribution" ); } const typed_flag_reader<decltype( gen.death_map )> death_reader{ gen.death_map, "invalid monster death function" }; optional( jo, was_loaded, "death_function", dies, death_reader ); if( dies.empty() ) { // TODO: really needed? Is an empty `dies` container not allowed? dies.push_back( mdeath::normal ); } if( jo.has_member( "special_when_hit" ) ) { JsonArray jsarr = jo.get_array( "special_when_hit" ); const auto iter = gen.defense_map.find( jsarr.get_string( 0 ) ); if( iter == gen.defense_map.end() ) { jsarr.throw_error( "Invalid monster defense function" ); } sp_defense = iter->second; def_chance = jsarr.get_int( 1 ); } else if( !was_loaded ) { sp_defense = &mdefense::none; def_chance = 0; } if( !was_loaded || jo.has_member( "special_attacks" ) ) { special_attacks.clear(); special_attacks_names.clear(); add_special_attacks( jo, "special_attacks" ); } else { // Note: special_attacks left as is, new attacks are added to it! // Note: member name prefixes are compatible with those used by generic_typed_reader remove_special_attacks( jo, "remove:special_attacks" ); add_special_attacks( jo, "add:special_attacks" ); } // Disable upgrading when JSON contains `"upgrades": false`, but fallback to the // normal behavior (including error checking) if "upgrades" is not boolean or not `false`. if( jo.has_bool( "upgrades" ) && !jo.get_bool( "upgrades" ) ) { upgrade_group = mongroup_id::NULL_ID; upgrade_into = mtype_id::NULL_ID; upgrades = false; } else if( jo.has_member( "upgrades" ) ) { JsonObject up = jo.get_object( "upgrades" ); optional( up, was_loaded, "half_life", half_life, -1 ); optional( up, was_loaded, "into_group", upgrade_group, auto_flags_reader<mongroup_id> {}, mongroup_id::NULL_ID ); optional( up, was_loaded, "into", upgrade_into, auto_flags_reader<mtype_id> {}, mtype_id::NULL_ID ); upgrades = true; } optional( jo, was_loaded, "burn_into", burn_into, auto_flags_reader<mtype_id> {}, mtype_id::NULL_ID ); const typed_flag_reader<decltype( gen.flag_map )> flag_reader{ gen.flag_map, "invalid monster flag" }; optional( jo, was_loaded, "flags", flags, flag_reader ); const typed_flag_reader<decltype( gen.trigger_map )> trigger_reader{ gen.trigger_map, "invalid monster trigger" }; optional( jo, was_loaded, "anger_triggers", anger, trigger_reader ); optional( jo, was_loaded, "placate_triggers", placate, trigger_reader ); optional( jo, was_loaded, "fear_triggers", fear, trigger_reader ); }
void mission_type::load( JsonObject &jo, const std::string &src ) { const bool strict = src == "dda"; mandatory( jo, was_loaded, "name", name ); mandatory( jo, was_loaded, "difficulty", difficulty ); mandatory( jo, was_loaded, "value", value ); if( jo.has_member( "origins" ) ) { origins.clear(); for( auto &m : jo.get_tags( "origins" ) ) { origins.emplace_back( io::string_to_enum_look_up( io::origin_map, m ) ); } } if( std::any_of( origins.begin(), origins.end(), []( mission_origin origin ) { return origin == ORIGIN_ANY_NPC || origin == ORIGIN_OPENER_NPC || origin == ORIGIN_SECONDARY; } ) ) { auto djo = jo.get_object( "dialogue" ); // TODO: There should be a cleaner way to do it mandatory( djo, was_loaded, "describe", dialogue[ "describe" ] ); mandatory( djo, was_loaded, "offer", dialogue[ "offer" ] ); mandatory( djo, was_loaded, "accepted", dialogue[ "accepted" ] ); mandatory( djo, was_loaded, "rejected", dialogue[ "rejected" ] ); mandatory( djo, was_loaded, "advice", dialogue[ "advice" ] ); mandatory( djo, was_loaded, "inquire", dialogue[ "inquire" ] ); mandatory( djo, was_loaded, "success", dialogue[ "success" ] ); mandatory( djo, was_loaded, "success_lie", dialogue[ "success_lie" ] ); mandatory( djo, was_loaded, "failure", dialogue[ "failure" ] ); } optional( jo, was_loaded, "urgent", urgent ); optional( jo, was_loaded, "item", item_id ); optional( jo, was_loaded, "count", item_count, 1 ); goal = jo.get_enum_value<decltype( goal )>( "goal" ); assign_function( jo, "place", place, tripoint_function_map ); if( jo.has_string( "start" ) ) { assign_function( jo, "start", start, mission_function_map ); } else if( jo.has_member( "start" ) ) { JsonObject j_start = jo.get_object( "start" ); parse_start( j_start ); } assign_function( jo, "end", end, mission_function_map ); assign_function( jo, "fail", fail, mission_function_map ); assign( jo, "deadline_low", deadline_low, false, 1_days ); assign( jo, "deadline_high", deadline_high, false, 1_days ); if( jo.has_member( "followup" ) ) { follow_up = mission_type_id( jo.get_string( "followup" ) ); } if( jo.has_member( "monster_species" ) ) { monster_species = species_id( jo.get_string( "monster_species" ) ); } if( jo.has_member( "monster_type" ) ) { monster_type = mtype_id( jo.get_string( "monster_type" ) ); } if( jo.has_member( "monster_kill_goal" ) ) { monster_kill_goal = jo.get_int( "monster_kill_goal" ); } assign( jo, "destination", target_id, strict ); }
void overmap_connection::load( JsonObject &jo, const std::string & ) { mandatory( jo, false, "subtypes", subtypes ); }
void material_type::load( JsonObject &jsobj, const std::string & ) { mandatory( jsobj, was_loaded, "name", _name ); mandatory( jsobj, was_loaded, "bash_resist", _bash_resist ); mandatory( jsobj, was_loaded, "cut_resist", _cut_resist ); mandatory( jsobj, was_loaded, "acid_resist", _acid_resist ); mandatory( jsobj, was_loaded, "elec_resist", _elec_resist ); mandatory( jsobj, was_loaded, "fire_resist", _fire_resist ); mandatory( jsobj, was_loaded, "chip_resist", _chip_resist ); mandatory( jsobj, was_loaded, "density", _density ); assign( jsobj, "salvaged_into", _salvaged_into ); optional( jsobj, was_loaded, "repaired_with", _repaired_with, "null" ); optional( jsobj, was_loaded, "edible", _edible, false ); optional( jsobj, was_loaded, "soft", _soft, false ); auto arr = jsobj.get_array( "vitamins" ); while( arr.has_more() ) { auto pair = arr.next_array(); _vitamins.emplace( vitamin_id( pair.get_string( 0 ) ), pair.get_float( 1 ) ); } mandatory( jsobj, was_loaded, "bash_dmg_verb", _bash_dmg_verb ); mandatory( jsobj, was_loaded, "cut_dmg_verb", _cut_dmg_verb ); JsonArray jsarr = jsobj.get_array( "dmg_adj" ); while( jsarr.has_more() ) { _dmg_adj.push_back( jsarr.next_string() ); } JsonArray burn_data_array = jsobj.get_array( "burn_data" ); for( size_t intensity = 0; intensity < MAX_FIELD_DENSITY; intensity++ ) { if( burn_data_array.has_more() ) { JsonObject brn = burn_data_array.next_object(); _burn_data[ intensity ] = load_mat_burn_data( brn ); } else { // If not specified, supply default bool flammable = _fire_resist <= static_cast<int>( intensity ); mat_burn_data mbd; if( flammable ) { mbd.burn = 1; } _burn_data[ intensity ] = mbd; } } auto bp_array = jsobj.get_array( "burn_products" ); while( bp_array.has_more( ) ) { auto pair = bp_array.next_array(); _burn_products.emplace_back( pair.get_string( 0 ), static_cast< float >( pair.get_float( 1 ) ) ); } auto compactor_in_array = jsobj.get_array( "compact_accepts" ); while( compactor_in_array.has_more( ) ) { _compact_accepts.emplace_back( compactor_in_array.next_string() ); } auto compactor_out_array = jsobj.get_array( "compacts_into" ); while( compactor_out_array.has_more( ) ) { _compacts_into.emplace_back( compactor_out_array.next_string() ); } }
void mutation_branch::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "id", id ); mandatory( jo, was_loaded, "name", raw_name, translated_string_reader ); mandatory( jo, was_loaded, "description", raw_desc, translated_string_reader ); mandatory( jo, was_loaded, "points", points ); optional( jo, was_loaded, "visibility", visibility, 0 ); optional( jo, was_loaded, "ugliness", ugliness, 0 ); optional( jo, was_loaded, "starting_trait", startingtrait, false ); optional( jo, was_loaded, "mixed_effect", mixed_effect, false ); optional( jo, was_loaded, "active", activated, false ); optional( jo, was_loaded, "starts_active", starts_active, false ); optional( jo, was_loaded, "destroys_gear", destroys_gear, false ); optional( jo, was_loaded, "allow_soft_gear", allow_soft_gear, false ); optional( jo, was_loaded, "cost", cost, 0 ); optional( jo, was_loaded, "time", cooldown, 0 ); optional( jo, was_loaded, "hunger", hunger, false ); optional( jo, was_loaded, "thirst", thirst, false ); optional( jo, was_loaded, "fatigue", fatigue, false ); optional( jo, was_loaded, "valid", valid, true ); optional( jo, was_loaded, "purifiable", purifiable, true ); if( jo.has_object( "spawn_item" ) ) { auto si = jo.get_object( "spawn_item" ); optional( si, was_loaded, "type", spawn_item ); optional( si, was_loaded, "message", raw_spawn_item_message ); } if( jo.has_object( "ranged_mutation" ) ) { auto si = jo.get_object( "ranged_mutation" ); optional( si, was_loaded, "type", ranged_mutation ); optional( si, was_loaded, "message", raw_ranged_mutation_message ); } optional( jo, was_loaded, "initial_ma_styles", initial_ma_styles ); if( jo.has_array( "bodytemp_modifiers" ) ) { auto bodytemp_array = jo.get_array( "bodytemp_modifiers" ); bodytemp_min = bodytemp_array.get_int( 0 ); bodytemp_max = bodytemp_array.get_int( 1 ); } optional( jo, was_loaded, "bodytemp_sleep", bodytemp_sleep, 0 ); optional( jo, was_loaded, "threshold", threshold, false ); optional( jo, was_loaded, "profession", profession, false ); optional( jo, was_loaded, "debug", debug, false ); optional( jo, was_loaded, "player_display", player_display, true ); JsonArray vr = jo.get_array( "vitamin_rates" ); while( vr.has_more() ) { auto pair = vr.next_array(); vitamin_rates.emplace( vitamin_id( pair.get_string( 0 ) ), time_duration::from_turns( pair.get_int( 1 ) ) ); } auto vam = jo.get_array( "vitamins_absorb_multi" ); while( vam.has_more() ) { auto pair = vam.next_array(); std::map<vitamin_id, double> vit; auto vit_array = pair.get_array( 1 ); // fill the inner map with vitamins while( vit_array.has_more() ) { auto vitamins = vit_array.next_array(); vit.emplace( vitamin_id( vitamins.get_string( 0 ) ), vitamins.get_float( 1 ) ); } // assign the inner vitamin map to the material_id key vitamin_absorb_multi.emplace( material_id( pair.get_string( 0 ) ), vit ); } optional( jo, was_loaded, "healing_awake", healing_awake, 0.0f ); optional( jo, was_loaded, "healing_resting", healing_resting, 0.0f ); optional( jo, was_loaded, "hp_modifier", hp_modifier, 0.0f ); optional( jo, was_loaded, "hp_modifier_secondary", hp_modifier_secondary, 0.0f ); optional( jo, was_loaded, "hp_adjustment", hp_adjustment, 0.0f ); optional( jo, was_loaded, "stealth_modifier", stealth_modifier, 0.0f ); optional( jo, was_loaded, "str_modifier", str_modifier, 0.0f ); optional( jo, was_loaded, "dodge_modifier", dodge_modifier, 0.0f ); optional( jo, was_loaded, "speed_modifier", speed_modifier, 1.0f ); optional( jo, was_loaded, "movecost_modifier", movecost_modifier, 1.0f ); optional( jo, was_loaded, "movecost_flatground_modifier", movecost_flatground_modifier, 1.0f ); optional( jo, was_loaded, "movecost_obstacle_modifier", movecost_obstacle_modifier, 1.0f ); optional( jo, was_loaded, "attackcost_modifier", attackcost_modifier, 1.0f ); optional( jo, was_loaded, "max_stamina_modifier", max_stamina_modifier, 1.0f ); optional( jo, was_loaded, "weight_capacity_modifier", weight_capacity_modifier, 1.0f ); optional( jo, was_loaded, "hearing_modifier", hearing_modifier, 1.0f ); optional( jo, was_loaded, "noise_modifier", noise_modifier, 1.0f ); optional( jo, was_loaded, "metabolism_modifier", metabolism_modifier, 0.0f ); optional( jo, was_loaded, "thirst_modifier", thirst_modifier, 0.0f ); optional( jo, was_loaded, "fatigue_modifier", fatigue_modifier, 0.0f ); optional( jo, was_loaded, "fatigue_regen_modifier", fatigue_regen_modifier, 0.0f ); optional( jo, was_loaded, "stamina_regen_modifier", stamina_regen_modifier, 0.0f ); optional( jo, was_loaded, "overmap_sight", overmap_sight, 0.0f ); optional( jo, was_loaded, "overmap_multiplier", overmap_multiplier, 1.0f ); if( jo.has_object( "social_modifiers" ) ) { JsonObject sm = jo.get_object( "social_modifiers" ); social_mods = load_mutation_social_mods( sm ); } load_mutation_mods( jo, "passive_mods", mods ); /* Not currently supported due to inability to save active mutation state load_mutation_mods(jsobj, "active_mods", new_mut.mods); */ optional( jo, was_loaded, "prereqs", prereqs ); optional( jo, was_loaded, "prereqs2", prereqs2 ); optional( jo, was_loaded, "threshreq", threshreq ); optional( jo, was_loaded, "cancels", cancels ); optional( jo, was_loaded, "changes_to", replacements ); optional( jo, was_loaded, "leads_to", additions ); optional( jo, was_loaded, "flags", flags ); optional( jo, was_loaded, "types", types ); auto jsarr = jo.get_array( "category" ); while( jsarr.has_more() ) { std::string s = jsarr.next_string(); category.push_back( s ); mutations_category[s].push_back( trait_id( id ) ); } jsarr = jo.get_array( "wet_protection" ); while( jsarr.has_more() ) { JsonObject jo = jsarr.next_object(); std::string part_id = jo.get_string( "part" ); int ignored = jo.get_int( "ignored", 0 ); int neutral = jo.get_int( "neutral", 0 ); int good = jo.get_int( "good", 0 ); tripoint protect = tripoint( ignored, neutral, good ); protection[get_body_part_token( part_id )] = protect; } jsarr = jo.get_array( "encumbrance_always" ); while( jsarr.has_more() ) { JsonArray jo = jsarr.next_array(); std::string part_id = jo.next_string(); int enc = jo.next_int(); encumbrance_always[get_body_part_token( part_id )] = enc; } jsarr = jo.get_array( "encumbrance_covered" ); while( jsarr.has_more() ) { JsonArray jo = jsarr.next_array(); std::string part_id = jo.next_string(); int enc = jo.next_int(); encumbrance_covered[get_body_part_token( part_id )] = enc; } jsarr = jo.get_array( "restricts_gear" ); while( jsarr.has_more() ) { restricts_gear.insert( get_body_part_token( jsarr.next_string() ) ); } jsarr = jo.get_array( "armor" ); while( jsarr.has_more() ) { JsonObject jo = jsarr.next_object(); auto parts = jo.get_tags( "parts" ); std::set<body_part> bps; for( const std::string &part_string : parts ) { if( part_string == "ALL" ) { // Shorthand, since many mutations protect whole body bps.insert( all_body_parts.begin(), all_body_parts.end() ); } else { bps.insert( get_body_part_token( part_string ) ); } } resistances res = load_resistances_instance( jo ); for( body_part bp : bps ) { armor[ bp ] = res; } } if( jo.has_array( "attacks" ) ) { jsarr = jo.get_array( "attacks" ); while( jsarr.has_more() ) { JsonObject jo = jsarr.next_object(); attacks_granted.emplace_back( load_mutation_attack( jo ) ); } } else if( jo.has_object( "attacks" ) ) { JsonObject attack = jo.get_object( "attacks" ); attacks_granted.emplace_back( load_mutation_attack( attack ) ); } }
void start_location::load( JsonObject &jo ) { mandatory( jo, was_loaded, "name", _name, translated_string_reader ); mandatory( jo, was_loaded, "target", _target ); optional( jo, was_loaded, "flags", _flags, auto_flags_reader<> {} ); }
void mtype::load( JsonObject &jo ) { MonsterGenerator &gen = MonsterGenerator::generator(); // Name and name plural are not translated here, but when needed in // combination with the actual count in `mtype::nname`. mandatory( jo, was_loaded, "name", name ); // default behaviour: Assume the regular plural form (appending an “s”) optional( jo, was_loaded, "name_plural", name_plural, name + "s" ); mandatory( jo, was_loaded, "description", description, translated_string_reader ); // Have to overwrite the default { "hflesh" } here if( !was_loaded || jo.has_member( "material" ) ) { mat = { jo.get_string( "material" ) }; } optional( jo, was_loaded, "species", species, auto_flags_reader<species_id> {} ); optional( jo, was_loaded, "categories", categories, auto_flags_reader<> {} ); // See monfaction.cpp if( !was_loaded || jo.has_member( "default_faction" ) ) { const auto faction = mfaction_str_id( jo.get_string( "default_faction" ) ); default_faction = monfactions::get_or_add_faction( faction ); } if( !was_loaded || jo.has_member( "symbol" ) ) { sym = jo.get_string( "symbol" ); if( utf8_wrapper( sym ).display_width() != 1 ) { jo.throw_error( "monster symbol should be exactly one console cell width", "symbol" ); } } mandatory( jo, was_loaded, "color", color, color_reader{} ); const typed_flag_reader<decltype( Creature::size_map )> size_reader{ Creature::size_map, "invalid creature size" }; optional( jo, was_loaded, "size", size, size_reader, MS_MEDIUM ); const typed_flag_reader<decltype( gen.phase_map )> phase_reader{ gen.phase_map, "invalid phase id" }; optional( jo, was_loaded, "phase", phase, phase_reader, SOLID ); optional( jo, was_loaded, "diff", difficulty, 0 ); optional( jo, was_loaded, "aggression", agro, 0 ); optional( jo, was_loaded, "morale", morale, 0 ); optional( jo, was_loaded, "speed", speed, 0 ); optional( jo, was_loaded, "attack_cost", attack_cost, 100 ); optional( jo, was_loaded, "melee_skill", melee_skill, 0 ); optional( jo, was_loaded, "melee_dice", melee_dice, 0 ); optional( jo, was_loaded, "melee_dice_sides", melee_sides, 0 ); optional( jo, was_loaded, "melee_cut", melee_cut, 0 ); optional( jo, was_loaded, "dodge", sk_dodge, 0 ); optional( jo, was_loaded, "armor_bash", armor_bash, 0 ); optional( jo, was_loaded, "armor_cut", armor_cut, 0 ); optional( jo, was_loaded, "armor_acid", armor_acid, armor_cut / 2 ); optional( jo, was_loaded, "armor_fire", armor_fire, 0 ); optional( jo, was_loaded, "hp", hp, 0 ); optional( jo, was_loaded, "starting_ammo", starting_ammo ); optional( jo, was_loaded, "luminance", luminance, 0 ); optional( jo, was_loaded, "revert_to_itype", revert_to_itype, "" ); optional( jo, was_loaded, "vision_day", vision_day, 40 ); optional( jo, was_loaded, "vision_night", vision_night, 1 ); optional( jo, was_loaded, "armor_stab", armor_stab, 0.8f * armor_cut ); // TODO: allow adding/removing specific entries if `was_loaded` is true if( jo.has_array( "attack_effs" ) ) { JsonArray jsarr = jo.get_array( "attack_effs" ); while( jsarr.has_more() ) { JsonObject e = jsarr.next_object(); mon_effect_data new_eff( efftype_id( e.get_string( "id" ) ), e.get_int( "duration", 0 ), get_body_part_token( e.get_string( "bp", "NUM_BP" ) ), e.get_bool( "permanent", false ), e.get_int( "chance", 100 ) ); atk_effs.push_back( new_eff ); } } if( jo.has_member( "death_drops" ) ) { JsonIn &stream = *jo.get_raw( "death_drops" ); death_drops = item_group::load_item_group( stream, "distribution" ); } const typed_flag_reader<decltype( gen.death_map )> death_reader{ gen.death_map, "invalid monster death function" }; optional( jo, was_loaded, "death_function", dies, death_reader ); if( dies.empty() ) { // TODO: really needed? Is an empty `dies` container not allowed? dies.push_back( mdeath::normal ); } // TODO: allow overriding/adding/removing those if `was_loaded` is true gen.load_special_defense( this, jo, "special_when_hit" ); gen.load_special_attacks( this, jo, "special_attacks" ); // Disable upgrading when JSON contains `"upgrades": false`, but fallback to the // normal behavior (including error checking) if "upgrades" is not boolean or not `false`. if( jo.has_bool( "upgrades" ) && !jo.get_bool( "upgrades" ) ) { upgrade_group = mongroup_id::NULL_ID; upgrade_into = mtype_id::NULL_ID; upgrades = false; } else if( jo.has_member( "upgrades" ) ) { JsonObject up = jo.get_object( "upgrades" ); optional( up, was_loaded, "half_life", half_life, -1 ); optional( up, was_loaded, "into_group", upgrade_group, auto_flags_reader<mongroup_id> {}, mongroup_id::NULL_ID ); optional( up, was_loaded, "into", upgrade_into, auto_flags_reader<mtype_id> {}, mtype_id::NULL_ID ); upgrades = true; } const typed_flag_reader<decltype( gen.flag_map )> flag_reader{ gen.flag_map, "invalid monster flag" }; optional( jo, was_loaded, "flags", flags, flag_reader ); const typed_flag_reader<decltype( gen.trigger_map )> trigger_reader{ gen.trigger_map, "invalid monster trigger" }; optional( jo, was_loaded, "anger_triggers", anger, trigger_reader ); optional( jo, was_loaded, "placate_triggers", placate, trigger_reader ); optional( jo, was_loaded, "fear_triggers", fear, trigger_reader ); }
static int optional(char *arg) { return(!mandatory(arg)); }
void start_location::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "name", _name ); mandatory( jo, was_loaded, "target", _target ); optional( jo, was_loaded, "flags", _flags, auto_flags_reader<> {} ); }
void quality::load( JsonObject &jo ) { mandatory( jo, was_loaded, "name", name, translated_string_reader ); }
void npc_class::load( JsonObject &jo, const std::string & ) { mandatory( jo, was_loaded, "name", name, translated_string_reader ); mandatory( jo, was_loaded, "job_description", job_description, translated_string_reader ); optional( jo, was_loaded, "common", common, true ); bonus_str = load_distribution( jo, "bonus_str" ); bonus_dex = load_distribution( jo, "bonus_dex" ); bonus_int = load_distribution( jo, "bonus_int" ); bonus_per = load_distribution( jo, "bonus_per" ); optional( jo, was_loaded, "shopkeeper_item_group", shopkeeper_item_group, "EMPTY_GROUP" ); optional( jo, was_loaded, "worn_override", worn_override ); optional( jo, was_loaded, "carry_override", carry_override ); optional( jo, was_loaded, "weapon_override", weapon_override ); if( jo.has_array( "traits" ) ) { traits = trait_group::load_trait_group( *jo.get_raw( "traits" ), "collection" ); } /* Mutation rounds can be specified as follows: * "mutation_rounds": { * "ANY" : { "constant": 1 }, * "INSECT" : { "rng": [1, 3] } * } */ if( jo.has_object( "mutation_rounds" ) ) { const std::map<std::string, mutation_category_trait> &mutation_categories = mutation_category_trait::get_all(); auto jo2 = jo.get_object( "mutation_rounds" ); for( auto &mutation : jo2.get_member_names() ) { auto category_match = [&mutation]( std::pair<const std::string, mutation_category_trait> p ) { return p.second.id == mutation; }; if( std::find_if( mutation_categories.begin(), mutation_categories.end(), category_match ) == mutation_categories.end() ) { debugmsg( "Unrecognized mutation category %s", mutation ); continue; } auto distrib = jo2.get_object( mutation ); mutation_rounds[mutation] = load_distribution( distrib ); } } if( jo.has_array( "skills" ) ) { JsonArray jarr = jo.get_array( "skills" ); while( jarr.has_more() ) { JsonObject skill_obj = jarr.next_object(); auto skill_ids = skill_obj.get_tags( "skill" ); if( skill_obj.has_object( "level" ) ) { distribution dis = load_distribution( skill_obj, "level" ); for( const auto &sid : skill_ids ) { skills[ skill_id( sid ) ] = dis; } } else { distribution dis = load_distribution( skill_obj, "bonus" ); for( const auto &sid : skill_ids ) { bonus_skills[ skill_id( sid ) ] = dis; } } } } }
void mtype::load( JsonObject &jo, const std::string &src ) { bool strict = src == "dda"; MonsterGenerator &gen = MonsterGenerator::generator(); // Name and name plural are not translated here, but when needed in // combination with the actual count in `mtype::nname`. mandatory( jo, was_loaded, "name", name ); // default behavior: Assume the regular plural form (appending an “s”) optional( jo, was_loaded, "name_plural", name_plural, name + "s" ); optional( jo, was_loaded, "description", description ); optional( jo, was_loaded, "material", mat, auto_flags_reader<material_id> {} ); optional( jo, was_loaded, "species", species, auto_flags_reader<species_id> {} ); optional( jo, was_loaded, "categories", categories, auto_flags_reader<> {} ); // See monfaction.cpp if( !was_loaded || jo.has_member( "default_faction" ) ) { const auto faction = mfaction_str_id( jo.get_string( "default_faction" ) ); default_faction = monfactions::get_or_add_faction( faction ); } if( !was_loaded || jo.has_member( "symbol" ) ) { sym = jo.get_string( "symbol" ); if( utf8_wrapper( sym ).display_width() != 1 ) { jo.throw_error( "monster symbol should be exactly one console cell width", "symbol" ); } } if( was_loaded && jo.has_member( "copy-from" ) && looks_like.empty() ) { looks_like = jo.get_string( "copy-from" ); } if( jo.has_member( "looks_like" ) ) { looks_like = jo.get_string( "looks_like" ); } assign( jo, "color", color ); const typed_flag_reader<decltype( Creature::size_map )> size_reader{ Creature::size_map, "invalid creature size" }; optional( jo, was_loaded, "size", size, size_reader, MS_MEDIUM ); const typed_flag_reader<decltype( gen.phase_map )> phase_reader{ gen.phase_map, "invalid phase id" }; optional( jo, was_loaded, "phase", phase, phase_reader, SOLID ); assign( jo, "diff", difficulty, strict, 0 ); assign( jo, "hp", hp, strict, 1 ); assign( jo, "speed", speed, strict, 0 ); assign( jo, "aggression", agro, strict, -100, 100 ); assign( jo, "morale", morale, strict ); assign( jo, "attack_cost", attack_cost, strict, 0 ); assign( jo, "melee_skill", melee_skill, strict, 0 ); assign( jo, "melee_dice", melee_dice, strict, 0 ); assign( jo, "melee_dice_sides", melee_sides, strict, 0 ); assign( jo, "dodge", sk_dodge, strict, 0 ); assign( jo, "armor_bash", armor_bash, strict, 0 ); assign( jo, "armor_cut", armor_cut, strict, 0 ); assign( jo, "armor_stab", armor_stab, strict, 0 ); assign( jo, "armor_acid", armor_acid, strict, 0 ); assign( jo, "armor_fire", armor_fire, strict, 0 ); assign( jo, "vision_day", vision_day, strict, 0 ); assign( jo, "vision_night", vision_night, strict, 0 ); optional( jo, was_loaded, "starting_ammo", starting_ammo ); optional( jo, was_loaded, "luminance", luminance, 0 ); optional( jo, was_loaded, "revert_to_itype", revert_to_itype, "" ); optional( jo, was_loaded, "attack_effs", atk_effs, mon_attack_effect_reader{} ); // TODO: make this work with `was_loaded` if( jo.has_array( "melee_damage" ) ) { JsonArray arr = jo.get_array( "melee_damage" ); melee_damage = load_damage_instance( arr ); } else if( jo.has_object( "melee_damage" ) ) { melee_damage = load_damage_instance( jo ); } if( jo.has_int( "melee_cut" ) ) { int bonus_cut = jo.get_int( "melee_cut" ); melee_damage.add_damage( DT_CUT, bonus_cut ); } if( jo.has_member( "death_drops" ) ) { JsonIn &stream = *jo.get_raw( "death_drops" ); death_drops = item_group::load_item_group( stream, "distribution" ); } assign( jo, "harvest", harvest, strict ); const typed_flag_reader<decltype( gen.death_map )> death_reader{ gen.death_map, "invalid monster death function" }; optional( jo, was_loaded, "death_function", dies, death_reader ); if( dies.empty() ) { // TODO: really needed? Is an empty `dies` container not allowed? dies.push_back( mdeath::normal ); } assign( jo, "emit_fields", emit_fields ); if( jo.has_member( "special_when_hit" ) ) { JsonArray jsarr = jo.get_array( "special_when_hit" ); const auto iter = gen.defense_map.find( jsarr.get_string( 0 ) ); if( iter == gen.defense_map.end() ) { jsarr.throw_error( "Invalid monster defense function" ); } sp_defense = iter->second; def_chance = jsarr.get_int( 1 ); } else if( !was_loaded ) { sp_defense = &mdefense::none; def_chance = 0; } if( !was_loaded || jo.has_member( "special_attacks" ) ) { special_attacks.clear(); special_attacks_names.clear(); add_special_attacks( jo, "special_attacks", src ); } else { // Note: special_attacks left as is, new attacks are added to it! // Note: member name prefixes are compatible with those used by generic_typed_reader if( jo.has_object( "extend" ) ) { auto tmp = jo.get_object( "extend" ); add_special_attacks( tmp, "special_attacks", src ); } if( jo.has_object( "delete" ) ) { auto tmp = jo.get_object( "delete" ); remove_special_attacks( tmp, "special_attacks", src ); } } // Disable upgrading when JSON contains `"upgrades": false`, but fallback to the // normal behavior (including error checking) if "upgrades" is not boolean or not `false`. if( jo.has_bool( "upgrades" ) && !jo.get_bool( "upgrades" ) ) { upgrade_group = mongroup_id::NULL_ID(); upgrade_into = mtype_id::NULL_ID(); upgrades = false; } else if( jo.has_member( "upgrades" ) ) { JsonObject up = jo.get_object( "upgrades" ); optional( up, was_loaded, "half_life", half_life, -1 ); optional( up, was_loaded, "age_grow", age_grow, -1 ); optional( up, was_loaded, "into_group", upgrade_group, auto_flags_reader<mongroup_id> {}, mongroup_id::NULL_ID() ); optional( up, was_loaded, "into", upgrade_into, auto_flags_reader<mtype_id> {}, mtype_id::NULL_ID() ); upgrades = true; } //Reproduction if( jo.has_member( "reproduction" ) ) { JsonObject repro = jo.get_object( "reproduction" ); optional( repro, was_loaded, "baby_count", baby_count, -1 ); optional( repro, was_loaded, "baby_timer", baby_timer, -1 ); optional( repro, was_loaded, "baby_monster", baby_monster, auto_flags_reader<mtype_id> {}, mtype_id::NULL_ID() ); optional( repro, was_loaded, "baby_egg", baby_egg, auto_flags_reader<itype_id> {}, "null" ); if( jo.has_member( "baby_flags" ) ) { baby_flags.clear(); JsonArray baby_tags = jo.get_array( "baby_flags" ); while( baby_tags.has_more() ) { baby_flags.push_back( baby_tags.next_string() ); } } reproduces = true; } if( jo.has_member( "biosignature" ) ) { JsonObject biosig = jo.get_object( "biosignature" ); optional( biosig, was_loaded, "biosig_timer", biosig_timer, -1 ); optional( biosig, was_loaded, "biosig_item", biosig_item, auto_flags_reader<itype_id> {}, "null" ); biosignatures = true; } optional( jo, was_loaded, "burn_into", burn_into, auto_flags_reader<mtype_id> {}, mtype_id::NULL_ID() ); const typed_flag_reader<decltype( gen.flag_map )> flag_reader{ gen.flag_map, "invalid monster flag" }; optional( jo, was_loaded, "flags", flags, flag_reader ); // Can't calculate yet - we want all flags first optional( jo, was_loaded, "bash_skill", bash_skill, -1 ); const typed_flag_reader<decltype( gen.trigger_map )> trigger_reader{ gen.trigger_map, "invalid monster trigger" }; optional( jo, was_loaded, "anger_triggers", anger, trigger_reader ); optional( jo, was_loaded, "placate_triggers", placate, trigger_reader ); optional( jo, was_loaded, "fear_triggers", fear, trigger_reader ); if( jo.has_member( "path_settings" ) ) { auto jop = jo.get_object( "path_settings" ); // Here rather than in pathfinding.cpp because we want monster-specific defaults and was_loaded optional( jop, was_loaded, "max_dist", path_settings.max_dist, 0 ); optional( jop, was_loaded, "max_length", path_settings.max_length, -1 ); optional( jop, was_loaded, "bash_strength", path_settings.bash_strength, -1 ); optional( jop, was_loaded, "allow_open_doors", path_settings.allow_open_doors, false ); optional( jop, was_loaded, "avoid_traps", path_settings.avoid_traps, false ); optional( jop, was_loaded, "allow_climb_stairs", path_settings.allow_climb_stairs, true ); } }
/* * Return 0 means data->status is valid. * protocol is only valid if svc == ppp. */ static int authorize_svc(char *user, int svc, char *protocol, char *svcname, struct author_data *data) { int max_args; char **out_args, **outp; char *nas_arg, *cfg_arg; int i, j; char **cfg_args; char **cfg_argp; int deny_by_default; NODE *node; int replaced = 0; int added = 0; int cfg_cnt; /* Does this service exist? */ node = cfg_get_svc_node(user, svc, protocol, svcname, TAC_PLUS_RECURSE); if (!node) { /* Service not found. If the default is permit, or this is an * PPP/LCP request and other ppp services are configured, * we'll allow it. */ if (cfg_user_svc_default_is_permit(user)) { if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "svc=%s protocol=%s svcname=%s not found, " "permitted by default", cfg_nodestring(svc), protocol ? protocol : "", svcname ? svcname : ""); data->status = AUTHOR_STATUS_PASS_ADD; data->num_out_args = 0; data->output_args = NULL; return(0); } if (ppp_lcp_allowed(svc, protocol, user)) { data->status = AUTHOR_STATUS_PASS_ADD; data->num_out_args = 0; data->output_args = NULL; return(0); } if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "svc=%s protocol=%s not found, denied by default", cfg_nodestring(svc), protocol ? protocol : ""); data->status = AUTHOR_STATUS_FAIL; data->num_out_args = 0; data->output_args = NULL; return(0); } /* Get server args configured in the config file. */ cfg_args = cfg_get_svc_attrs(node, &deny_by_default); /* Check the nas args for well-formedness */ for (i = 0; i < data->num_in_args; i++) { if (!arg_ok(data->input_args[i])) { char buf[MAX_INPUT_LINE_LEN+50]; snprintf(buf, sizeof(buf), "Illegal arg %s from NAS", data->input_args[i]); data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup(buf); report(LOG_ERR, "%s: Error %s", session.peer, buf); /* free any server arguments */ for (cfg_argp = cfg_args; cfg_args && *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } } /* How many configured AV pairs are there ? */ for (cfg_cnt = 0; cfg_args && cfg_args[cfg_cnt];) cfg_cnt++; /* Allocate space for in + out args */ max_args = cfg_cnt + data->num_in_args; out_args = (char **)tac_malloc((max_args + 1) * sizeof(char *)); outp = out_args; data->num_out_args = 0; memset(out_args, 0, (max_args + 1) * sizeof(char *)); for (i = 0; i < data->num_in_args; i++) { nas_arg = data->input_args[i]; /* always pass these pairs through unchanged */ if (match_attrs(nas_arg, "service=") || match_attrs(nas_arg, "protocol=") || match_attrs(nas_arg, "cmd=")) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s (passed thru)", nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; continue; } /* NAS AV pair is mandatory */ if (mandatory(nas_arg)) { /* * a). look for an exact attribute,value match in the daemon's * mandatory list. If found, add the AV pair to the output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (optional(cfg_arg)) continue; if (STREQ(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (a)", nas_arg, cfg_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } } /* * b). If an exact match doesn't exist, look in the daemon's * optional list for the first attribute match. If found, add the * NAS AV pair to the output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (mandatory(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s, svr:%s -> add %s (b)", nas_arg, cfg_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } } /* * c). If no attribute match exists, deny the command if the * default is to deny */ if (deny_by_default) { data->status = AUTHOR_STATUS_FAIL; if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:absent, default=deny -> " "denied (c)", nas_arg); } if (out_args) { for (i = 0; i < data->num_out_args; i++) free(out_args[i]); free(out_args); } data->num_out_args = 0; data->output_args = NULL; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } /* * d). If the default is permit, add the NAS AV pair to the output */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s, svr:absent, default=permit -> add %s (d)", nas_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } else { /* * NAS AV pair is Optional * * e). look for an exact attribute,value match in the mandatory * list. If found, add DAEMON's AV pair to output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg) && match_values(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(e)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * f). If not found, look for the first attribute match in the * mandatory list. If found, add DAEMONS's AV pair to output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(f)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * g). If no mandatory match exists, look for an exact * attribute,value pair match among the daemon's optional AV * pairs. If found add the DAEMON's matching AV pair to the * output. */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (!optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg) && match_values(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(g)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * h). If no exact match exists, locate the first attribute match * among the daemon's optional AV pairs. If found add the DAEMON's * matching AV pair to the output */ for (j = 0; j < cfg_cnt; j++) { cfg_arg = cfg_args[j]; if (!optional(cfg_arg)) continue; if (match_attrs(nas_arg, cfg_arg)) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:%s -> replace with %s " "(h)", nas_arg, cfg_arg, cfg_arg); } *outp++ = tac_strdup(cfg_arg); data->num_out_args++; replaced++; goto next_nas_arg; } } /* * i). If no match is found, delete the AV pair if default is deny */ if (deny_by_default) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:absent/deny -> delete %s (i)", nas_arg, nas_arg); } replaced++; goto next_nas_arg; } /* j). If the default is permit add the NAS AV pair to the output */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:%s svr:absent/permit -> add %s (j)", nas_arg, nas_arg); } *outp++ = tac_strdup(nas_arg); data->num_out_args++; goto next_nas_arg; } next_nas_arg:; } /* * k). After all AV pairs have been processed, for each mandatory DAEMON * AV pair, if there is no attribute match already in the output list, add * the AV pair (add only one AV pair for each mandatory attribute) */ for (i = 0; i < cfg_cnt; i++) { cfg_arg = cfg_args[i]; if (!mandatory(cfg_arg)) continue; for (j = 0; j < data->num_out_args; j++) { char *output_arg = out_args[j]; if (match_attrs(cfg_arg, output_arg)) { goto next_cfg_arg; } } /* Attr is required by daemon but not present in output. Add it */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "nas:absent, server:%s -> add %s (k)", cfg_arg, cfg_arg); } added++; *outp++ = tac_strdup(cfg_arg); data->num_out_args++; next_cfg_arg: ; } /* * If we replaced or deleted some pairs we must return the entire list we * have constructed */ if (replaced) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "replaced %d args", replaced); } data->status = AUTHOR_STATUS_PASS_REPL; data->output_args = out_args; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } /* * We added something not on the original nas list, but did not replace or * delete anything. We should return only the additions */ if (added) { if (debug & DEBUG_AUTHOR_FLAG) report(LOG_DEBUG, "added %d args", added); /* throw away output args which are just copies of the input args */ for (i = 0; i < data->num_in_args; i++) { if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "out_args[%d] = %s input copy discarded", i, out_args[i]); } free(out_args[i]); out_args[i] = NULL; } /* * Now compact the new args added to the end of the array down to the * beginning */ j = 0; for (i = data->num_in_args; i < data->num_out_args; i++) { if (out_args[j]) /* we goofed */ report(LOG_ERR, "%s: out_args[%d] should be NULL", session.peer, j); if (!out_args[i]) /* we goofed */ report(LOG_ERR, "%s: out_args[%d] should not be NULL", session.peer, i); if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "out_args[%d] = %s compacted to out_args[%d]", i, out_args[i], j); } out_args[j++] = out_args[i]; out_args[i] = NULL; } data->num_out_args = j; if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "%d output args", data->num_out_args); } /* should/could do a realloc here but it won't matter */ data->status = AUTHOR_STATUS_PASS_ADD; data->output_args = out_args; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } /* * no additions or replacements. Input and output are identical. Don't * need to return anything */ if (debug & DEBUG_AUTHOR_FLAG) { report(LOG_DEBUG, "added %d", added); } data->status = AUTHOR_STATUS_PASS_ADD; if (out_args) { for (i = 0; i < data->num_out_args; i++) { free(out_args[i]); } free(out_args); } /* Final sanity check */ if (data->num_out_args != data->num_in_args) { data->status = AUTHOR_STATUS_ERROR; data->admin_msg = tac_strdup("Bad output arg cnt from do_author"); report(LOG_ERR, "%s: Error %s", session.peer, data->admin_msg); /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); } data->num_out_args = 0; data->output_args = NULL; /* free the server arguments */ for (cfg_argp = cfg_args; *cfg_argp; cfg_argp++) free(*cfg_argp); free(cfg_args); return(0); }