|
| 1 | +local argparse = require("argparse") |
| 2 | +local eventful = require("plugins.eventful") |
| 3 | + |
| 4 | +local GLOBAL_KEY = "eggwatch" |
| 5 | +local EVENT_FREQ = 5 |
| 6 | +local print_prefix = "eggwatch: " |
| 7 | + |
| 8 | +enabled = enabled or false |
| 9 | +default_table = {} |
| 10 | +default_table.DEFAULT = 10 |
| 11 | +function isEnabled() |
| 12 | + return enabled |
| 13 | +end |
| 14 | + |
| 15 | +local function persist_state() |
| 16 | + dfhack.persistent.saveSiteData( |
| 17 | + GLOBAL_KEY, |
| 18 | + { |
| 19 | + enabled = enabled, |
| 20 | + verbose = verbose, |
| 21 | + target_eggs_count_per_race = target_eggs_count_per_race |
| 22 | + } |
| 23 | + ) |
| 24 | +end |
| 25 | + |
| 26 | +--- Load the saved state of the script |
| 27 | +local function load_state() |
| 28 | + -- load persistent data |
| 29 | + local persisted_data = dfhack.persistent.getSiteData(GLOBAL_KEY, {}) |
| 30 | + enabled = persisted_data.enabled or false |
| 31 | + verbose = persisted_data.verbose or false |
| 32 | + target_eggs_count_per_race = persisted_data.target_eggs_count_per_race or default_table |
| 33 | +end |
| 34 | + |
| 35 | +if dfhack_flags.module then |
| 36 | + return |
| 37 | +end |
| 38 | +local function print_local(text) |
| 39 | + print(print_prefix .. text) |
| 40 | +end |
| 41 | +local function handle_error(text) |
| 42 | + qerror(text) |
| 43 | +end |
| 44 | +local function print_status() |
| 45 | + print_local(("eggwatch is currently %s."):format(enabled and "enabled" or "disabled")) |
| 46 | + if verbose then |
| 47 | + print_local("eggwatch is in verbose mode") |
| 48 | + end |
| 49 | +end |
| 50 | + |
| 51 | +local function print_detalis(details) |
| 52 | + if verbose then |
| 53 | + print_local(details) |
| 54 | + end |
| 55 | +end |
| 56 | + |
| 57 | +local function is_egg(item) |
| 58 | + return df.item_type.EGG == item:getType() |
| 59 | +end |
| 60 | + |
| 61 | +-- local function find_current_nestbox (current_eggs) |
| 62 | +-- for _, nestbox in ipairs (df.global.world.buildings.other.NEST_BOX) do |
| 63 | +-- if nestbox.pos == current_eggs.pos then |
| 64 | +-- return nestbox |
| 65 | +-- end |
| 66 | +-- end |
| 67 | +-- end |
| 68 | + |
| 69 | +-- local function create_new_egg_stack (original_eggs, remaining_eggs, creature, caste) |
| 70 | + |
| 71 | +-- print('about to split create new egg stack') |
| 72 | +-- print(('type= %s'):format(original_eggs:getType())) |
| 73 | +-- print(('creature= %s'):format( creature.creature_id)) |
| 74 | +-- print(('caste= %s '):format(caste.caste_id)) |
| 75 | + |
| 76 | +-- --local created_items = dfhack.items.createItem(creator, original_eggs:getType(), -1, creature.creature_id, caste.caste_id) |
| 77 | + |
| 78 | +-- print('created item') |
| 79 | +-- local created_egg_stack = created_items[1] |
| 80 | +-- print('about to copy fields from orginal eggs') |
| 81 | +-- created_egg_stack.incumabtion_counter = original_eggs.incumabtion_counter |
| 82 | +-- created_egg_stack.flags = original_eggs.flags |
| 83 | +-- created_egg_stack.flags2 = original_eggs.flags2 |
| 84 | +-- created_egg_stack.egg_flags = original_eggs.egg_flags |
| 85 | +-- created_egg_stack.pos = original_eggs.pos |
| 86 | +-- created_egg_stack.hatchling_civ_id = original_eggs.hatchling_civ_id |
| 87 | +-- created_egg_stack.mothers_genes = original_eggs.mothers_genes |
| 88 | +-- created_egg_stack.mothers_caste = original_eggs.mothers_caste |
| 89 | +-- created_egg_stack.mother_hf = original_eggs.mother_hf |
| 90 | +-- created_egg_stack.fathers_genes = original_eggs.fathers_genes |
| 91 | +-- created_egg_stack.fathers_caste = original_eggs.fathers_caste |
| 92 | +-- created_egg_stack.father_hf = original_eggs.father_hf |
| 93 | +-- created_egg_stack.hatchling_flags1 = original_eggs.hatchling_flags1 |
| 94 | +-- created_egg_stack.hatchling_flags2 = original_eggs.hatchling_flags2 |
| 95 | +-- created_egg_stack.hatchling_flags3 = original_eggs.hatchling_flags3 |
| 96 | +-- created_egg_stack.hatchling_flags4 = original_eggs.hatchling_flags4 |
| 97 | +-- created_egg_stack.hatchling_training_level = original_eggs.hatchling_training_level |
| 98 | +-- created_egg_stack.hatchling_animal_population = original_eggs.hatchling_animal_population |
| 99 | +-- created_egg_stack.mother_id = original_eggs.mother_id |
| 100 | + |
| 101 | +-- print('about to move new stack to nestbox') |
| 102 | +-- dfhack.items.moveToContainer(created_egg_stack, find_current_nestbox(original_eggs)) |
| 103 | +-- end |
| 104 | + |
| 105 | +local function count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) |
| 106 | + print_detalis(("start count_forbidden_eggs_for_race_in_claimed_nestobxes")) |
| 107 | + local eggs_count = 0 |
| 108 | + for _, nestbox in ipairs(df.global.world.buildings.other.NEST_BOX) do |
| 109 | + if nestbox.claimed_by ~= -1 then |
| 110 | + print_detalis(("Found claimed nextbox")) |
| 111 | + for _, nestbox_contained_item in ipairs(nestbox.contained_items) do |
| 112 | + if nestbox_contained_item.use_mode == df.building_item_role_type.TEMP then |
| 113 | + print_detalis(("Found claimed nextbox containing items")) |
| 114 | + if df.item_type.EGG == nestbox_contained_item.item:getType() then |
| 115 | + print_detalis(("Found claimed nextbox containing items that are eggs")) |
| 116 | + if nestbox_contained_item.item.egg_flags.fertile and nestbox_contained_item.item.flags.forbid then |
| 117 | + print_detalis(("Eggs are fertile and forbidden")) |
| 118 | + if df.creature_raw.find(nestbox_contained_item.item.race).creature_id == race_creature_id then |
| 119 | + print_detalis(("Eggs belong to %s"):format(race_creature_id)) |
| 120 | + print_detalis( |
| 121 | + ("eggs_count %s + new %s"):format( |
| 122 | + eggs_count, |
| 123 | + nestbox_contained_item.item.stack_size |
| 124 | + ) |
| 125 | + ) |
| 126 | + eggs_count = eggs_count + nestbox_contained_item.item.stack_size |
| 127 | + print_detalis(("eggs_count after adding current nestbox %s "):format(eggs_count)) |
| 128 | + end |
| 129 | + end |
| 130 | + end |
| 131 | + end |
| 132 | + end |
| 133 | + end |
| 134 | + end |
| 135 | + print_detalis(("end count_forbidden_eggs_for_race_in_claimed_nestobxes")) |
| 136 | + return eggs_count |
| 137 | +end |
| 138 | +local function get_max_eggs_for_race(race_creature_id) |
| 139 | + for k, v in pairs(target_eggs_count_per_race) do |
| 140 | + if k == race_creature_id then |
| 141 | + return v |
| 142 | + end |
| 143 | + end |
| 144 | + target_eggs_count_per_race[race_creature_id] = target_eggs_count_per_race.DEFAULT |
| 145 | + persist_state() |
| 146 | + return target_eggs_count_per_race[race_creature_id] |
| 147 | +end |
| 148 | +local function handle_eggs(eggs) |
| 149 | + print_detalis(("start handle_eggs")) |
| 150 | + if not eggs.egg_flags.fertile then |
| 151 | + print_local("Newly laid eggs are not fertile, do nothing") |
| 152 | + return |
| 153 | + end |
| 154 | + |
| 155 | + local race_creature_id = df.creature_raw.find(eggs.race).creature_id |
| 156 | + local max_eggs = get_max_eggs_for_race(race_creature_id) |
| 157 | + local current_eggs = eggs.stack_size |
| 158 | + |
| 159 | + local total_count = current_eggs |
| 160 | + total_count = total_count + count_forbidden_eggs_for_race_in_claimed_nestobxes(race_creature_id) |
| 161 | + |
| 162 | + print_detalis(("Total count for %s eggs is %s"):format(race_creature_id, total_count)) |
| 163 | + |
| 164 | + if total_count - current_eggs < max_eggs then |
| 165 | + -- ###if possible split egg stack to forbid only part below max change previous condition to total_count < max_eggs |
| 166 | + -- elseif total_count - current_eggs < max_eggs and total_count > max_eggs then |
| 167 | + -- local forbid_eggs = max_eggs - total_count + current_eggs |
| 168 | + -- local remaining_eggs = current_eggs - forbid_eggs |
| 169 | + -- print('about to split eggs stack') |
| 170 | + -- create_new_egg_stack(eggs, remaining_eggs, df.creature_raw.find(eggs.race), race_creature.caste[eggs.caste]) |
| 171 | + -- eggs.stack_size = forbid_eggs |
| 172 | + -- eggs.flags.forbid = true |
| 173 | + -- 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)) |
| 174 | + eggs.flags.forbid = true |
| 175 | + print_local( |
| 176 | + ("Previously existing %s eggs is %s lower than maximum %s , forbidden %s new eggs."):format( |
| 177 | + race_creature_id, |
| 178 | + total_count - current_eggs, |
| 179 | + max_eggs, |
| 180 | + current_eggs |
| 181 | + ) |
| 182 | + ) |
| 183 | + else |
| 184 | + print_local( |
| 185 | + ("Total count for %s eggs is %s over maximum %s, newly laid eggs %s , no action taken."):format( |
| 186 | + race_creature_id, |
| 187 | + total_count, |
| 188 | + max_eggs, |
| 189 | + current_eggs |
| 190 | + ) |
| 191 | + ) |
| 192 | + end |
| 193 | + |
| 194 | + print_detalis(("end handle_eggs")) |
| 195 | +end |
| 196 | + |
| 197 | +local function check_item_created(item_id) |
| 198 | + local item = df.item.find(item_id) |
| 199 | + if not item or not is_egg(item) then |
| 200 | + return |
| 201 | + end |
| 202 | + handle_eggs(item) |
| 203 | +end |
| 204 | +local function do_enable() |
| 205 | + enabled = true |
| 206 | + eventful.enableEvent(eventful.eventType.ITEM_CREATED, EVENT_FREQ) |
| 207 | + eventful.onItemCreated[GLOBAL_KEY] = check_item_created |
| 208 | +end |
| 209 | + |
| 210 | +local function do_disable() |
| 211 | + enabled = false |
| 212 | + eventful.onItemCreated[GLOBAL_KEY] = nil |
| 213 | +end |
| 214 | + |
| 215 | +local function validate_creature_id(creature_id) |
| 216 | + for i, c in ipairs(df.global.world.raws.creatures.all) do |
| 217 | + if c.creature_id == creature_id then |
| 218 | + return true |
| 219 | + end |
| 220 | + end |
| 221 | + return false |
| 222 | +end |
| 223 | + |
| 224 | +local function set_target(target_race, target_count) |
| 225 | + if target_race == nil or target_race == "" then |
| 226 | + handle_error('must specify "DEFAULT" or valid creature_id') |
| 227 | + end |
| 228 | + local target_race_upper = string.upper(target_race) |
| 229 | + if tonumber(target_count) == nil or tonumber(target_count) < 0 then |
| 230 | + handle_error("No valid target count specified") |
| 231 | + end |
| 232 | + if target_race_upper == "DEFAULT" or validate_creature_id(target_race_upper) then |
| 233 | + target_eggs_count_per_race[target_race_upper] = tonumber(target_count) |
| 234 | + else |
| 235 | + handle_error('must specify "DEFAULT" or valid creature_id') |
| 236 | + end |
| 237 | + |
| 238 | + print_local(dump(target_eggs_count_per_race)) |
| 239 | +end |
| 240 | +function dump(o) |
| 241 | + if type(o) == "table" then |
| 242 | + local s = "{ " |
| 243 | + for k, v in pairs(o) do |
| 244 | + if type(k) ~= "number" then |
| 245 | + k = '"' .. k .. '"' |
| 246 | + end |
| 247 | + s = s .. "[" .. k .. "] = " .. dump(v) .. "," |
| 248 | + end |
| 249 | + return s .. "} " |
| 250 | + else |
| 251 | + return tostring(o) |
| 252 | + end |
| 253 | +end |
| 254 | + |
| 255 | +if df.global.gamemode ~= df.game_mode.DWARF or not dfhack.isMapLoaded() then |
| 256 | + dfhack.printerr("eggwatch needs a loaded fortress to work") |
| 257 | + return |
| 258 | +end |
| 259 | + |
| 260 | +local args, opts = {...}, {} |
| 261 | +if dfhack_flags and dfhack_flags.enable then |
| 262 | + args = {dfhack_flags.enable_state and "enable" or "disable"} |
| 263 | +end |
| 264 | + |
| 265 | +local positionals = |
| 266 | + argparse.processArgsGetopt( |
| 267 | + args, |
| 268 | + { |
| 269 | + {"h", "help", handler = function() |
| 270 | + opts.help = true |
| 271 | + end} |
| 272 | + } |
| 273 | +) |
| 274 | + |
| 275 | +load_state() |
| 276 | +local command = positionals[1] |
| 277 | + |
| 278 | +if command == "help" or opts.help then |
| 279 | + print(dfhack.script_help()) |
| 280 | +elseif command == "enable" then |
| 281 | + do_enable() |
| 282 | + print_status() |
| 283 | +elseif command == "disable" then |
| 284 | + do_disable() |
| 285 | + print_status() |
| 286 | +elseif command == "target" then |
| 287 | + set_target(positionals[2], positionals[3]) |
| 288 | + print_status() |
| 289 | +elseif command == "verbose" then |
| 290 | + verbose = not verbose |
| 291 | + print_status() |
| 292 | +elseif command == 'clear' then |
| 293 | +target_eggs_count_per_race = default_table |
| 294 | + |
| 295 | +elseif not command or command == "status" then |
| 296 | + print_status() |
| 297 | + print_local(dump(target_eggs_count_per_race)) |
| 298 | +end |
| 299 | +persist_state() |
0 commit comments