Merge remote-tracking branch 'CannibalVox/job_remove' into develop

develop
lethosor 2016-12-05 15:24:37 -05:00
commit 8521b830b2
3 changed files with 149 additions and 1 deletions

@ -1470,6 +1470,9 @@ static const LuaWrapper::FunctionReg dfhack_job_module[] = {
WRAPM(Job,getName),
WRAPM(Job,linkIntoWorld),
WRAPM(Job,removePostings),
WRAPM(Job,disconnectJobItem),
WRAPM(Job,disconnectJobGeneralRef),
WRAPM(Job,removeJob),
WRAPN(is_equal, jobEqual),
WRAPN(is_item_equal, jobItemEqual),
{ NULL, NULL }

@ -66,6 +66,21 @@ 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);
// This helpful method only removes the backref from the item to the job, but it doesn't
// remove the item ref from the job's vector, or delete it or anything. Think of it as a method
// that does all the needful to make an item ref ready to delete.
DFHACK_EXPORT void disconnectJobItem(df::job *job, df::job_item_ref *item);
// This helpful method only removes the backref from whatever the general_ref points to,
// it doesn't remove the general_ref from the job's vector, or delete it or anything.
// Think of it as a method that does all the needful to make a ref ready to delete.
// If it returns false, you've found a ref that the method doesn't know how to handle. Congratulations!
// You should report that and/or check in a fix.
DFHACK_EXPORT bool disconnectJobGeneralRef(df::job *job, df::general_ref *ref);
// 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 bool 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,135 @@ void DFHack::Job::setJobCooldown(df::building *workshop, df::unit *worker, int c
}
}
void DFHack::Job::disconnectJobItem(df::job *job, df::job_item_ref *ref) {
if (!ref) return;
auto item = ref->item;
if (!item) return;
//Work backward through the specific refs & remove/delete all specific refs to this job
int refCount = item->specific_refs.size();
bool stillHasJobs = false;
for(int refIndex = refCount-1; refIndex >= 0; refIndex--) {
auto ref = item->specific_refs[refIndex];
if (ref->type == df::specific_ref_type::JOB) {
if (ref->job == job) {
vector_erase_at(item->specific_refs, refIndex);
delete ref;
} else {
stillHasJobs = true;
}
}
}
if (!stillHasJobs) item->flags.bits.in_job = false;
}
bool DFHack::Job::disconnectJobGeneralRef(df::job *job, df::general_ref *ref) {
if (ref == NULL) return true;
df::building *building = NULL;
df::unit *unit = NULL;
switch (ref->getType()) {
case general_ref_type::BUILDING_HOLDER:
building = ref->getBuilding();
if (building != NULL) {
int jobIndex = linear_index(building->jobs, job);
if (jobIndex >= 0) {
vector_erase_at(building->jobs, jobIndex);
}
}
break;
case general_ref_type::UNIT_WORKER:
unit = ref->getUnit();
if (unit != NULL) {
if (unit->job.current_job == job) {
unit->job.current_job = NULL;
}
}
break;
default:
return false;
}
return true;
}
bool 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 false;
//We actually only know how to handle BUILDING_HOLDER and UNIT_WORKER refs- there's probably a great
//way to handle them, but until we have a good example, we'll just fail to remove jobs that have other sorts
//of refs, or any specific refs
if (job->specific_refs.size() > 0)
return false;
for (auto genRefItr = job->general_refs.begin(); genRefItr != job->general_refs.end(); ++genRefItr) {
auto ref = *genRefItr;
if (ref != NULL && (ref->getType() != general_ref_type::BUILDING_HOLDER && ref->getType() != general_ref_type::UNIT_WORKER))
return false;
}
//Disconnect, delete, and wipe all general refs
while (job->general_refs.size() > 0) {
auto ref = job->general_refs[0];
//Our code above should have ensured that this won't return false- if it does, there's not
//a great way of recovering since we can't properly destroy the job & we can't leave it
//around. Better to know the moment that becomes a problem.
bool success = disconnectJobGeneralRef(job, ref);
assert(success);
vector_erase_at(job->general_refs, 0);
if (ref != NULL) delete ref;
}
//Detach all items from the job
while (job->items.size() > 0) {
auto itemRef = job->items[0];
disconnectJobItem(job, itemRef);
vector_erase_at(job->items, 0);
if (itemRef != NULL) delete itemRef;
}
//Remove job from job board
Job::removePostings(job, true);
//Clean up job_items
while (job->job_items.size() > 0) {
auto jobItem = job->job_items[0];
vector_erase_at(job->job_items, 0);
if (jobItem) {
delete jobItem;
}
}
//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;
return true;
}
bool DFHack::Job::removeWorker(df::job *job, int cooldown)
{
CHECK_NULL_POINTER(job);
@ -397,6 +526,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;
}