From c1960e0da718bb5f83352fc00fe3d558c751216e Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 06:31:03 -0800 Subject: [PATCH 01/36] add workorder-detail-fix --- changelog.txt | 1 + docs/workorder-detail-fix.rst | 24 +++++++ workorder-detail-fix.lua | 114 ++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 docs/workorder-detail-fix.rst create mode 100644 workorder-detail-fix.lua diff --git a/changelog.txt b/changelog.txt index 439ad2ec69..f0b5d0aa19 100644 --- a/changelog.txt +++ b/changelog.txt @@ -40,6 +40,7 @@ Template for new versions: - `warn-stranded`: Update onZoom to use df's centering functionality - `ban-cooking`: fix banning creature alcohols resulting in error - `confirm`: properly detect clicks on the remove zone button even when the unit selection screen is also open (e.g. the vanilla assign animal to pasture panel) +- `workorder-detail-fix`: fix item types not being passed properly on some modified work order jobs ## Misc Improvements - `gui/control-panel`: reduce frequency for `warn-stranded` check to once every 2 days diff --git a/docs/workorder-detail-fix.rst b/docs/workorder-detail-fix.rst new file mode 100644 index 0000000000..423cc31414 --- /dev/null +++ b/docs/workorder-detail-fix.rst @@ -0,0 +1,24 @@ +workorder-detail-fix +========= + +.. dfhack-tool:: + :summary: Fixes a bug with modified work orders creating incorrect jobs. + :tags: fort bugfix workorders + +Some work order jobs have a bug when their input item details have been modified. + +Example 1: a Stud With Iron order, modified to stud a cabinet, instead creates a job to stud any furniture. + +Example 2: a Prepare Meal order, modified to use all plant type ingredients, instead creates a job to use any ingredients. + +This fix forces these jobs to properly inherit the item details from their work order. + +Usage +----- + +``workorder-detail-fix enable`` + enables the fix +``workorder-detail-fix disable`` + disables the fix +``workorder-detail-fix status`` + print fix status \ No newline at end of file diff --git a/workorder-detail-fix.lua b/workorder-detail-fix.lua new file mode 100644 index 0000000000..7bc1e7ee5b --- /dev/null +++ b/workorder-detail-fix.lua @@ -0,0 +1,114 @@ +local script_name = "workorder-detail-fix" +local eventful = require 'plugins.eventful' +if not handler_ref then local handler_ref = nil end + +local function get_job_id(match) + for val, name in ipairs(df.job_type) do + if name == match then return val end + end +end + +-- all jobs with the "any" (-1) type in its default job_items may be a problem +local offending_jobs = { + [get_job_id("EncrustWithGems")] = true, + [get_job_id("EncrustWithGlass")] = true, + [get_job_id("StudWith")] = true, + [get_job_id("PrepareMeal")] = true, + [get_job_id("DecorateWith")] = true, + [get_job_id("SewImage")] = true, + -- list may be incomplete +} + +-- copy order.item fields/flags over to job's job_item +-- only the essentials: stuff that is editable via gui/job-details +local function correct_item_details(job_item, order_item) + local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} + for _, field in pairs(fields) do + job_item[field] = order_item[field] + end + + local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} + for _, flags in pairs(flags_names) do + local order_flags = order_item[flags] + if type(order_flags) == "number" then + job_item[flags] = order_flags + else -- copy over the flags one by one + for o_flag, val in pairs(order_flags) do + job_item[flags][o_flag] = val + end + end + end +end + +-- correct each job as it's initialized +-- this is the handler, running after the job is dispatched +local function enforce_order_details(job) + if not job.job_items then return end -- never happens (error here?) + local order_id = job.order_id -- only jobs with an ORDER ID + if (order_id == -1) or (order_id == nil) then return end + + -- only jobs with the item type issue. encrusting, sewing, cooking, etc. + if not offending_jobs[job.job_type] then return end + + local order = nil -- get the order ref from order id + for _, ord in ipairs(df.global.world.manager_orders) do + if ord.id == order_id then order = ord; break end + end + + if not order then return end -- oops, no order + if not order.items then return end -- no order item details to enforce + + -- copy the item details over when the types don't match + for idx, job_item in ipairs(job.job_items) do + local order_item = order.items[idx] + if not order_item then break end -- never happens (error here?) + if job_item.item_type ~= order_item.item_type then + -- dfhack's isSuitableItem function will allow the orders we want, + -- but disallow insane combinations like meals made of shoes + local suitable = dfhack.job.isSuitableItem( + job_item, order_item.item_type, order_item.item_subtype ) + if suitable then + correct_item_details(job_item, order_item) + else --[[ error on unsuitable item?]] end + end + end +end + +local function enable() + print(script_name.." ENABLED") + -- set eventful onJobInitiated handler to run every tick (frequency 0) + eventful.enableEvent(eventful.eventType.JOB_INITIATED, 0) + eventful.onJobInitiated.workorder_detail_fix = enforce_order_details + handler_ref = eventful.onJobInitiated.workorder_detail_fix +end + +local function disable() + print(script_name.." DISABLED") + eventful.onJobInitiated.workorder_detail_fix = nil + handler_ref = nil +end + +local function status() + local status = "DISABLED" + local handler = eventful.onJobInitiated.workorder_detail_fix + if handler ~= nil then + -- ensure the handler still matches the one copied back from eventful + if handler == handler_ref then + status = "ENABLED" + else + status = "ERROR: Handler overwritten!" + print("why is this here:", handler) + print("should be", handler_ref) + end + end + print(script_name.." status: "..status) +end + +args={...} + +cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } + +cmd = cmd_table[args[1]:lower()] +if cmd then cmd() else + print(script_name.." valid cmds: enable, disable, status") +end \ No newline at end of file From 0ac190ca3837bda58277f6ca3fa7526b5c3b3367 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 08:31:14 -0800 Subject: [PATCH 02/36] better way to get the job type enums --- workorder-detail-fix.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/workorder-detail-fix.lua b/workorder-detail-fix.lua index 7bc1e7ee5b..21b9c05368 100644 --- a/workorder-detail-fix.lua +++ b/workorder-detail-fix.lua @@ -9,13 +9,13 @@ local function get_job_id(match) end -- all jobs with the "any" (-1) type in its default job_items may be a problem -local offending_jobs = { - [get_job_id("EncrustWithGems")] = true, - [get_job_id("EncrustWithGlass")] = true, - [get_job_id("StudWith")] = true, - [get_job_id("PrepareMeal")] = true, - [get_job_id("DecorateWith")] = true, - [get_job_id("SewImage")] = true, +offending_jobs = { + [df.job_type.EncrustWithGems] = true, + [df.job_type.EncrustWithGlass] = true, + [df.job_type.StudWith] = true, + [df.job_type.PrepareMeal] = true, + [df.job_type.DecorateWith] = true, + [df.job_type.SewImage] = true, -- list may be incomplete } From 152fe5b1b74fdb87df1a3a2949274773f3532d6a Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 08:34:17 -0800 Subject: [PATCH 03/36] print valid commands when no arg is passed --- workorder-detail-fix.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/workorder-detail-fix.lua b/workorder-detail-fix.lua index 21b9c05368..5e289f8a87 100644 --- a/workorder-detail-fix.lua +++ b/workorder-detail-fix.lua @@ -106,6 +106,11 @@ end args={...} +if not args[1] then + status() + return +end + cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } cmd = cmd_table[args[1]:lower()] From 4e42bd8c1a19df537df779396fb81919d5ffa3eb Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 08:38:21 -0800 Subject: [PATCH 04/36] print valid cmds when no arg is passed --- workorder-detail-fix.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/workorder-detail-fix.lua b/workorder-detail-fix.lua index 21b9c05368..80c7e31fa6 100644 --- a/workorder-detail-fix.lua +++ b/workorder-detail-fix.lua @@ -106,6 +106,11 @@ end args={...} +if not args[1] then + print(script_name.." valid cmds: enable, disable, status") + return +end + cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } cmd = cmd_table[args[1]:lower()] From 08a56b07d2c4cffd9b0054a15e67ebb4974b8cb0 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 08:46:57 -0800 Subject: [PATCH 05/36] delete old job name -> id function --- workorder-detail-fix.lua | 6 ------ 1 file changed, 6 deletions(-) diff --git a/workorder-detail-fix.lua b/workorder-detail-fix.lua index 80c7e31fa6..19bacdc000 100644 --- a/workorder-detail-fix.lua +++ b/workorder-detail-fix.lua @@ -2,12 +2,6 @@ local script_name = "workorder-detail-fix" local eventful = require 'plugins.eventful' if not handler_ref then local handler_ref = nil end -local function get_job_id(match) - for val, name in ipairs(df.job_type) do - if name == match then return val end - end -end - -- all jobs with the "any" (-1) type in its default job_items may be a problem offending_jobs = { [df.job_type.EncrustWithGems] = true, From b4b251827695dd7e32347c7b214c92e51a4c2875 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 12:48:09 -0800 Subject: [PATCH 06/36] workorder-detail-fix: use local variables --- workorder-detail-fix.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/workorder-detail-fix.lua b/workorder-detail-fix.lua index 19bacdc000..208d30aae5 100644 --- a/workorder-detail-fix.lua +++ b/workorder-detail-fix.lua @@ -3,7 +3,7 @@ local eventful = require 'plugins.eventful' if not handler_ref then local handler_ref = nil end -- all jobs with the "any" (-1) type in its default job_items may be a problem -offending_jobs = { +local offending_jobs = { [df.job_type.EncrustWithGems] = true, [df.job_type.EncrustWithGlass] = true, [df.job_type.StudWith] = true, @@ -98,16 +98,16 @@ local function status() print(script_name.." status: "..status) end -args={...} +local args={...} if not args[1] then print(script_name.." valid cmds: enable, disable, status") return end -cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } +local cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } -cmd = cmd_table[args[1]:lower()] +local cmd = cmd_table[args[1]:lower()] if cmd then cmd() else print(script_name.." valid cmds: enable, disable, status") end \ No newline at end of file From 9635fab1cf03db3dcdaf39d0d19d4de62ae074a4 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 12:59:30 -0800 Subject: [PATCH 07/36] fix trailing whitespace; doc underline --- docs/workorder-detail-fix.rst | 2 +- workorder-detail-fix.lua | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/workorder-detail-fix.rst b/docs/workorder-detail-fix.rst index 423cc31414..27c57df667 100644 --- a/docs/workorder-detail-fix.rst +++ b/docs/workorder-detail-fix.rst @@ -1,5 +1,5 @@ workorder-detail-fix -========= +==================== .. dfhack-tool:: :summary: Fixes a bug with modified work orders creating incorrect jobs. diff --git a/workorder-detail-fix.lua b/workorder-detail-fix.lua index 208d30aae5..dbdec9f015 100644 --- a/workorder-detail-fix.lua +++ b/workorder-detail-fix.lua @@ -1,6 +1,6 @@ local script_name = "workorder-detail-fix" local eventful = require 'plugins.eventful' -if not handler_ref then local handler_ref = nil end +if not handler_ref then local handler_ref = nil end -- all jobs with the "any" (-1) type in its default job_items may be a problem local offending_jobs = { @@ -17,35 +17,35 @@ local offending_jobs = { -- only the essentials: stuff that is editable via gui/job-details local function correct_item_details(job_item, order_item) local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} - for _, field in pairs(fields) do - job_item[field] = order_item[field] + for _, field in pairs(fields) do + job_item[field] = order_item[field] end local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} - for _, flags in pairs(flags_names) do + for _, flags in pairs(flags_names) do local order_flags = order_item[flags] - if type(order_flags) == "number" then + if type(order_flags) == "number" then job_item[flags] = order_flags else -- copy over the flags one by one - for o_flag, val in pairs(order_flags) do + for o_flag, val in pairs(order_flags) do job_item[flags][o_flag] = val end end end end --- correct each job as it's initialized +-- correct each job as it's initialized -- this is the handler, running after the job is dispatched local function enforce_order_details(job) if not job.job_items then return end -- never happens (error here?) - local order_id = job.order_id -- only jobs with an ORDER ID + local order_id = job.order_id -- only jobs with an ORDER ID if (order_id == -1) or (order_id == nil) then return end -- only jobs with the item type issue. encrusting, sewing, cooking, etc. if not offending_jobs[job.job_type] then return end local order = nil -- get the order ref from order id - for _, ord in ipairs(df.global.world.manager_orders) do + for _, ord in ipairs(df.global.world.manager_orders) do if ord.id == order_id then order = ord; break end end @@ -61,17 +61,17 @@ local function enforce_order_details(job) -- but disallow insane combinations like meals made of shoes local suitable = dfhack.job.isSuitableItem( job_item, order_item.item_type, order_item.item_subtype ) - if suitable then + if suitable then correct_item_details(job_item, order_item) else --[[ error on unsuitable item?]] end end end end -local function enable() +local function enable() print(script_name.." ENABLED") -- set eventful onJobInitiated handler to run every tick (frequency 0) - eventful.enableEvent(eventful.eventType.JOB_INITIATED, 0) + eventful.enableEvent(eventful.eventType.JOB_INITIATED, 0) eventful.onJobInitiated.workorder_detail_fix = enforce_order_details handler_ref = eventful.onJobInitiated.workorder_detail_fix end @@ -87,7 +87,7 @@ local function status() local handler = eventful.onJobInitiated.workorder_detail_fix if handler ~= nil then -- ensure the handler still matches the one copied back from eventful - if handler == handler_ref then + if handler == handler_ref then status = "ENABLED" else status = "ERROR: Handler overwritten!" @@ -100,7 +100,7 @@ end local args={...} -if not args[1] then +if not args[1] then print(script_name.." valid cmds: enable, disable, status") return end From 5c643b75b0c476659976391a7da7d288a753ef98 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 13:20:40 -0800 Subject: [PATCH 08/36] move workorder-detail-fix to fix directory, add enable API --- .../workorder-detail-fix.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) rename workorder-detail-fix.lua => fix/workorder-detail-fix.lua (90%) diff --git a/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua similarity index 90% rename from workorder-detail-fix.lua rename to fix/workorder-detail-fix.lua index dbdec9f015..9c416bbd81 100644 --- a/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -1,7 +1,14 @@ +--@enable = true +--@module = true local script_name = "workorder-detail-fix" local eventful = require 'plugins.eventful' if not handler_ref then local handler_ref = nil end +enabled = enabled or false +function isEnabled() + return enabled +end + -- all jobs with the "any" (-1) type in its default job_items may be a problem local offending_jobs = { [df.job_type.EncrustWithGems] = true, @@ -98,6 +105,17 @@ local function status() print(script_name.." status: "..status) end +-- check if script was called by enable API +if dfhack_flags.enable then + if dfhack_flags.enable_state then + enable() + else + disable() + end + return +end + +-- check the arguments local args={...} if not args[1] then From 69a5e1f791507f45f61856ac16d877ecc6e27817 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 13:40:23 -0800 Subject: [PATCH 09/36] no arg parsing when being used as a module (for enable api) --- fix/workorder-detail-fix.lua | 2 ++ internal/control-panel/registry.lua | 1 + 2 files changed, 3 insertions(+) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 9c416bbd81..96ccfd7efa 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -115,6 +115,8 @@ if dfhack_flags.enable then return end +if dfhack_flags.module then return end + -- check the arguments local args={...} diff --git a/internal/control-panel/registry.lua b/internal/control-panel/registry.lua index 491c4ade37..aeae86b6c7 100644 --- a/internal/control-panel/registry.lua +++ b/internal/control-panel/registry.lua @@ -64,6 +64,7 @@ COMMANDS_BY_IDX = { desc='Fix activity references on stuck instruments to make them usable again.', params={'--time', '1', '--timeUnits', 'days', '--command', '[', 'fix/stuck-instruments', ']'}}, {command='preserve-tombs', group='bugfix', mode='enable', default=true}, + {command='fix/workorder-detail-fix', group='bugfix', mode='enable', default=true}, -- gameplay tools {command='combine', group='gameplay', mode='repeat', From e52adbe64a6e2aee2e6c97efb8c4f00c7c3d637d Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 13:44:06 -0800 Subject: [PATCH 10/36] actually set enable state --- fix/workorder-detail-fix.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 96ccfd7efa..d89f501172 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -108,9 +108,9 @@ end -- check if script was called by enable API if dfhack_flags.enable then if dfhack_flags.enable_state then - enable() + enable(); enabled = true else - disable() + disable(); enabled = false end return end From b30668f7880680aa3e5f6852c39a891b8fd524c3 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 13:48:46 -0800 Subject: [PATCH 11/36] trailing whitespace again --- fix/workorder-detail-fix.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index d89f501172..1b2086b293 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -106,8 +106,8 @@ local function status() end -- check if script was called by enable API -if dfhack_flags.enable then - if dfhack_flags.enable_state then +if dfhack_flags.enable then + if dfhack_flags.enable_state then enable(); enabled = true else disable(); enabled = false From fae0027bd8b6342a5ed99930b95ce00f3f532b89 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 14:18:03 -0800 Subject: [PATCH 12/36] hopefully fix line endings (sublime text calls them 'unix' endings) --- docs/workorder-detail-fix.rst | 46 +++--- fix/workorder-detail-fix.lua | 264 +++++++++++++++++----------------- 2 files changed, 155 insertions(+), 155 deletions(-) diff --git a/docs/workorder-detail-fix.rst b/docs/workorder-detail-fix.rst index 27c57df667..99edd4a5c9 100644 --- a/docs/workorder-detail-fix.rst +++ b/docs/workorder-detail-fix.rst @@ -1,24 +1,24 @@ -workorder-detail-fix -==================== - -.. dfhack-tool:: - :summary: Fixes a bug with modified work orders creating incorrect jobs. - :tags: fort bugfix workorders - -Some work order jobs have a bug when their input item details have been modified. - -Example 1: a Stud With Iron order, modified to stud a cabinet, instead creates a job to stud any furniture. - -Example 2: a Prepare Meal order, modified to use all plant type ingredients, instead creates a job to use any ingredients. - -This fix forces these jobs to properly inherit the item details from their work order. - -Usage ------ - -``workorder-detail-fix enable`` - enables the fix -``workorder-detail-fix disable`` - disables the fix -``workorder-detail-fix status`` +workorder-detail-fix +==================== + +.. dfhack-tool:: + :summary: Fixes a bug with modified work orders creating incorrect jobs. + :tags: fort bugfix workorders + +Some work order jobs have a bug when their input item details have been modified. + +Example 1: a Stud With Iron order, modified to stud a cabinet, instead creates a job to stud any furniture. + +Example 2: a Prepare Meal order, modified to use all plant type ingredients, instead creates a job to use any ingredients. + +This fix forces these jobs to properly inherit the item details from their work order. + +Usage +----- + +``workorder-detail-fix enable`` + enables the fix +``workorder-detail-fix disable`` + disables the fix +``workorder-detail-fix status`` print fix status \ No newline at end of file diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 1b2086b293..f0e44220cd 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -1,133 +1,133 @@ ---@enable = true ---@module = true -local script_name = "workorder-detail-fix" -local eventful = require 'plugins.eventful' -if not handler_ref then local handler_ref = nil end - -enabled = enabled or false -function isEnabled() - return enabled -end - --- all jobs with the "any" (-1) type in its default job_items may be a problem -local offending_jobs = { - [df.job_type.EncrustWithGems] = true, - [df.job_type.EncrustWithGlass] = true, - [df.job_type.StudWith] = true, - [df.job_type.PrepareMeal] = true, - [df.job_type.DecorateWith] = true, - [df.job_type.SewImage] = true, - -- list may be incomplete -} - --- copy order.item fields/flags over to job's job_item --- only the essentials: stuff that is editable via gui/job-details -local function correct_item_details(job_item, order_item) - local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} - for _, field in pairs(fields) do - job_item[field] = order_item[field] - end - - local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} - for _, flags in pairs(flags_names) do - local order_flags = order_item[flags] - if type(order_flags) == "number" then - job_item[flags] = order_flags - else -- copy over the flags one by one - for o_flag, val in pairs(order_flags) do - job_item[flags][o_flag] = val - end - end - end -end - --- correct each job as it's initialized --- this is the handler, running after the job is dispatched -local function enforce_order_details(job) - if not job.job_items then return end -- never happens (error here?) - local order_id = job.order_id -- only jobs with an ORDER ID - if (order_id == -1) or (order_id == nil) then return end - - -- only jobs with the item type issue. encrusting, sewing, cooking, etc. - if not offending_jobs[job.job_type] then return end - - local order = nil -- get the order ref from order id - for _, ord in ipairs(df.global.world.manager_orders) do - if ord.id == order_id then order = ord; break end - end - - if not order then return end -- oops, no order - if not order.items then return end -- no order item details to enforce - - -- copy the item details over when the types don't match - for idx, job_item in ipairs(job.job_items) do - local order_item = order.items[idx] - if not order_item then break end -- never happens (error here?) - if job_item.item_type ~= order_item.item_type then - -- dfhack's isSuitableItem function will allow the orders we want, - -- but disallow insane combinations like meals made of shoes - local suitable = dfhack.job.isSuitableItem( - job_item, order_item.item_type, order_item.item_subtype ) - if suitable then - correct_item_details(job_item, order_item) - else --[[ error on unsuitable item?]] end - end - end -end - -local function enable() - print(script_name.." ENABLED") - -- set eventful onJobInitiated handler to run every tick (frequency 0) - eventful.enableEvent(eventful.eventType.JOB_INITIATED, 0) - eventful.onJobInitiated.workorder_detail_fix = enforce_order_details - handler_ref = eventful.onJobInitiated.workorder_detail_fix -end - -local function disable() - print(script_name.." DISABLED") - eventful.onJobInitiated.workorder_detail_fix = nil - handler_ref = nil -end - -local function status() - local status = "DISABLED" - local handler = eventful.onJobInitiated.workorder_detail_fix - if handler ~= nil then - -- ensure the handler still matches the one copied back from eventful - if handler == handler_ref then - status = "ENABLED" - else - status = "ERROR: Handler overwritten!" - print("why is this here:", handler) - print("should be", handler_ref) - end - end - print(script_name.." status: "..status) -end - --- check if script was called by enable API -if dfhack_flags.enable then - if dfhack_flags.enable_state then - enable(); enabled = true - else - disable(); enabled = false - end - return -end - -if dfhack_flags.module then return end - --- check the arguments -local args={...} - -if not args[1] then - print(script_name.." valid cmds: enable, disable, status") - return -end - -local cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } - -local cmd = cmd_table[args[1]:lower()] -if cmd then cmd() else - print(script_name.." valid cmds: enable, disable, status") +--@enable = true +--@module = true +local script_name = "workorder-detail-fix" +local eventful = require 'plugins.eventful' +if not handler_ref then local handler_ref = nil end + +enabled = enabled or false +function isEnabled() + return enabled +end + +-- all jobs with the "any" (-1) type in its default job_items may be a problem +local offending_jobs = { + [df.job_type.EncrustWithGems] = true, + [df.job_type.EncrustWithGlass] = true, + [df.job_type.StudWith] = true, + [df.job_type.PrepareMeal] = true, + [df.job_type.DecorateWith] = true, + [df.job_type.SewImage] = true, + -- list may be incomplete +} + +-- copy order.item fields/flags over to job's job_item +-- only the essentials: stuff that is editable via gui/job-details +local function correct_item_details(job_item, order_item) + local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} + for _, field in pairs(fields) do + job_item[field] = order_item[field] + end + + local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} + for _, flags in pairs(flags_names) do + local order_flags = order_item[flags] + if type(order_flags) == "number" then + job_item[flags] = order_flags + else -- copy over the flags one by one + for o_flag, val in pairs(order_flags) do + job_item[flags][o_flag] = val + end + end + end +end + +-- correct each job as it's initialized +-- this is the handler, running after the job is dispatched +local function enforce_order_details(job) + if not job.job_items then return end -- never happens (error here?) + local order_id = job.order_id -- only jobs with an ORDER ID + if (order_id == -1) or (order_id == nil) then return end + + -- only jobs with the item type issue. encrusting, sewing, cooking, etc. + if not offending_jobs[job.job_type] then return end + + local order = nil -- get the order ref from order id + for _, ord in ipairs(df.global.world.manager_orders) do + if ord.id == order_id then order = ord; break end + end + + if not order then return end -- oops, no order + if not order.items then return end -- no order item details to enforce + + -- copy the item details over when the types don't match + for idx, job_item in ipairs(job.job_items) do + local order_item = order.items[idx] + if not order_item then break end -- never happens (error here?) + if job_item.item_type ~= order_item.item_type then + -- dfhack's isSuitableItem function will allow the orders we want, + -- but disallow insane combinations like meals made of shoes + local suitable = dfhack.job.isSuitableItem( + job_item, order_item.item_type, order_item.item_subtype ) + if suitable then + correct_item_details(job_item, order_item) + else --[[ error on unsuitable item?]] end + end + end +end + +local function enable() + print(script_name.." ENABLED") + -- set eventful onJobInitiated handler to run every tick (frequency 0) + eventful.enableEvent(eventful.eventType.JOB_INITIATED, 0) + eventful.onJobInitiated.workorder_detail_fix = enforce_order_details + handler_ref = eventful.onJobInitiated.workorder_detail_fix +end + +local function disable() + print(script_name.." DISABLED") + eventful.onJobInitiated.workorder_detail_fix = nil + handler_ref = nil +end + +local function status() + local status = "DISABLED" + local handler = eventful.onJobInitiated.workorder_detail_fix + if handler ~= nil then + -- ensure the handler still matches the one copied back from eventful + if handler == handler_ref then + status = "ENABLED" + else + status = "ERROR: Handler overwritten!" + print("why is this here:", handler) + print("should be", handler_ref) + end + end + print(script_name.." status: "..status) +end + +-- check if script was called by enable API +if dfhack_flags.enable then + if dfhack_flags.enable_state then + enable(); enabled = true + else + disable(); enabled = false + end + return +end + +if dfhack_flags.module then return end + +-- check the arguments +local args={...} + +if not args[1] then + print(script_name.." valid cmds: enable, disable, status") + return +end + +local cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } + +local cmd = cmd_table[args[1]:lower()] +if cmd then cmd() else + print(script_name.." valid cmds: enable, disable, status") end \ No newline at end of file From 45f87dee4c9e2f4e97f1024e4018efd737cc6179 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 14:23:52 -0800 Subject: [PATCH 13/36] hopefully fix EOF error (just needed newline at end of file) --- docs/workorder-detail-fix.rst | 2 +- fix/workorder-detail-fix.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/workorder-detail-fix.rst b/docs/workorder-detail-fix.rst index 99edd4a5c9..2e830af0ed 100644 --- a/docs/workorder-detail-fix.rst +++ b/docs/workorder-detail-fix.rst @@ -21,4 +21,4 @@ Usage ``workorder-detail-fix disable`` disables the fix ``workorder-detail-fix status`` - print fix status \ No newline at end of file + print fix status diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index f0e44220cd..5c33d4756e 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -130,4 +130,4 @@ local cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } local cmd = cmd_table[args[1]:lower()] if cmd then cmd() else print(script_name.." valid cmds: enable, disable, status") -end \ No newline at end of file +end From 47761b64bb07f96b176029a074efbf4406c021a6 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 14:39:06 -0800 Subject: [PATCH 14/36] only print when called from command line. also fix to work better with enable api --- fix/workorder-detail-fix.lua | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 5c33d4756e..0c8ee2fc0e 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -76,17 +76,24 @@ local function enforce_order_details(job) end local function enable() - print(script_name.." ENABLED") + -- only print when called manually + if not dfhack_flags.enable then + print(script_name.." ENABLED") + end -- set eventful onJobInitiated handler to run every tick (frequency 0) eventful.enableEvent(eventful.eventType.JOB_INITIATED, 0) eventful.onJobInitiated.workorder_detail_fix = enforce_order_details handler_ref = eventful.onJobInitiated.workorder_detail_fix + enabled = true end local function disable() - print(script_name.." DISABLED") + if not dfhack_flags.enable then + print(script_name.." DISABLED") + end eventful.onJobInitiated.workorder_detail_fix = nil handler_ref = nil + enabled = false end local function status() @@ -108,9 +115,9 @@ end -- check if script was called by enable API if dfhack_flags.enable then if dfhack_flags.enable_state then - enable(); enabled = true + enable() else - disable(); enabled = false + disable() end return end From e1f170e316b5e19fdb2204cb2420d86a003f449d Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sat, 6 Jan 2024 21:27:45 -0800 Subject: [PATCH 15/36] move doc to docs/fix --- docs/{ => fix}/workorder-detail-fix.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{ => fix}/workorder-detail-fix.rst (100%) diff --git a/docs/workorder-detail-fix.rst b/docs/fix/workorder-detail-fix.rst similarity index 100% rename from docs/workorder-detail-fix.rst rename to docs/fix/workorder-detail-fix.rst From ea76f59d83da8e0d38f2bd6f53d65f69c477524a Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sun, 7 Jan 2024 02:37:32 -0800 Subject: [PATCH 16/36] workorder-detail-fix -> fix/workorder-detail-fix --- docs/fix/workorder-detail-fix.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/fix/workorder-detail-fix.rst b/docs/fix/workorder-detail-fix.rst index 2e830af0ed..5f2750c0c1 100644 --- a/docs/fix/workorder-detail-fix.rst +++ b/docs/fix/workorder-detail-fix.rst @@ -1,4 +1,4 @@ -workorder-detail-fix +fix/workorder-detail-fix ==================== .. dfhack-tool:: @@ -16,9 +16,9 @@ This fix forces these jobs to properly inherit the item details from their work Usage ----- -``workorder-detail-fix enable`` +``fix/workorder-detail-fix enable`` enables the fix -``workorder-detail-fix disable`` +``fix/workorder-detail-fix disable`` disables the fix -``workorder-detail-fix status`` +``fix/workorder-detail-fix status`` print fix status From 727200bcf057dcaf3b36c5189c4ff26d07a70abb Mon Sep 17 00:00:00 2001 From: flashy-man Date: Tue, 9 Jan 2024 05:09:22 -0800 Subject: [PATCH 17/36] add detail_fix_is_needed() to say when the fix is required --- fix/workorder-detail-fix.lua | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 0c8ee2fc0e..79dbeca388 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -75,6 +75,46 @@ local function enforce_order_details(job) end end +local PrepareMeal = df.job_type.PrepareMeal +local SewImage = df.job_type.SewImage +local NONE = df.job_type.NONE + +--[[ return true if order list contains an order that would trigger the bug. +it only happens when item types clash with an expected default of "any"/NONE, +so this is the only case it checks for. ]] +local function detail_fix_is_needed() + for _, order in ipairs(df.global.world.manager_orders) do + if not order.items then goto nextorder end + + local order_job_type = order.job_type + if not offending_jobs[order_job_type] then goto nextorder end + if #order.items == 0 then goto nextorder end -- doesn't happen + + -- for PrepareMeal jobs, any one of the items could be an issue. + if order_job_type == PrepareMeal then + for _, item in ipairs(order.items) do + if item.item_type ~= NONE then return true end + end + goto nextorder + end + + -- All other types are improve jobs. only the improved item is checked + local job_item_index = 1 + -- Only SewImage has the item-to-improve at items[0] + if order_job_type == SewImage then job_item_index = 0 end + + local item = order.items[job_item_index] + + -- only happens if someone has really mangled an order. deleted items, + -- switch positions, etc. gui/job-details doesn't let this happen. + if not item then goto nextorder end + + if item.item_type ~= NONE then return true end + :: nextorder :: + end + return false +end + local function enable() -- only print when called manually if not dfhack_flags.enable then From 39a4db4ef18c4d230c5b752d162894460ce5a80f Mon Sep 17 00:00:00 2001 From: flashy-man Date: Tue, 9 Jan 2024 05:13:55 -0800 Subject: [PATCH 18/36] rework: only enable handler when detail_fix_is_needed --- fix/workorder-detail-fix.lua | 63 +++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 79dbeca388..518ab564d3 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -2,8 +2,13 @@ --@module = true local script_name = "workorder-detail-fix" local eventful = require 'plugins.eventful' +local repeatutil = require 'repeat-util' if not handler_ref then local handler_ref = nil end +-- must be frequent enough to catch new orders before any jobs get dispatched +order_check_period = order_check_period or 300 +job_check_period = job_check_period or 0 -- might be overkill + enabled = enabled or false function isEnabled() return enabled @@ -75,6 +80,25 @@ local function enforce_order_details(job) end end +local function arm_job_handler() + -- set eventful onJobInitiated handler to run every tick (frequency 0) + -- NOTE: this affects every other script using this eventful handler. + eventful.enableEvent(eventful.eventType.JOB_INITIATED, job_check_period) + eventful.onJobInitiated.workorder_detail_fix = enforce_order_details + handler_ref = eventful.onJobInitiated.workorder_detail_fix + handler_armed = true +end + +local function disarm_job_handler() + --[[ would undo the onJobInitiated frequency here, but eventful has no way + to set a less frequent check- only a more frequent one. + having a PERMANENT side effect is not ideal but perhaps it should be taken + up with eventful.cpp ]] + eventful.onJobInitiated.workorder_detail_fix = nil + handler_ref = nil + handler_armed = false +end + local PrepareMeal = df.job_type.PrepareMeal local SewImage = df.job_type.SewImage local NONE = df.job_type.NONE @@ -115,25 +139,34 @@ local function detail_fix_is_needed() return false end -local function enable() - -- only print when called manually - if not dfhack_flags.enable then - print(script_name.." ENABLED") - end - -- set eventful onJobInitiated handler to run every tick (frequency 0) - eventful.enableEvent(eventful.eventType.JOB_INITIATED, 0) - eventful.onJobInitiated.workorder_detail_fix = enforce_order_details - handler_ref = eventful.onJobInitiated.workorder_detail_fix +local schedule_key = "workorder_detail_fix_order_check" +local function disable_order_checking() + repeatutil.cancel(schedule_key) + checking_orders = false +end + +-- checks orders for bug periodically & enables the main fix when applicable, +-- mainly to avoid setting unnecessary handlers +local function enable(yell) + -- embedded func could be factored out if we want to allow other + -- scripts to check. eg after importing orders, or modifying an order + checking_orders = true + repeatutil.scheduleEvery( schedule_key, order_check_period, "ticks", + function() + if detail_fix_is_needed() then + arm_job_handler() + disable_order_checking() + end + end ) enabled = true + if yell then print(script_name.." ENABLED") end end -local function disable() - if not dfhack_flags.enable then - print(script_name.." DISABLED") - end - eventful.onJobInitiated.workorder_detail_fix = nil - handler_ref = nil +local function disable(yell) + disable_order_checking() + disarm_job_handler() enabled = false + if yell then print(script_name.." DISABLED") end end local function status() From a4ce456170d302fc20bdb68b37ac07a7c29a7fde Mon Sep 17 00:00:00 2001 From: flashy-man Date: Tue, 9 Jan 2024 05:18:22 -0800 Subject: [PATCH 19/36] better status() function to account for more statuses --- fix/workorder-detail-fix.lua | 54 ++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 518ab564d3..05be4d3332 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -3,18 +3,20 @@ local script_name = "workorder-detail-fix" local eventful = require 'plugins.eventful' local repeatutil = require 'repeat-util' -if not handler_ref then local handler_ref = nil end -- must be frequent enough to catch new orders before any jobs get dispatched order_check_period = order_check_period or 300 job_check_period = job_check_period or 0 -- might be overkill -enabled = enabled or false -function isEnabled() - return enabled -end +-- these are only for debug/printing status +handler_ref = handler_ref or nil +handler_armed = handler_armed or false +checking_orders = checking_orders or false + +enabled = enabled or false -- "enabled API" stuff +function isEnabled() return enabled end --- all jobs with the "any" (-1) type in its default job_items may be a problem +-- all jobs with the NONE (-1) type in its default job_items may be a problem local offending_jobs = { [df.job_type.EncrustWithGems] = true, [df.job_type.EncrustWithGlass] = true, @@ -169,28 +171,44 @@ local function disable(yell) if yell then print(script_name.." DISABLED") end end +-- mostly for debugging. maybe could print the # of jobs corrected local function status() local status = "DISABLED" + if checking_orders then status = "ENABLED (Checking orders)" end + if handler_armed then status = "ENABLED (Handling jobs)" end + local extralines = {}; local num_err = 0 + local function err(msg, ...) + num_err = num_err + 1; status = ("ERROR (%d)"):format(num_err) + table.insert(extralines, msg:format(...)) + end + -- may become non-error if we want to get more dynamic with toggling + if checking_orders and handler_armed then + err("checking orders and handling jobs at the same time?") + end + local order_check_scheduled = repeatutil.repeating[schedule_key] ~= nil + if checking_orders ~= order_check_scheduled then + err( "%s we are checking orders but %s that a check is scheduled", + checking_orders, order_check_scheduled ) + end local handler = eventful.onJobInitiated.workorder_detail_fix - if handler ~= nil then - -- ensure the handler still matches the one copied back from eventful - if handler == handler_ref then - status = "ENABLED" - else - status = "ERROR: Handler overwritten!" - print("why is this here:", handler) - print("should be", handler_ref) - end + if handler_armed ~= (handler ~= nil) then + err( "%s that job handler should be armed but %s that one exists", + handler_armed, handler ~= nil ) + end + if not handler == handler_ref then + err( "job handler: %s\ndoesn't match stored handler: %s", + handler, handler_ref ) end print(script_name.." status: "..status) + for idx, message in pairs(extralines) do print(idx, message) end end -- check if script was called by enable API if dfhack_flags.enable then if dfhack_flags.enable_state then - enable() + enable(false) else - disable() + disable(false) end return end @@ -208,6 +226,6 @@ end local cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } local cmd = cmd_table[args[1]:lower()] -if cmd then cmd() else +if cmd then cmd(true) else print(script_name.." valid cmds: enable, disable, status") end From 8ad4f77eb6369990febe06ac50f86f62f1850489 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 10 Jan 2024 11:46:07 -0800 Subject: [PATCH 20/36] woowee doc underline --- docs/fix/workorder-detail-fix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fix/workorder-detail-fix.rst b/docs/fix/workorder-detail-fix.rst index 5f2750c0c1..132bc29398 100644 --- a/docs/fix/workorder-detail-fix.rst +++ b/docs/fix/workorder-detail-fix.rst @@ -1,5 +1,5 @@ fix/workorder-detail-fix -==================== +======================== .. dfhack-tool:: :summary: Fixes a bug with modified work orders creating incorrect jobs. From a5c2cc9dec7589e15d3a541a2a76b6e8b0809ec8 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sun, 14 Jan 2024 09:44:59 -0800 Subject: [PATCH 21/36] rename workorder-detail-fix in changelog --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index f0b5d0aa19..343f8b61c5 100644 --- a/changelog.txt +++ b/changelog.txt @@ -40,7 +40,7 @@ Template for new versions: - `warn-stranded`: Update onZoom to use df's centering functionality - `ban-cooking`: fix banning creature alcohols resulting in error - `confirm`: properly detect clicks on the remove zone button even when the unit selection screen is also open (e.g. the vanilla assign animal to pasture panel) -- `workorder-detail-fix`: fix item types not being passed properly on some modified work order jobs +- `fix/workorder-detail-fix`: fix item types not being passed properly on some modified work order jobs ## Misc Improvements - `gui/control-panel`: reduce frequency for `warn-stranded` check to once every 2 days From 4e950dd41ca2fd9de6f34104eccb2a6faf31698a Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sun, 14 Jan 2024 11:21:16 -0800 Subject: [PATCH 22/36] no more eventful; only repeatutil --- fix/workorder-detail-fix.lua | 433 ++++++++++++++++------------------- 1 file changed, 202 insertions(+), 231 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 05be4d3332..8c58d079ba 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -1,231 +1,202 @@ ---@enable = true ---@module = true -local script_name = "workorder-detail-fix" -local eventful = require 'plugins.eventful' -local repeatutil = require 'repeat-util' - --- must be frequent enough to catch new orders before any jobs get dispatched -order_check_period = order_check_period or 300 -job_check_period = job_check_period or 0 -- might be overkill - --- these are only for debug/printing status -handler_ref = handler_ref or nil -handler_armed = handler_armed or false -checking_orders = checking_orders or false - -enabled = enabled or false -- "enabled API" stuff -function isEnabled() return enabled end - --- all jobs with the NONE (-1) type in its default job_items may be a problem -local offending_jobs = { - [df.job_type.EncrustWithGems] = true, - [df.job_type.EncrustWithGlass] = true, - [df.job_type.StudWith] = true, - [df.job_type.PrepareMeal] = true, - [df.job_type.DecorateWith] = true, - [df.job_type.SewImage] = true, - -- list may be incomplete -} - --- copy order.item fields/flags over to job's job_item --- only the essentials: stuff that is editable via gui/job-details -local function correct_item_details(job_item, order_item) - local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} - for _, field in pairs(fields) do - job_item[field] = order_item[field] - end - - local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} - for _, flags in pairs(flags_names) do - local order_flags = order_item[flags] - if type(order_flags) == "number" then - job_item[flags] = order_flags - else -- copy over the flags one by one - for o_flag, val in pairs(order_flags) do - job_item[flags][o_flag] = val - end - end - end -end - --- correct each job as it's initialized --- this is the handler, running after the job is dispatched -local function enforce_order_details(job) - if not job.job_items then return end -- never happens (error here?) - local order_id = job.order_id -- only jobs with an ORDER ID - if (order_id == -1) or (order_id == nil) then return end - - -- only jobs with the item type issue. encrusting, sewing, cooking, etc. - if not offending_jobs[job.job_type] then return end - - local order = nil -- get the order ref from order id - for _, ord in ipairs(df.global.world.manager_orders) do - if ord.id == order_id then order = ord; break end - end - - if not order then return end -- oops, no order - if not order.items then return end -- no order item details to enforce - - -- copy the item details over when the types don't match - for idx, job_item in ipairs(job.job_items) do - local order_item = order.items[idx] - if not order_item then break end -- never happens (error here?) - if job_item.item_type ~= order_item.item_type then - -- dfhack's isSuitableItem function will allow the orders we want, - -- but disallow insane combinations like meals made of shoes - local suitable = dfhack.job.isSuitableItem( - job_item, order_item.item_type, order_item.item_subtype ) - if suitable then - correct_item_details(job_item, order_item) - else --[[ error on unsuitable item?]] end - end - end -end - -local function arm_job_handler() - -- set eventful onJobInitiated handler to run every tick (frequency 0) - -- NOTE: this affects every other script using this eventful handler. - eventful.enableEvent(eventful.eventType.JOB_INITIATED, job_check_period) - eventful.onJobInitiated.workorder_detail_fix = enforce_order_details - handler_ref = eventful.onJobInitiated.workorder_detail_fix - handler_armed = true -end - -local function disarm_job_handler() - --[[ would undo the onJobInitiated frequency here, but eventful has no way - to set a less frequent check- only a more frequent one. - having a PERMANENT side effect is not ideal but perhaps it should be taken - up with eventful.cpp ]] - eventful.onJobInitiated.workorder_detail_fix = nil - handler_ref = nil - handler_armed = false -end - -local PrepareMeal = df.job_type.PrepareMeal -local SewImage = df.job_type.SewImage -local NONE = df.job_type.NONE - ---[[ return true if order list contains an order that would trigger the bug. -it only happens when item types clash with an expected default of "any"/NONE, -so this is the only case it checks for. ]] -local function detail_fix_is_needed() - for _, order in ipairs(df.global.world.manager_orders) do - if not order.items then goto nextorder end - - local order_job_type = order.job_type - if not offending_jobs[order_job_type] then goto nextorder end - if #order.items == 0 then goto nextorder end -- doesn't happen - - -- for PrepareMeal jobs, any one of the items could be an issue. - if order_job_type == PrepareMeal then - for _, item in ipairs(order.items) do - if item.item_type ~= NONE then return true end - end - goto nextorder - end - - -- All other types are improve jobs. only the improved item is checked - local job_item_index = 1 - -- Only SewImage has the item-to-improve at items[0] - if order_job_type == SewImage then job_item_index = 0 end - - local item = order.items[job_item_index] - - -- only happens if someone has really mangled an order. deleted items, - -- switch positions, etc. gui/job-details doesn't let this happen. - if not item then goto nextorder end - - if item.item_type ~= NONE then return true end - :: nextorder :: - end - return false -end - -local schedule_key = "workorder_detail_fix_order_check" -local function disable_order_checking() - repeatutil.cancel(schedule_key) - checking_orders = false -end - --- checks orders for bug periodically & enables the main fix when applicable, --- mainly to avoid setting unnecessary handlers -local function enable(yell) - -- embedded func could be factored out if we want to allow other - -- scripts to check. eg after importing orders, or modifying an order - checking_orders = true - repeatutil.scheduleEvery( schedule_key, order_check_period, "ticks", - function() - if detail_fix_is_needed() then - arm_job_handler() - disable_order_checking() - end - end ) - enabled = true - if yell then print(script_name.." ENABLED") end -end - -local function disable(yell) - disable_order_checking() - disarm_job_handler() - enabled = false - if yell then print(script_name.." DISABLED") end -end - --- mostly for debugging. maybe could print the # of jobs corrected -local function status() - local status = "DISABLED" - if checking_orders then status = "ENABLED (Checking orders)" end - if handler_armed then status = "ENABLED (Handling jobs)" end - local extralines = {}; local num_err = 0 - local function err(msg, ...) - num_err = num_err + 1; status = ("ERROR (%d)"):format(num_err) - table.insert(extralines, msg:format(...)) - end - -- may become non-error if we want to get more dynamic with toggling - if checking_orders and handler_armed then - err("checking orders and handling jobs at the same time?") - end - local order_check_scheduled = repeatutil.repeating[schedule_key] ~= nil - if checking_orders ~= order_check_scheduled then - err( "%s we are checking orders but %s that a check is scheduled", - checking_orders, order_check_scheduled ) - end - local handler = eventful.onJobInitiated.workorder_detail_fix - if handler_armed ~= (handler ~= nil) then - err( "%s that job handler should be armed but %s that one exists", - handler_armed, handler ~= nil ) - end - if not handler == handler_ref then - err( "job handler: %s\ndoesn't match stored handler: %s", - handler, handler_ref ) - end - print(script_name.." status: "..status) - for idx, message in pairs(extralines) do print(idx, message) end -end - --- check if script was called by enable API -if dfhack_flags.enable then - if dfhack_flags.enable_state then - enable(false) - else - disable(false) - end - return -end - -if dfhack_flags.module then return end - --- check the arguments -local args={...} - -if not args[1] then - print(script_name.." valid cmds: enable, disable, status") - return -end - -local cmd_table = { ["enable"]=enable, ["disable"]=disable, ["status"]=status } - -local cmd = cmd_table[args[1]:lower()] -if cmd then cmd(true) else - print(script_name.." valid cmds: enable, disable, status") -end +--@enable = true +--@module = true +local repeatutil = require 'repeat-util' +local utils = require 'utils' +local script_name = "workorder-detail-fix" +local schedule_key = script_name..":dispatch" + +enabled = enabled or false -- enabled API +function isEnabled() return enabled end + +local managers = df.global.plotinfo.main.fortress_entity.assignments_by_type.MANAGE_PRODUCTION +if not managers then error("NO MANAGERS VECTOR!") end +local last_job_id = -1 +local jobs_corrected = 0 + +-- all jobs with the NONE (-1) type in its default job_items may be a problem +local offending_jobs = { + [df.job_type.EncrustWithGems] = true, + [df.job_type.EncrustWithGlass] = true, + [df.job_type.StudWith] = true, + [df.job_type.PrepareMeal] = true, + [df.job_type.DecorateWith] = true, + [df.job_type.SewImage] = true, + -- list may be incomplete +} + +-- copy order.item fields/flags over to job's job_item +-- only the essentials: stuff that is editable via gui/job-details +local function correct_item_details(job_item, order_item) + local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} + for _, field in pairs(fields) do + job_item[field] = order_item[field] + end + + local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} + for _, flags in pairs(flags_names) do + local order_flags = order_item[flags] + if type(order_flags) == 'number' then + job_item[flags] = order_flags + else -- copy over the flags one by one + for o_flag, val in pairs(order_flags) do + job_item[flags][o_flag] = val + end + end + end +end + +-- correct job's job_items to match the order's +local function enforce_order_details(job, order) + if not job.job_items then return end -- never happens (error here?) + local modified = false + for idx, job_item in ipairs(job.job_items) do + local order_item = order.items[idx] + if not order_item then break end -- never happens (error here?) + if job_item.item_type ~= order_item.item_type then + -- dfhack's isSuitableItem function will allow the orders we want, + -- but disallow insane combinations like meals made of shoes + local suitable = dfhack.job.isSuitableItem( + job_item, order_item.item_type, order_item.item_subtype ) + if suitable then + correct_item_details(job_item, order_item) + modified = true + else --[[ error on unsuitable item?]] end + end + end + if modified then jobs_corrected = jobs_corrected + 1 end +end + +-- check orders list to see if fix is needed +-- yields id-order table for the orders with issues, or nil when no issues +local PrepareMeal = df.job_type.PrepareMeal +local SewImage = df.job_type.SewImage +local NONE = df.job_type.NONE +local function get_bugged_orders() + local orders = {} + local num_bugged, improve_item_index, item = 0, 1, nil + for _, order in ipairs(df.global.world.manager_orders) do + if not order.items then goto nextorder end + local order_job_type = order.job_type + if not offending_jobs[order_job_type] then goto nextorder end + if #order.items == 0 then goto nextorder end -- doesn't happen + + -- for PrepareMeal jobs, any one of the items could be an issue. + if order_job_type == PrepareMeal then + for _, _item in ipairs(order.items) do + if _item.item_type ~= NONE then goto fix end + end + goto nextorder + end + + -- All other types are improve jobs; only the improved item is checked + -- Only SewImage has the item-to-improve at items[0] + improve_item_index = (order_job_type ~= SewImage) and 1 or 0 + item = order.items[improve_item_index] + if not item then goto nextorder end -- error here? + if item.item_type == NONE then goto nextorder end + :: fix :: + num_bugged = num_bugged + 1 + orders[order.id] = order + :: nextorder :: + end + if num_bugged == 0 then return nil end + return orders +end + +-- correct newly dispatched work order jobs +local disable +local function on_dispatch_tick() + if df.global.cur_year_tick % 150 ~= 30 then + print(script_name.." desynced from dispatch tick") + repeatutil.cancel(schedule_key) + disable(true) + end + if #managers == 0 then return end + if df.global.plotinfo.manager_timer ~= 10 then return end + local orders = get_bugged_orders() + if not orders then return end -- no bugs to fix + local highest = last_job_id + for _, job in utils.listpairs(df.global.world.jobs.list) do + if job.id <= last_job_id then goto nextjob end + highest = math.max(job.id, highest) + if job.order_id == -1 then goto nextjob end + local order = orders[job.order_id] + if not order then goto nextjob end -- order wasn't bugged + if #job.items ~= 0 then + -- job in progress: only happens on the first run. + -- experimental fix: remove the items and un-task them + while #job.items ~= 0 do + job.items[0].item.flags.in_job = false + job.items:erase(0) + end + end + enforce_order_details(job, order) + :: nextjob :: + end + last_job_id = highest +end + +timeout_id = timeout_id or nil +local function schedule_handler() + if repeatutil.cancel(schedule_key) then + print(script_name..": canceled old dispatch handler") + end + repeatutil.scheduleEvery(schedule_key, 150, 'ticks', on_dispatch_tick) + timeout_id = nil +end + +local function enable(yell) + local manager_timer = df.global.plotinfo.manager_timer + local d = df.global.cur_year_tick + local time_until = -(d - 30) % 150 -- + manager_timer * 150 + if time_until == 0 then + schedule_handler() + else + timeout_id = dfhack.timeout(time_until, 'ticks', schedule_handler) + end + enabled = true + if yell then print(script_name.." ENABLED") end +end + +function disable(yell) + if timeout_id then + dfhack.timeout_active(timeout_id, nil) + timeout_id = nil + end + repeatutil.cancel(schedule_key) + enabled = false + if yell then print(script_name.." DISABLED") end +end + +-- (not working with enabled API, probably something to do with module mode) +local function status() + local status = "DISABLED" or enabled and "ENABLED" + print(script_name.." status: "..status.." # jobs corrected: "..tostring(jobs_corrected)) +end + +-- check if script was called by enable API +if dfhack_flags.enable then + if dfhack_flags.enable_state then + enable(false) + else + disable(false) + end + return +end + +if dfhack_flags.module then return end + +-- check the arguments +local args={...} + +if not args[1] then + print(script_name.." valid cmds: enable, disable, status") + return +end + +local cmd_table = { ['enable']=enable, ['disable']=disable, ['status']=status } + +local cmd = cmd_table[args[1]:lower()] +if cmd then cmd(true) else + print(script_name.." valid cmds: enable, disable, status") +end From d6b0775451da4c891a755fa05263c868a59c32fd Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sun, 14 Jan 2024 16:03:04 -0800 Subject: [PATCH 23/36] fix redundancy, confusing modulo math --- fix/workorder-detail-fix.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 8c58d079ba..04188dc9ee 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -122,13 +122,11 @@ local function on_dispatch_tick() if job.order_id == -1 then goto nextjob end local order = orders[job.order_id] if not order then goto nextjob end -- order wasn't bugged - if #job.items ~= 0 then - -- job in progress: only happens on the first run. - -- experimental fix: remove the items and un-task them - while #job.items ~= 0 do - job.items[0].item.flags.in_job = false - job.items:erase(0) - end + -- job in progress: only happens on the first run. + -- experimental fix: remove the items and un-task them + while #job.items ~= 0 do + job.items[0].item.flags.in_job = false + job.items:erase(0) end enforce_order_details(job, order) :: nextjob :: @@ -148,7 +146,8 @@ end local function enable(yell) local manager_timer = df.global.plotinfo.manager_timer local d = df.global.cur_year_tick - local time_until = -(d - 30) % 150 -- + manager_timer * 150 + -- it's a potential dispatch tick when tick % 150 == 30 + local time_until = (30 - d) % 150 -- + manager_timer * 150 if time_until == 0 then schedule_handler() else From f6275f027e46673f3e49b4c0028a2a258de038dc Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sun, 14 Jan 2024 16:04:03 -0800 Subject: [PATCH 24/36] line endings --- fix/workorder-detail-fix.lua | 402 +++++++++++++++++------------------ 1 file changed, 201 insertions(+), 201 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 04188dc9ee..2786c51d94 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -1,201 +1,201 @@ ---@enable = true ---@module = true -local repeatutil = require 'repeat-util' -local utils = require 'utils' -local script_name = "workorder-detail-fix" -local schedule_key = script_name..":dispatch" - -enabled = enabled or false -- enabled API -function isEnabled() return enabled end - -local managers = df.global.plotinfo.main.fortress_entity.assignments_by_type.MANAGE_PRODUCTION -if not managers then error("NO MANAGERS VECTOR!") end -local last_job_id = -1 -local jobs_corrected = 0 - --- all jobs with the NONE (-1) type in its default job_items may be a problem -local offending_jobs = { - [df.job_type.EncrustWithGems] = true, - [df.job_type.EncrustWithGlass] = true, - [df.job_type.StudWith] = true, - [df.job_type.PrepareMeal] = true, - [df.job_type.DecorateWith] = true, - [df.job_type.SewImage] = true, - -- list may be incomplete -} - --- copy order.item fields/flags over to job's job_item --- only the essentials: stuff that is editable via gui/job-details -local function correct_item_details(job_item, order_item) - local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} - for _, field in pairs(fields) do - job_item[field] = order_item[field] - end - - local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} - for _, flags in pairs(flags_names) do - local order_flags = order_item[flags] - if type(order_flags) == 'number' then - job_item[flags] = order_flags - else -- copy over the flags one by one - for o_flag, val in pairs(order_flags) do - job_item[flags][o_flag] = val - end - end - end -end - --- correct job's job_items to match the order's -local function enforce_order_details(job, order) - if not job.job_items then return end -- never happens (error here?) - local modified = false - for idx, job_item in ipairs(job.job_items) do - local order_item = order.items[idx] - if not order_item then break end -- never happens (error here?) - if job_item.item_type ~= order_item.item_type then - -- dfhack's isSuitableItem function will allow the orders we want, - -- but disallow insane combinations like meals made of shoes - local suitable = dfhack.job.isSuitableItem( - job_item, order_item.item_type, order_item.item_subtype ) - if suitable then - correct_item_details(job_item, order_item) - modified = true - else --[[ error on unsuitable item?]] end - end - end - if modified then jobs_corrected = jobs_corrected + 1 end -end - --- check orders list to see if fix is needed --- yields id-order table for the orders with issues, or nil when no issues -local PrepareMeal = df.job_type.PrepareMeal -local SewImage = df.job_type.SewImage -local NONE = df.job_type.NONE -local function get_bugged_orders() - local orders = {} - local num_bugged, improve_item_index, item = 0, 1, nil - for _, order in ipairs(df.global.world.manager_orders) do - if not order.items then goto nextorder end - local order_job_type = order.job_type - if not offending_jobs[order_job_type] then goto nextorder end - if #order.items == 0 then goto nextorder end -- doesn't happen - - -- for PrepareMeal jobs, any one of the items could be an issue. - if order_job_type == PrepareMeal then - for _, _item in ipairs(order.items) do - if _item.item_type ~= NONE then goto fix end - end - goto nextorder - end - - -- All other types are improve jobs; only the improved item is checked - -- Only SewImage has the item-to-improve at items[0] - improve_item_index = (order_job_type ~= SewImage) and 1 or 0 - item = order.items[improve_item_index] - if not item then goto nextorder end -- error here? - if item.item_type == NONE then goto nextorder end - :: fix :: - num_bugged = num_bugged + 1 - orders[order.id] = order - :: nextorder :: - end - if num_bugged == 0 then return nil end - return orders -end - --- correct newly dispatched work order jobs -local disable -local function on_dispatch_tick() - if df.global.cur_year_tick % 150 ~= 30 then - print(script_name.." desynced from dispatch tick") - repeatutil.cancel(schedule_key) - disable(true) - end - if #managers == 0 then return end - if df.global.plotinfo.manager_timer ~= 10 then return end - local orders = get_bugged_orders() - if not orders then return end -- no bugs to fix - local highest = last_job_id - for _, job in utils.listpairs(df.global.world.jobs.list) do - if job.id <= last_job_id then goto nextjob end - highest = math.max(job.id, highest) - if job.order_id == -1 then goto nextjob end - local order = orders[job.order_id] - if not order then goto nextjob end -- order wasn't bugged - -- job in progress: only happens on the first run. - -- experimental fix: remove the items and un-task them - while #job.items ~= 0 do - job.items[0].item.flags.in_job = false - job.items:erase(0) - end - enforce_order_details(job, order) - :: nextjob :: - end - last_job_id = highest -end - -timeout_id = timeout_id or nil -local function schedule_handler() - if repeatutil.cancel(schedule_key) then - print(script_name..": canceled old dispatch handler") - end - repeatutil.scheduleEvery(schedule_key, 150, 'ticks', on_dispatch_tick) - timeout_id = nil -end - -local function enable(yell) - local manager_timer = df.global.plotinfo.manager_timer - local d = df.global.cur_year_tick - -- it's a potential dispatch tick when tick % 150 == 30 - local time_until = (30 - d) % 150 -- + manager_timer * 150 - if time_until == 0 then - schedule_handler() - else - timeout_id = dfhack.timeout(time_until, 'ticks', schedule_handler) - end - enabled = true - if yell then print(script_name.." ENABLED") end -end - -function disable(yell) - if timeout_id then - dfhack.timeout_active(timeout_id, nil) - timeout_id = nil - end - repeatutil.cancel(schedule_key) - enabled = false - if yell then print(script_name.." DISABLED") end -end - --- (not working with enabled API, probably something to do with module mode) -local function status() - local status = "DISABLED" or enabled and "ENABLED" - print(script_name.." status: "..status.." # jobs corrected: "..tostring(jobs_corrected)) -end - --- check if script was called by enable API -if dfhack_flags.enable then - if dfhack_flags.enable_state then - enable(false) - else - disable(false) - end - return -end - -if dfhack_flags.module then return end - --- check the arguments -local args={...} - -if not args[1] then - print(script_name.." valid cmds: enable, disable, status") - return -end - -local cmd_table = { ['enable']=enable, ['disable']=disable, ['status']=status } - -local cmd = cmd_table[args[1]:lower()] -if cmd then cmd(true) else - print(script_name.." valid cmds: enable, disable, status") -end +--@enable = true +--@module = true +local repeatutil = require 'repeat-util' +local utils = require 'utils' +local script_name = "workorder-detail-fix" +local schedule_key = script_name..":dispatch" + +enabled = enabled or false -- enabled API +function isEnabled() return enabled end + +local managers = df.global.plotinfo.main.fortress_entity.assignments_by_type.MANAGE_PRODUCTION +if not managers then error("NO MANAGERS VECTOR!") end +local last_job_id = -1 +local jobs_corrected = 0 + +-- all jobs with the NONE (-1) type in its default job_items may be a problem +local offending_jobs = { + [df.job_type.EncrustWithGems] = true, + [df.job_type.EncrustWithGlass] = true, + [df.job_type.StudWith] = true, + [df.job_type.PrepareMeal] = true, + [df.job_type.DecorateWith] = true, + [df.job_type.SewImage] = true, + -- list may be incomplete +} + +-- copy order.item fields/flags over to job's job_item +-- only the essentials: stuff that is editable via gui/job-details +local function correct_item_details(job_item, order_item) + local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} + for _, field in pairs(fields) do + job_item[field] = order_item[field] + end + + local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} + for _, flags in pairs(flags_names) do + local order_flags = order_item[flags] + if type(order_flags) == 'number' then + job_item[flags] = order_flags + else -- copy over the flags one by one + for o_flag, val in pairs(order_flags) do + job_item[flags][o_flag] = val + end + end + end +end + +-- correct job's job_items to match the order's +local function enforce_order_details(job, order) + if not job.job_items then return end -- never happens (error here?) + local modified = false + for idx, job_item in ipairs(job.job_items) do + local order_item = order.items[idx] + if not order_item then break end -- never happens (error here?) + if job_item.item_type ~= order_item.item_type then + -- dfhack's isSuitableItem function will allow the orders we want, + -- but disallow insane combinations like meals made of shoes + local suitable = dfhack.job.isSuitableItem( + job_item, order_item.item_type, order_item.item_subtype ) + if suitable then + correct_item_details(job_item, order_item) + modified = true + else --[[ error on unsuitable item?]] end + end + end + if modified then jobs_corrected = jobs_corrected + 1 end +end + +-- check orders list to see if fix is needed +-- yields id-order table for the orders with issues, or nil when no issues +local PrepareMeal = df.job_type.PrepareMeal +local SewImage = df.job_type.SewImage +local NONE = df.job_type.NONE +local function get_bugged_orders() + local orders = {} + local num_bugged, improve_item_index, item = 0, 1, nil + for _, order in ipairs(df.global.world.manager_orders) do + if not order.items then goto nextorder end + local order_job_type = order.job_type + if not offending_jobs[order_job_type] then goto nextorder end + if #order.items == 0 then goto nextorder end -- doesn't happen + + -- for PrepareMeal jobs, any one of the items could be an issue. + if order_job_type == PrepareMeal then + for _, _item in ipairs(order.items) do + if _item.item_type ~= NONE then goto fix end + end + goto nextorder + end + + -- All other types are improve jobs; only the improved item is checked + -- Only SewImage has the item-to-improve at items[0] + improve_item_index = (order_job_type ~= SewImage) and 1 or 0 + item = order.items[improve_item_index] + if not item then goto nextorder end -- error here? + if item.item_type == NONE then goto nextorder end + :: fix :: + num_bugged = num_bugged + 1 + orders[order.id] = order + :: nextorder :: + end + if num_bugged == 0 then return nil end + return orders +end + +-- correct newly dispatched work order jobs +local disable +local function on_dispatch_tick() + if df.global.cur_year_tick % 150 ~= 30 then + print(script_name.." desynced from dispatch tick") + repeatutil.cancel(schedule_key) + disable(true) + end + if #managers == 0 then return end + if df.global.plotinfo.manager_timer ~= 10 then return end + local orders = get_bugged_orders() + if not orders then return end -- no bugs to fix + local highest = last_job_id + for _, job in utils.listpairs(df.global.world.jobs.list) do + if job.id <= last_job_id then goto nextjob end + highest = math.max(job.id, highest) + if job.order_id == -1 then goto nextjob end + local order = orders[job.order_id] + if not order then goto nextjob end -- order wasn't bugged + -- job in progress: only happens on the first run. + -- experimental fix: remove the items and un-task them + while #job.items ~= 0 do + job.items[0].item.flags.in_job = false + job.items:erase(0) + end + enforce_order_details(job, order) + :: nextjob :: + end + last_job_id = highest +end + +timeout_id = timeout_id or nil +local function schedule_handler() + if repeatutil.cancel(schedule_key) then + print(script_name..": canceled old dispatch handler") + end + repeatutil.scheduleEvery(schedule_key, 150, 'ticks', on_dispatch_tick) + timeout_id = nil +end + +local function enable(yell) + local manager_timer = df.global.plotinfo.manager_timer + local d = df.global.cur_year_tick + -- it's a potential dispatch tick when tick % 150 == 30 + local time_until = (30 - d) % 150 -- + manager_timer * 150 + if time_until == 0 then + schedule_handler() + else + timeout_id = dfhack.timeout(time_until, 'ticks', schedule_handler) + end + enabled = true + if yell then print(script_name.." ENABLED") end +end + +function disable(yell) + if timeout_id then + dfhack.timeout_active(timeout_id, nil) + timeout_id = nil + end + repeatutil.cancel(schedule_key) + enabled = false + if yell then print(script_name.." DISABLED") end +end + +-- (not working with enabled API, probably something to do with module mode) +local function status() + local status = "DISABLED" or enabled and "ENABLED" + print(script_name.." status: "..status.." # jobs corrected: "..tostring(jobs_corrected)) +end + +-- check if script was called by enable API +if dfhack_flags.enable then + if dfhack_flags.enable_state then + enable(false) + else + disable(false) + end + return +end + +if dfhack_flags.module then return end + +-- check the arguments +local args={...} + +if not args[1] then + print(script_name.." valid cmds: enable, disable, status") + return +end + +local cmd_table = { ['enable']=enable, ['disable']=disable, ['status']=status } + +local cmd = cmd_table[args[1]:lower()] +if cmd then cmd(true) else + print(script_name.." valid cmds: enable, disable, status") +end From 384625d01e93f67aff71eef4802a96b7063f7f49 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Sun, 14 Jan 2024 17:21:43 -0800 Subject: [PATCH 25/36] copy flags whole instead of one-by-one --- fix/workorder-detail-fix.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-detail-fix.lua index 2786c51d94..a6eb61f088 100644 --- a/fix/workorder-detail-fix.lua +++ b/fix/workorder-detail-fix.lua @@ -37,10 +37,8 @@ local function correct_item_details(job_item, order_item) local order_flags = order_item[flags] if type(order_flags) == 'number' then job_item[flags] = order_flags - else -- copy over the flags one by one - for o_flag, val in pairs(order_flags) do - job_item[flags][o_flag] = val - end + else + job_item[flags].whole = order_flags.whole end end end From 3cff781aad07ed3b2c958649b91c168cd64c0492 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 18:06:25 -0800 Subject: [PATCH 26/36] alphebetize entry in control panel registry --- internal/control-panel/registry.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/control-panel/registry.lua b/internal/control-panel/registry.lua index c9b9d636a7..190bba0f4f 100644 --- a/internal/control-panel/registry.lua +++ b/internal/control-panel/registry.lua @@ -62,8 +62,8 @@ COMMANDS_BY_IDX = { {command='fix/stuck-instruments', group='bugfix', mode='repeat', default=true, desc='Fix activity references on stuck instruments to make them usable again.', params={'--time', '1', '--timeUnits', 'days', '--command', '[', 'fix/stuck-instruments', ']'}}, + {command='fix/workorder-details', group='bugfix', mode='enable', default=true}, {command='preserve-tombs', group='bugfix', mode='enable', default=true}, - {command='fix/workorder-detail-fix', group='bugfix', mode='enable', default=true}, -- gameplay tools {command='combine', group='gameplay', mode='repeat', From b0146e7fcbc372bf6e5b2d3cc6af7b3d87c1a8e4 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 18:39:09 -0800 Subject: [PATCH 27/36] update docs: new name/usage --- changelog.txt | 2 +- ...order-detail-fix.rst => workorder-details.rst} | 15 +++++++-------- ...order-detail-fix.lua => workorder-details.lua} | 0 3 files changed, 8 insertions(+), 9 deletions(-) rename docs/fix/{workorder-detail-fix.rst => workorder-details.rst} (71%) rename fix/{workorder-detail-fix.lua => workorder-details.lua} (100%) diff --git a/changelog.txt b/changelog.txt index 1110fd8f21..cc9a9a0d08 100644 --- a/changelog.txt +++ b/changelog.txt @@ -42,7 +42,7 @@ Template for new versions: - `warn-stranded`: Update onZoom to use df's centering functionality - `ban-cooking`: fix banning creature alcohols resulting in error - `confirm`: properly detect clicks on the remove zone button even when the unit selection screen is also open (e.g. the vanilla assign animal to pasture panel) -- `fix/workorder-detail-fix`: fix item types not being passed properly on some modified work order jobs +- `fix/workorder-details`: fix item types not being passed properly on some modified work order jobs - `quickfort`: if a blueprint specifies an up/down stair, but the tile the blueprint is applied to cannot make an up stair (e.g. it has already been dug out), still designate a down stair if possible ## Misc Improvements diff --git a/docs/fix/workorder-detail-fix.rst b/docs/fix/workorder-details.rst similarity index 71% rename from docs/fix/workorder-detail-fix.rst rename to docs/fix/workorder-details.rst index 132bc29398..bb222e9e81 100644 --- a/docs/fix/workorder-detail-fix.rst +++ b/docs/fix/workorder-details.rst @@ -1,5 +1,5 @@ -fix/workorder-detail-fix -======================== +fix/workorder-details +===================== .. dfhack-tool:: :summary: Fixes a bug with modified work orders creating incorrect jobs. @@ -16,9 +16,8 @@ This fix forces these jobs to properly inherit the item details from their work Usage ----- -``fix/workorder-detail-fix enable`` - enables the fix -``fix/workorder-detail-fix disable`` - disables the fix -``fix/workorder-detail-fix status`` - print fix status +:: + + enable fix/workorder-details + + fix/workorder-details [status] diff --git a/fix/workorder-detail-fix.lua b/fix/workorder-details.lua similarity index 100% rename from fix/workorder-detail-fix.lua rename to fix/workorder-details.lua From f504edceb053a2f8e78355b61cba0272daddda4b Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 20:44:41 -0800 Subject: [PATCH 28/36] fix problems with status()/jobs_corrected --- fix/workorder-details.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index a6eb61f088..5d9c817c27 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -2,7 +2,7 @@ --@module = true local repeatutil = require 'repeat-util' local utils = require 'utils' -local script_name = "workorder-detail-fix" +local script_name = "fix/workorder-details" local schedule_key = script_name..":dispatch" enabled = enabled or false -- enabled API @@ -11,7 +11,7 @@ function isEnabled() return enabled end local managers = df.global.plotinfo.main.fortress_entity.assignments_by_type.MANAGE_PRODUCTION if not managers then error("NO MANAGERS VECTOR!") end local last_job_id = -1 -local jobs_corrected = 0 +jobs_corrected = jobs_corrected or 0 -- all jobs with the NONE (-1) type in its default job_items may be a problem local offending_jobs = { @@ -142,6 +142,7 @@ local function schedule_handler() end local function enable(yell) + jobs_corrected = 0 local manager_timer = df.global.plotinfo.manager_timer local d = df.global.cur_year_tick -- it's a potential dispatch tick when tick % 150 == 30 @@ -167,8 +168,8 @@ end -- (not working with enabled API, probably something to do with module mode) local function status() - local status = "DISABLED" or enabled and "ENABLED" - print(script_name.." status: "..status.." # jobs corrected: "..tostring(jobs_corrected)) + local status = enabled and "Enabled" or "Disabled" + print(script_name.." status: "..status..". Jobs corrected: "..tostring(jobs_corrected)) end -- check if script was called by enable API @@ -187,7 +188,7 @@ if dfhack_flags.module then return end local args={...} if not args[1] then - print(script_name.." valid cmds: enable, disable, status") + status() return end From 027f410d550e7c1013cc8aea6e05811551cc48a2 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 20:54:16 -0800 Subject: [PATCH 29/36] fix redundant repeatutil usage --- fix/workorder-details.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index 5d9c817c27..e6f05276fa 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -134,9 +134,6 @@ end timeout_id = timeout_id or nil local function schedule_handler() - if repeatutil.cancel(schedule_key) then - print(script_name..": canceled old dispatch handler") - end repeatutil.scheduleEvery(schedule_key, 150, 'ticks', on_dispatch_tick) timeout_id = nil end From b7c0ce8a3511b4f9427562a58a9e12108bd0d3e1 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 21:08:21 -0800 Subject: [PATCH 30/36] print jobs_corrected only when enabled --- fix/workorder-details.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index e6f05276fa..8904d97d3d 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -166,7 +166,10 @@ end -- (not working with enabled API, probably something to do with module mode) local function status() local status = enabled and "Enabled" or "Disabled" - print(script_name.." status: "..status..". Jobs corrected: "..tostring(jobs_corrected)) + if enabled then + status = status..". Jobs corrected: "..tostring(jobs_corrected) + end + print(script_name.." status: "..status) end -- check if script was called by enable API From 4ca5a42b7535c9d14be70e4be2d6f5feff34c2af Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 21:42:47 -0800 Subject: [PATCH 31/36] remove active job fix (unstable) --- fix/workorder-details.lua | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index 8904d97d3d..c86b1ef981 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -120,12 +120,6 @@ local function on_dispatch_tick() if job.order_id == -1 then goto nextjob end local order = orders[job.order_id] if not order then goto nextjob end -- order wasn't bugged - -- job in progress: only happens on the first run. - -- experimental fix: remove the items and un-task them - while #job.items ~= 0 do - job.items[0].item.flags.in_job = false - job.items:erase(0) - end enforce_order_details(job, order) :: nextjob :: end From f961c8f3fcb0cc1eb5e36360bfed6f56dde49210 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 21:54:19 -0800 Subject: [PATCH 32/36] use ipairs, utils.invert --- fix/workorder-details.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index c86b1ef981..2c695680b4 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -14,26 +14,26 @@ local last_job_id = -1 jobs_corrected = jobs_corrected or 0 -- all jobs with the NONE (-1) type in its default job_items may be a problem -local offending_jobs = { - [df.job_type.EncrustWithGems] = true, - [df.job_type.EncrustWithGlass] = true, - [df.job_type.StudWith] = true, - [df.job_type.PrepareMeal] = true, - [df.job_type.DecorateWith] = true, - [df.job_type.SewImage] = true, +local offending_jobs = utils.invert({ + df.job_type.EncrustWithGems, + df.job_type.EncrustWithGlass, + df.job_type.StudWith, + df.job_type.PrepareMeal, + df.job_type.DecorateWith, + df.job_type.SewImage, -- list may be incomplete -} +}) -- copy order.item fields/flags over to job's job_item -- only the essentials: stuff that is editable via gui/job-details local function correct_item_details(job_item, order_item) local fields = {'item_type', 'item_subtype', 'mat_type', 'mat_index'} - for _, field in pairs(fields) do + for _, field in ipairs(fields) do job_item[field] = order_item[field] end local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'} - for _, flags in pairs(flags_names) do + for _, flags in ipairs(flags_names) do local order_flags = order_item[flags] if type(order_flags) == 'number' then job_item[flags] = order_flags From fa308ab84cdbcade5c1c3526c1a0e541e0a495ac Mon Sep 17 00:00:00 2001 From: flashy-man Date: Wed, 17 Jan 2024 22:32:42 -0800 Subject: [PATCH 33/36] better way to get manager; move local declarations --- fix/workorder-details.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index 2c695680b4..1d0d9fa5c1 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -8,8 +8,6 @@ local schedule_key = script_name..":dispatch" enabled = enabled or false -- enabled API function isEnabled() return enabled end -local managers = df.global.plotinfo.main.fortress_entity.assignments_by_type.MANAGE_PRODUCTION -if not managers then error("NO MANAGERS VECTOR!") end local last_job_id = -1 jobs_corrected = jobs_corrected or 0 @@ -71,13 +69,16 @@ local SewImage = df.job_type.SewImage local NONE = df.job_type.NONE local function get_bugged_orders() local orders = {} - local num_bugged, improve_item_index, item = 0, 1, nil + local num_bugged = 0 for _, order in ipairs(df.global.world.manager_orders) do if not order.items then goto nextorder end local order_job_type = order.job_type if not offending_jobs[order_job_type] then goto nextorder end if #order.items == 0 then goto nextorder end -- doesn't happen + local improve_item_index = 1 + local item + -- for PrepareMeal jobs, any one of the items could be an issue. if order_job_type == PrepareMeal then for _, _item in ipairs(order.items) do @@ -86,6 +87,7 @@ local function get_bugged_orders() goto nextorder end + -- All other types are improve jobs; only the improved item is checked -- Only SewImage has the item-to-improve at items[0] improve_item_index = (order_job_type ~= SewImage) and 1 or 0 @@ -109,7 +111,7 @@ local function on_dispatch_tick() repeatutil.cancel(schedule_key) disable(true) end - if #managers == 0 then return end + if not dfhack.units.getUnitByNobleRole('manager') then return end if df.global.plotinfo.manager_timer ~= 10 then return end local orders = get_bugged_orders() if not orders then return end -- no bugs to fix From e584bba12b892fab00baa42e9fed7b6f2ade2619 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Thu, 18 Jan 2024 00:45:18 -0800 Subject: [PATCH 34/36] automatic re-sync if lost --- fix/workorder-details.lua | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index 1d0d9fa5c1..796d408dab 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -87,7 +87,6 @@ local function get_bugged_orders() goto nextorder end - -- All other types are improve jobs; only the improved item is checked -- Only SewImage has the item-to-improve at items[0] improve_item_index = (order_job_type ~= SewImage) and 1 or 0 @@ -104,17 +103,13 @@ local function get_bugged_orders() end -- correct newly dispatched work order jobs -local disable +local disable, schedule_handler local function on_dispatch_tick() - if df.global.cur_year_tick % 150 ~= 30 then - print(script_name.." desynced from dispatch tick") - repeatutil.cancel(schedule_key) - disable(true) - end if not dfhack.units.getUnitByNobleRole('manager') then return end if df.global.plotinfo.manager_timer ~= 10 then return end local orders = get_bugged_orders() if not orders then return end -- no bugs to fix + local highest = last_job_id for _, job in utils.listpairs(df.global.world.jobs.list) do if job.id <= last_job_id then goto nextjob end @@ -122,29 +117,40 @@ local function on_dispatch_tick() if job.order_id == -1 then goto nextjob end local order = orders[job.order_id] if not order then goto nextjob end -- order wasn't bugged + -- skip jobs with items already gathered + if job.items and (#job.items > 0) then goto nextjob end enforce_order_details(job, order) :: nextjob :: end last_job_id = highest + + if df.global.cur_year_tick % 150 ~= 30 then + print(script_name..": lost sync with dispatch tick. Resetting...") + schedule_handler() + end end timeout_id = timeout_id or nil -local function schedule_handler() +local function start_handler() repeatutil.scheduleEvery(schedule_key, 150, 'ticks', on_dispatch_tick) timeout_id = nil end -local function enable(yell) - jobs_corrected = 0 +function schedule_handler() local manager_timer = df.global.plotinfo.manager_timer local d = df.global.cur_year_tick -- it's a potential dispatch tick when tick % 150 == 30 local time_until = (30 - d) % 150 -- + manager_timer * 150 if time_until == 0 then - schedule_handler() + start_handler() else - timeout_id = dfhack.timeout(time_until, 'ticks', schedule_handler) + timeout_id = dfhack.timeout(time_until, 'ticks', start_handler) end +end + +local function enable(yell) + jobs_corrected = 0 + schedule_handler() enabled = true if yell then print(script_name.." ENABLED") end end From fcc8546f0816d53145affb102f7cb193d25d4568 Mon Sep 17 00:00:00 2001 From: flashy-man Date: Thu, 18 Jan 2024 01:33:06 -0800 Subject: [PATCH 35/36] documented active job limitation --- docs/fix/workorder-details.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/fix/workorder-details.rst b/docs/fix/workorder-details.rst index bb222e9e81..f2372c30af 100644 --- a/docs/fix/workorder-details.rst +++ b/docs/fix/workorder-details.rst @@ -13,6 +13,8 @@ Example 2: a Prepare Meal order, modified to use all plant type ingredients, ins This fix forces these jobs to properly inherit the item details from their work order. +Jobs that were active before enabling the fix may not be affected. + Usage ----- From fea88ca49d048fd6a37aab57a574d022236a123c Mon Sep 17 00:00:00 2001 From: flashy-man Date: Thu, 18 Jan 2024 03:25:06 -0800 Subject: [PATCH 36/36] delimit block so goto can jump over local scope --- fix/workorder-details.lua | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/fix/workorder-details.lua b/fix/workorder-details.lua index 796d408dab..3d1a87267d 100644 --- a/fix/workorder-details.lua +++ b/fix/workorder-details.lua @@ -76,9 +76,6 @@ local function get_bugged_orders() if not offending_jobs[order_job_type] then goto nextorder end if #order.items == 0 then goto nextorder end -- doesn't happen - local improve_item_index = 1 - local item - -- for PrepareMeal jobs, any one of the items could be an issue. if order_job_type == PrepareMeal then for _, _item in ipairs(order.items) do @@ -88,11 +85,13 @@ local function get_bugged_orders() end -- All other types are improve jobs; only the improved item is checked - -- Only SewImage has the item-to-improve at items[0] - improve_item_index = (order_job_type ~= SewImage) and 1 or 0 - item = order.items[improve_item_index] - if not item then goto nextorder end -- error here? - if item.item_type == NONE then goto nextorder end + do + -- Only SewImage has the item-to-improve at items[0] + local improve_item_index = (order_job_type == SewImage) and 0 or 1 + local item = order.items[improve_item_index] + if not item then goto nextorder end -- error here? + if item.item_type == NONE then goto nextorder end + end :: fix :: num_bugged = num_bugged + 1 orders[order.id] = order @@ -165,7 +164,6 @@ function disable(yell) if yell then print(script_name.." DISABLED") end end --- (not working with enabled API, probably something to do with module mode) local function status() local status = enabled and "Enabled" or "Disabled" if enabled then