@@ -56,6 +56,7 @@ This skeletal logic has not been kept up-to-date since ~v0.5
5656#include " plugin.h"
5757#include " inlines.h"
5858#include " channel-manager.h"
59+ #include " active-job-manager.h"
5960#include " tile-cache.h"
6061
6162#include < Debug.h>
@@ -68,7 +69,9 @@ This skeletal logic has not been kept up-to-date since ~v0.5
6869#include < df/report.h>
6970#include < df/tile_traffic.h>
7071#include < df/world.h>
72+ #include < df/unit.h>
7173
74+ #include < ranges>
7275#include < cinttypes>
7376#include < unordered_map>
7477#include < unordered_set>
@@ -114,44 +117,9 @@ enum SettingConfigData {
114117};
115118
116119// dig-now.cpp
117- df::coord simulate_fall (const df::coord &pos) {
118- if unlikely (!Maps::isValidTilePos (pos)) {
119- ERR (plugin).print (" Error: simulate_fall(" COORD " ) - invalid coordinate\n " , COORDARGS (pos));
120- return {};
121- }
122- df::coord resting_pos (pos);
123-
124- while (Maps::ensureTileBlock (resting_pos)) {
125- df::tiletype tt = *Maps::getTileType (resting_pos);
126- if (isWalkable (tt))
127- break ;
128- --resting_pos.z ;
129- }
130-
131- return resting_pos;
132- }
133-
134- df::coord simulate_area_fall (const df::coord &pos) {
135- df::coord neighbours[8 ]{};
136- get_neighbours (pos, neighbours);
137- df::coord lowest = simulate_fall (pos);
138- for (auto p : neighbours) {
139- if unlikely (!Maps::isValidTilePos (p)) continue ;
140- auto nlow = simulate_fall (p);
141- if (nlow.z < lowest.z ) {
142- lowest = nlow;
143- }
144- }
145- return lowest;
146- }
147120
148121namespace CSP {
149- std::unordered_map<df::unit*, int32_t > endangered_units;
150- std::unordered_map<df::job*, int32_t > job_id_map;
151- std::unordered_map<int32_t , df::job*> active_jobs;
152- std::unordered_map<int32_t , df::unit*> active_workers;
153-
154- std::unordered_map<int32_t , df::coord> last_safe;
122+ ActiveJobManager active_job_manager;
155123 std::unordered_set<df::coord> dignow_queue;
156124
157125 static int32_t last_tick = 0 ;
@@ -162,11 +130,7 @@ namespace CSP {
162130 void ClearData () {
163131 ChannelManager::Get ().destroy_groups ();
164132 dignow_queue.clear ();
165- last_safe.clear ();
166- endangered_units.clear ();
167- active_workers.clear ();
168- active_jobs.clear ();
169- job_id_map.clear ();
133+ active_job_manager.clear ();
170134 }
171135
172136 void SaveSettings () {
@@ -214,7 +178,6 @@ namespace CSP {
214178 ERR (plugin).print (" %s\n " , e.what ());
215179 }
216180 }
217- active_workers.clear ();
218181 }
219182
220183 void UnpauseEvent (bool full_scan = false ){
@@ -225,85 +188,23 @@ namespace CSP {
225188 }
226189
227190 void JobStartedEvent (color_ostream &out, void * j) {
228- if (enabled && World::isFortressMode () && Maps::IsValid ()) {
229- TRACE (jobs).print (" JobStartedEvent()\n " );
230- auto job = (df::job*) j;
231- // validate job type
232- if (ChannelManager::Get ().exists (job->pos )) {
233- DEBUG (jobs).print (" valid channel job:\n " );
234- df::unit* worker = Job::getWorker (job);
235- // there is a valid worker (living citizen) on the job? right..
236- if (worker && Units::isAlive (worker) && Units::isCitizen (worker)) {
237- ChannelManager::Get ().jobs .mark_active (job->pos );
238- if (config.riskaverse ) {
239- if (ChannelManager::Get ().jobs .possible_cavein (job->pos )) {
240- cancel_job (job);
241- ChannelManager::Get ().manage_one (job->pos , true , true );
242- } else {
243- ChannelManager::Get ().manage_group (job->pos , true , false );
244- }
245- }
246- DEBUG (jobs).print (" valid worker:\n " );
247- // track workers on jobs
248- df::coord &pos = job->pos ;
249- TRACE (jobs).print (" -> Starting job at (" COORD " )\n " , COORDARGS (pos));
250- if (config.monitoring || config.resurrect ) {
251- job_id_map.emplace (job, job->id );
252- active_jobs.emplace (job->id , job);
253- active_workers[job->id ] = worker;
254- if (config.resurrect ) {
255- // this is the only place we can be 100% sure of "safety"
256- // (excluding deadly enemies that will have arrived)
257- last_safe[worker->id ] = worker->pos ;
258- }
259- }
260- // set tile to restricted
261- TRACE (jobs).print (" setting job tile to restricted\n " );
262- Maps::getTileDesignation (job->pos )->bits .traffic = df::tile_traffic::Restricted;
263- }
264- }
265- TRACE (jobs).print (" <- JobStartedEvent() exits normally\n " );
191+ if (!enabled || World::isFortressMode () || Maps::IsValid ()) {
192+ return ;
193+ }
194+ auto job = static_cast <df::job*>(j);
195+ if likely (is_channel_job (job)) {
196+ active_job_manager.on_job_start (job);
266197 }
267198 }
268199
269200 void JobCompletedEvent (color_ostream &out, void * j) {
270- if (enabled && World::isFortressMode () && Maps::IsValid ()) {
271- TRACE (jobs).print (" JobCompletedEvent()\n " );
272- auto job = (df::job*) j;
273- // we only care if the job is a channeling one
274- if (ChannelManager::Get ().exists (job->pos )) {
275- ChannelManager::Get ().manage_group (job->pos , true , false );
276- // check job outcome
277- auto block = Maps::getTileBlock (job->pos );
278- df::coord local (job->pos );
279- local.x = local.x % 16 ;
280- local.y = local.y % 16 ;
281- // verify completion
282- if (TileCache::Get ().hasChanged (job->pos , block->tiletype [Coord (local)])) {
283- // the job can be considered done
284- df::coord below (job->pos );
285- below.z --;
286- DEBUG (jobs).print (" -> (" COORD " ) is marked done, managing group below.\n " , COORDARGS (job->pos ));
287- // mark done and manage below (and the rest of the group, if there were cavein candidates)
288- block->designation [Coord (local)].bits .traffic = df::tile_traffic::Normal;
289- ChannelManager::Get ().mark_done (job->pos );
290- ChannelManager::Get ().manage_group (below);
291- if (config.resurrect ) {
292- // this is the only place we can be 100% sure of "safety"
293- // (excluding deadly enemies that may have arrived, and future digging)
294- if (active_workers.count (job->id )) {
295- df::unit* worker = active_workers[job->id ];
296- last_safe[worker->id ] = worker->pos ;
297- }
298- }
299- }
300- // clean up
301- auto jp = active_jobs[job->id ];
302- job_id_map.erase (jp);
303- active_workers.erase (job->id );
304- active_jobs.erase (job->id );
305- }
306- TRACE (jobs).print (" JobCompletedEvent() exits\n " );
201+ if (!enabled || World::isFortressMode () || Maps::IsValid ()) {
202+ return ;
203+ }
204+ auto job = static_cast <df::job*>(j);
205+ // we only care if the job is a channeling one
206+ if likely (is_channel_job (job)) {
207+ active_job_manager.on_job_completed (out, job);
307208 }
308209 }
309210
@@ -315,59 +216,15 @@ namespace CSP {
315216 WARN (plugin).print (" Error: NewReportEvent() received an invalid report_id - a report* cannot be found\n " );
316217 return ;
317218 }
318- switch (report->type ) {
319- case announcement_type::CANCEL_JOB:
320- if (config.insta_dig ) {
321- if (report->text .find (" cancels Dig" ) != std::string::npos ||
322- report->text .find (" path" ) != std::string::npos) {
323-
324- dignow_queue.emplace (report->pos );
325- }
326- DEBUG (plugin).print (" %d, pos: " COORD " , pos2: " COORD " \n %s\n " , report_id, COORDARGS (report->pos ),
327- COORDARGS (report->pos2 ), report->text .c_str ());
328- }
329- break ;
330- case announcement_type::CAVE_COLLAPSE:
331- if (config.resurrect ) {
332- DEBUG (plugin).print (" CAVE IN\n %d, pos: " COORD " , pos2: " COORD " \n %s\n " , report_id, COORDARGS (report->pos ),
333- COORDARGS (report->pos2 ), report->text .c_str ());
334-
335- df::coord below = report->pos ;
336- below.z -= 1 ;
337- below = simulate_area_fall (below);
338- df::coord areaMin{report->pos };
339- df::coord areaMax{areaMin};
340- areaMin.x -= 15 ;
341- areaMin.y -= 15 ;
342- areaMax.x += 15 ;
343- areaMax.y += 15 ;
344- areaMin.z = below.z ;
345- areaMax.z += 1 ;
346- std::vector<df::unit*> units;
347- Units::getUnitsInBox (units, COORDARGS (areaMin), COORDARGS (areaMax));
348- for (auto unit: units) {
349- endangered_units[unit] = tick;
350- DEBUG (plugin).print (" [id %d] was near a cave in.\n " , unit->id );
351- }
352- for (auto unit : world->units .all ) {
353- if (last_safe.count (unit->id )) {
354- endangered_units[unit] = tick;
355- DEBUG (plugin).print (" [id %d] is/was a worker, we'll track them too.\n " , unit->id );
356- }
357- }
358- }
359- break ;
360- default :
361- break ;
362- }
219+ active_job_manager.on_report_event (report);
363220 }
364221
365222 void OnUpdate (color_ostream &out) {
366223 if (World::ReadPauseState ())
367224 return ;
368225
226+ active_job_manager.on_update (out);
369227 int32_t tick = world->frame_counter ;
370-
371228 // Refreshing the group data with full scanning
372229 if (tick - last_refresh_tick >= config.refresh_freq ) {
373230 last_refresh_tick = tick;
@@ -384,116 +241,6 @@ namespace CSP {
384241 UnpauseEvent (false );
385242 TRACE (monitor).print (" OnUpdate() refresh done\n " );
386243 }
387-
388- // Clean up stale df::job*
389- if ((config.monitoring || config.resurrect ) && tick - last_tick >= 1 ) {
390- last_tick = tick;
391- // make note of valid jobs
392- std::unordered_map<int32_t , df::job*> valid_jobs;
393- for (df::job_list_link* link = &world->jobs .list ; link != nullptr ; link = link->next ) {
394- df::job* job = link->item ;
395- if (job && active_jobs.count (job->id )) {
396- valid_jobs.emplace (job->id , job);
397- }
398- }
399-
400- // erase the active jobs that aren't valid
401- std::unordered_set<df::job*> erase;
402- map_value_difference (active_jobs, valid_jobs, erase);
403- for (auto j : erase) {
404- auto id = job_id_map[j];
405- job_id_map.erase (j);
406- active_jobs.erase (id);
407- active_workers.erase (id);
408- }
409- }
410-
411- // Monitoring Active and Resurrecting Dead
412- if (config.monitoring && tick - last_monitor_tick >= config.monitor_freq ) {
413- last_monitor_tick = tick;
414- TRACE (monitor).print (" OnUpdate() monitoring now\n " );
415-
416- // iterate active jobs
417- for (auto pair: active_jobs) {
418- df::job* job = pair.second ;
419- df::unit* unit = active_workers[job->id ];
420- if (!unit) continue ;
421- if (!Maps::isValidTilePos (job->pos )) continue ;
422- TRACE (monitor).print (" -> check for job in tracking\n " );
423- if (Units::isAlive (unit)) {
424- if (!config.monitoring ) continue ;
425- TRACE (monitor).print (" -> compare positions of worker and job\n " );
426-
427- // check for fall safety
428- if (unit->pos == job->pos && !is_safe_fall (job->pos )) {
429- // unsafe
430- WARN (monitor).print (" -> unsafe job\n " );
431- Job::removeWorker (job);
432-
433- // decide to insta-dig or marker mode
434- if (config.insta_dig ) {
435- // delete the job
436- Job::removeJob (job);
437- // queue digging the job instantly
438- dignow_queue.emplace (job->pos );
439- DEBUG (monitor).print (" -> insta-dig\n " );
440- } else if (config.resurrect ) {
441- endangered_units.emplace (unit, tick);
442- } else {
443- // set marker mode
444- Maps::getTileOccupancy (job->pos )->bits .dig_marked = true ;
445-
446- // prevent algorithm from re-enabling designation
447- for (auto &be: Maps::getBlock (job->pos )->block_events ) {
448- if (auto bsedp = virtual_cast<df::block_square_event_designation_priorityst>(
449- be)) {
450- df::coord local (job->pos );
451- local.x = local.x % 16 ;
452- local.y = local.y % 16 ;
453- bsedp->priority [Coord (local)] = config.ignore_threshold * 1000 + 1 ;
454- break ;
455- }
456- }
457- DEBUG (monitor).print (" -> set marker mode\n " );
458- }
459- }
460- } else if (config.resurrect ) {
461- resurrect (out, unit->id );
462- if (last_safe.count (unit->id )) {
463- df::coord lowest = simulate_fall (last_safe[unit->id ]);
464- Units::teleport (unit, lowest);
465- }
466- }
467- }
468- TRACE (monitor).print (" OnUpdate() monitoring done\n " );
469- }
470-
471- // Resurrect Dead Workers
472- if (config.resurrect && tick - last_resurrect_tick >= 1 ) {
473- last_resurrect_tick = tick;
474-
475- // clean up any "endangered" workers that have been tracked 100 ticks or more
476- for (auto iter = endangered_units.begin (); iter != endangered_units.end ();) {
477- if (tick - iter->second >= 1200 ) { // keep watch 1 day
478- DEBUG (plugin).print (" It has been one day since [id %d]'s last incident.\n " , iter->first ->id );
479- iter = endangered_units.erase (iter);
480- continue ;
481- }
482- ++iter;
483- }
484-
485- // resurrect any dead units
486- for (auto pair : endangered_units) {
487- auto unit = pair.first ;
488- if (!Units::isAlive (unit)) {
489- resurrect (out, unit->id );
490- if (last_safe.count (unit->id )) {
491- df::coord lowest = simulate_fall (last_safe[unit->id ]);
492- Units::teleport (unit, lowest);
493- }
494- }
495- }
496- }
497244 }
498245}
499246
@@ -616,7 +363,7 @@ command_result channel_safely(color_ostream &out, std::vector<std::string> ¶
616363 // if this is a fresh start
617364 if (state && !config.resurrect ) {
618365 // we need a fresh start
619- CSP::active_workers. clear ();
366+ CSP::ClearData ();
620367 }
621368 }
622369 } else if (parameters[1 ] == " risk-averse" ) {
@@ -632,7 +379,7 @@ command_result channel_safely(color_ostream &out, std::vector<std::string> ¶
632379 // if this is a fresh start
633380 if (state && !config.monitoring ) {
634381 // we need a fresh start
635- CSP::active_workers. clear ();
382+ CSP::ClearData ();
636383 }
637384 }
638385 } else if (parameters[1 ] == " refresh-freq" && set && parameters.size () == 3 ) {
0 commit comments