/** *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_str_id( part.get_string( "part" ) ); assign( part, "ammo", pt.with_ammo, true, 0, 100 ); 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 ) ); } }
/** * Reads in a vehicle part from a JsonObject. */ void vpart_info::load( JsonObject &jo ) { vpart_info next_part; next_part.id = vpart_str_id( jo.get_string( "id" ) ); next_part.name = _(jo.get_string("name").c_str()); next_part.sym = jo.get_string("symbol")[0]; next_part.color = color_from_string(jo.get_string("color")); next_part.sym_broken = jo.get_string("broken_symbol")[0]; next_part.color_broken = color_from_string(jo.get_string("broken_color")); next_part.dmg_mod = jo.has_member("damage_modifier") ? jo.get_int("damage_modifier") : 100; next_part.durability = jo.get_int("durability"); next_part.power = jo.get_int("power", 0); next_part.epower = jo.get_int("epower", 0); next_part.folded_volume = jo.get_int("folded_volume", 0); next_part.range = jo.get_int( "range", 12 ); //Handle the par1 union as best we can by accepting any ONE of its elements int element_count = (jo.has_member("par1") ? 1 : 0) + (jo.has_member("size") ? 1 : 0) + (jo.has_member("wheel_width") ? 1 : 0) + (jo.has_member("bonus") ? 1 : 0); if(element_count == 0) { //If not specified, assume 0 next_part.par1 = 0; } else if(element_count == 1) { if(jo.has_member("par1")) { next_part.par1 = jo.get_int("par1"); } else if(jo.has_member("size")) { next_part.par1 = jo.get_int("size"); } else if(jo.has_member("wheel_width")) { next_part.par1 = jo.get_int("wheel_width"); } else { //bonus next_part.par1 = jo.get_int("bonus"); } } else { //Too many debugmsg("Error parsing vehicle part '%s': \ Use AT MOST one of: par1, power, size, wheel_width, bonus", next_part.name.c_str()); //Keep going to produce more messages if other parts are wrong next_part.par1 = 0; } next_part.fuel_type = jo.get_string( "fuel_type", "null" ); next_part.item = jo.get_string("item"); next_part.difficulty = jo.get_int("difficulty"); next_part.location = jo.has_member("location") ? jo.get_string("location") : ""; JsonArray jarr = jo.get_array("flags"); while (jarr.has_more()) { next_part.set_flag( jarr.next_string() ); } JsonArray breaks_into = jo.get_array("breaks_into"); while(breaks_into.has_more()) { JsonObject next_entry = breaks_into.next_object(); break_entry next_break_entry; next_break_entry.item_id = next_entry.get_string("item"); next_break_entry.min = next_entry.get_int("min"); next_break_entry.max = next_entry.get_int("max"); //Sanity check if(next_break_entry.max < next_break_entry.min) { debugmsg("For vehicle part %s: breaks_into item '%s' has min (%d) > max (%d)!", next_part.name.c_str(), next_break_entry.item_id.c_str(), next_break_entry.min, next_break_entry.max); } next_part.breaks_into.push_back(next_break_entry); } //Calculate and cache z-ordering based off of location // list_order is used when inspecting the vehicle if(next_part.location == "on_roof") { next_part.z_order = 9; next_part.list_order = 3; } else if(next_part.location == "on_cargo") { next_part.z_order = 8; next_part.list_order = 6; } else if(next_part.location == "center") { next_part.z_order = 7; next_part.list_order = 7; } else if(next_part.location == "under") { //Have wheels show up over frames next_part.z_order = 6; next_part.list_order = 10; } else if(next_part.location == "structure") { next_part.z_order = 5; next_part.list_order = 1; } else if(next_part.location == "engine_block") { //Should be hidden by frames next_part.z_order = 4; next_part.list_order = 8 ; } else if (next_part.location == "on_battery_mount"){ //Should be hidden by frames next_part.z_order = 3; next_part.list_order = 10; } else if(next_part.location == "fuel_source") { //Should be hidden by frames next_part.z_order = 3; next_part.list_order = 9; } else if(next_part.location == "roof") { //Shouldn't be displayed next_part.z_order = -1; next_part.list_order = 4; } else if(next_part.location == "armor") { //Shouldn't be displayed (the color is used, but not the symbol) next_part.z_order = -2; next_part.list_order = 2; } else { //Everything else next_part.z_order = 0; next_part.list_order = 5; } auto const iter = vehicle_part_types.find( next_part.id ); if( iter != vehicle_part_types.end() ) { // Entry in the map already exists, so the pointer in the vector is already correct // and does not need to be changed, only the int-id needs to be taken from the old entry. next_part.loadid = iter->second.loadid; iter->second = next_part; } else { // The entry is new, "generate" a new int-id and link the new entry from the vector. next_part.loadid = vpart_id( vehicle_part_int_types.size() ); vpart_info &new_entry = vehicle_part_types[next_part.id]; new_entry = next_part; vehicle_part_int_types.push_back( &new_entry ); } }
/** * Reads in a vehicle part from a JsonObject. */ void vpart_info::load( JsonObject &jo, const std::string &src ) { vpart_info def; if( jo.has_string( "copy-from" ) ) { auto const base = vehicle_part_types.find( vpart_str_id( jo.get_string( "copy-from" ) ) ); auto const ab = abstract_parts.find( vpart_str_id( jo.get_string( "copy-from" ) ) ); if( base != vehicle_part_types.end() ) { def = base->second; } else if( ab != abstract_parts.end() ) { def = ab->second; } else { deferred.emplace_back( jo.str(), src ); } } if( jo.has_string( "abstract" ) ) { def.id = vpart_str_id( jo.get_string( "abstract" ) ); } else { def.id = vpart_str_id( jo.get_string( "id" ) ); } assign( jo, "name", def.name_ ); assign( jo, "item", def.item ); assign( jo, "location", def.location ); assign( jo, "durability", def.durability ); assign( jo, "damage_modifier", def.dmg_mod ); assign( jo, "power", def.power ); assign( jo, "epower", def.epower ); assign( jo, "fuel_type", def.fuel_type ); assign( jo, "folded_volume", def.folded_volume ); assign( jo, "size", def.size ); assign( jo, "difficulty", def.difficulty ); assign( jo, "bonus", def.bonus ); assign( jo, "flags", def.flags ); auto reqs = jo.get_object( "requirements" ); if( reqs.has_object( "install" ) ) { auto ins = reqs.get_object( "install" ); auto sk = ins.get_array( "skills" ); if( !sk.empty() ) { def.install_skills.clear(); } while( sk.has_more() ) { auto cur = sk.next_array(); def.install_skills.emplace( skill_id( cur.get_string( 0 ) ) , cur.size() >= 2 ? cur.get_int( 1 ) : 1 ); } assign( ins, "time", def.install_moves ); if( ins.has_string( "using" ) ) { def.install_reqs = { { requirement_id( ins.get_string( "using" ) ), 1 } }; } else if( ins.has_array( "using" ) ) { auto arr = ins.get_array( "using" ); while( arr.has_more() ) { auto cur = arr.next_array(); def.install_reqs.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) ); } } else { auto req_id = std::string( "inline_vehins_" ) += def.id.str(); requirement_data::load_requirement( ins, req_id ); def.install_reqs = { { requirement_id( req_id ), 1 } }; } def.legacy = false; } if( reqs.has_object( "removal" ) ) { auto rem = reqs.get_object( "removal" ); auto sk = rem.get_array( "skills" ); if( !sk.empty() ) { def.removal_skills.clear(); } while( sk.has_more() ) { auto cur = sk.next_array(); def.removal_skills.emplace( skill_id( cur.get_string( 0 ) ) , cur.size() >= 2 ? cur.get_int( 1 ) : 1 ); } assign( rem, "time", def.removal_moves ); if( rem.has_string( "using" ) ) { def.removal_reqs = { { requirement_id( rem.get_string( "using" ) ), 1 } }; } else if( rem.has_array( "using" ) ) { auto arr = rem.get_array( "using" ); while( arr.has_more() ) { auto cur = arr.next_array(); def.removal_reqs.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) ); } } else { auto req_id = std::string( "inline_vehins_" ) += def.id.str(); requirement_data::load_requirement( rem, req_id ); def.removal_reqs = { { requirement_id( req_id ), 1 } }; } def.legacy = false; } if( jo.has_member( "symbol" ) ) { def.sym = jo.get_string( "symbol" )[ 0 ]; } if( jo.has_member( "broken_symbol" ) ) { def.sym_broken = jo.get_string( "broken_symbol" )[ 0 ]; } if( jo.has_member( "color" ) ) { def.color = color_from_string( jo.get_string( "color" ) ); } if( jo.has_member( "broken_color" ) ) { def.color_broken = color_from_string( jo.get_string( "broken_color" ) ); } if( jo.has_member( "breaks_into" ) ) { JsonIn& stream = *jo.get_raw( "breaks_into" ); def.breaks_into_group = item_group::load_item_group( stream, "collection" ); } auto qual = jo.get_array( "qualities" ); if( !qual.empty() ) { def.qualities.clear(); while( qual.has_more() ) { auto pair = qual.next_array(); def.qualities[ quality_id( pair.get_string( 0 ) ) ] = pair.get_int( 1 ); } } if( jo.has_member( "damage_reduction" ) ) { JsonObject dred = jo.get_object( "damage_reduction" ); def.damage_reduction = load_damage_array( dred ); } else { def.damage_reduction.fill( 0.0f ); } if( jo.has_string( "abstract" ) ) { abstract_parts[ def.id ] = def; return; } auto const iter = vehicle_part_types.find( def.id ); if( iter != vehicle_part_types.end() ) { // Entry in the map already exists, so the pointer in the vector is already correct // and does not need to be changed, only the int-id needs to be taken from the old entry. def.loadid = iter->second.loadid; iter->second = def; } else { // The entry is new, "generate" a new int-id and link the new entry from the vector. def.loadid = vpart_id( vehicle_part_int_types.size() ); vpart_info &new_entry = vehicle_part_types[ def.id ]; new_entry = def; vehicle_part_int_types.push_back( &new_entry ); } }