|
|
|
@ -46,6 +46,7 @@
|
|
|
|
|
#include "df/world.h"
|
|
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <unordered_set>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
@ -60,7 +61,7 @@ DFHACK_PLUGIN_IS_ENABLED(dwarfvet_enabled);
|
|
|
|
|
REQUIRE_GLOBAL(ui);
|
|
|
|
|
REQUIRE_GLOBAL(world);
|
|
|
|
|
|
|
|
|
|
static vector<int32_t> tracked_units;
|
|
|
|
|
static unordered_set<int32_t> tracked_units;
|
|
|
|
|
static int32_t howOften = 100;
|
|
|
|
|
|
|
|
|
|
struct hospital_spot {
|
|
|
|
@ -145,8 +146,8 @@ AnimalHospital::AnimalHospital(df::building * building, color_ostream &out) {
|
|
|
|
|
|
|
|
|
|
AnimalHospital::~AnimalHospital() {
|
|
|
|
|
// Go through and delete all the patients
|
|
|
|
|
for (vector<Patient*>::iterator accepted_patient = this->accepted_patients.begin(); accepted_patient != this->accepted_patients.end(); accepted_patient++) {
|
|
|
|
|
delete (*accepted_patient);
|
|
|
|
|
for (Patient* accepted_patient : this->accepted_patients) {
|
|
|
|
|
delete accepted_patient;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -159,7 +160,7 @@ bool AnimalHospital::acceptPatient(int32_t id, color_ostream &out) {
|
|
|
|
|
|
|
|
|
|
// Yup, let's find the next open spot,
|
|
|
|
|
// and give it to our patient
|
|
|
|
|
int spot_cur = 0; // fuck the STL for requiring a second counter to make this usable
|
|
|
|
|
int spot_cur = 0;
|
|
|
|
|
for (vector<bool>::iterator spot = this->spots_in_use.begin(); spot != this->spots_in_use.end(); spot++) {
|
|
|
|
|
if (*spot == false) {
|
|
|
|
|
*spot = true;
|
|
|
|
@ -193,11 +194,11 @@ void AnimalHospital::reportUsage(color_ostream &out) {
|
|
|
|
|
// Debugging tool to see parts of the hospital in use
|
|
|
|
|
int length_cursor = this->length;
|
|
|
|
|
|
|
|
|
|
for (vector<bool>::iterator spot = this->spots_in_use.begin(); spot != this->spots_in_use.end(); spot++) {
|
|
|
|
|
if (*spot) out.print("t");
|
|
|
|
|
if (!(*spot)) out.print("f");
|
|
|
|
|
for (bool spot : this->spots_in_use) {
|
|
|
|
|
if (spot) out.print("X");
|
|
|
|
|
else out.print("-");
|
|
|
|
|
length_cursor--;
|
|
|
|
|
if (length_cursor < 0) {
|
|
|
|
|
if (length_cursor <= 0) {
|
|
|
|
|
out.print("\n");
|
|
|
|
|
length_cursor = this->length;
|
|
|
|
|
}
|
|
|
|
@ -276,62 +277,16 @@ void AnimalHospital::calculateHospital(bool force, color_ostream &out) {
|
|
|
|
|
// NOTE: under some conditions, this generates a false warning. Not a lot I can do about it
|
|
|
|
|
|
|
|
|
|
// Mark spots used by that building as used; FIXME: handle special logic for traction benches and such
|
|
|
|
|
int building_offset_x = building->x1 - this->x1;
|
|
|
|
|
int building_offset_y = building->y1 - this->y1;
|
|
|
|
|
int building_length = building->x2 - building->x1 + 1;
|
|
|
|
|
int building_height = building->y2 - building->y1 + 1;
|
|
|
|
|
|
|
|
|
|
// Cap the used calculation to only include the part in the hospital
|
|
|
|
|
if (this->x1 > building->x1) {
|
|
|
|
|
building_offset_x -= (this->x1 - building->x1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this->y1 > building->y1) {
|
|
|
|
|
building_offset_y -= (building->y1 - this->y1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((this->x2 < building->x2) && building_offset_x) {
|
|
|
|
|
building_length -= (this->x2 - building->x2) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((this->y2 < building->y2) && building_offset_y) {
|
|
|
|
|
building_height = (building->y2 - this->y2) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Quick explination, if a building is north or east of the activity zone,
|
|
|
|
|
// we get a negative offset, we'll just skip those lines below. If its
|
|
|
|
|
// south or west, we make the building length/height lower to compinsate.
|
|
|
|
|
|
|
|
|
|
/* if we have a negative x offset, we correct that */
|
|
|
|
|
if (building_offset_x < 0) {
|
|
|
|
|
building_height += building_offset_x;
|
|
|
|
|
building_offset_x = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Handle negative y offset */
|
|
|
|
|
if (building_offset_y < 0) {
|
|
|
|
|
building_length += building_offset_y;
|
|
|
|
|
building_offset_y = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Advance the pointer to first row we need to mark */
|
|
|
|
|
int spot_cur = 0;
|
|
|
|
|
if (building_offset_y) {
|
|
|
|
|
spot_cur = (length+1) * building_offset_y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spot_cur += building_offset_x;
|
|
|
|
|
/* Start marking! */
|
|
|
|
|
for (int i = 0; i < building_height; i++) {
|
|
|
|
|
for (int j = 0; j < building_length; j++) {
|
|
|
|
|
spots_in_use[spot_cur+j] = true;
|
|
|
|
|
int overlap_x1 = std::max(building->x1, this->x1);
|
|
|
|
|
int overlap_y1 = std::max(building->y1, this->y1);
|
|
|
|
|
int overlap_x2 = std::min(building->x2, this->x2);
|
|
|
|
|
int overlap_y2 = std::min(building->y2, this->y2);
|
|
|
|
|
for (int x = overlap_x1; x <= overlap_x2; x++) {
|
|
|
|
|
for (int y = overlap_y1; y <= overlap_y2; y++) {
|
|
|
|
|
int spot_index = (x - this->x1) + (this->length * (y - this->y1));
|
|
|
|
|
spots_in_use.at(spot_index) = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wind the cursor to the start of the next row
|
|
|
|
|
spot_cur += length+1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// *phew*, done. Now repeat the process for the next building!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
@ -348,7 +303,7 @@ void AnimalHospital::dischargePatient(Patient * patient, color_ostream &out) {
|
|
|
|
|
if ( (*accepted_patient)->getID() == id) {
|
|
|
|
|
out.print("Discharging unit %d from hospital %d\n", id, this->id);
|
|
|
|
|
// Reclaim their spot
|
|
|
|
|
spots_in_use[(*accepted_patient)->getSpotIndex()] = false;
|
|
|
|
|
spots_in_use.at((*accepted_patient)->getSpotIndex()) = false;
|
|
|
|
|
this->spots_open++;
|
|
|
|
|
delete (*accepted_patient);
|
|
|
|
|
this->accepted_patients.erase(accepted_patient);
|
|
|
|
@ -357,36 +312,21 @@ void AnimalHospital::dischargePatient(Patient * patient, color_ostream &out) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// And the master list
|
|
|
|
|
for (vector<int32_t>::iterator it = tracked_units.begin(); it != tracked_units.end(); it++) {
|
|
|
|
|
if ((*it) == id) {
|
|
|
|
|
tracked_units.erase(it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tracked_units.erase(id);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AnimalHospital::processPatients(color_ostream &out) {
|
|
|
|
|
// Where the magic happens
|
|
|
|
|
for (vector<Patient*>::iterator patient = this->accepted_patients.begin(); patient != this->accepted_patients.end(); patient++) {
|
|
|
|
|
int id = (*patient)->getID();
|
|
|
|
|
df::unit * real_unit = nullptr;
|
|
|
|
|
// Appears the health bits can get freed/realloced too -_-;, Find the unit from the main
|
|
|
|
|
// index and check it there.
|
|
|
|
|
auto units = world->units.all;
|
|
|
|
|
|
|
|
|
|
for ( size_t a = 0; a < units.size(); a++ ) {
|
|
|
|
|
real_unit = units[a];
|
|
|
|
|
if (real_unit->id == id) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (Patient *patient : this->accepted_patients) {
|
|
|
|
|
int id = patient->getID();
|
|
|
|
|
df::unit *real_unit = df::unit::find(id);
|
|
|
|
|
|
|
|
|
|
// Check to make sure the unit hasn't expired before assigning a job, or if they've been healed
|
|
|
|
|
if (!real_unit || !Units::isActive(real_unit) || !real_unit->health->flags.bits.needs_healthcare) {
|
|
|
|
|
// discharge the patient from the hospital
|
|
|
|
|
this->dischargePatient(*patient, out);
|
|
|
|
|
this->dischargePatient(patient, out);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -396,9 +336,9 @@ void AnimalHospital::processPatients(color_ostream &out) {
|
|
|
|
|
df::job * job = new df::job;
|
|
|
|
|
DFHack::Job::linkIntoWorld(job);
|
|
|
|
|
|
|
|
|
|
job->pos.x = (*patient)->returnX();
|
|
|
|
|
job->pos.y = (*patient)->returnY();
|
|
|
|
|
job->pos.z = (*patient)->returnZ();
|
|
|
|
|
job->pos.x = patient->returnX();
|
|
|
|
|
job->pos.y = patient->returnY();
|
|
|
|
|
job->pos.z = patient->returnZ();
|
|
|
|
|
job->flags.bits.special = 1;
|
|
|
|
|
job->job_type = df::enums::job_type::Rest;
|
|
|
|
|
df::general_ref *ref = df::allocate<df::general_ref_unit_workerst>();
|
|
|
|
@ -418,8 +358,8 @@ void delete_animal_hospital_vector(color_ostream &out) {
|
|
|
|
|
if (dwarfvet_enabled) {
|
|
|
|
|
out.print("Clearing all animal hospitals\n");
|
|
|
|
|
}
|
|
|
|
|
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
|
|
|
|
|
delete (*animal_hospital);
|
|
|
|
|
for (AnimalHospital *animal_hospital : animal_hospital_zones) {
|
|
|
|
|
delete animal_hospital;
|
|
|
|
|
}
|
|
|
|
|
animal_hospital_zones.clear();
|
|
|
|
|
}
|
|
|
|
@ -474,8 +414,6 @@ void tickHandler(color_ostream& out, void* data) {
|
|
|
|
|
return;
|
|
|
|
|
CoreSuspender suspend;
|
|
|
|
|
int32_t own_race_id = df::global::ui->race_id;
|
|
|
|
|
int32_t own_civ_id = df::global::ui->civ_id;
|
|
|
|
|
auto units = world->units.all;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generate a list of animal hospitals on the map
|
|
|
|
@ -496,8 +434,7 @@ void tickHandler(color_ostream& out, void* data) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Walk the building tree, and generate a list of animal hospitals on the map
|
|
|
|
|
for (size_t b =0 ; b < world->buildings.all.size(); b++) {
|
|
|
|
|
df::building* building = world->buildings.all[b];
|
|
|
|
|
for (df::building* building : df::building::get_vector()) {
|
|
|
|
|
if (isActiveAnimalHospital(building)) {
|
|
|
|
|
hospitals_on_map.push_back(building);
|
|
|
|
|
}
|
|
|
|
@ -531,7 +468,7 @@ void tickHandler(color_ostream& out, void* data) {
|
|
|
|
|
// Now walk our list of known hospitals, do a bit of checking, then compare
|
|
|
|
|
// TODO: this doesn't handle zone resizes at all
|
|
|
|
|
|
|
|
|
|
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
|
|
|
|
|
for (AnimalHospital *animal_hospital : animal_hospital_zones) {
|
|
|
|
|
// If a zone is changed at all, DF seems to reallocate it.
|
|
|
|
|
//
|
|
|
|
|
// Each AnimalHospital has a "to_be_deleted" bool. We're going to set that to true, and clear it if we can't
|
|
|
|
@ -540,13 +477,13 @@ void tickHandler(color_ostream& out, void* data) {
|
|
|
|
|
//
|
|
|
|
|
// Surviving hospitals will be copied to scratch which will become the new AHZ vector
|
|
|
|
|
|
|
|
|
|
(*animal_hospital)->to_be_deleted = true;
|
|
|
|
|
for (vector<df::building*>::iterator current_hospital = hospitals_on_map.begin(); current_hospital != hospitals_on_map.end(); current_hospital++) {
|
|
|
|
|
animal_hospital->to_be_deleted = true;
|
|
|
|
|
for (df::building *current_hospital : hospitals_on_map) {
|
|
|
|
|
|
|
|
|
|
/* Keep the hospital if its still valid */
|
|
|
|
|
if ((*animal_hospital)->getID() == (*current_hospital)->id) {
|
|
|
|
|
ahz_scratch.push_back(*animal_hospital);
|
|
|
|
|
(*animal_hospital)->to_be_deleted = false;
|
|
|
|
|
if (animal_hospital->getID() == current_hospital->id) {
|
|
|
|
|
ahz_scratch.push_back(animal_hospital);
|
|
|
|
|
animal_hospital->to_be_deleted = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -556,20 +493,20 @@ void tickHandler(color_ostream& out, void* data) {
|
|
|
|
|
// Report what we're deleting by checking the to_be_deleted bool.
|
|
|
|
|
//
|
|
|
|
|
// Whatsever left is added to the pending add list
|
|
|
|
|
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
|
|
|
|
|
if ((*animal_hospital)->to_be_deleted) {
|
|
|
|
|
out.print("Hospital #%d removed\n", (*animal_hospital)->getID());
|
|
|
|
|
delete *animal_hospital;
|
|
|
|
|
for (AnimalHospital *animal_hospital : animal_hospital_zones) {
|
|
|
|
|
if (animal_hospital->to_be_deleted) {
|
|
|
|
|
out.print("Hospital #%d removed\n", animal_hospital->getID());
|
|
|
|
|
delete animal_hospital;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now we need to walk the scratch and add anything that is a hospital and wasn't in the vector */
|
|
|
|
|
|
|
|
|
|
for (vector<df::building*>::iterator current_hospital = hospitals_on_map.begin(); current_hospital != hospitals_on_map.end(); current_hospital++) {
|
|
|
|
|
for (df::building *current_hospital : hospitals_on_map) {
|
|
|
|
|
bool new_hospital = true;
|
|
|
|
|
|
|
|
|
|
for (vector<AnimalHospital*>::iterator animal_hospital = ahz_scratch.begin(); animal_hospital != ahz_scratch.end(); animal_hospital++) {
|
|
|
|
|
if ((*animal_hospital)->getID() == (*current_hospital)->id) {
|
|
|
|
|
for (AnimalHospital *animal_hospital : ahz_scratch) {
|
|
|
|
|
if (animal_hospital->getID() == current_hospital->id) {
|
|
|
|
|
// Next if we're already here
|
|
|
|
|
new_hospital = false;
|
|
|
|
|
break;
|
|
|
|
@ -577,19 +514,19 @@ void tickHandler(color_ostream& out, void* data) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add it if its new
|
|
|
|
|
if (new_hospital == true) to_be_added.push_back(*current_hospital);
|
|
|
|
|
if (new_hospital == true) to_be_added.push_back(current_hospital);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now add it to the scratch AHZ */
|
|
|
|
|
for (vector<df::building*>::iterator current_hospital = to_be_added.begin(); current_hospital != to_be_added.end(); current_hospital++) {
|
|
|
|
|
for (df::building *current_hospital : to_be_added) {
|
|
|
|
|
// Add it to the vector
|
|
|
|
|
out.print("Adding new hospital #id: %d at x1 %d y1: %d z: %d\n",
|
|
|
|
|
(*current_hospital)->id,
|
|
|
|
|
(*current_hospital)->x1,
|
|
|
|
|
(*current_hospital)->y1,
|
|
|
|
|
(*current_hospital)->z
|
|
|
|
|
current_hospital->id,
|
|
|
|
|
current_hospital->x1,
|
|
|
|
|
current_hospital->y1,
|
|
|
|
|
current_hospital->z
|
|
|
|
|
);
|
|
|
|
|
AnimalHospital * hospital = new AnimalHospital(*current_hospital, out);
|
|
|
|
|
AnimalHospital * hospital = new AnimalHospital(current_hospital, out);
|
|
|
|
|
ahz_scratch.push_back(hospital);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -621,9 +558,7 @@ void tickHandler(color_ostream& out, void* data) {
|
|
|
|
|
*/
|
|
|
|
|
processUnits:
|
|
|
|
|
/* Code borrowed from petcapRemover.cpp */
|
|
|
|
|
for ( size_t a = 0; a < units.size(); a++ ) {
|
|
|
|
|
df::unit* unit = units[a];
|
|
|
|
|
|
|
|
|
|
for (df::unit *unit : df::unit::get_vector()) {
|
|
|
|
|
/* As hilarious as it would be, lets not treat FB :) */
|
|
|
|
|
if ( !Units::isActive(unit) || unit->flags1.bits.active_invader || unit->flags2.bits.underworld || unit->flags2.bits.visitor_uninvited || unit->flags2.bits.visitor ) {
|
|
|
|
|
continue;
|
|
|
|
@ -723,12 +658,7 @@ processUnits:
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Now we need to find if this unit can be accepted at a hospital
|
|
|
|
|
bool awareOfUnit = false;
|
|
|
|
|
for (vector<int32_t>::iterator it = tracked_units.begin(); it != tracked_units.end(); it++) {
|
|
|
|
|
if ((*it) == unit->id) {
|
|
|
|
|
awareOfUnit = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bool awareOfUnit = tracked_units.count(unit->id);
|
|
|
|
|
// New unit for dwarfvet to be aware of!
|
|
|
|
|
if (!awareOfUnit) {
|
|
|
|
|
// The master list handles all patients which are accepted
|
|
|
|
@ -737,7 +667,7 @@ processUnits:
|
|
|
|
|
for (auto animal_hospital : animal_hospital_zones) {
|
|
|
|
|
if (animal_hospital->acceptPatient(unit->id, out)) {
|
|
|
|
|
out.print("Accepted patient %d at hospital %d\n", unit->id, animal_hospital->getID());
|
|
|
|
|
tracked_units.push_back(unit->id);
|
|
|
|
|
tracked_units.insert(unit->id);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -746,9 +676,9 @@ processUnits:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The final step, process all patients!
|
|
|
|
|
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
|
|
|
|
|
(*animal_hospital)->calculateHospital(true, out);
|
|
|
|
|
(*animal_hospital)->processPatients(out);
|
|
|
|
|
for (AnimalHospital *animal_hospital : animal_hospital_zones) {
|
|
|
|
|
animal_hospital->calculateHospital(true, out);
|
|
|
|
|
animal_hospital->processPatients(out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
@ -772,8 +702,7 @@ command_result dwarfvet (color_ostream &out, std::vector <std::string> & paramet
|
|
|
|
|
}
|
|
|
|
|
if ( parameters[a] == "report") {
|
|
|
|
|
out.print("Current animal hospitals are:\n");
|
|
|
|
|
for (size_t b =0 ; b < world->buildings.all.size(); b++) {
|
|
|
|
|
df::building* building = world->buildings.all[b];
|
|
|
|
|
for (df::building *building : df::building::get_vector()) {
|
|
|
|
|
if (isActiveAnimalHospital(building)) {
|
|
|
|
|
out.print(" at x1: %d, x2: %d, y1: %d, y2: %d, z: %d\n", building->x1, building->x2, building->y1, building->y2, building->z);
|
|
|
|
|
}
|
|
|
|
@ -782,9 +711,9 @@ command_result dwarfvet (color_ostream &out, std::vector <std::string> & paramet
|
|
|
|
|
}
|
|
|
|
|
if ( parameters[a] == "report-usage") {
|
|
|
|
|
out.print("Current animal hospitals are:\n");
|
|
|
|
|
for (vector<AnimalHospital*>::iterator animal_hospital = animal_hospital_zones.begin(); animal_hospital != animal_hospital_zones.end(); animal_hospital++) {
|
|
|
|
|
(*animal_hospital)->calculateHospital(true, out);
|
|
|
|
|
(*animal_hospital)->reportUsage(out);
|
|
|
|
|
for (AnimalHospital *animal_hospital : animal_hospital_zones) {
|
|
|
|
|
animal_hospital->calculateHospital(true, out);
|
|
|
|
|
animal_hospital->reportUsage(out);
|
|
|
|
|
}
|
|
|
|
|
return CR_OK;
|
|
|
|
|
}
|
|
|
|
|