fix paths/clean up code/use best practices

develop
Myk 2022-09-14 10:33:27 -07:00 committed by GitHub
parent 7ccacd7875
commit d68c17d070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 65 additions and 73 deletions

@ -336,7 +336,7 @@ structure that looks like this::
raw/init.d/example-mod.lua
raw/objects/...
raw/scripts/example-mod/main.lua
raw/scripts/example-mod.lua
raw/scripts/example-mod/...
README.md
@ -346,9 +346,9 @@ Let's go through that line by line.
loaded.
* Modifications to the game raws (potentially with custom raw tokens) go in
``raw/objects/``.
* A subfolder for your mod under ``raw/scripts/`` will contain all the scripts used by
your mod, including the main initialization code in ``main.lua`` which registers all
your timer and event callbacks.
* A control script in ``raw/scripts/`` that handles enabling and disabling your mod.
* A subfolder for your mod under ``raw/scripts/`` will contain all the internal scripts
used by your mod.
It is a good idea to use a version control system to organize changes to your mod code.
You can create a separate Git repository for each of your mods. The ``README.md`` file
@ -361,107 +361,99 @@ script paths in ``dfhack-config/script-paths.txt``::
+/path/to/mymods/example-mod/raw/scripts
Ok, you're all set up! Now, let's take a look at an example
``raw/scripts/example-mod/main.lua`` file::
``raw/scripts/example-mod.lua`` file::
-- main setup and teardown for example-mod
-- this next line indicates that the script supports the "enable" API so you can start
-- it by running "enable example-mod" and stop it by running "disable example-mod"
--@ enable = true
local usage = [[
Usage
-----
enable example-mod
disable example-mod
]]
local repeatUtil = require('repeat-util')
local eventful = require('plugins.eventful')
local modId = 'example-mod'
local args = {...}
if args[1] == "enable" then
-- The modules and what they link into the environment with
-- Each module exports functions named the way they are to be used
local moduleA = dfhack.reqscript("example-mod/module-a") -- on load,
-- every tick
local moduleB = dfhack.reqscript("example-mod/module-b") -- on load,
-- on unload, onReactionComplete
local moduleC = dfhack.reqscript("example-mod/module-c")
-- onReactionComplete
local moduleD = dfhack.reqscript("example-mod/module-d") -- every 100
-- frames, onProjItemCheckMovement, onProjUnitCheckMovement
-- Set up the modules
-- Order: on load, repeat-util ticks (from smallest interval to
-- largest), days, months, years, and frames, then eventful callbacks in
-- the same order as the first modules to use them
moduleA.onLoad()
moduleB.onLoad()
-- you can reference global values or functions declared in any of your internal scripts
local moduleA = reqscript('example-mod/module-a')
local moduleB = reqscript('example-mod/module-b')
local moduleC = reqscript('example-mod/module-c')
local moduleD = reqscript('example-mod/module-d')
repeatUtil.scheduleEvery(modId .. " 1 ticks", 1, "ticks", function()
moduleA.every1Tick()
end)
enabled = enabled or false
local modId = 'example-mod'
repeatUtil.scheduleEvery(modID .. " 100 frames", 1, "frames", function()
moduleD.every100Frames()
if not dfhack_flags.enable then
print(usage)
print()
print(('Example mod is currently '):format(enabled and 'enabled' or 'disabled'))
return
end
eventful.onReactionComplete[modId] = function(...)
-- Pass the event's parameters to the listeners, whatever they are
moduleB.onReactionComplete(...)
moduleC.onReactionComplete(...)
end
if dfhack_flags.enable_state then
-- do any initialization your internal scripts might require
moduleA.onLoad()
moduleB.onLoad()
eventful.onProjItemCheckMovement[modId] = function(...)
moduleD.onProjItemCheckMovement(...)
end
-- register your callbacks
repeatUtil.scheduleEvery(modId .. ' every tick', 1, 'ticks', moduleA.every1Tick)
repeatUtil.scheduleEvery(modId .. ' 100 frames', 1, 'frames', moduleD.every100Frames)
eventful.onProjUnitCheckMovement[modId] = function(...)
moduleD.onProjUnitCheckMovement(...)
eventful.onReactionComplete[modId] = function(reaction, reaction_product, unit, input_items, input_reagents, output_items)
-- pass the event's parameters to the listeners
moduleB.onReactionComplete(reaction, reaction_product, unit, input_items, input_reagents, output_items)
moduleC.onReactionComplete(reaction, reaction_product, unit, input_items, input_reagents, output_items)
end
print("Example mod enabled")
elseif args[1] == "disable" then
-- Order: on unload, then cancel the callbacks in the same order as
-- above
eventful.onProjItemCheckMovement[modId] = moduleD.onProjItemCheckMovement
eventful.onProjUnitCheckMovement[modId] = moduleD.onProjUnitCheckMovement
print('Example mod enabled')
enabled = true
else
-- call any shutdown functions your internal scripts might require
moduleA.onUnload()
repeatUtil.cancel(modId .. " 1 ticks")
repeatUtil.cancel(modId .. " 100 frames")
repeatUtil.cancel(modId .. ' every ticks')
repeatUtil.cancel(modId .. ' 100 frames')
eventful.onReactionComplete[modId] = nil
eventful.onProjItemCheckMovement[modId] = nil
eventful.onProjUnitCheckMovement[modId] = nil
print("Example mod disabled")
elseif not args[1] then
dfhack.printerr("No argument given to example-mod/main")
else
dfhack.printerr("Unknown argument \"" .. args[1] ..
"\" to example-mod/main")
print('Example mod disabled')
enabled = false
end
You can see there are four cases depending on arguments. Set up the callbacks
and call on load functions if enabled, dismantle the callbacks and call on
unload functions if disabled, no arguments given, and invalid argument(s) given.
Here is an example of an ``raw/init.d/`` file: ::
You can call ``enable example-mod`` and ``disable example-mod`` yourself while
developing, but for end users you can start your mod automatically from
``raw/init.d/example-mod.lua``::
dfhack.run_command("example-mod/main enable") -- Very simple. Could be
-- called "init-example-mod.lua"
dfhack.run_command('enable example-mod')
Here is what ``raw/scripts/module-a.lua`` would look like: ::
Inside ``raw/scripts/example-mod/module-a.lua`` you could have code like this::
--@ module = true
-- The above line is required for dfhack.reqscript to work
-- The above line is required for reqscript to work
function onLoad() -- global variables are exported
-- blah
-- do initialization here
end
local function usedByOnTick() -- local variables are not exported
-- blah
local function usedByOnTick(unit) -- local variables are not exported
-- this is an internal function: local functions/variables are not exported
end
function onTick() -- exported
for blah in ipairs(blah) do
usedByOnTick()
for _,unit in ipairs(df.global.world.units.all) do
usedByOnTick(unit)
end
end
It is recommended to check `reqscript <reqscript>`'s documentation.
``reqscript`` caches scripts but will reload scripts that have changed (it
checks the file's last modification date) so you can do live editing *and* have
common tables et cetera between scripts that require the same module.
The `reqscript` function reloads scripts that have changed, so you can modify your
scripts while DF is running and just disable/enable your mod to load the changes into
your ongoing game!