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

Fix consume activity creation from liquid handler #76819

Merged
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
283 changes: 183 additions & 100 deletions src/handle_liquid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,48 +124,19 @@ bool consume_liquid( item &liquid, const int radius, const item *const avoid )
return original_charges != liquid.charges;
}

bool handle_liquid_from_ground( const map_stack::iterator &on_ground,
const tripoint &pos,
const int radius )
bool handle_all_liquids_from_container( item_location &container, int radius )
{
// TODO: not all code paths on handle_liquid consume move points, fix that.
handle_liquid( *on_ground, nullptr, radius, &pos );
if( on_ground->charges > 0 ) {
return false;
}
get_map().i_at( pos ).erase( on_ground );
return true;
}

bool handle_liquid_from_container( item *in_container,
item &container, int radius )
{
// TODO: not all code paths on handle_liquid consume move points, fix that.
const int old_charges = in_container->charges;
handle_liquid( *in_container, &container, radius );
if( in_container->charges != old_charges ) {
container.on_contents_changed();
}

return in_container->charges <= 0;
}

bool handle_liquid_from_container( item &container, int radius )
{
std::vector<item *> remove;
bool handled = false;
for( item *contained : container.all_items_top() ) {
if( handle_liquid_from_container( contained, container, radius ) ) {
remove.push_back( contained );
for( item *contained : container->all_items_top() ) {
item_location loc( container, contained );
if( handle_liquid( loc, &*container, radius ) ) {
handled = true;
}
}
for( item *contained : remove ) {
container.remove_item( *contained );
}
return handled;
}

// todo: remove in favor of the item_location version
static bool get_liquid_target( item &liquid, const item *const source, const int radius,
const tripoint *const source_pos,
const vehicle *const source_veh,
Expand Down Expand Up @@ -348,13 +319,158 @@ static bool get_liquid_target( item &liquid, const item *const source, const int
return true;
}

bool perform_liquid_transfer( item &liquid, const tripoint *const source_pos,
vehicle *const source_veh, const int part_num,
const monster *const /*source_mon*/, liquid_dest_opt &target )
static bool get_liquid_target( item_location &liquid, const item *const source, const int radius,
liquid_dest_opt &target )
{
const tripoint *source_pos = nullptr;
const vehicle *source_veh = nullptr;
const monster *source_mon = nullptr;

tripoint pos;
switch( liquid.where() ) {
case item_location::type::container:
// intentionally empty
break;
case item_location::type::map:
pos = liquid.position();
source_pos = &pos;
break;
case item_location::type::vehicle:
source_veh = &liquid.veh_cursor()->veh;
break;
default:
debugmsg( "Tried to get liquid target %s from invalid location %s", liquid->display_name(),
liquid.where() );
return false;
}

return get_liquid_target( *liquid, source, radius, source_pos, source_veh, source_mon, target );
}

static bool handle_keg_or_ground_target( Character &player_character, item &liquid,
liquid_dest_opt &target, const std::function<bool()> &create_activity )
{
if( create_activity() ) {
serialize_liquid_target( player_character.activity, target.pos );
} else {
if( target.dest_opt == LD_KEG ) {
iexamine::pour_into_keg( tripoint_bub_ms( target.pos ), liquid );
} else {
get_map().add_item_or_charges( target.pos, liquid );
liquid.charges = 0;
}
player_character.mod_moves( -100 );
}
return true;
}

static bool handle_item_target( Character &player_character, item &liquid, liquid_dest_opt &target,
const std::function<bool()> &create_activity )
{
// Currently activities can only store item position in the players inventory,
// not on ground or similar. TODO: implement storing arbitrary container locations.
if( target.item_loc && create_activity() ) {
serialize_liquid_target( player_character.activity, target.item_loc );
} else if( player_character.pour_into( target.item_loc, liquid, true ) ) {
target.item_loc.make_active();
player_character.mod_moves( -100 );
}
return true;
}

static bool handle_vehicle_target( Character &player_character, item &liquid,
liquid_dest_opt &target, const std::function<bool()> &create_activity )
{
if( target.veh == nullptr ) {
return false;
}
auto sel = [&]( const vehicle_part & pt ) {
return pt.is_tank() && pt.can_reload( liquid );
};

const units::volume stack = units::legacy_volume_factor / liquid.type->stack_size;
const std::string title = string_format( _( "Select target tank for <color_%s>%.1fL %s</color>" ),
get_all_colors().get_name( liquid.color() ),
round_up( to_liter( liquid.charges * stack ), 1 ),
liquid.tname() );

const std::optional<vpart_reference> vpr = veh_interact::select_part( *target.veh, sel, title );
if( !vpr ) {
return false;
}

if( create_activity() ) {
serialize_liquid_target( player_character.activity, *vpr );
return true;
} else if( player_character.pour_into( *vpr, liquid ) ) {
// this branch is used in milking and magiclysm butchery blood draining
player_character.mod_moves( -1000 ); // consistent with veh_interact::do_refill activity
return true;
} else {
// unclear what can reach this branch but return false just in case
return false;
}
}

static bool check_liquid( item &liquid )
{
if( !liquid.made_of_from_type( phase_id::LIQUID ) ) {
dbg( D_ERROR ) << "game:handle_liquid: Tried to handle_liquid a non-liquid!";
debugmsg( "Tried to handle_liquid a non-liquid!" );
return false;
}
return true;
}

bool perform_liquid_transfer( item_location &liquid, liquid_dest_opt &target )
{
if( !check_liquid( *liquid ) ) {
// "canceled by the user" because we *can* not handle it.
return false;
}

// todo: migrate ACT_FILL_LIQUID to activity_actor
Character &player_character = get_player_character();
const auto create_activity = [&]() {
if( liquid.where() == item_location::type::vehicle ) {
player_character.assign_activity( ACT_FILL_LIQUID );
const vehicle_cursor *veh_cur = liquid.veh_cursor();
serialize_liquid_source( player_character.activity, veh_cur->veh, veh_cur->part, *liquid );
return true;
} else if( liquid.where() == item_location::type::map ) {
player_character.assign_activity( ACT_FILL_LIQUID );
serialize_liquid_source( player_character.activity, liquid.position(), *liquid );
return true;
} else {
return false;
}
};

switch( target.dest_opt ) {
case LD_CONSUME:
player_character.assign_activity( consume_activity_actor( liquid ) );
return true;
case LD_ITEM: {
return handle_item_target( player_character, *liquid, target, create_activity );
}
case LD_VEH: {
return handle_vehicle_target( player_character, *liquid, target, create_activity );
}
case LD_KEG:
case LD_GROUND:
return handle_keg_or_ground_target( player_character, *liquid, target, create_activity );
case LD_NULL:
default:
return false;
}
}

// todo: Remove in favor of the item_location version.
bool perform_liquid_transfer( item &liquid, const tripoint *const source_pos,
const vehicle *const source_veh, const int part_num,
const monster *const /*source_mon*/, liquid_dest_opt &target )
{
if( !check_liquid( liquid ) ) {
// "canceled by the user" because we *can* not handle it.
return false;
}
Expand All @@ -374,77 +490,20 @@ bool perform_liquid_transfer( item &liquid, const tripoint *const source_pos,
}
};

map &here = get_map();
item_location liquid_loc;
switch( target.dest_opt ) {
case LD_CONSUME:
if( source_pos ) {
liquid_loc = item_location( map_cursor( tripoint_bub_ms( *source_pos ) ), &liquid );
} else if( source_veh ) {
liquid_loc = item_location( vehicle_cursor( *source_veh, part_num ), &liquid );
} else {
player_character.assign_activity( consume_activity_actor( liquid ) );
return true;
}

player_character.assign_activity( consume_activity_actor( liquid_loc ) );
player_character.assign_activity( consume_activity_actor( liquid ) );
liquid.charges--;
return true;
case LD_ITEM: {
// Currently activities can only store item position in the players inventory,
// not on ground or similar. TODO: implement storing arbitrary container locations.
if( target.item_loc && create_activity() ) {
serialize_liquid_target( player_character.activity, target.item_loc );
} else if( player_character.pour_into( target.item_loc, liquid, true ) ) {
target.item_loc.make_active();
player_character.mod_moves( -100 );
}
return true;
return handle_item_target( player_character, liquid, target, create_activity );
}
case LD_VEH: {
if( target.veh == nullptr ) {
return false;
}
auto sel = [&]( const vehicle_part & pt ) {
return pt.is_tank() && pt.can_reload( liquid );
};

const units::volume stack = units::legacy_volume_factor / liquid.type->stack_size;
const std::string title = string_format( _( "Select target tank for <color_%s>%.1fL %s</color>" ),
get_all_colors().get_name( liquid.color() ),
round_up( to_liter( liquid.charges * stack ), 1 ),
liquid.tname() );

const std::optional<vpart_reference> vpr = veh_interact::select_part( *target.veh, sel, title );
if( !vpr ) {
return false;
}

if( create_activity() ) {
serialize_liquid_target( player_character.activity, *vpr );
return true;
} else if( player_character.pour_into( *vpr, liquid ) ) {
// this branch is used in milking and magiclysm butchery blood draining
player_character.mod_moves( -1000 ); // consistent with veh_interact::do_refill activity
return true;
} else {
// unclear what can reach this branch but return false just in case
return false;
}
return handle_vehicle_target( player_character, liquid, target, create_activity );
}
case LD_KEG:
case LD_GROUND:
if( create_activity() ) {
serialize_liquid_target( player_character.activity, target.pos );
} else {
if( target.dest_opt == LD_KEG ) {
iexamine::pour_into_keg( tripoint_bub_ms( target.pos ), liquid );
} else {
here.add_item_or_charges( target.pos, liquid );
liquid.charges = 0;
}
player_character.mod_moves( -100 );
}
return true;
return handle_keg_or_ground_target( player_character, liquid, target, create_activity );
case LD_NULL:
default:
return false;
Expand All @@ -468,7 +527,7 @@ bool can_handle_liquid( const item &liquid )

bool handle_liquid( item &liquid, const item *const source, const int radius,
const tripoint *const source_pos,
vehicle *const source_veh, const int part_num,
const vehicle *const source_veh, const int part_num,
const monster *const source_mon )
{
bool success = false;
Expand All @@ -488,4 +547,28 @@ bool handle_liquid( item &liquid, const item *const source, const int radius,
}
return false;
}

bool handle_liquid( item_location &liquid, const item *const source, const int radius )
{
bool success = false;
if( !can_handle_liquid( *liquid ) ) {
return false;
}
struct liquid_dest_opt liquid_target;
if( get_liquid_target( liquid, source, radius, liquid_target ) ) {
success = perform_liquid_transfer( liquid, liquid_target );
if( success && ( ( liquid_target.dest_opt == LD_ITEM &&
liquid_target.item_loc->is_watertight_container() ) || liquid_target.dest_opt == LD_KEG ) ) {
liquid->unset_flag( flag_id( json_flag_FROM_FROZEN_LIQUID ) );
if( liquid.where() == item_location::type::container ) {
liquid.parent_item().on_contents_changed();
}
if( liquid->charges == 0 ) {
liquid.remove_item();
}
}
return success;
}
return false;
}
} // namespace liquid_handler
33 changes: 7 additions & 26 deletions src/handle_liquid.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,35 +57,14 @@ void handle_all_liquid( item liquid, int radius, const item *avoid = nullptr );
bool consume_liquid( item &liquid, int radius = 0, const item *avoid = nullptr );

/**
* Handle finite liquid from ground. The function also handles consuming move points.
* This may start a player activity.
* @param on_ground Iterator to the item on the ground. Must be valid and point to an
* item in the stack at `m.i_at(pos)`
* @param pos The position of the item on the map.
* Handle liquids from inside a container item. The function also handles consuming move points.
* @param container Container of the liquid
* @param radius around position to handle liquid for
* @return Whether the item has been removed (which implies it was handled completely).
* The iterator is invalidated in that case. Otherwise the item remains but may have
* fewer charges.
*/
bool handle_liquid_from_ground( const map_stack::iterator &on_ground, const tripoint &pos,
int radius = 0 );

/**
* Handle liquid from inside a container item. The function also handles consuming move points.
* @param in_container Iterator to the liquid. Must be valid and point to an
* item in the @ref item::contents of the container.
* @param container Container of the liquid
* @param radius around position to handle liquid for
* @return Whether the item has been removed (which implies it was handled completely).
* The iterator is invalidated in that case. Otherwise the item remains but may have
* fewer charges.
*/
bool handle_liquid_from_container( item *in_container, item &container,
int radius = 0 );
/**
* Shortcut to the above: handles the first item in the container.
*/
bool handle_liquid_from_container( item &container, int radius = 0 );
bool handle_all_liquids_from_container( item_location &container, int radius = 0 );

bool can_handle_liquid( const item &liquid );

Expand All @@ -110,13 +89,15 @@ bool can_handle_liquid( const item &liquid );
*/
bool handle_liquid( item &liquid, const item *source = nullptr, int radius = 0,
const tripoint *source_pos = nullptr,
vehicle *source_veh = nullptr, int part_num = -1,
const vehicle *source_veh = nullptr, int part_num = -1,
const monster *source_mon = nullptr );
bool handle_liquid( item_location &liquid, const item *source = nullptr, int radius = 0 );

/* Not to be used directly. Use liquid_handler::handle_liquid instead. */
bool perform_liquid_transfer( item &liquid, const tripoint *source_pos,
vehicle *source_veh, int part_num,
const vehicle *source_veh, int part_num,
const monster * /*source_mon*/, liquid_dest_opt &target );
bool perform_liquid_transfer( item_location &liquid, liquid_dest_opt &target );
} // namespace liquid_handler

#endif // CATA_SRC_HANDLE_LIQUID_H
Loading
Loading