void recipe_dictionary::finalize() { DynamicDataLoader::get_instance().load_deferred( deferred ); // remove abstract recipes delete_if( []( const recipe & element ) { return element.abstract; } ); finalize_internal( recipe_dict.recipes ); finalize_internal( recipe_dict.uncraft ); for( auto &e : recipe_dict.recipes ) { auto &r = e.second; for( const auto &bk : r.booksets ) { const itype *booktype = item::find_type( bk.first ); int req = bk.second > 0 ? bk.second : std::max( booktype->book->req, r.difficulty ); islot_book::recipe_with_description_t desc{ &r, req, r.result_name(), false }; const_cast<islot_book &>( *booktype->book ).recipes.insert( desc ); } // if reversible and no specific uncraft recipe exists use this recipe if( r.reversible && !recipe_dict.uncraft.count( recipe_id( r.result() ) ) ) { recipe_dict.uncraft[ recipe_id( r.result() ) ] = r; } } // add pseudo uncrafting recipes for( const itype *e : item_controller->all() ) { const itype_id id = e->get_id(); const recipe_id rid = recipe_id( id ); // books that don't already have an uncrafting recipe if( e->book && !recipe_dict.uncraft.count( rid ) && e->volume > 0 ) { int pages = e->volume / units::from_milliliter( 12.5 ); auto &bk = recipe_dict.uncraft[rid]; bk.ident_ = rid; bk.result_ = id; bk.reversible = true; bk.requirements_ = *requirement_id( "uncraft_book" ) * pages; bk.time = pages * 10; // @todo: allow specifying time in requirement_data } } // Cache auto-learn recipes for( const auto &e : recipe_dict.recipes ) { if( e.second.autolearn ) { recipe_dict.autolearn.insert( &e.second ); } } }
/// Returns the description for the recipe of the next building @ref bldg std::string basecamp::om_upgrade_description( const std::string &bldg, bool trunc ) { const recipe &making = recipe_id( bldg ).obj(); const inventory &total_inv = g->u.crafting_inventory(); std::vector<std::string> component_print_buffer; const int pane = FULL_SCREEN_WIDTH; const auto tools = making.requirements().get_folded_tools_list( pane, c_white, total_inv, 1 ); const auto comps = making.requirements().get_folded_components_list( pane, c_white, total_inv, 1 ); component_print_buffer.insert( component_print_buffer.end(), tools.begin(), tools.end() ); component_print_buffer.insert( component_print_buffer.end(), comps.begin(), comps.end() ); std::string comp; for( auto &elem : component_print_buffer ) { comp = comp + elem + "\n"; } time_duration duration = time_duration::from_turns( making.time / 100 ); if( trunc ) { comp = string_format( _( "Notes:\n%s\n\nSkill used: %s\n%s\n" ), making.description, making.skill_used.obj().name(), comp ); } else { comp = string_format( _( "Notes:\n%s\n\nSkill used: %s\n" "Difficulty: %d\n%s \nRisk: None\nTime: %s\n" ), making.description, making.skill_used.obj().name(), making.difficulty, comp, to_string( duration ) ); } return comp; }
recipe &recipe_dictionary::load( JsonObject &jo, const std::string &src, std::map<recipe_id, recipe> &dest ) { recipe r; // defer entries dependent upon as-yet unparsed definitions if( jo.has_string( "copy-from" ) ) { auto base = recipe_id( jo.get_string( "copy-from" ) ); if( !dest.count( base ) ) { deferred.emplace_back( jo.str(), src ); return null_recipe; } r = dest[ base ]; } r.load( jo, src ); dest[ r.ident() ] = std::move( r ); return dest[ r.ident() ]; }
#include "itype.h" #include "map.h" #include "map_helpers.h" #include "npc.h" #include "output.h" #include "player.h" #include "player_helpers.h" #include "recipe_dictionary.h" TEST_CASE( "recipe_subset" ) { recipe_subset subset; REQUIRE( subset.size() == 0 ); GIVEN( "a recipe of rum" ) { const recipe *r = &recipe_id( "brew_rum" ).obj(); WHEN( "the recipe is included" ) { subset.include( r ); THEN( "it's in the subset" ) { CHECK( subset.size() == 1 ); CHECK( subset.contains( r ) ); } THEN( "it has its default difficulty" ) { CHECK( subset.get_custom_difficulty( r ) == r->difficulty ); } THEN( "it's in the right category" ) { const auto cat_recipes( subset.in_category( "CC_FOOD" ) ); CHECK( cat_recipes.size() == 1 );
void recipe::load( JsonObject &jo, const std::string &src ) { bool strict = src == "dda"; abstract = jo.has_string( "abstract" ); if( abstract ) { ident_ = recipe_id( jo.get_string( "abstract" ) ); } else { result_ = jo.get_string( "result" ); ident_ = recipe_id( result_ ); } assign( jo, "time", time, strict, 0 ); assign( jo, "difficulty", difficulty, strict, 0, MAX_SKILL ); assign( jo, "flags", flags ); // automatically set contained if we specify as container assign( jo, "contained", contained, strict ); contained |= assign( jo, "container", container, strict ); if( jo.has_array( "batch_time_factors" ) ) { auto batch = jo.get_array( "batch_time_factors" ); batch_rscale = batch.get_int( 0 ) / 100.0; batch_rsize = batch.get_int( 1 ); } assign( jo, "charges", charges ); assign( jo, "result_mult", result_mult ); assign( jo, "skill_used", skill_used, strict ); if( jo.has_member( "skills_required" ) ) { auto sk = jo.get_array( "skills_required" ); required_skills.clear(); if( sk.empty() ) { // clear all requirements } else if( sk.has_array( 0 ) ) { // multiple requirements while( sk.has_more() ) { auto arr = sk.next_array(); required_skills[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 ); } } else { // single requirement required_skills[skill_id( sk.get_string( 0 ) )] = sk.get_int( 1 ); } } // simplified autolearn sets requirements equal to required skills at finalization if( jo.has_bool( "autolearn" ) ) { assign( jo, "autolearn", autolearn ); } else if( jo.has_array( "autolearn" ) ) { autolearn = true; auto sk = jo.get_array( "autolearn" ); while( sk.has_more() ) { auto arr = sk.next_array(); autolearn_requirements[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 ); } } if( jo.has_member( "decomp_learn" ) ) { learn_by_disassembly.clear(); if( jo.has_int( "decomp_learn" ) ) { if( !skill_used ) { jo.throw_error( "decomp_learn specified with no skill_used" ); } assign( jo, "decomp_learn", learn_by_disassembly[skill_used] ); } else if( jo.has_array( "decomp_learn" ) ) { auto sk = jo.get_array( "decomp_learn" ); while( sk.has_more() ) { auto arr = sk.next_array(); learn_by_disassembly[skill_id( arr.get_string( 0 ) )] = arr.get_int( 1 ); } } } if( jo.has_member( "book_learn" ) ) { auto bk = jo.get_array( "book_learn" ); booksets.clear(); while( bk.has_more() ) { auto arr = bk.next_array(); booksets.emplace( arr.get_string( 0 ), arr.size() > 1 ? arr.get_int( 1 ) : -1 ); } } // recipes not specifying any external requirements inherit from their parent recipe (if any) if( jo.has_string( "using" ) ) { reqs_external = { { requirement_id( jo.get_string( "using" ) ), 1 } }; } else if( jo.has_array( "using" ) ) { auto arr = jo.get_array( "using" ); reqs_external.clear(); while( arr.has_more() ) { auto cur = arr.next_array(); reqs_external.emplace_back( requirement_id( cur.get_string( 0 ) ), cur.get_int( 1 ) ); } } const std::string type = jo.get_string( "type" ); if( type == "recipe" ) { if( jo.has_string( "id_suffix" ) ) { if( abstract ) { jo.throw_error( "abstract recipe cannot specify id_suffix", "id_suffix" ); } ident_ = recipe_id( ident_.str() + "_" + jo.get_string( "id_suffix" ) ); } assign( jo, "category", category, strict ); assign( jo, "subcategory", subcategory, strict ); assign( jo, "reversible", reversible, strict ); if( jo.has_member( "byproducts" ) ) { auto bp = jo.get_array( "byproducts" ); byproducts.clear(); while( bp.has_more() ) { auto arr = bp.next_array(); byproducts[ arr.get_string( 0 ) ] += arr.size() == 2 ? arr.get_int( 1 ) : 1; } } } else if( type == "uncraft" ) { reversible = true; } else { jo.throw_error( "unknown recipe type", "type" ); } // inline requirements are always replaced (cannot be inherited) const auto req_id = string_format( "inline_%s_%s", type.c_str(), ident_.c_str() ); requirement_data::load_requirement( jo, req_id ); reqs_internal = { { requirement_id( req_id ), 1 } }; }
const recipe &recipe_dictionary::get_uncraft( const itype_id &id ) { auto iter = recipe_dict.uncraft.find( recipe_id( id ) ); return iter != recipe_dict.uncraft.end() ? iter->second : null_recipe; }