Add job remove method

Job remove eliminates a job's worker & holder references, if any, puts
the worker on cd, if appropriate, removes the job's postings, eliminates
the job from the global linked list, and then finally deletes it.  This
code was tested by incorporating it into autochop and it does make the
plugin work.  However, chop jobs don't have holder building references,
and anyway, with DF being 90% edge case by volume, this could use a heck
of a lot more testing.

I saw elsewhere code that prevented worker removal if the job was a
special job, and that made me feel funny so I made the job remove method
not work if the job is a special job.
develop
Stephen Baynham 2016-11-17 00:11:15 -08:00
parent 98893793bd
commit 74f5df99db
2 changed files with 69 additions and 1 deletions

@ -66,6 +66,11 @@ namespace DFHack
DFHACK_EXPORT void setJobCooldown(df::building *workshop, df::unit *worker, int cooldown = 100);
DFHACK_EXPORT bool removeWorker(df::job *job, int cooldown = 100);
// Delete a job & remove all refs from everywhere.
// This method DELETES the job object! Everything related to it will be wiped
// clean from the earth, so make sure you pull what you need out before calling this!
DFHACK_EXPORT void removeJob(df::job *job);
// Instruct the game to check and assign workers
DFHACK_EXPORT void checkBuildingsNow();
DFHACK_EXPORT void checkDesignationsNow();

@ -212,7 +212,7 @@ void DFHack::Job::printJobDetails(color_ostream &out, df::job *job)
out.color(job->flags.bits.suspend ? COLOR_DARKGREY : COLOR_GREY);
out << "Job " << job->id << ": " << ENUM_KEY_STR(job_type,job->job_type);
if (job->flags.whole)
out << " (" << bitfield_to_string(job->flags) << ")";
out << " (" << bitfield_to_string(job->flags) << ")";
out << endl;
out.reset_color();
@ -304,6 +304,68 @@ void DFHack::Job::setJobCooldown(df::building *workshop, df::unit *worker, int c
}
}
void DFHack::Job::removeJob(df::job *job) {
using df::global::world;
CHECK_NULL_POINTER(job);
if (job->flags.bits.special) //I don't think you can cancel these, because DF wasn't build to expect it?
return;
//As far as I know there are only two general refs jobs have, the unit assigned to work it (if any)
//and the workshop it was created at (if any). It's possible there are others, so we go ahead and wipe all
//refs, but these two are the only ones that we really handle with any intelligence. If other refs
//exist that might have return-references that need to be cleared, that needs to be implemented!!!!
auto holderRef = getGeneralRef(job, general_ref_type::BUILDING_HOLDER);
auto workerRef = getGeneralRef(job, general_ref_type::UNIT_WORKER);
df::building *holder = NULL;
df::unit *worker = NULL;
if (holderRef) holder = holderRef->getBuilding();
if (workerRef) worker = workerRef->getUnit();
//removeWorker() adds a job cd about right now, but I chose not to do that because I'm pretty sure
//that's only to stop removed workers from immediately reclaiming the job before doing something
//else, and this job is gonna be dead in a second.
//Remove return-refs from the holder & worker
if (holder) {
int jobIndex = linear_index(holder->jobs, job);
if (jobIndex >= 0)
vector_erase_at(holder->jobs, jobIndex);
}
if (worker) {
if (worker->job.current_job == job)
worker->job.current_job = NULL;
}
//Wipe all refs out
while (job->general_refs.size() > 0) {
auto ref = job->general_refs[0];
vector_erase_at(job->general_refs, 0);
delete ref;
}
//Remove job from job board
Job::removePostings(job, true);
//Remove job from global list
if (job->list_link) {
auto prev = job->list_link->prev;
auto next = job->list_link->next;
if (prev)
prev->next = next;
if (next)
next->prev = prev;
delete job->list_link;
}
delete job;
}
bool DFHack::Job::removeWorker(df::job *job, int cooldown)
{
CHECK_NULL_POINTER(job);
@ -397,6 +459,7 @@ bool DFHack::Job::removePostings(df::job *job, bool remove_all)
{
if ((**it).job == job)
{
(**it).job = NULL;
(**it).flags.bits.dead = true;
removed = true;
}