Skip to content

Commit

Permalink
Merge pull request #1093 from Ajhaa/order-instruments
Browse files Browse the repository at this point in the history
Add instruments order
  • Loading branch information
myk002 committed Apr 30, 2024
2 parents bb11f9a + d4c683a commit 747aec4
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 33 deletions.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Template for new versions:
- Validated for adventure mode: `gui/sandbox`, `gui/create-item`

## New Features
- `instruments`: new subcommand ``instruments order`` for creating instrument work orders

## Fixes
- `modtools/create-item`: adjust for API changes, tolerate ``reaction-gloves`` tweak being active
Expand Down
31 changes: 27 additions & 4 deletions docs/instruments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,42 @@ instruments
===========

.. dfhack-tool::
:summary: Show how to craft instruments.
:tags: fort inspection
:summary: Show how to craft instruments or create work orders for them.
:tags: fort inspection workorders

This tool provides information on how to craft the instruments used by the
This tool is used to query information about instruments or to create work orders for them.

The ``list`` subcommand provides information on how to craft the instruments used by the
player civilization. For single-piece instruments, it shows the skill and
material needed to craft it. For multi-piece instruments, it displays the skill
used in its assembly as well as information on how to craft the necessary
pieces. It also shows whether the instrument is handheld or placed as a
building.

The ``order`` subcommand is used to create work orders for an instrument and all of it's parts.
The final assemble instrument -order waits for the part orders to complete before starting.

Usage
-----

::

instruments
instruments [list]
instruments order <instrument_name> [<amount>]

When ordering, the default is to order one of the specified instrument (including all
of its components).

Examples
--------

``instruments``
List instruments and their recipes.
``instruments order givel 10``
If the instrument named ``givel`` in your world has four components, this will
create a total of 5 work orders: one for assembling 10 givels, and an order
of 10 for each of the givel's parts. Instruments are randomly generated, so
your givel components may vary.

``instruments order ilul``
Creates work orders to assemble one ïlul. Spelling doesn't need to include the special ï character.
129 changes: 104 additions & 25 deletions instruments.lua
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
local workorder = reqscript('workorder')

-- civilization ID of the player civilization
local civ_id = df.global.plotinfo.civ_id
local raws = df.global.world.raws

---@type instrument itemdef_instrumentst
---@return reaction|nil
function getAssemblyReaction(instrument)
for _, reaction in ipairs(df.global.world.raws.reactions.reactions) do
function getAssemblyReaction(instrument_id)
for _, reaction in ipairs(raws.reactions.reactions) do
if reaction.source_enid == civ_id and
reaction.category == 'INSTRUMENT' and
reaction.name:find(instrument.name, 1, true)
reaction.code:find(instrument_id, 1, true)
then
return reaction
end
Expand All @@ -23,7 +26,7 @@ function reagentString(reagent)
local silk = reagent.flags2.silk and "silk " or ""
local yarn = reagent.flags2.yarn and "yarn " or ""
local plant = reagent.flags2.plant and "plant " or ""
return silk..yarn..plant.."thread"
return silk .. yarn .. plant .. "thread"
else
return reagent.code
end
Expand All @@ -40,31 +43,107 @@ function describeReaction(reaction)
return skill .. ": " .. table.concat(reagents, ", ")
end

-- gather instrument piece reactions and index them by the instrument they are part of
local instruments = {}
for _, reaction in ipairs(df.global.world.raws.reactions.reactions) do
if reaction.source_enid == civ_id and reaction.category == 'INSTRUMENT_PIECE' then
local iname = reaction.name:match("[^ ]+ ([^ ]+)")
table.insert(ensure_key(instruments, iname),
reaction.name.." ("..describeReaction(reaction)..")")
local function print_list()
-- gather instrument piece reactions and index them by the instrument they are part of
local instruments = {}
for _, reaction in ipairs(raws.reactions.reactions) do
if reaction.source_enid == civ_id and reaction.category == 'INSTRUMENT_PIECE' then
local iname = reaction.name:match("[^ ]+ ([^ ]+)")
table.insert(ensure_key(instruments, iname),
reaction.name .. " (" .. describeReaction(reaction) .. ")")
end
end

-- go over instruments
for _, instrument in ipairs(raws.itemdefs.instruments) do
if not (instrument.source_enid == civ_id) then goto continue end

local building_tag = instrument.flags.PLACED_AS_BUILDING and " (building, " or " (handheld, "
local reaction = getAssemblyReaction(instrument.id)
dfhack.print(instrument.name .. building_tag)
if #instrument.pieces == 0 then
print(describeReaction(reaction) .. ")")
else
print(df.job_skill[reaction.skill] .. "/assemble)")
for _, str in pairs(instruments[instrument.name]) do
print(" " .. str)
end
end
print()
::continue::
end
end

-- go over instruments
for _,instrument in ipairs(df.global.world.raws.itemdefs.instruments) do
if not (instrument.source_enid == civ_id) then goto continue end
local function order_instrument(name, amount)
local instrument = nil

local building_tag = instrument.flags.PLACED_AS_BUILDING and " (building, " or " (handheld, "
local reaction = getAssemblyReaction(instrument)
dfhack.print(instrument.name..building_tag)
if #instrument.pieces == 0 then
print(describeReaction(reaction)..")")
else
print(df.job_skill[reaction.skill].."/assemble)")
for _,str in pairs(instruments[instrument.name]) do
print(" "..str)
for _, instr in ipairs(raws.itemdefs.instruments) do
if dfhack.toSearchNormalized(instr.name) == name and instr.source_enid == civ_id then
instrument = instr
end
end
print()
::continue::

if not instrument then
qerror("Could not find instrument " .. name)
end

local orders = {}

for i, reaction in ipairs(raws.reactions.reactions) do
if reaction.source_enid == civ_id and reaction.category == 'INSTRUMENT_PIECE' and reaction.code:find(instrument.id, 1, true) then
local part_order = {
id=i,
amount_total=amount,
reaction=reaction.code,
job="CustomReaction",
}
table.insert(orders, part_order)
end
end

if #orders < #instrument.pieces then
print("Warning: Could not find reactions for all instrument pieces")
end

local assembly_reaction = getAssemblyReaction(instrument.id)

local assembly_order = {
id=-1,
amount_total=amount,
reaction=assembly_reaction.code,
job="CustomReaction",
order_conditions={}
}

for _, order in ipairs(orders) do
table.insert(
assembly_order.order_conditions,
{
condition="Completed",
order=order.id
}
)
end

table.insert(orders, assembly_order)

orders = workorder.preprocess_orders(orders)
workorder.fillin_defaults(orders)
workorder.create_orders(orders)

print("\nCreated " .. #orders .. " work orders")
end

local args = { ... }

if #args == 0 or args[1] == "list" then
print_list()
elseif args[1] == "order" then
local instrument_name = args[2]
if not instrument_name then
qerror("Usage: instruments order <instrument_name> [<amount>]")
end

local amount = args[3] or 1
order_instrument(instrument_name, amount)
end
19 changes: 15 additions & 4 deletions workorder.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
-- which is a great place to look up stuff like "How the hell do I find out if
-- a creature can be sheared?!!"

--@ module=true


local function print_help()
print(dfhack.script_help())
end
Expand Down Expand Up @@ -181,7 +184,7 @@ end

-- creates a df.manager_order from it's definition.
-- this is translated orders.cpp to Lua,
local function create_orders(orders)
function create_orders(orders)
-- is dfhack.with_suspend necessary?

-- we need id mapping to restore saved order_conditions
Expand Down Expand Up @@ -429,7 +432,11 @@ local function create_orders(orders)
order.amount_left = amount
order.amount_total = amount

print("Queuing " .. df.job_type[order.job_type]
local job_type = df.job_type[order.job_type]
if job_type == "CustomReaction" then
job_type = job_type .. " '" .. order.reaction_name .. "'"
end
print("Queuing " .. job_type
.. (amount==0 and " infinitely" or " x"..amount))
world.manager_orders:insert('#', order)
end
Expand All @@ -438,7 +445,7 @@ local function create_orders(orders)
end

-- set missing values, process special `amount_total` value
local function preprocess_orders(orders)
function preprocess_orders(orders)
-- if called with single order make an array
if orders.job then
orders = {orders}
Expand Down Expand Up @@ -506,7 +513,7 @@ local order_defaults = {
frequency = 'OneTime'
}
local _order_mt = {__index = order_defaults}
local function fillin_defaults(orders)
function fillin_defaults(orders)
for _, order in ipairs(orders) do
setmetatable(order, _order_mt)
end
Expand Down Expand Up @@ -648,5 +655,9 @@ actions = {
["--reset"] = function() initialized = false end,
}

if dfhack_flags.module then
return
end

-- Lua is beautiful.
(actions[ (...) or "?" ] or default_action)(...)

0 comments on commit 747aec4

Please sign in to comment.