ret_val<edible_rating> player::can_eat( const item &food ) const { // @todo This condition occurs way too often. Unify it. if( is_underwater() ) { return ret_val<edible_rating>::make_failure( _( "You can't do that while underwater." ) ); } const auto &comest = food.type->comestible; if( !comest ) { return ret_val<edible_rating>::make_failure( _( "That doesn't look edible." ) ); } const bool eat_verb = food.has_flag( "USE_EAT_VERB" ); const bool edible = eat_verb || comest->comesttype == "FOOD"; const bool drinkable = !eat_verb && comest->comesttype == "DRINK"; if( edible || drinkable ) { for( const auto &elem : food.type->materials ) { if( !elem->edible() ) { return ret_val<edible_rating>::make_failure( _( "That doesn't look edible in its current form." ) ); } } } if( comest->tool != "null" ) { const bool has = item::count_by_charges( comest->tool ) ? has_charges( comest->tool, 1 ) : has_amount( comest->tool, 1 ); if( !has ) { return ret_val<edible_rating>::make_failure( NO_TOOL, string_format( _( "You need a %s to consume that!" ), item::nname( comest->tool ).c_str() ) ); } } // For all those folks who loved eating marloss berries. D:< mwuhahaha if( has_trait( trait_id( "M_DEPENDENT" ) ) && food.typeId() != "mycus_fruit" ) { return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION, _( "We can't eat that. It's not right for us." ) ); } // Here's why PROBOSCIS is such a negative trait. if( has_trait( trait_id( "PROBOSCIS" ) ) && !drinkable ) { return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION, _( "Ugh, you can't drink that!" ) ); } if( has_trait( trait_id( "CARNIVORE" ) ) && nutrition_for( food ) > 0 && food.has_any_flag( carnivore_blacklist ) && !food.has_flag( "CARNIVORE_OK" ) ) { return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION, _( "Eww. Inedible plant stuff!" ) ); } if( ( has_trait( trait_id( "HERBIVORE" ) ) || has_trait( trait_id( "RUMINANT" ) ) ) && food.has_any_flag( herbivore_blacklist ) ) { // Like non-cannibal, but more strict! return ret_val<edible_rating>::make_failure( INEDIBLE_MUTATION, _( "The thought of eating that makes you feel sick." ) ); } return ret_val<edible_rating>::make_success(); }
int npc::value(item &it) { int ret = it.price() / 50; skill best = best_skill(); if (best != sk_unarmed) { int weapon_val = it.weapon_value(sklevel) - weapon.weapon_value(sklevel); if (weapon_val > 0) ret += weapon_val; } if (it.is_food()) { it_comest* comest = dynamic_cast<it_comest*>(it.type); if (comest->nutr > 0 || comest->quench > 0) ret++; if (hunger > 40) ret += (comest->nutr + hunger - 40) / 6; if (thirst > 40) ret += (comest->quench + thirst - 40) / 4; } if (it.is_ammo()) { it_ammo* ammo = dynamic_cast<it_ammo*>(it.type); it_gun* gun; if (weapon.is_gun()) { gun = dynamic_cast<it_gun*>(weapon.type); if (ammo->type == gun->ammo) ret += 14; } for (int i = 0; i < inv.size(); i++) { if (inv[i].is_gun()) { gun = dynamic_cast<it_gun*>(inv[i].type); if (ammo->type == gun->ammo) ret += 6; } } } if (it.is_book()) { it_book* book = dynamic_cast<it_book*>(it.type); if (book->intel <= int_cur) { ret += book->fun; if (sklevel[book->type] < book->level && sklevel[book->type] >= book->req) ret += book->level * 3; } } // TODO: Sometimes we want more than one tool? Also we don't want EVERY tool. if (it.is_tool() && !has_amount(itype_id(it.type->id), 1)) { ret += 8; } // TODO: Artifact hunting from relevant factions // ALSO TODO: Bionics hunting from relevant factions if (fac_has_job(FACJOB_DRUGS) && it.is_food() && (dynamic_cast<it_comest*>(it.type))->addict >= 5) ret += 10; if (fac_has_job(FACJOB_DOCTORS) && it.type->id >= itm_bandages && it.type->id <= itm_prozac) ret += 10; if (fac_has_value(FACVAL_BOOKS) && it.is_book()) ret += 14; if (fac_has_job(FACJOB_SCAVENGE)) { // Computed last for _reasons_. ret += 6; ret *= 1.3; } return ret; }
bool inventory::has_components(itype_id it, int quantity) const { return has_amount(it, quantity, false); }
bool inventory::has_tools(itype_id it, int quantity) const { return has_amount(it, quantity, true); }
comp_selection<tool_comp> player::select_tool_component( const std::vector<tool_comp> &tools, int batch, inventory &map_inv, const std::string &hotkeys, bool can_cancel ) { comp_selection<tool_comp> selected; bool found_nocharge = false; std::vector<tool_comp> player_has; std::vector<tool_comp> map_has; // Use charges of any tools that require charges used for( auto it = tools.begin(); it != tools.end() && !found_nocharge; ++it ) { itype_id type = it->type; if( it->count > 0 ) { long count = it->count * batch; if( has_charges( type, count ) ) { player_has.push_back( *it ); } if( map_inv.has_charges( type, count ) ) { map_has.push_back( *it ); } } else if( has_amount( type, 1 ) || map_inv.has_tools( type, 1 ) ) { selected.comp = *it; found_nocharge = true; } } if( found_nocharge ) { selected.use_from = use_from_none; return selected; // Default to using a tool that doesn't require charges } if( player_has.size() + map_has.size() == 1 ) { if( map_has.empty() ) { selected.use_from = use_from_player; selected.comp = player_has[0]; } else { selected.use_from = use_from_map; selected.comp = map_has[0]; } } else { // Variety of options, list them and pick one // Populate the list uimenu tmenu( hotkeys ); for( auto &map_ha : map_has ) { std::string tmpStr = item::nname( map_ha.type ) + _( " (nearby)" ); tmenu.addentry( tmpStr ); } for( auto &player_ha : player_has ) { tmenu.addentry( item::nname( player_ha.type ) ); } if( tmenu.entries.empty() ) { // This SHOULD only happen if cooking with a fire, selected.use_from = use_from_none; return selected; // and the fire goes out. } if( can_cancel ) { tmenu.addentry( -1, true, 'q', _( "Cancel" ) ); } // Get selection via a popup menu tmenu.title = _( "Use which tool?" ); tmenu.query(); if( tmenu.ret == static_cast<int>( map_has.size() + player_has.size() ) ) { selected.use_from = cancel; return selected; } size_t uselection = static_cast<size_t>( tmenu.ret ); if( uselection < map_has.size() ) { selected.use_from = use_from_map; selected.comp = map_has[uselection]; } else { uselection -= map_has.size(); selected.use_from = use_from_player; selected.comp = player_has[uselection]; } } return selected; }
/* 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; }
bool player::uninstall_bionic(bionic_id b_id) { // malfunctioning bionics don't have associated items and get a difficulty of 12 int difficulty = 12; if( item_controller->has_template(b_id) > 0) { const it_bionic *type = dynamic_cast<it_bionic *> (item_controller->find_template(b_id)); difficulty = type->difficulty; } if (!has_bionic(b_id)) { popup(_("You don't have this bionic installed.")); return false; } if (!(inv.has_items_with_quality("CUT", 1, 1) && has_amount("1st_aid", 1))) { popup(_("Removing bionics requires a cutting tool and a first aid kit.")); return false; } if ( b_id == "bio_blaster" ) { popup(_("Removing your Fusion Blaster Arm would leave you with a useless stump.")); return false; } // removal of bionics adds +2 difficulty over installation int chance_of_success = bionic_manip_cos(int_cur, skillLevel("electronics"), skillLevel("firstaid"), skillLevel("mechanics"), difficulty + 2); if (!query_yn(_("WARNING: %i percent chance of SEVERE bodily damage! Remove anyway?"), 100 - chance_of_success)) { return false; } use_charges("1st_aid", 1); practice( "electronics", int((100 - chance_of_success) * 1.5) ); practice( "firstaid", int((100 - chance_of_success) * 1.0) ); practice( "mechanics", int((100 - chance_of_success) * 0.5) ); int success = chance_of_success - rng(1, 100); if (success > 0) { add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."), pgettext("memorial_female", "Removed bionic: %s."), bionics[b_id]->name.c_str()); // until bionics can be flagged as non-removable add_msg(m_neutral, _("You jiggle your parts back into their familiar places.")); add_msg(m_good, _("Successfully removed %s."), bionics[b_id]->name.c_str()); remove_bionic(b_id); g->m.spawn_item(posx, posy, "burnt_out_bionic", 1); } else { add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."), pgettext("memorial_female", "Removed bionic: %s."), bionics[b_id]->name.c_str()); bionics_uninstall_failure(this); } g->refresh_all(); return true; }
bool inventory::has_components( const itype_id &it, int quantity, const std::function<bool( const item & )> &filter ) const { return has_amount( it, quantity, false, filter ); }
bool inventory::has_tools( const itype_id &it, int quantity, const std::function<bool( const item & )> &filter ) const { return has_amount( it, quantity, true, filter ); }
edible_rating player::can_eat( const item &food, bool interactive, bool force ) const { if( is_npc() || force ) { // Just to be sure interactive = false; } const std::string &itname = food.tname(); // Helper to avoid ton of `if( interactive )` // Prints if interactive is true, does nothing otherwise const auto maybe_print = [interactive, &itname] ( game_message_type type, const char *str ) { if( interactive ) { add_msg( type, str, itname.c_str() ); } }; // As above, but for queries // Asks if interactive and not force // Always true if force // Never true otherwise const auto maybe_query = [force, interactive, &itname, this]( const char *str ) { if( force ) { return true; } else if( !interactive ) { return false; } return query_yn( str, itname.c_str() ); }; const auto comest = food.type->comestible.get(); if( comest == nullptr ) { maybe_print( m_info, _( "That doesn't look edible." ) ); return INEDIBLE; } if( comest->tool != "null" ) { bool has = has_amount( comest->tool, 1 ); if( item::count_by_charges( comest->tool ) ) { has = has_charges( comest->tool, 1 ); } if( !has ) { if( interactive ) { add_msg_if_player( m_info, _( "You need a %s to consume that!" ), item::nname( comest->tool ).c_str() ); } return NO_TOOL; } } if( is_underwater() ) { maybe_print( m_info, _( "You can't do that while underwater." ) ); return INEDIBLE; } // For all those folks who loved eating marloss berries. D:< mwuhahaha if( has_trait( "M_DEPENDENT" ) && food.type->id != "mycus_fruit" ) { maybe_print( m_info, _( "We can't eat that. It's not right for us." ) ); return INEDIBLE_MUTATION; } const bool drinkable = comest->comesttype == "DRINK" && !food.has_flag( "USE_EAT_VERB" ); // Here's why PROBOSCIS is such a negative trait. if( has_trait( "PROBOSCIS" ) && !drinkable ) { maybe_print( m_info, _( "Ugh, you can't drink that!" ) ); return INEDIBLE_MUTATION; } int capacity = stomach_capacity(); // TODO: Move this cache to a structure and pass it around // to speed up checking entire inventory for edibles const bool gourmand = has_trait( "GOURMAND" ); const bool hibernate = has_active_mutation( "HIBERNATE" ); const bool eathealth = has_trait( "EATHEALTH" ); const bool slimespawner = has_trait( "SLIMESPAWNER" ); const int nutr = nutrition_for( food.type ); const int quench = comest->quench; bool spoiled = food.rotten(); const int temp_hunger = get_hunger() - nutr; const int temp_thirst = get_thirst() - quench; const bool overeating = get_hunger() < 0 && nutr >= 5 && !gourmand && !eathealth && !slimespawner && !hibernate; if( interactive && hibernate && ( get_hunger() >= -60 && get_thirst() >= -60 ) && ( temp_hunger < -60 || temp_thirst < -60 ) ) { if( !maybe_query( _( "You're adequately fueled. Prepare for hibernation?" ) ) ) { return TOO_FULL; } } const bool carnivore = has_trait( "CARNIVORE" ); if( carnivore && nutr > 0 && food.has_any_flag( carnivore_blacklist ) && !food.has_flag( "CARNIVORE_OK" ) ) { maybe_print( m_info, _( "Eww. Inedible plant stuff!" ) ); return INEDIBLE_MUTATION; } if( ( has_trait( "HERBIVORE" ) || has_trait( "RUMINANT" ) ) && food.has_any_flag( herbivore_blacklist ) ) { // Like non-cannibal, but more strict! maybe_print( m_info, _( "The thought of eating that makes you feel sick. You decide not to." ) ); return INEDIBLE_MUTATION; } if( food.has_flag( "CANNIBALISM" ) ) { if( !has_trait_flag( "CANNIBAL" ) && !maybe_query( _( "The thought of eating that makes you feel sick. Really do it?" ) ) ) { return CANNIBALISM; } } if( is_allergic( food ) && !maybe_query( _( "Really eat that %s? Your stomach won't be happy." ) ) ) { return ALLERGY; } if( carnivore && food.has_flag( "ALLERGEN_JUNK" ) && !food.has_flag( "CARNIVORE_OK" ) && !maybe_query( _( "Really eat that %s? Your stomach won't be happy." ) ) ) { return ALLERGY; } const bool saprophage = has_trait( "SAPROPHAGE" ); // The item is solid food const bool chew = comest->comesttype == "FOOD" || food.has_flag( "USE_EAT_VERB" ); if( spoiled ) { if( !saprophage && !has_trait( "SAPROVORE" ) && !maybe_query( _( "This %s smells awful! Eat it?" ) ) ) { return ROTTEN; } } else if( saprophage && chew && !food.has_flag( "FERTILIZER" ) && !maybe_query( _( "Really eat that %s? Your stomach won't be happy." ) ) ) { // Note: We're allowing all non-solid "food". This includes drugs // Hardcoding fertilizer for now - should be a separate flag later //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person. //~ Semantic difference, but greatly facilitates people being proud of their character. maybe_print( m_info, _( "It's too fresh, let it age a little first." ) ); return ROTTEN; } // Print at most one of those bool overfull = false; if( overeating ) { overfull = !maybe_query( _( "You're full. Force yourself to eat?" ) ); } else if( ( ( nutr > 0 && temp_hunger < capacity ) || ( comest->quench > 0 && temp_thirst < capacity ) ) && !eathealth && !slimespawner ) { overfull = !maybe_query( _( "You will not be able to finish it all. Consume it?" ) ); } if( overfull ) { return TOO_FULL; } // All checks ended, it's edible (or we're pretending it is) return EDIBLE; }