summaryrefslogtreecommitdiff
path: root/plugins/zone.cpp
diff options
context:
space:
mode:
authorRobert Heinrich2012-04-02 16:07:23 +0200
committerRobert Heinrich2012-04-02 16:07:23 +0200
commit772c6b1cbba496cf5653a20b0719853b77dca290 (patch)
tree5bad8c73139b35dffddb2b747b5d37c39c4f0568 /plugins/zone.cpp
parent39787e9cd5f336fee993c38010ab9cf2fa246555 (diff)
downloaddfhack-772c6b1cbba496cf5653a20b0719853b77dca290.tar.gz
dfhack-772c6b1cbba496cf5653a20b0719853b77dca290.tar.bz2
dfhack-772c6b1cbba496cf5653a20b0719853b77dca290.tar.xz
Added plugin 'zone'. Helps with assigning units to pens/pastures and pits.
Diffstat (limited to 'plugins/zone.cpp')
-rw-r--r--plugins/zone.cpp895
1 files changed, 895 insertions, 0 deletions
diff --git a/plugins/zone.cpp b/plugins/zone.cpp
new file mode 100644
index 00000000..eb9a1e80
--- /dev/null
+++ b/plugins/zone.cpp
@@ -0,0 +1,895 @@
+// Intention: help with activity zone management (auto-pasture animals, auto-pit goblins, ...)
+//
+// the following things would be nice:
+// - dump info about pastures, pastured animals, count non-pastured tame animals, print gender info
+// - help finding caged dwarves? (maybe even allow to build their cages for fast release)
+// - dump info about caged goblins, animals, ...
+// - full automation of handling mini-pastures over nestboxes:
+// go through all pens, check if they are empty and placed over a nestbox
+// find female tame egg-layer who is not assigned to another pen (allowing to grab them from cages would be nice)
+// maybe check for minimum age? it's not that useful to fill nestboxes with freshly hatched birds
+// assign to that pasture
+// - allow to mark old animals for slaughter automatically?
+// that should include a check to ensure that at least one male and one female remain for breeding
+// allow some fine-tuning like how many males/females should per race should be left alive
+// and at what age they are being marked for slaughter. don't slaughter pregnant females?
+// - count grass tiles on pastures, move grazers to new pasture if old pasture is empty
+// move hungry unpastured grazers to pasture with grass
+//
+// What is working so far:
+// - print detailed info about activity zone and units under cursor (mostly for checking refs and stuff)
+// - mark a zone which is used for future assignment commands
+// - assign single selected creature to a zone
+// - unassign single creature under cursor from current zone
+// - pitting own dwarves :)
+
+#include <iostream>
+#include <iomanip>
+#include <climits>
+#include <vector>
+#include <string>
+#include <sstream>
+#include <ctime>
+#include <cstdio>
+using namespace std;
+
+#include "Core.h"
+#include "Console.h"
+#include "Export.h"
+#include "PluginManager.h"
+#include "modules/Units.h"
+#include "modules/Maps.h"
+#include "modules/Gui.h"
+#include "modules/Materials.h"
+#include "modules/MapCache.h"
+#include "modules/Buildings.h"
+#include "MiscUtils.h"
+
+#include <df/ui.h>
+#include "df/world.h"
+#include "df/world_raws.h"
+#include "df/building_def.h"
+#include "df/building_civzonest.h"
+#include "df/building_cagest.h"
+#include "df/building_chainst.h"
+#include "df/general_ref_building_civzone_assignedst.h"
+#include <df/creature_raw.h>
+
+using std::vector;
+using std::string;
+using namespace DFHack;
+using namespace df::enums;
+using df::global::world;
+using df::global::cursor;
+using df::global::ui;
+
+using namespace DFHack::Gui;
+
+struct genref : df::general_ref_building_civzone_assignedst {};
+
+command_result df_zone (color_ostream &out, vector <string> & parameters);
+
+DFHACK_PLUGIN("zone");
+
+const string zone_help =
+ "Allows easier management of pens/pastures and pits.\n"
+ "Options:\n"
+ " set - set zone under cursor as default for future assigns\n"
+ " assign - assign selected creature to a pen or pit\n"
+ " (can be followed by valid zone id which will then be set)\n"
+ " unassign - unassign selected creature from it's zone\n"
+ " uinfo - print info about selected unit\n"
+ " zinfo - print info about zone(s) under cursor\n"
+ //" cinfo - print info about (built) cage under cursor\n"
+ //" rinfo - print info about restraint under cursor\n"
+ " all - in combination with 'zinfo' or 'cinfo': prints all zones/units\n"
+ " verbose - print some more info, mostly useless debug stuff\n"
+ "Example:\n"
+ " (ingame) move cursor to a pen/pasture or pit zone\n"
+ " (dfhack) 'zone set' to use this zone for future assignments\n"
+ " (dfhack) map 'zone assign' to a hotkey of your choice\n"
+ " (ingame) select a unit with 'v', 'k' or in the unit list or inside a cage\n"
+ " (ingame) press hotkey to assign unit to it's new home (or pit)\n"
+ ;
+
+
+DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
+{
+ commands.push_back(PluginCommand(
+ "zone", "manage activity zones.",
+ df_zone, false,
+ zone_help.c_str()
+ ));
+ return CR_OK;
+}
+
+DFhackCExport command_result plugin_shutdown ( color_ostream &out )
+{
+ return CR_OK;
+}
+
+
+///////////////
+// Various small tool functions
+//
+int32_t getCreatureAge(df::unit* unit);
+bool isTame(df::unit* unit);
+bool isTrained(df::unit* unit);
+bool isTrained(df::unit* creature);
+bool isWar(df::unit* creature);
+bool isHunter(df::unit* creature);
+bool isOwnCiv(df::unit* creature);
+bool isActivityZone(df::building * building);
+bool isPenPasture(df::building * building);
+bool isPit(df::building * building);
+bool isActive(df::building * building);
+int32_t findBuildingIndexById(int32_t id);
+int32_t findPenPitAtCursor();
+int32_t findCageAtCursor();
+int32_t findChainAtCursor();
+
+df::general_ref_building_civzone_assignedst * createCivzoneRef();
+bool unassignUnitFromZone(df::unit* unit);
+command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building* building, bool verbose);
+void unitInfo(color_ostream & out, df::unit* creature, bool list_refs);
+void zoneInfo(color_ostream & out, df::building* building, bool list_refs);
+void cageInfo(color_ostream & out, df::building* building, bool list_refs);
+void chainInfo(color_ostream & out, df::building* building, bool list_refs);
+
+
+int32_t getUnitAge(df::unit* unit)
+{
+ // If the birthday this year has not yet passed, subtract one year.
+ // ASSUMPTION: birth_time is on the same scale as cur_year_tick
+ int32_t yearDifference = *df::global::cur_year - unit->relations.birth_year;
+ if (unit->relations.birth_time >= *df::global::cur_year_tick)
+ yearDifference--;
+ return yearDifference;
+}
+
+bool isTame(df::unit* creature)
+{
+ bool tame = false;
+ if(creature->flags1.bits.tame)
+ {
+ switch (creature->training_level)
+ {
+ case df::animal_training_level::Trained:
+ case df::animal_training_level::WellTrained:
+ case df::animal_training_level::SkilfullyTrained:
+ case df::animal_training_level::ExpertlyTrained:
+ case df::animal_training_level::ExceptionallyTrained:
+ case df::animal_training_level::MasterfullyTrained:
+ case df::animal_training_level::Domesticated:
+ tame=true;
+ break;
+ case df::animal_training_level::SemiWild: //??
+ case df::animal_training_level::Unk8: //??
+ case df::animal_training_level::WildUntamed:
+ default:
+ tame=false;
+ break;
+ }
+ }
+ return tame;
+}
+
+// check if trained (might be useful if pasturing war dogs etc)
+bool isTrained(df::unit* creature)
+{
+ bool trained = false;
+ if(creature->flags1.bits.tame)
+ {
+ switch (creature->training_level)
+ {
+ case df::animal_training_level::Trained:
+ case df::animal_training_level::WellTrained:
+ case df::animal_training_level::SkilfullyTrained:
+ case df::animal_training_level::ExpertlyTrained:
+ case df::animal_training_level::ExceptionallyTrained:
+ case df::animal_training_level::MasterfullyTrained:
+ //case df::animal_training_level::Domesticated:
+ trained = true;
+ break;
+ default:
+ break;
+ }
+ }
+ return trained;
+}
+
+// check for profession "war creature"
+bool isWar(df::unit* creature)
+{
+ if( creature->profession == df::profession::TRAINED_WAR
+ || creature->profession2 == df::profession::TRAINED_WAR)
+ return true;
+ else
+ return false;
+}
+
+// check for profession "hunting creature"
+bool isHunter(df::unit* creature)
+{
+ if( creature->profession == df::profession::TRAINED_HUNTER
+ || creature->profession2 == df::profession::TRAINED_HUNTER)
+ return true;
+ else
+ return false;
+}
+
+// check if creature belongs to the player's civilization
+// (don't try to pasture/slaughter random untame animals)
+bool isOwnCiv(df::unit* creature)
+{
+ if(creature->civ_id == ui->civ_id)
+ return true;
+ else
+ return false;
+}
+
+// dump some unit info
+void unitInfo(color_ostream & out, df::unit* unit, bool list_refs = false)
+{
+ out.print("Unit %d", unit->id); //race %d, civ %d,", creature->race, creature->civ_id
+ if(unit->name.has_name)
+ out << ", name: " << unit->name.first_name << " " << unit->name.nickname;
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ out << " " << raw->creature_id << " (";
+ switch(unit->sex)
+ {
+ case -1:
+ out << "n/a";
+ break;
+ case 0:
+ out << "female";
+ break;
+ case 1:
+ out << "male";
+ break;
+ default:
+ out << "INVALID!";
+ break;
+ }
+ out << ")";
+ out << ", age: " << getUnitAge(unit);
+
+ if(isTame(unit))
+ out << ", tame";
+ if(isOwnCiv(unit))
+ out << ", owned";
+ if(isWar(unit))
+ out << ", war";
+ if(isHunter(unit))
+ out << ", hunter";
+
+ out << endl;
+
+ if(!list_refs)
+ return;
+
+ //out << "number of refs: " << creature->refs.size() << endl;
+ for(size_t r = 0; r<unit->refs.size(); r++)
+ {
+ df::general_ref* ref = unit->refs.at(r);
+ df::general_ref_type refType = ref->getType();
+ out << " ref#" << r <<" refType#" << refType << " "; //endl;
+ switch(refType)
+ {
+ case df::general_ref_type::BUILDING_CIVZONE_ASSIGNED:
+ {
+ out << "assigned to zone";
+ df::building_civzonest * civAss = (df::building_civzonest *) ref->getBuilding();
+ out << " #" << civAss->id;
+ }
+ break;
+ case df::general_ref_type::CONTAINED_IN_ITEM:
+ out << "contained in item";
+ break;
+ case df::general_ref_type::BUILDING_CAGED:
+ out << "caged";
+ break;
+ case df::general_ref_type::BUILDING_CHAIN:
+ out << "chained";
+ break;
+ default:
+ //out << "unhandled reftype";
+ break;
+ }
+ out << endl;
+ }
+}
+
+bool isActivityZone(df::building * building)
+{
+ if( building->getType() == building_type::Civzone
+ && building->getSubtype() == civzone_type::ActivityZone)
+ return true;
+ else
+ return false;
+}
+
+bool isPenPasture(df::building * building)
+{
+ if(!isActivityZone(building))
+ return false;
+
+ df::building_civzonest * civ = (df::building_civzonest *) building;
+
+ if(civ->zone_flags.bits.pen_pasture)
+ return true;
+ else
+ return false;
+}
+
+bool isPit(df::building * building)
+{
+ if(!isActivityZone(building))
+ return false;
+
+ df::building_civzonest * civ = (df::building_civzonest *) building;
+
+ if(civ->zone_flags.bits.pit_pond && civ->pit_flags==0)
+ return true;
+ else
+ return false;
+}
+
+bool isCage(df::building * building)
+{
+ if(building->getType() == building_type::Cage)
+ return true;
+ else
+ return false;
+}
+
+bool isChain(df::building * building)
+{
+ if(building->getType() == building_type::Chain)
+ return true;
+ else
+ return false;
+}
+
+bool isActive(df::building * building)
+{
+ if(!isActivityZone(building))
+ return false;
+
+ df::building_civzonest * civ = (df::building_civzonest *) building;
+ if(civ->zone_flags.bits.active)
+ return true;
+ else
+ return false;
+}
+
+int32_t findBuildingIndexById(int32_t id)
+{
+ for (size_t b = 0; b < world->buildings.all.size(); b++)
+ {
+ if(world->buildings.all.at(b)->id == id)
+ return b;
+ }
+ return -1;
+}
+
+// returns id of pen/pit at cursor position (-1 if nothing found)
+int32_t findPenPitAtCursor()
+{
+ int32_t foundID = -1;
+
+ if(cursor->x == -30000)
+ return -1;
+
+ for (size_t b = 0; b < world->buildings.all.size(); b++)
+ {
+ df::building* building = world->buildings.all[b];
+
+ // find zone under cursor
+ if (!(building->x1 <= cursor->x && cursor->x <= building->x2 &&
+ building->y1 <= cursor->y && cursor->y <= building->y2 &&
+ building->z == cursor->z))
+ continue;
+
+ if(isPenPasture(building) || isPit(building))
+ {
+ foundID = building->id;
+ break;
+ }
+ }
+ return foundID;
+}
+
+// returns id of cage at cursor position (-1 if nothing found)
+int32_t findCageAtCursor()
+{
+ int32_t foundID = -1;
+
+ if(cursor->x == -30000)
+ return -1;
+
+ for (size_t b = 0; b < world->buildings.all.size(); b++)
+ {
+ df::building* building = world->buildings.all[b];
+
+ if (!(building->x1 <= cursor->x && cursor->x <= building->x2 &&
+ building->y1 <= cursor->y && cursor->y <= building->y2 &&
+ building->z == cursor->z))
+ continue;
+
+ if(isCage(building))
+ {
+ foundID = building->id;
+ break;
+ }
+ }
+ return foundID;
+}
+
+int32_t findChainAtCursor()
+{
+ int32_t foundID = -1;
+
+ if(cursor->x == -30000)
+ return -1;
+
+ for (size_t b = 0; b < world->buildings.all.size(); b++)
+ {
+ df::building* building = world->buildings.all[b];
+
+ // find zone under cursor
+ if (!(building->x1 <= cursor->x && cursor->x <= building->x2 &&
+ building->y1 <= cursor->y && cursor->y <= building->y2 &&
+ building->z == cursor->z))
+ continue;
+
+ if(isChain(building))
+ {
+ foundID = building->id;
+ break;
+ }
+ }
+ return foundID;
+}
+
+df::general_ref_building_civzone_assignedst * createCivzoneRef()
+{
+ static bool vt_initialized = false;
+ df::general_ref_building_civzone_assignedst* newref = NULL;
+
+ // after having run successfully for the first time it's safe to simply create the object
+ if(vt_initialized)
+ {
+ newref = (df::general_ref_building_civzone_assignedst*)
+ df::general_ref_building_civzone_assignedst::_identity.instantiate();
+ return newref;
+ }
+
+ // being called for the first time, need to initialize the vtable
+ for(size_t i = 0; i < world->units.all.size(); i++)
+ {
+ df::unit * creature = world->units.all[i];
+ for(size_t r = 0; r<creature->refs.size(); r++)
+ {
+ df::general_ref* ref;
+ ref = creature->refs.at(r);
+ if(ref->getType() == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED)
+ {
+ if (strict_virtual_cast<df::general_ref_building_civzone_assignedst>(ref))
+ {
+ // !! calling new() doesn't work, need _identity.instantiate() instead !!
+ newref = (df::general_ref_building_civzone_assignedst*)
+ df::general_ref_building_civzone_assignedst::_identity.instantiate();
+ vt_initialized = true;
+ break;
+ }
+ }
+ }
+ if(vt_initialized)
+ break;
+ }
+ return newref;
+}
+
+// check if unit is already assigned to a zone, remove that ref from unit and old zone
+// returns false if no pasture information was found
+// helps as workaround for http://www.bay12games.com/dwarves/mantisbt/view.php?id=4475 by the way
+// (pastured animals assigned to chains will get hauled back and forth because the pasture ref is not deleted)
+bool unassignUnitFromZone(df::unit* unit)
+{
+ bool success = false;
+ for (size_t or = 0; or < unit->refs.size(); or++)
+ {
+ df::general_ref * oldref = unit->refs[or];
+ if(oldref->getType() == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED)
+ {
+ unit->refs.erase(unit->refs.begin() + or);
+ df::building_civzonest * oldciv = (df::building_civzonest *) oldref->getBuilding();
+ for(size_t oc=0; oc<oldciv->assigned_creature.size(); oc++)
+ {
+ if(oldciv->assigned_creature[oc] == unit->id)
+ {
+ oldciv->assigned_creature.erase(oldciv->assigned_creature.begin() + oc);
+ break;
+ }
+ }
+ delete oldref;
+ success = true;
+ break;
+ }
+ }
+ return success;
+}
+
+// assign to pen or pit
+command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building* building, bool verbose = false)
+{
+ //if(!isOwnCiv(unit) || !isTame(unit))
+ //{
+ // out << "Creature must be tame and your own." << endl;
+ // return CR_WRONG_USAGE;
+ //}
+
+ // building must be a pen/pasture or pit
+ //df::building * building = world->buildings.all.at(index);
+ if(!isPenPasture(building) && !isPit(building))
+ {
+ out << "Invalid building type. This is not a pen/pasture or pit." << endl;
+ //target_zone = -1;
+ return CR_WRONG_USAGE;
+ }
+
+ // try to get a fresh civzone ref
+ df::general_ref_building_civzone_assignedst * ref = createCivzoneRef();
+ if(!ref)
+ {
+ out << "Could not find a clonable activity zone reference" << endl
+ << "You need to pen/pasture/pit at least one creature" << endl
+ << "before using 'assign' for the first time." << endl;
+ return CR_WRONG_USAGE;
+ }
+
+ // check if unit is already pastured, remove that ref from unit and old pasture
+ if(verbose)
+ {
+ if(unassignUnitFromZone(unit))
+ out << "old zone info cleared.";
+ else
+ out << "no old zone info found.";
+ }
+
+ ref->building_id = building->id;
+ unit->refs.push_back(ref);
+
+ df::building_civzonest * civz = (df::building_civzonest *) building;
+ civz->assigned_creature.push_back(unit->id);
+
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ out << "Unit " << unit->id
+ << "(" << raw->creature_id << ")"
+ << " assigned to zone " << building->id;
+ if(isPit(building))
+ out << " (pit).";
+ if(isPenPasture(building))
+ out << " (pen/pasture).";
+ out << endl;
+
+ return CR_OK;
+}
+
+
+// dump some zone info
+void zoneInfo(color_ostream & out, df::building* building, bool list_refs = false)
+{
+ if(building->getType()!= building_type::Civzone)
+ return;
+
+ if(building->getSubtype() != civzone_type::ActivityZone)
+ return;
+
+ string name;
+ building->getName(&name);
+ out.print("Building %i - \"%s\" - type %s (%i)",
+ building->id,
+ name.c_str(),
+ ENUM_KEY_STR(building_type, building->getType()).c_str(),
+ building->getType());
+ out.print(", subtype %s (%i)",
+ ENUM_KEY_STR(civzone_type, (df::civzone_type)building->getSubtype()).c_str(),
+ building->getSubtype());
+ out.print("\n");
+
+ df::building_civzonest * civ = (df::building_civzonest *) building;
+ if(civ->zone_flags.bits.active)
+ out << "active";
+ else
+ out << "not active";
+
+ // look at zone flags, ignore fishing, water, hospital, ...
+ // in fact, only deal with pits and pens
+ if(civ->zone_flags.bits.pen_pasture)
+ out << ", pen/pasture";
+ else if (civ->zone_flags.bits.pit_pond && civ->pit_flags==0)
+ out << ", pit";
+ else
+ return;
+ out << endl;
+
+ int32_t creaturecount = civ->assigned_creature.size();
+ out << "Creatures in this zone: " << creaturecount << endl;
+ for(size_t c = 0; c < creaturecount; c++)
+ {
+ int32_t cindex = civ->assigned_creature.at(c);
+
+ // print list of all units assigned to that zone
+ for(size_t i = 0; i < world->units.all.size(); i++)
+ {
+ df::unit * creature = world->units.all[i];
+ if(creature->id != cindex)
+ continue;
+
+ unitInfo(out, creature, false);
+ }
+ }
+}
+
+// dump some cage info
+void cageInfo(color_ostream & out, df::building* building, bool list_refs = false)
+{
+ if(!isCage(building))
+ return;
+
+ string name;
+ building->getName(&name);
+ out.print("Building %i - \"%s\" - type %s (%i)",
+ building->id,
+ name.c_str(),
+ ENUM_KEY_STR(building_type, building->getType()).c_str(),
+ building->getType());
+ out.print("\n");
+
+ df::building_cagest * cage = (df::building_cagest*) building;
+ int32_t creaturecount = cage->assigned_creature.size();
+ out << "Creatures in this cage: " << creaturecount << endl;
+ for(size_t c = 0; c < creaturecount; c++)
+ {
+ int32_t cindex = cage->assigned_creature.at(c);
+
+ // print list of all units assigned to that cage
+ for(size_t i = 0; i < world->units.all.size(); i++)
+ {
+ df::unit * creature = world->units.all[i];
+ if(creature->id != cindex)
+ continue;
+
+ unitInfo(out, creature, false);
+ }
+ }
+}
+
+
+// dump some chain/restraint info
+void chainInfo(color_ostream & out, df::building* building, bool list_refs = false)
+{
+ if(!isChain(building))
+ return;
+
+ string name;
+ building->getName(&name);
+ out.print("Building %i - \"%s\" - type %s (%i)",
+ building->id,
+ name.c_str(),
+ ENUM_KEY_STR(building_type, building->getType()).c_str(),
+ building->getType());
+ out.print("\n");
+
+ df::building_chainst* chain = (df::building_chainst*) building;
+ if(chain->assigned)
+ {
+ out << "assigned: ";
+ unitInfo(out, chain->assigned, true);
+ }
+ if(chain->chained)
+ {
+ out << "chained: ";
+ unitInfo(out, chain->chained, true);
+ }
+}
+
+command_result df_zone (color_ostream &out, vector <string> & parameters)
+{
+ CoreSuspender suspend;
+
+ bool need_cursor = false; // for zone_info, zone_assign, ...
+ bool unit_info = false;
+ bool zone_info = false;
+ //bool cage_info = false;
+ //bool chain_info = false;
+
+ bool zone_assign = false;
+ bool zone_unassign = false;
+ bool zone_set = false;
+ bool verbose = false;
+ bool all = false;
+ static int target_zone = -1;
+
+ for (size_t i = 0; i < parameters.size(); i++)
+ {
+ string & p = parameters[i];
+
+ if (p == "help" || p == "?")
+ {
+ out << zone_help << endl;
+ return CR_OK;
+ }
+ else if(p == "zinfo")
+ {
+ zone_info = true;
+ }
+ else if(p == "uinfo")
+ {
+ unit_info = true;
+ }
+ //else if(p == "cinfo")
+ //{
+ // cage_info = true;
+ //}
+ //else if(p == "rinfo")
+ //{
+ // chain_info = true;
+ //}
+ else if(p == "verbose")
+ {
+ verbose = true;
+ }
+ else if(p == "unassign")
+ {
+ zone_unassign = true;
+ }
+ else if(p == "assign")
+ {
+ if(i == parameters.size()-1)
+ {
+ if(target_zone == -1)
+ {
+ out.printerr("No zone id specified and current one is invalid!");
+ return CR_WRONG_USAGE;
+ }
+ else
+ {
+ out << "No zone id specified. Will try to use #" << target_zone << endl;
+ zone_assign = true;
+ }
+ }
+ else
+ {
+ stringstream ss(parameters[i+1]);
+ i++;
+ ss >> target_zone;
+ out << "Assign selected unit to zone #" << target_zone <<std::endl;
+ zone_assign = true;
+ }
+ }
+ else if(p == "set")
+ {
+ zone_set = true;
+ }
+ else if(p == "all")
+ {
+ all = true;
+ }
+ else
+ return CR_WRONG_USAGE;
+ }
+
+ if((zone_info && !all) || zone_set)
+ need_cursor = true;
+
+ if(need_cursor && cursor->x == -30000)
+ {
+ out.printerr("No cursor; place cursor over activity zone.\n");
+ return CR_FAILURE;
+ }
+
+ // give info on unit(s)
+ if(unit_info)
+ {
+ if(all)
+ {
+ // todo: this should really be filtered somehow (prints even dead units etc)
+ for(size_t c=0; c<world->units.all.size(); c++)
+ {
+ df::unit *unit = world->units.all[c];
+ unitInfo(out, unit, verbose);
+ }
+ }
+ else
+ {
+ df::unit *unit = getSelectedUnit(out);
+ if (!unit)
+ return CR_FAILURE;
+ unitInfo(out, unit, verbose);
+ }
+ return CR_OK;
+ }
+
+ // give info on zone(s), chain or cage under cursor
+ // (doesn't use the findXyzAtCursor() methods because zones might overlap and contain a cage or chain)
+ if(zone_info) // || chain_info || cage_info)
+ {
+ for (size_t b = 0; b < world->buildings.all.size(); b++)
+ {
+ df::building * building = world->buildings.all[b];
+
+ // find building under cursor
+ if (!all &&
+ !(building->x1 <= cursor->x && cursor->x <= building->x2 &&
+ building->y1 <= cursor->y && cursor->y <= building->y2 &&
+ building->z == cursor->z))
+ continue;
+
+ zoneInfo(out, building, verbose);
+ chainInfo(out, building, verbose);
+ cageInfo(out, building, verbose);
+ }
+ return CR_OK;
+ }
+
+ // set building at cursor position to be new target zone
+ if(zone_set)
+ {
+ target_zone = findPenPitAtCursor();
+ if(target_zone==-1)
+ {
+ out << "No pen/pasture or pit under cursor!" << endl;
+ return CR_WRONG_USAGE;
+ }
+ out << "Current zone set to #" << target_zone << endl;
+ return CR_OK;
+ }
+
+ // de-assign from pen or pit
+ if(zone_unassign)
+ {
+ // must have unit selected
+ df::unit *unit = getSelectedUnit(out);
+ if (!unit)
+ {
+ out << "No unit selected." << endl;
+ return CR_WRONG_USAGE;
+ }
+
+ // remove assignment reference from unit and old zone
+ if(unassignUnitFromZone(unit))
+ out << "Unit unassigned." << endl;
+ else
+ out << "Unit is not assigned to a zone!" << endl;
+ return CR_OK;
+ }
+
+ // assign to pen or pit
+ if(zone_assign)
+ {
+ // must have unit selected
+ df::unit *unit = getSelectedUnit(out);
+ if (!unit)
+ {
+ out << "No unit selected." << endl;
+ return CR_WRONG_USAGE;
+ }
+
+ // try to get building index from the id
+ int32_t index = findBuildingIndexById(target_zone);
+ if(index == -1)
+ {
+ out << "Invalid building id." << endl;
+ target_zone = -1;
+ return CR_WRONG_USAGE;
+ }
+
+ df::building * building = world->buildings.all.at(index);
+ return assignUnitToZone(out, unit, building, verbose);
+ }
+
+ return CR_OK;
+}