1515#include < queue>
1616#include < set>
1717#include < string>
18+ #include < tuple>
1819#include < unordered_set>
1920#include < utility>
2021#include < vector>
@@ -190,8 +191,10 @@ static const activity_id ACT_MILK( "ACT_MILK" );
190191static const activity_id ACT_MOP ( " ACT_MOP" );
191192static const activity_id ACT_MOVE_ITEMS ( " ACT_MOVE_ITEMS" );
192193static const activity_id ACT_MOVE_LOOT ( " ACT_MOVE_LOOT" );
194+ static const activity_id ACT_MULTIPLE_BUTCHER ( " ACT_MULTIPLE_BUTCHER" );
193195static const activity_id ACT_MULTIPLE_CHOP_PLANKS ( " ACT_MULTIPLE_CHOP_PLANKS" );
194196static const activity_id ACT_MULTIPLE_CHOP_TREES ( " ACT_MULTIPLE_CHOP_TREES" );
197+ static const activity_id ACT_MULTIPLE_CONSTRUCTION ( " ACT_MULTIPLE_CONSTRUCTION" );
195198static const activity_id ACT_MULTIPLE_CRAFT ( " ACT_MULTIPLE_CRAFT" );
196199static const activity_id ACT_MULTIPLE_DIS ( " ACT_MULTIPLE_DIS" );
197200static const activity_id ACT_MULTIPLE_FARM ( " ACT_MULTIPLE_FARM" );
@@ -4763,10 +4766,13 @@ std::optional<requirement_id> multi_zone_activity_actor::multi_activity_requirem
47634766
47644767void multi_zone_activity_actor::do_turn ( player_activity &act, Character &you )
47654768{
4769+ activity_id prior_act = get_type ();
47664770 simulate_turn ( act, you, false );
47674771 // If this activity still exists, end it
4768- if ( !act.is_null () && act.is_multi_type () ) {
4769- act.set_to_null ();
4772+ if ( !act.is_null () && ( prior_act == you.activity .id () ) ) {
4773+ // Nuke the current activity, leaving the backlog alone
4774+ // No multi_zone_activity_actor functions will be called from this point, so `this` can be nullptr
4775+ you.activity = player_activity ();
47704776 }
47714777}
47724778
@@ -4784,19 +4790,16 @@ bool multi_zone_activity_actor::simulate_turn( player_activity &act, Character &
47844790 // now loop through the work-spot tiles and judge whether its worth traveling to it yet
47854791 // or if we need to fetch something first.
47864792
4787- // check: if a fetch activity was assigned but there's nothing to fetch, restore previous activity from backlog
4793+ // check: if a fetch activity was assigned but there's nothing to fetch, end fetch activity (restoring backlog)
47884794 // may cause infinite loop if something goes wrong
4789- // TODO: check whether a fetch activity should be assigned before it is
4795+ // TODO: this could be moved to a multi_zone_activity_actor virtual function for post-location checking
47904796 if ( current_activity == ACT_FETCH_REQUIRED && src_sorted.empty () ) {
47914797 // remind what you failed to fetch
47924798 if ( !check_only ) {
4793- if ( !you.backlog .empty () ) {
4794- player_activity &act_prev = you.backlog .front ();
4795- if ( !act_prev.str_values .empty () && you.as_npc () ) {
4796- you.as_npc ()->job .fetch_history [act_prev.str_values .back ()] = calendar::turn;
4797- }
4798- }
4799+ fetch_required_activity_actor *as_fetch = static_cast <fetch_required_activity_actor *>( this );
4800+ as_fetch->set_npc_fetch_history ( you );
47994801 }
4802+ act.set_to_null ();
48004803 return true ;
48014804 }
48024805
@@ -4834,7 +4837,9 @@ bool multi_zone_activity_actor::simulate_turn( player_activity &act, Character &
48344837 continue ;
48354838 }
48364839
4837- // route to destination if needed
4840+ // route to destination if needed
4841+ // this stores a destination_activity that is resumed via
4842+ // can_resume_with_internal() in Character::assign_activity()
48384843 std::optional<bool > route_result = multi_activity_actor::route (
48394844 you, current_act_copy, src_bub, req_fail_reason, check_only );
48404845 if ( !route_result ) {
@@ -4972,18 +4977,16 @@ requirement_check_result multi_zone_activity_actor::check_requirements( Characte
49724977 bool tool_pickup = multi_activity_actor::activity_reason_picks_up_tools ( reason );
49734978 // is it even worth fetching anything if there isn't enough nearby?
49744979 if ( !multi_activity_actor::are_requirements_nearby ( tool_pickup ? loot_zone_spots : combined_spots,
4975- what_we_need, you,
4976- act_id, tool_pickup, src_loc ) ) {
4980+ what_we_need, you, act_id, tool_pickup, src_loc ) ) {
4981+ const tripoint_bub_ms you_pos_bub = you. pos_bub ();
49774982 if ( zone ) {
4978- you.add_msg_player_or_npc ( m_info,
4979- _ ( " The required items are not available to complete the %s task at zone %s." ), act_id.c_str (),
4980- zone->get_name (),
4981- _ ( " The required items are not available to complete the %s task at zone %s." ), act_id.c_str (),
4982- zone->get_name () );
4983+ add_msg_if_player_sees ( you_pos_bub, m_info, string_format (
4984+ _ ( " The required items are not available to complete the %s task at zone %s." ),
4985+ act_id.c_str (), zone->get_name () ) );
49834986 } else {
4984- you. add_msg_player_or_npc ( m_info,
4985- _ ( " The required items are not available to complete the %s task." ), act_id. c_str ( ),
4986- _ ( " The required items are not available to complete the %s task. " ), act_id.c_str () );
4987+ add_msg_if_player_sees ( you_pos_bub, m_info, string_format (
4988+ _ ( " The required items are not available to complete the %s task." ),
4989+ act_id.c_str () ) );
49874990 }
49884991 // TODO: this is hacky, move it
49894992 if ( reason == do_activity_reason::NEEDS_VEH_DECONST ||
@@ -4993,21 +4996,137 @@ requirement_check_result multi_zone_activity_actor::check_requirements( Characte
49934996 return requirement_check_result::SKIP_LOCATION;
49944997 } else {
49954998 if ( !check_only ) {
4996- return multi_activity_actor::fetch_requirements ( you, what_we_need, act_id,
4997- act_info, src, src_loc, src_set );
4999+ return fetch_requirements ( you, what_we_need, act_info, src, src_loc, src_set );
49985000 }
49995001 return requirement_check_result::RETURN_EARLY;
50005002 }
50015003 }
50025004 return requirement_check_result::SKIP_LOCATION_NO_MATCH;
50035005}
50045006
5007+ bool multi_zone_activity_actor::can_resume_with_internal ( const activity_actor &,
5008+ const Character & ) const
5009+ {
5010+ return true ;
5011+ }
5012+
5013+ requirement_check_result multi_zone_activity_actor::fetch_requirements ( Character &you,
5014+ requirement_id what_we_need, activity_reason_info &act_info, const tripoint_abs_ms &src,
5015+ const tripoint_bub_ms &src_loc, const std::unordered_set<tripoint_abs_ms> &src_set )
5016+ {
5017+
5018+ map &here = get_map ();
5019+
5020+ if ( you.as_npc () && you.as_npc ()->job .fetch_history .count ( what_we_need.str () ) != 0 &&
5021+ you.as_npc ()->job .fetch_history [what_we_need.str ()] == calendar::turn ) {
5022+ // this may be a failed fetch already. Quit task to avoid infinite loop.
5023+ you.activity = player_activity ();
5024+ you.backlog .clear ();
5025+ multi_activity_actor::check_npc_revert ( you );
5026+ return requirement_check_result::SKIP_LOCATION;
5027+ }
5028+ // come back here after successfully fetching your stuff
5029+ std::vector<tripoint_bub_ms> local_src_set;
5030+ local_src_set.reserve ( src_set.size () );
5031+ for ( const tripoint_abs_ms &elem : src_set ) {
5032+ local_src_set.push_back ( here.get_bub ( elem ) );
5033+ }
5034+ std::vector<tripoint_bub_ms> candidates;
5035+ for ( const tripoint_bub_ms &point_elem :
5036+ here.points_in_radius ( src_loc, PICKUP_RANGE - 1 , 0 ) ) {
5037+ // we don't want to place the components where they could interfere with our ( or someone else's ) construction spots
5038+ if ( ( std::find ( local_src_set.begin (), local_src_set.end (),
5039+ point_elem ) != local_src_set.end () ) || !here.can_put_items_ter_furn ( point_elem ) ) {
5040+ continue ;
5041+ }
5042+ candidates.push_back ( point_elem );
5043+ }
5044+ if ( candidates.empty () ) {
5045+ you.activity = player_activity ();
5046+ you.backlog .clear ();
5047+ multi_activity_actor::check_npc_revert ( you );
5048+ return requirement_check_result::SKIP_LOCATION_NO_LOCATION;
5049+ }
5050+
5051+ you.assign_activity ( fetch_required_activity_actor ( what_we_need, act_info.reason , here.get_abs (
5052+ candidates[std::max ( 0 , static_cast <int >( candidates.size () / 2 ) )] ), src ) );
5053+ return requirement_check_result::RETURN_EARLY;
5054+ }
5055+
50055056void multi_zone_activity_actor::serialize ( JsonOut &jsout ) const
50065057{
50075058 jsout.start_object ();
50085059 jsout.end_object ();
50095060}
50105061
5062+ activity_reason_info fetch_required_activity_actor::multi_activity_can_do (
5063+ Character &you, const tripoint_bub_ms &src_loc )
5064+ {
5065+ return multi_activity_actor::fetch_can_do ( ACT_FETCH_REQUIRED, you,
5066+ src_loc );
5067+ }
5068+
5069+ void fetch_required_activity_actor::set_npc_fetch_history ( Character &you )
5070+ {
5071+ if ( !fetch_requirements.is_empty () && you.as_npc () ) {
5072+ you.as_npc ()->job .fetch_history [fetch_requirements.str ()] = calendar::turn;
5073+ }
5074+ }
5075+
5076+ bool fetch_required_activity_actor::fetch_activity_valid ( const Character &you ) const
5077+ {
5078+ if ( you.backlog .empty () ) {
5079+ debugmsg ( " fetch activity assigned without a corresponding multi-activity" );
5080+ return false ;
5081+ }
5082+ return true ;
5083+ }
5084+
5085+ std::unordered_set<tripoint_abs_ms> fetch_required_activity_actor::multi_activity_locations (
5086+ Character &you )
5087+ {
5088+ map &here = get_map ();
5089+ std::unordered_set<tripoint_abs_ms> src_set;
5090+
5091+ // get the right zones for the items in the requirements.
5092+ // we previously checked if the items are nearby before we set the fetch task
5093+ // but we will check again later, to be sure nothings changed.
5094+ std::vector<std::tuple<tripoint_bub_ms, itype_id, int >> mental_map =
5095+ requirements_map ( you, MAX_VIEW_DISTANCE );
5096+ for ( const auto &elem : mental_map ) {
5097+ const tripoint_bub_ms &elem_point = std::get<0 >( elem );
5098+ src_set.insert ( here.get_abs ( elem_point ) );
5099+ }
5100+ multi_activity_actor::prune_dangerous_field_locations ( src_set );
5101+
5102+ return src_set;
5103+ }
5104+
5105+ bool fetch_required_activity_actor::multi_activity_do ( Character &you,
5106+ const activity_reason_info &act_info,
5107+ const tripoint_abs_ms &, const tripoint_bub_ms &src_loc )
5108+ {
5109+ const do_activity_reason &reason = act_info.reason ;
5110+
5111+ if ( reason == do_activity_reason::CAN_DO_FETCH ) {
5112+ if ( fetch_activity ( you, src_loc, ACT_FETCH_REQUIRED, MAX_VIEW_DISTANCE ) ) {
5113+ if ( !you.is_npc () ) {
5114+ // Npcs will automatically start the next thing in the backlog, players need to be manually prompted
5115+ // Because some player activities are necessarily not marked as auto-resume.
5116+ activity_handlers::resume_for_multi_activities ( you );
5117+ }
5118+ return false ;
5119+ }
5120+ }
5121+ return true ;
5122+ }
5123+
5124+ std::unique_ptr<activity_actor> fetch_required_activity_actor::deserialize ( JsonValue & )
5125+ {
5126+ fetch_required_activity_actor actor;
5127+ return actor.clone ();
5128+ }
5129+
50115130std::unique_ptr<activity_actor> multi_mine_activity_actor::deserialize ( JsonValue & )
50125131{
50135132 multi_mine_activity_actor actor;
@@ -8114,6 +8233,40 @@ std::unique_ptr<activity_actor> build_construction_activity_actor::deserialize(
81148233 return actor.clone ();
81158234}
81168235
8236+ activity_reason_info multi_build_construction_activity_actor::multi_activity_can_do (
8237+ Character &you, const tripoint_bub_ms &src_loc )
8238+ {
8239+ return multi_activity_actor::construction_can_do ( ACT_MULTIPLE_CONSTRUCTION, you,
8240+ src_loc );
8241+ }
8242+
8243+ std::optional<requirement_id> multi_build_construction_activity_actor::multi_activity_requirements (
8244+ Character &you,
8245+ activity_reason_info &act_info, const tripoint_bub_ms &src_loc, const zone_data * )
8246+ {
8247+ return multi_activity_actor::construction_requirements ( you, act_info, src_loc );
8248+ }
8249+
8250+ std::unordered_set<tripoint_abs_ms>
8251+ multi_build_construction_activity_actor::multi_activity_locations (
8252+ Character &you )
8253+ {
8254+ return multi_activity_actor::construction_locations ( you, get_type () );
8255+ }
8256+
8257+ bool multi_build_construction_activity_actor::multi_activity_do ( Character &you,
8258+ const activity_reason_info &act_info,
8259+ const tripoint_abs_ms &src, const tripoint_bub_ms &src_loc )
8260+ {
8261+ return multi_activity_actor::construction_do ( you, act_info, src, src_loc );
8262+ }
8263+
8264+ std::unique_ptr<activity_actor> multi_build_construction_activity_actor::deserialize ( JsonValue & )
8265+ {
8266+ multi_build_construction_activity_actor actor;
8267+ return actor.clone ();
8268+ }
8269+
81178270void reel_cable_activity_actor::start ( player_activity &act, Character & )
81188271{
81198272 act.moves_total = moves_total;
@@ -12259,6 +12412,33 @@ std::unique_ptr<activity_actor> butchery_activity_actor::deserialize( JsonValue
1225912412 return actor.clone ();
1226012413}
1226112414
12415+ activity_reason_info multi_butchery_activity_actor::multi_activity_can_do (
12416+ Character &you, const tripoint_bub_ms &src_loc )
12417+ {
12418+ return multi_activity_actor::butcher_can_do ( ACT_MULTIPLE_BUTCHER, you,
12419+ src_loc );
12420+ }
12421+
12422+ std::optional<requirement_id> multi_butchery_activity_actor::multi_activity_requirements (
12423+ Character &you,
12424+ activity_reason_info &act_info, const tripoint_bub_ms &src_loc, const zone_data * )
12425+ {
12426+ return multi_activity_actor::butcher_requirements ( you, act_info, src_loc );
12427+ }
12428+
12429+ bool multi_butchery_activity_actor::multi_activity_do ( Character &you,
12430+ const activity_reason_info &act_info,
12431+ const tripoint_abs_ms &src, const tripoint_bub_ms &src_loc )
12432+ {
12433+ return multi_activity_actor::butcher_do ( you, act_info, src, src_loc );
12434+ }
12435+
12436+ std::unique_ptr<activity_actor> multi_butchery_activity_actor::deserialize ( JsonValue & )
12437+ {
12438+ multi_butchery_activity_actor actor;
12439+ return actor.clone ();
12440+ }
12441+
1226212442void wait_activity_actor::start ( player_activity &act, Character & )
1226312443{
1226412444 act.moves_total = to_moves<int >( initial_wait_time );
@@ -12897,6 +13077,7 @@ deserialize_functions = {
1289713077 { ACT_DROP, &drop_activity_actor::deserialize },
1289813078 { ACT_E_FILE, &efile_activity_actor::deserialize },
1289913079 { ACT_EBOOKSAVE, &ebooksave_activity_actor::deserialize },
13080+ { ACT_FETCH_REQUIRED, &fetch_required_activity_actor::deserialize },
1290013081 { ACT_FIELD_DRESS, &butchery_activity_actor::deserialize },
1290113082 { ACT_FIRSTAID, &firstaid_activity_actor::deserialize },
1290213083 { ACT_FISH, &fish_activity_actor::deserialize },
@@ -12926,8 +13107,10 @@ deserialize_functions = {
1292613107 { ACT_MOP, &mop_activity_actor::deserialize },
1292713108 { ACT_MOVE_ITEMS, &move_items_activity_actor::deserialize },
1292813109 { ACT_MOVE_LOOT, &zone_sort_activity_actor::deserialize },
13110+ { ACT_MULTIPLE_BUTCHER, &multi_butchery_activity_actor::deserialize },
1292913111 { ACT_MULTIPLE_CHOP_PLANKS, &multi_chop_planks_activity_actor::deserialize },
1293013112 { ACT_MULTIPLE_CHOP_TREES, &multi_chop_trees_activity_actor::deserialize },
13113+ { ACT_MULTIPLE_CONSTRUCTION, &multi_build_construction_activity_actor::deserialize },
1293113114 { ACT_MULTIPLE_CRAFT, &multi_craft_activity_actor::deserialize },
1293213115 { ACT_MULTIPLE_DIS, &multi_disassemble_activity_actor::deserialize },
1293313116 { ACT_MULTIPLE_FARM, &multi_farm_activity_actor::deserialize },
0 commit comments