Skip to content

Commit

Permalink
Fix handling, text of giving npc's items (#37654)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ramza13 authored Feb 4, 2020
1 parent 033401f commit 582eba6
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 118 deletions.
69 changes: 15 additions & 54 deletions src/npc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, num_bp> 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<hp_part>( 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;
Expand All @@ -1069,18 +1029,19 @@ 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;
}
}

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 );
}

Expand Down
2 changes: 1 addition & 1 deletion src/npc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> &fail_reasons );
Expand Down
125 changes: 62 additions & 63 deletions src/npctalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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 );
};
}

Expand Down Expand Up @@ -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" ) {
Expand All @@ -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() );
Expand Down Expand Up @@ -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." );
Expand All @@ -3218,84 +3221,80 @@ std::string give_item_to( npc &p, bool allow_use, bool allow_carry )
return _( "Are you <swear> 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<bool> 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;
Expand Down

0 comments on commit 582eba6

Please sign in to comment.