some cleanup in zone tool, added slaughter option, autonestbox is now an own command which can be set to run every X ticks

develop
Robert Heinrich 2012-04-05 20:32:44 +02:00
parent 511fceff0a
commit 577e333ac9
2 changed files with 327 additions and 105 deletions

@ -808,7 +808,6 @@ Options:
:set: Set zone under cursor as default for future assigns. :set: Set zone under cursor as default for future assigns.
:assign: Assign unit(s) to the pen or pit marked with the 'set' command. If no filters are set a unit must be selected in the in-game ui. Can also be followed by a valid zone id which will be set instead. :assign: Assign unit(s) to the pen or pit marked with the 'set' command. If no filters are set a unit must be selected in the in-game ui. Can also be followed by a valid zone id which will be set instead.
:unassign: Unassign selected creature from it's zone. :unassign: Unassign selected creature from it's zone.
:autonestbox: Assign all (unless count is specified) unpastured female egg-layers to empty pens which contain a nestbox. If the pen is bigger than 1x1 the nestbox must be placed at the top left corner to be recognized. Only 1 unit will be assigned per pen.
:uinfo: Print info about unit(s). If no filters are set a unit must be selected in the in-game ui. :uinfo: Print info about unit(s). If no filters are set a unit must be selected in the in-game ui.
:zinfo: Print info about zone(s). If no filters are set zones under the cursor are listed. :zinfo: Print info about zone(s). If no filters are set zones under the cursor are listed.
:verbose: Print some more info. :verbose: Print some more info.
@ -853,3 +852,13 @@ All filters can be used together with the 'assign' command. The only restriction
Assign up to 5 own female milkable creatures to the selected pasture. Assign up to 5 own female milkable creatures to the selected pasture.
``zone assign all own race DWARF maxage 2`` ``zone assign all own race DWARF maxage 2``
Throw all useless kids into a pit :) Throw all useless kids into a pit :)
autonestbox
===========
Assigns unpastured female egg-layers to nestbox zones. Requires that you create pen/pasture zones above nestboxes. If the pen is bigger than 1x1 the nestbox must be in the top left corner. Only 1 unit will be assigned per pen, regardless of the size. The age of the units is currently not checked, most birds grow up quite fast. When called without options autonestbox will instantly run once.
Options:
--------
:start: Start running every X frames (df simulation ticks). Default: X=6000, which would be every 60 seconds at 100fps.
:stop: Stop running automatically.
:sleep: Must be followed by number X. Changes the timer to sleep X frames between runs.

@ -65,9 +65,9 @@ using df::global::ui;
using namespace DFHack::Gui; 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_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"); DFHACK_PLUGIN("zone");
@ -78,10 +78,10 @@ const string zone_help =
" assign - assign creature(s) to a pen or pit\n" " assign - assign creature(s) to a pen or pit\n"
" if no filters are used, a single unit must be selected.\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" " 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" " 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" " uinfo - print info about selected unit\n"
" zinfo - print info about zone(s) under cursor\n" " zinfo - print info about zone(s) under cursor\n"
" verbose - print some more info, mostly useless debug stuff\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" " own - from own civilization (fortress)\n"
" war - trained war creature\n" " war - trained war creature\n"
" tamed - tamed\n" " tamed - tamed\n"
" named - has name or nickname\n"
" can be used to mark named units for slaughter\n"
" trained - obvious\n" " trained - obvious\n"
" untrained - obvious\n" " untrained - obvious\n"
" male - 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"; " 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) DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <PluginCommand> &commands)
{ {
commands.push_back(PluginCommand( commands.push_back(PluginCommand(
@ -144,6 +160,16 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
df_zone, false, df_zone, false,
zone_help.c_str() 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; return CR_OK;
} }
@ -152,6 +178,63 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK; 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 // Various small tool functions
@ -164,6 +247,7 @@ bool isTrained(df::unit* creature);
bool isWar(df::unit* creature); bool isWar(df::unit* creature);
bool isHunter(df::unit* creature); bool isHunter(df::unit* creature);
bool isOwnCiv(df::unit* creature); bool isOwnCiv(df::unit* creature);
bool isActivityZone(df::building * building); bool isActivityZone(df::building * building);
bool isPenPasture(df::building * building); bool isPenPasture(df::building * building);
bool isPit(df::building * building); bool isPit(df::building * building);
@ -177,10 +261,10 @@ int32_t findChainAtCursor();
df::general_ref_building_civzone_assignedst * createCivzoneRef(); df::general_ref_building_civzone_assignedst * createCivzoneRef();
bool unassignUnitFromZone(df::unit* unit); bool unassignUnitFromZone(df::unit* unit);
command_result assignUnitToZone(color_ostream& out, df::unit* unit, df::building* building, bool verbose); 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 unitInfo(color_ostream & out, df::unit* creature, bool verbose);
void zoneInfo(color_ostream & out, df::building* building, bool list_refs); void zoneInfo(color_ostream & out, df::building* building, bool verbose);
void cageInfo(color_ostream & out, df::building* building, bool list_refs); void cageInfo(color_ostream & out, df::building* building, bool verbose);
void chainInfo(color_ostream & out, df::building* building, bool list_refs); void chainInfo(color_ostream & out, df::building* building, bool verbose);
bool isBuiltCageAtPos(df::coord pos); bool isBuiltCageAtPos(df::coord pos);
int32_t getUnitAge(df::unit* unit) int32_t getUnitAge(df::unit* unit)
@ -195,23 +279,14 @@ int32_t getUnitAge(df::unit* unit)
bool isDead(df::unit* unit) bool isDead(df::unit* unit)
{ {
if(unit->flags1.bits.dead) return unit->flags1.bits.dead;
return true;
else
return false;
} }
// marked for slaughter?
bool isMarkedForSlaughter(df::unit* unit) bool isMarkedForSlaughter(df::unit* unit)
{ {
if(unit->flags2.bits.slaughter) return unit->flags2.bits.slaughter;
return true;
else
return false;
} }
// mark for slaughter
void doMarkForSlaughter(df::unit* unit) void doMarkForSlaughter(df::unit* unit)
{ {
unit->flags2.bits.slaughter = 1; unit->flags2.bits.slaughter = 1;
@ -293,20 +368,14 @@ bool isHunter(df::unit* creature)
// (don't try to pasture/slaughter random untame animals) // (don't try to pasture/slaughter random untame animals)
bool isOwnCiv(df::unit* creature) bool isOwnCiv(df::unit* creature)
{ {
if(creature->civ_id == ui->civ_id) return creature->civ_id == ui->civ_id;
return true;
else
return false;
} }
// check if creature belongs to the player's race // check if creature belongs to the player's race
// (in combination with check for civ helps to filter out own dwarves) // (in combination with check for civ helps to filter out own dwarves)
bool isOwnRace(df::unit* creature) bool isOwnRace(df::unit* creature)
{ {
if(creature->race == ui->race_id) return creature->race == ui->race_id;
return true;
else
return false;
} }
string getRaceName(df::unit* unit) 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]; df::creature_raw *raw = df::global::world->raws.creatures.all[unit->race];
return raw->creature_id; 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) bool isBaby(df::unit* unit)
{ {
if(unit->profession != df::profession::BABY) return unit->profession == df::profession::BABY;
return true;
else
return false;
} }
bool isChild(df::unit* unit) bool isChild(df::unit* unit)
{ {
if(unit->profession != df::profession::CHILD) return unit->profession == df::profession::CHILD;
return true; }
else
return false; bool isAdult(df::unit* unit)
{
return !isBaby(unit) && !isChild(unit);
} }
bool isEggLayer(df::unit* unit) bool isEggLayer(df::unit* unit)
@ -373,17 +451,12 @@ bool isMilkable(df::unit* unit)
bool isMale(df::unit* unit) bool isMale(df::unit* unit)
{ {
if(unit->sex==1) return unit->sex == 1;
return true;
else
return false;
} }
bool isFemale(df::unit* unit) bool isFemale(df::unit* unit)
{ {
if(unit->sex==0) return unit->sex == 0;
return true;
else
return false;
} }
// dump some unit info // dump some unit info
@ -406,6 +479,19 @@ void unitInfo(color_ostream & out, df::unit* unit, bool verbose = false)
out << " '" << unit->name.nickname << "'"; out << " '" << unit->name.nickname << "'";
out << ", "; 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) << " ("; out << getRaceName(unit) << " (";
switch(unit->sex) switch(unit->sex)
{ {
@ -516,18 +602,12 @@ bool isPit(df::building * building)
bool isCage(df::building * building) bool isCage(df::building * building)
{ {
if(building->getType() == building_type::Cage) return building->getType() == building_type::Cage;
return true;
else
return false;
} }
bool isChain(df::building * building) bool isChain(df::building * building)
{ {
if(building->getType() == building_type::Chain) return building->getType() == building_type::Chain;
return true;
else
return false;
} }
bool isActive(df::building * building) bool isActive(df::building * building)
@ -695,7 +775,6 @@ bool isAssigned(df::unit* unit)
return assigned; return assigned;
} }
// check if assigned to a chain or built cage // 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) // (need to check if the ref needs to be removed, until then touching them is forbidden)
bool isChained(df::unit* unit) bool isChained(df::unit* unit)
@ -714,8 +793,6 @@ bool isChained(df::unit* unit)
return contained; return contained;
} }
// check if contained in item (e.g. animals in cages) // check if contained in item (e.g. animals in cages)
bool isContainedInItem(df::unit* unit) bool isContainedInItem(df::unit* unit)
{ {
@ -787,14 +864,12 @@ bool isEmptyPasture(df::building* building)
df::building* findFreeNestboxZone() df::building* findFreeNestboxZone()
{ {
df::building * free_building = NULL; df::building * free_building = NULL;
//df::unit * free_unit = findFreeEgglayer();
bool cage = false; bool cage = false;
for (size_t b=0; b < world->buildings.all.size(); b++) for (size_t b=0; b < world->buildings.all.size(); b++)
{ {
df::building* building = world->buildings.all[b]; df::building* building = world->buildings.all[b];
if( isEmptyPasture(building) && if( isEmptyPasture(building) &&
isActive(building) &&
isNestboxAtPos(building->x1, building->y1, building->z)) isNestboxAtPos(building->x1, building->y1, building->z))
{ {
free_building = building; free_building = building;
@ -804,18 +879,27 @@ df::building* findFreeNestboxZone()
return free_building; 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 * findFreeEgglayer()
{ {
df::unit* free_unit = NULL; df::unit* free_unit = NULL;
for (size_t i=0; i < world->units.all.size(); i++) for (size_t i=0; i < world->units.all.size(); i++)
{ {
df::unit* unit = world->units.all[i]; df::unit* unit = world->units.all[i];
if( isFemale(unit) if(isFreeEgglayer(unit))
&& isTame(unit)
&& isOwnCiv(unit)
&& isEggLayer(unit)
&& !isAssigned(unit)
)
{ {
free_unit = unit; free_unit = unit;
break; break;
@ -824,6 +908,17 @@ df::unit * findFreeEgglayer()
return free_unit; 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 // 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 // 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_egglayer = false;
bool find_grazer = false; bool find_grazer = false;
bool find_milkable = false; bool find_milkable = false;
bool find_named = false;
bool find_agemin = false; bool find_agemin = false;
bool find_agemax = false; bool find_agemax = false;
@ -1111,7 +1207,6 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
bool zone_set = false; bool zone_set = false;
bool verbose = false; bool verbose = false;
bool all = false; bool all = false;
bool auto_nestbox = false;
bool unit_slaughter = false; bool unit_slaughter = false;
static int target_zone = -1; static int target_zone = -1;
@ -1231,10 +1326,10 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
out << "Filter by 'tame'." << endl; out << "Filter by 'tame'." << endl;
find_tame = true; find_tame = true;
} }
else if(p == "autonestbox") else if(p == "named")
{ {
out << "Auto-assign female tame owned egg-layers to free nestboxes." << endl; out << "Filter by 'has name or nickname'." << endl;
auto_nestbox = true; find_named = true;
} }
else if(p == "slaughter") 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) if((zone_info && !all) || zone_set)
need_cursor = true; need_cursor = true;
@ -1365,7 +1466,7 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
} }
// try to cope with user dumbness // 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; out << "Invalid values for minage/maxage specified! I'll swap them." << endl;
int oldmin = target_agemin; int oldmin = target_agemin;
@ -1373,41 +1474,6 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
target_agemax = oldmin; 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 // 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) // (doesn't use the findXyzAtCursor() methods because zones might overlap and contain a cage or chain)
if(zone_info) // || chain_info || cage_info) 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_milkable && !isMilkable(unit))
|| (find_male && !isMale(unit)) || (find_male && !isMale(unit))
|| (find_female && !isFemale(unit)) || (find_female && !isFemale(unit))
|| (find_named && !unit->name.has_name)
) )
continue; continue;
@ -1510,6 +1577,9 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
} }
else if(unit_slaughter) else if(unit_slaughter)
{ {
// don't slaughter named creatures unless told to do so
if(!find_named && unit->name.has_name)
continue;
doMarkForSlaughter(unit); doMarkForSlaughter(unit);
} }
@ -1540,6 +1610,13 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
} }
else if(unit_slaughter) 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); doMarkForSlaughter(unit);
return CR_OK; return CR_OK;
} }
@ -1571,3 +1648,139 @@ command_result df_zone (color_ostream &out, vector <string> & parameters)
return CR_OK; 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;
}