From 5a27e6309542110c12ce6ff2d0a82370b98e24e5 Mon Sep 17 00:00:00 2001 From: Vettlingr <93880203+Vettlingr@users.noreply.github.com> Date: Tue, 9 Nov 2021 21:55:35 +0100 Subject: [PATCH 0001/3514] Create combine. Combine combines both combine-drinks and combine-plants and adds a lot of new features. It can combine any food stuff in the selected stockpile or all stockpiles on the map. --- combine | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 combine diff --git a/combine b/combine new file mode 100644 index 0000000000..b16c53c614 --- /dev/null +++ b/combine @@ -0,0 +1,300 @@ +-- Merge food and drink stacks in the selected stockpile or every stockpile +--[====[ + +combine by Vettlingr +============== +Merge stacks of food in the selected stockpile or all stockpiles. + +]====] +local utils = require 'utils' + +local f = { + validArgs = utils.invert({ 'help', 'drinks', 'plants', 'meat', 'fish', 'fat', 'food', 'roasts', 'max', 'all', 'stockpile' }); + args = utils.processArgs({...}, validArgs); +help = [====[ +Combine +============= +Merge stacks of food in selected Stockpile or across all stockpiles on the map. +Valid commands: +:``-drinks``: + Merges drinks +:``-plants``: + Merges plants +:``-meat``: + Merges meat and intestines +:``-fat``: + Merges fat and tallow +:``-roasts``: + Merges prepared food +:``-fish``: + Merges fish +:``-food``: + Merges all food categories +:``-all``: + Selects all stockpiles +:``-max``: + Selects a maximum stacksize, if unspecified it will be set to 500 + +Examples: +combine -drinks -fish -all + Combines drinks and fish stacks in all stockpiles + +combine -food -all + Combines all food types across all stockpiles + +combine -fat -roasts -max 50 + Combines fat and prepared food in the selected stockpile with a preferred stacksize of 50 + +]====] +} + +local max = 30 + +local drinks={} +local plants={} +local meats={} +local fat={} +local roasts={} +local fish={} + +--Stockpile Stack sizes: +drinks.max = 30 +plants.max = 6 +meats.max = 20 +fat.max = 20 +roasts.max = 20 +fish.max = 10 + +--Not sure if these are needed. +drinks.Tot=0 drinks.xTot=0 +plants.Tot=0 plants.xTot=0 +meats.Tot=0 meats.xTot=0 +fat.Tot=0 fat.xTot=0 +roasts.Tot=0 roasts.xTot=0 +fish.Tot=0 fish.xTot=0 + +if f.args.max then max = tonumber(f.args.max) end + +local stockpile = nil +if f.args.stockpile then stockpile = df.building.find(tonumber(f.args.stockpile)) end + +local function itemsCompatible(item0, item1) + return item0:getType() == item1:getType() + and item0.mat_type == item1.mat_type + and item0.mat_index == item1.mat_index +end + +local function FishitemsCompatible(item0, item1) + return item0:getType() == item1:getType() + and item0.race == item1.race + and item0.caste == item1.caste +end + +function getItems(items, item, index, bool) + repeat + local nextBatch = {} + for _,v in pairs(items) do + -- Skip items currently tasked + if #v.specific_refs == 0 then + if bool==1 and ( v:getType() == df.item_type.DRINK )then + item[index] = v + index = index + 1 + elseif bool==2 and ( v:getType() == df.item_type.PLANT or v:getType() == df.item_type.PLANT_GROWTH ) then + item[index] = v + index = index + 1 + elseif bool==3 and (v:getType() == df.item_type.MEAT ) then + item[index] = v + index = index + 1 + elseif bool==4 and (v:getType() == df.item_type.GLOB ) then + item[index] = v + index = index + 1 + elseif bool==5 and (v:getType() == df.item_type.FOOD or v:getType() == df.item_type.CHEESE ) then + item[index] = v + index = index + 1 + elseif bool==10 and (v:getType() == df.item_type.FISH or v:getType() == df.item_type.FISH_RAW or v:getType() == df.item_type.EGG ) then + item[index] = v + index = index + 1 + else + local containedItems = dfhack.items.getContainedItems(v) + if (bool==1 and #containedItems == 1) or (bool>1 and #containedItems > 0) then + for _,w in pairs(containedItems) do + table.insert(nextBatch, w) + end + end + end + end + end + items = nextBatch + until #items == 0 + return index +end + +function Combineitems(building, tabl, food, bool) + local rootItems + if building then + rootItems = dfhack.buildings.getStockpileContents(building) + else + rootItems = dfhack.items.getContainedItems(item) + end + if #rootItems == 0 and not f.args.all then + qerror("Select a non-empty container") + return + else + local foodCount = getItems(rootItems, food, 0, bool) + local removedFood = { } --as:bool[] + food.max=max + if f.args.max then max = tonumber(f.args.max) + if tonumber(f.args.max)== 0 then max = 500 + end + end + for i=0,(foodCount-2) do + local currentFood = food[i] --as:df.item_foodst + local itemsNeeded = max - currentFood.stack_size + + if removedFood[currentFood.id] == nil and itemsNeeded > 0 then + local j = i+1 + local last = foodCount + repeat + local sourceFood = food[j] + if bool>=10 and removedFood[sourceFood.id] == nil and FishitemsCompatible(currentFood, sourceFood) then + local amountToMove = math.min(itemsNeeded, sourceFood.stack_size) + itemsNeeded = itemsNeeded - amountToMove + currentFood.stack_size = currentFood.stack_size + amountToMove + + if sourceFood.stack_size == amountToMove then + removedFood[sourceFood.id] = true + sourceFood.stack_size = 1 + else + sourceFood.stack_size = sourceFood.stack_size - amountToMove + end + -- else print("failed") + elseif bool <10 and removedFood[sourceFood.id] == nil and itemsCompatible(currentFood, sourceFood) then + local amountToMove = math.min(itemsNeeded, sourceFood.stack_size) + itemsNeeded = itemsNeeded - amountToMove + currentFood.stack_size = currentFood.stack_size + amountToMove + + if sourceFood.stack_size == amountToMove then + removedFood[sourceFood.id] = true + if bool>1 then sourceFood.stack_size = 1 end + else + sourceFood.stack_size = sourceFood.stack_size - amountToMove + end + -- else print("failed") + end + j = j + 1 + until j == foodCount or itemsNeeded == 0 + end + end + local removedCount = 0 + for id,removed in pairs(removedFood) do + if removed then + removedCount = removedCount + 1 + local removedFood = df.item.find(id) + dfhack.items.remove(removedFood) + end + end + if food.Tot == nil then food.Tot = 0 end + if food.xTot == nil then food.xTot = 0 end + food.Tot = food.Tot + foodCount + food.xTot = food.xTot + removedCount + end +end + +if f.args.help then + print(f.help) + return +end +if not f.args.all then + local building = stockpile or dfhack.gui.getSelectedBuilding(true) + if building ~= nil and building:getType() ~= 29 then building = nil + end + if building ~= nil then + if f.args.drinks or f.args.food then + Combineitems(building, f, drinks, 1) + print("found " .. drinks.Tot .. " drinks") + print("merged " .. drinks.xTot .. " drinks") + end + if f.args.plants or f.args.food then + Combineitems(building, f, plants, 2) + print("found " .. plants.Tot .. " plants") + print("merged " .. plants.xTot .. " plants") + end + if f.args.meat or f.args.food then + Combineitems(building, f, meats, 3) + print("found " .. meats.Tot .. " meat") + print("merged " .. meats.xTot .. " meat") + end + if f.args.fat or f.args.food then + Combineitems(building, f, fat, 4) + print("found " .. fat.Tot .. " fat") + print("merged " .. fat.xTot .. " fat") + end + if f.args.roasts or f.args.food then + Combineitems(building, f, roasts, 5) + print("found " .. roasts.Tot .. " prepared food") + print("merged " .. roasts.xTot .. " prepared food") + end + if f.args.fish or f.args.food then + Combineitems(building, f, fish, 10) + print("found " .. fish.Tot .. " fish") + print("merged " .. fish.xTot .. " fish") + end + else + print('select a stockpile') + end +else + if f.args.all then + print('Combining all food...') + for _, building in pairs(df.global.world.buildings.all) do + if building:getType() == 29 and building ~= nil then + if building ~= nil then + if f.args.drinks or f.args.food then + Combineitems(building, f, drinks, 1) + end + if f.args.plants or f.args.food then + Combineitems(building, f, plants, 2) + end + if f.args.meat or f.args.food then + Combineitems(building, f, meats, 3) + end + if f.args.fat or f.args.food then + Combineitems(building, f, fat, 4) + end + if f.args.roasts or f.args.food then + Combineitems(building, f, roasts, 5) + end + if f.args.fish or f.args.food then + Combineitems(building, f, fish, 10) + end + else + print('invalid') + end + end + end + if f.args.drinks or f.args.food then + print("found " .. drinks.Tot .. " drinks") + print("merged " .. drinks.xTot .. " drinks") + end + if f.args.plants or f.args.food then + print("found " .. plants.Tot .. " plants") + print("merged " .. plants.xTot .. " plants") + end + if f.args.meat or f.args.food then + print("found " .. meats.Tot .. " meat") + print("merged " .. meats.xTot .. " meat") + end + if f.args.fat or f.args.food then + print("found " .. fat.Tot .. " fat or tallow") + print("merged " .. fat.xTot .. " fat or tallow") + end + if f.args.roasts or f.args.food then + print("found " .. roasts.Tot .. " prepared food") + print("merged " .. roasts.xTot .. " prepared food") + end + if f.args.fish or f.args.food then + print("found " .. fish.Tot .. " fish") + print("merged " .. fish.xTot .. " fish") + end + end + return +end From aeef527ab7a2dbbe60d090af395dc302857a85e3 Mon Sep 17 00:00:00 2001 From: Vettlingr <93880203+Vettlingr@users.noreply.github.com> Date: Thu, 11 Nov 2021 07:23:12 +0100 Subject: [PATCH 0002/3514] Update and rename combine to combine.lua Now you can for instance write combine -drinks 50 to combine drinks at a stack size of 50 --- combine => combine.lua | 7 +++++++ 1 file changed, 7 insertions(+) rename combine => combine.lua (96%) diff --git a/combine b/combine.lua similarity index 96% rename from combine rename to combine.lua index b16c53c614..bd3fab6e5c 100644 --- a/combine +++ b/combine.lua @@ -65,6 +65,13 @@ fat.max = 20 roasts.max = 20 fish.max = 10 +if f.args.drinks then drinks.max = tonumber(f.args.drinks) end +if f.args.plants then plants.max = tonumber(f.args.plants) end +if f.args.meats then meats.max = tonumber(f.args.meats) end +if f.args.fat then fat.max = tonumber(f.args.fat) end +if f.args.roasts then roasts.max = tonumber(f.args.roasts) end +if f.args.fish then fish.max = tonumber(f.args.fish) end + --Not sure if these are needed. drinks.Tot=0 drinks.xTot=0 plants.Tot=0 plants.xTot=0 From 49edca2046daa4c85381b86d8c2958f7f09edad0 Mon Sep 17 00:00:00 2001 From: Vettlingr <93880203+Vettlingr@users.noreply.github.com> Date: Thu, 11 Nov 2021 07:38:27 +0100 Subject: [PATCH 0003/3514] max to food.max --- combine.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/combine.lua b/combine.lua index bd3fab6e5c..dfdd1d9e39 100644 --- a/combine.lua +++ b/combine.lua @@ -149,14 +149,13 @@ function Combineitems(building, tabl, food, bool) else local foodCount = getItems(rootItems, food, 0, bool) local removedFood = { } --as:bool[] - food.max=max - if f.args.max then max = tonumber(f.args.max) - if tonumber(f.args.max)== 0 then max = 500 + if f.args.max then food.max = tonumber(f.args.max) + if tonumber(f.args.max)== 0 then food.max = 500 end end for i=0,(foodCount-2) do local currentFood = food[i] --as:df.item_foodst - local itemsNeeded = max - currentFood.stack_size + local itemsNeeded = food.max - currentFood.stack_size if removedFood[currentFood.id] == nil and itemsNeeded > 0 then local j = i+1 From 9a893d6f1329cffd949122cec040fc832c055ad7 Mon Sep 17 00:00:00 2001 From: Timur Kelman Date: Mon, 9 May 2022 18:53:26 +0200 Subject: [PATCH 0004/3514] Configure `merge=union` for `changelog.txt` to reduce merge conflicts --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..8b077fbd74 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +changelog.txt merge=union From 16612f33e6ecd130fcaebaf07db4dd5a15b43995 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 20:40:38 +0100 Subject: [PATCH 0005/3514] Add pop-control and max-wave --- max-wave.lua | 50 +++++++++++++++++++++++++++ pop-control.lua | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 max-wave.lua create mode 100644 pop-control.lua diff --git a/max-wave.lua b/max-wave.lua new file mode 100644 index 0000000000..18ef6f5dcc --- /dev/null +++ b/max-wave.lua @@ -0,0 +1,50 @@ +--Dynamically limit the next immigration wave +--[[=begin + +max-wave.lua +============ +Set the population cap to the lesser of current_pop + wave_size or max_pop. +Use with the `repeat` command to set a rolling immigration limit. + +Usage examples:: + + max-wave wave_size (max_pop) + + repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] + +The first example is abstract and only sets the population cap once; +the second will update the population cap monthly, allowing a +maximum of 10 immigrants per wave, up to a total population of 200. + +=end]] + +local args = {...} + +local wave_size = tonumber(args[1]) +local max_pop = tonumber(args[2]) +local current_pop = 0 + +if not wave_size then + print('max-wave: wave_size required') + return +end + +--One would think the game would track this value somewhere... +for k,v in ipairs(df.global.world.units.active) do + if dfhack.units.isCitizen(v) or + (dfhack.units.isOwnCiv(v) and + dfhack.units.isAlive(v) and + df.global.world.raws.creatures.all[v.race].caste[v.caste].flags.CAN_LEARN and + not (dfhack.units.isMerchant(v) or dfhack.units.isForest(v) or v.flags1.diplomat or v.flags2.visitor) + ) + then + current_pop = current_pop + 1 + end + end + +local new_limit = current_pop + wave_size + +if max_pop and new_limit > max_pop then new_limit = max_pop end + +df.global.d_init.population_cap = new_limit +print('max-wave: Population cap set to '.. new_limit) diff --git a/pop-control.lua b/pop-control.lua new file mode 100644 index 0000000000..d717df83ef --- /dev/null +++ b/pop-control.lua @@ -0,0 +1,89 @@ +-- pop-control +-- by Tachytaenius +-- Script to control the various population caps as well as use of max-wave and hermit persistently per fortress +-- Put "pop-control onLoad" in your onMapLoad.init. This will prompt settings on embark and use them every time you reload the fortress save +-- If you ever want to change the settings for a fortress run "pop-control reenterSettings" + +local script = require("gui.script") +local persistTable = require("persist-table") + +-- (Hopefully) get original settings +originalPopCap = originalPopCap or df.global.d_init.population_cap +originalStrictPopCap = originalStrictPopCap or df.global.d_init.strict_population_cap +originalVisitorCap = originalVisitorCap or df.global.d_init.visitor_cap + +if df.global.gamemode ~= 0 then + return -- not fort mode! +end + +if not persistTable.GlobalTable.fortPopInfo then + persistTable.GlobalTable.fortPopInfo = {} +end + +local siteId = df.global.ui.site_id + +local function popControl(forceEnterSettings) + script.start(function() + local siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] + if not siteInfo or forceEnterSettings then + -- get new settings + persistTable.GlobalTable.fortPopInfo[siteId] = nil -- i don't know if persist-table works well with reassignent + persistTable.GlobalTable.fortPopInfo[siteId] = {} + siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] + if script.showYesNoPrompt("Hermit", "Hermit mode?") then + siteInfo.hermit = "true" + return + else + siteInfo.hermit = "false" + end + local _ -- ignore + -- migrant cap + local migrantCapInput + while not tonumber(migrantCapInput) do + _, migrantCapInput = script.showInputPrompt("Migrant cap", "Maximum migrants per wave?") + end + siteInfo.migrantCap = migrantCapInput + -- pop cap + local popCapInput + while not tonumber(popCapInput) or popCapInput == "" do + _, popCapInput = script.showInputPrompt("Population cap", "Maximum population? Settings population cap: " .. originalPopCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.popCap = tostring(tonumber(popCapInput) or originalPopCap) + -- strict pop cap + local strictPopCapInput + while not tonumber(strictPopCapInput) or strictPopCapInput == "" do + _, strictPopCapInput = script.showInputPrompt("Strict population cap", "Strict maximum population? Settings strict population cap " .. originalStrictPopCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.strictPopCap = tostring(tonumber(strictPopCapInput) or originalStrictPopCap) + -- visitor cap + local visitorCapInput + while not tonumber(visitorCapInput) or visitorCapInput == "" do + _, visitorCapInput = script.showInputPrompt("Visitors", "Vistitor cap? Settings visitor cap " .. originalVisitorCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.visitorCap = tostring(tonumber(visitorCap) or originalVisitorCap) + end + -- use settings + if siteInfo.hermit == "true" then + dfhack.run_command("hermit enable") + -- NOTE: could, maybe should cancel max-wave repeat here + else + dfhack.run_command("hermit disable") + dfhack.run_command("repeat -name max-wave -timeUnits months -time 1 -command [ max-wave " .. siteInfo.migrantCap .. " " .. siteInfo.popCap .. " ]") + df.global.d_init.strict_population_cap = tonumber(siteInfo.strictPopCap) + df.global.d_init.visitor_cap = tonumber(siteInfo.visitorCap) + end + end) +end + +local function help() + print("syntax: pop-control [reenterSettings|onLoad]") +end + +local action_switch = { + reenterSettings = function() popControl(true) end, + onLoad = function() popControl(false) end +} +setmetatable(action_switch, {__index = function() return help end}) + +local args = {...} +action_switch[args[1] or "help"]() From d7ac9cd5bebf73a16edcae6c762d0c4caac04f3c Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 20:42:38 +0100 Subject: [PATCH 0006/3514] Add credits to max-wave http://dwarffortresswiki.org/index.php/User:Fleeting_Frames/max-wave, which links to http://www.bay12forums.com/smf/index.php?topic=158503.msg7027874#msg7027874 --- max-wave.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/max-wave.lua b/max-wave.lua index 18ef6f5dcc..9ae3c8396e 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -1,4 +1,5 @@ --Dynamically limit the next immigration wave +--By Loci, modified by Fleeting Frames --[[=begin max-wave.lua From bdfe570c6022ace589b1875584542a8f7a0afe5c Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 20:44:39 +0100 Subject: [PATCH 0007/3514] Update changelog.txt --- changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index 3a04e5a1bc..90758dc7ad 100644 --- a/changelog.txt +++ b/changelog.txt @@ -15,6 +15,8 @@ that repo. ## New Scripts +- `max-wave`: dynamically limit the next immigration wave, can be set to repeat +- `pop-control`: persistent per-fortress population cap management, also persistently handles use of `hermit` and `max-wave` - `assign-minecarts`: assign minecarts to hauling routes that don't have one - `deteriorate`: combines, replaces, and extends previous `deteriorateclothes`, `deterioratecorpses`, and `deterioratefood` scripts. - `gui/petitions`: shows list of fort's petitions From a73b2f155dddba81dbd3475d9f2f17df9d0c84f1 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 20:55:25 +0100 Subject: [PATCH 0008/3514] Update pop-control.lua reenterSettings, onLoad -> reenter-settings, on-load fixed bug where hermit enabling wouldn't work when entering settings --- pop-control.lua | 61 ++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/pop-control.lua b/pop-control.lua index d717df83ef..54a6d7a5a4 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -1,8 +1,8 @@ -- pop-control -- by Tachytaenius -- Script to control the various population caps as well as use of max-wave and hermit persistently per fortress --- Put "pop-control onLoad" in your onMapLoad.init. This will prompt settings on embark and use them every time you reload the fortress save --- If you ever want to change the settings for a fortress run "pop-control reenterSettings" +-- Put "pop-control on-load" in your onMapLoad.init. This will prompt settings on embark and use them every time you reload the fortress save +-- If you ever want to change the settings for a fortress run "pop-control reenter-settings" local script = require("gui.script") local persistTable = require("persist-table") @@ -32,35 +32,34 @@ local function popControl(forceEnterSettings) siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] if script.showYesNoPrompt("Hermit", "Hermit mode?") then siteInfo.hermit = "true" - return else siteInfo.hermit = "false" + local _ -- ignore + -- migrant cap + local migrantCapInput + while not tonumber(migrantCapInput) do + _, migrantCapInput = script.showInputPrompt("Migrant cap", "Maximum migrants per wave?") + end + siteInfo.migrantCap = migrantCapInput + -- pop cap + local popCapInput + while not tonumber(popCapInput) or popCapInput == "" do + _, popCapInput = script.showInputPrompt("Population cap", "Maximum population? Settings population cap: " .. originalPopCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.popCap = tostring(tonumber(popCapInput) or originalPopCap) + -- strict pop cap + local strictPopCapInput + while not tonumber(strictPopCapInput) or strictPopCapInput == "" do + _, strictPopCapInput = script.showInputPrompt("Strict population cap", "Strict maximum population? Settings strict population cap " .. originalStrictPopCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.strictPopCap = tostring(tonumber(strictPopCapInput) or originalStrictPopCap) + -- visitor cap + local visitorCapInput + while not tonumber(visitorCapInput) or visitorCapInput == "" do + _, visitorCapInput = script.showInputPrompt("Visitors", "Vistitor cap? Settings visitor cap " .. originalVisitorCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.visitorCap = tostring(tonumber(visitorCap) or originalVisitorCap) end - local _ -- ignore - -- migrant cap - local migrantCapInput - while not tonumber(migrantCapInput) do - _, migrantCapInput = script.showInputPrompt("Migrant cap", "Maximum migrants per wave?") - end - siteInfo.migrantCap = migrantCapInput - -- pop cap - local popCapInput - while not tonumber(popCapInput) or popCapInput == "" do - _, popCapInput = script.showInputPrompt("Population cap", "Maximum population? Settings population cap: " .. originalPopCap .. "\n(assuming wasn't changed before first call of this script)") - end - siteInfo.popCap = tostring(tonumber(popCapInput) or originalPopCap) - -- strict pop cap - local strictPopCapInput - while not tonumber(strictPopCapInput) or strictPopCapInput == "" do - _, strictPopCapInput = script.showInputPrompt("Strict population cap", "Strict maximum population? Settings strict population cap " .. originalStrictPopCap .. "\n(assuming wasn't changed before first call of this script)") - end - siteInfo.strictPopCap = tostring(tonumber(strictPopCapInput) or originalStrictPopCap) - -- visitor cap - local visitorCapInput - while not tonumber(visitorCapInput) or visitorCapInput == "" do - _, visitorCapInput = script.showInputPrompt("Visitors", "Vistitor cap? Settings visitor cap " .. originalVisitorCap .. "\n(assuming wasn't changed before first call of this script)") - end - siteInfo.visitorCap = tostring(tonumber(visitorCap) or originalVisitorCap) end -- use settings if siteInfo.hermit == "true" then @@ -76,12 +75,12 @@ local function popControl(forceEnterSettings) end local function help() - print("syntax: pop-control [reenterSettings|onLoad]") + print("syntax: pop-control [reenter-settings|on-load]") end local action_switch = { - reenterSettings = function() popControl(true) end, - onLoad = function() popControl(false) end + ["reenter-settings"] = function() popControl(true) end, + ["on-load"] = function() popControl(false) end } setmetatable(action_switch, {__index = function() return help end}) From 401fb65fdfdde6faeb296789d09d9e49fd084c3c Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 20:57:56 +0100 Subject: [PATCH 0009/3514] Update changelog.txt --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 90758dc7ad..87de102965 100644 --- a/changelog.txt +++ b/changelog.txt @@ -16,7 +16,7 @@ that repo. ## New Scripts - `max-wave`: dynamically limit the next immigration wave, can be set to repeat -- `pop-control`: persistent per-fortress population cap management, also persistently handles use of `hermit` and `max-wave` +- `pop-control`: persistent per fortress population cap, `hermit`, and `max-wave` management - `assign-minecarts`: assign minecarts to hauling routes that don't have one - `deteriorate`: combines, replaces, and extends previous `deteriorateclothes`, `deterioratecorpses`, and `deterioratefood` scripts. - `gui/petitions`: shows list of fort's petitions From ab67f7baed4118fe1e48e65291a20fccb96670bc Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 21:06:23 +0100 Subject: [PATCH 0010/3514] Match proper script autodoc format for pop-control --- pop-control.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/pop-control.lua b/pop-control.lua index 54a6d7a5a4..00df46c898 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -1,8 +1,16 @@ --- pop-control --- by Tachytaenius -- Script to control the various population caps as well as use of max-wave and hermit persistently per fortress --- Put "pop-control on-load" in your onMapLoad.init. This will prompt settings on embark and use them every time you reload the fortress save --- If you ever want to change the settings for a fortress run "pop-control reenter-settings" +-- by Tachytaenius +--[====[ +pop-control +=========== +Controls the various population caps as well as use of max-wave and hermit persistently per fortress +Intended to be placed within `onMapLoad.init` as `pop-control on-load` +Available arguments: + +- ``on-load`` automatically checks for settings for this site and prompts them to be entered if not present + +- ``reenter-settings`` revise settings for this site +]====] local script = require("gui.script") local persistTable = require("persist-table") From ad001940f4daae91b646ea6cd8dd7fae6cbd429c Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 21:13:25 +0100 Subject: [PATCH 0011/3514] Hopefully get max-wave's autodoc to spec --- max-wave.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index 9ae3c8396e..7b476c229b 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -1,6 +1,6 @@ --Dynamically limit the next immigration wave --By Loci, modified by Fleeting Frames ---[[=begin +--[====[ max-wave.lua ============ @@ -17,7 +17,7 @@ The first example is abstract and only sets the population cap once; the second will update the population cap monthly, allowing a maximum of 10 immigrants per wave, up to a total population of 200. -=end]] +]====] local args = {...} From d102fd93e5ca636039a13c24b05ca303b6cf3463 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 21:15:03 +0100 Subject: [PATCH 0012/3514] Continue to fix docs errors in max-wave --- max-wave.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index 7b476c229b..52a03eb378 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -2,8 +2,8 @@ --By Loci, modified by Fleeting Frames --[====[ -max-wave.lua -============ +max-wave +======== Set the population cap to the lesser of current_pop + wave_size or max_pop. Use with the `repeat` command to set a rolling immigration limit. From e4fb72b8c4834c17ad579bee4e496872fdcd0468 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 21:19:34 +0100 Subject: [PATCH 0013/3514] Fix misused `s in pop-control.lua (?) --- pop-control.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pop-control.lua b/pop-control.lua index 00df46c898..46d4529ef8 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -4,7 +4,7 @@ pop-control =========== Controls the various population caps as well as use of max-wave and hermit persistently per fortress -Intended to be placed within `onMapLoad.init` as `pop-control on-load` +Intended to be placed within ``onMapLoad.init`` as ``pop-control on-load`` Available arguments: - ``on-load`` automatically checks for settings for this site and prompts them to be entered if not present From 59d9f4233adc179cda10550fd3626b5d96bca3ea Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 21:24:08 +0100 Subject: [PATCH 0014/3514] Trim trailing whitespace in max-wave.lua --- max-wave.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index 52a03eb378..c9fe1d6865 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -10,7 +10,7 @@ Use with the `repeat` command to set a rolling immigration limit. Usage examples:: max-wave wave_size (max_pop) - + repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] The first example is abstract and only sets the population cap once; @@ -32,9 +32,9 @@ end --One would think the game would track this value somewhere... for k,v in ipairs(df.global.world.units.active) do - if dfhack.units.isCitizen(v) or - (dfhack.units.isOwnCiv(v) and - dfhack.units.isAlive(v) and + if dfhack.units.isCitizen(v) or + (dfhack.units.isOwnCiv(v) and + dfhack.units.isAlive(v) and df.global.world.raws.creatures.all[v.race].caste[v.caste].flags.CAN_LEARN and not (dfhack.units.isMerchant(v) or dfhack.units.isForest(v) or v.flags1.diplomat or v.flags2.visitor) ) @@ -44,7 +44,7 @@ for k,v in ipairs(df.global.world.units.active) do end local new_limit = current_pop + wave_size - + if max_pop and new_limit > max_pop then new_limit = max_pop end df.global.d_init.population_cap = new_limit From b092e7d6b517018bff857d1b9eca437de3a65a9e Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 8 Jun 2022 21:28:55 +0100 Subject: [PATCH 0015/3514] Hopefully deal with whitespace difficulties with fresh copies --- max-wave.lua | 102 +++++++++++++++++++++++----------------------- pop-control.lua | 106 ++++++++++++++++++++++++------------------------ 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index c9fe1d6865..6993196f1c 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -1,51 +1,51 @@ ---Dynamically limit the next immigration wave ---By Loci, modified by Fleeting Frames ---[====[ - -max-wave -======== -Set the population cap to the lesser of current_pop + wave_size or max_pop. -Use with the `repeat` command to set a rolling immigration limit. - -Usage examples:: - - max-wave wave_size (max_pop) - - repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] - -The first example is abstract and only sets the population cap once; -the second will update the population cap monthly, allowing a -maximum of 10 immigrants per wave, up to a total population of 200. - -]====] - -local args = {...} - -local wave_size = tonumber(args[1]) -local max_pop = tonumber(args[2]) -local current_pop = 0 - -if not wave_size then - print('max-wave: wave_size required') - return -end - ---One would think the game would track this value somewhere... -for k,v in ipairs(df.global.world.units.active) do - if dfhack.units.isCitizen(v) or - (dfhack.units.isOwnCiv(v) and - dfhack.units.isAlive(v) and - df.global.world.raws.creatures.all[v.race].caste[v.caste].flags.CAN_LEARN and - not (dfhack.units.isMerchant(v) or dfhack.units.isForest(v) or v.flags1.diplomat or v.flags2.visitor) - ) - then - current_pop = current_pop + 1 - end - end - -local new_limit = current_pop + wave_size - -if max_pop and new_limit > max_pop then new_limit = max_pop end - -df.global.d_init.population_cap = new_limit -print('max-wave: Population cap set to '.. new_limit) +--Dynamically limit the next immigration wave +--By Loci, modified by Fleeting Frames +--[====[ + +max-wave +======== +Set the population cap to the lesser of current_pop + wave_size or max_pop. +Use with the `repeat` command to set a rolling immigration limit. + +Usage examples:: + + max-wave wave_size (max_pop) + + repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] + +The first example is abstract and only sets the population cap once; +the second will update the population cap monthly, allowing a +maximum of 10 immigrants per wave, up to a total population of 200. + +]====] + +local args = {...} + +local wave_size = tonumber(args[1]) +local max_pop = tonumber(args[2]) +local current_pop = 0 + +if not wave_size then + print('max-wave: wave_size required') + return +end + +--One would think the game would track this value somewhere... +for k,v in ipairs(df.global.world.units.active) do + if dfhack.units.isCitizen(v) or + (dfhack.units.isOwnCiv(v) and + dfhack.units.isAlive(v) and + df.global.world.raws.creatures.all[v.race].caste[v.caste].flags.CAN_LEARN and + not (dfhack.units.isMerchant(v) or dfhack.units.isForest(v) or v.flags1.diplomat or v.flags2.visitor) + ) + then + current_pop = current_pop + 1 + end + end + +local new_limit = current_pop + wave_size + +if max_pop and new_limit > max_pop then new_limit = max_pop end + +df.global.d_init.population_cap = new_limit +print('max-wave: Population cap set to '.. new_limit) diff --git a/pop-control.lua b/pop-control.lua index 46d4529ef8..4f360906c3 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -21,65 +21,65 @@ originalStrictPopCap = originalStrictPopCap or df.global.d_init.strict_populatio originalVisitorCap = originalVisitorCap or df.global.d_init.visitor_cap if df.global.gamemode ~= 0 then - return -- not fort mode! + return -- not fort mode! end if not persistTable.GlobalTable.fortPopInfo then - persistTable.GlobalTable.fortPopInfo = {} + persistTable.GlobalTable.fortPopInfo = {} end local siteId = df.global.ui.site_id local function popControl(forceEnterSettings) - script.start(function() - local siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] - if not siteInfo or forceEnterSettings then - -- get new settings - persistTable.GlobalTable.fortPopInfo[siteId] = nil -- i don't know if persist-table works well with reassignent - persistTable.GlobalTable.fortPopInfo[siteId] = {} - siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] - if script.showYesNoPrompt("Hermit", "Hermit mode?") then - siteInfo.hermit = "true" - else - siteInfo.hermit = "false" - local _ -- ignore - -- migrant cap - local migrantCapInput - while not tonumber(migrantCapInput) do - _, migrantCapInput = script.showInputPrompt("Migrant cap", "Maximum migrants per wave?") - end - siteInfo.migrantCap = migrantCapInput - -- pop cap - local popCapInput - while not tonumber(popCapInput) or popCapInput == "" do - _, popCapInput = script.showInputPrompt("Population cap", "Maximum population? Settings population cap: " .. originalPopCap .. "\n(assuming wasn't changed before first call of this script)") - end - siteInfo.popCap = tostring(tonumber(popCapInput) or originalPopCap) - -- strict pop cap - local strictPopCapInput - while not tonumber(strictPopCapInput) or strictPopCapInput == "" do - _, strictPopCapInput = script.showInputPrompt("Strict population cap", "Strict maximum population? Settings strict population cap " .. originalStrictPopCap .. "\n(assuming wasn't changed before first call of this script)") - end - siteInfo.strictPopCap = tostring(tonumber(strictPopCapInput) or originalStrictPopCap) - -- visitor cap - local visitorCapInput - while not tonumber(visitorCapInput) or visitorCapInput == "" do - _, visitorCapInput = script.showInputPrompt("Visitors", "Vistitor cap? Settings visitor cap " .. originalVisitorCap .. "\n(assuming wasn't changed before first call of this script)") - end - siteInfo.visitorCap = tostring(tonumber(visitorCap) or originalVisitorCap) - end - end - -- use settings - if siteInfo.hermit == "true" then - dfhack.run_command("hermit enable") - -- NOTE: could, maybe should cancel max-wave repeat here - else - dfhack.run_command("hermit disable") - dfhack.run_command("repeat -name max-wave -timeUnits months -time 1 -command [ max-wave " .. siteInfo.migrantCap .. " " .. siteInfo.popCap .. " ]") - df.global.d_init.strict_population_cap = tonumber(siteInfo.strictPopCap) - df.global.d_init.visitor_cap = tonumber(siteInfo.visitorCap) - end - end) + script.start(function() + local siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] + if not siteInfo or forceEnterSettings then + -- get new settings + persistTable.GlobalTable.fortPopInfo[siteId] = nil -- i don't know if persist-table works well with reassignent + persistTable.GlobalTable.fortPopInfo[siteId] = {} + siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] + if script.showYesNoPrompt("Hermit", "Hermit mode?") then + siteInfo.hermit = "true" + else + siteInfo.hermit = "false" + local _ -- ignore + -- migrant cap + local migrantCapInput + while not tonumber(migrantCapInput) do + _, migrantCapInput = script.showInputPrompt("Migrant cap", "Maximum migrants per wave?") + end + siteInfo.migrantCap = migrantCapInput + -- pop cap + local popCapInput + while not tonumber(popCapInput) or popCapInput == "" do + _, popCapInput = script.showInputPrompt("Population cap", "Maximum population? Settings population cap: " .. originalPopCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.popCap = tostring(tonumber(popCapInput) or originalPopCap) + -- strict pop cap + local strictPopCapInput + while not tonumber(strictPopCapInput) or strictPopCapInput == "" do + _, strictPopCapInput = script.showInputPrompt("Strict population cap", "Strict maximum population? Settings strict population cap " .. originalStrictPopCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.strictPopCap = tostring(tonumber(strictPopCapInput) or originalStrictPopCap) + -- visitor cap + local visitorCapInput + while not tonumber(visitorCapInput) or visitorCapInput == "" do + _, visitorCapInput = script.showInputPrompt("Visitors", "Vistitor cap? Settings visitor cap " .. originalVisitorCap .. "\n(assuming wasn't changed before first call of this script)") + end + siteInfo.visitorCap = tostring(tonumber(visitorCap) or originalVisitorCap) + end + end + -- use settings + if siteInfo.hermit == "true" then + dfhack.run_command("hermit enable") + -- NOTE: could, maybe should cancel max-wave repeat here + else + dfhack.run_command("hermit disable") + dfhack.run_command("repeat -name max-wave -timeUnits months -time 1 -command [ max-wave " .. siteInfo.migrantCap .. " " .. siteInfo.popCap .. " ]") + df.global.d_init.strict_population_cap = tonumber(siteInfo.strictPopCap) + df.global.d_init.visitor_cap = tonumber(siteInfo.visitorCap) + end + end) end local function help() @@ -87,8 +87,8 @@ local function help() end local action_switch = { - ["reenter-settings"] = function() popControl(true) end, - ["on-load"] = function() popControl(false) end + ["reenter-settings"] = function() popControl(true) end, + ["on-load"] = function() popControl(false) end } setmetatable(action_switch, {__index = function() return help end}) From 4fa3a8d520ecf0abb7a783186a006664bc2c8aa6 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Thu, 9 Jun 2022 19:02:14 +0100 Subject: [PATCH 0016/3514] Add error when running pop-control reenter-settings outside of fortress mode --- pop-control.lua | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/pop-control.lua b/pop-control.lua index 4f360906c3..90ba762354 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -20,17 +20,24 @@ originalPopCap = originalPopCap or df.global.d_init.population_cap originalStrictPopCap = originalStrictPopCap or df.global.d_init.strict_population_cap originalVisitorCap = originalVisitorCap or df.global.d_init.visitor_cap -if df.global.gamemode ~= 0 then - return -- not fort mode! -end - -if not persistTable.GlobalTable.fortPopInfo then - persistTable.GlobalTable.fortPopInfo = {} -end - -local siteId = df.global.ui.site_id - local function popControl(forceEnterSettings) + if df.global.gamemode ~= 0 then + if forceEnterSettings then + -- did reenter-settings, show an error + qerror("Not in fort mode") + return + else + -- silent automatic behaviour + return + end + end + + if not persistTable.GlobalTable.fortPopInfo then + persistTable.GlobalTable.fortPopInfo = {} + end + + local siteId = df.global.ui.site_id + script.start(function() local siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] if not siteInfo or forceEnterSettings then From 01c61059f8a103184c7d5646e04be3a300821792 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Thu, 9 Jun 2022 19:22:49 +0100 Subject: [PATCH 0017/3514] Remove whitespace only lines in pop-control.lua? --- pop-control.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pop-control.lua b/pop-control.lua index 90ba762354..78c1fcd9e6 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -31,13 +31,13 @@ local function popControl(forceEnterSettings) return end end - + if not persistTable.GlobalTable.fortPopInfo then persistTable.GlobalTable.fortPopInfo = {} end - + local siteId = df.global.ui.site_id - + script.start(function() local siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] if not siteInfo or forceEnterSettings then From 4d0f66e32211fd82cdb27c4c7aa4979f17d0c6bd Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Mon, 13 Jun 2022 21:03:32 +0100 Subject: [PATCH 0018/3514] Replace magic number 0 with df.game_mode.DWARF --- pop-control.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pop-control.lua b/pop-control.lua index 78c1fcd9e6..bce89fc8d1 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -21,7 +21,7 @@ originalStrictPopCap = originalStrictPopCap or df.global.d_init.strict_populatio originalVisitorCap = originalVisitorCap or df.global.d_init.visitor_cap local function popControl(forceEnterSettings) - if df.global.gamemode ~= 0 then + if df.global.gamemode ~= df.game_mode.DWARF then if forceEnterSettings then -- did reenter-settings, show an error qerror("Not in fort mode") From f1432cda59bf798b7476619a3de0c76a86cbabee Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Sat, 18 Jun 2022 21:29:51 +0100 Subject: [PATCH 0019/3514] Update max-wave.lua Co-authored-by: Myk --- max-wave.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/max-wave.lua b/max-wave.lua index 6993196f1c..a0304fa76c 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -4,7 +4,7 @@ max-wave ======== -Set the population cap to the lesser of current_pop + wave_size or max_pop. +Limit the number of migrants that can arrive in the next wave. Use with the `repeat` command to set a rolling immigration limit. Usage examples:: From d2d91fe2e79e70297098b470c64815013e300f67 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Sat, 18 Jun 2022 21:29:57 +0100 Subject: [PATCH 0020/3514] Update max-wave.lua Co-authored-by: Myk --- max-wave.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index a0304fa76c..8955fd3d0f 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -7,10 +7,13 @@ max-wave Limit the number of migrants that can arrive in the next wave. Use with the `repeat` command to set a rolling immigration limit. -Usage examples:: +Syntax:: - max-wave wave_size (max_pop) + max-wave [max_pop] +Examples:: + + max-wave 5 repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] The first example is abstract and only sets the population cap once; From c3ba343ea4f15b6f1d3ef37e138b5f8b3110d9df Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Sat, 18 Jun 2022 21:30:05 +0100 Subject: [PATCH 0021/3514] Update max-wave.lua Co-authored-by: Myk --- max-wave.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index 8955fd3d0f..444383663e 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -16,8 +16,8 @@ Examples:: max-wave 5 repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] -The first example is abstract and only sets the population cap once; -the second will update the population cap monthly, allowing a +The first example ensures the next migration wave has 5 or fewer +dwarves. The second example ensures all future seasons have a maximum of 10 immigrants per wave, up to a total population of 200. ]====] From 0e6f821bd791c0632d59e344293b0e4d15c0586a Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Sat, 18 Jun 2022 22:18:09 +0100 Subject: [PATCH 0022/3514] Implement remaining suggested changes to max-wave.lua --- max-wave.lua | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index 444383663e..a4eaacaba2 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -1,10 +1,10 @@ --Dynamically limit the next immigration wave ---By Loci, modified by Fleeting Frames +--By Loci, modified by Fleeting Frames and Tachytaenius --[====[ max-wave ======== -Limit the number of migrants that can arrive in the next wave. +Limit the number of migrants that can arrive in the next wave by overriding the population cap value in data/init/d_init.txt (not safe with gui/settings-manager) Use with the `repeat` command to set a rolling immigration limit. Syntax:: @@ -29,19 +29,20 @@ local max_pop = tonumber(args[2]) local current_pop = 0 if not wave_size then - print('max-wave: wave_size required') - return + qerror('max-wave: wave_size required') end +local function isCitizen(unit) + return dfhack.units.isCitizen(unit) or + (dfhack.units.isOwnCiv(unit) and + dfhack.units.isAlive(unit) and + df.global.world.raws.creatures.all[unit.race].caste[unit.caste].flags.CAN_LEARN and + not (dfhack.units.isMerchant(unit) or dfhack.units.isForest(unit) or unit.flags1.diplomat or unit.flags2.visitor)) + end + --One would think the game would track this value somewhere... for k,v in ipairs(df.global.world.units.active) do - if dfhack.units.isCitizen(v) or - (dfhack.units.isOwnCiv(v) and - dfhack.units.isAlive(v) and - df.global.world.raws.creatures.all[v.race].caste[v.caste].flags.CAN_LEARN and - not (dfhack.units.isMerchant(v) or dfhack.units.isForest(v) or v.flags1.diplomat or v.flags2.visitor) - ) - then + if isCitizen(v) then current_pop = current_pop + 1 end end @@ -50,5 +51,9 @@ local new_limit = current_pop + wave_size if max_pop and new_limit > max_pop then new_limit = max_pop end -df.global.d_init.population_cap = new_limit -print('max-wave: Population cap set to '.. new_limit) +if new_limit == df.global.d_init.population_cap then + print('max-wave: Population cap (' .. new_limit .. ') not changed, maximum population reached') +else + df.global.d_init.population_cap = new_limit + print('max-wave: Population cap set to ' .. new_limit) +end From 1cd2378d913ce9265196b2ca6d311f60e944196c Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Thu, 23 Jun 2022 18:38:52 +0100 Subject: [PATCH 0023/3514] Added view-settings to pop-control --- pop-control.lua | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/pop-control.lua b/pop-control.lua index bce89fc8d1..6ec6a391a0 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -9,7 +9,9 @@ Available arguments: - ``on-load`` automatically checks for settings for this site and prompts them to be entered if not present -- ``reenter-settings`` revise settings for this site +- ``reenter-settings`` lets you revise settings for this site + +- ``view-settings`` shows you the current settings for this site ]====] local script = require("gui.script") @@ -89,13 +91,32 @@ local function popControl(forceEnterSettings) end) end +local function viewSettings() + local siteId = df.global.ui.site_id + if not persistTable.GlobalTable.fortPopInfo or not persistTable.GlobalTable.fortPopInfo[siteId] then + print("Could not find site information") + return + end + local siteInfo = persistTable.GlobalTable.fortPopInfo[siteId] + if siteInfo.hermit == "true" then + print("Hermit: true") + return + end + print("Hermit: false") + print("Migrant cap: " .. siteInfo.migrantCap) + print("Population cap: " .. siteInfo.popCap) + print("Strict population cap: " .. siteInfo.strictPopCap) + print("Visitor cap: " .. siteInfo.visitorCap) +end + local function help() - print("syntax: pop-control [reenter-settings|on-load]") + print("syntax: pop-control [on-load|reenter-settings|view-settings]") end local action_switch = { + ["on-load"] = function() popControl(false) end, ["reenter-settings"] = function() popControl(true) end, - ["on-load"] = function() popControl(false) end + ["view-settings"] = function() viewSettings() end } setmetatable(action_switch, {__index = function() return help end}) From da8ade914657e42b9cb5030677166e0f8cccdd78 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Fri, 24 Jun 2022 11:42:15 +0100 Subject: [PATCH 0024/3514] Remove accidental changelog line --- changelog.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 503734fc03..641daa00bc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -17,7 +17,6 @@ that repo. - `max-wave`: dynamically limit the next immigration wave, can be set to repeat - `pop-control`: persistent per fortress population cap, `hermit`, and `max-wave` management -- `assign-minecarts`: assign minecarts to hauling routes that don't have one ## Fixes From 28b1c3c5752ec1530763119c10ed3767a193fc66 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Fri, 24 Jun 2022 11:56:45 +0100 Subject: [PATCH 0025/3514] Implement some suggested changes --- max-wave.lua | 6 +++++- pop-control.lua | 13 ++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/max-wave.lua b/max-wave.lua index a4eaacaba2..6ca4a4607a 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -4,7 +4,10 @@ max-wave ======== -Limit the number of migrants that can arrive in the next wave by overriding the population cap value in data/init/d_init.txt (not safe with gui/settings-manager) +Limit the number of migrants that can arrive in the next wave by +overriding the population cap value in data/init/d_init.txt. +Not safe with gui/settings-manager as it will commit temporary +population cap changes permanently to file. Use with the `repeat` command to set a rolling immigration limit. Syntax:: @@ -29,6 +32,7 @@ local max_pop = tonumber(args[2]) local current_pop = 0 if not wave_size then + print(dfhack.script_help()) qerror('max-wave: wave_size required') end diff --git a/pop-control.lua b/pop-control.lua index 6ec6a391a0..fbad6fefdb 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -3,15 +3,18 @@ --[====[ pop-control =========== -Controls the various population caps as well as use of max-wave and hermit persistently per fortress -Intended to be placed within ``onMapLoad.init`` as ``pop-control on-load`` +Controls hermit and the various population caps per-fortress. +Not safe with gui/settings-manager as it will commit temporary +population cap changes permanently to file. +Intended to be placed within ``onMapLoad.init`` as ``pop-control on-load``. Available arguments: -- ``on-load`` automatically checks for settings for this site and prompts them to be entered if not present +- ``on-load`` automatically checks for settings for this site and + prompts them to be entered if not present. -- ``reenter-settings`` lets you revise settings for this site +- ``reenter-settings`` lets you revise settings for this site. -- ``view-settings`` shows you the current settings for this site +- ``view-settings`` shows you the current settings for this site. ]====] local script = require("gui.script") From 52c108699980fb9d8259fe3142bfd7590137a9c5 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Thu, 15 Sep 2022 10:55:34 +0100 Subject: [PATCH 0026/3514] Move max-wave and pop-control documentation into docs/ --- docs/max-wave.rst | 30 ++++++++++++++++++++++++++++++ docs/pop-control.rst | 19 +++++++++++++++++++ max-wave.lua | 27 --------------------------- pop-control.lua | 19 ------------------- 4 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 docs/max-wave.rst create mode 100644 docs/pop-control.rst diff --git a/docs/max-wave.rst b/docs/max-wave.rst new file mode 100644 index 0000000000..0926064ed5 --- /dev/null +++ b/docs/max-wave.rst @@ -0,0 +1,30 @@ +pop-control +=========== + +.. dfhack-tool:: + :summary: Dynamically limit the next immigration wave. + :tags: fort units + +Limit the number of migrants that can arrive in the next wave by +overriding the population cap value from data/init/d_init.txt. +Use with the `repeat` command to set a rolling immigration limit. +Original credit was for Loci. + +Usage +----- + +:: + + max-wave [max_pop] + +Examples +-------- + +:: + + max-wave 5 + repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] + +The first example ensures the next migration wave has 5 or fewer +immigrants. The second example ensures all future seasons have a +maximum of 10 immigrants per wave, up to a total population of 200. diff --git a/docs/pop-control.rst b/docs/pop-control.rst new file mode 100644 index 0000000000..e548c8aa80 --- /dev/null +++ b/docs/pop-control.rst @@ -0,0 +1,19 @@ +pop-control +=========== + +.. dfhack-tool:: + :summary: Controls population caps, hermit, and max-wave persistently per-fort. + :tags: fort units + +Controls hermit and the various population caps per-fortress. +Intended to be placed within ``onMapLoad.init`` as ``pop-control on-load``. + +Arguments +--------- + +- ``on-load`` automatically checks for settings for this site and + prompts them to be entered if not present. + +- ``reenter-settings`` lets you revise settings for this site. + +- ``view-settings`` shows you the current settings for this site. diff --git a/max-wave.lua b/max-wave.lua index 6ca4a4607a..bb78e91304 100644 --- a/max-wave.lua +++ b/max-wave.lua @@ -1,30 +1,3 @@ ---Dynamically limit the next immigration wave ---By Loci, modified by Fleeting Frames and Tachytaenius ---[====[ - -max-wave -======== -Limit the number of migrants that can arrive in the next wave by -overriding the population cap value in data/init/d_init.txt. -Not safe with gui/settings-manager as it will commit temporary -population cap changes permanently to file. -Use with the `repeat` command to set a rolling immigration limit. - -Syntax:: - - max-wave [max_pop] - -Examples:: - - max-wave 5 - repeat -time 1 -timeUnits months -command [ max-wave 10 200 ] - -The first example ensures the next migration wave has 5 or fewer -dwarves. The second example ensures all future seasons have a -maximum of 10 immigrants per wave, up to a total population of 200. - -]====] - local args = {...} local wave_size = tonumber(args[1]) diff --git a/pop-control.lua b/pop-control.lua index fbad6fefdb..2181a5da9d 100644 --- a/pop-control.lua +++ b/pop-control.lua @@ -1,22 +1,3 @@ --- Script to control the various population caps as well as use of max-wave and hermit persistently per fortress --- by Tachytaenius ---[====[ -pop-control -=========== -Controls hermit and the various population caps per-fortress. -Not safe with gui/settings-manager as it will commit temporary -population cap changes permanently to file. -Intended to be placed within ``onMapLoad.init`` as ``pop-control on-load``. -Available arguments: - -- ``on-load`` automatically checks for settings for this site and - prompts them to be entered if not present. - -- ``reenter-settings`` lets you revise settings for this site. - -- ``view-settings`` shows you the current settings for this site. -]====] - local script = require("gui.script") local persistTable = require("persist-table") From d189b22b42cc351825e92224ba8d7c9d34cdd8e5 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Thu, 15 Sep 2022 10:58:21 +0100 Subject: [PATCH 0027/3514] Fix foolish error in max-wave's documentation --- docs/max-wave.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/max-wave.rst b/docs/max-wave.rst index 0926064ed5..56df3d7b9e 100644 --- a/docs/max-wave.rst +++ b/docs/max-wave.rst @@ -1,5 +1,5 @@ -pop-control -=========== +max-wave +======== .. dfhack-tool:: :summary: Dynamically limit the next immigration wave. From b1036d9c5dce9cdf31184f73da17174252524bd6 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Thu, 15 Sep 2022 12:28:35 +0100 Subject: [PATCH 0028/3514] Add warnings about population cap overriding. --- docs/gui/settings-manager.rst | 3 +++ docs/max-wave.rst | 4 ++++ docs/pop-control.rst | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/docs/gui/settings-manager.rst b/docs/gui/settings-manager.rst index 195ba31d74..fdd1fc099e 100644 --- a/docs/gui/settings-manager.rst +++ b/docs/gui/settings-manager.rst @@ -11,6 +11,9 @@ back to the init files so they will be loaded the next time you start DF. For settings that can be dynamically adjusted, such as the population cap, the active value used by the game is updated immediately. +Editing the population caps will override any modifications made by scripts such +as `max-wave`. + Usage ----- diff --git a/docs/max-wave.rst b/docs/max-wave.rst index 56df3d7b9e..41d297f2dd 100644 --- a/docs/max-wave.rst +++ b/docs/max-wave.rst @@ -10,6 +10,10 @@ overriding the population cap value from data/init/d_init.txt. Use with the `repeat` command to set a rolling immigration limit. Original credit was for Loci. +If you edit the population caps using `gui/settings-manager` after +running this script, your population caps will be reset and you may +get more migrants than you expected. + Usage ----- diff --git a/docs/pop-control.rst b/docs/pop-control.rst index e548c8aa80..1c85de0a47 100644 --- a/docs/pop-control.rst +++ b/docs/pop-control.rst @@ -8,6 +8,10 @@ pop-control Controls hermit and the various population caps per-fortress. Intended to be placed within ``onMapLoad.init`` as ``pop-control on-load``. +If you edit the population caps using `gui/settings-manager` after +running this script, your population caps will be reset and you may +get more migrants than you expected. + Arguments --------- From e1503df467fc2bf3867b80b5b7b566ca5af97bf4 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 14 Sep 2022 23:13:56 +0100 Subject: [PATCH 0029/3514] Fix incorrect warn-stealers documentation and remove deprecated header. --- docs/warn-stealers.rst | 2 +- warn-stealers.lua | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/warn-stealers.rst b/docs/warn-stealers.rst index 3bfc25266e..d9fa26162d 100644 --- a/docs/warn-stealers.rst +++ b/docs/warn-stealers.rst @@ -14,4 +14,4 @@ Usage :: - warn-stealers [start|stop] + warn-stealers [enable|disable] diff --git a/warn-stealers.lua b/warn-stealers.lua index aa10f4ee2f..74027fbd5c 100644 --- a/warn-stealers.lua +++ b/warn-stealers.lua @@ -1,13 +1,4 @@ --- Script to warn when creatures that may steal food become visible --@ enable = true ---[====[ -warn-stealers -============= -Will make a zoomable announcement whenever a creature that can eat food, guzzle drinks, or steal items enters the map and moves into a revealed location. -Usage:: - - warn-stealers [start|stop] -]====] local eventful = require("plugins.eventful") local repeatUtil = require("repeat-util") From b04f4e72c49f51db15814ef82711c59abb3317f5 Mon Sep 17 00:00:00 2001 From: Tachytaenius Date: Wed, 14 Sep 2022 23:42:38 +0100 Subject: [PATCH 0030/3514] Update warn-stealers.rst --- docs/warn-stealers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/warn-stealers.rst b/docs/warn-stealers.rst index d9fa26162d..e934881ac0 100644 --- a/docs/warn-stealers.rst +++ b/docs/warn-stealers.rst @@ -14,4 +14,4 @@ Usage :: - warn-stealers [enable|disable] + enable warn-stealers From 09000acd0d9a01e4db000b247ca9a6a9b97d119d Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 12 Sep 2022 16:00:05 -0700 Subject: [PATCH 0031/3514] dismiss launcher if parent screen changes not just if the new parent screen is a dfhack screen --- gui/launcher.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gui/launcher.lua b/gui/launcher.lua index c8bc3a0f98..8c79765714 100644 --- a/gui/launcher.lua +++ b/gui/launcher.lua @@ -516,11 +516,10 @@ function LauncherUI:run_command(reappear, command) print() print(output) end - -- if we displayed a new dfhack screen, don't come back up even if reappear + -- if we displayed a different screen, don't come back up even if reappear -- is true so the user can interact with the new screen. local parent_focus = dfhack.gui.getFocusString(self._native.parent) - if not reappear or (parent_focus:startswith('dfhack/') and - parent_focus ~= self.parent_focus) then + if not reappear or parent_focus ~= self.parent_focus then self:dismiss() return end From 519b5ffcf4477addd304d98ef7c2293c3b335668 Mon Sep 17 00:00:00 2001 From: Myk Date: Mon, 12 Sep 2022 20:40:31 -0700 Subject: [PATCH 0032/3514] Remove unused script module reference --- gui/launcher.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/launcher.lua b/gui/launcher.lua index 8c79765714..2a9fa95824 100644 --- a/gui/launcher.lua +++ b/gui/launcher.lua @@ -10,7 +10,6 @@ Stub documentation. local gui = require('gui') local helpdb = require('helpdb') local json = require('json') -local script = require('gui.script') local utils = require('utils') local widgets = require('gui.widgets') From 39dd27b40a4139eb44d33dfbccf9e1bfaef83edb Mon Sep 17 00:00:00 2001 From: myk002 Date: Wed, 14 Sep 2022 09:26:31 -0700 Subject: [PATCH 0033/3514] detect changes in parent viewscreen address instead of using the focus string, which will not change even if the parent screen is replaced with another instance of the same screen --- gui/launcher.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gui/launcher.lua b/gui/launcher.lua index 2a9fa95824..2fe362da29 100644 --- a/gui/launcher.lua +++ b/gui/launcher.lua @@ -372,7 +372,6 @@ LauncherUI.ATTRS{ frame_title='DFHack Launcher', frame_style = gui.GREY_LINE_FRAME, focus_path='launcher', - parent_focus=DEFAULT_NIL, } function LauncherUI:init() @@ -504,6 +503,8 @@ function LauncherUI:run_command(reappear, command) if #command == 0 then return end dfhack.addCommandToHistory(HISTORY_ID, HISTORY_FILE, command) record_command(command) + -- remember the previous parent screen address so we can detect changes + local _,prev_parent_addr = self._native.parent:sizeof() -- remove our viewscreen from the stack while we run the command. this -- allows hotkey guards and tools that interact with the top viewscreen -- without checking whether it is active to work reliably. @@ -517,13 +518,12 @@ function LauncherUI:run_command(reappear, command) end -- if we displayed a different screen, don't come back up even if reappear -- is true so the user can interact with the new screen. - local parent_focus = dfhack.gui.getFocusString(self._native.parent) - if not reappear or parent_focus ~= self.parent_focus then + local _,parent_addr = self._native.parent:sizeof() + if not reappear or parent_addr ~= prev_parent_addr then self:dismiss() return end -- reappear and show the command output - self.parent_focus = parent_focus self.subviews.edit:set_text('') self:on_edit_input('') self.subviews.help:set_help(('> %s\n\n%s'):format(command, output)) @@ -599,7 +599,7 @@ if view then -- hotkey a second time) should close the dialog view:dismiss() else - view = LauncherUI{parent_focus=dfhack.gui.getCurFocus(true)} + view = LauncherUI{} view:show() local initial_command = table.concat({...}, ' ') view.subviews.edit:set_text(initial_command) From 486063371e8287bfe548c8f1208908778a463f52 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 18 Sep 2022 08:38:03 -0700 Subject: [PATCH 0034/3514] use the new getMousePos API --- gui/blueprint.lua | 8 +++----- gui/quantum.lua | 10 ++++------ gui/quickfort.lua | 5 +++++ 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/gui/blueprint.lua b/gui/blueprint.lua index 02d6500265..e5dd2425f2 100644 --- a/gui/blueprint.lua +++ b/gui/blueprint.lua @@ -481,11 +481,9 @@ function BlueprintUI:onInput(keys) local pos = nil if keys._MOUSE_L then - local x, y = dfhack.screen.getMousePos() - if gui.is_in_rect(guidm.getPanelLayout().map, x, y) then - pos = xyz2pos(df.global.window_x + x - 1, - df.global.window_y + y - 1, - df.global.window_z) + local map_pos = xyz2pos(dfhack.gui.getMousePos()) + if map_pos.x >= 0 then + pos = map_pos guidm.setCursorPos(pos) end elseif keys.SELECT then diff --git a/gui/quantum.lua b/gui/quantum.lua index 9dad8b8dab..11c5f86ee0 100644 --- a/gui/quantum.lua +++ b/gui/quantum.lua @@ -229,13 +229,11 @@ function QuantumUI:onInput(keys) local pos = nil if keys._MOUSE_L then - local x, y = dfhack.screen.getMousePos() - if gui.is_in_rect(self.df_layout.map, x, y) then - pos = xyz2pos(df.global.window_x + x - 1, - df.global.window_y + y - 1, - df.global.window_z) + local map_pos = xyz2pos(dfhack.gui.getMousePos()) + if map_pos.x >= 0 then + pos = map_pos guidm.setCursorPos(pos) - end + end elseif keys.SELECT then pos = guidm.getCursorPos() end diff --git a/gui/quickfort.lua b/gui/quickfort.lua index cdeaf566f1..9c2c6a6406 100644 --- a/gui/quickfort.lua +++ b/gui/quickfort.lua @@ -623,6 +623,11 @@ function QuickfortUI:onInput(keys) end end + if keys._MOUSE_L then + local pos = xyz2pos(dfhack.gui.getMousePos()) + if pos.x >= 0 then guidm.setCursorPos(pos) end + end + return self:propagateMoveKeys(keys) end From a39d044d2a134ae63492f1ff66f23c159aab7bc8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 18 Sep 2022 13:32:52 -0700 Subject: [PATCH 0035/3514] use new dfhack.gui.getMousePos return behavior --- gui/blueprint.lua | 5 ++--- gui/quantum.lua | 7 +++---- gui/quickfort.lua | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/gui/blueprint.lua b/gui/blueprint.lua index e5dd2425f2..6b284a82c2 100644 --- a/gui/blueprint.lua +++ b/gui/blueprint.lua @@ -481,9 +481,8 @@ function BlueprintUI:onInput(keys) local pos = nil if keys._MOUSE_L then - local map_pos = xyz2pos(dfhack.gui.getMousePos()) - if map_pos.x >= 0 then - pos = map_pos + pos = dfhack.gui.getMousePos() + if pos then guidm.setCursorPos(pos) end elseif keys.SELECT then diff --git a/gui/quantum.lua b/gui/quantum.lua index 11c5f86ee0..69a830dba3 100644 --- a/gui/quantum.lua +++ b/gui/quantum.lua @@ -229,11 +229,10 @@ function QuantumUI:onInput(keys) local pos = nil if keys._MOUSE_L then - local map_pos = xyz2pos(dfhack.gui.getMousePos()) - if map_pos.x >= 0 then - pos = map_pos + pos = dfhack.gui.getMousePos() + if pos then guidm.setCursorPos(pos) - end + end elseif keys.SELECT then pos = guidm.getCursorPos() end diff --git a/gui/quickfort.lua b/gui/quickfort.lua index 9c2c6a6406..f955043908 100644 --- a/gui/quickfort.lua +++ b/gui/quickfort.lua @@ -624,8 +624,8 @@ function QuickfortUI:onInput(keys) end if keys._MOUSE_L then - local pos = xyz2pos(dfhack.gui.getMousePos()) - if pos.x >= 0 then guidm.setCursorPos(pos) end + local pos = dfhack.gui.getMousePos() + if pos then guidm.setCursorPos(pos) end end return self:propagateMoveKeys(keys) From 1fc667f63a82ee96da55acf2af1fc0cbd3400bb8 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 18 Sep 2022 13:34:13 -0700 Subject: [PATCH 0036/3514] update changelog --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 9524188ccb..b741351a1f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -31,6 +31,7 @@ that repo. - `devel/query`: increased default maxlength value from 257 to 2048 - `gui/gm-unit`: don't clear the list filter when you adjust a skill value - `gui/quickfort`: better formatting for the generated manager orders report +- `gui/quickfort`: you can now click on the map to move the blueprint anchor point to that tile instead of having to use the cursor movement keys - `gui/quickfort`: display an error message when the blueprints directory cannot be found - `quickfort`: library blueprints are now included by default in ``quickfort list`` output. Use the new ``--useronly`` (or just ``-u``) option to filter out library bluerpints. - `quickfort`: better error message when the blueprints directory cannot be found From 0fa2dee77b7307fb2ca74433e96a85ac2f23d9e8 Mon Sep 17 00:00:00 2001 From: Alex Noir Date: Tue, 20 Sep 2022 01:11:20 +0300 Subject: [PATCH 0037/3514] Update adv-rumors to 0.3 with new features (PR pending) --- adv-rumors.lua | 181 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 146 insertions(+), 35 deletions(-) diff --git a/adv-rumors.lua b/adv-rumors.lua index 1099df621b..922d55ffa7 100644 --- a/adv-rumors.lua +++ b/adv-rumors.lua @@ -1,5 +1,5 @@ --- Improve "Bring up specific incident or rumor" menu in Adventure mode ---@ module = true +-- Improve "Bring up specific incident or rumor", "Ask for Directions" and "Ask about Somebody" menus in Adventure mode +--@ enable = true --[====[ adv-rumors @@ -8,76 +8,187 @@ Improves the "Bring up specific incident or rumor" menu in Adventure mode. - Moves entries into one line - Adds a "slew" keyword for filtering, making it easy to find your kills and not your companions' +- Adds "gaining", "placement" and "giving" keywords for artifacts - Trims repetitive words ]====] --======================== --- Author : 1337G4mer on bay12 and reddit --- Version : 0.2 --- Description : A small utility based on dfhack to improve the rumor UI in adventure mode. +-- Author : 1337G4mer on bay12 and reddit, version 0.3 by Crystalwarrior on Discord +-- Version : 0.3 +-- Description : An utility based on dfhack to improve the rumor, directions and ask about... UIs in adventure mode. -- --- In game when you want to boast about your kill to someone. Start conversation and choose --- the menu "Bring up specific incident or rumor" --- type rumors in dfhack window and hit enter. Or do the below keybind and use that directly from DF window. +-- "Bring up specific incident or rumor", "Ask for Directions" and "Ask about Somebody" menus will +-- be automatically improved with better searchability and organization. -- -- Prior Configuration: (you can skip this if you want) -- Set the three boolean values below and play around with the script as to how you like -- improveReadability = will move everything in one line --- addKeywordSlew = will add a keyword for filtering using slew, making it easy to find your kills and not your companion's +-- addPrefix = will add prefixes for every supported type of incident (Fight: , Event: , Rumor: , Place: , Person: ) +-- addKeywords = will add a lot more keywords to make the rumors more searchable, such as missing names, "slew" keyword, "me" where "you" is used, etc. -- shortenString = will further shorten the line to = slew "XYZ" ( "n time" ago in " Region") --======================= +local improveReadability = true +local addPrefix = true +local addKeywords = true +local shortenString = true local utils = require "utils" -local names_blacklist = utils.invert{"a", "an", "you", "attacked", "slew", "was", "slain", "by"} - function condenseChoiceTitle(choice) while #choice.title > 1 do - choice.title[0].value = choice.title[0].value .. ' ' .. choice.title[1].value + choice.title[0].value = choice.title[0].value .. choice.title[1].value choice.title:erase(1) end end +function addPrefixByType(choice) + if choice.choice.type == df.talk_choice_type.SummarizeConflict and not string.find(choice.title[0].value, "^Fight: ") then + choice.title[0].value = "Fight: " .. choice.title[0].value + end + if choice.choice.type == df.talk_choice_type.BringUpEvent and not string.find(choice.title[0].value, "^Event: ") then + choice.title[0].value = "Event: " .. choice.title[0].value + end + if choice.choice.type == df.talk_choice_type.SpreadRumor and not string.find(choice.title[0].value, "^Rumor: ") then + choice.title[0].value = "Rumor: " .. choice.title[0].value + end + if choice.choice.type == df.talk_choice_type.AskDirectionsPlace and not string.find(choice.title[0].value, "^Place: ") then + choice.title[0].value = "Place: " .. choice.title[0].value + end + if choice.choice.type == df.talk_choice_type.AskAboutPerson and not string.find(choice.title[0].value, "^Whom: ") then + choice.title[0].value = "Whom: " .. choice.title[0].value + end + if choice.choice.type == df.talk_choice_type.AskWhereabouts and not string.find(choice.title[0].value, "^Who: ") then + choice.title[0].value = "Who: " .. choice.title[0].value + end +end + +function addKeywordsForChoice(choice) + if string.find(choice.title[0].value, "slew") then + addKeyword(choice, 'slew') + end + if string.find(choice.title[0].value, "attack") then + addKeyword(choice, 'attack') + end + if string.find(choice.title[0].value, " you ") or string.find(choice.title[0].value, " your ") then + addKeyword(choice, 'me') + end + if choice.choice.type == df.talk_choice_type.SummarizeConflict then + addKeyword(choice, "conflict") -- keyword 'conflict' already exists + end + if choice.choice.type == df.talk_choice_type.BringUpEvent then + addKeyword(choice, "event") + end + if choice.choice.type == df.talk_choice_type.SpreadRumor then + addKeyword(choice, "rumor") + end + if choice.choice.type == df.talk_choice_type.AskDirectionsPlace then + addKeyword(choice, "directions") + addKeyword(choice, "place") + end + if choice.choice.type == df.talk_choice_type.AskWhereabouts then + addKeyword(choice, "person") + addKeyword(choice, "whereabouts") + end + + -- Transform the whole thing into keywords barring blacklist + local names_blacklist = utils.invert{"the", "a", "an", "you", "your", "of", "to", "attacked", "slew", "was", "slain", "by"} + local title = choice.title[0].value + if title:find('%(') then + title = title:sub(1, title:find('%(') - 1) + end + local keywords = title:gmatch('%w+') + for keyword in keywords do + keyword = keyword:lower() + if not names_blacklist[keyword] then + addKeyword(choice, keyword) + end + end +end + +function shortenChoice(choice) + choice.title[0].value = choice.title[0].value + :gsub("Summarize the conflict in which +", "") + :gsub("This occurred +", "") + :gsub("Bring up +", "") + :gsub("Spread rumor of +", "") + :gsub("Ask about +", "") + :gsub("Ask for directions to +", "where is ") + :gsub("Ask for the whereabouts of +", "where is ") +end + function addKeyword(choice, keyword) + -- Prevent duplicate keywords + for i, kword in ipairs(choice.keywords) do + if kword.value == keyword then + return + end + end local keyword_ptr = df.new('string') keyword_ptr.value = keyword choice.keywords:insert('#', keyword_ptr) end function rumorUpdate() - local improveReadability = true - local addKeywordSlew = true - local shortenString = true - local addKeywordNames = true - for i, choice in ipairs(df.global.ui_advmode.conversation.choices) do - if choice.choice.type == df.talk_choice_type.SummarizeConflict then + if choice.choice.type == df.talk_choice_type.SummarizeConflict or + choice.choice.type == df.talk_choice_type.BringUpEvent or + choice.choice.type == df.talk_choice_type.SpreadRumor or + choice.choice.type == df.talk_choice_type.AskAboutPerson or + choice.choice.type == df.talk_choice_type.AskDirectionsPlace or + choice.choice.type == df.talk_choice_type.AskWhereabouts then if improveReadability then condenseChoiceTitle(choice) end + if addPrefix then + addPrefixByType(choice) + end if shortenString then condenseChoiceTitle(choice) - choice.title[0].value = choice.title[0].value - :gsub("Summarize the conflict in which +", "") - :gsub("This occurred +", "") + shortenChoice(choice) end - if addKeywordSlew then - if string.find(choice.title[0].value, "slew") then - addKeyword(choice, 'slew') - end - end - if addKeywordNames then - local title = choice.title[0].value - for keyword in title:sub(1, title:find('%(') - 1):gmatch('%w+') do - keyword = dfhack.utf2df(dfhack.df2utf(keyword):lower()) - if not names_blacklist[keyword] then - addKeyword(choice, keyword) - end - end + if addKeywords then + addKeywordsForChoice(choice) end end end end -rumorUpdate() +active = active or false +function rumorloop() + if active then dfhack.timeout(1, 'frames', check) end +end + +local first_choice = nil +function check() + if not dfhack.world.isAdventureMode() then + return + end + if df.global.ui_advmode.menu ~= df.ui_advmode_menu.ConversationSpeak then + first_choice = nil + rumorloop() + return + end + if df.global.ui_advmode.conversation.choices[0] ~= first_choice then + rumorUpdate() + first_choice = df.global.ui_advmode.conversation.choices[0] + end + rumorloop() +end + +function dfhack.onStateChange.advRumorConversation (code) + if code == SC_VIEWSCREEN_CHANGED and dfhack.isWorldLoaded() then + local scr = dfhack.gui.getCurViewscreen() + set_listener_active(scr._type == df.viewscreen_dungeonmodest) + end +end + +function set_listener_active(tog) + active = tog + if active then + print("activating rumor listener") + check() + else + print("de-activating rumor listener") + end +end \ No newline at end of file From af285b25365c8db9f151e39f1b32a5a57ed39beb Mon Sep 17 00:00:00 2001 From: Alex Noir Date: Tue, 20 Sep 2022 01:40:07 +0300 Subject: [PATCH 0038/3514] add "new_choice" helper function for future possibilities in expanding the talking system --- adv-rumors.lua | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/adv-rumors.lua b/adv-rumors.lua index 922d55ffa7..e2d15b4465 100644 --- a/adv-rumors.lua +++ b/adv-rumors.lua @@ -129,6 +129,26 @@ function addKeyword(choice, keyword) choice.keywords:insert('#', keyword_ptr) end +-- Helper function to create new dialog choices +function new_choice(choice_type, title, keywords) + local dialog = df.global.ui_advmode.conversation + dialog.choices:insert("#", {new = df.ui_advmode.T_conversation.T_choices,}) + local choice_idx = #dialog.choices-1 + local choice = dialog.choices[choice_idx] + if choice.choice == nil then + choice.choice = df.talk_choice:new() + end + choice.choice.type = choice_type + choice.title:insert("#",df.new("string")) + choice.title[0].value=title + for i, key in ipairs(keywords) do + addKeyword(choice, key) + end + + dialog.page_bottom_choices[0] = choice_idx +end + +-- Condense the rumor system choices function rumorUpdate() for i, choice in ipairs(df.global.ui_advmode.conversation.choices) do if choice.choice.type == df.talk_choice_type.SummarizeConflict or @@ -154,28 +174,41 @@ function rumorUpdate() end end +-- Optionally add new choices in addition of existing ones +function choiceUpdate() + if df.global.ui_advmode.conversation.activity_event[0].menu == df.conversation_menu.MainMenu then + -- Essentially the "weather talking" exploit (as described by Rumrusher) in a single speaking action, put this in more menus to see its awesome potential. + new_choice(df.talk_choice_type.AskTargetAction, "Ask what will they do about it", {"initiative", "action", "speak", "opinion"}) + end +end + +-- Main Loop active = active or false function rumorloop() if active then dfhack.timeout(1, 'frames', check) end end -local first_choice = nil +-- Check if Continue Looping +local last_menu = nil function check() if not dfhack.world.isAdventureMode() then return end if df.global.ui_advmode.menu ~= df.ui_advmode_menu.ConversationSpeak then - first_choice = nil + last_menu = nil rumorloop() return end - if df.global.ui_advmode.conversation.choices[0] ~= first_choice then + if df.global.ui_advmode.conversation.activity_event[0].menu ~= last_menu then rumorUpdate() - first_choice = df.global.ui_advmode.conversation.choices[0] + -- Experimental "add extra choices" system, disabled by default. Uncomment the line below to test it out! + -- choiceUpdate() + last_menu = df.global.ui_advmode.conversation.activity_event[0].menu end rumorloop() end +-- onStateChange listener to start/stop looping in relevant context function dfhack.onStateChange.advRumorConversation (code) if code == SC_VIEWSCREEN_CHANGED and dfhack.isWorldLoaded() then local scr = dfhack.gui.getCurViewscreen() @@ -183,6 +216,7 @@ function dfhack.onStateChange.advRumorConversation (code) end end +-- Toggle Loop on/off function set_listener_active(tog) active = tog if active then From 8c03de45ba8e772239224b49af2617fc714fbaa9 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Tue, 20 Sep 2022 23:14:51 -0700 Subject: [PATCH 0039/3514] fix gui/blueprint tests --- test/gui/blueprint.lua | 39 +++++++-------------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/test/gui/blueprint.lua b/test/gui/blueprint.lua index 82405ebaa7..0e15b08cec 100644 --- a/test/gui/blueprint.lua +++ b/test/gui/blueprint.lua @@ -285,45 +285,20 @@ function test.reset_ui() end -- mouse support for selecting boundary tiles -local function click_mouse_and_test(screenx, screeny, should_mark, comment) - mock.patch(dfhack.screen, 'getMousePos', mock.func(screenx, screeny), +function test.set_with_mouse() + local pos = {x=df.global.window_x, + y=df.global.window_y, + z=df.global.window_z} + mock.patch(dfhack.gui, 'getMousePos', mock.func(pos), function() local view = load_ui() view:onInput({_MOUSE_L=true}) - if not should_mark then - expect.nil_(view.mark, comment) - else - local expected_mark = {x=df.global.window_x+screenx-1, - y=df.global.window_y+screeny-1, - z=df.global.window_z} - expect.table_eq(expected_mark, view.mark, comment) - send_keys('LEAVESCREEN') -- cancel selection - end + expect.table_eq(pos, view.mark, comment) + send_keys('LEAVESCREEN') -- cancel selection send_keys('LEAVESCREEN') -- cancel out of UI end) end -function test.set_with_mouse() - click_mouse_and_test(0, 0) - click_mouse_and_test(0, 5) - click_mouse_and_test(5, 0) - click_mouse_and_test(5, -1) - click_mouse_and_test(-1, 5) - - click_mouse_and_test(5, 7, true, 'interior tile') - - guidm.enterSidebarMode(df.ui_sidebar_mode.LookAround) - local _, screen_height = dfhack.screen.getWindowSize() - local map_x2 = dfhack.gui.getDwarfmodeViewDims().map_x2 - click_mouse_and_test(map_x2, 7, true, - 'just to left of border between map and blueprint gui') - click_mouse_and_test(map_x2 + 1, 7, false, - 'on border between map and blueprint gui') - click_mouse_and_test(5, screen_height - 2, true, 'above bottom border') - click_mouse_and_test(5, screen_height - 1, false, 'on bottom border') - guidm.enterSidebarMode(df.ui_sidebar_mode.Default) -end - -- live status line showing the dimensions of the currently selected area function test.render_status_line() local view = load_ui() From a1374b5726b3dd682ac41d21701f186e748c0974 Mon Sep 17 00:00:00 2001 From: myk002 Date: Fri, 23 Sep 2022 15:49:27 -0700 Subject: [PATCH 0040/3514] update docs for devel scripts --- docs/devel/all-bob.rst | 12 +- docs/devel/annc-monitor.rst | 18 ++- docs/devel/block-borders.rst | 12 +- docs/devel/check-other-ids.rst | 12 +- docs/devel/check-release.rst | 11 +- docs/devel/clear-script-env.rst | 20 ++- docs/devel/click-monitor.rst | 12 +- docs/devel/cmptiles.rst | 16 ++- docs/devel/dump-offsets.rst | 22 +-- docs/devel/eventful-client.rst | 12 +- docs/devel/export-dt-ini.rst | 10 +- docs/devel/find-offsets.rst | 43 +++--- docs/devel/find-primitive.rst | 21 +-- docs/devel/find-twbt.rst | 10 +- docs/devel/inject-raws.rst | 31 +++-- docs/devel/inspect-screen.rst | 15 ++- docs/devel/kill-hf.rst | 34 ++--- docs/devel/light.rst | 16 ++- docs/devel/list-filters.rst | 14 +- docs/devel/lsmem.rst | 13 +- docs/devel/lua-example.rst | 7 +- docs/devel/luacov.rst | 40 +++--- docs/devel/modstate-monitor.rst | 13 +- docs/devel/nuke-items.rst | 13 +- docs/devel/pop-screen.rst | 21 +-- docs/devel/prepare-save.rst | 17 ++- docs/devel/print-args.rst | 14 +- docs/devel/print-args2.rst | 13 +- docs/devel/print-event.rst | 14 +- docs/devel/query.rst | 209 ++++++++++++----------------- docs/devel/save-version.rst | 14 +- docs/devel/sc.rst | 28 ++-- docs/devel/scanitemother.rst | 26 +++- docs/devel/send-key.rst | 27 ++-- docs/devel/spawn-unit-helper.rst | 34 +++-- docs/devel/test-perlin.rst | 14 +- docs/devel/unit-path.rst | 14 +- docs/devel/visualize-structure.rst | 22 ++- docs/devel/watch-minecarts.rst | 13 +- 39 files changed, 518 insertions(+), 389 deletions(-) diff --git a/docs/devel/all-bob.rst b/docs/devel/all-bob.rst index c822b37927..a56ae8a28f 100644 --- a/docs/devel/all-bob.rst +++ b/docs/devel/all-bob.rst @@ -1,11 +1,15 @@ - devel/all-bob ============= .. dfhack-tool:: - :summary: todo. + :summary: Changes the first name of all units to "Bob".. :tags: dev - -Changes the first name of all units to "Bob". Useful for testing `modtools/interaction-trigger` events. + +Usage +----- + +:: + + devel/all-bob diff --git a/docs/devel/annc-monitor.rst b/docs/devel/annc-monitor.rst index 906058cf2f..72c1f24c03 100644 --- a/docs/devel/annc-monitor.rst +++ b/docs/devel/annc-monitor.rst @@ -1,15 +1,19 @@ - devel/annc-monitor ================== .. dfhack-tool:: - :summary: todo. + :summary: Track announcements and reports and echo them to the console. :tags: dev +This tool monitors announcements and reports and echoes their contents to the +console. + +Usage +----- + +:: -Displays announcements and reports in the console. + enable devel/annc-monitor + devel/annc-monitor report enable|disable -:enable|start: Begins monitoring -:disable|stop: Stops monitoring -:report enable: Show combat reports -:report disable: Only show announcements +Combat report monitoring is disabled by default. diff --git a/docs/devel/block-borders.rst b/docs/devel/block-borders.rst index 37b174ce23..a75293aa0b 100644 --- a/docs/devel/block-borders.rst +++ b/docs/devel/block-borders.rst @@ -1,12 +1,16 @@ - devel/block-borders =================== .. dfhack-tool:: - :summary: todo. + :summary: Outline map blocks on the map screen. :tags: dev map +This tool displays an overlay that highlights the borders of map blocks. See +:doc:`/docs/api/Maps` for details on map blocks. + +Usage +----- +:: -An overlay that draws borders of map blocks. See :doc:`/docs/api/Maps` for -details on map blocks. + devel/block-borders diff --git a/docs/devel/check-other-ids.rst b/docs/devel/check-other-ids.rst index 924a5c2df8..4861217100 100644 --- a/docs/devel/check-other-ids.rst +++ b/docs/devel/check-other-ids.rst @@ -1,12 +1,16 @@ - devel/check-other-ids ===================== .. dfhack-tool:: - :summary: todo. + :summary: Verify that game entities are referenced by the correct vectors. :tags: dev +This script runs through all ``world.items.other`` and ``world.buildings.other`` vectors +and verifies that the items contained in them have the expected types. + +Usage +----- +:: -This script runs through all world.items.other and world.buildings.other vectors -and verifies that the items contained in them have the expected types. + devel/check-other-ids diff --git a/docs/devel/check-release.rst b/docs/devel/check-release.rst index 70265281c7..91083a3646 100644 --- a/docs/devel/check-release.rst +++ b/docs/devel/check-release.rst @@ -2,8 +2,15 @@ devel/check-release =================== .. dfhack-tool:: - :summary: todo. + :summary: Perform basic checks for DFHack release readiness. :tags: dev +This script is run as part of the DFHack release process to check that release +flags are properly set. -Basic checks for release readiness +Usage +----- + +:: + + devel/check-release diff --git a/docs/devel/clear-script-env.rst b/docs/devel/clear-script-env.rst index 70fa3a22f3..ea3af0813f 100644 --- a/docs/devel/clear-script-env.rst +++ b/docs/devel/clear-script-env.rst @@ -1,10 +1,24 @@ - devel/clear-script-env ====================== .. dfhack-tool:: - :summary: todo. + :summary: Clear a lua script environment. :tags: dev +This tool can clear the environment of the specified lua script(s). This is +useful during development since if you remove a global function, an old version +of the function will stick around in the environment until it is cleared. + +Usage +----- + +:: + + devel/clear-script-env