diff --git a/src/npc.cpp b/src/npc.cpp index 402147311529e..073b119cb658b 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -982,82 +982,42 @@ void npc::do_npc_read() } } -bool npc::wear_if_wanted( const item &it ) +bool npc::wear_if_wanted( const item &it, std::string &reason ) { // Note: this function isn't good enough to use with NPC AI alone // Restrict it to player's orders for now if( !it.is_armor() ) { + reason = _( "This can't be worn." ); return false; } - // TODO: Make it depend on stuff - static const std::array max_encumb = {{ - 30, // bp_torso - Higher if ranged? - 100, // bp_head - 30, // bp_eyes - Lower if using ranged? - 30, // bp_mouth - 30, // bp_arm_l - 30, // bp_arm_r - 30, // bp_hand_l - Lower if throwing? - 30, // bp_hand_r - // Must be enough to allow hazmat, turnout etc. - 30, // bp_leg_l - Higher if ranged? - 30, // bp_leg_r - // Doesn't hurt much - 50, // bp_foot_l - 50, // bp_foot_r - } - }; - // Splints ignore limits, but only when being equipped on a broken part // TODO: Drop splints when healed - bool splint = it.has_flag( "SPLINT" ); - if( splint ) { - splint = false; + if( it.has_flag( "SPLINT" ) ) { for( int i = 0; i < num_hp_parts; i++ ) { hp_part hpp = static_cast( i ); body_part bp = player::hp_to_bp( hpp ); if( is_limb_broken( hpp ) && !has_effect( effect_mending, bp ) && it.covers( bp ) ) { - splint = true; - break; + reason = _( "Thanks, I'll wear that now." ); + return !!wear_item( it, false ); } } } - if( splint ) { - return !!wear_item( it, false ); - } - - if( !can_wear( it, true ).success() ) { - return false; - } - - const int it_encumber = it.get_encumber( *this ); while( !worn.empty() ) { auto size_before = worn.size(); - bool encumb_ok = true; - const auto new_enc = get_encumbrance( it ); // Strip until we can put the new item on // This is one of the reasons this command is not used by the AI - for( const body_part bp : all_body_parts ) { - if( !it.covers( bp ) ) { - continue; - } + if( can_wear( it ).success() ) { + // TODO: Hazmat/power armor makes this not work due to 1 boots/headgear limit - if( it_encumber > max_encumb[bp] ) { - // Not an NPC-friendly item + if( !!wear_item( it, false ) ) { + reason = _( "Thanks, I'll wear that now." ); + return true; + } else { + reason = _( "I tried but couldn't wear it." ); return false; } - - if( new_enc[bp].encumbrance > max_encumb[bp] ) { - encumb_ok = false; - break; - } - } - - if( encumb_ok && can_wear( it ).success() ) { - // TODO: Hazmat/power armor makes this not work due to 1 boots/headgear limit - return !!wear_item( it, false ); } // Otherwise, maybe we should take off one or more items and replace them bool took_off = false; @@ -1069,7 +1029,7 @@ bool npc::wear_if_wanted( const item &it ) auto iter = std::find_if( worn.begin(), worn.end(), [bp]( const item & armor ) { return armor.covers( bp ); } ); - if( iter != worn.end() ) { + if( iter != worn.end() && !( is_limb_broken( bp_to_hp( bp ) ) && iter->has_flag( "SPLINT" ) ) ) { took_off = takeoff( *iter ); break; } @@ -1077,10 +1037,11 @@ bool npc::wear_if_wanted( const item &it ) if( !took_off || worn.size() >= size_before ) { // Shouldn't happen, but does + reason = _( "I tried but couldn't wear it." ); return false; } } - + reason = _( "Thanks, I'll wear that now." ); return worn.empty() && wear_item( it, false ); } diff --git a/src/npc.h b/src/npc.h index 38999dc1e167d..098cec5a93239 100644 --- a/src/npc.h +++ b/src/npc.h @@ -907,7 +907,7 @@ class npc : public player void update_worst_item_value(); int value( const item &it ) const; int value( const item &it, int market_price ) const; - bool wear_if_wanted( const item &it ); + bool wear_if_wanted( const item &it, std::string &reason ); void start_read( item &chosen, player *pl ); void finish_read( item &book ); bool can_read( const item &book, std::vector &fail_reasons ); diff --git a/src/npctalk.cpp b/src/npctalk.cpp index b17101e27dc69..5bdf508c69bba 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -87,7 +87,7 @@ int topic_category( const talk_topic &the_topic ); const talk_topic &special_talk( char ch ); -std::string give_item_to( npc &p, bool allow_use, bool allow_carry ); +std::string give_item_to( npc &p, bool allow_use ); std::string talk_trial::name() const { @@ -2243,7 +2243,7 @@ void talk_effect_fun_t::set_bulk_trade_accept( bool is_trade, bool is_npc ) void talk_effect_fun_t::set_npc_gets_item( bool to_use ) { function = [to_use]( const dialogue & d ) { - d.reason = give_item_to( *( d.beta ), to_use, !to_use ); + d.reason = give_item_to( *( d.beta ), to_use ); }; } @@ -3154,6 +3154,8 @@ static consumption_result try_consume( npc &p, item &it, std::string &reason ) if( !p.eat( to_eat ) ) { reason = _( "It doesn't look like a good idea to consume this…" ); return REFUSED; + } else { + reason = _( "Thanks, that hit the spot." ); } } else if( to_eat.is_medication() || to_eat.get_contained().is_medication() ) { if( comest->tool != "null" ) { @@ -3167,6 +3169,7 @@ static consumption_result try_consume( npc &p, item &it, std::string &reason ) return REFUSED; } p.use_charges( comest->tool, 1 ); + reason = _( "Thanks, I feel better already." ); } if( to_eat.type->has_use() ) { amount_used = to_eat.type->invoke( p, to_eat, p.pos() ); @@ -3196,7 +3199,7 @@ static consumption_result try_consume( npc &p, item &it, std::string &reason ) return CONSUMED_ALL; } -std::string give_item_to( npc &p, bool allow_use, bool allow_carry ) +std::string give_item_to( npc &p, bool allow_use ) { if( p.is_hallucination() ) { return _( "No thanks, I'm good." ); @@ -3218,84 +3221,80 @@ std::string give_item_to( npc &p, bool allow_use, bool allow_carry ) return _( "Are you insane!?" ); } - std::string no_consume_reason; + bool taken = false; + std::string reason = _( "Nope." ); + int our_ammo = p.ammo_count_for( p.weapon ); + int new_ammo = p.ammo_count_for( given ); + const double new_weapon_value = p.weapon_value( given, new_ammo ); + const double cur_weapon_value = p.weapon_value( p.weapon, our_ammo ); + add_msg( m_debug, "NPC evaluates own %s (%d ammo): %0.1f", + p.weapon.typeId(), our_ammo, cur_weapon_value ); + add_msg( m_debug, "NPC evaluates your %s (%d ammo): %0.1f", + given.typeId(), new_ammo, new_weapon_value ); if( allow_use ) { // Eating first, to avoid evaluating bread as a weapon - const auto consume_res = try_consume( p, given, no_consume_reason ); - if( consume_res == CONSUMED_ALL ) { - g->u.i_rem( &given ); - } + const auto consume_res = try_consume( p, given, reason ); if( consume_res != REFUSED ) { + if( consume_res == CONSUMED_ALL ) { + g->u.i_rem( &given ); + } g->u.moves -= 100; if( given.is_container() ) { given.on_contents_changed(); } - return _( "Here we go…" ); - } - } - - bool taken = false; - int our_ammo = p.ammo_count_for( p.weapon ); - int new_ammo = p.ammo_count_for( given ); - const double new_weapon_value = p.weapon_value( given, new_ammo ); - const double cur_weapon_value = p.weapon_value( p.weapon, our_ammo ); - if( allow_use ) { - add_msg( m_debug, "NPC evaluates own %s (%d ammo): %0.1f", - p.weapon.type->get_id(), our_ammo, cur_weapon_value ); - add_msg( m_debug, "NPC evaluates your %s (%d ammo): %0.1f", - given.type->get_id(), new_ammo, new_weapon_value ); - if( new_weapon_value > cur_weapon_value ) { + }// wield it if its a weapon + else if( new_weapon_value > cur_weapon_value ) { p.wield( given ); + reason = _( "Thanks, I'll wield that now." ); taken = true; + }// HACK: is_gun here is a hack to prevent NPCs wearing guns if they don't want to use them + else if( !given.is_gun() && given.is_armor() ) { + //if it is impossible to wear return why + ret_val can_wear = p.can_wear( given, true ); + if( !can_wear.success() ) { + reason = can_wear.str(); + } else { + //if we can wear it with equip changes prompt first + can_wear = p.can_wear( given ); + if( ( can_wear.success() || + query_yn( can_wear.str() + _( " Should I take something off?" ) ) ) + && p.wear_if_wanted( given, reason ) ) { + taken = true; + } else { + reason = can_wear.str(); + } + } + } else { + reason += string_format( + _( "My current weapon is better than this. \n(new weapon value: %.1f vs %.1f)." ), new_weapon_value, + cur_weapon_value ); } - - // HACK: is_gun here is a hack to prevent NPCs wearing guns if they don't want to use them - if( !taken && !given.is_gun() && p.wear_if_wanted( given ) ) { + } else {//allow_use is false so try to carry instead + if( p.can_pickVolume( given ) && p.can_pickWeight( given ) ) { + reason = _( "Thanks, I'll carry that now." ); taken = true; + p.i_add( given ); + } else { + if( !p.can_pickVolume( given ) ) { + const units::volume free_space = p.volume_capacity() - p.volume_carried(); + reason += "\n" + std::string( _( "I have no space to store it." ) ) + "\n"; + if( free_space > 0_ml ) { + reason += string_format( _( "I can only store %s %s more." ), + format_volume( free_space ), volume_units_long() ); + } else { + reason += _( "…or to store anything else for that matter." ); + } + } + if( !p.can_pickWeight( given ) ) { + reason += std::string( "\n" ) + _( "It is too heavy for me to carry." ); + } } } - if( !taken && allow_carry && - p.can_pickVolume( given ) && - p.can_pickWeight( given ) ) { - taken = true; - p.i_add( given ); - } - if( taken ) { g->u.i_rem( &given ); g->u.moves -= 100; p.has_new_items = true; - return _( "Thanks!" ); - } - - std::string reason = _( "Nope." ); - if( allow_use ) { - if( !no_consume_reason.empty() ) { - reason += no_consume_reason + "\n"; - } - - reason += _( "My current weapon is better than this." ); - reason += "\n" + string_format( _( "(new weapon value: %.1f vs %.1f)." ), new_weapon_value, - cur_weapon_value ); - if( !given.is_gun() && given.is_armor() ) { - reason += std::string( "\n" ) + _( "It's too encumbering to wear." ); - } - } - if( allow_carry ) { - if( !p.can_pickVolume( given ) ) { - const units::volume free_space = p.volume_capacity() - p.volume_carried(); - reason += "\n" + std::string( _( "I have no space to store it." ) ) + "\n"; - if( free_space > 0_ml ) { - reason += string_format( _( "I can only store %s %s more." ), - format_volume( free_space ), volume_units_long() ); - } else { - reason += _( "…or to store anything else for that matter." ); - } - } - if( !p.can_pickWeight( given ) ) { - reason += std::string( "\n" ) + _( "It is too heavy for me to carry." ); - } } return reason;