std::vector<comp_selection<item_comp>> craft_command::check_item_components_missing( const inventory &map_inv ) const { std::vector<comp_selection<item_comp>> missing; for( const auto &item_sel : item_selections ) { itype_id type = item_sel.comp.type; item_comp component = item_sel.comp; long count = ( component.count > 0 ) ? component.count * batch_size : abs( component.count ); if( item::count_by_charges( type ) && count > 0 ) { switch( item_sel.use_from ) { case use_from_player: if( !crafter->has_charges( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_map: if( !map_inv.has_charges( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_both: if( !( crafter->charges_of( type ) + map_inv.charges_of( type ) >= count ) ) { missing.push_back( item_sel ); } break; case use_from_none: case cancel: break; } } else { // Counting by units, not charges. switch( item_sel.use_from ) { case use_from_player: if( !crafter->has_amount( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_map: if( !map_inv.has_components( type, count ) ) { missing.push_back( item_sel ); } break; case use_from_both: if( !( crafter->amount_of( type ) + map_inv.amount_of( type ) >= count ) ) { missing.push_back( item_sel ); } break; case use_from_none: case cancel: break; } } } return missing; }
static bool player_can_build(player &p, inventory pinv, construction *con) { if (p.skillLevel(con->skill) < con->difficulty) { return false; } bool has_tool = false; bool has_component = false; bool tools_required = false; bool components_required = false; for (int j = 0; j < con->tools.size(); j++) { if (con->tools[j].size() > 0) { tools_required = true; has_tool = false; for (unsigned k = 0; k < con->tools[j].size(); k++) { if (pinv.has_tools(con->tools[j][k].type, 1)) { has_tool = true; con->tools[j][k].available = 1; } else { con->tools[j][k].available = -1; } } if (!has_tool) { // missing one of the tools for this stage break; } } } for (int j = 0; j < con->components.size(); ++j) { if (con->components[j].size() > 0) { components_required = true; has_component = false; for (unsigned k = 0; k < con->components[j].size(); k++) { if (( item_controller->find_template(con->components[j][k].type)->is_ammo() && pinv.has_charges(con->components[j][k].type, con->components[j][k].count) ) || (!item_controller->find_template(con->components[j][k].type)->is_ammo() && pinv.has_components (con->components[j][k].type, con->components[j][k].count) )) { has_component = true; con->components[j][k].available = 1; } else { con->components[j][k].available = -1; } } if (!has_component) { // missing one of the comps for this stage break; } } } return (has_component || !components_required) && (has_tool || !tools_required); }
bool item_comp::has( const inventory &crafting_inv, int batch ) const { if( g->u.has_trait( "DEBUG_HS" ) ) { return true; } const int cnt = std::abs( count ) * batch; if( item::count_by_charges( type ) ) { return crafting_inv.has_charges( type, cnt ); } else { return crafting_inv.has_components( type, cnt ); } }
std::string item_comp::get_color( bool has_one, const inventory &crafting_inv, int batch ) const { const int cnt = std::abs( count ) * batch; if( available == a_insufficent ) { return "brown"; } else if( item::count_by_charges( type ) ) { if( crafting_inv.has_charges( type, cnt ) ) { return "green"; } } else if( crafting_inv.has_components( type, cnt ) ) { return "green"; } return has_one ? "dkgray" : "red"; }
bool requirements::check_enough_materials( const item_comp& comp, const inventory& crafting_inv ) const { if( comp.available != a_true ) { return false; } const itype *it = item_controller->find_template( comp.type ); const tool_comp *tq = find_by_type( tools, comp.type ); if( tq != nullptr ) { // The very same item type is also needed as tool! // Use charges of it, or use it by count? const int tc = tq->count < 0 ? std::abs( tq->count ) : 1; // Check for components + tool count. Check item amount (excludes // pseudo items) and tool amount (includes pseudo items) // Imagine: required = 1 welder (component) + 1 welder (tool), // available = 1 welder (real item), 1 welding rig (creates // a pseudo welder item). has_components(welder,2) returns false // as there is only one real welder available, but has_tools(welder,2) // returns true. // Keep in mind that both requirements (tool+component) are checked // before this. That assures that one real item is actually available, // two welding rigs (and no real welder) would make this component // non-available even before this function is called. // Only ammo and (some) food is counted by charges, both are unlikely // to appear as tool, but it's possible /-: bool has_comps; if( it->count_by_charges() && comp.count > 0 ) { has_comps = crafting_inv.has_charges( comp.type, comp.count + tc ); } else { has_comps = crafting_inv.has_components( comp.type, abs( comp.count ) + tc ); } if( !has_comps && !crafting_inv.has_tools( comp.type, comp.count + tc ) ) { comp.available = a_insufficent; } } for( const auto &ql : it->qualities ) { const quality_requirement *qr = find_by_type( qualities, ql.first ); if( qr == nullptr || qr->level > ql.second ) { continue; } // This item can be used for the quality requirement, same as above for specific // tools applies. if( !crafting_inv.has_items_with_quality( qr->type, qr->level, qr->count + abs(comp.count) ) ) { comp.available = a_insufficent; } } return comp.available == a_true; }
std::string item_comp::get_color( bool has_one, const inventory &crafting_inv, int batch ) const { if( type == "rope_30" || type == "rope_6" ) { if( g->u.has_trait( "WEB_ROPE" ) && g->u.get_hunger() <= 300 ) { return "ltgreen"; // Show that WEB_ROPE is on the job! } } const int cnt = std::abs( count ) * batch; if( available == a_insufficent ) { return "brown"; } else if( item::count_by_charges( type ) ) { if( crafting_inv.has_charges( type, cnt ) ) { return "green"; } } else if( crafting_inv.has_components( type, cnt ) ) { return "green"; } return has_one ? "dkgray" : "red"; }
bool item_comp::has( const inventory &crafting_inv, int batch ) const { // If you've Rope Webs, you can spin up the webbing to replace any amount of // rope your projects may require. But you need to be somewhat nourished: // Famished or worse stops it. if( type == "rope_30" || type == "rope_6" ) { // NPC don't craft? // TODO: what about the amount of ropes vs the hunger? if( g->u.has_trait( "WEB_ROPE" ) && g->u.get_hunger() <= 300 ) { return true; } } const int cnt = std::abs( count ) * batch; if( item::count_by_charges( type ) ) { return crafting_inv.has_charges( type, cnt ); } else { return crafting_inv.has_components( type, cnt ); } }
std::string item_comp::get_color( bool has_one, const inventory &crafting_inv ) const { if( type == "rope_30" || type == "rope_6" ) { if( g->u.has_trait( "WEB_ROPE" ) && g->u.hunger <= 300 ) { return "ltgreen"; // Show that WEB_ROPE is on the job! } } const itype *it = item_controller->find_template( type ); if( available == a_insufficent ) { return "brown"; } else if( it->count_by_charges() && count > 0 ) { if( crafting_inv.has_charges( type, count ) ) { return "green"; } } else if( crafting_inv.has_components( type, abs( count ) ) ) { return "green"; } return has_one ? "dkgray" : "red"; }
bool item_comp::has( const inventory &crafting_inv ) const { // If you've Rope Webs, you can spin up the webbing to replace any amount of // rope your projects may require. But you need to be somewhat nourished: // Famished or worse stops it. if( type == "rope_30" || type == "rope_6" ) { // NPC don't craft? // TODO: what about the amount of ropes vs the hunger? if( g->u.has_trait( "WEB_ROPE" ) && g->u.hunger <= 300 ) { return true; } } const itype *it = item_controller->find_template( type ); if( it->count_by_charges() && count > 0 ) { return crafting_inv.has_charges( type, count ); } else { return crafting_inv.has_components( type, abs( count ) ); } }
/* selection of component if a recipe requirement has multiple options (e.g. 'duct tap' or 'welder') */ comp_selection<item_comp> player::select_item_component( const std::vector<item_comp> &components, int batch, inventory &map_inv, bool can_cancel ) { std::vector<item_comp> player_has; std::vector<item_comp> map_has; std::vector<item_comp> mixed; comp_selection<item_comp> selected; for( const auto &component : components ) { itype_id type = component.type; int count = ( component.count > 0 ) ? component.count * batch : abs( component.count ); bool pl = false, mp = false; if( item::count_by_charges( type ) && count > 0 ) { if( has_charges( type, count ) ) { player_has.push_back( component ); pl = true; } if( map_inv.has_charges( type, count ) ) { map_has.push_back( component ); mp = true; } if( !pl && !mp && charges_of( type ) + map_inv.charges_of( type ) >= count ) { mixed.push_back( component ); } } else { // Counting by units, not charges if( has_amount( type, count ) ) { player_has.push_back( component ); pl = true; } if( map_inv.has_components( type, count ) ) { map_has.push_back( component ); mp = true; } if( !pl && !mp && amount_of( type ) + map_inv.amount_of( type ) >= count ) { mixed.push_back( component ); } } } /* select 1 component to use */ if( player_has.size() + map_has.size() + mixed.size() == 1 ) { // Only 1 choice if( player_has.size() == 1 ) { selected.use_from = use_from_player; selected.comp = player_has[0]; } else if( map_has.size() == 1 ) { selected.use_from = use_from_map; selected.comp = map_has[0]; } else { selected.use_from = use_from_both; selected.comp = mixed[0]; } } else { // Let the player pick which component they want to use uimenu cmenu; // Populate options with the names of the items for( auto &map_ha : map_has ) { std::string tmpStr = item::nname( map_ha.type ) + _( " (nearby)" ); cmenu.addentry( tmpStr ); } for( auto &player_ha : player_has ) { cmenu.addentry( item::nname( player_ha.type ) ); } for( auto &elem : mixed ) { std::string tmpStr = item::nname( elem.type ) + _( " (on person & nearby)" ); cmenu.addentry( tmpStr ); } // Unlike with tools, it's a bad thing if there aren't any components available if( cmenu.entries.empty() ) { if( has_trait( trait_id( "DEBUG_HS" ) ) ) { selected.use_from = use_from_player; return selected; } debugmsg( "Attempted a recipe with no available components!" ); selected.use_from = cancel; return selected; } if( can_cancel ) { cmenu.addentry( -1, true, 'q', _( "Cancel" ) ); } // Get the selection via a menu popup cmenu.title = _( "Use which component?" ); cmenu.query(); if( cmenu.ret == static_cast<int>( map_has.size() + player_has.size() + mixed.size() ) ) { selected.use_from = cancel; return selected; } size_t uselection = static_cast<size_t>( cmenu.ret ); if( uselection < map_has.size() ) { selected.use_from = usage::use_from_map; selected.comp = map_has[uselection]; } else if( uselection < map_has.size() + player_has.size() ) { uselection -= map_has.size(); selected.use_from = usage::use_from_player; selected.comp = player_has[uselection]; } else { uselection -= map_has.size() + player_has.size(); selected.use_from = usage::use_from_both; selected.comp = mixed[uselection]; } } return selected; }