Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

workorder-detail-fix: Enforce order item details for buggy job types #929

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c1960e0
add workorder-detail-fix
flashy-man Jan 6, 2024
0ac190c
better way to get the job type enums
flashy-man Jan 6, 2024
152fe5b
print valid commands when no arg is passed
flashy-man Jan 6, 2024
4e42bd8
print valid cmds when no arg is passed
flashy-man Jan 6, 2024
323121d
fix bad commit
flashy-man Jan 6, 2024
08a56b0
delete old job name -> id function
flashy-man Jan 6, 2024
b4b2518
workorder-detail-fix: use local variables
flashy-man Jan 6, 2024
9635fab
fix trailing whitespace; doc underline
flashy-man Jan 6, 2024
5c643b7
move workorder-detail-fix to fix directory, add enable API
flashy-man Jan 6, 2024
69a5e1f
no arg parsing when being used as a module (for enable api)
flashy-man Jan 6, 2024
e52adbe
actually set enable state
flashy-man Jan 6, 2024
b30668f
trailing whitespace again
flashy-man Jan 6, 2024
fae0027
hopefully fix line endings (sublime text calls them 'unix' endings)
flashy-man Jan 6, 2024
45f87de
hopefully fix EOF error (just needed newline at end of file)
flashy-man Jan 6, 2024
47761b6
only print when called from command line. also fix to work better wit…
flashy-man Jan 6, 2024
e1f170e
move doc to docs/fix
flashy-man Jan 7, 2024
ea76f59
workorder-detail-fix -> fix/workorder-detail-fix
flashy-man Jan 7, 2024
727200b
add detail_fix_is_needed() to say when the fix is required
flashy-man Jan 9, 2024
39a4db4
rework: only enable handler when detail_fix_is_needed
flashy-man Jan 9, 2024
a4ce456
better status() function to account for more statuses
flashy-man Jan 9, 2024
8ad4f77
woowee doc underline
flashy-man Jan 10, 2024
a5c2cc9
rename workorder-detail-fix in changelog
flashy-man Jan 14, 2024
4e950dd
no more eventful; only repeatutil
flashy-man Jan 14, 2024
a39ec2e
merged changelog
flashy-man Jan 14, 2024
d6b0775
fix redundancy, confusing modulo math
flashy-man Jan 15, 2024
f6275f0
line endings
flashy-man Jan 15, 2024
384625d
copy flags whole instead of one-by-one
flashy-man Jan 15, 2024
3cff781
alphebetize entry in control panel registry
flashy-man Jan 18, 2024
b0146e7
update docs: new name/usage
flashy-man Jan 18, 2024
f504edc
fix problems with status()/jobs_corrected
flashy-man Jan 18, 2024
027f410
fix redundant repeatutil usage
flashy-man Jan 18, 2024
b7c0ce8
print jobs_corrected only when enabled
flashy-man Jan 18, 2024
4ca5a42
remove active job fix (unstable)
flashy-man Jan 18, 2024
f961c8f
use ipairs, utils.invert
flashy-man Jan 18, 2024
fa308ab
better way to get manager; move local declarations
flashy-man Jan 18, 2024
e584bba
automatic re-sync if lost
flashy-man Jan 18, 2024
fcc8546
documented active job limitation
flashy-man Jan 18, 2024
fea88ca
delimit block so goto can jump over local scope
flashy-man Jan 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -42,6 +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-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
25 changes: 25 additions & 0 deletions docs/fix/workorder-details.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
fix/workorder-details
=====================

.. 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.

Jobs that were active before enabling the fix may not be affected.

Usage
-----

::

enable fix/workorder-details

fix/workorder-details [status]
200 changes: 200 additions & 0 deletions fix/workorder-details.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
--@enable = true
--@module = true
local repeatutil = require 'repeat-util'
local utils = require 'utils'
local script_name = "fix/workorder-details"
local schedule_key = script_name..":dispatch"

enabled = enabled or false -- enabled API
function isEnabled() return enabled end

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 = 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 ipairs(fields) do
job_item[field] = order_item[field]
end

local flags_names = {'flags1', 'flags2', 'flags3', 'flags4', 'flags5'}
for _, flags in ipairs(flags_names) do
local order_flags = order_item[flags]
if type(order_flags) == 'number' then
job_item[flags] = order_flags
else
job_item[flags].whole = order_flags.whole
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 = 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

-- 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
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
:: nextorder ::
end
if num_bugged == 0 then return nil end
return orders
end

-- correct newly dispatched work order jobs
local disable, schedule_handler
local function on_dispatch_tick()
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
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
-- 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 start_handler()
repeatutil.scheduleEvery(schedule_key, 150, 'ticks', on_dispatch_tick)
timeout_id = nil
end

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
start_handler()
else
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

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

local function status()
local status = enabled and "Enabled" or "Disabled"
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
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
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
1 change: 1 addition & 0 deletions internal/control-panel/registry.lua
Original file line number Diff line number Diff line change
@@ -62,6 +62,7 @@ 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},

-- gameplay tools