summaryrefslogtreecommitdiff
path: root/plugins/zone.cpp
diff options
context:
space:
mode:
authorRobert Heinrich2012-04-04 14:58:53 +0200
committerRobert Heinrich2012-04-04 14:58:53 +0200
commit5ce0e7dbf268cca19e4f5360147ff3d208cd12ca (patch)
treeb03e1dfb615bfcc6ed7d66172fb90f0c9f045fc5 /plugins/zone.cpp
parent707aee82bd2ed9fa022f01c5e79b963d0aa4dee8 (diff)
downloaddfhack-5ce0e7dbf268cca19e4f5360147ff3d208cd12ca.tar.gz
dfhack-5ce0e7dbf268cca19e4f5360147ff3d208cd12ca.tar.bz2
dfhack-5ce0e7dbf268cca19e4f5360147ff3d208cd12ca.tar.xz
zone tool: added lots of filters, added mass-assigning of nestboxes
Diffstat (limited to 'plugins/zone.cpp')
-rw-r--r--plugins/zone.cpp775
1 files changed, 694 insertions, 81 deletions
diff --git a/plugins/zone.cpp b/plugins/zone.cpp
index 03c38b10..657b2bd3 100644
--- a/plugins/zone.cpp
+++ b/plugins/zone.cpp
@@ -4,11 +4,6 @@
// - 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
@@ -22,6 +17,10 @@
// - assign single selected creature to a zone
// - unassign single creature under cursor from current zone
// - pitting own dwarves :)
+// - 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 and assign it to nestbox pasture
+// maybe check for minimum age? it's not that useful to fill nestboxes with freshly hatched birds
#include <iostream>
#include <iomanip>
@@ -54,6 +53,7 @@ using namespace std;
#include "df/building_chainst.h"
#include "df/general_ref_building_civzone_assignedst.h"
#include <df/creature_raw.h>
+#include <df/caste_raw.h>
using std::vector;
using std::string;
@@ -75,22 +75,66 @@ 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"
+ " assign - assign creature(s) to a pen or pit\n"
+ " if no filters are used, a single unit must be selected.\n"
+ " can be followed by valid zone id which will then be set.\n"
" unassign - unassign selected creature from it's zone\n"
+ " autonestbox - assign unpastured female egg-layers to nestbox zones\n"
+ " requires you to create 1-tile pastures above nestboxes\n"
+ " filters (except count) will be ignored currently\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"
+ " filters - print list of supported filters\n"
+ " examples - print some usage examples\n";
+
+const string zone_help_filters =
+ "Filters (to be used in combination with 'all' or 'count'):\n"
+ " all - in combinations with zinfo/uinfo: print all zones/units\n"
+ " in combination with assign: process all units\n"
+ " should be used in combination with further filters\n"
+ " count - must be followed by number. process X units\n"
+ " should be used in combination with further filters\n"
+ " race - must be followed by a race raw id (e.g. BIRD_TURKEY)\n"
+ " unassigned - not assigned to zone, chain or built cage\n"
+ " caged - in a built cage\n"
+ " uncaged - not in cage\n"
+ " foreign - not of own civilization (i.e. own fortress)\n"
+ " own - from own civilization (fortress)\n"
+ " war - trained war creature\n"
+ " tamed - tamed\n"
+ " trained - obvious\n"
+ " untrained - obvious\n"
+ " male - obvious\n"
+ " female - obvious\n"
+ " egglayer - race lays eggs (use together with 'female')\n"
+ " grazer - obvious\n"
+ " milkable - race is milkable (use together with 'female')\n"
+ " minage - minimum age. must be followed by number\n"
+ " maxage - maximum age. must be followed by number\n";
+
+const string zone_help_examples =
+ "Example for assigning single units:\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) select unit with 'v', 'k' or from unit list or inside a cage\n"
" (ingame) press hotkey to assign unit to it's new home (or pit)\n"
- ;
+ "Examples for assigning with filters:\n"
+ " (this assumes you have already set up a target zone)\n"
+ " zone assign all own grazer maxage 10\n"
+ " zone assign count 5 own female milkable\n"
+ " zone assign all own race DWARF maxage 2\n"
+ " throw all useless kids into a pit :)\n"
+ "Notes:\n"
+ " Assigning per filters ignores built cages and chains currently.\n"
+ " Usually you should always use the filter 'own' (which implies tame)\n"
+ " unless you want to use the zone tool for pitting hostiles.\n"
+ " 'own' ignores own dwarves unless you specify 'race DWARF'\n"
+ " (so it's safe to use 'assign all own' to one big pasture\n"
+ " if you want to have all your animals at the same place).\n"
+ " 'egglayer' and 'milkable' should be used together with 'female'\n"
+ " unless you have a mod with egg-laying male elves who give milk.\n";
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
@@ -112,7 +156,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
///////////////
// Various small tool functions
// probably many of these should be moved to Unit.h and Building.h sometime later...
-int32_t getCreatureAge(df::unit* unit);
+
+int32_t getUnitAge(df::unit* unit);
bool isTame(df::unit* unit);
bool isTrained(df::unit* unit);
bool isTrained(df::unit* creature);
@@ -123,6 +168,7 @@ 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();
@@ -135,7 +181,7 @@ 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);
-
+bool isBuiltCageAtPos(df::coord pos);
int32_t getUnitAge(df::unit* unit)
{
@@ -236,30 +282,109 @@ bool isOwnCiv(df::unit* creature)
return false;
}
+// check if creature belongs to the player's race
+// (in combination with check for civ helps to filter out own dwarves)
+bool isOwnRace(df::unit* creature)
+{
+ if(creature->race == ui->race_id)
+ return true;
+ else
+ return false;
+}
+
+string getRaceName(df::unit* unit)
+{
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ return raw->creature_id;
+}
+
+bool isEggLayer(df::unit* unit)
+{
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ size_t sizecas = raw->caste.size();
+ for (size_t j = 0; j < sizecas;j++)
+ {
+ df::caste_raw *caste = raw->caste[j];
+ if( caste->flags.is_set(caste_raw_flags::LAYS_EGGS)
+ || caste->flags.is_set(caste_raw_flags::LAYS_UNUSUAL_EGGS))
+ return true;
+ }
+ return false;
+}
+
+bool isGrazer(df::unit* unit)
+{
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ size_t sizecas = raw->caste.size();
+ for (size_t j = 0; j < sizecas;j++)
+ {
+ df::caste_raw *caste = raw->caste[j];
+ if(caste->flags.is_set(caste_raw_flags::GRAZER))
+ return true;
+ }
+ return false;
+}
+
+bool isMilkable(df::unit* unit)
+{
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ size_t sizecas = raw->caste.size();
+ for (size_t j = 0; j < sizecas;j++)
+ {
+ df::caste_raw *caste = raw->caste[j];
+ if(caste->flags.is_set(caste_raw_flags::MILKABLE))
+ return true;
+ }
+ return false;
+}
+
+bool isMale(df::unit* unit)
+{
+ if(unit->sex==1)
+ return true;
+ else
+ return false;
+}
+bool isFemale(df::unit* unit)
+{
+ if(unit->sex==0)
+ return true;
+ else
+ return false;
+}
+
// dump some unit info
-void unitInfo(color_ostream & out, df::unit* unit, bool list_refs = false)
+void unitInfo(color_ostream & out, df::unit* unit, bool verbose = false)
{
if(isDead(unit))
return;
- out.print("Unit %d", unit->id); //race %d, civ %d,", creature->race, creature->civ_id
+ 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 << " (";
+ {
+ // units given a nick with the rename tool might not have a first name (animals etc)
+ string firstname = unit->name.first_name;
+ if(firstname.size() > 0)
+ {
+ firstname[0] = toupper(firstname[0]);
+ out << "Name: " << firstname;
+ }
+ if(unit->name.nickname.size() > 0)
+ out << " '" << unit->name.nickname << "'";
+ out << ", ";
+ }
+ out << getRaceName(unit) << " (";
switch(unit->sex)
{
- case -1:
- out << "n/a";
- break;
case 0:
out << "female";
break;
case 1:
out << "male";
break;
+ case -1:
default:
- out << "INVALID!";
+ out << "n/a";
break;
}
out << ")";
@@ -274,9 +399,19 @@ void unitInfo(color_ostream & out, df::unit* unit, bool list_refs = false)
if(isHunter(unit))
out << ", hunter";
+ if(verbose)
+ {
+ //out << ". Pos: ("<<unit->pos.x << "/"<< unit->pos.y << "/" << unit->pos.z << ")" << endl;
+ if(isEggLayer(unit))
+ out << ", egglayer";
+ if(isGrazer(unit))
+ out << ", grazer";
+ if(isMilkable(unit))
+ out << ", milkable";
+ }
out << endl;
- if(!list_refs)
+ if(!verbose)
return;
//out << "number of refs: " << creature->refs.size() << endl;
@@ -502,6 +637,161 @@ df::general_ref_building_civzone_assignedst * createCivzoneRef()
return newref;
}
+// check if assigned to pen, pit, (built) cage or chain
+// note: BUILDING_CAGED is not set for animals (maybe it's used for dwarves who get caged as sentence)
+// animals in cages (no matter if built or on stockpile) get the ref CONTAINED_IN_ITEM instead
+// removing them from cages on stockpiles is no problem even without clearing the ref
+// and usually it will be desired behavior to do so.
+bool isAssigned(df::unit* unit)
+{
+ bool assigned = false;
+ for (size_t r=0; r < unit->refs.size(); r++)
+ {
+ df::general_ref * ref = unit->refs[r];
+ auto rtype = ref->getType();
+ if( rtype == df::general_ref_type::BUILDING_CIVZONE_ASSIGNED
+ || rtype == df::general_ref_type::BUILDING_CAGED
+ || rtype == df::general_ref_type::BUILDING_CHAIN
+ || (rtype == df::general_ref_type::CONTAINED_IN_ITEM && isBuiltCageAtPos(unit->pos))
+ )
+ {
+ assigned = true;
+ break;
+ }
+ }
+ return assigned;
+}
+
+
+// check if assigned to a chain or built cage
+// (need to check if the ref needs to be removed, until then touching them is forbidden)
+bool isChained(df::unit* unit)
+{
+ bool contained = false;
+ for (size_t r=0; r < unit->refs.size(); r++)
+ {
+ df::general_ref * ref = unit->refs[r];
+ auto rtype = ref->getType();
+ if(rtype == df::general_ref_type::BUILDING_CHAIN)
+ {
+ contained = true;
+ break;
+ }
+ }
+ return contained;
+}
+
+
+
+// check if contained in item (e.g. animals in cages)
+bool isContainedInItem(df::unit* unit)
+{
+ bool contained = false;
+ for (size_t r=0; r < unit->refs.size(); r++)
+ {
+ df::general_ref * ref = unit->refs[r];
+ auto rtype = ref->getType();
+ if(rtype == df::general_ref_type::CONTAINED_IN_ITEM)
+ {
+ contained = true;
+ break;
+ }
+ }
+ return contained;
+}
+
+// check a map position for a built cage
+// animals in cages are CONTAINED_IN_ITEM, no matter if they are on a stockpile or inside a built cage
+// if they are on animal stockpiles they should count as unassigned to allow pasturing them
+// if they are inside built cages they should be ignored in case the cage is a zoo or linked to a lever or whatever
+bool isBuiltCageAtPos(df::coord pos)
+{
+ bool cage = false;
+ for (size_t b=0; b < world->buildings.all.size(); b++)
+ {
+ df::building* building = world->buildings.all[b];
+ if( building->getType() == building_type::Cage
+ && building->x1 == pos.x
+ && building->y1 == pos.y
+ && building->z == pos.z )
+ {
+ cage = true;
+ break;
+ }
+ }
+ return cage;
+}
+
+bool isNestboxAtPos(int32_t x, int32_t y, int32_t z)
+{
+ bool found = false;
+ for (size_t b=0; b < world->buildings.all.size(); b++)
+ {
+ df::building* building = world->buildings.all[b];
+ if( building->getType() == building_type::NestBox
+ && building->x1 == x
+ && building->y1 == y
+ && building->z == z )
+ {
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+bool isEmptyPasture(df::building* building)
+{
+ if(!isPenPasture(building))
+ return false;
+ df::building_civzonest * civ = (df::building_civzonest *) building;
+ if(civ->assigned_creature.size() == 0)
+ return true;
+ else
+ return false;
+}
+
+df::building* findFreeNestboxZone()
+{
+ df::building * free_building = NULL;
+
+ //df::unit * free_unit = findFreeEgglayer();
+
+ bool cage = false;
+ for (size_t b=0; b < world->buildings.all.size(); b++)
+ {
+ df::building* building = world->buildings.all[b];
+ if( isEmptyPasture(building) &&
+ isNestboxAtPos(building->x1, building->y1, building->z))
+ {
+ free_building = building;
+ break;
+ }
+ }
+ return free_building;
+}
+
+df::unit * findFreeEgglayer()
+{
+ df::unit* free_unit = NULL;
+ for (size_t i=0; i < world->units.all.size(); i++)
+ {
+ df::unit* unit = world->units.all[i];
+ if( isFemale(unit)
+ && isTame(unit)
+ && isOwnCiv(unit)
+ && isEggLayer(unit)
+ && !isAssigned(unit)
+ )
+ {
+ free_unit = unit;
+ break;
+ }
+ }
+ return free_unit;
+}
+
+
// 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
@@ -561,9 +851,14 @@ command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building
}
// check if unit is already pastured, remove that ref from unit and old pasture
+ // testing showed that this only seems to be necessary for pastured creatures
+ // if they are in cages on stockpiles the game unassigns them automatically
+ // (need to check if that is also true for chains and built cages)
+ bool cleared_old = unassignUnitFromZone(unit);
+
if(verbose)
{
- if(unassignUnitFromZone(unit))
+ if(cleared_old)
out << "old zone info cleared.";
else
out << "no old zone info found.";
@@ -588,9 +883,36 @@ command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building
return CR_OK;
}
+command_result assignUnitToCage(color_ostream& out, df::unit* unit, df::building* building, bool verbose = false)
+{
+ out << "sorry. assigning to cages is not possible yet." << endl;
+ return CR_WRONG_USAGE;
+}
+
+command_result assignUnitToChain(color_ostream& out, df::unit* unit, df::building* building, bool verbose = false)
+{
+ out << "sorry. assigning to chains is not possible yet." << endl;
+ return CR_WRONG_USAGE;
+}
+
+command_result assignUnitToBuilding(color_ostream& out, df::unit* unit, df::building* building, bool verbose = false)
+{
+ command_result result = CR_WRONG_USAGE;
+
+ if(isActivityZone(building))
+ result = assignUnitToZone(out, unit, building, verbose);
+ else if(isCage(building))
+ result = assignUnitToCage(out, unit, building, verbose);
+ else if(isChain(building))
+ result = assignUnitToChain(out, unit, building, verbose);
+ else
+ out << "Cannot assign units to this type of building!" << endl;
+
+ return result;
+}
// dump some zone info
-void zoneInfo(color_ostream & out, df::building* building, bool list_refs = false)
+void zoneInfo(color_ostream & out, df::building* building, bool verbose = false)
{
if(building->getType()!= building_type::Civzone)
return;
@@ -616,8 +938,6 @@ void zoneInfo(color_ostream & out, df::building* building, bool list_refs = fals
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)
@@ -625,6 +945,12 @@ void zoneInfo(color_ostream & out, df::building* building, bool list_refs = fals
else
return;
out << endl;
+ out << "x1:" <<building->x1
+ << " x2:" <<building->x2
+ << " y1:" <<building->y1
+ << " y2:" <<building->y2
+ << " z:" <<building->z
+ << endl;
int32_t creaturecount = civ->assigned_creature.size();
out << "Creatures in this zone: " << creaturecount << endl;
@@ -639,13 +965,13 @@ void zoneInfo(color_ostream & out, df::building* building, bool list_refs = fals
if(creature->id != cindex)
continue;
- unitInfo(out, creature, false);
+ unitInfo(out, creature, verbose);
}
}
}
// dump some cage info
-void cageInfo(color_ostream & out, df::building* building, bool list_refs = false)
+void cageInfo(color_ostream & out, df::building* building, bool verbose = false)
{
if(!isCage(building))
return;
@@ -659,6 +985,11 @@ void cageInfo(color_ostream & out, df::building* building, bool list_refs = fals
building->getType());
out.print("\n");
+ out << "x:" << building->x1
+ << " y:" << building->y1
+ << " z:" << building->z
+ << endl;
+
df::building_cagest * cage = (df::building_cagest*) building;
int32_t creaturecount = cage->assigned_creature.size();
out << "Creatures in this cage: " << creaturecount << endl;
@@ -673,12 +1004,11 @@ void cageInfo(color_ostream & out, df::building* building, bool list_refs = fals
if(creature->id != cindex)
continue;
- unitInfo(out, creature, false);
+ unitInfo(out, creature, verbose);
}
}
}
-
// dump some chain/restraint info
void chainInfo(color_ostream & out, df::building* building, bool list_refs = false)
{
@@ -716,12 +1046,39 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
bool zone_info = false;
//bool cage_info = false;
//bool chain_info = false;
+
+ bool find_unassigned = false;
+ bool find_caged = false;
+ bool find_uncaged = false;
+ bool find_foreign = false;
+ bool find_untrained = false;
+ //bool find_trained = false;
+ bool find_war = false;
+ bool find_own = false;
+ bool find_tame = false;
+ bool find_male = false;
+ bool find_female = false;
+ bool find_egglayer = false;
+ bool find_grazer = false;
+ bool find_milkable = false;
+
+ bool find_agemin = false;
+ bool find_agemax = false;
+ int target_agemin = 0;
+ int target_agemax = 0;
+
+ bool find_count = false;
+ size_t target_count = 0;
+
+ bool find_race = false;
+ string target_race = "";
bool zone_assign = false;
bool zone_unassign = false;
bool zone_set = false;
bool verbose = false;
bool all = false;
+ bool auto_nestbox = false;
static int target_zone = -1;
for (size_t i = 0; i < parameters.size(); i++)
@@ -733,6 +1090,16 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
out << zone_help << endl;
return CR_OK;
}
+ if (p == "filters")
+ {
+ out << zone_help_filters << endl;
+ return CR_OK;
+ }
+ if (p == "examples")
+ {
+ out << zone_help_examples << endl;
+ return CR_OK;
+ }
else if(p == "zinfo")
{
zone_info = true;
@@ -741,14 +1108,6 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
{
unit_info = true;
}
- //else if(p == "cinfo")
- //{
- // cage_info = true;
- //}
- //else if(p == "rinfo")
- //{
- // chain_info = true;
- //}
else if(p == "verbose")
{
verbose = true;
@@ -781,6 +1140,158 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
zone_assign = true;
}
}
+ else if(p == "race")
+ {
+ if(i == parameters.size()-1)
+ {
+ out.printerr("No race id specified!");
+ return CR_WRONG_USAGE;
+ }
+ else
+ {
+ target_race = parameters[i+1];
+ i++;
+ out << "Filter by race " << target_race << endl;
+ find_race = true;
+ }
+ }
+ else if(p == "foreign")
+ {
+ out << "Filter by 'foreign' (i.e. not from the fortress civ, can be a dwarf)." << endl;
+ find_foreign = true;
+ }
+ else if(p == "unassigned")
+ {
+ out << "Filter by 'unassigned'." << endl;
+ find_unassigned = true;
+ }
+ else if(p == "caged")
+ {
+ out << "Filter by 'caged' (ignores built cages)." << endl;
+ find_caged = true;
+ }
+ else if(p == "uncaged")
+ {
+ out << "Filter by 'uncaged'." << endl;
+ find_uncaged = true;
+ }
+ else if(p == "untrained")
+ {
+ out << "Filter by 'untrained'." << endl;
+ find_untrained = true;
+ }
+ else if(p == "war")
+ {
+ out << "Filter by 'trained war creature'." << endl;
+ find_war = true;
+ }
+ else if(p == "own")
+ {
+ out << "Filter by 'own civilization'." << endl;
+ find_own = true;
+ }
+ else if(p == "tame")
+ {
+ out << "Filter by 'tame'." << endl;
+ find_tame = true;
+ }
+ else if(p == "autonestbox")
+ {
+ out << "Auto-assign female tame owned egg-layers to free nestboxes." << endl;
+ auto_nestbox = true;
+ }
+ else if(p == "count")
+ {
+ if(i == parameters.size()-1)
+ {
+ out.printerr("No count specified!");
+ return CR_WRONG_USAGE;
+ }
+ else
+ {
+ stringstream ss(parameters[i+1]);
+ i++;
+ ss >> target_count;
+ if(target_count <= 0)
+ {
+ out.printerr("Invalid count specified (must be > 0)!");
+ return CR_WRONG_USAGE;
+ }
+ find_count = true;
+ out << "Process up to " << target_count << " units." << endl;
+ }
+ }
+ else if(p == "age")
+ {
+ if(i == parameters.size()-1)
+ {
+ out.printerr("No age specified!");
+ return CR_WRONG_USAGE;
+ }
+ else
+ {
+ stringstream ss(parameters[i+1]);
+ i++;
+ ss >> target_agemin;
+ ss >> target_agemax;
+ find_agemin = true;
+ find_agemax = true;
+ out << "Filter by exact age of " << target_agemin << endl;
+ }
+ }
+ else if(p == "minage")
+ {
+ if(i == parameters.size()-1)
+ {
+ out.printerr("No age specified!");
+ return CR_WRONG_USAGE;
+ }
+ else
+ {
+ stringstream ss(parameters[i+1]);
+ i++;
+ ss >> target_agemin;
+ find_agemin = true;
+ out << "Filter by minimum age of " << target_agemin << endl;
+ }
+ }
+ else if(p == "maxage")
+ {
+ if(i == parameters.size()-1)
+ {
+ out.printerr("No age specified!");
+ return CR_WRONG_USAGE;
+ }
+ else
+ {
+ stringstream ss(parameters[i+1]);
+ i++;
+ ss >> target_agemax;
+ find_agemax = true;
+ out << "Filter by maximum age of " << target_agemax << endl;
+ }
+ }
+ else if(p == "male")
+ {
+ find_male = true;
+ }
+ else if(p == "female")
+ {
+ find_female = true;
+ }
+ else if(p == "egglayer")
+ {
+ find_egglayer = true;
+ }
+ else if(p == "grazer")
+ {
+ find_grazer = true;
+ }
+ else if(p == "milkable")
+ {
+ find_milkable = true;
+ }
+
else if(p == "set")
{
zone_set = true;
@@ -790,7 +1301,10 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
all = true;
}
else
+ {
+ out << "Unknown command: " << p << endl;
return CR_WRONG_USAGE;
+ }
}
if((zone_info && !all) || zone_set)
@@ -802,25 +1316,54 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
return CR_FAILURE;
}
- // give info on unit(s)
- if(unit_info)
+ // search for male and female is exclusive, so drop the flags if both are specified
+ if(find_male && find_female)
{
- if(all)
+ find_male=false;
+ find_female=false;
+ }
+
+ // try to cope with user dumbness
+ if(target_agemin > target_agemax || target_agemax < target_agemin)
+ {
+ out << "Invalid values for minage/maxage specified! I'll swap them." << endl;
+ int oldmin = target_agemin;
+ target_agemin = target_agemax;
+ target_agemax = oldmin;
+ }
+
+ // auto-assign to empty nestboxes. this requires an empty 1x1 pen/pasture zone placed over a nestbox
+ // currently it will not be checked if the nestbox is already claimed by another egglayer
+ if(auto_nestbox)
+ {
+ bool stop = false;
+ size_t processed = 0;
+ do
{
- // todo: this should really be filtered somehow (prints even dead units etc)
- for(size_t c=0; c<world->units.all.size(); c++)
+ df::building * free_building = findFreeNestboxZone();
+ df::unit * free_unit = findFreeEgglayer();
+ if(free_building && free_unit)
{
- df::unit *unit = world->units.all[c];
- unitInfo(out, unit, verbose);
+ command_result result = assignUnitToBuilding(out, free_unit, free_building, verbose);
+ if(result != CR_OK)
+ return result;
+ processed ++;
+ if(find_count && processed >= target_count)
+ stop = true;
}
- }
- else
- {
- df::unit *unit = getSelectedUnit(out);
- if (!unit)
- return CR_FAILURE;
- unitInfo(out, unit, verbose);
- }
+ else
+ {
+ stop = true;
+ if(free_unit && !free_building)
+ {
+ out << "Not enough free nestbox zones found!" << endl;
+ out << "You can check how many more you need with:" << endl;
+ out << "'zone uinfo all unassigned own female egglayer'" << endl;
+ out << "Or simply build some more and use 'zone autonestbox' again." << endl;
+ }
+ }
+ } while (!stop);
+ out << processed << " units assigned to their new nestboxes." << endl;
return CR_OK;
}
@@ -859,27 +1402,102 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
return CR_OK;
}
- // de-assign from pen or pit
- if(zone_unassign)
+ // assign to pen or pit
+ if(zone_assign || unit_info)
{
- // must have unit selected
- df::unit *unit = getSelectedUnit(out);
- if (!unit)
+ df::building * building;
+ if(zone_assign)
{
- 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;
+ }
+ building = world->buildings.all.at(index);
}
- // remove assignment reference from unit and old zone
- if(unassignUnitFromZone(unit))
- out << "Unit unassigned." << endl;
+ if(all || find_count)
+ {
+ size_t count = 0;
+ for(size_t c = 0; c < world->units.all.size(); c++)
+ {
+ df::unit *unit = world->units.all[c];
+ if(find_race && getRaceName(unit) != target_race)
+ continue;
+ // ignore own dwarves by default
+ if(isOwnCiv(unit) && isOwnRace(unit))
+ {
+ if(!find_race)
+ {
+ continue;
+ }
+ }
+
+ if( (find_unassigned && isAssigned(unit))
+ || (isContainedInItem(unit) && (find_uncaged || isBuiltCageAtPos(unit->pos)))
+ || (isChained(unit))
+ || (find_caged && !isContainedInItem(unit))
+ || (find_own && !isOwnCiv(unit))
+ || (find_foreign && isOwnCiv(unit))
+ || (find_tame && !isTame(unit))
+ || (find_untrained && isTrained(unit))
+ || (find_war && !isWar(unit))
+ || (find_agemin && getUnitAge(unit)<target_agemin)
+ || (find_agemax && getUnitAge(unit)>target_agemax)
+ || (find_grazer && !isGrazer(unit))
+ || (find_egglayer && !isEggLayer(unit))
+ || (find_milkable && !isMilkable(unit))
+ || (find_male && !isMale(unit))
+ || (find_female && !isFemale(unit))
+ )
+ continue;
+
+ if(unit_info)
+ {
+ if(unit->pos.x <0 || unit->pos.y<0 || unit->pos.z<0)
+ out << "invalid unit pos" << endl;
+ unitInfo(out, unit, verbose);
+ }
+ else
+ {
+ command_result result = assignUnitToBuilding(out, unit, building, verbose);
+ if(result != CR_OK)
+ return result;
+ }
+ count++;
+ if(find_count && count >= target_count)
+ break;
+ }
+ out << "Processed creatures: " << count << endl;
+ }
else
- out << "Unit is not assigned to a zone!" << endl;
- return CR_OK;
+ {
+ // must have unit selected
+ df::unit *unit = getSelectedUnit(out);
+ if (!unit)
+ {
+ out << "No unit selected." << endl;
+ return CR_WRONG_USAGE;
+ }
+
+ if(unit_info)
+ {
+ unitInfo(out, unit, verbose);
+ return CR_OK;
+ }
+
+ return assignUnitToBuilding(out, unit, building, verbose);
+ }
}
- // assign to pen or pit
- if(zone_assign)
+ // de-assign from pen or pit
+ // using the zone tool to free creatures from cages or chains
+ // is pointless imo since that is already quite easy using the ingame UI.
+ // but it's easy to implement so I might as well add it later
+ if(zone_unassign)
{
// must have unit selected
df::unit *unit = getSelectedUnit(out);
@@ -889,17 +1507,12 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
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);
+ // remove assignment reference from unit and old zone
+ if(unassignUnitFromZone(unit))
+ out << "Unit unassigned." << endl;
+ else
+ out << "Unit is not assigned to an activity zone!" << endl;
+ return CR_OK;
}
return CR_OK;