Skip to content

Commit c27c38e

Browse files
committed
Remove tired or hungry siege operators from duty when at peace.
The threshold is set at the level when they start to blink - normally they would continue on with the job until they get a thirsty/hungry thought, but immediately run off to eat if they lose the job (thus refusing to load the engine after firing it). The code checks for active sieges and whether there is a free replacement unit.
1 parent fc98263 commit c27c38e

7 files changed

Lines changed: 146 additions & 2 deletions

File tree

Lua API.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,14 @@ <h3><a class="toc-backref" href="#id21">Job module</a></h3>
12201220
<li><p class="first"><tt class="docutils literal">dfhack.job.getWorker(job)</tt></p>
12211221
<p>Returns the unit performing the job.</p>
12221222
</li>
1223+
<li><p class="first"><tt class="docutils literal">dfhack.job.setJobCooldown(building,worker,timeout)</tt></p>
1224+
<p>Prevent the worker from taking jobs at the specified workshop for the specified time.
1225+
This doesn't decrease the timeout in any circumstances.</p>
1226+
</li>
1227+
<li><p class="first"><tt class="docutils literal">dfhack.job.removeWorker(job,timeout)</tt></p>
1228+
<p>Removes the worker from the specified workshop job, and sets the cooldown.
1229+
Returns <em>true</em> on success.</p>
1230+
</li>
12231231
<li><p class="first"><tt class="docutils literal">dfhack.job.checkBuildingsNow()</tt></p>
12241232
<p>Instructs the game to check buildings for jobs next frame and assign workers.</p>
12251233
</li>

Lua API.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,16 @@ Job module
938938

939939
Returns the unit performing the job.
940940

941+
* ``dfhack.job.setJobCooldown(building,worker,timeout)``
942+
943+
Prevent the worker from taking jobs at the specified workshop for the specified time.
944+
This doesn't decrease the timeout in any circumstances.
945+
946+
* ``dfhack.job.removeWorker(job,timeout)``
947+
948+
Removes the worker from the specified workshop job, and sets the cooldown.
949+
Returns *true* on success.
950+
941951
* ``dfhack.job.checkBuildingsNow()``
942952

943953
Instructs the game to check buildings for jobs next frame and assign workers.

NEWS

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,15 @@ DFHack future
1818
Misc improvements:
1919
- digfort: improved csv parsing, add start() comment handling
2020
- exterminate: allow specifying a caste (exterminate gob:male)
21-
- siege-engine: engine quality and distance to target now affect accuracy;
22-
firing the siege engine produces a combat report.
2321
- createitem: in adventure mode it now defaults to the controlled unit as maker.
2422

23+
Siege engine plugin:
24+
- engine quality and distance to target now affect accuracy
25+
- firing the siege engine at a target produces a combat report
26+
- improved movement speed computation for meandering units
27+
- operators in Prepare To Fire mode are released from duty once
28+
hungry/thirsty if there is a free replacement
29+
2530
DFHack v0.34.11-r4
2631

2732
New commands:

library/LuaApi.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,6 +1331,8 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
13311331
WRAPM(Job,getSpecificRef),
13321332
WRAPM(Job,getHolder),
13331333
WRAPM(Job,getWorker),
1334+
WRAPM(Job,setJobCooldown),
1335+
WRAPM(Job,removeWorker),
13341336
WRAPM(Job,checkBuildingsNow),
13351337
WRAPM(Job,checkDesignationsNow),
13361338
WRAPM(Job,isSuitableItem),

library/include/modules/Job.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ namespace DFHack
6363
DFHACK_EXPORT df::building *getHolder(df::job *job);
6464
DFHACK_EXPORT df::unit *getWorker(df::job *job);
6565

66+
DFHACK_EXPORT void setJobCooldown(df::building *workshop, df::unit *worker, int cooldown = 100);
67+
DFHACK_EXPORT bool removeWorker(df::job *job, int cooldown = 100);
68+
6669
// Instruct the game to check and assign workers
6770
DFHACK_EXPORT void checkBuildingsNow();
6871
DFHACK_EXPORT void checkDesignationsNow();

library/modules/Job.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ using namespace std;
4444
#include "DataDefs.h"
4545
#include "df/world.h"
4646
#include "df/ui.h"
47+
#include "df/unit.h"
48+
#include "df/building.h"
4749
#include "df/job.h"
4850
#include "df/job_item.h"
4951
#include "df/job_list_link.h"
@@ -286,6 +288,63 @@ df::unit *DFHack::Job::getWorker(df::job *job)
286288
return NULL;
287289
}
288290

291+
void DFHack::Job::setJobCooldown(df::building *workshop, df::unit *worker, int cooldown)
292+
{
293+
CHECK_NULL_POINTER(workshop);
294+
CHECK_NULL_POINTER(worker);
295+
296+
if (cooldown <= 0)
297+
return;
298+
299+
int idx = linear_index(workshop->job_claim_suppress, &df::building::T_job_claim_suppress::unit, worker);
300+
301+
if (idx < 0)
302+
{
303+
auto obj = new df::building::T_job_claim_suppress;
304+
obj->unit = worker;
305+
obj->timer = cooldown;
306+
workshop->job_claim_suppress.push_back(obj);
307+
}
308+
else
309+
{
310+
auto obj = workshop->job_claim_suppress[idx];
311+
obj->timer = std::max(obj->timer, cooldown);
312+
}
313+
}
314+
315+
bool DFHack::Job::removeWorker(df::job *job, int cooldown)
316+
{
317+
CHECK_NULL_POINTER(job);
318+
319+
if (job->flags.bits.special)
320+
return false;
321+
322+
auto holder = getHolder(job);
323+
if (!holder || linear_index(holder->jobs,job) < 0)
324+
return false;
325+
326+
for (size_t i = 0; i < job->general_refs.size(); i++)
327+
{
328+
VIRTUAL_CAST_VAR(ref, df::general_ref_unit_workerst, job->general_refs[i]);
329+
if (!ref)
330+
continue;
331+
332+
auto worker = ref->getUnit();
333+
if (!worker || worker->job.current_job != job)
334+
return false;
335+
336+
setJobCooldown(holder, worker, cooldown);
337+
338+
vector_erase_at(job->general_refs, i);
339+
worker->job.current_job = NULL;
340+
delete ref;
341+
342+
return true;
343+
}
344+
345+
return false;
346+
}
347+
289348
void DFHack::Job::checkBuildingsNow()
290349
{
291350
if (df::global::process_jobs)

plugins/siege-engine.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include "df/strain_type.h"
5656
#include "df/material.h"
5757
#include "df/flow_type.h"
58+
#include "df/invasion_info.h"
5859

5960
#include "MiscUtils.h"
6061

@@ -1459,6 +1460,55 @@ static int computeNearbyWeight(lua_State *L)
14591460
return 0;
14601461
}
14611462

1463+
static bool isTired(df::unit *worker)
1464+
{
1465+
return worker->counters2.exhaustion >= 1000 ||
1466+
worker->counters2.thirst_timer >= 25000 ||
1467+
worker->counters2.hunger_timer >= 50000 ||
1468+
worker->counters2.sleepiness_timer >= 57600;
1469+
}
1470+
1471+
static void releaseTiredWorker(EngineInfo *engine, df::job *job, df::unit *worker)
1472+
{
1473+
// If not in siege
1474+
auto &sieges = ui->invasions.list;
1475+
1476+
for (size_t i = 0; i < sieges.size(); i++)
1477+
if (sieges[i]->flags.bits.active)
1478+
return;
1479+
1480+
// And there is a free replacement
1481+
auto &others = world->units.active;
1482+
1483+
for (size_t i = 0; i < others.size(); i++)
1484+
{
1485+
auto unit = others[i];
1486+
1487+
if (unit == worker ||
1488+
unit->job.current_job || !unit->status.labors[unit_labor::SIEGEOPERATE] ||
1489+
!Units::isCitizen(unit) || Units::getMiscTrait(unit, misc_trait_type::OnBreak) ||
1490+
isTired(unit) || !Maps::canWalkBetween(job->pos, unit->pos))
1491+
continue;
1492+
1493+
int skill2 = Units::getEffectiveSkill(unit, job_skill::SIEGEOPERATE);
1494+
1495+
if (skill2 >= engine->profile.min_level && skill2 <= engine->profile.max_level)
1496+
{
1497+
// Remove the worker and request a recheck
1498+
if (Job::removeWorker(job))
1499+
{
1500+
color_ostream_proxy out(Core::getInstance().getConsole());
1501+
out.print("Released tired operator %d from siege engine.\n", worker->id);
1502+
1503+
if (df::global::process_jobs)
1504+
*df::global::process_jobs = true;
1505+
}
1506+
1507+
return;
1508+
}
1509+
}
1510+
}
1511+
14621512
/*
14631513
* Projectile hook
14641514
*/
@@ -1796,6 +1846,7 @@ struct building_hook : df::building_siegeenginest {
17961846
{
17971847
auto job = jobs[0];
17981848
bool save_op = false;
1849+
bool load_op = false;
17991850

18001851
switch (job->job_type)
18011852
{
@@ -1830,12 +1881,18 @@ struct building_hook : df::building_siegeenginest {
18301881
// fallthrough
18311882

18321883
case job_type::LoadBallista:
1884+
load_op = true;
1885+
18331886
case job_type::FireCatapult:
18341887
case job_type::FireBallista:
18351888
if (auto worker = Job::getWorker(job))
18361889
{
18371890
engine->operator_id = worker->id;
18381891
engine->operator_frame = world->frame_counter;
1892+
1893+
if (action == PrepareToFire && !load_op &&
1894+
(world->frame_counter%100) == 0 && isTired(worker))
1895+
releaseTiredWorker(engine, job, worker);
18391896
}
18401897
break;
18411898

0 commit comments

Comments
 (0)