Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

game: refactor handle_liquids() #24684

Merged
merged 2 commits into from
Aug 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 135 additions & 74 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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!";
Expand All @@ -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 ) {
Expand All @@ -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." ) );
Expand All @@ -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<vehicle *> opts;
for( const auto &e : g->m.points_in_radius( g->u.pos(), 1 ) ) {
Expand All @@ -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;
} );
}

Expand All @@ -9570,50 +9530,40 @@ 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 ) {
add_msg( m_info, _( "Clearing out the %s would take forever." ), m.name( *source_pos ).c_str() );
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;
Expand All @@ -9636,6 +9586,117 @@ 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:
if( create_activity() ) {
serialize_liquid_target( u.activity, target.pos );
} else {
if( target.dest_opt == LD_KEG ) {
iexamine::pour_into_keg( target.pos, liquid );
} else {
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 ) {
Expand Down
29 changes: 29 additions & 0 deletions src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "int_id.h"
#include "cursesdef.h"
#include "pimpl.h"
#include "item_location.h"

#include <array>
#include <vector>
Expand Down Expand Up @@ -118,6 +119,22 @@ class loading_ui;

typedef std::function<bool( const item & )> 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;
Expand Down Expand Up @@ -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 );
Expand Down