diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index af5d91e863e4d..6679a49543ebf 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -2177,7 +2177,8 @@ void pickup_activity_actor::do_turn( player_activity &, Character &who ) } // False indicates that the player canceled pickup when met with some prompt - const bool keep_going = Pickup::do_pickup( target_items, quantities, autopickup, stash_successful ); + const bool keep_going = Pickup::do_pickup( target_items, quantities, autopickup, + stash_successful, info ); // If there are items left we ran out of moves, so continue the activity // Otherwise, we are done. @@ -2210,6 +2211,7 @@ void pickup_activity_actor::serialize( JsonOut &jsout ) const jsout.member( "starting_pos", starting_pos ); jsout.member( "stash_successful", stash_successful ); jsout.member( "autopickup", autopickup ); + jsout.member( "info", info ); jsout.end_object(); } @@ -2225,6 +2227,7 @@ std::unique_ptr pickup_activity_actor::deserialize( JsonValue &j data.read( "starting_pos", actor.starting_pos ); data.read( "stash_successful", actor.stash_successful ); data.read( "autopickup", actor.autopickup ); + data.read( "info", actor.info ); return actor.clone(); } diff --git a/src/activity_actor_definitions.h b/src/activity_actor_definitions.h index d912a515db4c4..c951da589ea23 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -20,6 +20,7 @@ #include "itype.h" #include "item_location.h" #include "memory_fast.h" +#include "pickup.h" #include "point.h" #include "string_id.h" #include "type_id.h" @@ -495,6 +496,7 @@ class pickup_activity_actor : public activity_actor /** Target items and the quantities thereof */ std::vector target_items; std::vector quantities; + Pickup::pick_info info; /** * Position of the character when the activity is started. This is diff --git a/src/pickup.cpp b/src/pickup.cpp index f7d32cb3c2731..27242c7e50d3e 100644 --- a/src/pickup.cpp +++ b/src/pickup.cpp @@ -162,15 +162,40 @@ bool Pickup::query_thief() return false; } +static bool is_bulk_load( const Pickup::pick_info &lhs, const Pickup::pick_info &rhs ) +{ + // Check if storage is the same + if( lhs.dst && rhs.dst && lhs.dst->stacks_with( *rhs.dst ) && + lhs.dst.where() == item_location::type::container && + rhs.dst.where() == item_location::type::container && + lhs.dst.parent_item() == rhs.dst.parent_item() ) { + // Check if source is the same + if( lhs.src_type == rhs.src_type ) { + switch( lhs.src_type ) { + case item_location::type::container: + return lhs.src_container == rhs.src_container; + break; + case item_location::type::map: + case item_location::type::vehicle: + return lhs.src_pos == rhs.src_pos; + break; + default: + break; + } + } + } + return false; +} + // Returns false if pickup caused a prompt and the player selected to cancel pickup static bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool &got_gas, - PickupMap &mapPickup, - bool autopickup, bool &stash_successful, bool &got_frozen_liquid ) + PickupMap &mapPickup, bool autopickup, bool &stash_successful, bool &got_frozen_liquid, + Pickup::pick_info &info ) { Character &player_character = get_player_character(); - int moves_taken = loc.obtain_cost( player_character, quantity ); bool picked_up = false; bool crushed = false; + Pickup::pick_info pre_info( info ); pickup_answer option = CANCEL; @@ -290,11 +315,25 @@ static bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool picked_up = true; } } + if( picked_up ) { + // Update info + info.set_dst( added_it ); + } break; } } if( picked_up ) { + info.set_src( loc ); + info.total_bulk_volume += loc->volume( false, false, quantity ); + if( !is_bulk_load( pre_info, info ) ) { + // Cost to take an item from a container or map + player_character.moves -= loc.obtain_cost( player_character, quantity ); + } else { + // Pure cost to handling item excluding overhead. + player_character.moves -= std::max( player_character.item_handling_cost( *loc, true, 0, quantity, + true ), 1 ); + } contents_change_handler handler; handler.unseal_pocket_containing( loc ); item &orig_it = *loc.get_item(); @@ -306,7 +345,6 @@ static bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool } else { loc.remove_item(); } - player_character.moves -= moves_taken; player_character.flag_encumbrance(); player_character.invalidate_weight_carried_cache(); } @@ -315,7 +353,8 @@ static bool pick_one_up( item_location &loc, int quantity, bool &got_water, bool } bool Pickup::do_pickup( std::vector &targets, std::vector &quantities, - bool autopickup, bool &stash_successful ) + bool autopickup, + bool &stash_successful, Pickup::pick_info &info ) { bool got_water = false; bool got_gas = false; @@ -340,10 +379,12 @@ bool Pickup::do_pickup( std::vector &targets, std::vector &q debugmsg( "lost target item of ACT_PICKUP" ); continue; } - problem = !pick_one_up( target, quantity, got_water, got_gas, mapPickup, autopickup, - stash_successful, - got_frozen_liquid ); + stash_successful, got_frozen_liquid, info ); + if( info.total_bulk_volume > 200_ml ) { + // Bulk loading is not allowed beyond a certain volume + info = Pickup::pick_info(); + } } if( !mapPickup.empty() ) { @@ -438,6 +479,41 @@ int Pickup::cost_to_move_item( const Character &who, const item &it ) return std::min( 400, ret ); } +void Pickup::pick_info::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + jsout.member( "total_bulk_volume", total_bulk_volume ); + jsout.member( "src_type", static_cast( src_type ) ); + jsout.member( "src_pos", src_pos ); + jsout.member( "src_container", src_container ); + jsout.member( "dst", dst ); + jsout.end_object(); +} + +void Pickup::pick_info::deserialize( const JsonObject &jsobj ) +{ + int src_type_; + jsobj.read( "total_bulk_volume", total_bulk_volume ); + jsobj.read( "src_type", src_type_ ); + src_type = static_cast( src_type_ ); + jsobj.read( "src_pos", src_pos ); + jsobj.read( "src_container", src_container ); + jsobj.read( "dst", dst ); +} + +void Pickup::pick_info::set_src( const item_location &src_ ) +{ + // item_location of source may become invalid after the item is moved, so save the information separately. + src_pos = src_.position(); + src_container = src_.parent_item(); + src_type = src_.where(); +} + +void Pickup::pick_info::set_dst( const item_location &dst_ ) +{ + dst = dst_; +} + std::vector Pickup::pickup_rect::list; Pickup::pickup_rect *Pickup::pickup_rect::find_by_coordinate( const point &p ) diff --git a/src/pickup.h b/src/pickup.h index 4797e58493904..686afd18f0486 100644 --- a/src/pickup.h +++ b/src/pickup.h @@ -5,20 +5,35 @@ #include #include "cuboid_rectangle.h" +#include "item_location.h" #include "point.h" class Character; class item; -class item_location; namespace Pickup { +/** Pick up information reminder for bulk loading */ +struct pick_info { + pick_info() = default; + void serialize( JsonOut &jsout ) const; + void deserialize( const JsonObject &jsobj ); + void set_src( const item_location &src_ ); + void set_dst( const item_location &dst_ ); + + units::volume total_bulk_volume = 0_ml; + item_location::type src_type = item_location::type::invalid; + tripoint src_pos; + item_location src_container; + item_location dst; +}; + /** * Returns `false` if the player was presented a prompt and decided to cancel the pickup. * `true` in other cases. */ bool do_pickup( std::vector &targets, std::vector &quantities, - bool autopickup, bool &stash_successful ); + bool autopickup, bool &stash_successful, Pickup::pick_info &info ); bool query_thief(); enum from_where : int {