diff options
| author | Robert Heinrich | 2012-04-05 20:32:44 +0200 |
|---|---|---|
| committer | Robert Heinrich | 2012-04-05 20:32:44 +0200 |
| commit | 577e333ac9e277da88ca28b8cdecdfeeacd77978 (patch) | |
| tree | b872db5d27cbf537aafbf31ef993077d78d4e8eb /plugins/zone.cpp | |
| parent | 511fceff0a8ab739dd04cb80527112cdeafc12b2 (diff) | |
| download | dfhack-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.cpp | 421 |
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 |
