From 7d69d2151ac83dab3d127694c3936a0d135a5fda Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Tue, 7 Aug 2018 13:35:37 -0500 Subject: [PATCH 1/2] game: refactor handle_liquids() split the logic of handle_liquids() into two functions: * get_liquid_target() handles the menu items to get the target of the liquid transfer and populated a liquid_dest_opt structure * perform_liquid_transfer() handles transfering liquids from the source to the target, according to the liquid_dest_opt structure handle_liquids() is now a wrapper for the new functions. in the future, this will make it easier to write activity handlers that need special cases for handling liquid transfers, because they can call get_liquid_target() without commiting to perform_liquid_transfer(). I intend to use this functionality in a future PR that will turn milking into a player_activity. --- src/game.cpp | 213 +++++++++++++++++++++++++++++++++------------------ src/game.h | 29 +++++++ 2 files changed, 168 insertions(+), 74 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index d6df7774bde25..a43d117495e98 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9449,10 +9449,11 @@ extern void serialize_liquid_target( player_activity &act, int container_item_po extern void serialize_liquid_target( player_activity &act, const tripoint &pos ); extern void serialize_liquid_target( player_activity &act, const monster &mon ); -bool game::handle_liquid( item &liquid, item * const source, const int radius, +bool game::get_liquid_target( item &liquid, item * const source, const int radius, const tripoint * const source_pos, const vehicle * const source_veh, - const monster * const source_mon) + const monster * const source_mon, + liquid_dest_opt &target ) { if( !liquid.made_of(LIQUID) ) { dbg(D_ERROR) << "game:handle_liquid: Tried to handle_liquid a non-liquid!"; @@ -9461,24 +9462,10 @@ bool game::handle_liquid( item &liquid, item * const source, const int radius, return false; } - const auto create_activity = [&]() { - if( source_veh != nullptr ) { - u.assign_activity( activity_id( "ACT_FILL_LIQUID" ) ); - serialize_liquid_source( u.activity, *source_veh, liquid.typeId() ); - return true; - } else if( source_pos != nullptr ) { - u.assign_activity( activity_id( "ACT_FILL_LIQUID" ) ); - serialize_liquid_source( u.activity, *source_pos, liquid ); - return true; - } else { - return false; - } - }; - - const std::string liquid_name = liquid.display_name( liquid.charges ); - uimenu menu; menu.return_invalid = true; + + const std::string liquid_name = liquid.display_name( liquid.charges ); if( source_pos != nullptr ) { menu.text = string_format( _( "What to do with the %s from %s?" ), liquid_name.c_str(), m.name( *source_pos ).c_str() ); } else if( source_veh != nullptr ) { @@ -9492,17 +9479,15 @@ bool game::handle_liquid( item &liquid, item * const source, const int radius, if( u.can_consume( liquid ) && !source_mon ) { menu.addentry( -1, true, 'e', _( "Consume it" ) ); - actions.emplace_back( [&]() { - // consume_item already consumes moves. - u.consume_item( liquid ); + actions.emplace_back( [ & ]() { + target.dest_opt = LD_CONSUME; } ); } - // This handles containers found anywhere near the player, including on the map and in vehicle storage. menu.addentry( -1, true, 'c', _( "Pour into a container" ) ); - actions.emplace_back( [&]() { - item_location target = game_menus::inv::container_for( u, liquid, radius ); - item *const cont = target.get_item(); + actions.emplace_back( [ & ]() { + target.item_loc = game_menus::inv::container_for( u, liquid, radius ); + item *const cont = target.item_loc.get_item(); if( cont == nullptr || cont->is_null() ) { add_msg( _( "Never mind." ) ); @@ -9512,30 +9497,8 @@ bool game::handle_liquid( item &liquid, item * const source, const int radius, add_msg( m_info, _( "That's the same container!" ) ); return; // The user has intended to do something, but mistyped. } - const int item_index = u.get_item_position( cont ); - // Currently activities can only store item position in the players inventory, - // not on ground or similar. TODO: implement storing arbitrary container locations. - if( item_index != INT_MIN && create_activity() ) { - serialize_liquid_target( u.activity, item_index ); - } else if( u.pour_into( *cont, liquid ) ) { - if( cont->needs_processing() ) { - // Polymorphism fail, have to introspect into the type to set the target container as active. - switch( target.where() ) { - case item_location::type::map: - m.make_active( target ); - break; - case item_location::type::vehicle: - m.veh_at( target.position() )->vehicle().make_active( target ); - break; - case item_location::type::character: - case item_location::type::invalid: - break; - } - } - u.mod_moves( -100 ); - } + target.dest_opt = LD_ITEM; } ); - // This handles liquids stored in vehicle parts directly (e.g. tanks). std::set opts; for( const auto &e : g->m.points_in_radius( g->u.pos(), 1 ) ) { @@ -9552,12 +9515,9 @@ bool game::handle_liquid( item &liquid, item * const source, const int radius, continue; } menu.addentry( -1, true, MENU_AUTOASSIGN, _( "Fill nearby vehicle %s" ), veh->name.c_str() ); - actions.emplace_back( [&, veh]() { - if( create_activity() ) { - serialize_liquid_target( u.activity, *veh ); - } else if( u.pour_into( *veh, liquid ) ) { - u.mod_moves( -1000 ); // consistent with veh_interact::do_refill activity - } + actions.emplace_back( [ &, veh]() { + target.veh = veh; + target.dest_opt = LD_VEH; } ); } @@ -9570,18 +9530,14 @@ bool game::handle_liquid( item &liquid, item * const source, const int radius, } const std::string dir = direction_name( direction_from( u.pos(), target_pos ) ); menu.addentry( -1, true, MENU_AUTOASSIGN, _( "Pour into an adjacent keg (%s)" ), dir.c_str() ); - actions.emplace_back( [&, target_pos]() { - if( create_activity() ) { - serialize_liquid_target( u.activity, target_pos ); - } else { - iexamine::pour_into_keg( target_pos, liquid ); - u.mod_moves( -100 ); - } + actions.emplace_back( [ &, target_pos ]() { + target.pos = target_pos; + target.dest_opt = LD_KEG; } ); } menu.addentry( -1, true, 'g', _( "Pour on the ground" ) ); - actions.emplace_back( [&]() { + actions.emplace_back( [ & ]() { // From infinite source to the ground somewhere else. The target has // infinite space and the liquid can not be used from there anyway. if( liquid.has_infinite_charges() && source_pos != nullptr ) { @@ -9589,31 +9545,25 @@ bool game::handle_liquid( item &liquid, item * const source, const int radius, return; } - tripoint target_pos = u.pos(); + target.pos = u.pos(); const std::string liqstr = string_format( _( "Pour %s where?" ), liquid_name.c_str() ); refresh_all(); - if( !choose_adjacent( liqstr, target_pos ) ) { + if( !choose_adjacent( liqstr, target.pos ) ) { return; } - if( source_pos != nullptr && *source_pos == target_pos ) { + if( source_pos != nullptr && *source_pos == target.pos ) { add_msg( m_info, _( "That's where you took it from!" ) ); return; } - if( !m.can_put_items_ter_furn( target_pos ) ) { + if( !m.can_put_items_ter_furn( target.pos ) ) { add_msg( m_info, _( "You can't pour there!" ) ); return; } - - if( create_activity() ) { - serialize_liquid_target( u.activity, target_pos ); - } else { - m.add_item_or_charges( target_pos, liquid ); - liquid.charges = 0; - u.mod_moves( -100 ); - } + target.dest_opt = LD_GROUND; } ); + if( liquid.rotten() ) { // Pre-select this one as it is the most likely one for rotten liquids menu.selected = menu.entries.size() - 1; @@ -9636,6 +9586,121 @@ bool game::handle_liquid( item &liquid, item * const source, const int radius, return true; } +bool game::perform_liquid_transfer( item &liquid, const tripoint * const source_pos, + const vehicle * const source_veh, + const monster * const source_mon, liquid_dest_opt &target ) +{ + bool transfer_ok = false; + if( !liquid.made_of(LIQUID) ) { + dbg(D_ERROR) << "game:handle_liquid: Tried to handle_liquid a non-liquid!"; + debugmsg("Tried to handle_liquid a non-liquid!"); + // "canceled by the user" because we *can* not handle it. + return transfer_ok; + } + + const auto create_activity = [&]() { + if( source_veh != nullptr ) { + u.assign_activity( activity_id( "ACT_FILL_LIQUID" ) ); + serialize_liquid_source( u.activity, *source_veh, liquid.typeId() ); + return true; + } else if( source_pos != nullptr ) { + u.assign_activity( activity_id( "ACT_FILL_LIQUID" ) ); + serialize_liquid_source( u.activity, *source_pos, liquid ); + return true; + } else if( source_mon != nullptr) { + return false; + } else { + return false; + } + }; + + switch( target.dest_opt ) { + case LD_CONSUME: + u.consume_item( liquid ); + transfer_ok = true; + break; + case LD_ITEM: { + item *const cont = target.item_loc.get_item(); + const int item_index = u.get_item_position( cont ); + // Currently activities can only store item position in the players inventory, + // not on ground or similar. TODO: implement storing arbitrary container locations. + if( item_index != INT_MIN && create_activity() ) { + serialize_liquid_target( u.activity, item_index ); + } else if( u.pour_into( *cont, liquid ) ) { + if( cont->needs_processing() ) { + // Polymorphism fail, have to introspect into the type to set the target container as active. + switch( target.item_loc.where() ) { + case item_location::type::map: + m.make_active( target.item_loc ); + break; + case item_location::type::vehicle: + m.veh_at( target.item_loc.position() )->vehicle().make_active( target.item_loc ); + break; + case item_location::type::character: + case item_location::type::invalid: + break; + } + } + u.mod_moves( -100 ); + } + transfer_ok = true; + break; + } + case LD_VEH: + if( target.veh == nullptr ) { + break; + } + if( create_activity() ) { + serialize_liquid_target( u.activity, *target.veh ); + } else if( u.pour_into( *target.veh, liquid ) ) { + u.mod_moves( -1000 ); // consistent with veh_interact::do_refill activity + } + transfer_ok = true; + break; + case LD_KEG: + case LD_GROUND: + add_msg( "filling a %s", target.dest_opt == LD_KEG ? "keg" : "ground" ); + if( create_activity() ) { + add_msg( "created a an activity" ); + serialize_liquid_target( u.activity, target.pos ); + } else { + if( target.dest_opt == LD_KEG ) { + add_msg( "calling iexamine" ); + iexamine::pour_into_keg( target.pos, liquid ); + } else { + add_msg( "calling add_item" ); + m.add_item_or_charges( target.pos, liquid ); + liquid.charges = 0; + } + u.mod_moves( -100 ); + } + transfer_ok = true; + break; + case LD_NULL: + default: + break; + } + return transfer_ok; +} + +bool game::handle_liquid( item &liquid, item * const source, const int radius, + const tripoint * const source_pos, + const vehicle * const source_veh, + const monster * const source_mon) +{ + if( !liquid.made_of(LIQUID) ) { + dbg(D_ERROR) << "game:handle_liquid: Tried to handle_liquid a non-liquid!"; + debugmsg("Tried to handle_liquid a non-liquid!"); + // "canceled by the user" because we *can* not handle it. + return false; + } + struct liquid_dest_opt liquid_target; + if( get_liquid_target( liquid, source, radius, source_pos, source_veh, source_mon, liquid_target ) ) { + return perform_liquid_transfer( liquid, source_pos, source_veh, source_mon, liquid_target ); + } + return false; +} + void game::drop( int pos, const tripoint &where ) { if( pos != INT_MIN ) { diff --git a/src/game.h b/src/game.h index bfd7f44366ac6..027a032ffbdfa 100644 --- a/src/game.h +++ b/src/game.h @@ -9,6 +9,7 @@ #include "int_id.h" #include "cursesdef.h" #include "pimpl.h" +#include "item_location.h" #include #include @@ -118,6 +119,22 @@ class loading_ui; typedef std::function item_filter; +enum liquid_dest : int { + LD_NULL, + LD_CONSUME, + LD_ITEM, + LD_VEH, + LD_KEG, + LD_GROUND +}; + +struct liquid_dest_opt { + liquid_dest dest_opt = LD_NULL; + item_location item_loc; + vehicle *veh = nullptr; + tripoint pos; +}; + class game { friend class editmap; @@ -748,6 +765,18 @@ class game const tripoint *source_pos = nullptr, const vehicle *source_veh = nullptr, const monster *source_mon = nullptr ); + /** + * These are helper functions for transfer liquid, for times when you just want to + * get the target of the transfer, or know the target and just want to transfer the + * liquid. They take the same arguments as handle_liquid, plus + * @param liquid_target structure containing information about the target + */ + bool get_liquid_target( item &liquid, item *const source, const int radius, + const tripoint *source_pos, const vehicle *const source_veh, + const monster *const source_mon, liquid_dest_opt &target ); + bool perform_liquid_transfer( item &liquid, + const tripoint *source_pos, const vehicle *const source_veh, + const monster *const source_mon, liquid_dest_opt &target ); /**@}*/ void open_gate( const tripoint &p ); From 21614747f5b23ce4c24bfdbba6725b177d51bc06 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Tue, 7 Aug 2018 16:51:09 -0500 Subject: [PATCH 2/2] game: remove debug messages from handle_liquid refactor oops. --- src/game.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index a43d117495e98..7a58042242923 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9659,16 +9659,12 @@ bool game::perform_liquid_transfer( item &liquid, const tripoint * const source_ break; case LD_KEG: case LD_GROUND: - add_msg( "filling a %s", target.dest_opt == LD_KEG ? "keg" : "ground" ); if( create_activity() ) { - add_msg( "created a an activity" ); serialize_liquid_target( u.activity, target.pos ); } else { if( target.dest_opt == LD_KEG ) { - add_msg( "calling iexamine" ); iexamine::pour_into_keg( target.pos, liquid ); } else { - add_msg( "calling add_item" ); m.add_item_or_charges( target.pos, liquid ); liquid.charges = 0; }