From 4b5836b99672eb4fc66d9adcb9a07c1fb2177977 Mon Sep 17 00:00:00 2001 From: Janeene Beeforth Date: Sat, 11 Feb 2023 23:04:50 +1100 Subject: [PATCH] Fix for #2871 * Add check for unit->name.nickname field is not an empty string * Refactor repeated checks for inappropriate and protected units --- docs/changelog.txt | 1 + plugins/autobutcher.cpp | 100 +++++++++++++++------------------------- 2 files changed, 37 insertions(+), 64 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index c667417b7..03ff8f5bc 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -37,6 +37,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Fixes -@ `nestboxes`: fixed bug causing nestboxes themselves to be forbidden, which prevented citizens from using them to lay eggs. Now only eggs are forbidden. +- `autobutcher`: implemented work-around for Dwarf Fortress not setting nicknames properly, so that nicknames created in the in-game interface are detected & protect animals from being butchered properly. Note that nicknames for unnamed units are not currently saved by dwarf fortress - use ``enable fix/protect-nicks`` to fix any nicknames created/removed within dwarf fortress so they can be saved/reloaded when you reload the game. ## Misc Improvements diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index 28667bbe7..bee3a4503 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -732,6 +732,30 @@ static bool isInBuiltCageRoom(df::unit *unit) { return false; } +// This can be used to identify completely inappropriate units (dead, undead, not belonging to the fort, ...) +// that autobutcher should be ignoring. +static bool isInappropriateUnit(df::unit *unit) { + return !Units::isActive(unit) + || Units::isUndead(unit) + || Units::isMerchant(unit) // ignore merchants' draft animals + || Units::isForest(unit) // ignore merchants' caged animals + || !Units::isOwnCiv(unit); +} + +// This can be used to identify protected units that should be counted towards fort totals, but not scheduled +// for butchering. This way they count towards target quota, so if you order that you want 1 female adult cat +// and have 2 cats, one of them being a pet, the other gets butchered +static bool isProtectedUnit(df::unit *unit) { + return Units::isWar(unit) // ignore war dogs etc + || Units::isHunter(unit) // ignore hunting dogs etc + // ignore creatures in built cages which are defined as rooms to leave zoos alone + // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) + || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() + || Units::isAvailableForAdoption(unit) + || unit->name.has_name + || !unit->name.nickname.empty(); +} + static void autobutcher_cycle(color_ostream &out) { // mark that we have recently run cycle_timestamp = world->frame_counter; @@ -757,14 +781,9 @@ static void autobutcher_cycle(color_ostream &out) { // then let autowatch add units to the watchlist which will probably start breeding (owned pets, war animals, ...) // then process units counting those which can't be butchered (war animals, named pets, ...) // so that they are treated as "own stock" as well and count towards the target quota - if ( !Units::isActive(unit) - || Units::isUndead(unit) + if (isInappropriateUnit(unit) || Units::isMarkedForSlaughter(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - || !Units::isTame(unit) - ) + || !Units::isTame(unit)) continue; // found a bugged unit which had invalid coordinates but was not in a cage. @@ -794,13 +813,7 @@ static void autobutcher_cycle(color_ostream &out) { // don't butcher protected units, but count them as stock as well // this way they count towards target quota, so if you order that you want 1 female adult cat // and have 2 cats, one of them being a pet, the other gets butchered - if( Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name) + if(isProtectedUnit(unit)) w->PushProtectedUnit(unit); else if ( Units::isGay(unit) || Units::isGelded(unit)) @@ -833,12 +846,7 @@ static WatchedRace * checkRaceStocksTotal(color_ostream &out, int race) { if (unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - ) + if (isInappropriateUnit(unit)) continue; if(!isContainedInItem(unit) && !hasValidMapPos(unit)) @@ -855,12 +863,7 @@ WatchedRace * checkRaceStocksProtected(color_ostream &out, int race) { if (unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - ) + if (isInappropriateUnit(unit)) continue; // found a bugged unit which had invalid coordinates but was not in a cage. @@ -868,14 +871,8 @@ WatchedRace * checkRaceStocksProtected(color_ostream &out, int race) { if (!isContainedInItem(unit) && !hasValidMapPos(unit)) continue; - if ( !Units::isTame(unit) - || Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name ) + if ( !Units::isTame(unit) + || isProtectedUnit(unit)) w->PushUnit(unit); } return w; @@ -887,19 +884,9 @@ WatchedRace * checkRaceStocksButcherable(color_ostream &out, int race) { if (unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) + if ( isInappropriateUnit(unit) || !Units::isTame(unit) - || Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name + || isProtectedUnit(unit) ) continue; @@ -917,12 +904,7 @@ WatchedRace * checkRaceStocksButcherFlag(color_ostream &out, int race) { if(unit->race != race) continue; - if ( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draft animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) - ) + if (isInappropriateUnit(unit)) continue; if (!isContainedInItem(unit) && !hasValidMapPos(unit)) @@ -1013,19 +995,9 @@ static void autobutcher_butcherRace(color_ostream &out, int id) { if(unit->race != id) continue; - if( !Units::isActive(unit) - || Units::isUndead(unit) - || Units::isMerchant(unit) // ignore merchants' draught animals - || Units::isForest(unit) // ignore merchants' caged animals - || !Units::isOwnCiv(unit) + if( isInappropriateUnit(unit) || !Units::isTame(unit) - || Units::isWar(unit) // ignore war dogs etc - || Units::isHunter(unit) // ignore hunting dogs etc - // ignore creatures in built cages which are defined as rooms to leave zoos alone - // (TODO: better solution would be to allow some kind of slaughter cages which you can place near the butcher) - || (isContainedInItem(unit) && isInBuiltCageRoom(unit)) // !!! see comments in isBuiltCageRoom() - || Units::isAvailableForAdoption(unit) - || unit->name.has_name + || isProtectedUnit(unit) ) continue;