Skip to content

Commit

Permalink
Merge pull request #73856 from Procyonae/Backport72759And73703
Browse files Browse the repository at this point in the history
Backport #72759 and #73703
  • Loading branch information
Maleclypse authored May 17, 2024
2 parents 94de1a8 + 05d03e9 commit 3b5b9d7
Showing 1 changed file with 126 additions and 48 deletions.
174 changes: 126 additions & 48 deletions src/npctalk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ static const efftype_id effect_riding( "riding" );
static const efftype_id effect_sleep( "sleep" );
static const efftype_id effect_under_operation( "under_operation" );

static const flag_id json_flag_NO_UNLOAD( "NO_UNLOAD" );

static const itype_id fuel_type_animal( "animal" );
static const itype_id itype_foodperson_mask( "foodperson_mask" );
static const itype_id itype_foodperson_mask_on( "foodperson_mask_on" );
Expand Down Expand Up @@ -3814,67 +3816,143 @@ void talk_effect_fun_t::set_bulk_trade_accept( const JsonObject &jo, std::string
} else {
dov_quantity.min.dbl_val = -1;
}

bool is_trade = member == "u_bulk_trade_accept" || member == "npc_bulk_trade_accept";
function = [is_trade, is_npc, dov_quantity]( dialogue & d ) {
talker *seller = d.actor( is_npc );
talker *buyer = d.actor( !is_npc );
item tmp( d.cur_item );
int quantity = dov_quantity.evaluate( d );
int seller_has = 0;
if( tmp.count_by_charges() ) {
seller_has = seller->charges_of( d.cur_item );
} else {
seller_has = seller->items_with( [&tmp]( const item & e ) {
return tmp.type == e.type;
} ).size();
itype_id traded_itype_id = d.cur_item;
int number_to_transfer = dov_quantity.evaluate( d );

// Use a set! No duplicates! Duplicate pointers will be bad(crash) when we try to remove the item later.
std::set<item *> items_to_transfer;

seller->get_character()->visit_items( [&]( item * visited, item * parent ) {
if( visited->typeId() == traded_itype_id ) {
if( parent && ( parent->all_pockets_sealed() || visited->made_of( phase_id::LIQUID ) ||
parent->has_flag( json_flag_NO_UNLOAD ) ) ) {
items_to_transfer.emplace( parent );
} else {
items_to_transfer.emplace( visited );
}
}
return VisitResponse::NEXT;
} );

if( seller->get_character()->is_avatar() ) {
std::string warning = _( "Really continue? You will hand over the following items:" );
int num_warnings = 0;
for( item *checked_item : items_to_transfer ) {
warning += "\n" + checked_item->tname();
num_warnings++;
if( !is_trade && num_warnings >= number_to_transfer ) {
break; // bulk donate has a variable number to transfer but bulk trade always takes everything
}
}
if( !query_yn( warning ) ) {
return;
}
}
seller_has = ( quantity == -1 ) ? seller_has : std::min( seller_has, quantity );
tmp.charges = seller_has;

if( is_trade ) {

const int npc_debt = d.actor( true )->debt();
int price = total_price( *seller, d.cur_item ) * ( is_npc ? -1 : 1 ) + npc_debt;
if( d.actor( true )->get_faction() && !d.actor( true )->get_faction()->currency.is_empty() ) {
const itype_id &pay_in = d.actor( true )->get_faction()->currency;
item pay( pay_in );
const int value = d.actor( true )->value( pay );
if( value > 0 ) {
int required = price / value;
int buyer_has = required;
if( is_npc ) {
buyer_has = std::min( buyer_has, buyer->charges_of( pay_in ) );
buyer->use_charges( pay_in, buyer_has );
} else {
if( buyer_has == 1 ) {
//~ %1%s is the NPC name, %2$s is an item
popup( _( "%1$s gives you a %2$s." ), buyer->disp_name(),
pay.tname() );
} else if( buyer_has > 1 ) {
//~ %1%s is the NPC name, %2$d is a number of items, %3$s are items
popup( _( "%1$s gives you %2$d %3$s." ), buyer->disp_name(), buyer_has,
pay.tname() );
}
}
for( int i = 0; i < buyer_has; i++ ) {
seller->i_add( pay );
price -= value;
}
} else {
debugmsg( "%s pays in bulk_trade_accept with faction currency worth 0!",
d.actor( true )->disp_name() );
}
} else {
int price = total_price( *seller, traded_itype_id ) * ( is_npc ? -1 : 1 ) + npc_debt;
if( d.actor( true )->get_faction() && d.actor( true )->get_faction()->currency.is_empty() ) {
debugmsg( "%s has no faction currency to pay with in bulk_trade_accept!",
d.actor( true )->disp_name() );
return; // Fatal, no reasonable way to recover.
}

// This can be very confusing, so for the benefit of future contributors I have elected to make the variable names
// terribly verbose.
const itype_id &currency_type = d.actor( true )->get_faction()->currency;
item one_currency_unit( currency_type );
const int int_value_of_one_currency_unit = d.actor( true )->value( one_currency_unit );
if( int_value_of_one_currency_unit <= 0 ) {
debugmsg( "%s pays in bulk_trade_accept with faction currency worth %d!",
d.actor( true )->disp_name(), int_value_of_one_currency_unit );
return; // Fatal, no reasonable way to recover.
}

int num_transferred_currency_units = price / int_value_of_one_currency_unit;
if( is_npc ) {
num_transferred_currency_units = std::min( num_transferred_currency_units,
buyer->charges_of( currency_type ) );
buyer->use_charges( currency_type, num_transferred_currency_units );
} else {

if( num_transferred_currency_units == 1 ) {
//~ %1%s is the NPC name, %2$s is an item
popup( _( "%1$s gives you a %2$s." ), buyer->disp_name(),
one_currency_unit.tname() );
} else if( num_transferred_currency_units > 1 ) {
//~ %1%s is the NPC name, %2$d is a number of items, %3$s are items
popup( _( "%1$s gives you %2$d %3$s." ), buyer->disp_name(), num_transferred_currency_units,
one_currency_unit.tname() );
}

}

// Currency is actually transferred
for( int i = 0; i < num_transferred_currency_units; i++ ) {
seller->i_add( one_currency_unit );
price -= int_value_of_one_currency_unit;
}

// Tally up debts
d.actor( true )->add_debt( -npc_debt );
d.actor( true )->add_debt( price );

// Now let's actually transfer the items!
for( item *transferred : items_to_transfer ) {
item transfer_copy( *transferred );
buyer->i_add_or_drop( transfer_copy );

auto remove_items_filter = [&]( const item & it ) {
return &it == const_cast<const item *>( transferred );
};

seller->remove_items_with( remove_items_filter );
}


} else { // u_bulk_donate

int number_transferred = 0;
for( item *transferred : items_to_transfer ) {
item transfer_copy( *transferred );
buyer->i_add_or_drop( transfer_copy );

// Count now, while the item still exists
if( transferred->typeId() != traded_itype_id ) {
// Container traded over
if( item( traded_itype_id ).count_by_charges() ) {
number_transferred += transferred->charges_of( traded_itype_id );
} else {
number_transferred += transferred->amount_of( traded_itype_id );
}
} else {
// Loose items traded over
if( transferred->count_by_charges() ) {
number_transferred += transferred->charges;
} else {
number_transferred++;
}
}

auto remove_items_filter = [&]( const item & it ) {
return &it == const_cast<const item *>( transferred );
};

seller->remove_items_with( remove_items_filter );

if( number_transferred >= number_to_transfer ) {
return;
}
}

}
if( tmp.count_by_charges() ) {
seller->use_charges( d.cur_item, seller_has );
} else {
seller->use_amount( d.cur_item, seller_has );
}
buyer->i_add( tmp );
};
}

Expand Down

0 comments on commit 3b5b9d7

Please sign in to comment.