void mtype::add_special_attacks( JsonObject &jo, const std::string &member, const std::string &src ) { if( !jo.has_array( member ) ) { return; } JsonArray outer = jo.get_array( member ); while( outer.has_more() ) { if( outer.test_array() ) { add_special_attack( outer.next_array(), src ); } else if( outer.test_object() ) { add_special_attack( outer.next_object(), src ); } else { outer.throw_error( "array element is neither array nor object." ); } } }
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 MonsterGroupManager::LoadMonsterGroup(JsonObject &jo) { MonsterGroup g; g.name = jo.get_string("name"); g.defaultMonster = jo.get_string("default"); if (jo.has_array("monsters")){ JsonArray monarr = jo.get_array("monsters"); while (monarr.has_more()) { JsonObject mon = monarr.next_object(); std::string name = mon.get_string("monster"); int freq = mon.get_int("freq"); int cost = mon.get_int("cost_multiplier"); int pack_min = 1; int pack_max = 1; if(mon.has_member("pack_size")){ JsonArray packarr = mon.get_array("pack_size"); pack_min = packarr.next_int(); pack_max = packarr.next_int(); } int starts = 0; int ends = 0; if(mon.has_member("starts")){ starts = mon.get_int("starts"); } if(mon.has_member("ends")){ ends = mon.get_int("ends"); } MonsterGroupEntry new_mon_group = MonsterGroupEntry(name,freq,cost,pack_min,pack_max,starts,ends); if(mon.has_member("conditions")){ JsonArray conditions_arr = mon.get_array("conditions"); while(conditions_arr.has_more()){ new_mon_group.conditions.push_back(conditions_arr.next_string()); } } g.monsters.push_back(new_mon_group); } } monsterGroupMap[g.name] = g; }
void map_data_common_t::load( JsonObject &jo, const std::string &src ) { if( jo.has_member( "examine_action" ) ) { examine = iexamine_function_from_string( jo.get_string( "examine_action" ) ); } else { examine = iexamine_function_from_string( "none" ); } if( jo.has_array( "harvest_by_season" ) ) { JsonArray jsarr = jo.get_array( "harvest_by_season" ); while( jsarr.has_more() ) { JsonObject harvest_jo = jsarr.next_object(); auto season_strings = harvest_jo.get_tags( "seasons" ); std::set<season_type> seasons; std::transform( season_strings.begin(), season_strings.end(), std::inserter( seasons, seasons.begin() ), []( const std::string & data ) { return io::string_to_enum<season_type>( data ); } ); harvest_id hl; if( harvest_jo.has_array( "entries" ) ) { // @todo: A better inline name - can't use id or name here because it's not set yet size_t num = harvest_list::all().size() + 1; hl = harvest_list::load( harvest_jo, src, string_format( "harvest_inline_%d", static_cast<int>( num ) ) ); } else if( harvest_jo.has_string( "id" ) ) { hl = harvest_id( harvest_jo.get_string( "id" ) ); } else { jo.throw_error( "Each harvest entry must specify either \"entries\" or \"id\"", "harvest_by_season" ); } for( season_type s : seasons ) { harvest_by_season[ s ] = hl; } } } optional( jo, false, "description", description, translated_string_reader ); }
void mutation_branch::load_trait_group( JsonArray &entries, const trait_group::Trait_group_tag &gid, const bool is_collection ) { Trait_group &tg = make_group_or_throw( gid, is_collection ); while( entries.has_more() ) { // Backwards-compatibility with old format ["TRAIT", 100] if( entries.test_array() ) { JsonArray subarr = entries.next_array(); trait_id id( subarr.get_string( 0 ) ); std::unique_ptr<Trait_creation_data> ptr( new Single_trait_creator( id, subarr.get_int( 1 ) ) ); tg.add_entry( ptr ); // Otherwise load new format {"trait": ... } or {"group": ...} } else { JsonObject subobj = entries.next_object(); add_entry( tg, subobj ); } } }
/* * Invlet cache: player specific, thus not wrapped in inventory::json_load/save */ void inventory::json_load_invcache(JsonIn &jsin) { try { JsonArray ja = jsin.get_array(); while ( ja.has_more() ) { JsonObject jo = ja.next_object(); std::set<std::string> members = jo.get_member_names(); for (std::set<std::string>::iterator it = members.begin(); it != members.end(); ++it) { std::vector<char> vect; JsonArray pvect = jo.get_array(*it); while ( pvect.has_more() ) { vect.push_back( pvect.next_int() ); } invlet_cache[*it] = vect; } } } catch (std::string jsonerr) { debugmsg("bad invcache json:\n%s", jsonerr.c_str() ); } }
void mutation_branch::add_entry( Trait_group &tg, JsonObject &obj ) { std::unique_ptr<Trait_creation_data> ptr; int probability = obj.get_int( "prob", 100 ); JsonArray jarr; if( obj.has_member( "collection" ) ) { ptr.reset( new Trait_group_collection( probability ) ); jarr = obj.get_array( "collection" ); } else if( obj.has_member( "distribution" ) ) { ptr.reset( new Trait_group_distribution( probability ) ); jarr = obj.get_array( "distribution" ); } if( ptr ) { Trait_group &tg2 = dynamic_cast<Trait_group &>( *ptr ); while( jarr.has_more() ) { JsonObject job2 = jarr.next_object(); add_entry( tg2, job2 ); } tg.add_entry( ptr ); return; } if( obj.has_member( "trait" ) ) { trait_id id( obj.get_string( "trait" ) ); ptr.reset( new Single_trait_creator( id, probability ) ); } else if( obj.has_member( "group" ) ) { ptr.reset( new Trait_group_creator( trait_group::Trait_group_tag( obj.get_string( "group" ) ), probability ) ); } if( !ptr ) { return; } tg.add_entry( ptr ); }
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 ); }
void load_mutation(JsonObject &jsobj) { trait new_trait; JsonArray jsarr; std::string id = jsobj.get_string("id"); new_trait.id = id; new_trait.name = _(jsobj.get_string("name").c_str()); new_trait.description = _(jsobj.get_string("description").c_str()); new_trait.points = jsobj.get_int("points"); new_trait.visibility = jsobj.get_int("visibility", 0); new_trait.ugliness = jsobj.get_int("ugliness", 0); new_trait.startingtrait = jsobj.get_bool("starting_trait", false); new_trait.mixed_effect = jsobj.get_bool("mixed_effect", false); new_trait.activated = jsobj.get_bool("active", false); new_trait.cost = jsobj.get_int("cost", 0); new_trait.cooldown = jsobj.get_int("time",0); new_trait.hunger = jsobj.get_bool("hunger",false); new_trait.thirst = jsobj.get_bool("thirst",false); new_trait.fatigue = jsobj.get_bool("fatigue",false); new_trait.charge = 0; traits[id] = new_trait; mutation_data[id].valid = jsobj.get_bool("valid", true); mutation_data[id].purifiable = jsobj.get_bool("purifiable", true); mutation_data[id].threshold = jsobj.get_bool("threshold", false); jsarr = jsobj.get_array("prereqs"); while (jsarr.has_more()) { mutation_data[id].prereqs.push_back(jsarr.next_string()); } // Helps to be able to have a trait require more than one other trait // (Individual prereq-lists are "OR", not "AND".) // Traits shoud NOT appear in both lists for a given mutation, unless // you want that trait to satisfy both requirements. // These are additional to the first list. jsarr = jsobj.get_array("prereqs2"); while (jsarr.has_more()) { mutation_data[id].prereqs2.push_back(jsarr.next_string()); } // Dedicated-purpose prereq slot for Threshold mutations jsarr = jsobj.get_array("threshreq"); // Stuff like Huge might fit in more than one mutcat post-threshold, so yeah while (jsarr.has_more()) { mutation_data[id].threshreq.push_back(jsarr.next_string()); } jsarr = jsobj.get_array("cancels"); while (jsarr.has_more()) { mutation_data[id].cancels.push_back(jsarr.next_string()); } jsarr = jsobj.get_array("changes_to"); while (jsarr.has_more()) { mutation_data[id].replacements.push_back(jsarr.next_string()); } jsarr = jsobj.get_array("leads_to"); while (jsarr.has_more()) { mutation_data[id].additions.push_back(jsarr.next_string()); } jsarr = jsobj.get_array("category"); while (jsarr.has_more()) { std::string s = jsarr.next_string(); mutation_data[id].category.push_back(s); mutations_category[s].push_back(id); } jsarr = jsobj.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); mutation_data[id].protection[part_id] = mutation_wet(body_parts[part_id], protect); } }
void input_manager::init() { init_keycode_mapping(); std::ifstream data_file; std::string file_name = "data/raw/keybindings.json"; data_file.open(file_name.c_str(), std::ifstream::in | std::ifstream::binary); if(!data_file.good()) { throw "Could not read " + file_name; } JsonIn jsin(data_file); //Crawl through once and create an entry for every definition jsin.start_array(); while (!jsin.end_array()) { // JSON object representing the action JsonObject action = jsin.get_object(); const std::string action_id = action.get_string("id"); actionID_to_name[action_id] = action.get_string("name", action_id); const std::string context = action.get_string("category", "default"); // Iterate over the bindings JSON array JsonArray bindings = action.get_array("bindings"); const bool defaultcontext = (context == "default"); while (bindings.has_more()) { JsonObject keybinding = bindings.next_object(); std::string input_method = keybinding.get_string("input_method"); input_event new_event; if(input_method == "keyboard") { new_event.type = CATA_INPUT_KEYBOARD; } else if(input_method == "gamepad") { new_event.type = CATA_INPUT_GAMEPAD; } else if(input_method == "mouse") { new_event.type = CATA_INPUT_MOUSE; } if (keybinding.has_array("key")) { JsonArray keys = keybinding.get_array("key"); while (keys.has_more()) { new_event.sequence.push_back( get_keycode(keys.next_string()) ); } } else { // assume string if not array, and throw if not string new_event.sequence.push_back( get_keycode(keybinding.get_string("key")) ); } if (defaultcontext) { action_to_input[action_id].push_back(new_event); } else { action_contexts[context][action_id].push_back(new_event); } } } data_file.close(); }
void mutation_branch::load( JsonObject &jsobj ) { const std::string id = jsobj.get_string( "id" ); mutation_branch &new_mut = mutation_data[id]; JsonArray jsarr; new_mut.name = _(jsobj.get_string("name").c_str()); new_mut.description = _(jsobj.get_string("description").c_str()); new_mut.points = jsobj.get_int("points"); new_mut.visibility = jsobj.get_int("visibility", 0); new_mut.ugliness = jsobj.get_int("ugliness", 0); new_mut.startingtrait = jsobj.get_bool("starting_trait", false); new_mut.mixed_effect = jsobj.get_bool("mixed_effect", false); new_mut.activated = jsobj.get_bool("active", false); new_mut.cost = jsobj.get_int("cost", 0); new_mut.cooldown = jsobj.get_int("time",0); new_mut.hunger = jsobj.get_bool("hunger",false); new_mut.thirst = jsobj.get_bool("thirst",false); new_mut.fatigue = jsobj.get_bool("fatigue",false); new_mut.valid = jsobj.get_bool("valid", true); new_mut.purifiable = jsobj.get_bool("purifiable", true); for( auto & s : jsobj.get_string_array( "initial_ma_styles" ) ) { new_mut.initial_ma_styles.push_back( matype_id( s ) ); } JsonArray bodytemp_array = jsobj.get_array( "bodytemp_modifiers" ); if( bodytemp_array.has_more() ) { new_mut.bodytemp_min = bodytemp_array.get_int( 0 ); new_mut.bodytemp_max = bodytemp_array.get_int( 1 ); } new_mut.bodytemp_sleep = jsobj.get_int( "bodytemp_sleep", 0 ); new_mut.threshold = jsobj.get_bool("threshold", false); new_mut.profession = jsobj.get_bool("profession", false); load_mutation_mods(jsobj, "passive_mods", new_mut.mods); /* Not currently supported due to inability to save active mutation state load_mutation_mods(jsobj, "active_mods", new_mut.mods); */ new_mut.prereqs = jsobj.get_string_array( "prereqs" ); // Helps to be able to have a trait require more than one other trait // (Individual prereq-lists are "OR", not "AND".) // Traits shoud NOT appear in both lists for a given mutation, unless // you want that trait to satisfy both requirements. // These are additional to the first list. new_mut.prereqs2 = jsobj.get_string_array( "prereqs2" ); // Dedicated-purpose prereq slot for Threshold mutations // Stuff like Huge might fit in more than one mutcat post-threshold, so yeah new_mut.threshreq = jsobj.get_string_array( "threshreq" ); new_mut.cancels = jsobj.get_string_array( "cancels" ); new_mut.replacements = jsobj.get_string_array( "changes_to" ); new_mut.additions = jsobj.get_string_array( "leads_to" ); jsarr = jsobj.get_array("category"); while (jsarr.has_more()) { std::string s = jsarr.next_string(); new_mut.category.push_back(s); mutations_category[s].push_back(id); } jsarr = jsobj.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); new_mut.protection[get_body_part_token( part_id )] = protect; } }
/** *Caches a vehicle definition from a JsonObject to be loaded after itypes is initialized. */ void vehicle_prototype::load(JsonObject &jo) { vehicle_prototype &vproto = vtypes[ vproto_id( jo.get_string( "id" ) ) ]; // If there are already parts defined, this vehicle prototype overrides an existing one. // If the json contains a name, it means a completely new prototype (replacing the // original one), therefor the old data has to be cleared. // If the json does not contain a name (the prototype would have no name), it means appending // to the existing prototype (the parts are not cleared). if( !vproto.parts.empty() && jo.has_string( "name" ) ) { vproto = vehicle_prototype(); } if( vproto.parts.empty() ) { vproto.name = jo.get_string( "name" ); } vgroups[vgroup_id(jo.get_string("id"))].add_vehicle(vproto_id(jo.get_string("id")), 100); JsonArray parts = jo.get_array("parts"); while (parts.has_more()) { JsonObject part = parts.next_object(); part_def pt; pt.pos = point( part.get_int( "x" ), part.get_int( "y" ) ); pt.part = vpart_id( part.get_string( "part" ) ); assign( part, "ammo", pt.with_ammo, true, 0, 100 ); assign( part, "ammo_types", pt.ammo_types, true ); assign( part, "ammo_qty", pt.ammo_qty, true, 0 ); assign( part, "fuel", pt.fuel, true ); vproto.parts.push_back( pt ); } JsonArray items = jo.get_array("items"); while(items.has_more()) { JsonObject spawn_info = items.next_object(); vehicle_item_spawn next_spawn; next_spawn.pos.x = spawn_info.get_int("x"); next_spawn.pos.y = spawn_info.get_int("y"); next_spawn.chance = spawn_info.get_int("chance"); if(next_spawn.chance <= 0 || next_spawn.chance > 100) { debugmsg("Invalid spawn chance in %s (%d, %d): %d%%", vproto.name.c_str(), next_spawn.pos.x, next_spawn.pos.y, next_spawn.chance); } // constrain both with_magazine and with_ammo to [0-100] next_spawn.with_magazine = std::max( std::min( spawn_info.get_int( "magazine", next_spawn.with_magazine ), 100 ), 0 ); next_spawn.with_ammo = std::max( std::min( spawn_info.get_int( "ammo", next_spawn.with_ammo ), 100 ), 0 ); if(spawn_info.has_array("items")) { //Array of items that all spawn together (ie jack+tire) JsonArray item_group = spawn_info.get_array("items"); while(item_group.has_more()) { next_spawn.item_ids.push_back(item_group.next_string()); } } else if(spawn_info.has_string("items")) { //Treat single item as array next_spawn.item_ids.push_back(spawn_info.get_string("items")); } if(spawn_info.has_array("item_groups")) { //Pick from a group of items, just like map::place_items JsonArray item_group_names = spawn_info.get_array("item_groups"); while(item_group_names.has_more()) { next_spawn.item_groups.push_back(item_group_names.next_string()); } } else if(spawn_info.has_string("item_groups")) { next_spawn.item_groups.push_back(spawn_info.get_string("item_groups")); } vproto.item_spawns.push_back( std::move( next_spawn ) ); } }
void input_manager::load(const std::string &file_name, bool is_user_preferences) { std::ifstream data_file(file_name.c_str(), std::ifstream::in | std::ifstream::binary); if(!data_file.good()) { // Only throw if this is the first file to load, that file _must_ exist, // otherwise the keybindings can not be read at all. if (action_contexts.empty()) { throw "Could not read " + file_name; } return; } JsonIn jsin(data_file); //Crawl through once and create an entry for every definition jsin.start_array(); while (!jsin.end_array()) { // JSON object representing the action JsonObject action = jsin.get_object(); const std::string action_id = action.get_string("id"); const std::string context = action.get_string("category", default_context_id); t_actions &actions = action_contexts[context]; if (!is_user_preferences && action.has_member("name")) { // Action names are not user preferences. Some experimental builds // post-0.A had written action names into the user preferences // config file. Any names that exist in user preferences will be // ignored. actions[action_id].name = action.get_string("name"); } // Iterate over the bindings JSON array JsonArray bindings = action.get_array("bindings"); t_input_event_list events; while (bindings.has_more()) { JsonObject keybinding = bindings.next_object(); std::string input_method = keybinding.get_string("input_method"); input_event new_event; if(input_method == "keyboard") { new_event.type = CATA_INPUT_KEYBOARD; } else if(input_method == "gamepad") { new_event.type = CATA_INPUT_GAMEPAD; } else if(input_method == "mouse") { new_event.type = CATA_INPUT_MOUSE; } if (keybinding.has_array("key")) { JsonArray keys = keybinding.get_array("key"); while (keys.has_more()) { new_event.sequence.push_back( get_keycode(keys.next_string()) ); } } else { // assume string if not array, and throw if not string new_event.sequence.push_back( get_keycode(keybinding.get_string("key")) ); } events.push_back(new_event); } // An invariant of this class is that user-created, local keybindings // with an empty set of input_events do not exist in the // action_contexts map. In prior versions of this class, this was not // true, so users of experimental builds post-0.A will have empty // local keybindings saved in their keybindings.json config. // // To be backwards compatible with keybindings.json from prior // experimental builds, we will detect user-created, local keybindings // with empty input_events and disregard them. When keybindings are // later saved, these remnants won't be saved. if (!is_user_preferences || !events.empty() || context == default_context_id || actions.count(action_id) > 0) { // In case this is the second file containing user preferences, // this replaces the default bindings with the user's preferences. action_attributes &attributes = actions[action_id]; attributes.input_events = events; if (action.has_member("is_user_created")) { attributes.is_user_created = action.get_bool("is_user_created"); } } } }
void mutation_branch::load( JsonObject &jsobj ) { const std::string id = jsobj.get_string( "id" ); mutation_branch &new_mut = mutation_data[id]; JsonArray jsarr; new_mut.name = _(jsobj.get_string("name").c_str()); new_mut.description = _(jsobj.get_string("description").c_str()); new_mut.points = jsobj.get_int("points"); new_mut.visibility = jsobj.get_int("visibility", 0); new_mut.ugliness = jsobj.get_int("ugliness", 0); new_mut.startingtrait = jsobj.get_bool("starting_trait", false); new_mut.mixed_effect = jsobj.get_bool("mixed_effect", false); new_mut.activated = jsobj.get_bool("active", false); new_mut.starts_active = jsobj.get_bool("starts_active", false); new_mut.destroys_gear = jsobj.get_bool("destroys_gear", false); new_mut.allow_soft_gear = jsobj.get_bool("allow_soft_gear", false); new_mut.cost = jsobj.get_int("cost", 0); new_mut.cooldown = jsobj.get_int("time",0); new_mut.hunger = jsobj.get_bool("hunger",false); new_mut.thirst = jsobj.get_bool("thirst",false); new_mut.fatigue = jsobj.get_bool("fatigue",false); new_mut.valid = jsobj.get_bool("valid", true); new_mut.purifiable = jsobj.get_bool("purifiable", true); for( auto & s : jsobj.get_string_array( "initial_ma_styles" ) ) { new_mut.initial_ma_styles.push_back( matype_id( s ) ); } JsonArray bodytemp_array = jsobj.get_array( "bodytemp_modifiers" ); if( bodytemp_array.has_more() ) { new_mut.bodytemp_min = bodytemp_array.get_int( 0 ); new_mut.bodytemp_max = bodytemp_array.get_int( 1 ); } new_mut.bodytemp_sleep = jsobj.get_int( "bodytemp_sleep", 0 ); new_mut.threshold = jsobj.get_bool("threshold", false); new_mut.profession = jsobj.get_bool("profession", false); auto vr = jsobj.get_array( "vitamin_rates" ); while( vr.has_more() ) { auto pair = vr.next_array(); new_mut.vitamin_rates[ vitamin_id( pair.get_string( 0 ) ) ] = pair.get_int( 1 ); } load_mutation_mods(jsobj, "passive_mods", new_mut.mods); /* Not currently supported due to inability to save active mutation state load_mutation_mods(jsobj, "active_mods", new_mut.mods); */ new_mut.prereqs = jsobj.get_string_array( "prereqs" ); // Helps to be able to have a trait require more than one other trait // (Individual prereq-lists are "OR", not "AND".) // Traits shoud NOT appear in both lists for a given mutation, unless // you want that trait to satisfy both requirements. // These are additional to the first list. new_mut.prereqs2 = jsobj.get_string_array( "prereqs2" ); // Dedicated-purpose prereq slot for Threshold mutations // Stuff like Huge might fit in more than one mutcat post-threshold, so yeah new_mut.threshreq = jsobj.get_string_array( "threshreq" ); new_mut.cancels = jsobj.get_string_array( "cancels" ); new_mut.replacements = jsobj.get_string_array( "changes_to" ); new_mut.additions = jsobj.get_string_array( "leads_to" ); new_mut.flags = jsobj.get_tags( "flags" ); jsarr = jsobj.get_array("category"); while (jsarr.has_more()) { std::string s = jsarr.next_string(); new_mut.category.push_back(s); mutations_category[s].push_back(id); } jsarr = jsobj.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); new_mut.protection[get_body_part_token( part_id )] = protect; } jsarr = jsobj.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(); new_mut.encumbrance_always[get_body_part_token( part_id )] = enc; } jsarr = jsobj.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(); new_mut.encumbrance_covered[get_body_part_token( part_id )] = enc; } jsarr = jsobj.get_array("restricts_gear"); while( jsarr.has_more() ) { new_mut.restricts_gear.insert( get_body_part_token( jsarr.next_string() ) ); } jsarr = jsobj.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 muts protect whole body for( size_t i = 0; i < num_bp; i++ ) { bps.insert( static_cast<body_part>( i ) ); } } else { bps.insert( get_body_part_token( part_string ) ); } } resistances res = load_resistances_instance( jo ); for( body_part bp : bps ) { new_mut.armor[ bp ] = res; } } }
void profession::load_profession(JsonObject &jsobj) { profession prof; JsonArray jsarr; prof._ident = jsobj.get_string("ident"); //If the "name" is an object then we have to deal with gender-specific titles, //otherwise we assume "name" is a string and use its value for prof._name if(jsobj.has_object("name")) { JsonObject name_obj=jsobj.get_object("name"); prof._name_male = _(name_obj.get_string("male").c_str()); prof._name_female = _(name_obj.get_string("female").c_str()); prof._name = ""; } else { // Json only has a gender neutral name, construct additional // gender specific names using a prefix. // extract_json_strings.py contains code that automatically adds // these constructed strings to the translation table. const std::string name = jsobj.get_string("name"); const std::string name_female = std::string("female ") + name; const std::string name_male = std::string("male ") + name; // Now attempt to translate them... prof._name = _(name.c_str()); prof._name_female = _(name_female.c_str()); prof._name_male = _(name_male.c_str()); // ... if it fails, translate the gender prefix and use it to // construct generic specific names: if (prof._name_female == name_female) { //~ player info: "female <gender unspecific profession>" prof._name_female = string_format(_("female %s"), prof._name.c_str()); } if (prof._name_male == name_male) { //~ player info: "male <gender unspecific profession>" prof._name_male = string_format(_("male %s"), prof._name.c_str()); } } prof._description = _(jsobj.get_string("description").c_str()); prof._point_cost = jsobj.get_int("points"); JsonObject items_obj=jsobj.get_object("items"); prof.add_items_from_jsonarray(items_obj.get_array("both"), "both"); prof.add_items_from_jsonarray(items_obj.get_array("male"), "male"); prof.add_items_from_jsonarray(items_obj.get_array("female"), "female"); jsarr = jsobj.get_array("skills"); while (jsarr.has_more()) { JsonObject jo = jsarr.next_object(); prof.add_skill(jo.get_string("name"), jo.get_int("level")); } jsarr = jsobj.get_array("addictions"); while (jsarr.has_more()) { JsonObject jo = jsarr.next_object(); prof.add_addiction(addiction_type(jo.get_string("type")), jo.get_int("intensity")); } jsarr = jsobj.get_array("CBMs"); while (jsarr.has_more()) { prof.add_CBM(jsarr.next_string()); } jsarr = jsobj.get_array("flags"); while (jsarr.has_more()) { prof.flags.insert(jsarr.next_string()); } _all_profs[prof._ident] = prof; //dout(D_INFO) << "Loaded profession: " << prof._name; }
void MonsterGenerator::load_monster(JsonObject &jo) { // id std::string mid; if (jo.has_member("id")) { mid = jo.get_string("id"); if (mon_templates.count(mid) > 0) { delete mon_templates[mid]; } mtype *newmon = new mtype; newmon->id = mid; newmon->name = jo.get_string("name").c_str(); if(jo.has_member("name_plural")) { newmon->name_plural = jo.get_string("name_plural"); } else { // default behaviour: Assume the regular plural form (appending an “s”) newmon->name_plural = newmon->name + "s"; } newmon->description = _(jo.get_string("description").c_str()); newmon->mat = jo.get_string("material"); newmon->species = jo.get_tags("species"); newmon->categories = jo.get_tags("categories"); newmon->sym = jo.get_string("symbol"); if( utf8_wrapper( newmon->sym ).display_width() != 1 ) { jo.throw_error( "monster symbol should be exactly one console cell width", "symbol" ); } newmon->color = color_from_string(jo.get_string("color")); newmon->size = get_from_string(jo.get_string("size", "MEDIUM"), size_map, MS_MEDIUM); newmon->phase = get_from_string(jo.get_string("phase", "SOLID"), phase_map, SOLID); newmon->difficulty = jo.get_int("diff", 0); newmon->agro = jo.get_int("aggression", 0); newmon->morale = jo.get_int("morale", 0); newmon->speed = jo.get_int("speed", 0); newmon->melee_skill = jo.get_int("melee_skill", 0); newmon->melee_dice = jo.get_int("melee_dice", 0); newmon->melee_sides = jo.get_int("melee_dice_sides", 0); newmon->melee_cut = jo.get_int("melee_cut", 0); newmon->sk_dodge = jo.get_int("dodge", 0); newmon->armor_bash = jo.get_int("armor_bash", 0); newmon->armor_cut = jo.get_int("armor_cut", 0); newmon->hp = jo.get_int("hp", 0); jo.read("starting_ammo", newmon->starting_ammo); newmon->luminance = jo.get_float("luminance", 0); newmon->revert_to_itype = jo.get_string( "revert_to_itype", "" ); 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(e.get_string("id", "null"), e.get_int("duration", 0), body_parts[e.get_string("bp", "NUM_BP")], e.get_bool("permanent", false), e.get_int("chance", 100)); newmon->atk_effs.push_back(new_eff); } } if (jo.has_string("death_drops")) { newmon->death_drops = jo.get_string("death_drops"); } else if (jo.has_object("death_drops")) { JsonObject death_frop_json = jo.get_object("death_drops"); // Make up a group name, should be unique (include the monster id), newmon->death_drops = newmon->id + "_death_drops_auto"; const std::string subtype = death_frop_json.get_string("subtype", "distribution"); // and load the entry as a standard item group using the made up name. item_group::load_item_group(death_frop_json, newmon->death_drops, subtype); } else if (jo.has_member("death_drops")) { jo.throw_error("invalid type, must be string or object", "death_drops"); } newmon->dies = get_death_functions(jo, "death_function"); load_special_defense(newmon, jo, "special_when_hit"); load_special_attacks(newmon, jo, "special_attacks"); std::set<std::string> flags, anger_trig, placate_trig, fear_trig; flags = jo.get_tags("flags"); anger_trig = jo.get_tags("anger_triggers"); placate_trig = jo.get_tags("placate_triggers"); fear_trig = jo.get_tags("fear_triggers"); newmon->flags = get_set_from_tags(flags, flag_map, MF_NULL); newmon->anger = get_set_from_tags(anger_trig, trigger_map, MTRIG_NULL); newmon->fear = get_set_from_tags(fear_trig, trigger_map, MTRIG_NULL); newmon->placate = get_set_from_tags(placate_trig, trigger_map, MTRIG_NULL); mon_templates[mid] = newmon; } }
void MonsterGenerator::load_monster(JsonObject &jo) { const mtype_id mid = mtype_id( jo.get_string("id") ); if (mon_templates.count(mid) > 0) { delete mon_templates[mid]; } mtype *newmon = new mtype; newmon->id = mid; newmon->name = jo.get_string("name").c_str(); if(jo.has_member("name_plural")) { newmon->name_plural = jo.get_string("name_plural"); } else { // default behaviour: Assume the regular plural form (appending an “s”) newmon->name_plural = newmon->name + "s"; } newmon->description = _(jo.get_string("description").c_str()); // Have to overwrite the default { "hflesh" } here newmon->mat = { jo.get_string("material") }; for( auto &s : jo.get_tags( "species" ) ) { newmon->species.insert( species_id( s ) ); } newmon->categories = jo.get_tags("categories"); // See monfaction.cpp newmon->default_faction = monfactions::get_or_add_faction( mfaction_str_id( jo.get_string("default_faction") ) ); newmon->sym = jo.get_string("symbol"); if( utf8_wrapper( newmon->sym ).display_width() != 1 ) { jo.throw_error( "monster symbol should be exactly one console cell width", "symbol" ); } newmon->color = color_from_string(jo.get_string("color")); newmon->size = get_from_string(jo.get_string("size", "MEDIUM"), Creature::size_map, MS_MEDIUM); newmon->phase = get_from_string(jo.get_string("phase", "SOLID"), phase_map, SOLID); newmon->difficulty = jo.get_int("diff", 0); newmon->agro = jo.get_int("aggression", 0); newmon->morale = jo.get_int("morale", 0); newmon->speed = jo.get_int("speed", 0); newmon->attack_cost = jo.get_int("attack_cost", 100); newmon->melee_skill = jo.get_int("melee_skill", 0); newmon->melee_dice = jo.get_int("melee_dice", 0); newmon->melee_sides = jo.get_int("melee_dice_sides", 0); newmon->melee_cut = jo.get_int("melee_cut", 0); newmon->sk_dodge = jo.get_int("dodge", 0); newmon->armor_bash = jo.get_int("armor_bash", 0); newmon->armor_cut = jo.get_int("armor_cut", 0); newmon->hp = jo.get_int("hp", 0); jo.read("starting_ammo", newmon->starting_ammo); newmon->luminance = jo.get_float("luminance", 0); newmon->revert_to_itype = jo.get_string( "revert_to_itype", "" ); newmon->vision_day = jo.get_int("vision_day", 40); newmon->vision_night = jo.get_int("vision_night", 1); 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(e.get_string("id", "null"), 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)); newmon->atk_effs.push_back(new_eff); } } if( jo.has_member( "death_drops" ) ) { JsonIn& stream = *jo.get_raw( "death_drops" ); newmon->death_drops = item_group::load_item_group( stream, "distribution" ); } newmon->dies = get_death_functions(jo, "death_function"); load_special_defense(newmon, jo, "special_when_hit"); load_special_attacks(newmon, jo, "special_attacks"); if (jo.has_member("upgrades")) { JsonObject upgrades = jo.get_object("upgrades"); newmon->half_life = upgrades.get_int("half_life", -1); newmon->upgrade_group = mongroup_id( upgrades.get_string("into_group", mongroup_id::NULL_ID.str() ) ); newmon->upgrade_into = mtype_id( upgrades.get_string("into", mtype_id::NULL_ID.str() ) ); newmon->upgrades = true; } std::set<std::string> flags, anger_trig, placate_trig, fear_trig; flags = jo.get_tags("flags"); anger_trig = jo.get_tags("anger_triggers"); placate_trig = jo.get_tags("placate_triggers"); fear_trig = jo.get_tags("fear_triggers"); newmon->flags = get_set_from_tags(flags, flag_map, MF_NULL); newmon->anger = get_set_from_tags(anger_trig, trigger_map, MTRIG_NULL); newmon->fear = get_set_from_tags(fear_trig, trigger_map, MTRIG_NULL); newmon->placate = get_set_from_tags(placate_trig, trigger_map, MTRIG_NULL); mon_templates[mid] = newmon; }
void load_martial_art(JsonObject &jo) { martialart ma; JsonArray jsarr; ma.id = jo.get_string("id"); ma.name = _(jo.get_string("name").c_str()); ma.description = _(jo.get_string("description").c_str()); jsarr = jo.get_array("static_buffs"); while (jsarr.has_more()) { JsonObject jsobj = jsarr.next_object(); ma.static_buffs.push_back(load_buff(jsobj)); } jsarr = jo.get_array("onmove_buffs"); while (jsarr.has_more()) { JsonObject jsobj = jsarr.next_object(); ma.onmove_buffs.push_back(load_buff(jsobj)); } jsarr = jo.get_array("onhit_buffs"); while (jsarr.has_more()) { JsonObject jsobj = jsarr.next_object(); ma.onhit_buffs.push_back(load_buff(jsobj)); } jsarr = jo.get_array("onattack_buffs"); while (jsarr.has_more()) { JsonObject jsobj = jsarr.next_object(); ma.onattack_buffs.push_back(load_buff(jsobj)); } jsarr = jo.get_array("ondodge_buffs"); while (jsarr.has_more()) { JsonObject jsobj = jsarr.next_object(); ma.ondodge_buffs.push_back(load_buff(jsobj)); } jsarr = jo.get_array("onblock_buffs"); while (jsarr.has_more()) { JsonObject jsobj = jsarr.next_object(); ma.onblock_buffs.push_back(load_buff(jsobj)); } jsarr = jo.get_array("ongethit_buffs"); while (jsarr.has_more()) { JsonObject jsobj = jsarr.next_object(); ma.onblock_buffs.push_back(load_buff(jsobj)); } ma.techniques = jo.get_tags("techniques"); ma.weapons = jo.get_tags("weapons"); ma.leg_block = jo.get_int("leg_block", -1); ma.arm_block = jo.get_int("arm_block", -1); ma.arm_block_with_bio_armor_arms = jo.get_bool("arm_block_with_bio_armor_arms", false); ma.leg_block_with_bio_armor_legs = jo.get_bool("leg_block_with_bio_armor_legs", false); martialarts[ma.id] = ma; }
bool map_bash_info::load(JsonObject &jsobj, std::string member, bool isfurniture) { if( jsobj.has_object(member) ) { JsonObject j = jsobj.get_object(member); if ( jsonint(j, "num_tests", num_tests ) == false ) { if ( jsonint(j, "str_min", str_min ) && jsonint(j, "str_max", str_max ) ) { num_tests = 1; } } else if ( num_tests > 0 ) { str_min = j.get_int("str_min"); str_max = j.get_int("str_max"); } jsonint(j, "str_min_blocked", str_min_blocked ); jsonint(j, "str_max_blocked", str_max_blocked ); jsonint(j, "str_min_roll", str_min_roll ); jsonint(j, "explosive", explosive ); jsonint(j, "chance", chance ); jsonstring(j, "sound", sound ); jsonstring(j, "sound_fail", sound_fail ); jsonstring(j, "furn_set", furn_set ); if ( jsonstring(j, "ter_set", ter_set ) == false && isfurniture == false ) { ter_set = "t_rubble"; debugmsg("terrain[\"%s\"].bash.ter_set is not set!",jsobj.get_string("id").c_str() ); } if ( j.has_array("items") ) { JsonArray ja = j.get_array("items"); if (ja.size() > 0) { int c=0; while ( ja.has_more() ) { if ( ja.has_object(c) ) { JsonObject jio = ja.next_object(); if ( jio.has_string("item") && jio.has_int("amount") ) { if ( jio.has_int("minamount") ) { map_bash_item_drop drop( jio.get_string("item"), jio.get_int("amount"), jio.get_int("minamount") ); jsonint(jio, "chance", drop.chance); items.push_back(drop); } else { map_bash_item_drop drop( jio.get_string("item"), jio.get_int("amount") ); jsonint(jio, "chance", drop.chance); items.push_back(drop); } } else { debugmsg("terrain[\"%s\"].bash.items[%d]: invalid entry",jsobj.get_string("id").c_str(),c); } } else { debugmsg("terrain[\"%s\"].bash.items[%d]: invalid entry",jsobj.get_string("id").c_str(),c); } c++; } } } //debugmsg("%d/%d %s %s/%s %d",str_min,str_max, ter_set.c_str(), sound.c_str(), sound_fail.c_str(), items.size() ); return true; } else { return false; } }
void MonsterGroupManager::LoadMonsterGroup( JsonObject &jo ) { float mon_upgrade_factor = get_option<float>( "MONSTER_UPGRADE_FACTOR" ); MonsterGroup g; g.name = mongroup_id( jo.get_string( "name" ) ); bool extending = false; //If already a group with that name, add to it instead of overwriting it if( monsterGroupMap.count( g.name ) != 0 && !jo.get_bool( "override", false ) ) { g = monsterGroupMap[g.name]; extending = true; } if( !extending || jo.has_string( "default" ) ) { //Not mandatory to specify default if extending existing group g.defaultMonster = mtype_id( jo.get_string( "default" ) ); } g.is_animal = jo.get_bool( "is_animal", false ); if( jo.has_array( "monsters" ) ) { JsonArray monarr = jo.get_array( "monsters" ); while( monarr.has_more() ) { JsonObject mon = monarr.next_object(); const mtype_id name = mtype_id( mon.get_string( "monster" ) ); int freq = mon.get_int( "freq" ); int cost = mon.get_int( "cost_multiplier" ); int pack_min = 1; int pack_max = 1; if( mon.has_member( "pack_size" ) ) { JsonArray packarr = mon.get_array( "pack_size" ); pack_min = packarr.next_int(); pack_max = packarr.next_int(); } static const time_duration tdfactor = 1_hours; time_duration starts = 0_turns; time_duration ends = 0_turns; if( mon.has_member( "starts" ) ) { starts = tdfactor * mon.get_int( "starts" ) * ( mon_upgrade_factor > 0 ? mon_upgrade_factor : 1 ); } if( mon.has_member( "ends" ) ) { ends = tdfactor * mon.get_int( "ends" ) * ( mon_upgrade_factor > 0 ? mon_upgrade_factor : 1 ); } MonsterGroupEntry new_mon_group = MonsterGroupEntry( name, freq, cost, pack_min, pack_max, starts, ends ); if( mon.has_member( "conditions" ) ) { JsonArray conditions_arr = mon.get_array( "conditions" ); while( conditions_arr.has_more() ) { new_mon_group.conditions.push_back( conditions_arr.next_string() ); } } g.monsters.push_back( new_mon_group ); } } g.replace_monster_group = jo.get_bool( "replace_monster_group", false ); g.new_monster_group = mongroup_id( jo.get_string( "new_monster_group_id", mongroup_id::NULL_ID().str() ) ); assign( jo, "replacement_time", g.monster_group_time, false, 1_days ); g.is_safe = jo.get_bool( "is_safe", false ); g.freq_total = jo.get_int( "freq_total", ( extending ? g.freq_total : 1000 ) ); if( jo.get_bool( "auto_total", false ) ) { //Fit the max size to the sum of all freqs int total = 0; for( MonsterGroupEntry &mon : g.monsters ) { total += mon.frequency; } g.freq_total = total; } monsterGroupMap[g.name] = g; }
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; } } } } }