summaryrefslogtreecommitdiff
path: root/plugins/zone.cpp
diff options
context:
space:
mode:
authorRobert Heinrich2012-04-05 20:32:44 +0200
committerRobert Heinrich2012-04-05 20:32:44 +0200
commit577e333ac9e277da88ca28b8cdecdfeeacd77978 (patch)
treeb872db5d27cbf537aafbf31ef993077d78d4e8eb /plugins/zone.cpp
parent511fceff0a8ab739dd04cb80527112cdeafc12b2 (diff)
downloaddfhack-577e333ac9e277da88ca28b8cdecdfeeacd77978.tar.gz
dfhack-577e333ac9e277da88ca28b8cdecdfeeacd77978.tar.bz2
dfhack-577e333ac9e277da88ca28b8cdecdfeeacd77978.tar.xz
some cleanup in zone tool, added slaughter option, autonestbox is now an own command which can be set to run every X ticks
Diffstat (limited to 'plugins/zone.cpp')
-rw-r--r--plugins/zone.cpp421
1 files changed, 317 insertions, 104 deletions
diff --git a/plugins/zone.cpp b/plugins/zone.cpp
index 6b9204b5..9aa8547c 100644
--- a/plugins/zone.cpp
+++ b/plugins/zone.cpp
@@ -65,9 +65,9 @@ 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);
+command_result df_autonestbox (color_ostream &out, vector <string> & parameters);
+//command_result df_autoslaughter (color_ostream &out, vector <string> & parameters);
DFHACK_PLUGIN("zone");
@@ -78,10 +78,10 @@ const string zone_help =
" 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"
+ " slaughter - mark creature(s) for slaughter\n"
+ " if no filters are used, a single unit must be selected.\n"
+ " with filters named units are ignored unless specified.\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"
" verbose - print some more info, mostly useless debug stuff\n"
@@ -103,6 +103,8 @@ const string zone_help_filters =
" own - from own civilization (fortress)\n"
" war - trained war creature\n"
" tamed - tamed\n"
+ " named - has name or nickname\n"
+ " can be used to mark named units for slaughter\n"
" trained - obvious\n"
" untrained - obvious\n"
" male - obvious\n"
@@ -137,6 +139,20 @@ const string zone_help_examples =
" unless you have a mod with egg-laying male elves who give milk.\n";
+const string autonestbox_help =
+ "Assigns unpastured female egg-layers to nestbox zones.\n"
+ "Requires that you create pen/pasture zones above nestboxes.\n"
+ "If the pen is bigger than 1x1 the nestbox must be in the top left corner.\n"
+ "Only 1 unit will be assigned per pen, regardless of the size.\n"
+ "The age of the units is currently not checked, most birds grow up quite fast.\n"
+ "When called without options autonestbox will instantly run once.\n"
+ "Options:\n"
+ " start - run every X frames (df simulation ticks)\n"
+ " default: X=6000 (~60 seconds at 100fps)\n"
+ " stop - stop running automatically\n"
+ " sleep X - change timer to sleep X frames between runs.\n";
+
+
DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{
commands.push_back(PluginCommand(
@@ -144,6 +160,16 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
df_zone, false,
zone_help.c_str()
));
+ commands.push_back(PluginCommand(
+ "autonestbox", "auto-assign nestbox zones.",
+ df_autonestbox, false,
+ zone_help.c_str()
+ ));
+// commands.push_back(PluginCommand(
+// "autoslaughter", "auto-butcher lifestock.",
+// df_autoslaughter, false,
+// zone_help.c_str()
+// ));
return CR_OK;
}
@@ -152,6 +178,63 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}
+///////////////
+// stuff for autonestbox and autoslaughter
+// should be moved to own plugin once the tool methods it shares with the zone plugin are moved to Unit.h / Building.h
+
+command_result autoNestbox( color_ostream &out, bool verbose );
+command_result autoSlaughter( color_ostream &out, bool verbose );
+
+static bool enable_autonestbox = false;
+static bool enable_autoslaughter = false;
+static size_t sleep_autonestbox = 6000;
+static size_t sleep_autoslaughter = 6000;
+static bool autonestbox_did_complain = false; // avoids message spam
+
+DFhackCExport command_result plugin_onstatechange(color_ostream &out, state_change_event event)
+{
+ switch (event)
+ {
+ case DFHack::SC_MAP_LOADED:
+ // initialize from the world just loaded
+ break;
+ case DFHack::SC_MAP_UNLOADED:
+ enable_autonestbox = false;
+ enable_autoslaughter = false;
+ // cleanup
+ break;
+ default:
+ break;
+ }
+ return CR_OK;
+}
+
+DFhackCExport command_result plugin_onupdate ( color_ostream &out )
+{
+ static size_t ticks_autonestbox = 0;
+ static size_t ticks_autoslaughter = 0;
+
+ if(enable_autonestbox)
+ {
+ if(++ticks_autonestbox >= sleep_autonestbox)
+ {
+ ticks_autonestbox = 0;
+ autoNestbox(out, false);
+ }
+ }
+
+ if(enable_autoslaughter)
+ {
+ if(++ticks_autoslaughter >= sleep_autoslaughter)
+ {
+ ticks_autoslaughter = 0;
+ autoSlaughter(out, false);
+ }
+ }
+
+ return CR_OK;
+}
+
///////////////
// Various small tool functions
@@ -164,6 +247,7 @@ 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);
@@ -177,10 +261,10 @@ 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);
+void unitInfo(color_ostream & out, df::unit* creature, bool verbose);
+void zoneInfo(color_ostream & out, df::building* building, bool verbose);
+void cageInfo(color_ostream & out, df::building* building, bool verbose);
+void chainInfo(color_ostream & out, df::building* building, bool verbose);
bool isBuiltCageAtPos(df::coord pos);
int32_t getUnitAge(df::unit* unit)
@@ -195,23 +279,14 @@ int32_t getUnitAge(df::unit* unit)
bool isDead(df::unit* unit)
{
- if(unit->flags1.bits.dead)
- return true;
- else
- return false;
+ return unit->flags1.bits.dead;
}
-
-// marked for slaughter?
bool isMarkedForSlaughter(df::unit* unit)
{
- if(unit->flags2.bits.slaughter)
- return true;
- else
- return false;
+ return unit->flags2.bits.slaughter;
}
-// mark for slaughter
void doMarkForSlaughter(df::unit* unit)
{
unit->flags2.bits.slaughter = 1;
@@ -293,20 +368,14 @@ bool isHunter(df::unit* creature)
// (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;
+ return creature->civ_id == ui->civ_id;
}
// 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;
+ return creature->race == ui->race_id;
}
string getRaceName(df::unit* unit)
@@ -314,21 +383,30 @@ string getRaceName(df::unit* unit)
df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
return raw->creature_id;
}
+string getRaceBabyName(df::unit* unit)
+{
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ return raw->general_baby_name[0];
+}
+string getRaceChildName(df::unit* unit)
+{
+ df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
+ return raw->general_child_name[0];
+}
bool isBaby(df::unit* unit)
{
- if(unit->profession != df::profession::BABY)
- return true;
- else
- return false;
+ return unit->profession == df::profession::BABY;
}
bool isChild(df::unit* unit)
{
- if(unit->profession != df::profession::CHILD)
- return true;
- else
- return false;
+ return unit->profession == df::profession::CHILD;
+}
+
+bool isAdult(df::unit* unit)
+{
+ return !isBaby(unit) && !isChild(unit);
}
bool isEggLayer(df::unit* unit)
@@ -373,17 +451,12 @@ bool isMilkable(df::unit* unit)
bool isMale(df::unit* unit)
{
- if(unit->sex==1)
- return true;
- else
- return false;
+ return unit->sex == 1;
}
+
bool isFemale(df::unit* unit)
{
- if(unit->sex==0)
- return true;
- else
- return false;
+ return unit->sex == 0;
}
// dump some unit info
@@ -406,6 +479,19 @@ void unitInfo(color_ostream & out, df::unit* unit, bool verbose = false)
out << " '" << unit->name.nickname << "'";
out << ", ";
}
+
+ if(isAdult(unit))
+ out << "adult";
+ else if(isBaby(unit))
+ out << "baby";
+ else if(isChild(unit))
+ out << "child";
+ out << " ";
+ // sometimes empty even in vanilla RAWS, sometimes contains full race name (e.g. baby alpaca)
+ // all animals I looked at don't have babies anyways, their offspring starts as CHILD
+ //out << getRaceBabyName(unit);
+ //out << getRaceChildName(unit);
+
out << getRaceName(unit) << " (";
switch(unit->sex)
{
@@ -516,18 +602,12 @@ bool isPit(df::building * building)
bool isCage(df::building * building)
{
- if(building->getType() == building_type::Cage)
- return true;
- else
- return false;
+ return building->getType() == building_type::Cage;
}
bool isChain(df::building * building)
{
- if(building->getType() == building_type::Chain)
- return true;
- else
- return false;
+ return building->getType() == building_type::Chain;
}
bool isActive(df::building * building)
@@ -695,7 +775,6 @@ bool isAssigned(df::unit* unit)
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)
@@ -714,8 +793,6 @@ bool isChained(df::unit* unit)
return contained;
}
-
-
// check if contained in item (e.g. animals in cages)
bool isContainedInItem(df::unit* unit)
{
@@ -787,14 +864,12 @@ bool isEmptyPasture(df::building* building)
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) &&
+ isActive(building) &&
isNestboxAtPos(building->x1, building->y1, building->z))
{
free_building = building;
@@ -804,18 +879,27 @@ df::building* findFreeNestboxZone()
return free_building;
}
+bool isFreeEgglayer(df::unit * unit)
+{
+ if( !isDead(unit)
+ && isFemale(unit)
+ && isTame(unit)
+ && isOwnCiv(unit)
+ && isEggLayer(unit)
+ && !isAssigned(unit)
+ )
+ return true;
+ else
+ return false;
+}
+
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)
- )
+ if(isFreeEgglayer(unit))
{
free_unit = unit;
break;
@@ -824,6 +908,17 @@ df::unit * findFreeEgglayer()
return free_unit;
}
+size_t countFreeEgglayers()
+{
+ size_t count = 0;
+ for (size_t i=0; i < world->units.all.size(); i++)
+ {
+ df::unit* unit = world->units.all[i];
+ if(isFreeEgglayer(unit))
+ count ++;
+ }
+ return count;
+}
// 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
@@ -1094,6 +1189,7 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
bool find_egglayer = false;
bool find_grazer = false;
bool find_milkable = false;
+ bool find_named = false;
bool find_agemin = false;
bool find_agemax = false;
@@ -1111,7 +1207,6 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
bool zone_set = false;
bool verbose = false;
bool all = false;
- bool auto_nestbox = false;
bool unit_slaughter = false;
static int target_zone = -1;
@@ -1231,10 +1326,10 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
out << "Filter by 'tame'." << endl;
find_tame = true;
}
- else if(p == "autonestbox")
+ else if(p == "named")
{
- out << "Auto-assign female tame owned egg-layers to free nestboxes." << endl;
- auto_nestbox = true;
+ out << "Filter by 'has name or nickname'." << endl;
+ find_named = true;
}
else if(p == "slaughter")
{
@@ -1348,6 +1443,12 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
}
}
+ if (!Maps::IsValid())
+ {
+ out.printerr("Map is not available!\n");
+ return CR_FAILURE;
+ }
+
if((zone_info && !all) || zone_set)
need_cursor = true;
@@ -1365,7 +1466,7 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
}
// try to cope with user dumbness
- if(target_agemin > target_agemax || target_agemax < target_agemin)
+ if(target_agemin > target_agemax)
{
out << "Invalid values for minage/maxage specified! I'll swap them." << endl;
int oldmin = target_agemin;
@@ -1373,41 +1474,6 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
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
- {
- df::building * free_building = findFreeNestboxZone();
- df::unit * free_unit = findFreeEgglayer();
- if(free_building && free_unit)
- {
- 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
- {
- 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;
- }
-
// 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)
@@ -1493,6 +1559,7 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
|| (find_milkable && !isMilkable(unit))
|| (find_male && !isMale(unit))
|| (find_female && !isFemale(unit))
+ || (find_named && !unit->name.has_name)
)
continue;
@@ -1510,6 +1577,9 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
}
else if(unit_slaughter)
{
+ // don't slaughter named creatures unless told to do so
+ if(!find_named && unit->name.has_name)
+ continue;
doMarkForSlaughter(unit);
}
@@ -1540,6 +1610,13 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
}
else if(unit_slaughter)
{
+ // by default behave like the game: only allow slaughtering of named war/hunting pets
+ //if(unit->name.has_name && !find_named && !(isWar(unit)||isHunter(unit)))
+ //{
+ // out << "Slaughter of named unit denied. Use the filter 'named' to override this check." << endl;
+ // return CR_OK;
+ //}
+
doMarkForSlaughter(unit);
return CR_OK;
}
@@ -1571,3 +1648,139 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
return CR_OK;
}
+
+command_result df_autonestbox(color_ostream &out, vector <string> & parameters)
+{
+ CoreSuspender suspend;
+
+ bool verbose = false;
+
+ for (size_t i = 0; i < parameters.size(); i++)
+ {
+ string & p = parameters[i];
+
+ if (p == "help" || p == "?")
+ {
+ out << autonestbox_help << endl;
+ return CR_OK;
+ }
+ if (p == "start")
+ {
+ enable_autonestbox = true;
+ autonestbox_did_complain = false;
+ out << "Autonestbox started.";
+ return autoNestbox(out, verbose);
+ //return CR_OK;
+ }
+ if (p == "stop")
+ {
+ enable_autonestbox = false;
+ out << "Autonestbox stopped.";
+ return CR_OK;
+ }
+ else if(p == "verbose")
+ {
+ verbose = true;
+ }
+ else if(p == "sleep")
+ {
+ if(i == parameters.size()-1)
+ {
+ out.printerr("No duration specified!");
+ return CR_WRONG_USAGE;
+ }
+ else
+ {
+ size_t ticks = 0;
+ stringstream ss(parameters[i+1]);
+ i++;
+ ss >> ticks;
+ if(ticks <= 0)
+ {
+ out.printerr("Invalid duration specified (must be > 0)!");
+ return CR_WRONG_USAGE;
+ }
+ sleep_autonestbox = ticks;
+ out << "New sleep timer for autonestbox: " << ticks << " ticks." << endl;
+ return CR_OK;
+ }
+ }
+ else
+ {
+ out << "Unknown command: " << p << endl;
+ return CR_WRONG_USAGE;
+ }
+ }
+ return autoNestbox(out, verbose);
+ //return CR_OK;
+}
+
+command_result autoNestbox( color_ostream &out, bool verbose = false )
+{
+ bool stop = false;
+ size_t processed = 0;
+
+ if (!Maps::IsValid())
+ {
+ out.printerr("Map is not available!\n");
+ enable_autonestbox = false;
+ return CR_FAILURE;
+ }
+
+ do
+ {
+ df::building * free_building = findFreeNestboxZone();
+ df::unit * free_unit = findFreeEgglayer();
+ if(free_building && free_unit)
+ {
+ 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
+ {
+ stop = true;
+ if(free_unit && !free_building)
+ {
+ static size_t old_count = 0;
+ size_t freeEgglayers = countFreeEgglayers();
+ // avoid spamming the same message
+ if(old_count != freeEgglayers)
+ autonestbox_did_complain = false;
+ old_count = freeEgglayers;
+ if(!autonestbox_did_complain)
+ {
+ stringstream ss;
+ ss << freeEgglayers;
+ string announce = "Not enough free nestbox zones found! You need " + ss.str() + " more.";
+ Gui::showAnnouncement(announce, 6, true);
+ out << announce << endl;
+ autonestbox_did_complain = true;
+ }
+ }
+ }
+ } while (!stop);
+ if(processed > 0)
+ {
+ stringstream ss;
+ ss << processed;
+ string announce;
+ announce = ss.str() + " nestboxes were assigned.";
+ Gui::showAnnouncement(announce, 2, false);
+ out << announce << endl;
+ // can complain again
+ // (might lead to spamming the same message twice, but catches the case
+ // where for example 2 new egglayers hatched right after 2 zones were created and assigned)
+ autonestbox_did_complain = false;
+ }
+ return CR_OK;
+}
+
+command_result autoSlaughter( color_ostream &out, bool verbose = false )
+{
+ out << "Autoslaughter would run now." << endl;
+ return CR_OK;
+} \ No newline at end of file