From 8108eaca35c857c0b972a6fbcb9a36877a70796a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sat, 7 Sep 2024 21:32:50 +0200 Subject: [PATCH 01/22] add eggwatch first version --- eggwatch.lua | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 eggwatch.lua diff --git a/eggwatch.lua b/eggwatch.lua new file mode 100644 index 000000000..4a4decc3c --- /dev/null +++ b/eggwatch.lua @@ -0,0 +1,299 @@ +local argparse = require("argparse") +local eventful = require("plugins.eventful") + +local GLOBAL_KEY = "eggwatch" +local EVENT_FREQ = 5 +local print_prefix = "eggwatch: " + +enabled = enabled or false +default_table = {} +default_table.DEFAULT = 10 +function isEnabled() + return enabled +end + +local function persist_state() + dfhack.persistent.saveSiteData( + GLOBAL_KEY, + { + enabled = enabled, + verbose = verbose, + target_eggs_count_per_race = target_eggs_count_per_race + } + ) +end + +--- Load the saved state of the script +local function load_state() + -- load persistent data + local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) + enabled = persisted_data.enabled or false + verbose = persisted_data.verbose or false + target_eggs_count_per_race = persisted_data.target_eggs_count_per_race or default_table +end + +if dfhack_flags.module then + return +end +local function print_local(text) + print(print_prefix .. text) +end +local function handle_error(text) + qerror(text) +end +local function print_status() + print_local(("eggwatch is currently %s."):format(enabled and "enabled" or "disabled")) + if verbose then + print_local("eggwatch is in verbose mode") + end +end + +local function print_detalis(details) + if verbose then + print_local(details) + end +end + +local function is_egg(item) + return df.item_type.EGG == item:getType() +end + +-- local function find_current_nestbox (current_eggs) +-- for _, nestbox in ipairs (df.global.world.buildings.other.NEST_BOX) do +-- if nestbox.pos == current_eggs.pos then +-- return nestbox +-- end +-- end +-- end + +-- local function create_new_egg_stack (original_eggs, remaining_eggs, creature, caste) + +-- print('about to split create new egg stack') +-- print(('type= %s'):format(original_eggs:getType())) +-- print(('creature= %s'):format( creature.creature_id)) +-- print(('caste= %s '):format(caste.caste_id)) + +-- --local created_items = dfhack.items.createItem(creator, original_eggs:getType(), -1, creature.creature_id, caste.caste_id) + +-- print('created item') +-- local created_egg_stack = created_items[1] +-- print('about to copy fields from orginal eggs') +-- created_egg_stack.incumabtion_counter = original_eggs.incumabtion_counter +-- created_egg_stack.flags = original_eggs.flags +-- created_egg_stack.flags2 = original_eggs.flags2 +-- created_egg_stack.egg_flags = original_eggs.egg_flags +-- created_egg_stack.pos = original_eggs.pos +-- created_egg_stack.hatchling_civ_id = original_eggs.hatchling_civ_id +-- created_egg_stack.mothers_genes = original_eggs.mothers_genes +-- created_egg_stack.mothers_caste = original_eggs.mothers_caste +-- created_egg_stack.mother_hf = original_eggs.mother_hf +-- created_egg_stack.fathers_genes = original_eggs.fathers_genes +-- created_egg_stack.fathers_caste = original_eggs.fathers_caste +-- created_egg_stack.father_hf = original_eggs.father_hf +-- created_egg_stack.hatchling_flags1 = original_eggs.hatchling_flags1 +-- created_egg_stack.hatchling_flags2 = original_eggs.hatchling_flags2 +-- created_egg_stack.hatchling_flags3 = original_eggs.hatchling_flags3 +-- created_egg_stack.hatchling_flags4 = original_eggs.hatchling_flags4 +-- created_egg_stack.hatchling_training_level = original_eggs.hatchling_training_level +-- created_egg_stack.hatchling_animal_population = original_eggs.hatchling_animal_population +-- created_egg_stack.mother_id = original_eggs.mother_id + +-- print('about to move new stack to nestbox') +-- dfhack.items.moveToContainer(created_egg_stack, find_current_nestbox(original_eggs)) +-- end + +local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) + print_detalis(("start count_forbidden_eggs_for_race_in_claimed_nestobxes")) + local eggs_count = 0 + for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do + if nestbox.claimed_by ~= -1 then + print_detalis(("Found claimed nextbox")) + for _, nestbox_contained_item in ipairs(nestbox.contained_items) do + if nestbox_contained_item.use_mode == df.building_item_role_type.TEMP then + print_detalis(("Found claimed nextbox containing items")) + if df.item_type.EGG == nestbox_contained_item.item:getType() then + print_detalis(("Found claimed nextbox containing items that are eggs")) + if nestbox_contained_item.item.egg_flags.fertile and nestbox_contained_item.item.flags.forbid then + print_detalis(("Eggs are fertile and forbidden")) + if df.creature_raw.find(nestbox_contained_item.item.race).creature_id == race_creature_id then + print_detalis(("Eggs belong to %s"):format(race_creature_id)) + print_detalis( + ("eggs_count %s + new %s"):format( + eggs_count, + nestbox_contained_item.item.stack_size + ) + ) + eggs_count = eggs_count + nestbox_contained_item.item.stack_size + print_detalis(("eggs_count after adding current nestbox %s "):format(eggs_count)) + end + end + end + end + end + end + end + print_detalis(("end count_forbidden_eggs_for_race_in_claimed_nestobxes")) + return eggs_count +end +local function get_max_eggs_for_race(race_creature_id) + for k, v in pairs(target_eggs_count_per_race) do + if k == race_creature_id then + return v + end + end + target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT + persist_state() + return target_eggs_count_per_race[race_creature_id] +end +local function handle_eggs(eggs) + print_detalis(("start handle_eggs")) + if not eggs.egg_flags.fertile then + print_local("Newly laid eggs are not fertile, do nothing") + return + end + + local race_creature_id = df.creature_raw.find(eggs.race).creature_id + local max_eggs = get_max_eggs_for_race(race_creature_id) + local current_eggs = eggs.stack_size + + local total_count = current_eggs + total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) + + print_detalis(("Total count for %s eggs is %s"):format(race_creature_id, total_count)) + + if total_count - current_eggs < max_eggs then + -- ###if possible split egg stack to forbid only part below max change previous condition to total_count < max_eggs + -- elseif total_count - current_eggs < max_eggs and total_count > max_eggs then + -- local forbid_eggs = max_eggs - total_count + current_eggs + -- local remaining_eggs = current_eggs - forbid_eggs + -- print('about to split eggs stack') + -- create_new_egg_stack(eggs, remaining_eggs, df.creature_raw.find(eggs.race), race_creature.caste[eggs.caste]) + -- eggs.stack_size = forbid_eggs + -- eggs.flags.forbid = true + -- print(('Total count for %s eggs is %s over maximum %s , forbidden %s eggs out of clutch of %s.'):format(race_creature_id, total_count, max_eggs, forbid_eggs, current_eggs)) + eggs.flags.forbid = true + print_local( + ("Previously existing %s eggs is %s lower than maximum %s , forbidden %s new eggs."):format( + race_creature_id, + total_count - current_eggs, + max_eggs, + current_eggs + ) + ) + else + print_local( + ("Total count for %s eggs is %s over maximum %s, newly laid eggs %s , no action taken."):format( + race_creature_id, + total_count, + max_eggs, + current_eggs + ) + ) + end + + print_detalis(("end handle_eggs")) +end + +local function check_item_created(item_id) + local item = df.item.find(item_id) + if not item or not is_egg(item) then + return + end + handle_eggs(item) +end +local function do_enable() + enabled = true + eventful.enableEvent(eventful.eventType.ITEM_CREATED, EVENT_FREQ) + eventful.onItemCreated[GLOBAL_KEY] = check_item_created +end + +local function do_disable() + enabled = false + eventful.onItemCreated[GLOBAL_KEY] = nil +end + +local function validate_creature_id(creature_id) + for i, c in ipairs(df.global.world.raws.creatures.all) do + if c.creature_id == creature_id then + return true + end + end + return false +end + +local function set_target(target_race, target_count) + if target_race == nil or target_race == "" then + handle_error('must specify "DEFAULT" or valid creature_id') + end + local target_race_upper = string.upper(target_race) + if tonumber(target_count) == nil or tonumber(target_count) < 0 then + handle_error("No valid target count specified") + end + if target_race_upper == "DEFAULT" or validate_creature_id(target_race_upper) then + target_eggs_count_per_race[target_race_upper] = tonumber(target_count) + else + handle_error('must specify "DEFAULT" or valid creature_id') + end + + print_local(dump(target_eggs_count_per_race)) +end +function dump(o) + if type(o) == "table" then + local s = "{ " + for k, v in pairs(o) do + if type(k) ~= "number" then + k = '"' .. k .. '"' + end + s = s .. "[" .. k .. "] = " .. dump(v) .. "," + end + return s .. "} " + else + return tostring(o) + end +end + +if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then + dfhack.printerr("eggwatch needs a loaded fortress to work") + return +end + +local args, opts = {...}, {} +if dfhack_flags and dfhack_flags.enable then + args = {dfhack_flags.enable_state and "enable" or "disable"} +end + +local positionals = + argparse.processArgsGetopt( + args, + { + {"h", "help", handler = function() + opts.help = true + end} + } +) + +load_state() +local command = positionals[1] + +if command == "help" or opts.help then + print(dfhack.script_help()) +elseif command == "enable" then + do_enable() + print_status() +elseif command == "disable" then + do_disable() + print_status() +elseif command == "target" then + set_target(positionals[2], positionals[3]) + print_status() +elseif command == "verbose" then + verbose = not verbose + print_status() +elseif command == 'clear' then +target_eggs_count_per_race = default_table + +elseif not command or command == "status" then + print_status() + print_local(dump(target_eggs_count_per_race)) +end +persist_state() From 30ac9bce4d2cbef1ca87e4cc0c7dd17e5aebe96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sat, 7 Sep 2024 21:36:57 +0200 Subject: [PATCH 02/22] Add possibility to include children and adult animals in egg count --- eggwatch.lua | 78 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index 4a4decc3c..e479692c1 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -7,7 +7,7 @@ local print_prefix = "eggwatch: " enabled = enabled or false default_table = {} -default_table.DEFAULT = 10 +default_table.DEFAULT = {10, false, false} function isEnabled() return enabled end @@ -35,12 +35,15 @@ end if dfhack_flags.module then return end + local function print_local(text) print(print_prefix .. text) end + local function handle_error(text) qerror(text) end + local function print_status() print_local(("eggwatch is currently %s."):format(enabled and "enabled" or "disabled")) if verbose then @@ -135,16 +138,71 @@ local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_ print_detalis(("end count_forbidden_eggs_for_race_in_claimed_nestobxes")) return eggs_count end + local function get_max_eggs_for_race(race_creature_id) for k, v in pairs(target_eggs_count_per_race) do if k == race_creature_id then - return v + return v[1] end end target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT persist_state() - return target_eggs_count_per_race[race_creature_id] + return target_eggs_count_per_race[race_creature_id][1] end + +local function count_children_for_race(race_creature_id) + for k, v in pairs(target_eggs_count_per_race) do + if k == race_creature_id then + return v[2] + end + end + target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT + return target_eggs_count_per_race[race_creature_id][2] +end + +local function count_adults_for_race(race_creature_id) + for k, v in pairs(target_eggs_count_per_race) do + if k == race_creature_id then + return v[3] + end + end + target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT + return target_eggs_count_per_race[race_creature_id][3] +end + +local function is_valid_animal(unit) + return unit and + dfhack.units.isActive(unit) and + dfhack.units.isAnimal(unit) and + dfhack.units.isFortControlled(unit) and + dfhack.units.isTame(unit) and + not dfhack.units.isDead(unit) +end +local function count_live_animals(race_creature_id) + local count_adults = count_adults_for_race(race_creature_id) + if count_adults then print_detalis(('we are counting adults for %s'):format(race_creature_id)) end + local count_children = count_children_for_race(race_creature_id) + if count_children then print_detalis(('we are counting children and babies for %s'):format(race_creature_id)) end + + local count = 0 + if not count_adults and not count_children then + return count + end +--dfhack.units.isAdult(unit) + for _,unit in ipairs(df.global.world.units.active) do + if race_creature_id == df.creature_raw.find(unit.race).creature_id + and is_valid_animal(unit) + and ( (count_adults and dfhack.units.isAdult(unit)) + or (count_children and ( dfhack.units.isChild(unit) or dfhack.units.isBaby(unit))) + ) then + count = count + 1 + end + end + print_detalis(('found %s life animals'):format(count)) + return count +end + + local function handle_eggs(eggs) print_detalis(("start handle_eggs")) if not eggs.egg_flags.fertile then @@ -157,7 +215,7 @@ local function handle_eggs(eggs) local current_eggs = eggs.stack_size local total_count = current_eggs - total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) + total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) + count_live_animals(race_creature_id) print_detalis(("Total count for %s eggs is %s"):format(race_creature_id, total_count)) @@ -201,6 +259,7 @@ local function check_item_created(item_id) end handle_eggs(item) end + local function do_enable() enabled = true eventful.enableEvent(eventful.eventType.ITEM_CREATED, EVENT_FREQ) @@ -221,7 +280,9 @@ local function validate_creature_id(creature_id) return false end -local function set_target(target_race, target_count) +local function set_target(target_race, target_count, count_children, count_adult) +local stringtoboolean={ ["true"]=true, ["false"]=false, ["1"] = true , ["0"] = false , ["Y"] = true , ["N"] = false} + if target_race == nil or target_race == "" then handle_error('must specify "DEFAULT" or valid creature_id') end @@ -230,13 +291,14 @@ local function set_target(target_race, target_count) handle_error("No valid target count specified") end if target_race_upper == "DEFAULT" or validate_creature_id(target_race_upper) then - target_eggs_count_per_race[target_race_upper] = tonumber(target_count) + target_eggs_count_per_race[target_race_upper] = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} else handle_error('must specify "DEFAULT" or valid creature_id') end print_local(dump(target_eggs_count_per_race)) end + function dump(o) if type(o) == "table" then local s = "{ " @@ -284,7 +346,7 @@ elseif command == "disable" then do_disable() print_status() elseif command == "target" then - set_target(positionals[2], positionals[3]) + set_target(positionals[2], positionals[3], positionals[4], positionals[5]) print_status() elseif command == "verbose" then verbose = not verbose @@ -295,5 +357,7 @@ target_eggs_count_per_race = default_table elseif not command or command == "status" then print_status() print_local(dump(target_eggs_count_per_race)) +else + handle_error(('Command "%s" is not recognized'):format(command)) end persist_state() From be7db5989ef9d471c9b9f3a631996d901e9f6ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sat, 7 Sep 2024 21:59:56 +0200 Subject: [PATCH 03/22] removed trailing whitespaces --- eggwatch.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index e479692c1..abdb116c0 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -180,10 +180,10 @@ local function is_valid_animal(unit) end local function count_live_animals(race_creature_id) local count_adults = count_adults_for_race(race_creature_id) - if count_adults then print_detalis(('we are counting adults for %s'):format(race_creature_id)) end + if count_adults then print_detalis(('we are counting adults for %s'):format(race_creature_id)) end local count_children = count_children_for_race(race_creature_id) - if count_children then print_detalis(('we are counting children and babies for %s'):format(race_creature_id)) end - + if count_children then print_detalis(('we are counting children and babies for %s'):format(race_creature_id)) end + local count = 0 if not count_adults and not count_children then return count @@ -192,7 +192,7 @@ local function count_live_animals(race_creature_id) for _,unit in ipairs(df.global.world.units.active) do if race_creature_id == df.creature_raw.find(unit.race).creature_id and is_valid_animal(unit) - and ( (count_adults and dfhack.units.isAdult(unit)) + and ( (count_adults and dfhack.units.isAdult(unit)) or (count_children and ( dfhack.units.isChild(unit) or dfhack.units.isBaby(unit))) ) then count = count + 1 @@ -200,7 +200,7 @@ local function count_live_animals(race_creature_id) end print_detalis(('found %s life animals'):format(count)) return count -end +end local function handle_eggs(eggs) @@ -248,7 +248,7 @@ local function handle_eggs(eggs) ) ) end - + print_detalis(("end handle_eggs")) end @@ -357,7 +357,7 @@ target_eggs_count_per_race = default_table elseif not command or command == "status" then print_status() print_local(dump(target_eggs_count_per_race)) -else +else handle_error(('Command "%s" is not recognized'):format(command)) end persist_state() From ad5268e0c7827916d63e17bf4178016f5550983d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sat, 7 Sep 2024 23:26:24 +0200 Subject: [PATCH 04/22] do first limit check after counting just eggs in case there is no need to count live animals, it should improve performance in one edge case --- eggwatch.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/eggwatch.lua b/eggwatch.lua index abdb116c0..b646c1d61 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -215,7 +215,15 @@ local function handle_eggs(eggs) local current_eggs = eggs.stack_size local total_count = current_eggs - total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) + count_live_animals(race_creature_id) + total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) + + if total_count - current_eggs < max_eggs then + print_detalis(("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format(race_creature_id, total_count - current_eggs)) + total_count = total_count + count_live_animals(race_creature_id) + else + print_detalis(("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format(race_creature_id, total_count, max_eggs)) + return + end print_detalis(("Total count for %s eggs is %s"):format(race_creature_id, total_count)) From 4ec8979e256439b69bf391fe4c4cc6d761ff6c15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sun, 8 Sep 2024 09:08:44 +0200 Subject: [PATCH 05/22] consolidate functions getting config for race --- eggwatch.lua | 47 +++++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index b646c1d61..8554d6b43 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -139,35 +139,16 @@ local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_ return eggs_count end -local function get_max_eggs_for_race(race_creature_id) +local function get_config_for_race(race_creature_id) + print_detalis(("getting config for race %s "):format(race_creature_id)) for k, v in pairs(target_eggs_count_per_race) do if k == race_creature_id then - return v[1] + return v end end target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT persist_state() - return target_eggs_count_per_race[race_creature_id][1] -end - -local function count_children_for_race(race_creature_id) - for k, v in pairs(target_eggs_count_per_race) do - if k == race_creature_id then - return v[2] - end - end - target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT - return target_eggs_count_per_race[race_creature_id][2] -end - -local function count_adults_for_race(race_creature_id) - for k, v in pairs(target_eggs_count_per_race) do - if k == race_creature_id then - return v[3] - end - end - target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT - return target_eggs_count_per_race[race_creature_id][3] + return target_eggs_count_per_race[race_creature_id] end local function is_valid_animal(unit) @@ -178,17 +159,16 @@ local function is_valid_animal(unit) dfhack.units.isTame(unit) and not dfhack.units.isDead(unit) end -local function count_live_animals(race_creature_id) - local count_adults = count_adults_for_race(race_creature_id) + +local function count_live_animals(race_creature_id, count_children, count_adults) if count_adults then print_detalis(('we are counting adults for %s'):format(race_creature_id)) end - local count_children = count_children_for_race(race_creature_id) if count_children then print_detalis(('we are counting children and babies for %s'):format(race_creature_id)) end local count = 0 if not count_adults and not count_children then return count end ---dfhack.units.isAdult(unit) + for _,unit in ipairs(df.global.world.units.active) do if race_creature_id == df.creature_raw.find(unit.race).creature_id and is_valid_animal(unit) @@ -202,7 +182,6 @@ local function count_live_animals(race_creature_id) return count end - local function handle_eggs(eggs) print_detalis(("start handle_eggs")) if not eggs.egg_flags.fertile then @@ -211,7 +190,15 @@ local function handle_eggs(eggs) end local race_creature_id = df.creature_raw.find(eggs.race).creature_id - local max_eggs = get_max_eggs_for_race(race_creature_id) + local race_config = get_config_for_race(race_creature_id) + local max_eggs = race_config[1] + local count_children = race_config[2] + local count_adults = race_config[3] + + print_detalis(("max_eggs %s "):format(max_eggs)) + print_detalis(("count_children %s "):format(count_children)) + print_detalis(("count_adults %s "):format(count_adults)) + local current_eggs = eggs.stack_size local total_count = current_eggs @@ -219,7 +206,7 @@ local function handle_eggs(eggs) if total_count - current_eggs < max_eggs then print_detalis(("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format(race_creature_id, total_count - current_eggs)) - total_count = total_count + count_live_animals(race_creature_id) + total_count = total_count + count_live_animals(race_creature_id, count_children, count_adults) else print_detalis(("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format(race_creature_id, total_count, max_eggs)) return From 47b9aa3b989458fe617319a589b26a0e8057e64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sun, 8 Sep 2024 11:20:27 +0200 Subject: [PATCH 06/22] change event frequency, no need to check more often than nestboxes --- eggwatch.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eggwatch.lua b/eggwatch.lua index 8554d6b43..0365ad748 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -2,7 +2,7 @@ local argparse = require("argparse") local eventful = require("plugins.eventful") local GLOBAL_KEY = "eggwatch" -local EVENT_FREQ = 5 +local EVENT_FREQ = 7 local print_prefix = "eggwatch: " enabled = enabled or false From 59945d3b33aedf248d032250076200c87d31d744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sun, 8 Sep 2024 13:15:44 +0200 Subject: [PATCH 07/22] add local state variable and move persisted variables inside, add dfhack.onStateChange hook --- eggwatch.lua | 151 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 57 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index 0365ad748..704ef407c 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -1,39 +1,28 @@ +--@enable = true +--@module = true local argparse = require("argparse") local eventful = require("plugins.eventful") +local utils = require('utils') local GLOBAL_KEY = "eggwatch" local EVENT_FREQ = 7 local print_prefix = "eggwatch: " -enabled = enabled or false -default_table = {} +local default_table = {} default_table.DEFAULT = {10, false, false} -function isEnabled() - return enabled -end -local function persist_state() - dfhack.persistent.saveSiteData( - GLOBAL_KEY, - { - enabled = enabled, - verbose = verbose, - target_eggs_count_per_race = target_eggs_count_per_race - } - ) +local function get_default_state() + return { + enabled = false, + verbose = false, + target_eggs_count_per_race = default_table + } end ---- Load the saved state of the script -local function load_state() - -- load persistent data - local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) - enabled = persisted_data.enabled or false - verbose = persisted_data.verbose or false - target_eggs_count_per_race = persisted_data.target_eggs_count_per_race or default_table -end +local state = state or get_default_state() -if dfhack_flags.module then - return +function isEnabled() + return state.enabled end local function print_local(text) @@ -45,18 +34,67 @@ local function handle_error(text) end local function print_status() - print_local(("eggwatch is currently %s."):format(enabled and "enabled" or "disabled")) - if verbose then + print_local(("eggwatch is currently %s."):format(state.enabled and "enabled" or "disabled")) + if state.verbose then print_local("eggwatch is in verbose mode") end end local function print_detalis(details) - if verbose then + if state.verbose then print_local(details) end end +local function persist_state() + dfhack.persistent.saveSiteData(GLOBAL_KEY, state) +end + +--- Load the saved state of the script +local function load_state() + -- load persistent data + state = get_default_state() + utils.assign(state, dfhack.persistent.getSiteData(GLOBAL_KEY, state)) +end + +local function update_event_listener() + if isEnabled() then + eventful.enableEvent(eventful.eventType.ITEM_CREATED, EVENT_FREQ) + eventful.onItemCreated[GLOBAL_KEY] = check_item_created + print_local(("Subscribing in eventful for %s with frequency %s"):format("ITEM_CREATED", EVENT_FREQ)) + else + eventful.onItemCreated[GLOBAL_KEY] = nil + print_local(("Unregistering from eventful for %s"):format("ITEM_CREATED")) + end +end + +local function do_enable() + state.enabled = true + update_event_listener() +end + +local function do_disable() + state.enabled = false + update_event_listener() +end + +dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc == SC_MAP_UNLOADED then + do_disable() + return + end + if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then + return + end + load_state() + print_status() + update_event_listener() +end + +if dfhack_flags.module then + return +end + local function is_egg(item) return df.item_type.EGG == item:getType() end @@ -141,14 +179,14 @@ end local function get_config_for_race(race_creature_id) print_detalis(("getting config for race %s "):format(race_creature_id)) - for k, v in pairs(target_eggs_count_per_race) do + for k, v in pairs(state.target_eggs_count_per_race) do if k == race_creature_id then return v end end - target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT + state.target_eggs_count_per_race[race_creature_id] = state.target_eggs_count_per_race.DEFAULT persist_state() - return target_eggs_count_per_race[race_creature_id] + return state.target_eggs_count_per_race[race_creature_id] end local function is_valid_animal(unit) @@ -247,7 +285,8 @@ local function handle_eggs(eggs) print_detalis(("end handle_eggs")) end -local function check_item_created(item_id) +function check_item_created(item_id) + local item = df.item.find(item_id) if not item or not is_egg(item) then return @@ -255,17 +294,6 @@ local function check_item_created(item_id) handle_eggs(item) end -local function do_enable() - enabled = true - eventful.enableEvent(eventful.eventType.ITEM_CREATED, EVENT_FREQ) - eventful.onItemCreated[GLOBAL_KEY] = check_item_created -end - -local function do_disable() - enabled = false - eventful.onItemCreated[GLOBAL_KEY] = nil -end - local function validate_creature_id(creature_id) for i, c in ipairs(df.global.world.raws.creatures.all) do if c.creature_id == creature_id then @@ -286,12 +314,12 @@ local stringtoboolean={ ["true"]=true, ["false"]=false, ["1"] = true , ["0"] = handle_error("No valid target count specified") end if target_race_upper == "DEFAULT" or validate_creature_id(target_race_upper) then - target_eggs_count_per_race[target_race_upper] = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} + state.target_eggs_count_per_race[target_race_upper] = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} else handle_error('must specify "DEFAULT" or valid creature_id') end - print_local(dump(target_eggs_count_per_race)) + print_local(dump(state.target_eggs_count_per_race)) end function dump(o) @@ -314,6 +342,7 @@ if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then return end +load_state() local args, opts = {...}, {} if dfhack_flags and dfhack_flags.enable then args = {dfhack_flags.enable_state and "enable" or "disable"} @@ -329,30 +358,38 @@ local positionals = } ) -load_state() +if dfhack_flags.enable then + if dfhack_flags.enable_state then + do_enable() + print_status() + else + do_disable() + print_status() + end +end + local command = positionals[1] if command == "help" or opts.help then print(dfhack.script_help()) -elseif command == "enable" then - do_enable() - print_status() -elseif command == "disable" then - do_disable() - print_status() elseif command == "target" then set_target(positionals[2], positionals[3], positionals[4], positionals[5]) print_status() elseif command == "verbose" then - verbose = not verbose + state.verbose = not state.verbose print_status() elseif command == 'clear' then -target_eggs_count_per_race = default_table - + state.target_eggs_count_per_race = default_table +elseif command == 'hardreset' then + state = get_default_state() + update_event_listener() elseif not command or command == "status" then print_status() - print_local(dump(target_eggs_count_per_race)) -else + -- print_local(dump(state.enabled)) + -- print_local(dump(state.verbose)) + -- print_local(dump(state)) + print_local(dump(state.target_eggs_count_per_race)) +elseif (command ~= 'enable' or command ~= 'disable') and not dfhack_flags.enable then handle_error(('Command "%s" is not recognized'):format(command)) end -persist_state() +persist_state() \ No newline at end of file From 3e667d0625a99282616a648cd1235acc034829f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Sun, 8 Sep 2024 19:22:25 +0200 Subject: [PATCH 08/22] replace creature_id with race for internal use, creature_id left for target input --- eggwatch.lua | 144 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 87 insertions(+), 57 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index 704ef407c..2b37f4dc3 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -7,15 +7,30 @@ local utils = require('utils') local GLOBAL_KEY = "eggwatch" local EVENT_FREQ = 7 local print_prefix = "eggwatch: " +local default_table = {10, false, false} +local stringtoboolean={ ["true"]=true, ["false"]=false, ["1"] = true , ["0"] = false , ["Y"] = true , ["N"] = false} -local default_table = {} -default_table.DEFAULT = {10, false, false} +function dump(o) + if type(o) == "table" then + local s = "{ " + for k, v in pairs(o) do + if type(k) ~= "number" then + k = '"' .. k .. '"' + end + s = s .. "[" .. k .. "] = " .. dump(v) .. "," + end + return s .. "} " + else + return tostring(o) + end +end local function get_default_state() return { enabled = false, verbose = false, - target_eggs_count_per_race = default_table + default = default_table, + target_eggs_count_per_race = {} } end @@ -32,9 +47,17 @@ end local function handle_error(text) qerror(text) end - +local function format_target_count_row (header, row) +return header..': ' .. 'target count: ' .. row[1] .. '; count children: ' .. tostring(row[2]) .. '; count adults: ' .. tostring(row[3]) +end local function print_status() print_local(("eggwatch is currently %s."):format(state.enabled and "enabled" or "disabled")) + print_local(format_target_count_row('Default', state.default)) + if state.target_eggs_count_per_race ~= nil then + for k, v in pairs(state.target_eggs_count_per_race) do + print_local(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) + end + end if state.verbose then print_local("eggwatch is in verbose mode") end @@ -46,15 +69,39 @@ local function print_detalis(details) end end + local function persist_state() - dfhack.persistent.saveSiteData(GLOBAL_KEY, state) +local state_to_persist = {} +state_to_persist.enabled = state.enabled +state_to_persist.verbose = state.verbose +state_to_persist.default = state.default +state_to_persist.target_eggs_count_per_race = {} +if state.target_eggs_count_per_race ~= nil then + for k, v in pairs(state.target_eggs_count_per_race) do + state_to_persist.target_eggs_count_per_race[tostring(k)]= v + end +end +dfhack.persistent.saveSiteData(GLOBAL_KEY, state_to_persist) end --- Load the saved state of the script local function load_state() -- load persistent data - state = get_default_state() - utils.assign(state, dfhack.persistent.getSiteData(GLOBAL_KEY, state)) + local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, persisted_data) + state = {} + if persisted_data ~= nil then + state.enabled = persisted_data.enabled + state.verbose = persisted_data.verbose + state.default = persisted_data.default + state.target_eggs_count_per_race = {} + if persisted_data.target_eggs_count_per_race ~= nil then + for k, v in pairs(persisted_data.target_eggs_count_per_race) do + state.target_eggs_count_per_race[tonumber(k)]= v + end + end + else + state = get_default_state() + end end local function update_event_listener() @@ -143,7 +190,7 @@ end -- dfhack.items.moveToContainer(created_egg_stack, find_current_nestbox(original_eggs)) -- end -local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) +local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race) print_detalis(("start count_forbidden_eggs_for_race_in_claimed_nestobxes")) local eggs_count = 0 for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do @@ -156,8 +203,8 @@ local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_ print_detalis(("Found claimed nextbox containing items that are eggs")) if nestbox_contained_item.item.egg_flags.fertile and nestbox_contained_item.item.flags.forbid then print_detalis(("Eggs are fertile and forbidden")) - if df.creature_raw.find(nestbox_contained_item.item.race).creature_id == race_creature_id then - print_detalis(("Eggs belong to %s"):format(race_creature_id)) + if nestbox_contained_item.item.race == race then + print_detalis(("Eggs belong to %s"):format(race)) print_detalis( ("eggs_count %s + new %s"):format( eggs_count, @@ -177,16 +224,16 @@ local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_ return eggs_count end -local function get_config_for_race(race_creature_id) - print_detalis(("getting config for race %s "):format(race_creature_id)) +local function get_config_for_race(race) + print_detalis(("getting config for race %s "):format(race)) for k, v in pairs(state.target_eggs_count_per_race) do - if k == race_creature_id then + if k == race then return v end end - state.target_eggs_count_per_race[race_creature_id] = state.target_eggs_count_per_race.DEFAULT + state.target_eggs_count_per_race[race] = state.default persist_state() - return state.target_eggs_count_per_race[race_creature_id] + return state.target_eggs_count_per_race[race] end local function is_valid_animal(unit) @@ -198,9 +245,9 @@ local function is_valid_animal(unit) not dfhack.units.isDead(unit) end -local function count_live_animals(race_creature_id, count_children, count_adults) - if count_adults then print_detalis(('we are counting adults for %s'):format(race_creature_id)) end - if count_children then print_detalis(('we are counting children and babies for %s'):format(race_creature_id)) end +local function count_live_animals(race, count_children, count_adults) + if count_adults then print_detalis(('we are counting adults for %s'):format(race)) end + if count_children then print_detalis(('we are counting children and babies for %s'):format(race)) end local count = 0 if not count_adults and not count_children then @@ -208,7 +255,7 @@ local function count_live_animals(race_creature_id, count_children, count_adults end for _,unit in ipairs(df.global.world.units.active) do - if race_creature_id == df.creature_raw.find(unit.race).creature_id + if race == unit.race and is_valid_animal(unit) and ( (count_adults and dfhack.units.isAdult(unit)) or (count_children and ( dfhack.units.isChild(unit) or dfhack.units.isBaby(unit))) @@ -227,8 +274,8 @@ local function handle_eggs(eggs) return end - local race_creature_id = df.creature_raw.find(eggs.race).creature_id - local race_config = get_config_for_race(race_creature_id) + local race = eggs.race + local race_config = get_config_for_race(race) local max_eggs = race_config[1] local count_children = race_config[2] local count_adults = race_config[3] @@ -240,17 +287,17 @@ local function handle_eggs(eggs) local current_eggs = eggs.stack_size local total_count = current_eggs - total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) + total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race) if total_count - current_eggs < max_eggs then - print_detalis(("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format(race_creature_id, total_count - current_eggs)) - total_count = total_count + count_live_animals(race_creature_id, count_children, count_adults) + print_detalis(("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format(race, total_count - current_eggs)) + total_count = total_count + count_live_animals(race, count_children, count_adults) else - print_detalis(("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format(race_creature_id, total_count, max_eggs)) + print_detalis(("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format(race, total_count, max_eggs)) return end - print_detalis(("Total count for %s eggs is %s"):format(race_creature_id, total_count)) + print_detalis(("Total count for %s eggs is %s"):format(race, total_count)) if total_count - current_eggs < max_eggs then -- ###if possible split egg stack to forbid only part below max change previous condition to total_count < max_eggs @@ -261,11 +308,11 @@ local function handle_eggs(eggs) -- create_new_egg_stack(eggs, remaining_eggs, df.creature_raw.find(eggs.race), race_creature.caste[eggs.caste]) -- eggs.stack_size = forbid_eggs -- eggs.flags.forbid = true - -- print(('Total count for %s eggs is %s over maximum %s , forbidden %s eggs out of clutch of %s.'):format(race_creature_id, total_count, max_eggs, forbid_eggs, current_eggs)) + -- print(('Total count for %s eggs is %s over maximum %s , forbidden %s eggs out of clutch of %s.'):format(race, total_count, max_eggs, forbid_eggs, current_eggs)) eggs.flags.forbid = true print_local( ("Previously existing %s eggs is %s lower than maximum %s , forbidden %s new eggs."):format( - race_creature_id, + race, total_count - current_eggs, max_eggs, current_eggs @@ -274,7 +321,7 @@ local function handle_eggs(eggs) else print_local( ("Total count for %s eggs is %s over maximum %s, newly laid eggs %s , no action taken."):format( - race_creature_id, + race, total_count, max_eggs, current_eggs @@ -297,45 +344,34 @@ end local function validate_creature_id(creature_id) for i, c in ipairs(df.global.world.raws.creatures.all) do if c.creature_id == creature_id then - return true + return i end end - return false + return -1 end local function set_target(target_race, target_count, count_children, count_adult) -local stringtoboolean={ ["true"]=true, ["false"]=false, ["1"] = true , ["0"] = false , ["Y"] = true , ["N"] = false} if target_race == nil or target_race == "" then handle_error('must specify "DEFAULT" or valid creature_id') end + local target_race_upper = string.upper(target_race) + if tonumber(target_count) == nil or tonumber(target_count) < 0 then handle_error("No valid target count specified") end - if target_race_upper == "DEFAULT" or validate_creature_id(target_race_upper) then - state.target_eggs_count_per_race[target_race_upper] = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} + local race = validate_creature_id(target_race_upper) + if target_race_upper == "DEFAULT" then + state.default = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} + elseif race >= 0 then + print(race) + state.target_eggs_count_per_race[race] = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} else handle_error('must specify "DEFAULT" or valid creature_id') end - - print_local(dump(state.target_eggs_count_per_race)) end -function dump(o) - if type(o) == "table" then - local s = "{ " - for k, v in pairs(o) do - if type(k) ~= "number" then - k = '"' .. k .. '"' - end - s = s .. "[" .. k .. "] = " .. dump(v) .. "," - end - return s .. "} " - else - return tostring(o) - end -end if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then dfhack.printerr("eggwatch needs a loaded fortress to work") @@ -379,17 +415,11 @@ elseif command == "verbose" then state.verbose = not state.verbose print_status() elseif command == 'clear' then - state.target_eggs_count_per_race = default_table -elseif command == 'hardreset' then state = get_default_state() update_event_listener() elseif not command or command == "status" then print_status() - -- print_local(dump(state.enabled)) - -- print_local(dump(state.verbose)) - -- print_local(dump(state)) - print_local(dump(state.target_eggs_count_per_race)) elseif (command ~= 'enable' or command ~= 'disable') and not dfhack_flags.enable then handle_error(('Command "%s" is not recognized'):format(command)) end -persist_state() \ No newline at end of file +persist_state() From 0d747e8a07100f6d6e5700a89f00930b94133e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Mon, 9 Sep 2024 14:40:19 +0200 Subject: [PATCH 09/22] split egg stack if only part of them is over limit --- eggwatch.lua | 285 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 169 insertions(+), 116 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index 2b37f4dc3..3f21d044a 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -2,11 +2,9 @@ --@module = true local argparse = require("argparse") local eventful = require("plugins.eventful") -local utils = require('utils') +local utils = require("utils") local GLOBAL_KEY = "eggwatch" -local EVENT_FREQ = 7 -local print_prefix = "eggwatch: " local default_table = {10, false, false} local stringtoboolean={ ["true"]=true, ["false"]=false, ["1"] = true , ["0"] = false , ["Y"] = true , ["N"] = false} @@ -30,6 +28,8 @@ local function get_default_state() enabled = false, verbose = false, default = default_table, + EVENT_FREQ = 7, + split_stacks = true, target_eggs_count_per_race = {} } end @@ -41,88 +41,96 @@ function isEnabled() end local function print_local(text) - print(print_prefix .. text) + print(GLOBAL_KEY ..": " .. text) end local function handle_error(text) qerror(text) end + +local function print_details(details) + if state.verbose then + print_local(details) + end +end local function format_target_count_row (header, row) -return header..': ' .. 'target count: ' .. row[1] .. '; count children: ' .. tostring(row[2]) .. '; count adults: ' .. tostring(row[3]) +return header..": " .. "target count: " .. row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) end local function print_status() print_local(("eggwatch is currently %s."):format(state.enabled and "enabled" or "disabled")) - print_local(format_target_count_row('Default', state.default)) + print_local (("egg stack splitting is %s"):format(state.split_stacks and "enabled" or "disabled")) + print_local(format_target_count_row("Default", state.default)) if state.target_eggs_count_per_race ~= nil then for k, v in pairs(state.target_eggs_count_per_race) do print_local(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) end end - if state.verbose then - print_local("eggwatch is in verbose mode") - end -end + print_details("eggwatch is in verbose mode") + print_details(dump(state)) -local function print_detalis(details) - if state.verbose then - print_local(details) - end end + local function persist_state() -local state_to_persist = {} -state_to_persist.enabled = state.enabled -state_to_persist.verbose = state.verbose -state_to_persist.default = state.default -state_to_persist.target_eggs_count_per_race = {} -if state.target_eggs_count_per_race ~= nil then - for k, v in pairs(state.target_eggs_count_per_race) do - state_to_persist.target_eggs_count_per_race[tostring(k)]= v + print_details(("start load_state")) + local state_to_persist = {} + state_to_persist = utils.clone(state) + state_to_persist.target_eggs_count_per_race = {} + if state.target_eggs_count_per_race ~= nil then + for k, v in pairs(state.target_eggs_count_per_race) do + state_to_persist.target_eggs_count_per_race[tostring(k)]= v + end end -end -dfhack.persistent.saveSiteData(GLOBAL_KEY, state_to_persist) + dfhack.persistent.saveSiteData(GLOBAL_KEY, state_to_persist) + print_details(("end load_state")) end --- Load the saved state of the script local function load_state() + print_details(("start load_state")) -- load persistent data - local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, persisted_data) - state = {} + local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) + local processed_persisted_data= {} if persisted_data ~= nil then - state.enabled = persisted_data.enabled - state.verbose = persisted_data.verbose - state.default = persisted_data.default - state.target_eggs_count_per_race = {} + processed_persisted_data = utils.clone(persisted_data) + processed_persisted_data.target_eggs_count_per_race = {} if persisted_data.target_eggs_count_per_race ~= nil then for k, v in pairs(persisted_data.target_eggs_count_per_race) do - state.target_eggs_count_per_race[tonumber(k)]= v + processed_persisted_data.target_eggs_count_per_race[tonumber(k)]= v end end - else - state = get_default_state() end + state = get_default_state() + utils.assign(state, processed_persisted_data) + print_details(("end load_state")) end local function update_event_listener() - if isEnabled() then - eventful.enableEvent(eventful.eventType.ITEM_CREATED, EVENT_FREQ) + print_details(("start update_event_listener")) + if state.enabled then + eventful.enableEvent(eventful.eventType.ITEM_CREATED, state.EVENT_FREQ) eventful.onItemCreated[GLOBAL_KEY] = check_item_created - print_local(("Subscribing in eventful for %s with frequency %s"):format("ITEM_CREATED", EVENT_FREQ)) + print_local(("Subscribing in eventful for %s with frequency %s"):format("ITEM_CREATED", state.EVENT_FREQ)) else eventful.onItemCreated[GLOBAL_KEY] = nil print_local(("Unregistering from eventful for %s"):format("ITEM_CREATED")) end + print_details(("end update_event_listener")) end local function do_enable() + print_details(("start do_enable")) state.enabled = true update_event_listener() + print_details(("end do_enable")) end local function do_disable() + print_details(("start do_disable")) state.enabled = false update_event_listener() + print_details(("end do_disable")) end dfhack.onStateChange[GLOBAL_KEY] = function(sc) @@ -146,73 +154,105 @@ local function is_egg(item) return df.item_type.EGG == item:getType() end --- local function find_current_nestbox (current_eggs) --- for _, nestbox in ipairs (df.global.world.buildings.other.NEST_BOX) do --- if nestbox.pos == current_eggs.pos then --- return nestbox --- end --- end --- end - --- local function create_new_egg_stack (original_eggs, remaining_eggs, creature, caste) - --- print('about to split create new egg stack') --- print(('type= %s'):format(original_eggs:getType())) --- print(('creature= %s'):format( creature.creature_id)) --- print(('caste= %s '):format(caste.caste_id)) - --- --local created_items = dfhack.items.createItem(creator, original_eggs:getType(), -1, creature.creature_id, caste.caste_id) - --- print('created item') --- local created_egg_stack = created_items[1] --- print('about to copy fields from orginal eggs') --- created_egg_stack.incumabtion_counter = original_eggs.incumabtion_counter --- created_egg_stack.flags = original_eggs.flags --- created_egg_stack.flags2 = original_eggs.flags2 --- created_egg_stack.egg_flags = original_eggs.egg_flags --- created_egg_stack.pos = original_eggs.pos --- created_egg_stack.hatchling_civ_id = original_eggs.hatchling_civ_id --- created_egg_stack.mothers_genes = original_eggs.mothers_genes --- created_egg_stack.mothers_caste = original_eggs.mothers_caste --- created_egg_stack.mother_hf = original_eggs.mother_hf --- created_egg_stack.fathers_genes = original_eggs.fathers_genes --- created_egg_stack.fathers_caste = original_eggs.fathers_caste --- created_egg_stack.father_hf = original_eggs.father_hf --- created_egg_stack.hatchling_flags1 = original_eggs.hatchling_flags1 --- created_egg_stack.hatchling_flags2 = original_eggs.hatchling_flags2 --- created_egg_stack.hatchling_flags3 = original_eggs.hatchling_flags3 --- created_egg_stack.hatchling_flags4 = original_eggs.hatchling_flags4 --- created_egg_stack.hatchling_training_level = original_eggs.hatchling_training_level --- created_egg_stack.hatchling_animal_population = original_eggs.hatchling_animal_population --- created_egg_stack.mother_id = original_eggs.mother_id - --- print('about to move new stack to nestbox') --- dfhack.items.moveToContainer(created_egg_stack, find_current_nestbox(original_eggs)) --- end +local function copy_egg_fields(source_egg, target_egg) + print_details("start copy_egg_fields") + target_egg.incubation_counter = source_egg.incubation_counter + print_details("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) + print_details("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) + print_details("hatchling_animal_population done") + target_egg.hatchling_mother_id= source_egg.hatchling_mother_id + print_details("hatchling_mother_id done") + target_egg.mother_hf = source_egg.mother_hf + target_egg.father_hf = source_egg.mother_hf + print_details("mother_hf father_hf done") + target_egg.mothers_caste = source_egg.mothers_caste + target_egg.fathers_caste = source_egg.fathers_caste + print_details("mothers_caste fathers_caste done") + target_egg.mothers_genes = source_egg.mothers_genes + target_egg.fathers_genes = source_egg.fathers_genes + print_details("mothers_genes fathers_genes done") + target_egg.hatchling_civ_id = source_egg.hatchling_civ_id + print_details("hatchling_civ_id done") + print_details("end copy_egg_fields") + end + + local function resize_egg_stack(egg_stack, new_stack_size) + print_details("start resize_egg_stack") + egg_stack.stack_size=new_stack_size + --TODO check if weight or size need adjustment + print_details("end resize_egg_stack") + end + +local function create_new_egg_stack (original_eggs, remaining_eggs) + print_details("start create_new_egg_stack") + + print_details("about to split create new egg stack") + print_details(("type= %s"):format(original_eggs:getType())) + print_details(("creature= %s"):format( original_eggs.race)) + print_details(("caste= %s "):format(original_eggs.caste)) + print_details (remaining_eggs) + + local created_items = dfhack.items.createItem(df.unit.find(original_eggs.hatchling_mother_id), original_eggs:getType(), -1 , original_eggs.race, original_eggs.caste) + print_details("created new egg stack") + local created_egg_stack = created_items[0] or created_items[1] + print_details(df.creature_raw.find(created_egg_stack.race).creature_id) + print_details("about to copy fields from orginal eggs") + copy_egg_fields(original_eggs, created_egg_stack) + + print_details("about to resize new egg stack") + resize_egg_stack(created_egg_stack, remaining_eggs) + + print_details("about to move new stack to nestbox") + if dfhack.items.moveToBuilding(created_egg_stack, dfhack.items.getHolderBuilding(original_eggs)) then + print_details("moved new egg stack to nestbox") + else + print_local("move of separated eggs to nestbox failed") + end + print_details("end create_new_egg_stack") +end + +local function split_egg_stack (source_egg_stack, to_be_left_in_source_stack) + print_details("start split_egg_stack") + local egg_count_in_new_stack_size = source_egg_stack.stack_size - to_be_left_in_source_stack + if egg_count_in_new_stack_size > 0 then + create_new_egg_stack (source_egg_stack, egg_count_in_new_stack_size) + resize_egg_stack(source_egg_stack, to_be_left_in_source_stack) + else + print_details("nothing to do, wrong egg_count_in_new_stack_size") + end + print_details("end split_egg_stack") +end local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race) - print_detalis(("start count_forbidden_eggs_for_race_in_claimed_nestobxes")) + print_details(("start count_forbidden_eggs_for_race_in_claimed_nestobxes")) local eggs_count = 0 for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do if nestbox.claimed_by ~= -1 then - print_detalis(("Found claimed nextbox")) + print_details(("Found claimed nextbox")) for _, nestbox_contained_item in ipairs(nestbox.contained_items) do if nestbox_contained_item.use_mode == df.building_item_role_type.TEMP then - print_detalis(("Found claimed nextbox containing items")) + print_details(("Found claimed nextbox containing items")) if df.item_type.EGG == nestbox_contained_item.item:getType() then - print_detalis(("Found claimed nextbox containing items that are eggs")) + print_details(("Found claimed nextbox containing items that are eggs")) if nestbox_contained_item.item.egg_flags.fertile and nestbox_contained_item.item.flags.forbid then - print_detalis(("Eggs are fertile and forbidden")) + print_details(("Eggs are fertile and forbidden")) if nestbox_contained_item.item.race == race then - print_detalis(("Eggs belong to %s"):format(race)) - print_detalis( + print_details(("Eggs belong to %s"):format(race)) + print_details( ("eggs_count %s + new %s"):format( eggs_count, nestbox_contained_item.item.stack_size ) ) eggs_count = eggs_count + nestbox_contained_item.item.stack_size - print_detalis(("eggs_count after adding current nestbox %s "):format(eggs_count)) + print_details(("eggs_count after adding current nestbox %s "):format(eggs_count)) end end end @@ -220,19 +260,22 @@ local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race) end end end - print_detalis(("end count_forbidden_eggs_for_race_in_claimed_nestobxes")) + print_details(("end count_forbidden_eggs_for_race_in_claimed_nestobxes")) return eggs_count end local function get_config_for_race(race) - print_detalis(("getting config for race %s "):format(race)) + print_details(("start get_config_for_race")) + print_details(("getting config for race %s "):format(race)) for k, v in pairs(state.target_eggs_count_per_race) do if k == race then + print_details(("end 1 get_config_for_race")) return v end end state.target_eggs_count_per_race[race] = state.default persist_state() + print_details(("end 2 get_config_for_race")) return state.target_eggs_count_per_race[race] end @@ -246,11 +289,13 @@ local function is_valid_animal(unit) end local function count_live_animals(race, count_children, count_adults) - if count_adults then print_detalis(('we are counting adults for %s'):format(race)) end - if count_children then print_detalis(('we are counting children and babies for %s'):format(race)) end + print_details(("start count_live_animals")) + if count_adults then print_details(("we are counting adults for %s"):format(race)) end + if count_children then print_details(("we are counting children and babies for %s"):format(race)) end local count = 0 if not count_adults and not count_children then + print_details(("end 1 count_live_animals")) return count end @@ -263,12 +308,13 @@ local function count_live_animals(race, count_children, count_adults) count = count + 1 end end - print_detalis(('found %s life animals'):format(count)) + print_details(("found %s life animals"):format(count)) + print_details(("end 2 count_live_animals")) return count end local function handle_eggs(eggs) - print_detalis(("start handle_eggs")) + print_details(("start handle_eggs")) if not eggs.egg_flags.fertile then print_local("Newly laid eggs are not fertile, do nothing") return @@ -280,9 +326,9 @@ local function handle_eggs(eggs) local count_children = race_config[2] local count_adults = race_config[3] - print_detalis(("max_eggs %s "):format(max_eggs)) - print_detalis(("count_children %s "):format(count_children)) - print_detalis(("count_adults %s "):format(count_adults)) + print_details(("max_eggs %s "):format(max_eggs)) + print_details(("count_children %s "):format(count_children)) + print_details(("count_adults %s "):format(count_adults)) local current_eggs = eggs.stack_size @@ -290,25 +336,22 @@ local function handle_eggs(eggs) total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race) if total_count - current_eggs < max_eggs then - print_detalis(("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format(race, total_count - current_eggs)) + print_details(("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format(race, total_count - current_eggs)) total_count = total_count + count_live_animals(race, count_children, count_adults) else - print_detalis(("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format(race, total_count, max_eggs)) + print_details(("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format(race, total_count, max_eggs)) + print_details(("end 1 handle_eggs")) return end - print_detalis(("Total count for %s eggs is %s"):format(race, total_count)) + print_details(("Total count for %s eggs is %s"):format(race, total_count)) if total_count - current_eggs < max_eggs then - -- ###if possible split egg stack to forbid only part below max change previous condition to total_count < max_eggs - -- elseif total_count - current_eggs < max_eggs and total_count > max_eggs then - -- local forbid_eggs = max_eggs - total_count + current_eggs - -- local remaining_eggs = current_eggs - forbid_eggs - -- print('about to split eggs stack') - -- create_new_egg_stack(eggs, remaining_eggs, df.creature_raw.find(eggs.race), race_creature.caste[eggs.caste]) - -- eggs.stack_size = forbid_eggs - -- eggs.flags.forbid = true - -- print(('Total count for %s eggs is %s over maximum %s , forbidden %s eggs out of clutch of %s.'):format(race, total_count, max_eggs, forbid_eggs, current_eggs)) + if state.split_stacks and total_count > max_eggs then + local egg_count_to_leave_in_source_stack = max_eggs - total_count + current_eggs + split_egg_stack (eggs, egg_count_to_leave_in_source_stack) + end + eggs.flags.forbid = true print_local( ("Previously existing %s eggs is %s lower than maximum %s , forbidden %s new eggs."):format( @@ -328,32 +371,38 @@ local function handle_eggs(eggs) ) ) end - - print_detalis(("end handle_eggs")) + print_details(("end 2 handle_eggs")) end function check_item_created(item_id) - + --print_details(("start check_item_created")) local item = df.item.find(item_id) if not item or not is_egg(item) then + --print_details(("end 1 check_item_created")) return end + --print_local(("item_id for original eggs: %s"):format (item_id)) handle_eggs(item) + --print_details(("end 2 check_item_created")) end local function validate_creature_id(creature_id) + print_details(("start validate_creature_id")) for i, c in ipairs(df.global.world.raws.creatures.all) do if c.creature_id == creature_id then + print_details(("end 1 validate_creature_id")) return i end end + print_details(("end 2 validate_creature_id")) return -1 end local function set_target(target_race, target_count, count_children, count_adult) + print_details(("start set_target")) if target_race == nil or target_race == "" then - handle_error('must specify "DEFAULT" or valid creature_id') + handle_error("must specify DEFAULT or valid creature_id") end local target_race_upper = string.upper(target_race) @@ -368,8 +417,9 @@ local function set_target(target_race, target_count, count_children, count_adult print(race) state.target_eggs_count_per_race[race] = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} else - handle_error('must specify "DEFAULT" or valid creature_id') + handle_error("must specify DEFAULT or valid creature_id") end + print_details(("end set_target")) end @@ -414,12 +464,15 @@ elseif command == "target" then elseif command == "verbose" then state.verbose = not state.verbose print_status() -elseif command == 'clear' then +elseif command == "clear" then state = get_default_state() update_event_listener() +elseif command == "split_stacks" then + state.split_stacks = stringtoboolean[positionals[2]] + print_status() elseif not command or command == "status" then print_status() -elseif (command ~= 'enable' or command ~= 'disable') and not dfhack_flags.enable then - handle_error(('Command "%s" is not recognized'):format(command)) +elseif (command ~= "enable" or command ~= "disable") and not dfhack_flags.enable then + handle_error(("Command "%s" is not recognized"):format(command)) end persist_state() From 6f51fd57aed9ae32d2f0abe080472a7218336665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Mon, 9 Sep 2024 14:51:39 +0200 Subject: [PATCH 10/22] beautified code --- eggwatch.lua | 148 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 89 insertions(+), 59 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index 3f21d044a..66a4ac833 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -6,7 +6,7 @@ local utils = require("utils") local GLOBAL_KEY = "eggwatch" local default_table = {10, false, false} -local stringtoboolean={ ["true"]=true, ["false"]=false, ["1"] = true , ["0"] = false , ["Y"] = true , ["N"] = false} +local stringtoboolean = {["true"] = true, ["false"] = false, ["1"] = true, ["0"] = false, ["Y"] = true, ["N"] = false} function dump(o) if type(o) == "table" then @@ -41,7 +41,7 @@ function isEnabled() end local function print_local(text) - print(GLOBAL_KEY ..": " .. text) + print(GLOBAL_KEY .. ": " .. text) end local function handle_error(text) @@ -53,25 +53,25 @@ local function print_details(details) print_local(details) end end -local function format_target_count_row (header, row) -return header..": " .. "target count: " .. row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) +local function format_target_count_row(header, row) + return header .. + ": " .. + "target count: " .. + row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) end local function print_status() print_local(("eggwatch is currently %s."):format(state.enabled and "enabled" or "disabled")) - print_local (("egg stack splitting is %s"):format(state.split_stacks and "enabled" or "disabled")) + print_local(("egg stack splitting is %s"):format(state.split_stacks and "enabled" or "disabled")) print_local(format_target_count_row("Default", state.default)) if state.target_eggs_count_per_race ~= nil then - for k, v in pairs(state.target_eggs_count_per_race) do - print_local(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) - end + for k, v in pairs(state.target_eggs_count_per_race) do + print_local(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) + end end - print_details("eggwatch is in verbose mode") - print_details(dump(state)) - + print_details("eggwatch is in verbose mode") + print_details(dump(state)) end - - local function persist_state() print_details(("start load_state")) local state_to_persist = {} @@ -79,7 +79,7 @@ local function persist_state() state_to_persist.target_eggs_count_per_race = {} if state.target_eggs_count_per_race ~= nil then for k, v in pairs(state.target_eggs_count_per_race) do - state_to_persist.target_eggs_count_per_race[tostring(k)]= v + state_to_persist.target_eggs_count_per_race[tostring(k)] = v end end dfhack.persistent.saveSiteData(GLOBAL_KEY, state_to_persist) @@ -91,13 +91,13 @@ local function load_state() print_details(("start load_state")) -- load persistent data local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) - local processed_persisted_data= {} - if persisted_data ~= nil then + local processed_persisted_data = {} + if persisted_data ~= nil then processed_persisted_data = utils.clone(persisted_data) processed_persisted_data.target_eggs_count_per_race = {} if persisted_data.target_eggs_count_per_race ~= nil then for k, v in pairs(persisted_data.target_eggs_count_per_race) do - processed_persisted_data.target_eggs_count_per_race[tonumber(k)]= v + processed_persisted_data.target_eggs_count_per_race[tonumber(k)] = v end end end @@ -165,9 +165,9 @@ local function copy_egg_fields(source_egg, target_egg) target_egg.hatchling_flags4 = utils.clone(source_egg.hatchling_flags4, true) print_details("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) + utils.assign(target_egg.hatchling_animal_population, source_egg.hatchling_animal_population) print_details("hatchling_animal_population done") - target_egg.hatchling_mother_id= source_egg.hatchling_mother_id + target_egg.hatchling_mother_id = source_egg.hatchling_mother_id print_details("hatchling_mother_id done") target_egg.mother_hf = source_egg.mother_hf target_egg.father_hf = source_egg.mother_hf @@ -181,25 +181,32 @@ local function copy_egg_fields(source_egg, target_egg) target_egg.hatchling_civ_id = source_egg.hatchling_civ_id print_details("hatchling_civ_id done") print_details("end copy_egg_fields") - end +end - local function resize_egg_stack(egg_stack, new_stack_size) +local function resize_egg_stack(egg_stack, new_stack_size) print_details("start resize_egg_stack") - egg_stack.stack_size=new_stack_size + egg_stack.stack_size = new_stack_size --TODO check if weight or size need adjustment print_details("end resize_egg_stack") - end +end -local function create_new_egg_stack (original_eggs, remaining_eggs) +local function create_new_egg_stack(original_eggs, remaining_eggs) print_details("start create_new_egg_stack") print_details("about to split create new egg stack") print_details(("type= %s"):format(original_eggs:getType())) - print_details(("creature= %s"):format( original_eggs.race)) + print_details(("creature= %s"):format(original_eggs.race)) print_details(("caste= %s "):format(original_eggs.caste)) - print_details (remaining_eggs) - - local created_items = dfhack.items.createItem(df.unit.find(original_eggs.hatchling_mother_id), original_eggs:getType(), -1 , original_eggs.race, original_eggs.caste) + print_details(remaining_eggs) + + local created_items = + dfhack.items.createItem( + df.unit.find(original_eggs.hatchling_mother_id), + original_eggs:getType(), + -1, + original_eggs.race, + original_eggs.caste + ) print_details("created new egg stack") local created_egg_stack = created_items[0] or created_items[1] print_details(df.creature_raw.find(created_egg_stack.race).creature_id) @@ -218,14 +225,14 @@ local function create_new_egg_stack (original_eggs, remaining_eggs) print_details("end create_new_egg_stack") end -local function split_egg_stack (source_egg_stack, to_be_left_in_source_stack) +local function split_egg_stack(source_egg_stack, to_be_left_in_source_stack) print_details("start split_egg_stack") local egg_count_in_new_stack_size = source_egg_stack.stack_size - to_be_left_in_source_stack if egg_count_in_new_stack_size > 0 then - create_new_egg_stack (source_egg_stack, egg_count_in_new_stack_size) - resize_egg_stack(source_egg_stack, to_be_left_in_source_stack) + create_new_egg_stack(source_egg_stack, egg_count_in_new_stack_size) + resize_egg_stack(source_egg_stack, to_be_left_in_source_stack) else - print_details("nothing to do, wrong egg_count_in_new_stack_size") + print_details("nothing to do, wrong egg_count_in_new_stack_size") end print_details("end split_egg_stack") end @@ -280,18 +287,19 @@ local function get_config_for_race(race) end local function is_valid_animal(unit) - return unit and - dfhack.units.isActive(unit) and - dfhack.units.isAnimal(unit) and - dfhack.units.isFortControlled(unit) and + return unit and dfhack.units.isActive(unit) and dfhack.units.isAnimal(unit) and dfhack.units.isFortControlled(unit) and dfhack.units.isTame(unit) and not dfhack.units.isDead(unit) end local function count_live_animals(race, count_children, count_adults) print_details(("start count_live_animals")) - if count_adults then print_details(("we are counting adults for %s"):format(race)) end - if count_children then print_details(("we are counting children and babies for %s"):format(race)) end + if count_adults then + print_details(("we are counting adults for %s"):format(race)) + end + if count_children then + print_details(("we are counting children and babies for %s"):format(race)) + end local count = 0 if not count_adults and not count_children then @@ -299,12 +307,12 @@ local function count_live_animals(race, count_children, count_adults) return count end - for _,unit in ipairs(df.global.world.units.active) do - if race == unit.race - and is_valid_animal(unit) - and ( (count_adults and dfhack.units.isAdult(unit)) - or (count_children and ( dfhack.units.isChild(unit) or dfhack.units.isBaby(unit))) - ) then + for _, unit in ipairs(df.global.world.units.active) do + if + race == unit.race and is_valid_animal(unit) and + ((count_adults and dfhack.units.isAdult(unit)) or + (count_children and (dfhack.units.isChild(unit) or dfhack.units.isBaby(unit)))) + then count = count + 1 end end @@ -336,10 +344,21 @@ local function handle_eggs(eggs) total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race) if total_count - current_eggs < max_eggs then - print_details(("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format(race, total_count - current_eggs)) + print_details( + ("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format( + race, + total_count - current_eggs + ) + ) total_count = total_count + count_live_animals(race, count_children, count_adults) else - print_details(("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format(race, total_count, max_eggs)) + print_details( + ("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format( + race, + total_count, + max_eggs + ) + ) print_details(("end 1 handle_eggs")) return end @@ -348,8 +367,8 @@ local function handle_eggs(eggs) if total_count - current_eggs < max_eggs then if state.split_stacks and total_count > max_eggs then - local egg_count_to_leave_in_source_stack = max_eggs - total_count + current_eggs - split_egg_stack (eggs, egg_count_to_leave_in_source_stack) + local egg_count_to_leave_in_source_stack = max_eggs - total_count + current_eggs + split_egg_stack(eggs, egg_count_to_leave_in_source_stack) end eggs.flags.forbid = true @@ -378,7 +397,7 @@ function check_item_created(item_id) --print_details(("start check_item_created")) local item = df.item.find(item_id) if not item or not is_egg(item) then - --print_details(("end 1 check_item_created")) + --print_details(("end 1 check_item_created")) return end --print_local(("item_id for original eggs: %s"):format (item_id)) @@ -412,17 +431,24 @@ local function set_target(target_race, target_count, count_children, count_adult end local race = validate_creature_id(target_race_upper) if target_race_upper == "DEFAULT" then - state.default = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} + state.default = { + tonumber(target_count), + stringtoboolean[count_children] or false, + stringtoboolean[count_adult] or false + } elseif race >= 0 then - print(race) - state.target_eggs_count_per_race[race] = {tonumber(target_count), stringtoboolean[count_children] or false, stringtoboolean[count_adult] or false} + print(race) + state.target_eggs_count_per_race[race] = { + tonumber(target_count), + stringtoboolean[count_children] or false, + stringtoboolean[count_adult] or false + } else handle_error("must specify DEFAULT or valid creature_id") end print_details(("end set_target")) end - if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then dfhack.printerr("eggwatch needs a loaded fortress to work") return @@ -438,9 +464,13 @@ local positionals = argparse.processArgsGetopt( args, { - {"h", "help", handler = function() + { + "h", + "help", + handler = function() opts.help = true - end} + end + } } ) @@ -448,7 +478,7 @@ if dfhack_flags.enable then if dfhack_flags.enable_state then do_enable() print_status() - else + else do_disable() print_status() end @@ -459,7 +489,7 @@ local command = positionals[1] if command == "help" or opts.help then print(dfhack.script_help()) elseif command == "target" then - set_target(positionals[2], positionals[3], positionals[4], positionals[5]) + set_target(positionals[2], positionals[3], positionals[4], positionals[5]) print_status() elseif command == "verbose" then state.verbose = not state.verbose @@ -468,11 +498,11 @@ elseif command == "clear" then state = get_default_state() update_event_listener() elseif command == "split_stacks" then - state.split_stacks = stringtoboolean[positionals[2]] + state.split_stacks = stringtoboolean[positionals[2]] print_status() elseif not command or command == "status" then print_status() elseif (command ~= "enable" or command ~= "disable") and not dfhack_flags.enable then - handle_error(("Command "%s" is not recognized"):format(command)) + handle_error(("Command " % s " is not recognized"):format(command)) end persist_state() From 99c32002e9cbb532258ddc82957bd39074041947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Mon, 9 Sep 2024 15:04:06 +0200 Subject: [PATCH 11/22] fixed unrecognized command error message --- eggwatch.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eggwatch.lua b/eggwatch.lua index 66a4ac833..e0358b340 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -503,6 +503,6 @@ elseif command == "split_stacks" then elseif not command or command == "status" then print_status() elseif (command ~= "enable" or command ~= "disable") and not dfhack_flags.enable then - handle_error(("Command " % s " is not recognized"):format(command)) + handle_error(("Command '% s' is not recognized"):format(command)) end persist_state() From 0aacfa789e1c58d4f8acb66882ad05bb43bcb48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Mon, 9 Sep 2024 16:25:43 +0200 Subject: [PATCH 12/22] validate eggs before handling them --- eggwatch.lua | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index e0358b340..785be4f6e 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -321,10 +321,33 @@ local function count_live_animals(race, count_children, count_adults) return count end +local function validate_eggs(eggs) + if not eggs.egg_flags.fertile then + print_details("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 + print_details("Found nestbox, continue with egg handling") + return true + end + end + print_details("Newly laid eggs are in building different than nestbox, we were to late") + return false + else + print_details("Newly laid eggs are not in building, we were to late") + return false + end + return true +end + local function handle_eggs(eggs) print_details(("start handle_eggs")) - if not eggs.egg_flags.fertile then - print_local("Newly laid eggs are not fertile, do nothing") + + if not validate_eggs(eggs) then return end From 266f6e904120488bfda9fa8e2b879dccf8a8b02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Tue, 10 Sep 2024 23:39:19 +0200 Subject: [PATCH 13/22] migrate enabled status from cpp nestboxes --- eggwatch.lua | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index 785be4f6e..7cdfeed89 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -6,7 +6,7 @@ local utils = require("utils") local GLOBAL_KEY = "eggwatch" local default_table = {10, false, false} -local stringtoboolean = {["true"] = true, ["false"] = false, ["1"] = true, ["0"] = false, ["Y"] = true, ["N"] = false} +local string_or_int_to_boolean = {["true"] = true, ["false"] = false, ["1"] = true, ["0"] = false, ["Y"] = true, ["N"] = false, [1] = true, [0] = false} function dump(o) if type(o) == "table" then @@ -30,6 +30,7 @@ local function get_default_state() default = default_table, EVENT_FREQ = 7, split_stacks = true, + migration_from_cpp_to_lua_done = false, target_eggs_count_per_race = {} } end @@ -60,7 +61,7 @@ local function format_target_count_row(header, row) row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) end local function print_status() - print_local(("eggwatch is currently %s."):format(state.enabled and "enabled" or "disabled")) + print_local((GLOBAL_KEY .. " is currently %s."):format(state.enabled and "enabled" or "disabled")) print_local(("egg stack splitting is %s"):format(state.split_stacks and "enabled" or "disabled")) print_local(format_target_count_row("Default", state.default)) if state.target_eggs_count_per_race ~= nil then @@ -68,12 +69,12 @@ local function print_status() print_local(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) end end - print_details("eggwatch is in verbose mode") + print_details("verbose mode enabled") print_details(dump(state)) end local function persist_state() - print_details(("start load_state")) + print_details(("start persist_state")) local state_to_persist = {} state_to_persist = utils.clone(state) state_to_persist.target_eggs_count_per_race = {} @@ -83,7 +84,7 @@ local function persist_state() end end dfhack.persistent.saveSiteData(GLOBAL_KEY, state_to_persist) - print_details(("end load_state")) + print_details(("end persist_state")) end --- Load the saved state of the script @@ -101,8 +102,25 @@ local function load_state() end end end + state = get_default_state() utils.assign(state, processed_persisted_data) + + if not state.migration_from_cpp_to_lua_done then + print_local("About to attempt migration from cpp to lua") + local nestboxes_status = dfhack.run_command_silent("nestboxes migrate_enabled_status") + if nestboxes_status ~= nil and string_or_int_to_boolean[nestboxes_status] then + print_local(("Migrating status %s from cpp nestboxes to lua"):format(string_or_int_to_boolean[nestboxes_status] and "enabled" or "disabled")) + state.enabled = string_or_int_to_boolean[nestboxes_status] + state.migration_from_cpp_to_lua_done = true + dfhack.persistent['deleteSiteData']("nestboxes/config") + persist_state() + else + print_local("Did not get valid response from cpp nestboxes") + end + print_local("Migrating from cpp to lua done") + end + print_details(("end load_state")) end @@ -456,15 +474,15 @@ local function set_target(target_race, target_count, count_children, count_adult if target_race_upper == "DEFAULT" then state.default = { tonumber(target_count), - stringtoboolean[count_children] or false, - stringtoboolean[count_adult] or false + string_or_int_to_boolean[count_children] or false, + string_or_int_to_boolean[count_adult] or false } elseif race >= 0 then print(race) state.target_eggs_count_per_race[race] = { tonumber(target_count), - stringtoboolean[count_children] or false, - stringtoboolean[count_adult] or false + string_or_int_to_boolean[count_children] or false, + string_or_int_to_boolean[count_adult] or false } else handle_error("must specify DEFAULT or valid creature_id") @@ -473,16 +491,12 @@ local function set_target(target_race, target_count, count_children, count_adult end if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then - dfhack.printerr("eggwatch needs a loaded fortress to work") + dfhack.printerr(GLOBAL_KEY .. " needs a loaded fortress to work") return end load_state() local args, opts = {...}, {} -if dfhack_flags and dfhack_flags.enable then - args = {dfhack_flags.enable_state and "enable" or "disable"} -end - local positionals = argparse.processArgsGetopt( args, @@ -500,10 +514,8 @@ local positionals = if dfhack_flags.enable then if dfhack_flags.enable_state then do_enable() - print_status() else do_disable() - print_status() end end @@ -515,17 +527,17 @@ elseif command == "target" then set_target(positionals[2], positionals[3], positionals[4], positionals[5]) print_status() elseif command == "verbose" then - state.verbose = not state.verbose + state.verbose = string_or_int_to_boolean[positionals[2]] print_status() elseif command == "clear" then state = get_default_state() update_event_listener() elseif command == "split_stacks" then - state.split_stacks = stringtoboolean[positionals[2]] + state.split_stacks = string_or_int_to_boolean[positionals[2]] print_status() elseif not command or command == "status" then print_status() -elseif (command ~= "enable" or command ~= "disable") and not dfhack_flags.enable then +else handle_error(("Command '% s' is not recognized"):format(command)) end persist_state() From f72441287689036954d6537195398d7f2e1c3fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Wed, 11 Sep 2024 14:05:35 +0200 Subject: [PATCH 14/22] changed how migration is handled --- eggwatch.lua | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/eggwatch.lua b/eggwatch.lua index 7cdfeed89..2abb4dbf6 100644 --- a/eggwatch.lua +++ b/eggwatch.lua @@ -87,6 +87,21 @@ local function persist_state() print_details(("end persist_state")) end +local function read_persistent_config(key, index) + return dfhack.internal.readPersistentSiteConfigInt(key, index) +end + +local function migrate_enabled_status_from_ccp_nestboxes() + print_local("About to attempt migration from cpp to lua") + local nestboxes_status = read_persistent_config("nestboxes/config", "0") + print_local(("Migrating status %s from cpp nestboxes to lua"):format(string_or_int_to_boolean[nestboxes_status] and "enabled" or "disabled")) + state.enabled = string_or_int_to_boolean[nestboxes_status] or false + state.migration_from_cpp_to_lua_done = true + dfhack.persistent['deleteSiteData']("nestboxes/config") + persist_state() + print_local("Migrating from cpp to lua done") +end + --- Load the saved state of the script local function load_state() print_details(("start load_state")) @@ -107,18 +122,7 @@ local function load_state() utils.assign(state, processed_persisted_data) if not state.migration_from_cpp_to_lua_done then - print_local("About to attempt migration from cpp to lua") - local nestboxes_status = dfhack.run_command_silent("nestboxes migrate_enabled_status") - if nestboxes_status ~= nil and string_or_int_to_boolean[nestboxes_status] then - print_local(("Migrating status %s from cpp nestboxes to lua"):format(string_or_int_to_boolean[nestboxes_status] and "enabled" or "disabled")) - state.enabled = string_or_int_to_boolean[nestboxes_status] - state.migration_from_cpp_to_lua_done = true - dfhack.persistent['deleteSiteData']("nestboxes/config") - persist_state() - else - print_local("Did not get valid response from cpp nestboxes") - end - print_local("Migrating from cpp to lua done") + migrate_enabled_status_from_ccp_nestboxes() end print_details(("end load_state")) From 91d1aa93a905287e6e41b9718672c23a5d9451ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Wed, 11 Sep 2024 18:10:47 +0200 Subject: [PATCH 15/22] rename script to nestboxes --- eggwatch.lua => nestboxes.lua | 67 +++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 30 deletions(-) rename eggwatch.lua => nestboxes.lua (98%) diff --git a/eggwatch.lua b/nestboxes.lua similarity index 98% rename from eggwatch.lua rename to nestboxes.lua index 2abb4dbf6..316710b48 100644 --- a/eggwatch.lua +++ b/nestboxes.lua @@ -1,27 +1,13 @@ + --@enable = true --@module = true + local argparse = require("argparse") local eventful = require("plugins.eventful") local utils = require("utils") -local GLOBAL_KEY = "eggwatch" +local GLOBAL_KEY = "nestboxes" local default_table = {10, false, false} -local string_or_int_to_boolean = {["true"] = true, ["false"] = false, ["1"] = true, ["0"] = false, ["Y"] = true, ["N"] = false, [1] = true, [0] = false} - -function dump(o) - if type(o) == "table" then - local s = "{ " - for k, v in pairs(o) do - if type(k) ~= "number" then - k = '"' .. k .. '"' - end - s = s .. "[" .. k .. "] = " .. dump(v) .. "," - end - return s .. "} " - else - return tostring(o) - end -end local function get_default_state() return { @@ -41,6 +27,24 @@ function isEnabled() return state.enabled end +local function dump(o) + if type(o) == "table" then + local s = "{ " + for k, v in pairs(o) do + if type(k) ~= "number" then + k = '"' .. k .. '"' + end + s = s .. "[" .. k .. "] = " .. dump(v) .. "," + end + return s .. "} " + else + return tostring(o) + end +end + +local string_or_int_to_boolean = {["true"] = true, ["false"] = false, ["1"] = true, ["0"] = false, ["Y"] = true, ["N"] = false, [1] = true, [0] = false} + + local function print_local(text) print(GLOBAL_KEY .. ": " .. text) end @@ -54,12 +58,14 @@ local function print_details(details) print_local(details) end end + local function format_target_count_row(header, row) return header .. ": " .. "target count: " .. row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) end + local function print_status() print_local((GLOBAL_KEY .. " is currently %s."):format(state.enabled and "enabled" or "disabled")) print_local(("egg stack splitting is %s"):format(state.split_stacks and "enabled" or "disabled")) @@ -91,7 +97,7 @@ local function read_persistent_config(key, index) return dfhack.internal.readPersistentSiteConfigInt(key, index) end -local function migrate_enabled_status_from_ccp_nestboxes() +local function migrate_enabled_status_from_cpp_nestboxes() print_local("About to attempt migration from cpp to lua") local nestboxes_status = read_persistent_config("nestboxes/config", "0") print_local(("Migrating status %s from cpp nestboxes to lua"):format(string_or_int_to_boolean[nestboxes_status] and "enabled" or "disabled")) @@ -122,7 +128,7 @@ local function load_state() utils.assign(state, processed_persisted_data) if not state.migration_from_cpp_to_lua_done then - migrate_enabled_status_from_ccp_nestboxes() + migrate_enabled_status_from_cpp_nestboxes() end print_details(("end load_state")) @@ -168,10 +174,6 @@ dfhack.onStateChange[GLOBAL_KEY] = function(sc) update_event_listener() end -if dfhack_flags.module then - return -end - local function is_egg(item) return df.item_type.EGG == item:getType() end @@ -499,8 +501,16 @@ if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then return end +if dfhack_flags.module then + return +end + load_state() + local args, opts = {...}, {} +if dfhack_flags and dfhack_flags.enable then + args = {dfhack_flags.enable_state and "enable" or "disable"} +end local positionals = argparse.processArgsGetopt( args, @@ -515,18 +525,15 @@ local positionals = } ) -if dfhack_flags.enable then - if dfhack_flags.enable_state then - do_enable() - else - do_disable() - end -end local command = positionals[1] if command == "help" or opts.help then print(dfhack.script_help()) +elseif command == "enable" then + do_enable() +elseif command == "disable" then + do_disable() elseif command == "target" then set_target(positionals[2], positionals[3], positionals[4], positionals[5]) print_status() From 512f980683bece2f7375dd5ae6226f8dddbc72e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Wed, 11 Sep 2024 22:21:38 +0200 Subject: [PATCH 16/22] Remove jobs related to forbidden eggs --- nestboxes.lua | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/nestboxes.lua b/nestboxes.lua index 316710b48..3355b8a1d 100644 --- a/nestboxes.lua +++ b/nestboxes.lua @@ -413,23 +413,35 @@ local function handle_eggs(eggs) print_details(("Total count for %s eggs is %s"):format(race, total_count)) if total_count - current_eggs < max_eggs then + local egg_count_to_leave_in_source_stack = current_eggs if state.split_stacks and total_count > max_eggs then - local egg_count_to_leave_in_source_stack = max_eggs - total_count + current_eggs + egg_count_to_leave_in_source_stack = max_eggs - total_count + current_eggs split_egg_stack(eggs, egg_count_to_leave_in_source_stack) end eggs.flags.forbid = true + + if eggs.flags.in_job then + local job_ref = dfhack.items.getSpecificRef(eggs, df.specific_ref_type.JOB) + if job_ref then + print_details(("About to remove job related to egg(s)")) + dfhack.job.removeJob(job_ref.data.job) + eggs.flags.in_job = false + end + end + print_local( - ("Previously existing %s eggs is %s lower than maximum %s , forbidden %s new eggs."):format( + ("Previously existing %s egg(s) is %s lower than maximum %s , forbidden %s egg(s) out of %s new"):format( race, total_count - current_eggs, max_eggs, + egg_count_to_leave_in_source_stack, current_eggs ) ) else print_local( - ("Total count for %s eggs is %s over maximum %s, newly laid eggs %s , no action taken."):format( + ("Total count for %s egg(s) is %s over maximum %s, newly laid egg(s) %s , no action taken."):format( race, total_count, max_eggs, From 9f57884201f44d24e380efc5361c62a00543d841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Thu, 12 Sep 2024 12:20:31 +0200 Subject: [PATCH 17/22] changed state variable to global to fix script not working with enable api properly --- nestboxes.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nestboxes.lua b/nestboxes.lua index 3355b8a1d..ad8abc037 100644 --- a/nestboxes.lua +++ b/nestboxes.lua @@ -1,4 +1,3 @@ - --@enable = true --@module = true @@ -21,7 +20,7 @@ local function get_default_state() } end -local state = state or get_default_state() +state = state or get_default_state() function isEnabled() return state.enabled From 30eb00263ac9fb56a0e822330339d03d489ea2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Thu, 12 Sep 2024 14:32:55 +0200 Subject: [PATCH 18/22] add possibility to ignore race --- nestboxes.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/nestboxes.lua b/nestboxes.lua index ad8abc037..2e51387d3 100644 --- a/nestboxes.lua +++ b/nestboxes.lua @@ -5,8 +5,8 @@ local argparse = require("argparse") local eventful = require("plugins.eventful") local utils = require("utils") -local GLOBAL_KEY = "nestboxes" -local default_table = {10, false, false} +local GLOBAL_KEY = "eggwatch" +local default_table = {10, false, false, false} local function get_default_state() return { @@ -379,6 +379,12 @@ local function handle_eggs(eggs) local max_eggs = race_config[1] local count_children = race_config[2] local count_adults = race_config[3] + local ignore = race_config[4] + + if ignore then + print_details(("race is ignored, nothing to do here")) + return + end if; print_details(("max_eggs %s "):format(max_eggs)) print_details(("count_children %s "):format(count_children)) @@ -475,7 +481,7 @@ local function validate_creature_id(creature_id) return -1 end -local function set_target(target_race, target_count, count_children, count_adult) +local function set_target(target_race, target_count, count_children, count_adult, ignore) print_details(("start set_target")) if target_race == nil or target_race == "" then @@ -492,14 +498,16 @@ local function set_target(target_race, target_count, count_children, count_adult state.default = { tonumber(target_count), string_or_int_to_boolean[count_children] or false, - string_or_int_to_boolean[count_adult] or false + string_or_int_to_boolean[count_adult] or false, + string_or_int_to_boolean[ignore] or false } elseif race >= 0 then print(race) state.target_eggs_count_per_race[race] = { tonumber(target_count), string_or_int_to_boolean[count_children] or false, - string_or_int_to_boolean[count_adult] or false + string_or_int_to_boolean[count_adult] or false, + string_or_int_to_boolean[ignore] or false } else handle_error("must specify DEFAULT or valid creature_id") @@ -546,7 +554,7 @@ elseif command == "enable" then elseif command == "disable" then do_disable() elseif command == "target" then - set_target(positionals[2], positionals[3], positionals[4], positionals[5]) + set_target(positionals[2], positionals[3], positionals[4], positionals[5], positionals[6]) print_status() elseif command == "verbose" then state.verbose = string_or_int_to_boolean[positionals[2]] From 18db95f0294526b362fab8f637fbd43079fb5cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Thu, 12 Sep 2024 14:40:23 +0200 Subject: [PATCH 19/22] fix if checking for ignored race, handle possibility of not existhing function to read persistent site config int --- nestboxes.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nestboxes.lua b/nestboxes.lua index 2e51387d3..1b2136e00 100644 --- a/nestboxes.lua +++ b/nestboxes.lua @@ -93,7 +93,11 @@ local function persist_state() end local function read_persistent_config(key, index) - return dfhack.internal.readPersistentSiteConfigInt(key, index) + if dfhack.internal.readPersistentSiteConfigInt ~= nil then + return dfhack.internal.readPersistentSiteConfigInt(key, index) + else + return nil + end end local function migrate_enabled_status_from_cpp_nestboxes() @@ -384,7 +388,7 @@ local function handle_eggs(eggs) if ignore then print_details(("race is ignored, nothing to do here")) return - end if; + end print_details(("max_eggs %s "):format(max_eggs)) print_details(("count_children %s "):format(count_children)) From 09570d456d72b49cbe8437e53c85582c780dd552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Thu, 12 Sep 2024 17:12:19 +0200 Subject: [PATCH 20/22] fix handling for new default values if added, add information about race being ignored to status --- nestboxes.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nestboxes.lua b/nestboxes.lua index 1b2136e00..d2c6507ca 100644 --- a/nestboxes.lua +++ b/nestboxes.lua @@ -62,7 +62,7 @@ local function format_target_count_row(header, row) return header .. ": " .. "target count: " .. - row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) + row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) .. "; ignore race: " .. tostring(row[4]) end local function print_status() @@ -93,9 +93,9 @@ local function persist_state() end local function read_persistent_config(key, index) - if dfhack.internal.readPersistentSiteConfigInt ~= nil then - return dfhack.internal.readPersistentSiteConfigInt(key, index) - else + if dfhack.internal.readPersistentSiteConfigInt ~= nil then + return dfhack.internal.readPersistentSiteConfigInt(key, index) + else return nil end end @@ -122,7 +122,8 @@ local function load_state() processed_persisted_data.target_eggs_count_per_race = {} if persisted_data.target_eggs_count_per_race ~= nil then for k, v in pairs(persisted_data.target_eggs_count_per_race) do - processed_persisted_data.target_eggs_count_per_race[tonumber(k)] = v + local default = utils.clone(default_table) + processed_persisted_data.target_eggs_count_per_race[tonumber(k)] = utils.assign(default, v) end end end From d35e3bb858a0189a00dc82523a61dd47888972be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Fri, 13 Sep 2024 15:30:38 +0200 Subject: [PATCH 21/22] split into modules --- internal/nestboxes/common.lua | 18 ++ internal/nestboxes/event.lua | 267 +++++++++++++++++ nestboxes.lua | 537 ++++++++++------------------------ 3 files changed, 441 insertions(+), 381 deletions(-) create mode 100644 internal/nestboxes/common.lua create mode 100644 internal/nestboxes/event.lua diff --git a/internal/nestboxes/common.lua b/internal/nestboxes/common.lua new file mode 100644 index 000000000..1e416f052 --- /dev/null +++ b/internal/nestboxes/common.lua @@ -0,0 +1,18 @@ +-- common logic for the nestboxes modules +--@ module = true +verbose = verbose or nil +GLOBAL_KEY = GLOBAL_KEY or "" + +function print_local(text) + print(GLOBAL_KEY .. ": " .. text) +end +--------------------------------------------------------------------------------------------------- +function handle_error(text) + qerror(GLOBAL_KEY .. ": " .. text) +end +--------------------------------------------------------------------------------------------------- +function print_details(text) + if verbose then + print_local(text) + end +end diff --git a/internal/nestboxes/event.lua b/internal/nestboxes/event.lua new file mode 100644 index 000000000..323bcc2a8 --- /dev/null +++ b/internal/nestboxes/event.lua @@ -0,0 +1,267 @@ +-- event logic for the nestboxes +--@ module = true +local utils = require("utils") +local nestboxes_common = reqscript("internal/nestboxes/common") +local print_local = nestboxes_common.print_local +local print_details = nestboxes_common.print_details +local handle_error = nestboxes_common.handle_error +--------------------------------------------------------------------------------------------------- +--ITEM_CREATED event handling functions +local function copy_egg_fields(source_egg, target_egg) + print_details("start copy_egg_fields") + target_egg.incubation_counter = source_egg.incubation_counter + print_details("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) + print_details("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) + print_details("hatchling_animal_population done") + target_egg.hatchling_mother_id = source_egg.hatchling_mother_id + print_details("hatchling_mother_id done") + target_egg.mother_hf = source_egg.mother_hf + target_egg.father_hf = source_egg.mother_hf + print_details("mother_hf father_hf done") + target_egg.mothers_caste = source_egg.mothers_caste + target_egg.fathers_caste = source_egg.fathers_caste + print_details("mothers_caste fathers_caste done") + target_egg.mothers_genes = source_egg.mothers_genes + target_egg.fathers_genes = source_egg.fathers_genes + print_details("mothers_genes fathers_genes done") + target_egg.hatchling_civ_id = source_egg.hatchling_civ_id + print_details("hatchling_civ_id done") + print_details("end copy_egg_fields") +end +--------------------------------------------------------------------------------------------------- +local function resize_egg_stack(egg_stack, new_stack_size) + print_details("start resize_egg_stack") + egg_stack.stack_size = new_stack_size + --TODO check if weight or size need adjustment + print_details("end resize_egg_stack") +end +--------------------------------------------------------------------------------------------------- +local function create_new_egg_stack(original_eggs, new_stack_count) + print_details("start create_new_egg_stack") + print_details("about to split create new egg stack") + print_details(("type= %s"):format(original_eggs:getType())) + print_details(("creature= %s"):format(original_eggs.race)) + print_details(("caste= %s "):format(original_eggs.caste)) + print_details(("stack size for new eggs = %s "):format(new_stack_count)) + + local created_items = + dfhack.items.createItem( + df.unit.find(original_eggs.hatchling_mother_id), + original_eggs:getType(), + -1, + original_eggs.race, + original_eggs.caste + ) + print_details("created new egg stack") + local created_egg_stack = created_items[0] or created_items[1] + print_details(df.creature_raw.find(created_egg_stack.race).creature_id) + print_details("about to copy fields from orginal eggs") + copy_egg_fields(original_eggs, created_egg_stack) + + print_details("about to resize new egg stack") + resize_egg_stack(created_egg_stack, new_stack_count) + + print_details("about to move new stack to nestbox") + if dfhack.items.moveToBuilding(created_egg_stack, dfhack.items.getHolderBuilding(original_eggs)) then + print_details("moved new egg stack to nestbox") + else + print_local("move of separated eggs to nestbox failed") + end + print_details("end create_new_egg_stack") +end +--------------------------------------------------------------------------------------------------- +local function split_egg_stack(source_egg_stack, to_be_left_in_source_stack) + print_details("start split_egg_stack") + local egg_count_in_new_stack_size = source_egg_stack.stack_size - to_be_left_in_source_stack + if egg_count_in_new_stack_size > 0 then + create_new_egg_stack(source_egg_stack, egg_count_in_new_stack_size) + resize_egg_stack(source_egg_stack, to_be_left_in_source_stack) + else + print_details("nothing to do, wrong egg_count_in_new_stack_size") + end + print_details("end split_egg_stack") +end +--------------------------------------------------------------------------------------------------- +local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race) + print_details(("start count_forbidden_eggs_for_race_in_claimed_nestobxes")) + local eggs_count = 0 + for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do + if nestbox.claimed_by ~= -1 then + print_details(("Found claimed nextbox")) + for _, nestbox_contained_item in ipairs(nestbox.contained_items) do + if nestbox_contained_item.use_mode == df.building_item_role_type.TEMP then + print_details(("Found claimed nextbox containing items")) + if df.item_type.EGG == nestbox_contained_item.item:getType() then + print_details(("Found claimed nextbox containing items that are eggs")) + if nestbox_contained_item.item.egg_flags.fertile and nestbox_contained_item.item.flags.forbid then + print_details(("Eggs are fertile and forbidden")) + if nestbox_contained_item.item.race == race then + print_details(("Eggs belong to %s"):format(race)) + print_details( + ("eggs_count %s + new %s"):format( + eggs_count, + nestbox_contained_item.item.stack_size + ) + ) + eggs_count = eggs_count + nestbox_contained_item.item.stack_size + print_details(("eggs_count after adding current nestbox %s "):format(eggs_count)) + end + end + end + end + end + end + end + print_details(("end count_forbidden_eggs_for_race_in_claimed_nestobxes")) + return eggs_count +end +--------------------------------------------------------------------------------------------------- +local function is_valid_animal(unit) + return unit and dfhack.units.isActive(unit) and dfhack.units.isAnimal(unit) and dfhack.units.isFortControlled(unit) and + dfhack.units.isTame(unit) and + not dfhack.units.isDead(unit) +end +--------------------------------------------------------------------------------------------------- +local function count_live_animals(race, count_children, count_adults) + print_details(("start count_live_animals")) + if count_adults then + print_details(("we are counting adults for %s"):format(race)) + end + if count_children then + print_details(("we are counting children and babies for %s"):format(race)) + end + + local count = 0 + if not count_adults and not count_children then + print_details(("end 1 count_live_animals")) + return count + end + + for _, unit in ipairs(df.global.world.units.active) do + if + race == unit.race and is_valid_animal(unit) and + ((count_adults and dfhack.units.isAdult(unit)) or + (count_children and (dfhack.units.isChild(unit) or dfhack.units.isBaby(unit)))) + then + count = count + 1 + end + end + print_details(("found %s life animals"):format(count)) + print_details(("end 2 count_live_animals")) + return count +end +--------------------------------------------------------------------------------------------------- +function validate_eggs(eggs) + if not eggs.egg_flags.fertile then + print_details("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 + print_details("Found nestbox, continue with egg handling") + return true + end + end + print_details("Newly laid eggs are in building different than nestbox, we were to late") + return false + else + print_details("Newly laid eggs are not in building, we were to late") + return false + end + return true +end +--------------------------------------------------------------------------------------------------- +function handle_eggs(eggs, race_config, split_stacks) + print_details(("start handle_eggs")) + + local race = eggs.race + local max_eggs = race_config[1] + local count_children = race_config[2] + local count_adults = race_config[3] + local ignore = race_config[4] + + if ignore then + print_details(("race is ignored, nothing to do here")) + return + end + + print_details(("max_eggs %s "):format(max_eggs)) + print_details(("count_children %s "):format(count_children)) + print_details(("count_adults %s "):format(count_adults)) + + local current_eggs = eggs.stack_size + + local total_count = current_eggs + total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race) + + if total_count - current_eggs < max_eggs then + print_details( + ("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format( + race, + total_count - current_eggs + ) + ) + total_count = total_count + count_live_animals(race, count_children, count_adults) + else + print_details( + ("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format( + race, + total_count, + max_eggs + ) + ) + print_details(("end 1 handle_eggs")) + return + end + + print_details(("Total count for %s eggs is %s"):format(race, total_count)) + + if total_count - current_eggs < max_eggs then + local egg_count_to_leave_in_source_stack = current_eggs + if split_stacks and total_count > max_eggs then + egg_count_to_leave_in_source_stack = max_eggs - total_count + current_eggs + split_egg_stack(eggs, egg_count_to_leave_in_source_stack) + end + + eggs.flags.forbid = true + + if eggs.flags.in_job then + local job_ref = dfhack.items.getSpecificRef(eggs, df.specific_ref_type.JOB) + if job_ref then + print_details(("About to remove job related to egg(s)")) + dfhack.job.removeJob(job_ref.data.job) + eggs.flags.in_job = false + end + end + print_local( + ("Previously existing %s egg(s) is %s lower than maximum %s , forbidden %s egg(s) out of %s new"):format( + race, + total_count - current_eggs, + max_eggs, + egg_count_to_leave_in_source_stack, + current_eggs + ) + ) + else + print_local( + ("Total count for %s egg(s) is %s over maximum %s, newly laid egg(s) %s , no action taken."):format( + race, + total_count, + max_eggs, + current_eggs + ) + ) + end + print_details(("end 2 handle_eggs")) +end +--------------------------------------------------------------------------------------------------- diff --git a/nestboxes.lua b/nestboxes.lua index d2c6507ca..15a5b57de 100644 --- a/nestboxes.lua +++ b/nestboxes.lua @@ -4,10 +4,23 @@ local argparse = require("argparse") local eventful = require("plugins.eventful") local utils = require("utils") - -local GLOBAL_KEY = "eggwatch" +local nestboxes_common = reqscript("internal/nestboxes/common") +local print_local = nestboxes_common.print_local +local print_details = nestboxes_common.print_details +local handle_error = nestboxes_common.handle_error +local GLOBAL_KEY = "nestboxes" local default_table = {10, false, false, false} - +local string_or_int_to_boolean = { + ["true"] = true, + ["false"] = false, + ["1"] = true, + ["0"] = false, + ["Y"] = true, + ["N"] = false, + [1] = true, + [0] = false +} +--------------------------------------------------------------------------------------------------- local function get_default_state() return { enabled = false, @@ -21,64 +34,14 @@ local function get_default_state() end state = state or get_default_state() - +-- isEnabled added for enabled API function isEnabled() return state.enabled end - -local function dump(o) - if type(o) == "table" then - local s = "{ " - for k, v in pairs(o) do - if type(k) ~= "number" then - k = '"' .. k .. '"' - end - s = s .. "[" .. k .. "] = " .. dump(v) .. "," - end - return s .. "} " - else - return tostring(o) - end -end - -local string_or_int_to_boolean = {["true"] = true, ["false"] = false, ["1"] = true, ["0"] = false, ["Y"] = true, ["N"] = false, [1] = true, [0] = false} - - -local function print_local(text) - print(GLOBAL_KEY .. ": " .. text) -end - -local function handle_error(text) - qerror(text) -end - -local function print_details(details) - if state.verbose then - print_local(details) - end -end - -local function format_target_count_row(header, row) - return header .. - ": " .. - "target count: " .. - row[1] .. "; count children: " .. tostring(row[2]) .. "; count adults: " .. tostring(row[3]) .. "; ignore race: " .. tostring(row[4]) -end - -local function print_status() - print_local((GLOBAL_KEY .. " is currently %s."):format(state.enabled and "enabled" or "disabled")) - print_local(("egg stack splitting is %s"):format(state.split_stacks and "enabled" or "disabled")) - print_local(format_target_count_row("Default", state.default)) - if state.target_eggs_count_per_race ~= nil then - for k, v in pairs(state.target_eggs_count_per_race) do - print_local(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) - end - end - print_details("verbose mode enabled") - print_details(dump(state)) -end - -local function persist_state() +--------------------------------------------------------------------------------------------------- +--------------------------------------------------------------------------------------------------- +-- +function persist_state() print_details(("start persist_state")) local state_to_persist = {} state_to_persist = utils.clone(state) @@ -91,7 +54,7 @@ local function persist_state() dfhack.persistent.saveSiteData(GLOBAL_KEY, state_to_persist) print_details(("end persist_state")) end - +--------------------------------------------------------------------------------------------------- local function read_persistent_config(key, index) if dfhack.internal.readPersistentSiteConfigInt ~= nil then return dfhack.internal.readPersistentSiteConfigInt(key, index) @@ -99,18 +62,27 @@ local function read_persistent_config(key, index) return nil end end - +--------------------------------------------------------------------------------------------------- local function migrate_enabled_status_from_cpp_nestboxes() print_local("About to attempt migration from cpp to lua") local nestboxes_status = read_persistent_config("nestboxes/config", "0") - print_local(("Migrating status %s from cpp nestboxes to lua"):format(string_or_int_to_boolean[nestboxes_status] and "enabled" or "disabled")) + print_local( + ("Migrating status %s from cpp nestboxes to lua"):format( + string_or_int_to_boolean[nestboxes_status] and "enabled" or "disabled" + ) + ) state.enabled = string_or_int_to_boolean[nestboxes_status] or false state.migration_from_cpp_to_lua_done = true - dfhack.persistent['deleteSiteData']("nestboxes/config") + dfhack.persistent["deleteSiteData"]("nestboxes/config") persist_state() print_local("Migrating from cpp to lua done") end - +--------------------------------------------------------------------------------------------------- +local function init_nestboxes_common() + nestboxes_common.verbose = state.verbose + nestboxes_common.GLOBAL_KEY = GLOBAL_KEY +end +--------------------------------------------------------------------------------------------------- --- Load the saved state of the script local function load_state() print_details(("start load_state")) @@ -135,9 +107,10 @@ local function load_state() migrate_enabled_status_from_cpp_nestboxes() end + init_nestboxes_common() print_details(("end load_state")) end - +--------------------------------------------------------------------------------------------------- local function update_event_listener() print_details(("start update_event_listener")) if state.enabled then @@ -150,155 +123,24 @@ local function update_event_listener() end print_details(("end update_event_listener")) end - +--------------------------------------------------------------------------------------------------- local function do_enable() print_details(("start do_enable")) state.enabled = true update_event_listener() print_details(("end do_enable")) end - +--------------------------------------------------------------------------------------------------- local function do_disable() print_details(("start do_disable")) state.enabled = false update_event_listener() print_details(("end do_disable")) end - -dfhack.onStateChange[GLOBAL_KEY] = function(sc) - if sc == SC_MAP_UNLOADED then - do_disable() - return - end - if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then - return - end - load_state() - print_status() - update_event_listener() -end - -local function is_egg(item) - return df.item_type.EGG == item:getType() -end - -local function copy_egg_fields(source_egg, target_egg) - print_details("start copy_egg_fields") - target_egg.incubation_counter = source_egg.incubation_counter - print_details("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) - print_details("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) - print_details("hatchling_animal_population done") - target_egg.hatchling_mother_id = source_egg.hatchling_mother_id - print_details("hatchling_mother_id done") - target_egg.mother_hf = source_egg.mother_hf - target_egg.father_hf = source_egg.mother_hf - print_details("mother_hf father_hf done") - target_egg.mothers_caste = source_egg.mothers_caste - target_egg.fathers_caste = source_egg.fathers_caste - print_details("mothers_caste fathers_caste done") - target_egg.mothers_genes = source_egg.mothers_genes - target_egg.fathers_genes = source_egg.fathers_genes - print_details("mothers_genes fathers_genes done") - target_egg.hatchling_civ_id = source_egg.hatchling_civ_id - print_details("hatchling_civ_id done") - print_details("end copy_egg_fields") -end - -local function resize_egg_stack(egg_stack, new_stack_size) - print_details("start resize_egg_stack") - egg_stack.stack_size = new_stack_size - --TODO check if weight or size need adjustment - print_details("end resize_egg_stack") -end - -local function create_new_egg_stack(original_eggs, remaining_eggs) - print_details("start create_new_egg_stack") - - print_details("about to split create new egg stack") - print_details(("type= %s"):format(original_eggs:getType())) - print_details(("creature= %s"):format(original_eggs.race)) - print_details(("caste= %s "):format(original_eggs.caste)) - print_details(remaining_eggs) - - local created_items = - dfhack.items.createItem( - df.unit.find(original_eggs.hatchling_mother_id), - original_eggs:getType(), - -1, - original_eggs.race, - original_eggs.caste - ) - print_details("created new egg stack") - local created_egg_stack = created_items[0] or created_items[1] - print_details(df.creature_raw.find(created_egg_stack.race).creature_id) - print_details("about to copy fields from orginal eggs") - copy_egg_fields(original_eggs, created_egg_stack) - - print_details("about to resize new egg stack") - resize_egg_stack(created_egg_stack, remaining_eggs) - - print_details("about to move new stack to nestbox") - if dfhack.items.moveToBuilding(created_egg_stack, dfhack.items.getHolderBuilding(original_eggs)) then - print_details("moved new egg stack to nestbox") - else - print_local("move of separated eggs to nestbox failed") - end - print_details("end create_new_egg_stack") -end - -local function split_egg_stack(source_egg_stack, to_be_left_in_source_stack) - print_details("start split_egg_stack") - local egg_count_in_new_stack_size = source_egg_stack.stack_size - to_be_left_in_source_stack - if egg_count_in_new_stack_size > 0 then - create_new_egg_stack(source_egg_stack, egg_count_in_new_stack_size) - resize_egg_stack(source_egg_stack, to_be_left_in_source_stack) - else - print_details("nothing to do, wrong egg_count_in_new_stack_size") - end - print_details("end split_egg_stack") -end - -local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race) - print_details(("start count_forbidden_eggs_for_race_in_claimed_nestobxes")) - local eggs_count = 0 - for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do - if nestbox.claimed_by ~= -1 then - print_details(("Found claimed nextbox")) - for _, nestbox_contained_item in ipairs(nestbox.contained_items) do - if nestbox_contained_item.use_mode == df.building_item_role_type.TEMP then - print_details(("Found claimed nextbox containing items")) - if df.item_type.EGG == nestbox_contained_item.item:getType() then - print_details(("Found claimed nextbox containing items that are eggs")) - if nestbox_contained_item.item.egg_flags.fertile and nestbox_contained_item.item.flags.forbid then - print_details(("Eggs are fertile and forbidden")) - if nestbox_contained_item.item.race == race then - print_details(("Eggs belong to %s"):format(race)) - print_details( - ("eggs_count %s + new %s"):format( - eggs_count, - nestbox_contained_item.item.stack_size - ) - ) - eggs_count = eggs_count + nestbox_contained_item.item.stack_size - print_details(("eggs_count after adding current nestbox %s "):format(eggs_count)) - end - end - end - end - end - end - end - print_details(("end count_forbidden_eggs_for_race_in_claimed_nestobxes")) - return eggs_count -end - +-- +--------------------------------------------------------------------------------------------------- +--------------------------------------------------------------------------------------------------- +-- local function get_config_for_race(race) print_details(("start get_config_for_race")) print_details(("getting config for race %s "):format(race)) @@ -313,167 +155,24 @@ local function get_config_for_race(race) print_details(("end 2 get_config_for_race")) return state.target_eggs_count_per_race[race] end - -local function is_valid_animal(unit) - return unit and dfhack.units.isActive(unit) and dfhack.units.isAnimal(unit) and dfhack.units.isFortControlled(unit) and - dfhack.units.isTame(unit) and - not dfhack.units.isDead(unit) -end - -local function count_live_animals(race, count_children, count_adults) - print_details(("start count_live_animals")) - if count_adults then - print_details(("we are counting adults for %s"):format(race)) - end - if count_children then - print_details(("we are counting children and babies for %s"):format(race)) - end - - local count = 0 - if not count_adults and not count_children then - print_details(("end 1 count_live_animals")) - return count - end - - for _, unit in ipairs(df.global.world.units.active) do - if - race == unit.race and is_valid_animal(unit) and - ((count_adults and dfhack.units.isAdult(unit)) or - (count_children and (dfhack.units.isChild(unit) or dfhack.units.isBaby(unit)))) - then - count = count + 1 - end - end - print_details(("found %s life animals"):format(count)) - print_details(("end 2 count_live_animals")) - return count -end - -local function validate_eggs(eggs) - if not eggs.egg_flags.fertile then - print_details("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 - print_details("Found nestbox, continue with egg handling") - return true - end - end - print_details("Newly laid eggs are in building different than nestbox, we were to late") - return false - else - print_details("Newly laid eggs are not in building, we were to late") - return false - end - return true -end - -local function handle_eggs(eggs) - print_details(("start handle_eggs")) - - if not validate_eggs(eggs) then - return - end - - local race = eggs.race - local race_config = get_config_for_race(race) - local max_eggs = race_config[1] - local count_children = race_config[2] - local count_adults = race_config[3] - local ignore = race_config[4] - - if ignore then - print_details(("race is ignored, nothing to do here")) - return - end - - print_details(("max_eggs %s "):format(max_eggs)) - print_details(("count_children %s "):format(count_children)) - print_details(("count_adults %s "):format(count_adults)) - - local current_eggs = eggs.stack_size - - local total_count = current_eggs - total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race) - - if total_count - current_eggs < max_eggs then - print_details( - ("Total count for %s only existing eggs is %s, about to count life animals if enabled"):format( - race, - total_count - current_eggs - ) - ) - total_count = total_count + count_live_animals(race, count_children, count_adults) - else - print_details( - ("Total count for %s eggs only is %s greater than maximum %s, no need to count life animals"):format( - race, - total_count, - max_eggs - ) - ) - print_details(("end 1 handle_eggs")) - return - end - - print_details(("Total count for %s eggs is %s"):format(race, total_count)) - - if total_count - current_eggs < max_eggs then - local egg_count_to_leave_in_source_stack = current_eggs - if state.split_stacks and total_count > max_eggs then - egg_count_to_leave_in_source_stack = max_eggs - total_count + current_eggs - split_egg_stack(eggs, egg_count_to_leave_in_source_stack) - end - - eggs.flags.forbid = true - - if eggs.flags.in_job then - local job_ref = dfhack.items.getSpecificRef(eggs, df.specific_ref_type.JOB) - if job_ref then - print_details(("About to remove job related to egg(s)")) - dfhack.job.removeJob(job_ref.data.job) - eggs.flags.in_job = false - end - end - - print_local( - ("Previously existing %s egg(s) is %s lower than maximum %s , forbidden %s egg(s) out of %s new"):format( - race, - total_count - current_eggs, - max_eggs, - egg_count_to_leave_in_source_stack, - current_eggs - ) - ) - else - print_local( - ("Total count for %s egg(s) is %s over maximum %s, newly laid egg(s) %s , no action taken."):format( - race, - total_count, - max_eggs, - current_eggs - ) - ) - end - print_details(("end 2 handle_eggs")) -end - +--------------------------------------------------------------------------------------------------- +-- check_item_created function, called from eventfful on ITEM_CREATED event function check_item_created(item_id) - --print_details(("start check_item_created")) local item = df.item.find(item_id) - if not item or not is_egg(item) then - --print_details(("end 1 check_item_created")) + if item == nil or df.item_type.EGG ~= item:getType() then return + else + local nestboxes_event = reqscript("internal/nestboxes/event") + if nestboxes_event.validate_eggs(item) then + local race_config = get_config_for_race(item.race) + nestboxes_event.handle_eggs(item, race_config, state.split_stacks) + end end - --print_local(("item_id for original eggs: %s"):format (item_id)) - handle_eggs(item) - --print_details(("end 2 check_item_created")) end - +-- +--------------------------------------------------------------------------------------------------- +--------------------------------------------------------------------------------------------------- +-- local function validate_creature_id(creature_id) print_details(("start validate_creature_id")) for i, c in ipairs(df.global.world.raws.creatures.all) do @@ -485,7 +184,7 @@ local function validate_creature_id(creature_id) print_details(("end 2 validate_creature_id")) return -1 end - +--------------------------------------------------------------------------------------------------- local function set_target(target_race, target_count, count_children, count_adult, ignore) print_details(("start set_target")) @@ -519,22 +218,99 @@ local function set_target(target_race, target_count, count_children, count_adult end print_details(("end set_target")) end +--------------------------------------------------------------------------------------------------- +local function set_split_stacks(value) + state.split_stacks = string_or_int_to_boolean[value] +end +--------------------------------------------------------------------------------------------------- +local function clear_config(value) + state = get_default_state() + update_event_listener() +end +--------------- +local function set_verbose(value) + state.verbose = string_or_int_to_boolean[value] + nestboxes_common.verbose = state.verbose +end +-- +--------------------------------------------------------------------------------------------------- +--------------------------------------------------------------------------------------------------- +-- +local function dump(o) + if type(o) == "table" then + local s = "{ " + for k, v in pairs(o) do + if type(k) ~= "number" then + k = '"' .. k .. '"' + end + s = s .. "[" .. k .. "] = " .. dump(v) .. "," + end + return s .. "} " + else + return tostring(o) + end +end +--------------------------------------------------------------------------------------------------- +local function format_target_count_row(category, row) + return category .. + ": " .. + "target count: " .. + row[1] .. + "; count children: " .. + tostring(row[2]) .. + "; count adults: " .. tostring(row[3]) .. "; ignore race: " .. tostring(row[4]) +end +--------------------------------------------------------------------------------------------------- +local function print_status() + print_local((GLOBAL_KEY .. " is currently %s."):format(state.enabled and "enabled" or "disabled")) + print_local(("Egg stack splitting is %s"):format(state.split_stacks and "enabled" or "disabled")) + print_local(format_target_count_row("Default", state.default)) + if state.target_eggs_count_per_race ~= nil then + for k, v in pairs(state.target_eggs_count_per_race) do + print_local(format_target_count_row(df.global.world.raws.creatures.all[k].creature_id, v)) + end + end + print_details("verbose mode enabled") + print_details(dump(state)) +end +-- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +--------------------------------------------------------------------------------------------------- -if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then - dfhack.printerr(GLOBAL_KEY .. " needs a loaded fortress to work") +if dfhack_flags.module then return end -if dfhack_flags.module then +dfhack.onStateChange[GLOBAL_KEY] = function(sc) + if sc == SC_MAP_UNLOADED then + do_disable() + if state.unforbid_eggs then + repeatutil.cancel(GLOBAL_KEY) + end + return + end + if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then + return + end + load_state() + print_status() + update_event_listener() + schedule_loop() +end + +if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then + dfhack.printerr(GLOBAL_KEY .. " needs a loaded fortress to work") return end load_state() local args, opts = {...}, {} + if dfhack_flags and dfhack_flags.enable then - args = {dfhack_flags.enable_state and "enable" or "disable"} + args = {dfhack_flags.enable_state and "ENABLE" or "DISABLE"} end + local positionals = argparse.processArgsGetopt( args, @@ -549,30 +325,29 @@ local positionals = } ) - local command = positionals[1] -if command == "help" or opts.help then +if command ~= nil then + command = string.upper(command) +end + +if command == "HELP" or opts.help then print(dfhack.script_help()) -elseif command == "enable" then +elseif command == "ENABLE" then do_enable() -elseif command == "disable" then +elseif command == "DISABLE" then do_disable() -elseif command == "target" then +elseif command == "TARGET" then set_target(positionals[2], positionals[3], positionals[4], positionals[5], positionals[6]) - print_status() -elseif command == "verbose" then - state.verbose = string_or_int_to_boolean[positionals[2]] - print_status() -elseif command == "clear" then - state = get_default_state() - update_event_listener() -elseif command == "split_stacks" then - state.split_stacks = string_or_int_to_boolean[positionals[2]] - print_status() -elseif not command or command == "status" then - print_status() -else - handle_error(("Command '% s' is not recognized"):format(command)) +elseif command == "VERBOSE" then + set_verbose(positionals[2]) +elseif command == "CLEAR" then + clear_config() +elseif command == "SPLIT_STACKS" then + set_split_stacks(positionals[2]) +elseif positionals[1] ~= nil then + handle_error(("Command '% s' is not recognized"):format(positionals[1])) end + +print_status() persist_state() From 3d5a6093e048196be885a38c794840f2bcc0f94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ryszard=20Panto=C5=82?= Date: Fri, 13 Sep 2024 18:07:33 +0200 Subject: [PATCH 22/22] move event frequency to local variable, add doc --- docs/nestboxes.rst | 43 +++++++++++++++++++++++++++++++++++++++++++ nestboxes.lua | 6 +++--- 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 docs/nestboxes.rst diff --git a/docs/nestboxes.rst b/docs/nestboxes.rst new file mode 100644 index 000000000..d53f82353 --- /dev/null +++ b/docs/nestboxes.rst @@ -0,0 +1,43 @@ +nestboxes +========= + +.. dfhack-tool:: + :summary: Protect fertile eggs incubating in a nestbox. + :tags: fort auto animals + +This script will automatically check newly laid fertile eggs, compare with limit according to configuration. Any newly laid fertile eggs below limit will be forbidden so that dwarves won't come to collect them for eating. The eggs will hatch normally, even when forbidden. Adult and/or child animals can be included in check against limit. Race can be marked as ignored by script. + +Usage +----- + +:: + + ``enable nestboxes`` + + ``disable nestboxes`` + + ``nestboxes target `` + + target command allows to change how script handles specyfic animal race, or DEFAULT settings for new animal spiecies. + Default settings are assigned first time fertile egg is found for given race. either "DEFAULT" or creature_id. boolean if children for specified race should be added to count of existing forbidden eggs. boolean if adults for specified race should be added to count of existing forbidden eggs. boolean if race should be ignored by script. + Script will accept "true"/"false", "0"/"1", "Y"/"N" as boolean values. If not specified value will be set to false. + Domestic egglayers have folowing creature_id(s): BIRD_CHICKEN, BIRD_DUCK, BIRD_GOOSE, BIRD_GUINEAFOWL, BIRD_PEAFOWL_BLUE, BIRD_TURKEY. + + ``nestboxes split_stacks `` + split_stacks command allows to specify how egg stacks that are only partialy over limit should be handled. If set to false whole stack will be forbidden. If set to true only eggs below limit will be forbidden, remaining part of stack will be separated and left for dwarves to collect. + +Examples +-------- + + ``nestboxes target BEAK_DOG 30 1 1 0`` + command sets limit for beak dogs to 30 including adult and children animals, race is not ignored + + ``nestboxes target BIRD_TURKEY 10`` + command sets limit for turkeys to 10, count of live animals will not be checked, race is not ignored + + ``nestboxes target BIRD_GOOSE 10 1 1 1`` + command marks geese as ignored, geeseeggs will not be checked by script at all + + ``nestboxes target BIRD_GOOSE 10 1 1 1`` + command marks geese as ignored, geeseeggs will not be checked by script at all + diff --git a/nestboxes.lua b/nestboxes.lua index 15a5b57de..191fddeef 100644 --- a/nestboxes.lua +++ b/nestboxes.lua @@ -9,6 +9,7 @@ local print_local = nestboxes_common.print_local local print_details = nestboxes_common.print_details local handle_error = nestboxes_common.handle_error local GLOBAL_KEY = "nestboxes" +local EVENT_FREQ = 7 local default_table = {10, false, false, false} local string_or_int_to_boolean = { ["true"] = true, @@ -26,7 +27,6 @@ local function get_default_state() enabled = false, verbose = false, default = default_table, - EVENT_FREQ = 7, split_stacks = true, migration_from_cpp_to_lua_done = false, target_eggs_count_per_race = {} @@ -114,9 +114,9 @@ end local function update_event_listener() print_details(("start update_event_listener")) if state.enabled then - eventful.enableEvent(eventful.eventType.ITEM_CREATED, state.EVENT_FREQ) + eventful.enableEvent(eventful.eventType.ITEM_CREATED, EVENT_FREQ) eventful.onItemCreated[GLOBAL_KEY] = check_item_created - print_local(("Subscribing in eventful for %s with frequency %s"):format("ITEM_CREATED", state.EVENT_FREQ)) + print_local(("Subscribing in eventful for %s with frequency %s"):format("ITEM_CREATED", EVENT_FREQ)) else eventful.onItemCreated[GLOBAL_KEY] = nil print_local(("Unregistering from eventful for %s"):format("ITEM_CREATED"))