diff --git a/docs/plugins/autobutcher.rst b/docs/plugins/autobutcher.rst index b6133fbf21..6f8d7b85f1 100644 --- a/docs/plugins/autobutcher.rst +++ b/docs/plugins/autobutcher.rst @@ -30,6 +30,21 @@ The default targets are: 2 male kids, 4 female kids, 2 male adults, and 4 female adults. Note that you may need to set a target above 1 to have a reliable breeding population due to asexuality etc. +Nestboxes module extends autobutcher functionality to monitoring and protection +of fertile eggs. It facilitates steady supplay of eggs protected for breading +purposes while maintaining egg based food production. With default settings it +will forbid fertile eggs in claimed nestboxes up to 4 + number of annimals +missing to autobutcher target. This will allow for larger number of hatchilings +in initial breadin program phase when increasing livestock. +When population reaches intended target only base amount of eggs will be left +for breading purpose. +It is possible to alter this behaviour and compleatly stop protection of eggs +when population target is reached. +In case of clutch larger than needed target eggs will be split in two separate +stacks and only one of them forbidden. +Check for forbidding eggs is made when fertile eggs are laid for one of +watched races. + Usage ----- @@ -78,6 +93,50 @@ Usage ``autobutcher list_export`` Print commands required to set the current settings in another fort. +``autobutcher nestboxes enable (autobutcher nb e)`` +``autobutcher nestboxes disable (autobutcher nb d)`` + + It is possible to enable/ disable autobutcher nestboxes module. + +``autobutcher nestboxes ignore (autobutcher nb i <1/0>)`` + + By default nestboxes module respects main plugin's enabled status, + autowatch and watched status for specific races. + It is possible to allow nestboxes module to ignore that and work + on it's own. In case like that missing animal count will not be added + to target of protected eggs. + +``autobutcher nestboxes target `` +``autobutcher nb t `` + + Nestboxes target command allows to change how script handles specific + animal race, DEFAULT settings for new races or ALL curently watched races + and default value for new ones. + parameter accepts "DEFAULT", "ALL", creature id (e.g. BIRD_TURKEY) + or race id (190 for Turkey) + base number of fertile eggs that will be protected by + frobidding them in nestboxes. Default 4. + true/false value, if set to true animal count missing to autobutcher + popualtion targets will be added to base target for protected eggs. + Default true. + if set to true module will stop protection of eggs for race as long + as population target is maintained. Default true. + If eggs laid by race should be monitored and protected. + Default true. + If parameter is not specified already existing value will be mantained. + If new race is added missing values will be taken from default settings. + +``autobutcher nestboxes split_stacks (autobutcher nb s <1/0>)`` + + split_stacks command allows to specify how egg stacks that are only + partialy over target should be handled. If set to false whole stack will + be forbidden. If set to true only eggs below target will be forbidden, + remaining part of stack will be separated and left for dwarves to collect. + +``autobutcher nestboxes clear (autobutcher nb clear)`` + + Remove all settings for module and restore them to initial default values. + To see a list of all races, run this command:: devel/query --table df.global.world.raws.creatures.all --search ^creature_id --maxdepth 1 @@ -116,3 +175,27 @@ fortress:: autobutcher target 2 2 4 2 ALPACA SHEEP LLAMA autobutcher target 5 5 6 2 PIG autobutcher target 0 0 0 0 new + + + autobutcher nb t BEAK_DOG 10 1 1 1 + autobutcher nestboxes target BEAK_DOG 5 true true true + +Command sets base target for beak dog eggs to 5, animals missing to population tresholds will be added to base target. +Once autobutcher population target is reached no new eggs will be forbidden as long as population is at or above target. + + autobutcher nb t DEFAULT 4 1 0 0 + autobutcher nestboxes target DEFAULT 4 true true false + +Command will change default settings for watching new races disabling it. + + autobutcher nb t ALL 15 0 1 1 + autobutcher nestboxes target ALL 15 false true true + +Command will change setting for all currently watched egg races as well as default ones. +Target for protected eggs is set to 15, missing animals count to livestock targets is not taken into account. +Once population target is reached eggs will no longer be protected. All current and new races will be watched. + + autobutcher nestboxes split_stacks false + autobutcher nb s 0 + +Disable spliting of egg stacks. diff --git a/plugins/autobutcher.cpp b/plugins/autobutcher.cpp index 16c2131dd0..3b99650278 100644 --- a/plugins/autobutcher.cpp +++ b/plugins/autobutcher.cpp @@ -983,7 +983,7 @@ static int autobutcher_getInfoForNestboxes(lua_State* L) { int raceId = lua_tointeger(L, 1); lua_newtable(L); int ctable = lua_gettop(L); - Lua::SetField(L, config.get_bool(CONFIG_IS_ENABLED), ctable, "autobutcher_enabled"); + Lua::SetField(L, config.get_bool(CONFIG_IS_ENABLED), ctable, "enabled"); if (!out) out = &Core::getInstance().getConsole(); diff --git a/plugins/lua/autobutcher/nestboxes.lua b/plugins/lua/autobutcher/nestboxes.lua index 6d5ba14868..5814b85cb8 100644 --- a/plugins/lua/autobutcher/nestboxes.lua +++ b/plugins/lua/autobutcher/nestboxes.lua @@ -28,7 +28,7 @@ local default_table = { watched = true, -- monitor eggs for race target = 4, --basic target for protected(forbidden) eggs ama = true, --Add Missing Animals to basic target for prottected eggs, difference between current amount of animals and target set for autobutcher, speeds up population growth in initial phase whiile limitting population explosion near end if egg target has low value - full_stop = false -- stop protecting eggs once autobutcher target for live animals is reached + stop = true -- stop protecting eggs once autobutcher target for live animals is reached } --------------------------------------------------------------------------------------------------- local function getDefaultState() @@ -140,14 +140,14 @@ local function doDisable() printDetails(('end doDisable')) end --doDisable --------------------------------------------------------------------------------------------------- -local function getConfigForRace(race) +local function getConfigForRace(race , autobutcher_watched) printDetails(('start getConfigForRace')) printDetails(('getting config for race %s '):format(race)) - if state.config_per_race[race] == nil then + if state.config_per_race[race] == nil and autobutcher_watched and state.default.watched then state.config_per_race[race] = state.default persistState() end - printDetails(('end 2 getConfigForRace')) + printDetails(('end getConfigForRace')) return state.config_per_race[race] end --getConfigForRace --------------------------------------------------------------------------------------------------- @@ -169,7 +169,7 @@ local function validateCreatureOrRace(value) handleError(('could not find %s'):format(value)) end --validateCreatureOrRace --------------------------------------------------------------------------------------------------- -local function setTarget(target_race, target_count, add_missing_animals, full_stop, watch_race) +local function setTarget(target_race, target_count, add_missing_animals, stop, watch_race) printDetails(('start setTarget')) if type(target_race) == 'string' then @@ -184,7 +184,7 @@ local function setTarget(target_race, target_count, add_missing_animals, full_st watched = getBoolean(watch_race), target = tonumber(target_count), ama = getBoolean(add_missing_animals), - full_stop = getBoolean(full_stop) + stop = getBoolean(stop) } local race = validateCreatureOrRace(target_race) @@ -193,6 +193,7 @@ local function setTarget(target_race, target_count, add_missing_animals, full_st elseif race == 'ALL' then for _, v in pairs(state.config_per_race) do utils.assign(v, new_config) + utils.assign(state.default, new_config) end elseif race >= 0 then utils.assign(state.config_per_race[race], new_config) @@ -211,26 +212,30 @@ local function clearConfig() updateEventListener() end --------------------------------------------------------------------------------------------------- +local function setIgnore(value) + state.ignore_autobutcher = getBoolean(value) +end +--------------------------------------------------------------------------------------------------- local function setVerbose(value) state.verbose = getBoolean(value) nestboxesCommon.verbose = state.verbose end --------------------------------------------------------------------------------------------------- local function format_target_count_row(category, row) - return (('%s: watched %s; target %s; add missing animals %s; full stop once animal target reached %s'):format( + return (('%s: watched: %s; target: %s; ama: %s; stop: %s'):format( category, row.watched and 'enabled' or 'disabled', tostring(row.target), row.ama and 'enabled' or 'disabled', - row.full_stop and 'enabled' or 'disabled' + row.stop and 'enabled' or 'disabled' )) end --------------------------------------------------------------------------------------------------- local function printStatus() - printLocal(('Script is currently %s.'):format(state.enabled and 'enabled' or 'disabled')) - printLocal(('Egg stack splitting is %s'):format(state.split_stacks and 'enabled' or 'disabled')) + printLocal(('Status %s.'):format(state.enabled and 'enabled' or 'disabled')) + printLocal(('Stack splitting: %s'):format(state.split_stacks and 'enabled' or 'disabled')) printLocal( - ('Script is %s autobutcher\'s enabled status'):format(state.ignore_autobutcher and 'ignoring' or 'respecting') + ('%s autobutcher\'s enabled status'):format(state.ignore_autobutcher and 'Ignoring' or 'Tespecting') ) printDetails(('verbose mode is %s'):format(state.verbose and 'enabled' or 'disabled')) printDetails( @@ -242,7 +247,7 @@ local function printStatus() printLocal(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) end end - printDetails(dumpToString(state)) + --printDetails(dumpToString(state)) end --------------------------------------------------------------------------------------------------- local function handleOnStateChange(sc) @@ -260,7 +265,7 @@ end --handleOnStateChange --------------------------------------------------------------------------------------------------- local function getInfoFromAutobutcher(race) local v_return = { - autobutcher_enabled = false, + enabled = false, watched = false, mac = 0 } @@ -279,29 +284,6 @@ local function getInfoFromAutobutcher(race) return v_return end --getInfoFromAutobutcher --------------------------------------------------------------------------------------------------- -local function validateEggs(eggs) - if not eggs.egg_flags.fertile then - printDetails('Newly laid eggs are not fertile, do nothing') - return false - end - - local should_be_nestbox = dfhack.items.getHolderBuilding(eggs) - if should_be_nestbox ~= nil then - for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do - if nestbox == should_be_nestbox then - printDetails('Found nestbox, continue with egg handling') - return true - end - end - printDetails('Newly laid eggs are in building different than nestbox, we were to late') - return false - else - printDetails('Newly laid eggs are not in building, we were to late') - return false - end - return true -end --validateEggs ---------------------------------------------------------------------------------------------------- -- --------------------------------------------------------------------------------------------------- -- checkItemCreated function, called from eventfful on ITEM_CREATED event @@ -310,16 +292,21 @@ function checkItemCreated(item_id) if item == nil or df.item_type.EGG ~= item:getType() then return else - if validateEggs(item) then + if nestboxesEvent.validateEggs(item) then local autobutcher_info = getInfoFromAutobutcher(item.race) - local race_config = getConfigForRace(item.race) + + local race_config = getConfigForRace(item.race, (autobutcher_info.watched and autobutcher_info.enabled) or state.ignore_autobutcher) + if race_config == nil then -- if nil new race without config and no new races are being watched + return + end if - (((autobutcher_info.watched and autobutcher_info.autobutcher_enabled and not state.ignore_autobutcher -- check eggs respecting autobutcher settings - and not (race_config.full_stop and autobutcher_info.mac == 0)) -- do not check eggs if reached animal targets for race and full stop enabled + (((autobutcher_info.watched and autobutcher_info.enabled and not state.ignore_autobutcher -- check eggs respecting autobutcher settings + and not (race_config.stop and autobutcher_info.mac == 0)) -- do not check eggs if reached animal targets for race and full stop enabled or state.ignore_autobutcher) -- or check eggs ignoring autobutcher settings - and race_config.watched)-- check eggs only when nestboxes is watching race + and race_config.watched -- check eggs only when nestboxes is watching race + ) then - nestboxesEvent.handleEggs(item, race_config, state.split_stacks, autobutcher_info.mac) + nestboxesEvent.handleEggs(item, race_config.target, race_config.ama, state.split_stacks, autobutcher_info.mac) end end end @@ -345,6 +332,8 @@ function handleCommand(positionals, opts) updateEventListener() elseif command == 'TARGET' or command == 'T' then setTarget(positionals[3], positionals[4], positionals[5], positionals[6], positionals[7]) + elseif command == 'IGNORE' or command == 'I' then + setIgnore(positionals[3]) elseif command == 'VERBOSE' or command == 'V' then setVerbose(positionals[3]) elseif command == 'SPLIT_STACKS' or command == 'S' then diff --git a/plugins/lua/autobutcher/nestboxesEvent.lua b/plugins/lua/autobutcher/nestboxesEvent.lua index d5cf94fba9..ca353a801c 100644 --- a/plugins/lua/autobutcher/nestboxesEvent.lua +++ b/plugins/lua/autobutcher/nestboxesEvent.lua @@ -3,38 +3,39 @@ local _ENV = mkmodule('plugins.autobutcher.nestboxesEvent') local nestboxesCommon = require('plugins.autobutcher.common') local printLocal = nestboxesCommon.printLocal local printDetails = nestboxesCommon.printDetails -local dumpToString = nestboxesCommon.dumpToString local utils = require('utils') --------------------------------------------------------------------------------------------------- ---ITEM_CREATED event handling functions -local function copyEeggFields(source_egg, target_egg) - printDetails('start copyEeggFields') +local function copyEggFields(source_egg, target_egg) + printDetails('start copyEggFields') target_egg.incubation_counter = source_egg.incubation_counter - printDetails('incubation_counter done') - target_egg.egg_flags = utils.clone(source_egg.egg_flags, true) - target_egg.hatchling_flags1 = utils.clone(source_egg.hatchling_flags1, true) - target_egg.hatchling_flags2 = utils.clone(source_egg.hatchling_flags2, true) - target_egg.hatchling_flags3 = utils.clone(source_egg.hatchling_flags3, true) - target_egg.hatchling_flags4 = utils.clone(source_egg.hatchling_flags4, true) - printDetails('flags done') - target_egg.hatchling_training_level = utils.clone(source_egg.hatchling_training_level, true) - utils.assign(target_egg.hatchling_animal_population, source_egg.hatchling_animal_population) - printDetails('hatchling_animal_population done') + target_egg.egg_flags = utils.clone(source_egg.egg_flags) + target_egg.hatchling_flags1 = utils.clone(source_egg.hatchling_flags1) + target_egg.hatchling_flags2 = utils.clone(source_egg.hatchling_flags2) + target_egg.hatchling_flags3 = utils.clone(source_egg.hatchling_flags3) + target_egg.hatchling_flags4 = utils.clone(source_egg.hatchling_flags4) + target_egg.hatchling_training_level = source_egg.hatchling_training_level target_egg.hatchling_mother_id = source_egg.hatchling_mother_id - printDetails('hatchling_mother_id done') target_egg.mother_hf = source_egg.mother_hf target_egg.father_hf = source_egg.mother_hf - printDetails('mother_hf father_hf done') target_egg.mothers_caste = source_egg.mothers_caste target_egg.fathers_caste = source_egg.fathers_caste - printDetails('mothers_caste fathers_caste done') - target_egg.mothers_genes = source_egg.mothers_genes - target_egg.fathers_genes = source_egg.fathers_genes - printDetails('mothers_genes fathers_genes done') + + local mothers_genes = df.unit_genes:new() + mothers_genes.appearance:assign(source_egg.mothers_genes.appearance) + mothers_genes.colors:assign(source_egg.mothers_genes.colors) + + local fathers_genes = df.unit_genes:new() + fathers_genes.appearance:assign(source_egg.fathers_genes.appearance) + fathers_genes.colors:assign(source_egg.fathers_genes.colors) + + target_egg.mothers_genes = mothers_genes + target_egg.fathers_genes = fathers_genes + printDetails("mothers_genes fathers_genes done") + target_egg.hatchling_civ_id = source_egg.hatchling_civ_id printDetails('hatchling_civ_id done') - printDetails('end copyEeggFields') -end --copyEeggFields + printDetails('end copyEggFields') +end --copyEggFields --------------------------------------------------------------------------------------------------- local function resizeEggStack(egg_stack, new_stack_size) printDetails('start resizeEggStack') @@ -63,7 +64,7 @@ local function createNewEggStack(original_eggs, new_stack_count) local created_egg_stack = created_items[0] or created_items[1] printDetails(df.creature_raw.find(created_egg_stack.race).creature_id) printDetails('about to copy fields from orginal eggs') - copyEeggFields(original_eggs, created_egg_stack) + copyEggFields(original_eggs, created_egg_stack) printDetails('about to resize new egg stack') resizeEggStack(created_egg_stack, new_stack_count) @@ -122,20 +123,43 @@ local function countForbiddenEggsForRaceInClaimedNestobxes(race) return eggs_count end --countForbiddenEggsForRaceInClaimedNestobxes --------------------------------------------------------------------------------------------------- -function handleEggs(eggs, race_config, split_stacks, missing_animals_count) +function validateEggs(eggs) + if not eggs.egg_flags.fertile then + printDetails('Newly laid eggs are not fertile, do nothing') + return false + end + + local should_be_nestbox = dfhack.items.getHolderBuilding(eggs) + if should_be_nestbox ~= nil then + for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do + if nestbox == should_be_nestbox then + printDetails('Found nestbox, continue with egg handling') + return true + end + end + printDetails('Newly laid eggs are in building different than nestbox, we were to late') + return false + else + printDetails('Newly laid eggs are not in building, we were to late') + return false + end + return true +end --validateEggs +--------------------------------------------------------------------------------------------------- +function handleEggs(eggs, base_target, add_missing_animals, split_stacks, missing_animals_count) printDetails(('start handleEggs')) local race = eggs.race printDetails(('Handling eggs for race %s'):format(race)) - printDetails('race_config: ' .. dumpToString(race_config)) + printDetails(('base_target: %s'):format(base_target)) + printDetails(('add_missing_animals: %s'):format(race)) printDetails(('split_stacks: %s'):format(tostring(split_stacks))) printDetails(('missing_animals_count: %s'):format(tostring(missing_animals_count))) - local target_eggs = race_config.target - local add_missing_animals = race_config.ama + local target_eggs = base_target local creature = df.creature_raw.find(eggs.race).creature_id if add_missing_animals then - printDetails(('adding missing animal count %s'):format(missing_animals_count)) + printDetails(('adding missing animal count %s to target'):format(missing_animals_count)) target_eggs = target_eggs + missing_animals_count end @@ -143,12 +167,21 @@ function handleEggs(eggs, race_config, split_stacks, missing_animals_count) local total_count = current_eggs total_count = total_count + countForbiddenEggsForRaceInClaimedNestobxes(race) + printLocal(("total_count %s"):format(total_count)); + printLocal(("current_eggs %s"):format(current_eggs)); + printLocal(("target_eggs %s"):format(target_eggs)); + + printLocal(('Total count for %s egg(s) is %s'):format(creature, total_count)) - printDetails(('Total count for %s egg(s) is %s'):format(creature, total_count)) if total_count - current_eggs < target_eggs then local egg_count_to_leave_in_source_stack = current_eggs + if split_stacks and total_count > target_eggs then + printLocal(("1 total_count %s"):format(total_count)); + printLocal(("1 current_eggs %s"):format(current_eggs)); + printLocal(("1 target_eggs %s"):format(target_eggs)); egg_count_to_leave_in_source_stack = target_eggs - total_count + current_eggs + printLocal(("egg_count_to_leave_in_source_stack %s"):format(egg_count_to_leave_in_source_stack)); splitEggStack(eggs, egg_count_to_leave_in_source_stack) end @@ -162,12 +195,13 @@ function handleEggs(eggs, race_config, split_stacks, missing_animals_count) eggs.flags.in_job = false end end + printLocal( ('Previously existing %s egg(s) %s is lower than maximum %s (%s base target + %s missing animals), forbidden %s egg(s) out of %s new'):format( creature, total_count - current_eggs, target_eggs, - race_config.target, + base_target, missing_animals_count, egg_count_to_leave_in_source_stack, current_eggs @@ -179,7 +213,7 @@ function handleEggs(eggs, race_config, split_stacks, missing_animals_count) creature, total_count, target_eggs, - race_config.target, + base_target, missing_animals_count, current_eggs )