Skip to content

Commit

Permalink
add separate item_location handling path to liquid_handler and use it…
Browse files Browse the repository at this point in the history
… where possible, revert item reference uses to create consume activity from that instead of faulty item_locations
  • Loading branch information
mqrause committed Oct 5, 2024
1 parent 0c8023e commit 20f8ea8
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 135 deletions.
282 changes: 183 additions & 99 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;

switch( liquid.where() ) {
case item_location::type::container:
// intentionally empty
break;
case item_location::type::map: {
tripoint 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 @@ -375,76 +491,20 @@ bool perform_liquid_transfer( item &liquid, const tripoint *const source_pos,
};

map &here = get_map();

Check failure on line 493 in src/handle_liquid.cpp

View workflow job for this annotation

GitHub Actions / build (src)

unused variable 'here' [clang-diagnostic-unused-variable,-warnings-as-errors]

Check failure on line 493 in src/handle_liquid.cpp

View workflow job for this annotation

GitHub Actions / Basic Build and Test (Clang 10, Ubuntu, Curses)

unused variable 'here' [-Werror,-Wunused-variable]
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 +528,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 +548,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 &loc, liquid_dest_opt &target );

Check failure on line 100 in src/handle_liquid.h

View workflow job for this annotation

GitHub Actions / build (src)

function 'liquid_handler::perform_liquid_transfer' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name,-warnings-as-errors]
} // namespace liquid_handler

#endif // CATA_SRC_HANDLE_LIQUID_H
Loading

0 comments on commit 20f8ea8

Please sign in to comment.