Skip to content

Commit

Permalink
kind of working
Browse files Browse the repository at this point in the history
  • Loading branch information
bombasticSlacks committed Jul 27, 2022
1 parent f3ced37 commit c76090d
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 272 deletions.
3 changes: 2 additions & 1 deletion data/json/player_activities.json
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,8 @@
"type": "activity_type",
"activity_level": "MODERATE_EXERCISE",
"verb": "unloading loot",
"based_on": "neither"
"based_on": "neither",
"multi_activity": true
},
{
"id": "ACT_FETCH_REQUIRED",
Expand Down
328 changes: 328 additions & 0 deletions src/activity_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ static const activity_id ACT_TENT_DECONSTRUCT( "ACT_TENT_DECONSTRUCT" );
static const activity_id ACT_TENT_PLACE( "ACT_TENT_PLACE" );
static const activity_id ACT_TRY_SLEEP( "ACT_TRY_SLEEP" );
static const activity_id ACT_UNLOAD( "ACT_UNLOAD" );
static const activity_id ACT_UNLOAD_LOOT( "ACT_UNLOAD_LOOT" );
static const activity_id ACT_WEAR( "ACT_WEAR" );
static const activity_id ACT_WIELD( "ACT_WIELD" );
static const activity_id ACT_WORKOUT_ACTIVE( "ACT_WORKOUT_ACTIVE" );
Expand Down Expand Up @@ -208,6 +209,12 @@ static const ter_str_id ter_t_underbrush_harvested_winter( "t_underbrush_harvest

static const trait_id trait_SCHIZOPHRENIC( "SCHIZOPHRENIC" );

static const zone_type_id zone_type_LOOT_IGNORE( "LOOT_IGNORE" );
static const zone_type_id zone_type_LOOT_IGNORE_FAVORITES( "LOOT_IGNORE_FAVORITES" );
static const zone_type_id zone_type_zone_disassemble( "zone_disassemble" );
static const zone_type_id zone_type_zone_strip( "zone_strip" );
static const zone_type_id zone_type_zone_unload_all( "zone_unload_all" );

std::string activity_actor::get_progress_message( const player_activity &act ) const
{
if( act.moves_total > 0 ) {
Expand Down Expand Up @@ -6004,6 +6011,326 @@ std::unique_ptr<activity_actor> mop_activity_actor::deserialize( JsonValue &jsin
return actor.clone();
}


void unload_loot_activity_actor::serialize( JsonOut &jsout ) const
{
jsout.start_object();

jsout.member( "moves", moves );

jsout.end_object();
}

std::unique_ptr<activity_actor> unload_loot_activity_actor::deserialize( JsonValue &jsin )
{
unload_loot_activity_actor actor( {} );

JsonObject data = jsin.get_object();

data.read( "moves", actor.moves );

return actor.clone();
}

void unload_loot_activity_actor::start( player_activity &act, Character & )
{
act.moves_total = moves;
act.moves_left = moves;
}

void unload_loot_activity_actor::finish( player_activity &act, Character &who )
{
act.set_to_null();
}

static void move_item( Character &you, item &it, const int quantity, const tripoint &src,
const tripoint &dest, vehicle *src_veh, int src_part )
{
item leftovers = it;

if( quantity != 0 && it.count_by_charges() ) {
// Reinserting leftovers happens after item removal to avoid stacking issues.
leftovers.charges = it.charges - quantity;
if( leftovers.charges > 0 ) {
it.charges = quantity;
}
} else {
leftovers.charges = 0;
}

map &here = get_map();
// Check that we can pick it up.
if( !it.made_of_from_type( phase_id::LIQUID ) ) {
you.mod_moves( -activity_handlers::move_cost( it, src, dest ) );

put_into_vehicle_or_drop( you, item_drop_reason::deliberate, { it }, dest );
// Remove from map or vehicle.
if( src_veh ) {
src_veh->remove_item( src_part, &it );
} else {
here.i_rem( src, &it );
}
}

// If we didn't pick up a whole stack, put the remainder back where it came from.
if( leftovers.charges > 0 ) {
if( src_veh ) {
if( !src_veh->add_item( src_part, leftovers ) ) {
debugmsg( "SortLoot: Source vehicle failed to receive leftover charges." );
}
} else {
here.add_item_or_charges( src, leftovers );
}
}
}

void unload_loot_activity_actor::do_turn( player_activity &act, Character &you )
{
enum activity_stage : int {
//Initial stage
INIT = 0,
//Think about what to do first: choose destination
THINK,
//Do activity
DO,
};

faction const *fac = you.get_faction();
faction_id fac_id = fac == nullptr ? faction_id() : fac->id;

map &here = get_map();
const tripoint_abs_ms abspos = you.get_location();
zone_manager &mgr = zone_manager::get_manager();
if( here.check_vehicle_zones( here.get_abs_sub().z() ) ) {
mgr.cache_vzones();
}

if( stage == INIT ) {
// TODO: fix point types
coord_set.clear();
for( const tripoint_abs_ms &p :
mgr.get_near( zone_type_zone_unload_all, abspos, ACTIVITY_SEARCH_DISTANCE, nullptr,
fac_id ) ) {
coord_set.insert( p.raw() );
}

for( const tripoint_abs_ms &p :
mgr.get_near( zone_type_zone_strip, abspos, ACTIVITY_SEARCH_DISTANCE, nullptr,
fac_id ) ) {
coord_set.insert( p.raw() );
}
stage = THINK;
}

if( stage == THINK ) {
//initialize num_processed
num_processed = 0;
// TODO: fix point types
std::vector<tripoint_abs_ms> src_set;
for( const tripoint &p : coord_set ) {
src_set.emplace_back( tripoint_abs_ms( p ) );
}
// sort source tiles by distance
const auto &src_sorted = get_sorted_tiles_by_distance( abspos, src_set );

for( const tripoint_abs_ms &src : src_sorted ) {
// TODO: fix point types
placement = src.raw();
coord_set.erase( src.raw() );

const tripoint &src_loc = here.getlocal( src );
if( !here.inbounds( src_loc ) ) {
if( !here.inbounds( you.pos() ) ) {
// p is implicitly an NPC that has been moved off the map, so reset the activity
// and unload them
you.cancel_activity();
you.assign_activity( ACT_UNLOAD_LOOT );
you.set_moves( 0 );
g->reload_npcs();
return;
}
std::vector<tripoint> route;
route = here.route( you.pos(), src_loc, you.get_pathfinding_settings(),
you.get_path_avoid() );
if( route.empty() ) {
// can't get there, can't do anything, skip it
continue;
}
stage = DO;
you.set_destination( route, act );
you.activity.set_to_null();
return;
}

// skip tiles in IGNORE zone and tiles on fire
// (to prevent taking out wood off the lit brazier)
// and inaccessible furniture, like filled charcoal kiln
if( mgr.has( zone_type_LOOT_IGNORE, src, fac_id ) ||
here.get_field( src_loc, fd_fire ) != nullptr ||
!here.can_put_items_ter_furn( src_loc ) ) {
continue;
}

//nothing to sort?
const cata::optional<vpart_reference> vp = here.veh_at( src_loc ).part_with_feature( "CARGO",
false );
if( ( !vp || vp->vehicle().get_items( vp->part_index() ).empty() )
&& here.i_at( src_loc ).empty() ) {
continue;
}

bool is_adjacent_or_closer = square_dist( you.pos(), src_loc ) <= 1;
// before we unload any item, check if player is at or
// adjacent to the loot source tile
if( !is_adjacent_or_closer ) {
std::vector<tripoint> route;
bool adjacent = false;

// get either direct route or route to nearest adjacent tile if
// source tile is impassable
if( here.passable( src_loc ) ) {
route = here.route( you.pos(), src_loc, you.get_pathfinding_settings(),
you.get_path_avoid() );
} else {
// impassable source tile (locker etc.),
// get route to nearest adjacent tile instead
route = route_adjacent( you, src_loc );
adjacent = true;
}

// check if we found path to source / adjacent tile
if( route.empty() ) {
add_msg( m_info, _( "%s can't reach the source tile." ),
you.disp_name() );
continue;
}

// shorten the route to adjacent tile, if necessary
if( !adjacent ) {
route.pop_back();
}

// set the destination and restart activity after player arrives there
// we don't need to check for safe mode,
// activity will be restarted only if
// player arrives on destination tile
stage = DO;
you.set_destination( route, act );
you.activity.set_to_null();
return;
}
stage = DO;
break;
}
}
if( stage == DO ) {
// TODO: fix point types
const tripoint_abs_ms src( placement );
const tripoint src_loc = here.getlocal( src );

bool is_adjacent_or_closer = square_dist( you.pos(), src_loc ) <= 1;
// before we move any item, check if player is at or
// adjacent to the loot source tile
if( !is_adjacent_or_closer ) {
stage = THINK;
return;
}

// the boolean in this pair being true indicates the item is from a vehicle storage space
auto items = std::vector<std::pair<item *, bool>>();
vehicle *src_veh;
int src_part;

//Check source for cargo part
//map_stack and vehicle_stack are different types but inherit from item_stack
// TODO: use one for loop
if( const cata::optional<vpart_reference> vp = here.veh_at( src_loc ).part_with_feature( "CARGO",
false ) ) {
src_veh = &vp->vehicle();
src_part = vp->part_index();
for( item &it : src_veh->get_items( src_part ) ) {
items.emplace_back( &it, true );
}
} else {
src_veh = nullptr;
src_part = -1;
}
for( item &it : here.i_at( src_loc ) ) {
items.emplace_back( &it, false );
}

//Skip items that have already been processed
for( auto it = items.begin() + num_processed; it < items.end(); ++it ) {
++num_processed;
item &thisitem = *it->first;

// skip unpickable liquid
if( thisitem.made_of_from_type( phase_id::LIQUID ) ) {
continue;
}

// skip favorite items in ignore favorite zones
if( thisitem.is_favorite && mgr.has( zone_type_LOOT_IGNORE_FAVORITES, src, fac_id ) ) {
continue;
}

// Only if it's from a vehicle do we use the vehicle source location information.
vehicle *this_veh = it->second ? src_veh : nullptr;
const int this_part = it->second ? src_part : -1;

// if this item isn't going anywhere and its not sealed
// check if it is in a unload zone or a strip corpse zone
// then we should unload it and see what is inside
if( mgr.has_near( zone_type_zone_unload_all, abspos, 1, fac_id ) ||
( mgr.has_near( zone_type_zone_strip, abspos, 1, fac_id ) && it->first->is_corpse() ) ) {
if( you.rate_action_unload( *it->first ) == hint_rating::good &&
!it->first->any_pockets_sealed() ) {
for( item *contained : it->first->all_items_top( item_pocket::pocket_type::CONTAINER ) ) {
// no liquids don't want to spill stuff
if( !contained->made_of( phase_id::LIQUID ) && !contained->made_of( phase_id::GAS ) ) {
move_item( you, *contained, contained->count(), src_loc, src_loc, this_veh, this_part );
it->first->remove_item( *contained );
}
}
for( item *contained : it->first->all_items_top( item_pocket::pocket_type::MAGAZINE ) ) {
// no liquids don't want to spill stuff
if( !contained->made_of( phase_id::LIQUID ) && !contained->made_of( phase_id::GAS ) ) {
move_item( you, *contained, contained->count(), src_loc, src_loc, this_veh, this_part );
it->first->remove_item( *contained );
}
}
for( item *contained : it->first->all_items_top( item_pocket::pocket_type::MAGAZINE_WELL ) ) {
// no liquids don't want to spill stuff
if( !contained->made_of( phase_id::LIQUID ) && !contained->made_of( phase_id::GAS ) ) {
move_item( you, *contained, contained->count(), src_loc, src_loc, this_veh, this_part );
it->first->remove_item( *contained );
}
}
// after dumping items go back to start of activity loop
// so that can re-assess the items in the tile
return;
}
}

if( you.moves <= 0 ) {
return;
}
}

//this location is sorted
stage = THINK;
return;
}

// If we got here without restarting the activity, it means we're done
add_msg( m_info, _( "%s sorted out every item possible." ), you.disp_name( false, true ) );
if( you.is_npc() ) {
npc *guy = dynamic_cast<npc *>( &you );
guy->revert_after_activity();
}
act.set_to_null();
}

namespace activity_actors
{

Expand Down Expand Up @@ -6061,6 +6388,7 @@ deserialize_functions = {
{ ACT_TENT_PLACE, &tent_placement_activity_actor::deserialize },
{ ACT_TRY_SLEEP, &try_sleep_activity_actor::deserialize },
{ ACT_UNLOAD, &unload_activity_actor::deserialize },
{ ACT_UNLOAD_LOOT, &unload_loot_activity_actor::deserialize },
{ ACT_WEAR, &wear_activity_actor::deserialize },
{ ACT_WIELD, &wield_activity_actor::deserialize},
{ ACT_WORKOUT_ACTIVE, &workout_activity_actor::deserialize },
Expand Down
29 changes: 29 additions & 0 deletions src/activity_actor_definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -1843,4 +1843,33 @@ class mop_activity_actor : public activity_actor
int moves;
};

class unload_loot_activity_actor : public activity_actor
{
public:
unload_loot_activity_actor() = default;
explicit unload_loot_activity_actor( int moves ) : moves( moves ) {}

activity_id get_type() const override {
return activity_id( "ACT_UNLOAD_LOOT" );
}

void start( player_activity &act, Character &who ) override;
void do_turn( player_activity &act, Character &you ) override;
void finish( player_activity &act, Character &who ) override;

std::unique_ptr<activity_actor> clone() const override {
return std::make_unique<unload_loot_activity_actor>( *this );
}

void serialize( JsonOut &jsout ) const override;
static std::unique_ptr<activity_actor> deserialize( JsonValue &jsin );

private:
int moves;
int num_processed;
int stage;
std::unordered_set<tripoint> coord_set;
tripoint placement;
};

#endif // CATA_SRC_ACTIVITY_ACTOR_DEFINITIONS_H
Loading

0 comments on commit c76090d

Please sign in to comment.