void quality_requirement::load( JsonArray &jsarr ) { JsonObject quality_data = jsarr.next_object(); type = quality_id( quality_data.get_string( "id" ) ); level = quality_data.get_int( "level", 1 ); count = quality_data.get_int( "amount", 1 ); if( count <= 0 ) { quality_data.throw_error( "quality amount must be a positive number", "amount" ); } // Note: level is not checked, negative values and 0 are allow, see butchering quality. }
int visitable<Character>::amount_of( const std::string& what, bool pseudo, int limit ) const { auto self = static_cast<const Character *>( this ); if( what == "toolset" && pseudo && self->has_active_bionic( "bio_tools" ) ) { return 1; } if( what == "apparatus" && pseudo ) { int qty = 0; visit_items( [&qty, &limit] ( const item *e ) { qty += e->get_quality( quality_id( "SMOKE_PIPE" ) ) >= 1; return qty < limit ? VisitResponse::SKIP : VisitResponse::ABORT; } ); return std::min( qty, limit ); } return amount_of_internal( *this, what, pseudo, limit ); }
/** * 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 = vpart_info_all.find( vpart_id( jo.get_string( "copy-from" ) ) ); auto const ab = abstract_parts.find( vpart_id( jo.get_string( "copy-from" ) ) ); if( base != vpart_info_all.end() ) { def = base->second; } else if( ab != abstract_parts.end() ) { def = ab->second; } else { deferred.emplace_back( jo.str(), src ); return; } } if( jo.has_string( "abstract" ) ) { def.id = vpart_id( jo.get_string( "abstract" ) ); } else { def.id = vpart_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, "default_ammo", def.default_ammo ); 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 ); if( jo.has_member( "requirements" ) ) { auto reqs = jo.get_object( "requirements" ); parse_vp_reqs( reqs, def.id.str(), "install", def.install_reqs, def.install_skills, def.install_moves ); parse_vp_reqs( reqs, def.id.str(), "removal", def.removal_reqs, def.removal_skills, def.removal_moves ); parse_vp_reqs( reqs, def.id.str(), "repair", def.repair_reqs, def.repair_skills, def.repair_moves ); 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; } else { vpart_info_all[def.id] = def; } }
requirement_data requirement_data::disassembly_requirements() const { // TODO: // Allow jsonizing those tool replacements // Make a copy // Maybe TODO: Cache it somewhere and return a reference instead requirement_data ret = *this; auto new_qualities = std::vector<quality_requirement>(); for( auto &it : ret.tools ) { bool replaced = false; for( const auto &tool : it ) { const itype_id &type = tool.type; // If crafting required a welder or forge then disassembly requires metal sawing if( type == "welder" || type == "welder_crude" || type == "oxy_torch" || type == "forge" || type == "char_forge" ) { new_qualities.emplace_back( quality_id( "SAW_M_FINE" ), 1, 1 ); replaced = true; break; } if( type == "sewing_kit" || type == "mold_plastic" ) { new_qualities.emplace_back( quality_id( "CUT" ), 1, 1 ); replaced = true; break; } if( type == "crucible" ) { replaced = true; break; } } if( replaced ) { // Replace the entire block of variants // This avoids the pesky integrated toolset it.clear(); } } // Warning: This depends on the fact that tool qualities // are all mandatory (don't use variants) // If that ever changes, this will be wrong! if( ret.qualities.empty() ) { ret.qualities.resize( 1 ); } auto &qualities = ret.qualities[0]; qualities.insert( qualities.end(), new_qualities.begin(), new_qualities.end() ); // Remove duplicate qualities { auto itr = std::unique( qualities.begin(), qualities.end(), []( const quality_requirement & a, const quality_requirement & b ) { return a.type == b.type; } ); qualities.resize( std::distance( qualities.begin(), itr ) ); } // Remove empty variant sections ret.tools.erase( std::remove_if( ret.tools.begin(), ret.tools.end(), []( const std::vector<tool_comp> &tcv ) { return tcv.empty(); } ), ret.tools.end() ); // Remove unrecoverable components ret.components.erase( std::remove_if( ret.components.begin(), ret.components.end(), []( std::vector<item_comp> &cov ) { cov.erase( std::remove_if( cov.begin(), cov.end(), []( const item_comp &comp ) { return !comp.recoverable || item( comp.type ).has_flag( "UNRECOVERABLE" ); } ), cov.end() ); return cov.empty(); } ), ret.components.end() ); return ret; }
/** * 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 ); } }
requirement_data requirement_data::disassembly_requirements() const { // TODO: // Allow jsonizing those tool replacements // Make a copy // Maybe TODO: Cache it somewhere and return a reference instead requirement_data ret = *this; auto new_qualities = std::vector<quality_requirement>(); for( auto &it : ret.tools ) { bool replaced = false; for( const auto &tool : it ) { const itype_id &type = tool.type; // If crafting required a welder or forge then disassembly requires metal sawing if( type == "welder" || type == "welder_crude" || type == "oxy_torch" || type == "forge" || type == "char_forge" ) { new_qualities.emplace_back( quality_id( "SAW_M_FINE" ), 1, 1 ); replaced = true; break; } //This only catches instances where the two tools are explicitly stated, and not just the required sewing quality if( type == "sewing_kit" || type == "mold_plastic" ) { new_qualities.emplace_back( quality_id( "CUT" ), 1, 1 ); replaced = true; break; } if( type == "crucible" ) { replaced = true; break; } } if( replaced ) { // Replace the entire block of variants // This avoids the pesky integrated toolset it.clear(); } } // Warning: This depends on the fact that tool qualities // are all mandatory (don't use variants) // If that ever changes, this will be wrong! if( ret.qualities.empty() ) { ret.qualities.resize( 1 ); } else { //If the required quality level is not empty, iterate through and replace or remove //qualities with deconstruction equivalents for( auto &it : ret.qualities ) { bool replaced = false; for( const auto &quality : it ) { if( quality.type == quality_id( "SEW" ) ) { replaced = true; new_qualities.emplace_back( quality_id( "CUT" ), 1, quality.level ); break; } if( quality.type == quality_id( "GLARE" ) ) { replaced = true; //Just remove the glare protection requirement from deconstruction //This only happens in case of a reversible recipe, an explicit //deconstruction recipe can still specify glare protection break; } if( quality.type == quality_id( "KNIT" ) ) { replaced = true; //Ditto for knitting needles break; } } if( replaced ) { it.clear(); } } } auto &qualities = ret.qualities[0]; qualities.insert( qualities.end(), new_qualities.begin(), new_qualities.end() ); // Remove duplicate qualities { auto itr = std::unique( qualities.begin(), qualities.end(), []( const quality_requirement & a, const quality_requirement & b ) { return a.type == b.type; } ); qualities.resize( std::distance( qualities.begin(), itr ) ); } // Remove empty variant sections ret.tools.erase( std::remove_if( ret.tools.begin(), ret.tools.end(), []( const std::vector<tool_comp> &tcv ) { return tcv.empty(); } ), ret.tools.end() ); // Remove unrecoverable components ret.components.erase( std::remove_if( ret.components.begin(), ret.components.end(), []( std::vector<item_comp> &cov ) { cov.erase( std::remove_if( cov.begin(), cov.end(), []( const item_comp & comp ) { return !comp.recoverable || item( comp.type ).has_flag( "UNRECOVERABLE" ); } ), cov.end() ); return cov.empty(); } ), ret.components.end() ); return ret; }