diff --git a/data/json/player_activities.json b/data/json/player_activities.json index e473c9e8b1541..cc46fd9d7c43a 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -333,9 +333,7 @@ "type": "activity_type", "activity_level": "MODERATE_EXERCISE", "verb": "sorting out the loot", - "suspendable": false, - "based_on": "neither", - "no_resume": true + "based_on": "neither" }, { "id": "ACT_TILL_PLOT", diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index 7d002830733df..33046c00e1560 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -1947,64 +1947,145 @@ static bool chop_plank_activity( player &p, tripoint src_loc ) return false; } -void activity_on_turn_move_loot( player_activity &, player &p ) +void activity_on_turn_move_loot( player_activity &act, player &p ) { - const activity_id act_move_loot = activity_id( "ACT_MOVE_LOOT" ); + const int search_range = 60; + + enum activity_stage : int { + INIT = 0, //Initial stage + THINK, //Think about what to do first: choose destination + DO, //Do activity + }; + + int &stage = act.index; + //Prepare activity stage + if( stage < 0 ) { + stage = INIT; + act.values.push_back( 0 ); //num_processed + } + int &num_processed = act.values[ 0 ]; + + const auto abspos = g->m.getabs( p.pos() ); auto &mgr = zone_manager::get_manager(); if( g->m.check_vehicle_zones( g->get_levz() ) ) { mgr.cache_vzones(); } - const auto abspos = g->m.getabs( p.pos() ); - const auto &src_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos ); - vehicle *src_veh, *dest_veh; - int src_part, dest_part; - // Nuke the current activity, leaving the backlog alone. - p.activity = player_activity(); + if( stage == INIT ) { + act.coord_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos, search_range ); + stage = THINK; + } - // sort source tiles by distance - const auto &src_sorted = get_sorted_tiles_by_distance( abspos, src_set ); + if( stage == THINK ) { + num_processed = 0; //initialize num_processed + const auto &src_set = act.coord_set; + // sort source tiles by distance + const auto &src_sorted = get_sorted_tiles_by_distance( abspos, src_set ); - if( !mgr.is_sorting() ) { - mgr.start_sort( src_sorted ); - } + for( auto &src : src_sorted ) { + act.placement = src; + act.coord_set.erase( src ); - for( auto &src : src_sorted ) { - const auto &src_loc = g->m.getlocal( src ); - if( !g->m.inbounds( src_loc ) ) { - if( !g->m.inbounds( p.pos() ) ) { - // p is implicitly an NPC that has been moved off the map, so reset the activity - // and unload them - p.assign_activity( act_move_loot ); - p.set_moves( 0 ); - g->reload_npcs(); - mgr.end_sort(); + const auto &src_loc = g->m.getlocal( src ); + if( !g->m.inbounds( src_loc ) ) { + if( !g->m.inbounds( p.pos() ) ) { + // p is implicitly an NPC that has been moved off the map, so reset the activity + // and unload them + p.cancel_activity(); + p.assign_activity( activity_id( "ACT_MOVE_LOOT" ) ); + p.set_moves( 0 ); + g->reload_npcs(); + return; + } + std::vector route; + route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), + p.get_path_avoid() ); + if( route.empty() ) { + // can't get there, can't do anything, skip it + continue; + } + stage = DO; + p.set_destination( route, act ); + p.activity.set_to_null(); return; } - std::vector route; - route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), - p.get_path_avoid() ); - if( route.empty() ) { - // can't get there, can't do anything, skip it + + // 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_id( "LOOT_IGNORE" ), src ) || + g->m.get_field( src_loc, fd_fire ) != nullptr || + !g->m.can_put_items_ter_furn( src_loc ) ) { continue; } - p.set_destination( route, player_activity( act_move_loot ) ); - mgr.end_sort(); - return; + + //nothing to sort? + const cata::optional vp = g->m.veh_at( src_loc ).part_with_feature( "CARGO", + false ); + if( !vp && g->m.i_at( src_loc ).empty( ) ) { + continue; + } + + bool is_adjacent_or_closer = square_dist( p.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 ) { + std::vector route; + bool adjacent = false; + + // get either direct route or route to nearest adjacent tile if + // source tile is impassable + if( g->m.passable( src_loc ) ) { + route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), + p.get_path_avoid() ); + } else { + // immpassable source tile (locker etc.), + // get route to nerest adjacent tile instead + route = route_adjacent( p, 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. Try to sort out loot without a cart." ), + p.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; + p.set_destination( route, act ); + p.activity.set_to_null(); + return; + } + stage = DO; + break; } + } + if( stage == DO ) { + const tripoint &src = act.placement; + const tripoint &src_loc = g->m.getlocal( src ); bool is_adjacent_or_closer = square_dist( p.pos(), src_loc ) <= 1; - // 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_id( "LOOT_IGNORE" ), src ) || - g->m.get_field( src_loc, fd_fire ) != nullptr || - !g->m.can_put_items_ter_furn( src_loc ) ) { - continue; + // 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>(); + vehicle *src_veh, *dest_veh; + int src_part, dest_part; //Check source for cargo part //map_stack and vehicle_stack are different types but inherit from item_stack @@ -2023,14 +2104,14 @@ void activity_on_turn_move_loot( player_activity &, player &p ) for( auto &it : g->m.i_at( src_loc ) ) { items.push_back( std::make_pair( &it, false ) ); } - //Skip items that have already been processed - for( auto it = items.begin() + mgr.get_num_processed( src ); it < items.end(); it++ ) { - mgr.increment_num_processed( src ); - - const auto thisitem = it->first; + //Skip items that have already been processed + for( auto it = items.begin() + num_processed; it < items.end(); ++it ) { + ++num_processed; + item &thisitem = *it->first; - if( thisitem->made_of_from_type( LIQUID ) ) { // skip unpickable liquid + // skip unpickable liquid + if( thisitem.made_of_from_type( LIQUID ) ) { continue; } @@ -2038,96 +2119,60 @@ void activity_on_turn_move_loot( player_activity &, player &p ) vehicle *this_veh = it->second ? src_veh : nullptr; const int this_part = it->second ? src_part : -1; - const auto id = mgr.get_near_zone_type_for_item( *thisitem, abspos ); + const zone_type_id id = mgr.get_near_zone_type_for_item( thisitem, abspos, search_range ); // checks whether the item is already on correct loot zone or not // if it is, we can skip such item, if not we move the item to correct pile // think empty bag on food pile, after you ate the content - if( !mgr.has( id, src ) ) { - const auto &dest_set = mgr.get_near( id, abspos, 60, thisitem ); + if( mgr.has( id, src ) ) { + continue; + } - for( auto &dest : dest_set ) { - const auto &dest_loc = g->m.getlocal( dest ); + const std::unordered_set &dest_set = mgr.get_near( id, abspos, search_range, &thisitem ); + for( const tripoint &dest : dest_set ) { + const tripoint &dest_loc = g->m.getlocal( dest ); - //Check destination for cargo part - if( const cata::optional vp = g->m.veh_at( dest_loc ).part_with_feature( "CARGO", - false ) ) { - dest_veh = &vp->vehicle(); - dest_part = vp->part_index(); - } else { - dest_veh = nullptr; - dest_part = -1; - } - - // skip tiles with inaccessible furniture, like filled charcoal kiln - if( !g->m.can_put_items_ter_furn( dest_loc ) ) { - continue; - } + //Check destination for cargo part + if( const cata::optional vp = g->m.veh_at( dest_loc ).part_with_feature( "CARGO", + false ) ) { + dest_veh = &vp->vehicle(); + dest_part = vp->part_index(); + } else { + dest_veh = nullptr; + dest_part = -1; + } - units::volume free_space; - // if there's a vehicle with space do not check the tile beneath - if( dest_veh ) { - free_space = dest_veh->free_volume( dest_part ); - } else { - free_space = g->m.free_volume( dest_loc ); - } - // check free space at destination - if( free_space >= thisitem->volume() ) { - // before we move any item, check if player is at or - // adjacent to the loot source tile - if( !is_adjacent_or_closer ) { - std::vector route; - bool adjacent = false; - - // get either direct route or route to nearest adjacent tile if - // source tile is impassable - if( g->m.passable( src_loc ) ) { - route = g->m.route( p.pos(), src_loc, p.get_pathfinding_settings(), - p.get_path_avoid() ); - } else { - // immpassable source tile (locker etc.), - // get route to nerest adjacent tile instead - route = route_adjacent( p, 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. Try to sort out loot without a cart." ), - p.disp_name() ); - mgr.end_sort(); - return; - } - - // 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 - p.set_destination( route, player_activity( act_move_loot ) ); - mgr.end_sort(); - return; - } - move_item( p, *thisitem, thisitem->count(), src_loc, dest_loc, this_veh, this_part ); + // skip tiles with inaccessible furniture, like filled charcoal kiln + if( !g->m.can_put_items_ter_furn( dest_loc ) ) { + continue; + } - // moved item away from source so decrement - mgr.decrement_num_processed( src ); + units::volume free_space; + // if there's a vehicle with space do not check the tile beneath + if( dest_veh ) { + free_space = dest_veh->free_volume( dest_part ); + } else { + free_space = g->m.free_volume( dest_loc ); + } + // check free space at destination + if( free_space >= thisitem.volume() ) { + move_item( p, thisitem, thisitem.count(), src_loc, dest_loc, this_veh, this_part ); - break; + // moved item away from source so decrement + if( num_processed > 0 ) { + --num_processed; } - } - if( p.moves <= 0 ) { - // Restart activity and break from cycle. - p.assign_activity( act_move_loot ); - mgr.end_sort(); - return; + break; } } + if( p.moves <= 0 ) { + return; + } } + + //this location is sorted + stage = THINK; + return; } // If we got here without restarting the activity, it means we're done @@ -2137,7 +2182,7 @@ void activity_on_turn_move_loot( player_activity &, player &p ) guy->revert_after_activity(); guy->current_activity_id = activity_id::NULL_ID(); } - mgr.end_sort(); + p.activity.set_to_null(); } static int chop_moves( player &p, item *it ) diff --git a/src/clzones.cpp b/src/clzones.cpp index 1990e00f38696..8c66869251a5c 100644 --- a/src/clzones.cpp +++ b/src/clzones.cpp @@ -834,8 +834,8 @@ zone_type_id zone_manager::get_near_zone_type_for_item( const item &it, const tripoint &where, int range ) const { auto cat = it.get_category(); - if( has_near( zone_type_id( "LOOT_CUSTOM" ), where ) ) { - for( const auto elem : get_near( zone_type_id( "LOOT_CUSTOM" ), where, 60, &it ) ) { + if( has_near( zone_type_id( "LOOT_CUSTOM" ), where, range ) ) { + for( const auto elem : get_near( zone_type_id( "LOOT_CUSTOM" ), where, range, &it ) ) { ( void )elem; return zone_type_id( "LOOT_CUSTOM" ); } @@ -1121,45 +1121,6 @@ void zone_manager::rotate_zones( map &target_map, const int turns ) } } -void zone_manager::start_sort( const std::vector &src_sorted ) -{ - for( auto &src : src_sorted ) { - num_processed[src] = 0; - } -} - -void zone_manager::end_sort() -{ - num_processed.clear(); -} - -bool zone_manager::is_sorting() const -{ - return !num_processed.empty(); -} - -int zone_manager::get_num_processed( const tripoint &src ) const -{ - auto it = num_processed.find( src ); - if( it != num_processed.end() ) { - return it->second; - } - return 0; -} - -void zone_manager::increment_num_processed( const tripoint &src ) -{ - num_processed[src]++; -} - -void zone_manager::decrement_num_processed( const tripoint &src ) -{ - num_processed[src]--; - if( num_processed[src] < 0 ) { - num_processed[src] = 0; - } -} - std::vector zone_manager::get_zones( const faction_id &fac ) { auto zones = std::vector(); diff --git a/src/clzones.h b/src/clzones.h index 774cf58288141..c23a9f783ee6a 100644 --- a/src/clzones.h +++ b/src/clzones.h @@ -407,12 +407,6 @@ class zone_manager const faction_id &fac = your_fac ) const; std::unordered_set get_point_set_loot( const tripoint &where, int radius, bool npc_search, const faction_id &fac = your_fac ) const; - void start_sort( const std::vector &src_sorted ); - void end_sort(); - bool is_sorting() const; - int get_num_processed( const tripoint &src ) const; - void increment_num_processed( const tripoint &src ); - void decrement_num_processed( const tripoint &src ); // 'direct' access to zone_manager::zones, giving direct access was nono std::vector get_zones( const faction_id &fac = your_fac );