From 0a9b5ba2b41e3127a4d5c7968e2adf7e2c17417d Mon Sep 17 00:00:00 2001 From: ipcyborg Date: Sat, 31 Aug 2019 15:27:29 +0300 Subject: [PATCH] Rebased from PR #32876. Added save/load for "coord_set" in "player_activity". --- data/json/player_activities.json | 12 +- src/activity_handlers.cpp | 4 - src/activity_handlers.h | 2 +- src/activity_item_handling.cpp | 767 ++++++++++++++++++------------- src/clzones.cpp | 39 -- src/clzones.h | 9 - src/construction.cpp | 6 - src/savegame_json.cpp | 2 + 8 files changed, 444 insertions(+), 397 deletions(-) diff --git a/data/json/player_activities.json b/data/json/player_activities.json index be56def36ff14..9003387dd8dc3 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -289,9 +289,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", @@ -307,18 +305,14 @@ "type": "activity_type", "activity_level": "MODERATE_EXERCISE", "verb": "fetching components", - "suspendable": false, - "based_on": "neither", - "no_resume": true + "based_on": "neither" }, { "id": "ACT_MULTIPLE_FARM", "type": "activity_type", "activity_level": "ACTIVE_EXERCISE", "verb": "farming", - "suspendable": false, - "based_on": "neither", - "no_resume": true + "based_on": "neither" }, { "id": "ACT_PLANT_PLOT", diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 4e721beadaa8e..5898b9b7b8d92 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -3101,10 +3101,6 @@ void activity_handlers::churn_finish( player_activity *act, player *p ) // Go back to what we were doing before // could be player zone activity, or could be NPC multi-farming act->set_to_null(); - if( !p->backlog.empty() ) { - p->activity = p->backlog.front(); - p->backlog.pop_front(); - } } void activity_handlers::build_do_turn( player_activity *act, player *p ) diff --git a/src/activity_handlers.h b/src/activity_handlers.h index f62ea6e92d217..72bc640784601 100644 --- a/src/activity_handlers.h +++ b/src/activity_handlers.h @@ -42,6 +42,7 @@ enum do_activity_reason : int { NEEDS_HARVESTING, // For farming - tile is harvestable now. NEEDS_PLANTING, // For farming - tile can be planted NEEDS_TILLING, // For farming - tile can be tilled + NEEDS_FETCHING, // Generic fetching required BLOCKING_TILE // Something has made it's way onto the tile, so the activity cannot proceed }; @@ -76,7 +77,6 @@ int butcher_time_to_cut( const player &u, const item &corpse_item, butcher_type // activity_item_handling.cpp void activity_on_turn_drop(); void activity_on_turn_move_items( player_activity &act, player &p ); -void activity_on_turn_move_loot( player_activity &act, player &p ); void generic_multi_activity_handler( player_activity &act, player &p ); void activity_on_turn_fetch( player_activity &, player *p ); void activity_on_turn_pickup(); diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index 814bc74afa60b..998bef6bfc4ac 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -62,6 +62,14 @@ const zone_type_id z_loot_unsorted( "LOOT_UNSORTED" ); const trap_str_id tr_firewood_source( "tr_firewood_source" ); const trap_str_id tr_unfinished_construction( "tr_unfinished_construction" ); +enum act_fetch_str_value_idx : int { + THINGS_TO_FETCH = 0 +}; + +enum act_sort_value_idx : int { + NUM_PROCESSED = 0 +}; + /** Activity-associated item */ struct act_item { const item *it; /// Pointer to the inventory item @@ -861,7 +869,7 @@ static int move_cost( const item &it, const tripoint &src, const tripoint &dest static void move_item( player &p, item &it, int quantity, const tripoint &src, const tripoint &dest, vehicle *src_veh, int src_part, - activity_id activity_to_restore = activity_id::NULL_ID() ) + const activity_id &act_id = activity_id::NULL_ID() ) { item leftovers = it; @@ -878,9 +886,9 @@ static void move_item( player &p, item &it, int quantity, const tripoint &src, // Check that we can pick it up. if( !it.made_of_from_type( LIQUID ) ) { p.mod_moves( -move_cost( it, src, dest ) ); - if( activity_to_restore == activity_id( "ACT_TIDY_UP" ) ) { + if( act_id == activity_id( "ACT_TIDY_UP" ) ) { it.erase_var( "activity_var" ); - } else if( activity_to_restore == activity_id( "ACT_FETCH_REQUIRED" ) ) { + } else if( act_id == activity_id( "ACT_FETCH_REQUIRED" ) ) { it.set_var( "activity_var", p.name ); } put_into_vehicle_or_drop( p, item_drop_reason::deliberate, { it }, dest ); @@ -1056,15 +1064,14 @@ static activity_reason_info find_base_construction( } static bool are_requirements_nearby( const std::vector &loot_spots, - const requirement_id &needed_things, const player &p, const activity_id activity_to_restore, - bool in_loot_zones ) + const requirement_id &needed_things, const player &p, + const activity_id &act_id, bool in_loot_zones ) { zone_manager &mgr = zone_manager::get_manager(); inventory temp_inv; units::volume volume_allowed = p.volume_capacity() - p.volume_carried(); units::mass weight_allowed = p.weight_capacity() - p.weight_carried(); - const bool check_weight = p.backlog.front().id() == activity_id( "ACT_MULTIPLE_FARM" ) || - activity_to_restore == activity_id( "ACT_MULTIPLE_FARM" ); + const bool check_weight = act_id == activity_id( "ACT_MULTIPLE_FARM" ); for( const tripoint &elem : loot_spots ) { // if we are searching for things to fetch, we can skip certain thngs. // if, however they are already near the work spot, then the crafting / inventory fucntions will have their own method to use or discount them. @@ -1130,9 +1137,13 @@ static activity_reason_info can_do_activity_there( const activity_id &act, playe return activity_reason_info::fail( NO_ZONE ); } if( act == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { - const std::vector &list_constructions = get_constructions(); zones = mgr.get_zones( zone_type_id( "CONSTRUCTION_BLUEPRINT" ), g->m.getabs( src_loc ) ); + if( zones.empty() ) { + return activity_reason_info::fail( NO_ZONE ); + } + const zone_data &zone = zones.front(); + const partial_con *part_con = g->m.partial_con_at( src_loc ); cata::optional part_con_idx; if( part_con ) { @@ -1142,17 +1153,18 @@ static activity_reason_info can_do_activity_there( const activity_id &act, playe // PICKUP_RANGE -1 because we will be adjacent to the spot when arriving. const inventory pre_inv = p.crafting_inventory( src_loc, PICKUP_RANGE - 1 ); - for( const zone_data &zone : zones ) { - const blueprint_options options = dynamic_cast( zone.get_options() ); - const int index = options.get_index(); - if( !stuff_there.empty() ) { - return activity_reason_info::build( BLOCKING_TILE, false, static_cast( index ) ); - } - std::set used_idx; - const activity_reason_info act_info = find_base_construction( list_constructions, p, pre_inv, - src_loc, part_con_idx, static_cast( index ), used_idx ); - return act_info; + + const blueprint_options options = dynamic_cast( zone.get_options() ); + const int index = options.get_index(); + if( !stuff_there.empty() ) { + return activity_reason_info::build( BLOCKING_TILE, false, static_cast( index ) ); } + + std::set used_idx; + const std::vector &list_constructions = get_constructions(); + const activity_reason_info act_info = find_base_construction( list_constructions, p, pre_inv, + src_loc, part_con_idx, static_cast( index ), used_idx ); + return act_info; } else if( act == activity_id( "ACT_MULTIPLE_FARM" ) ) { zones = mgr.get_zones( zone_type_id( "FARM_PLOT" ), g->m.getabs( src_loc ) ); @@ -1206,10 +1218,10 @@ static activity_reason_info can_do_activity_there( const activity_id &act, playe return activity_reason_info::fail( NO_ZONE ); } -static std::vector> requirements_map( player &p ) +static std::vector> requirements_map( player &p, + const player_activity &act ) { - const requirement_data things_to_fetch = requirement_id( p.backlog.front().str_values[0] ).obj(); - const activity_id activity_to_restore = p.backlog.front().id(); + const requirement_data things_to_fetch = requirement_id( act.str_values[THINGS_TO_FETCH] ).obj(); // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) requirement_id things_to_fetch_id = things_to_fetch.id(); std::vector> req_comps = things_to_fetch.get_components(); @@ -1218,7 +1230,7 @@ static std::vector> requirements_map( player const units::volume volume_allowed = p.volume_capacity() - p.volume_carried(); const units::mass weight_allowed = p.weight_capacity() - p.weight_carried(); zone_manager &mgr = zone_manager::get_manager(); - const bool pickup_task = p.backlog.front().id() == activity_id( "ACT_MULTIPLE_FARM" ); + const bool pickup_task = act.id() == activity_id( "ACT_MULTIPLE_FARM" ); // where it is, what it is, how much of it, and how much in total is required of that item. std::vector> requirement_map; std::vector> final_map; @@ -1226,12 +1238,12 @@ static std::vector> requirements_map( player std::vector already_there_spots; std::vector combined_spots; std::map total_map; - for( const auto elem : g->m.points_in_radius( g->m.getlocal( p.backlog.front().placement ), + for( const tripoint &elem : g->m.points_in_radius( g->m.getlocal( act.placement ), PICKUP_RANGE - 1 ) ) { already_there_spots.push_back( elem ); combined_spots.push_back( elem ); } - for( const tripoint elem : mgr.get_point_set_loot( g->m.getabs( p.pos() ), 60, p.is_npc() ) ) { + for( const tripoint &elem : mgr.get_point_set_loot( g->m.getabs( p.pos() ), 60, p.is_npc() ) ) { // if there is a loot zone thats already near the work spot, we dont want it to be added twice. if( std::find( already_there_spots.begin(), already_there_spots.end(), elem ) != already_there_spots.end() ) { @@ -1250,13 +1262,13 @@ static std::vector> requirements_map( player } // if the requirements arent available, then stop. if( !are_requirements_nearby( pickup_task ? loot_spots : combined_spots, things_to_fetch_id, p, - activity_to_restore, pickup_task ) ) { + act.id(), pickup_task ) ) { return requirement_map; } // if the requirements are already near the work spot and its a construction/crafting task, then no need to fetch anything more. if( !pickup_task && - are_requirements_nearby( already_there_spots, things_to_fetch_id, p, activity_to_restore, - false ) ) { + are_requirements_nearby( already_there_spots, things_to_fetch_id, p, + act.id(), false ) ) { return requirement_map; } // a vector of every item in every tile that matches any part of the requirements. @@ -1489,8 +1501,7 @@ static bool plant_activity( player &p, const zone_data *zone, const tripoint src } static void construction_activity( player &p, const zone_data *zone, const tripoint src_loc, - const activity_reason_info &act_info, const std::vector &list_constructions, - activity_id activity_to_restore ) + const activity_reason_info &act_info, const std::vector &list_constructions ) { const blueprint_options options = dynamic_cast( zone->get_options() ); // the actual desired construction @@ -1518,12 +1529,11 @@ static void construction_activity( player &p, const zone_data *zone, const tripo for( const std::vector &it : built_chosen.requirements->get_tools() ) { p.consume_tools( it ); } - p.backlog.push_front( activity_to_restore ); p.assign_activity( activity_id( "ACT_BUILD" ) ); p.activity.placement = g->m.getabs( src_loc ); } -static bool tidy_activity( player &p, const tripoint src_loc, activity_id activity_to_restore ) +static bool tidy_activity( player &p, const tripoint src_loc, const player_activity &act ) { auto &mgr = zone_manager::get_manager(); tripoint loot_abspos = g->m.getabs( src_loc ); @@ -1558,7 +1568,7 @@ static bool tidy_activity( player &p, const tripoint src_loc, activity_id activi for( auto &it : items_there ) { if( it.has_var( "activity_var" ) && it.get_var( "activity_var", "" ) == p.name ) { move_item( p, it, it.count(), src_loc, loot_src_lot, dest_veh, dest_part, - activity_to_restore ); + act.id() ); break; } } @@ -1574,12 +1584,16 @@ static bool tidy_activity( player &p, const tripoint src_loc, activity_id activi return true; } -static void fetch_activity( player &p, const tripoint src_loc, activity_id activity_to_restore ) +static void fetch_activity( player &p, const tripoint src_loc, const player_activity &act ) { - if( !g->m.can_put_items_ter_furn( g->m.getlocal( p.backlog.front().coords.back() ) ) ) { + if( act.coords.empty() ) { + return; + } + const tripoint &coord = *act.coords.begin(); + if( !g->m.can_put_items_ter_furn( g->m.getlocal( coord ) ) ) { return; } - const std::vector> mental_map_2 = requirements_map( p ); + const std::vector> mental_map_2 = requirements_map( p, act ); int pickup_count = 1; auto items_there = g->m.i_at( src_loc ); vehicle *src_veh = nullptr; @@ -1597,9 +1611,9 @@ static void fetch_activity( player &p, const tripoint src_loc, activity_id activ for( auto &veh_elem : src_veh->get_items( src_part ) ) { for( auto elem : mental_map_2 ) { if( std::get<0>( elem ) == src_loc && veh_elem.typeId() == std::get<1>( elem ) ) { - if( !p.backlog.empty() && p.backlog.front().id() == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { + if( act.id() == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { move_item( p, veh_elem, veh_elem.count_by_charges() ? std::get<2>( elem ) : 1, src_loc, - g->m.getlocal( p.backlog.front().coords.back() ), src_veh, src_part, activity_to_restore ); + g->m.getlocal( coord ), src_veh, src_part ); return; } } @@ -1610,12 +1624,12 @@ static void fetch_activity( player &p, const tripoint src_loc, activity_id activ for( auto elem : mental_map_2 ) { if( std::get<0>( elem ) == src_loc && it->typeId() == std::get<1>( elem ) ) { // construction/crafting tasks want the requred item moved near the work spot. - if( !p.backlog.empty() && p.backlog.front().id() == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { + if( act.id() == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { move_item( p, *it, it->count_by_charges() ? std::get<2>( elem ) : 1, src_loc, - g->m.getlocal( p.backlog.front().coords.back() ), src_veh, src_part, activity_to_restore ); + g->m.getlocal( coord ), src_veh, src_part ); return; // other tasks want the tool picked up - } else if( !p.backlog.empty() && p.backlog.front().id() == activity_id( "ACT_MULTIPLE_FARM" ) ) { + } else if( act.id() == activity_id( "ACT_MULTIPLE_FARM" ) ) { if( it->volume() > volume_allowed || it->weight() > weight_allowed ) { continue; } @@ -1653,7 +1667,7 @@ static void fetch_activity( player &p, const tripoint src_loc, activity_id activ } static bool move_loot_activity( player &p, tripoint src_loc, zone_manager &mgr, - activity_id activity_to_restore ) + player_activity &act ) { // the boolean in this pair being true indicates the item is from a vehicle storage space auto items = std::vector>(); @@ -1678,10 +1692,12 @@ static bool move_loot_activity( player &p, tripoint src_loc, zone_manager &mgr, 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 ); + int &num_processed = act.values[ NUM_PROCESSED ]; + + //Skip items that have already been processed + for( auto it = items.begin() + num_processed; it < items.end(); ++it ) { + ++num_processed; const auto thisitem = it->first; @@ -1698,50 +1714,50 @@ static bool move_loot_activity( player &p, tripoint src_loc, zone_manager &mgr, // 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 ); + if( mgr.has( id, src ) ) { + continue; + } + const auto &dest_set = mgr.get_near( id, abspos ); - for( auto &dest : dest_set ) { - const auto &dest_loc = g->m.getlocal( dest ); + for( auto &dest : dest_set ) { + const auto &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() ) { - 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; } + break; } - if( p.moves <= 0 ) { - // Restart activity and break from cycle. - p.assign_activity( activity_to_restore ); - mgr.end_sort(); - return true; - } + } + if( p.moves <= 0 ) { + // Restart activity and break from cycle. + return true; } } return false; @@ -1761,261 +1777,369 @@ static std::string random_string( size_t length ) return str; } -void generic_multi_activity_handler( player_activity &act, player &p ) +static void generic_multi_activity_finish( player &p ) +{ + // if we got here, we need to revert otherwise NPC will be stuck in AI Limbo and have a head explosion. + p.activity = player_activity(); + if( p.backlog.empty() || p.activity.coord_set.empty() ) { + if( p.is_npc() ) { + npc *guy = dynamic_cast( &p ); + guy->revert_after_activity(); + } + } +} + +static activity_reason_info can_do_generic_activity_there( const player_activity &act, player &p, + const tripoint &abspos, const tripoint &src, const tripoint &src_loc ) { - // First get the things that are activity-agnostic. - zone_manager &mgr = zone_manager::get_manager(); - const tripoint abspos = g->m.getabs( p.pos() ); - // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) - activity_id activity_to_restore = act.id(); - const tripoint localpos = p.pos(); - bool dark_capable = false; - // the set of target work spots - potentally after we have fetched required tools. - std::unordered_set src_set; // we may need a list of all constructions later. const std::vector &list_constructions = get_constructions(); - // Nuke the current activity, leaving the backlog alone - p.activity = player_activity(); - // now we setup the target spots based on whch activity is occuring - if( activity_to_restore == activity_id( "ACT_MOVE_LOOT" ) ) { - dark_capable = true; - if( g->m.check_vehicle_zones( g->get_levz() ) ) { - mgr.cache_vzones(); - } - src_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos ); - } - if( activity_to_restore == activity_id( "ACT_TIDY_UP" ) ) { - dark_capable = true; - tripoint unsorted_spot; - for( const tripoint elem : g->m.points_in_radius( g->m.getlocal( abspos ), 60 ) ) { - if( mgr.has( zone_type_id( z_loot_unsorted ), g->m.getabs( elem ) ) ) { - // it already has a unsorted loot spot, and therefore dont need to go and pick up items there. - if( unsorted_spot == tripoint_zero ) { - unsorted_spot = elem; - } - continue; + zone_manager &mgr = zone_manager::get_manager(); + + const activity_id &act_id = act.id(); + activity_reason_info act_info = can_do_activity_there( act_id, p, + src_loc ); + const bool &can_do_it = act_info.can_do; + const do_activity_reason &reason = act_info.reason; + const zone_data *zone = mgr.get_zone_at( src ); + const bool needs_to_be_in_zone = act_id == activity_id( "ACT_FETCH_REQUIRED" ) || + act_id == activity_id( "ACT_MULTIPLE_FARM" ) || + ( act_id == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) && + !g->m.partial_con_at( src_loc ) ); + // some activities require the target tile to be part of a zone. + // tidy up activity dosnt - it wants things that may not be in a zone already - things that may have been left lying around. + if( needs_to_be_in_zone && !zone ) { + act_info.can_do = false; + return act_info; + } + if( ( !can_do_it ) && ( reason == DONT_HAVE_SKILL || reason == NO_ZONE || reason == ALREADY_DONE || + reason == BLOCKING_TILE || reason == UNKNOWN_ACTIVITY ) ) { + // we can discount this tile, the work can't be done. + if( reason == DONT_HAVE_SKILL ) { + p.add_msg_if_player( m_info, _( "You don't have the skill for this task." ) ); + } else if( reason == BLOCKING_TILE ) { + p.add_msg_if_player( m_info, _( "There is something blocking the location for this task." ) ); + } else if( reason != ALREADY_DONE ) { + p.add_msg_if_player( m_info, _( "You cannot do this task there." ) ); + } + act_info.can_do = false; + return act_info; + } else if( ( !can_do_it ) && ( reason == NO_COMPONENTS || reason == NEEDS_PLANTING || + reason == NEEDS_TILLING ) ) { + // we can do it, but we need to fetch some stuff first + // before we set the task to fetch components - is it even worth it? are the components anywhere? + requirement_id what_we_need; + std::vector loot_zone_spots; + std::vector combined_spots; + for( const tripoint &elem : mgr.get_point_set_loot( abspos, 60, p.is_npc() ) ) { + loot_zone_spots.push_back( elem ); + combined_spots.push_back( elem ); + } + for( const tripoint &elem : g->m.points_in_radius( src_loc, PICKUP_RANGE - 1 ) ) { + combined_spots.push_back( elem ); + } + if( ( reason == NO_COMPONENTS || reason == NO_COMPONENTS_PREREQ ) && + act_id == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { + if( !act_info.con_idx ) { + debugmsg( "no construction selected" ); + act_info.can_do = false; + return act_info; } - for( const auto &stack_elem : g->m.i_at( elem ) ) { - if( stack_elem.has_var( "activity_var" ) && stack_elem.get_var( "activity_var", "" ) == p.name ) { - const furn_t &f = g->m.furn( elem ).obj(); - if( !f.has_flag( "PLANT" ) ) { - src_set.insert( g->m.getabs( elem ) ); - break; + // its a construction and we need the components. + const construction &built_chosen = list_constructions[ *act_info.con_idx ]; + what_we_need = built_chosen.requirements; + } else if( reason == NEEDS_TILLING || reason == NEEDS_PLANTING ) { + std::vector> requirement_comp_vector; + std::vector> quality_comp_vector; + std::vector> tool_comp_vector; + if( reason == NEEDS_TILLING ) { + quality_comp_vector.push_back( std::vector { quality_requirement( quality_id( "DIG" ), 1, 1 ) } ); + } else if( reason == NEEDS_PLANTING ) { + requirement_comp_vector.push_back( std::vector { item_comp( itype_id( dynamic_cast + ( zone->get_options() ).get_seed() ), 1 ) + } ); + } + // ok, we need a shovel/hoe/axe/etc + // this is an activity that only requires this one tool, so we will fetch and wield it. + requirement_data reqs_data = requirement_data( tool_comp_vector, quality_comp_vector, + requirement_comp_vector ); + const std::string ran_str = random_string( 10 ); + const requirement_id req_id( ran_str ); + requirement_data::save_requirement( reqs_data, req_id ); + what_we_need = req_id; + } + bool tool_pickup = reason == NEEDS_TILLING || reason == NEEDS_PLANTING; + // is it even worth fetching anything if there isnt enough nearby? + if( !are_requirements_nearby( tool_pickup ? loot_zone_spots : combined_spots, what_we_need, p, + act_id, tool_pickup ) ) { + p.add_msg_if_player( m_info, _( "The required items are not available to complete this task." ) ); + act_info.can_do = false; + return act_info; + } else { + p.assign_activity( activity_id( "ACT_FETCH_REQUIRED" ) ); + player_activity &act_new = p.activity; + act_new.str_values.push_back( what_we_need.str() ); + act_new.values.push_back( reason ); + // come back here after succesfully fetching your stuff + act_new.placement = src; + std::vector candidates; + if( act_new.coords.empty() ) { + const std::unordered_set &src_set = act.coord_set; + std::vector local_src_set; + for( const tripoint &elem : src_set ) { + local_src_set.push_back( g->m.getlocal( elem ) ); + } + std::vector candidates; + for( const tripoint &point_elem : g->m.points_in_radius( src_loc, PICKUP_RANGE - 1 ) ) { + // we dont want to place the components where they could interfere with our ( or someone elses ) construction spots + if( ( std::find( local_src_set.begin(), local_src_set.end(), + point_elem ) != local_src_set.end() ) || !g->m.can_put_items_ter_furn( point_elem ) ) { + continue; } + candidates.push_back( point_elem ); } - } - } - if( src_set.empty() && unsorted_spot != tripoint_zero ) { - for( auto inv_elem : p.inv_dump() ) { - if( inv_elem->has_var( "activity_var" ) ) { - // we've gone to tidy up all the thngs lying around, now tidy up the things we picked up. - src_set.insert( g->m.getabs( unsorted_spot ) ); - break; + if( candidates.empty() ) { + act_info.can_do = false; + act_info.reason = NEEDS_FETCHING; + return act_info; } + act_new.coords.push_back( g->m.getabs( candidates[std::max( 0, + static_cast( candidates.size() / 2 ) )] ) ); } + + act_info.can_do = true; + act_info.reason = NEEDS_FETCHING; + return act_info; } } - // multiple construction will form a list of targets based on blueprint zones and unfinished constructions - if( activity_to_restore == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { - src_set = mgr.get_near( zone_type_id( "CONSTRUCTION_BLUEPRINT" ), abspos, 60 ); - for( const tripoint &elem : g->m.points_in_radius( localpos, 40 ) ) { - partial_con *pc = g->m.partial_con_at( elem ); - if( pc ) { - src_set.insert( g->m.getabs( elem ) ); - } - } - // farming activies encompass tilling, planting, harvesting. - } - if( activity_to_restore == activity_id( "ACT_MULTIPLE_FARM" ) ) { - src_set = mgr.get_near( zone_type_id( "FARM_PLOT" ), abspos, 60 ); - // fetch required will always be following on from a previous activity - } - if( activity_to_restore == activity_id( "ACT_FETCH_REQUIRED" ) ) { - dark_capable = true; - // get the right zones for the items in the requirements. - // we previously checked if the items are nearby before we set the fetch task - // but we will check again later, to be sure nothings changed. - std::vector> mental_map = requirements_map( p ); - for( auto elem : mental_map ) { - tripoint elem_point = std::get<0>( elem ); - src_set.insert( g->m.getabs( elem_point ) ); - } - } - // prune the set to remove tiles that are never gonna work out. - for( auto it2 = src_set.begin(); it2 != src_set.end(); ) { - // remove dangerous tiles - tripoint set_pt = g->m.getlocal( *it2 ); - if( g->m.dangerous_field_at( set_pt ) ) { - it2 = src_set.erase( it2 ); - // remove tiles in darkness, if we arent lit-up ourselves - } else if( !dark_capable && p.fine_detail_vision_mod( set_pt ) > 4.0 ) { - it2 = src_set.erase( it2 ); - } else { - ++it2; - } + + return act_info; +} + +void generic_multi_activity_handler( player_activity &act, player &p ) +{ + enum activity_stage : int { + INIT = 0, //Initial stage + THINK, //Think about what to do first: choose destination + MOVE, //Move to destination + DO, //Do activity + }; + + // First get the things that are activity-agnostic. + zone_manager &mgr = zone_manager::get_manager(); + const tripoint abspos = g->m.getabs( p.pos() ); + + int &stage = act.index; + + //Prepare activity stage + if( stage < 0 ) { + stage = INIT; } - // now we have our final set of points - std::vector src_sorted = get_sorted_tiles_by_distance( abspos, src_set ); - if( activity_to_restore == activity_id( "ACT_MOVE_LOOT" ) ) { - if( !mgr.is_sorting() ) { - mgr.start_sort( src_sorted ); + + //Initialize activity + if( stage == INIT ) { + //can be automatically resumed after sub-task + act.auto_resume = true; + + const tripoint localpos = p.pos(); + bool dark_capable = false; + // the set of target work spots - potentally after we have fetched required tools. + std::unordered_set src_set; + + // now we setup the target spots based on whch activity is occuring + if( act.id() == activity_id( "ACT_MOVE_LOOT" ) ) { + dark_capable = true; + if( g->m.check_vehicle_zones( g->get_levz() ) ) { + mgr.cache_vzones(); + } + src_set = mgr.get_near( zone_type_id( "LOOT_UNSORTED" ), abspos ); + + if( act.values.size() < 1 ) { + act.values.resize( 1 ); + act.values[NUM_PROCESSED] = 0; + } } - } - // now loop through the work-spot tiles and judge whether its worth travelling to it yet - // or if we need to fetch something first. - for( const tripoint &src : src_sorted ) { - const tripoint &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( activity_to_restore ); - p.set_moves( 0 ); - g->reload_npcs(); - if( activity_to_restore == activity_id( "ACT_MOVE_LOOT" ) ) { - mgr.end_sort(); + if( act.id() == activity_id( "ACT_TIDY_UP" ) ) { + dark_capable = true; + tripoint unsorted_spot; + for( const tripoint &elem : g->m.points_in_radius( g->m.getlocal( abspos ), 60 ) ) { + if( mgr.has( zone_type_id( z_loot_unsorted ), g->m.getabs( elem ) ) ) { + // it already has a unsorted loot spot, and therefore dont need to go and pick up items there. + if( unsorted_spot == tripoint_zero ) { + unsorted_spot = elem; + } + continue; + } + for( const auto &stack_elem : g->m.i_at( elem ) ) { + if( stack_elem.has_var( "activity_var" ) && stack_elem.get_var( "activity_var", "" ) == p.name ) { + const furn_t &f = g->m.furn( elem ).obj(); + if( !f.has_flag( "PLANT" ) ) { + src_set.insert( g->m.getabs( elem ) ); + break; + } + } } - return; } - const std::vector route = route_adjacent( p, src_loc ); - if( route.empty() ) { - // can't get there, can't do anything, skip it - continue; + if( src_set.empty() && unsorted_spot != tripoint_zero ) { + for( auto inv_elem : p.inv_dump() ) { + if( inv_elem->has_var( "activity_var" ) ) { + // we've gone to tidy up all the thngs lying around, now tidy up the things we picked up. + src_set.insert( g->m.getabs( unsorted_spot ) ); + break; + } + } } - p.set_moves( 0 ); - p.set_destination( route, player_activity( activity_to_restore ) ); - return; } - activity_reason_info act_info = can_do_activity_there( activity_to_restore, p, - src_loc ); - const bool &can_do_it = act_info.can_do; - const do_activity_reason &reason = act_info.reason; - const zone_data *zone = mgr.get_zone_at( src ); - const bool needs_to_be_in_zone = activity_to_restore == activity_id( "ACT_FETCH_REQUIRED" ) || - activity_to_restore == activity_id( "ACT_MULTIPLE_FARM" ) || - ( activity_to_restore == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) && - !g->m.partial_con_at( src_loc ) ); - // some activities require the target tile to be part of a zone. - // tidy up activity dosnt - it wants things that may not be in a zone already - things that may have been left lying around. - if( needs_to_be_in_zone && !zone ) { - continue; - } - if( ( !can_do_it ) && ( reason == DONT_HAVE_SKILL || reason == NO_ZONE || reason == ALREADY_DONE || - reason == BLOCKING_TILE || reason == UNKNOWN_ACTIVITY ) ) { - // we can discount this tile, the work can't be done. - if( reason == DONT_HAVE_SKILL ) { - p.add_msg_if_player( m_info, _( "You don't have the skill for this task." ) ); - } else if( reason == BLOCKING_TILE ) { - p.add_msg_if_player( m_info, _( "There is something blocking the location for this task." ) ); - } else if( reason != ALREADY_DONE ) { - p.add_msg_if_player( m_info, _( "You cannot do this task there." ) ); + // multiple construction will form a list of targets based on blueprint zones and unfinished constructions + if( act.id() == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { + src_set = mgr.get_near( zone_type_id( "CONSTRUCTION_BLUEPRINT" ), abspos, 60 ); + for( const tripoint &elem : g->m.points_in_radius( localpos, 40 ) ) { + partial_con *pc = g->m.partial_con_at( elem ); + if( pc ) { + src_set.insert( g->m.getabs( elem ) ); + } } - continue; - } else if( ( !can_do_it ) && ( reason == NO_COMPONENTS || reason == NEEDS_PLANTING || - reason == NEEDS_TILLING ) ) { - // we can do it, but we need to fetch some stuff first - // before we set the task to fetch components - is it even worth it? are the components anywhere? - requirement_id what_we_need; - std::vector loot_zone_spots; - std::vector combined_spots; - for( const tripoint elem : mgr.get_point_set_loot( abspos, 60, p.is_npc() ) ) { - loot_zone_spots.push_back( elem ); - combined_spots.push_back( elem ); + // farming activies encompass tilling, planting, harvesting. + } + if( act.id() == activity_id( "ACT_MULTIPLE_FARM" ) ) { + src_set = mgr.get_near( zone_type_id( "FARM_PLOT" ), abspos, 60 ); + // fetch required will always be following on from a previous activity + } + if( act.id() == activity_id( "ACT_FETCH_REQUIRED" ) ) { + dark_capable = true; + // get the right zones for the items in the requirements. + // we previously checked if the items are nearby before we set the fetch task + // but we will check again later, to be sure nothings changed. + std::vector> mental_map = requirements_map( p, act ); + for( auto elem : mental_map ) { + tripoint elem_point = std::get<0>( elem ); + src_set.insert( g->m.getabs( elem_point ) ); } - for( const tripoint elem : g->m.points_in_radius( src_loc, PICKUP_RANGE - 1 ) ) { - combined_spots.push_back( elem ); + } + // prune the set to remove tiles that are never gonna work out. + for( auto it2 = src_set.begin(); it2 != src_set.end(); ) { + // remove dangerous tiles + tripoint set_pt = g->m.getlocal( *it2 ); + if( g->m.dangerous_field_at( set_pt ) ) { + it2 = src_set.erase( it2 ); + // remove tiles in darkness, if we arent lit-up ourselves + } else if( !dark_capable && p.fine_detail_vision_mod( set_pt ) > 4.0 ) { + it2 = src_set.erase( it2 ); + } else { + ++it2; } - if( ( reason == NO_COMPONENTS || reason == NO_COMPONENTS_PREREQ ) && - activity_to_restore == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { - if( !act_info.con_idx ) { - debugmsg( "no construction selected" ); - continue; + } + + //nothing to do? + if( src_set.empty() ) { + p.activity = player_activity(); + return; + } + + // now we have our final set of points + act.coord_set = src_set; + stage = THINK; + } + + if( stage == THINK ) { + // now loop through the work-spot tiles and judge whether its worth travelling to it yet + // or if we need to fetch something first. + std::vector coords = get_sorted_tiles_by_distance( abspos, act.coord_set ); + for( const tripoint &src : coords ) { + //remember current destination point + act.placement = src; + const tripoint &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.set_moves( 0 ); + g->reload_npcs(); + stage = INIT; + return; } - // its a construction and we need the components. - const construction &built_chosen = list_constructions[ *act_info.con_idx ]; - what_we_need = built_chosen.requirements; - } else if( reason == NEEDS_TILLING || reason == NEEDS_PLANTING ) { - std::vector> requirement_comp_vector; - std::vector> quality_comp_vector; - std::vector> tool_comp_vector; - if( reason == NEEDS_TILLING ) { - quality_comp_vector.push_back( std::vector { quality_requirement( quality_id( "DIG" ), 1, 1 ) } ); - } else if( reason == NEEDS_PLANTING ) { - requirement_comp_vector.push_back( std::vector { item_comp( itype_id( dynamic_cast - ( zone->get_options() ).get_seed() ), 1 ) - } ); + const std::vector route = route_adjacent( p, src_loc ); + if( route.empty() ) { + // can't get there, can't do anything, skip it + continue; } - // ok, we need a shovel/hoe/axe/etc - // this is an activity that only requires this one tool, so we will fetch and wield it. - requirement_data reqs_data = requirement_data( tool_comp_vector, quality_comp_vector, - requirement_comp_vector ); - const std::string ran_str = random_string( 10 ); - const requirement_id req_id( ran_str ); - requirement_data::save_requirement( reqs_data, req_id ); - what_we_need = req_id; - } - bool tool_pickup = reason == NEEDS_TILLING || reason == NEEDS_PLANTING; - // is it even worth fetching anything if there isnt enough nearby? - if( !are_requirements_nearby( tool_pickup ? loot_zone_spots : combined_spots, what_we_need, p, - activity_to_restore, tool_pickup ) ) { - p.add_msg_if_player( m_info, _( "The required items are not available to complete this task." ) ); - continue; - } else { - p.backlog.push_front( activity_to_restore ); - p.assign_activity( activity_id( "ACT_FETCH_REQUIRED" ) ); - p.backlog.front().str_values.push_back( what_we_need.str() ); - p.backlog.front().values.push_back( reason ); - // come back here after succesfully fetching your stuff - std::vector candidates; - if( p.backlog.front().coords.empty() ) { - std::vector local_src_set; - for( const auto elem : src_set ) { - local_src_set.push_back( g->m.getlocal( elem ) ); - } - std::vector candidates; - for( const auto point_elem : g->m.points_in_radius( src_loc, PICKUP_RANGE - 1 ) ) { - // we dont want to place the components where they could interfere with our ( or someone elses ) construction spots - if( ( std::find( local_src_set.begin(), local_src_set.end(), - point_elem ) != local_src_set.end() ) || !g->m.can_put_items_ter_furn( point_elem ) ) { - continue; - } - candidates.push_back( point_elem ); - } - if( candidates.empty() ) { - p.activity = player_activity(); - p.backlog.clear(); - return; + //have to think first after we have arrived to distant location + stage = THINK; + //mark all coords before this one as "already processed" to avoid looping between two distant locations + for( const tripoint &src_to_remove : coords ) { + act.coord_set.erase( src_to_remove ); + if( src == src_to_remove ) { + break; } - p.backlog.front().coords.push_back( g->m.getabs( candidates[std::max( 0, - static_cast( candidates.size() / 2 ) )] ) ); } - p.backlog.front().placement = src; - + p.set_moves( 0 ); + p.set_destination( route, act ); + p.activity = player_activity(); return; } + + activity_reason_info act_info = can_do_generic_activity_there( act, p, + abspos, src, src_loc ); + if( act_info.can_do ) { + if( act_info.reason == NEEDS_FETCHING ) { + //already set new fetching activity + return; + } else { + stage = MOVE; + } + break; + } + } //end of coords loop + + //so nothing is choosen? + if( stage == THINK ) { + generic_multi_activity_finish( p ); + return; } + } + + if( stage == MOVE ) { + const tripoint &src = act.placement; + const tripoint &src_loc = g->m.getlocal( src ); + if( square_dist( p.pos(), src_loc ) > 1 ) { // not adjacent std::vector route = route_adjacent( p, src_loc ); // check if we found path to source / adjacent tile if( route.empty() ) { - if( activity_to_restore == activity_id( "ACT_MOVE_LOOT" ) ) { - mgr.end_sort(); - } - return; - } - if( p.moves <= 0 ) { - // Restart activity and break from cycle. - p.assign_activity( activity_to_restore ); + //can't find a path, bad point, skip it and think again + stage = THINK; + act.coord_set.erase( src ); return; } // 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( activity_to_restore ) ); + p.set_destination( route, act ); + p.activity = player_activity(); return; } + //alright, we are here + stage = DO; + } + + if( stage == DO ) { + const tripoint &src = act.placement; + const tripoint &src_loc = g->m.getlocal( src ); + const zone_data *zone = mgr.get_zone_at( src ); + + activity_reason_info act_info = can_do_generic_activity_there( act.id(), p, + abspos, src, src_loc ); + const bool &can_do_it = act_info.can_do; + const do_activity_reason &reason = act_info.reason; + + if( !can_do_it ) { + //can't work here, bad point, skip it and think again + stage = THINK; + act.coord_set.erase( src ); + } + // something needs to be done, now we are there. // it was here earlier, in the space of one turn, maybe it got harvested by someone else. if( reason == NEEDS_HARVESTING && g->m.has_flag_furn( "GROWTH_HARVEST", src_loc ) ) { @@ -2023,63 +2147,48 @@ void generic_multi_activity_handler( player_activity &act, player &p ) } else if( reason == NEEDS_TILLING && g->m.has_flag( "PLOWABLE", src_loc ) && p.has_quality( quality_id( "DIG" ), 1 ) && !g->m.has_furn( src_loc ) ) { p.assign_activity( activity_id( "ACT_CHURN" ), 18000, -1 ); - p.backlog.push_front( activity_to_restore ); p.activity.placement = src; return; } else if( reason == NEEDS_PLANTING && g->m.has_flag_ter_or_furn( "PLANTABLE", src_loc ) ) { - if( !plant_activity( p, zone, src_loc ) ) { - continue; - } + plant_activity( p, zone, src_loc ); } else if( reason == CAN_DO_CONSTRUCTION && g->m.partial_con_at( src_loc ) ) { - p.backlog.push_front( activity_to_restore ); p.assign_activity( activity_id( "ACT_BUILD" ) ); p.activity.placement = src; return; } else if( reason == CAN_DO_CONSTRUCTION || reason == CAN_DO_PREREQ ) { if( g->m.partial_con_at( src_loc ) ) { - p.backlog.push_front( activity_to_restore ); p.assign_activity( activity_id( "ACT_BUILD" ) ); p.activity.placement = src; return; } - construction_activity( p, zone, src_loc, act_info, list_constructions, activity_to_restore ); + const std::vector &list_constructions = get_constructions(); + construction_activity( p, zone, src_loc, act_info, list_constructions ); return; - } else if( reason == CAN_DO_FETCH && activity_to_restore == activity_id( "ACT_TIDY_UP" ) ) { - if( !tidy_activity( p, src_loc, activity_to_restore ) ) { + } else if( reason == CAN_DO_FETCH && act.id() == activity_id( "ACT_TIDY_UP" ) ) { + if( !tidy_activity( p, src_loc, act ) ) { return; } - } else if( reason == CAN_DO_FETCH && activity_to_restore == activity_id( "ACT_FETCH_REQUIRED" ) ) { - fetch_activity( p, src_loc, activity_to_restore ); + } else if( reason == CAN_DO_FETCH && act.id() == activity_id( "ACT_FETCH_REQUIRED" ) ) { + fetch_activity( p, src_loc, act ); return; - } else if( reason == CAN_DO_FETCH && activity_to_restore == activity_id( "ACT_MOVE_LOOT" ) ) { - if( move_loot_activity( p, src_loc, mgr, activity_to_restore ) ) { + } else if( reason == CAN_DO_FETCH && act.id() == activity_id( "ACT_MOVE_LOOT" ) ) { + if( move_loot_activity( p, src_loc, mgr, act ) ) { return; } - continue; + } else if( reason != ALREADY_DONE ) { + debugmsg( "Unhandled activity [%s] reason %d", act.name, reason ); } + + //done, think about what to do next + stage = THINK; + act.coord_set.erase( src ); } + if( p.moves <= 0 ) { // Restart activity and break from cycle. - p.assign_activity( activity_to_restore ); - if( activity_to_restore == activity_id( "ACT_MOVE_LOOT" ) ) { - mgr.end_sort(); - } return; } - // if we got here, we need to revert otherwise NPC will be stuck in AI Limbo and have a head explosion. - if( p.backlog.empty() || src_set.empty() ) { - if( p.is_npc() ) { - npc *guy = dynamic_cast( &p ); - guy->revert_after_activity(); - } - // tidy up leftover moved parts and tools left lying near the work spots. - if( activity_to_restore == activity_id( "ACT_MULTIPLE_FARM" ) || - activity_to_restore == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { - p.assign_activity( activity_id( "ACT_TIDY_UP" ) ); - } else if( activity_to_restore == activity_id( "ACT_MOVE_LOOT" ) ) { - mgr.end_sort(); - } - } + generic_multi_activity_finish( p ); } static cata::optional find_best_fire( diff --git a/src/clzones.cpp b/src/clzones.cpp index 244e4863e392a..32f655a29278d 100644 --- a/src/clzones.cpp +++ b/src/clzones.cpp @@ -1104,45 +1104,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..f517f7c4d2786 100644 --- a/src/clzones.h +++ b/src/clzones.h @@ -344,9 +344,6 @@ class zone_manager std::unordered_set get_vzone_set( const zone_type_id &type, const faction_id &fac = your_fac ) const; - //Cache number of items already checked on each source tile when sorting - std::unordered_map num_processed; - public: zone_manager(); ~zone_manager() = default; @@ -407,12 +404,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 ); diff --git a/src/construction.cpp b/src/construction.cpp index d477f5e499ac5..0965bccdeb2e8 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -928,12 +928,6 @@ void complete_construction( player *p ) // This comes after clearing the activity, in case the function interrupts // activities built.post_special( terp ); - // npcs will automatically resume backlog, players wont. - if( p->is_player() && !p->backlog.empty() && - p->backlog.front().id() == activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ) { - p->backlog.clear(); - p->assign_activity( activity_id( "ACT_MULTIPLE_CONSTRUCTION" ) ); - } } bool construct::check_empty( const tripoint &p ) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 7a19596ca2ec1..4ce2b128634ef 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -245,6 +245,7 @@ void player_activity::serialize( JsonOut &json ) const json.member( "index", index ); json.member( "position", position ); json.member( "coords", coords ); + json.member( "coord_set", coord_set ); json.member( "name", name ); json.member( "targets", targets ); json.member( "placement", placement ); @@ -281,6 +282,7 @@ void player_activity::deserialize( JsonIn &jsin ) data.read( "index", index ); position = tmppos; data.read( "coords", coords ); + data.read( "coord_set", coord_set ); data.read( "name", name ); data.read( "targets", targets ); data.read( "placement", placement );