Merge branch 'develop' into spectate
commit
ddf7850d90
Can't render this file because it has a wrong number of fields in line 56.
|
@ -1 +1 @@
|
|||||||
Subproject commit ae19aebd795d6d91803e60f46de037b604593cb4
|
Subproject commit 6ed8aa46462ea01a1122fc49422840a2facc9757
|
@ -1,11 +1,14 @@
|
|||||||
===========
|
===========
|
||||||
User Guides
|
User guides
|
||||||
===========
|
===========
|
||||||
|
|
||||||
These pages are detailed guides covering DFHack tools.
|
These pages are detailed guides covering DFHack tools.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
:glob:
|
|
||||||
|
|
||||||
*
|
/docs/guides/examples-guide
|
||||||
|
/docs/guides/modding-guide
|
||||||
|
/docs/guides/quickfort-library-guide
|
||||||
|
/docs/guides/quickfort-user-guide
|
||||||
|
/docs/guides/quickfort-alias-guide
|
||||||
|
@ -0,0 +1,496 @@
|
|||||||
|
.. _modding-guide:
|
||||||
|
|
||||||
|
DFHack modding guide
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. highlight:: lua
|
||||||
|
|
||||||
|
What is the difference between a script and a mod?
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
A script is a single file that can be run as a command in DFHack, like something
|
||||||
|
that modifies or displays game data on request. A mod is something you install
|
||||||
|
to get persistent behavioural changes in the game and/or add new content. Mods
|
||||||
|
can contain and use scripts in addition to (or instead of) modifications to the
|
||||||
|
DF game raws.
|
||||||
|
|
||||||
|
DFHack scripts are written in Lua. If you don't already know Lua, there's a
|
||||||
|
great primer at `lua.org <https://www.lua.org/pil/contents.html>`__.
|
||||||
|
|
||||||
|
Why not just mod the raws?
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
It depends on what you want to do. Some mods *are* better to do in just the
|
||||||
|
raws. You don't need DFHack to add a new race or modify attributes, for example.
|
||||||
|
However, DFHack scripts can do many things that you just can't do in the raws,
|
||||||
|
like make a creature that trails smoke. Some things *could* be done in the raws,
|
||||||
|
but writing a script is less hacky, easier to maintain, easier to extend, and is
|
||||||
|
not prone to side-effects. A great example is adding a syndrome when a reaction
|
||||||
|
is performed. If done in the raws, you have to create an exploding boulder to
|
||||||
|
apply the syndrome. DFHack scripts can add the syndrome directly and with much
|
||||||
|
more flexibility. In the end, complex mods will likely require a mix of raw
|
||||||
|
modding and DFHack scripting.
|
||||||
|
|
||||||
|
A mod-maker's development environment
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
While you're writing your mod, you need a place to store your in-development
|
||||||
|
scripts that will:
|
||||||
|
|
||||||
|
- be directly runnable by DFHack
|
||||||
|
- not get lost when you upgrade DFHack
|
||||||
|
|
||||||
|
The recommended approach is to create a directory somewhere outside of your DF
|
||||||
|
installation (let's call it "/path/to/own-scripts") and do all your script
|
||||||
|
development in there.
|
||||||
|
|
||||||
|
Inside your DF installation folder, there is a file named
|
||||||
|
:file:`dfhack-config/script-paths.txt`. If you add a line like this to that
|
||||||
|
file::
|
||||||
|
|
||||||
|
+/path/to/own-scripts
|
||||||
|
|
||||||
|
Then that directory will be searched when you run DFHack commands from inside
|
||||||
|
the game. The ``+`` at the front of the path means to search that directory
|
||||||
|
first, before any other script directory (like :file:`hack/scripts` or
|
||||||
|
:file:`raw/scripts`). That way, your latest changes will always be used instead
|
||||||
|
of older copies that you may have installed in a DF directory.
|
||||||
|
|
||||||
|
For scripts with the same name, the `order of precedence <script-paths>` will
|
||||||
|
be:
|
||||||
|
|
||||||
|
1. ``own-scripts/``
|
||||||
|
2. ``data/save/*/raw/scripts/``
|
||||||
|
3. ``raw/scripts/``
|
||||||
|
4. ``hack/scripts/``
|
||||||
|
|
||||||
|
The structure of the game
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
"The game" is in the global variable `df <lua-df>`. The game's memory can be
|
||||||
|
found in ``df.global``, containing things like the list of all items, whether to
|
||||||
|
reindex pathfinding, et cetera. Also relevant to us in ``df`` are the various
|
||||||
|
types found in the game, e.g. ``df.pronoun_type`` which we will be using in this
|
||||||
|
guide. We'll explore more of the game structures below.
|
||||||
|
|
||||||
|
Your first script
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
So! It's time to write your first script. This section will walk you through how
|
||||||
|
to make a script that will get the pronoun type of the currently selected unit.
|
||||||
|
|
||||||
|
First line, we get the unit::
|
||||||
|
|
||||||
|
local unit = dfhack.gui.getSelectedUnit()
|
||||||
|
|
||||||
|
If no unit is selected, an error message will be printed (which can be silenced
|
||||||
|
by passing ``true`` to ``getSelectedUnit``) and ``unit`` will be ``nil``.
|
||||||
|
|
||||||
|
If ``unit`` is ``nil``, we don't want the script to run anymore::
|
||||||
|
|
||||||
|
if not unit then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Now, the field ``sex`` in a unit is an integer, but each integer corresponds to
|
||||||
|
a string value ("it", "she", or "he"). We get this value by indexing the
|
||||||
|
bidirectional map ``df.pronoun_type``. Indexing the other way, incidentally,
|
||||||
|
with one of the strings, will yield its corresponding number. So::
|
||||||
|
|
||||||
|
local pronounTypeString = df.pronoun_type[unit.sex]
|
||||||
|
print(pronounTypeString)
|
||||||
|
|
||||||
|
Simple. Save this as a Lua file in your own scripts directory and run it as
|
||||||
|
shown before when a unit is selected in the Dwarf Fortress UI.
|
||||||
|
|
||||||
|
Exploring DF structures
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
So how could you have known about the field and type we just used? Well, there
|
||||||
|
are two main tools for discovering the various fields in the game's data
|
||||||
|
structures. The first is the ``df-structures``
|
||||||
|
`repository <https://github.com/DFHack/df-structures>`__ that contains XML files
|
||||||
|
describing the contents of the game's structures. These are complete, but
|
||||||
|
difficult to read (for a human). The second option is the `gui/gm-editor`
|
||||||
|
script, an interactive data explorer. You can run the script while objects like
|
||||||
|
units are selected to view the data within them. You can also run
|
||||||
|
``gui/gm-editor scr`` to view the data for the current screen. Press :kbd:`?`
|
||||||
|
while the script is active to view help.
|
||||||
|
|
||||||
|
Familiarising yourself with the many structs of the game will help with ideas
|
||||||
|
immensely, and you can always ask for help in the `right places <support>`.
|
||||||
|
|
||||||
|
Detecting triggers
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The common method for injecting new behaviour into the game is to define a
|
||||||
|
callback function and get it called when something interesting happens. DFHack
|
||||||
|
provides two libraries for this, ``repeat-util`` and `eventful <eventful-api>`.
|
||||||
|
``repeat-util`` is used to run a function once per a configurable number of
|
||||||
|
frames (paused or unpaused), ticks (unpaused), in-game days, months, or years.
|
||||||
|
If you need to be aware the instant something happens, you'll need to run a
|
||||||
|
check once a tick. Be careful not to do this gratuitiously, though, since
|
||||||
|
running that often can slow down the game!
|
||||||
|
|
||||||
|
``eventful``, on the other hand, is much more performance-friendly since it will
|
||||||
|
only call your callback when a relevant event happens, like a reaction or job
|
||||||
|
being completed or a projectile moving.
|
||||||
|
|
||||||
|
To get something to run once per tick, we can call
|
||||||
|
``repeat-util.scheduleEvery()``. First, we load the module::
|
||||||
|
|
||||||
|
local repeatUtil = require('repeat-util')
|
||||||
|
|
||||||
|
Both ``repeat-util`` and ``eventful`` require keys for registered callbacks. You
|
||||||
|
should use something unique, like your mod name::
|
||||||
|
|
||||||
|
local modId = "callback-example-mod"
|
||||||
|
|
||||||
|
Then, we pass the key, amount of time units between function calls, what the
|
||||||
|
time units are, and finally the callback function itself::
|
||||||
|
|
||||||
|
repeatUtil.scheduleEvery(modId, 1, "ticks", function()
|
||||||
|
-- Do something like iterating over all active units and
|
||||||
|
-- check for something interesting
|
||||||
|
for _, unit in ipairs(df.global.world.units.active) do
|
||||||
|
...
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
``eventful`` is slightly more involved. First get the module::
|
||||||
|
|
||||||
|
local eventful = require('plugins.eventful')
|
||||||
|
|
||||||
|
``eventful`` contains a table for each event which you populate with functions.
|
||||||
|
Each function in the table is then called with the appropriate arguments when
|
||||||
|
the event occurs. So, for example, to print the position of a moving (item)
|
||||||
|
projectile::
|
||||||
|
|
||||||
|
eventful.onProjItemCheckMovement[modId] = function(projectile)
|
||||||
|
print(projectile.cur_pos.x, projectile.cur_pos.y,
|
||||||
|
projectile.cur_pos.z)
|
||||||
|
end
|
||||||
|
|
||||||
|
Check out the `full list of supported events <eventful-api>` to see what else
|
||||||
|
you can react to with ``eventful``.
|
||||||
|
|
||||||
|
Now, you may have noticed that you won't be able to register multiple callbacks
|
||||||
|
with a single key named after your mod. You can, of course, call all the
|
||||||
|
functions you want from a single registed callback. Alternately, you can create
|
||||||
|
multiple callbacks using different keys, using your mod ID as a key name prefix.
|
||||||
|
If you do register multiple callbacks, though, there are no guarantees about the
|
||||||
|
call order.
|
||||||
|
|
||||||
|
Custom raw tokens
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. highlight:: none
|
||||||
|
|
||||||
|
In this section, we are going to use `custom raw tokens <custom-raw-tokens>`
|
||||||
|
applied to a reaction to transfer the material of a reagent to a product as a
|
||||||
|
handle improvement (like on artifact buckets), and then we are going to see how
|
||||||
|
you could make boots that make units go faster when worn.
|
||||||
|
|
||||||
|
First, let's define a custom crossbow with its own custom reaction. The
|
||||||
|
crossbow::
|
||||||
|
|
||||||
|
[ITEM_WEAPON:ITEM_WEAPON_CROSSBOW_SIEGE]
|
||||||
|
[NAME:crossbow:crossbows]
|
||||||
|
[SIZE:600]
|
||||||
|
[SKILL:HAMMER]
|
||||||
|
[RANGED:CROSSBOW:BOLT]
|
||||||
|
[SHOOT_FORCE:4000]
|
||||||
|
[SHOOT_MAXVEL:800]
|
||||||
|
[TWO_HANDED:0]
|
||||||
|
[MINIMUM_SIZE:17500]
|
||||||
|
[MATERIAL_SIZE:4]
|
||||||
|
[ATTACK:BLUNT:10000:4000:bash:bashes:NO_SUB:1250]
|
||||||
|
[ATTACK_PREPARE_AND_RECOVER:3:3]
|
||||||
|
[SIEGE_CROSSBOW_MOD_FIRE_RATE_MULTIPLIER:2] custom token (you'll see)
|
||||||
|
|
||||||
|
The reaction to make it (you would add the reaction and not the weapon to an
|
||||||
|
entity raw)::
|
||||||
|
|
||||||
|
[REACTION:MAKE_SIEGE_CROSSBOW]
|
||||||
|
[NAME:make siege crossbow]
|
||||||
|
[BUILDING:BOWYER:NONE]
|
||||||
|
[SKILL:BOWYER]
|
||||||
|
[REAGENT:mechanism 1:2:TRAPPARTS:NONE:NONE:NONE]
|
||||||
|
[REAGENT:bar:150:BAR:NONE:NONE:NONE]
|
||||||
|
[METAL_ITEM_MATERIAL]
|
||||||
|
[REAGENT:handle 1:1:BLOCKS:NONE:NONE:NONE] wooden handles
|
||||||
|
[ANY_PLANT_MATERIAL]
|
||||||
|
[REAGENT:handle 2:1:BLOCKS:NONE:NONE:NONE]
|
||||||
|
[ANY_PLANT_MATERIAL]
|
||||||
|
[SIEGE_CROSSBOW_MOD_TRANSFER_HANDLE_MATERIAL_TO_PRODUCT_IMPROVEMENT:1]
|
||||||
|
another custom token
|
||||||
|
[PRODUCT:100:1:WEAPON:ITEM_WEAPON_CROSSBOW_SIEGE:GET_MATERIAL_FROM_REAGENT:bar:NONE]
|
||||||
|
|
||||||
|
So, we are going to use the ``eventful`` module to make it so that (after the
|
||||||
|
script is run) when this crossbow is crafted, it will have two handles, each
|
||||||
|
with the material given by the block reagents.
|
||||||
|
|
||||||
|
.. highlight:: lua
|
||||||
|
|
||||||
|
First, require the modules we are going to use::
|
||||||
|
|
||||||
|
local eventful = require("plugins.eventful")
|
||||||
|
local customRawTokens = require("custom-raw-tokens")
|
||||||
|
|
||||||
|
Now, let's make a callback (we'll be defining the body of this function soon)::
|
||||||
|
|
||||||
|
local modId = "siege-crossbow-mod"
|
||||||
|
eventful.onReactionComplete[modId] = function(reaction,
|
||||||
|
reactionProduct, unit, inputItems, inputReagents,
|
||||||
|
outputItems)
|
||||||
|
|
||||||
|
First, we check to see if it the reaction that just happened is relevant to this
|
||||||
|
callback::
|
||||||
|
|
||||||
|
if not customRawTokens.getToken(reaction,
|
||||||
|
"SIEGE_CROSSBOW_MOD_TRANSFER_HANDLE_MATERIAL_TO_PRODUCT_IMPROVEMENT")
|
||||||
|
then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Then, we get the product number listed. Next, for every reagent, if the reagent
|
||||||
|
name starts with "handle" then we get the corresponding item, and...
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
for i, reagent in ipairs(inputReagents) do
|
||||||
|
if reagent.code:startswith('handle') then
|
||||||
|
-- Found handle reagent
|
||||||
|
local item = inputItems[i]
|
||||||
|
|
||||||
|
...We then add a handle improvement to the listed product within our loop::
|
||||||
|
|
||||||
|
local new = df.itemimprovement_itemspecificst:new()
|
||||||
|
new.mat_type, new.mat_index = item.mat_type, item.mat_index
|
||||||
|
new.type = df.itemimprovement_specific_type.HANDLE
|
||||||
|
outputItems[productNumber - 1].improvements:insert('#', new)
|
||||||
|
|
||||||
|
This works well as long as you don't have multiple stacks filling up one
|
||||||
|
reagent.
|
||||||
|
|
||||||
|
Let's also make some code to modify the fire rate of our siege crossbow::
|
||||||
|
|
||||||
|
eventful.onProjItemCheckMovement[modId] = function(projectile)
|
||||||
|
if projectile.distance_flown > 0 then
|
||||||
|
-- don't make this adjustment more than once
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local firer = projectile.firer
|
||||||
|
if not firer then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local weapon = df.item.find(projectile.bow_id)
|
||||||
|
if not weapon then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local multiplier = tonumber(customRawTokens.getToken(
|
||||||
|
weapon.subtype,
|
||||||
|
"SIEGE_CROSSBOW_MOD_FIRE_RATE_MULTIPLIER")) or 1
|
||||||
|
firer.counters.think_counter = math.floor(
|
||||||
|
firer.counters.think_counter * multiplier)
|
||||||
|
end
|
||||||
|
|
||||||
|
.. highlight:: none
|
||||||
|
|
||||||
|
Now, let's see how we could make some "pegasus boots". First, let's define the
|
||||||
|
item in the raws::
|
||||||
|
|
||||||
|
[ITEM_SHOES:ITEM_SHOES_BOOTS_PEGASUS]
|
||||||
|
[NAME:pegasus boot:pegasus boots]
|
||||||
|
[ARMORLEVEL:1]
|
||||||
|
[UPSTEP:1]
|
||||||
|
[METAL_ARMOR_LEVELS]
|
||||||
|
[LAYER:OVER]
|
||||||
|
[COVERAGE:100]
|
||||||
|
[LAYER_SIZE:25]
|
||||||
|
[LAYER_PERMIT:15]
|
||||||
|
[MATERIAL_SIZE:2]
|
||||||
|
[METAL]
|
||||||
|
[LEATHER]
|
||||||
|
[HARD]
|
||||||
|
[PEGASUS_BOOTS_MOD_MOVEMENT_TIMER_REDUCTION_PER_TICK:5] custom raw token
|
||||||
|
(you don't have to comment the custom token every time,
|
||||||
|
but it does clarify what it is)
|
||||||
|
|
||||||
|
.. highlight:: lua
|
||||||
|
|
||||||
|
Then, let's make a ``repeat-util`` callback for once a tick::
|
||||||
|
|
||||||
|
repeatUtil.scheduleEvery(modId, 1, "ticks", function()
|
||||||
|
|
||||||
|
Let's iterate over every active unit, and for every unit, iterate over their
|
||||||
|
worn items to calculate how much we are going to take from their movement
|
||||||
|
timer::
|
||||||
|
|
||||||
|
for _, unit in ipairs(df.global.world.units.active) do
|
||||||
|
local amount = 0
|
||||||
|
for _, entry in ipairs(unit.inventory) do
|
||||||
|
if entry.mode == df.unit_inventory_item.T_mode.Worn then
|
||||||
|
local reduction = customRawTokens.getToken(
|
||||||
|
entry.item,
|
||||||
|
'PEGASUS_BOOTS_MOD_MOVEMENT_TIMER_REDUCTION_PER_TICK')
|
||||||
|
amount = amount + (tonumber(reduction) or 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Subtract amount from movement timer if currently moving
|
||||||
|
dfhack.units.addMoveTimer(-amount)
|
||||||
|
|
||||||
|
The structure of a full mod
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Create a folder for mod projects somewhere outside your Dwarf Fortress
|
||||||
|
installation directory (e.g. ``/path/to/mymods/``) and use your mod IDs as the
|
||||||
|
names for the mod folders within it. In the example below, we'll use a mod ID of
|
||||||
|
``example-mod``. I'm sure your mods will have more creative names! The
|
||||||
|
``example-mod`` mod will be developed in the ``/path/to/mymods/example-mod/``
|
||||||
|
directory and has a basic structure that looks like this::
|
||||||
|
|
||||||
|
raw/init.d/example-mod.lua
|
||||||
|
raw/objects/...
|
||||||
|
raw/scripts/example-mod.lua
|
||||||
|
raw/scripts/example-mod/...
|
||||||
|
README.md
|
||||||
|
|
||||||
|
Let's go through that line by line.
|
||||||
|
|
||||||
|
* A short (one-line) script in ``raw/init.d/`` to initialise your
|
||||||
|
mod when a save is loaded.
|
||||||
|
* Modifications to the game raws (potentially with custom raw tokens) go in
|
||||||
|
``raw/objects/``.
|
||||||
|
* 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 and/or modules 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 will be your mod help text when people browse to your online
|
||||||
|
repository.
|
||||||
|
|
||||||
|
Unless you want to install your ``raw/`` folder into your DF game folder every
|
||||||
|
time you make a change to your scripts, you should add your development scripts
|
||||||
|
directory to your 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.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')
|
||||||
|
|
||||||
|
-- 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')
|
||||||
|
|
||||||
|
enabled = enabled or false
|
||||||
|
local modId = 'example-mod'
|
||||||
|
|
||||||
|
if not dfhack_flags.enable then
|
||||||
|
print(usage)
|
||||||
|
print()
|
||||||
|
print(('Example mod is currently '):format(
|
||||||
|
enabled and 'enabled' or 'disabled'))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if dfhack_flags.enable_state then
|
||||||
|
-- do any initialization your internal scripts might require
|
||||||
|
moduleA.onLoad()
|
||||||
|
moduleB.onLoad()
|
||||||
|
|
||||||
|
-- register your callbacks
|
||||||
|
repeatUtil.scheduleEvery(modId .. ' every tick', 1, 'ticks',
|
||||||
|
moduleA.every1Tick)
|
||||||
|
repeatUtil.scheduleEvery(modId .. ' 100 frames', 1, 'frames',
|
||||||
|
moduleD.every100Frames)
|
||||||
|
|
||||||
|
-- multiple functions in the same callback
|
||||||
|
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
|
||||||
|
|
||||||
|
-- one function per callback (you can put them in the
|
||||||
|
-- above format if you prefer)
|
||||||
|
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 .. ' every ticks')
|
||||||
|
repeatUtil.cancel(modId .. ' 100 frames')
|
||||||
|
|
||||||
|
eventful.onReactionComplete[modId] = nil
|
||||||
|
eventful.onProjItemCheckMovement[modId] = nil
|
||||||
|
eventful.onProjUnitCheckMovement[modId] = nil
|
||||||
|
|
||||||
|
print('Example mod disabled')
|
||||||
|
enabled = false
|
||||||
|
end
|
||||||
|
|
||||||
|
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('enable example-mod')
|
||||||
|
|
||||||
|
Inside ``raw/scripts/example-mod/module-a.lua`` you could have code like this::
|
||||||
|
|
||||||
|
--@ module = true
|
||||||
|
-- The above line is required for reqscript to work
|
||||||
|
|
||||||
|
function onLoad() -- global variables are exported
|
||||||
|
-- do initialization here
|
||||||
|
end
|
||||||
|
|
||||||
|
-- this is an internal function: local functions/variables
|
||||||
|
-- are not exported
|
||||||
|
local function usedByOnTick(unit)
|
||||||
|
-- ...
|
||||||
|
end
|
||||||
|
|
||||||
|
function onTick() -- exported
|
||||||
|
for _,unit in ipairs(df.global.world.units.all) do
|
||||||
|
usedByOnTick(unit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
The `reqscript <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!
|
@ -0,0 +1,20 @@
|
|||||||
|
overlay
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. dfhack-tool::
|
||||||
|
:summary: Provide an on-screen clickable DFHack launcher button.
|
||||||
|
:tags: dfhack interface
|
||||||
|
|
||||||
|
This tool places a small button in the lower left corner of the screen that you
|
||||||
|
can click to run DFHack commands with `gui/launcher`.
|
||||||
|
|
||||||
|
If you would rather always run `gui/launcher` with the hotkeys, or just don't
|
||||||
|
want the DFHack button on-screen, just disable the plugin with
|
||||||
|
``disable overlay``.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
enable overlay
|
@ -1 +1 @@
|
|||||||
Subproject commit dc118c5e90aea6181a290e4bbf40e7f2974fb053
|
Subproject commit f5fab13fb652dd953e9d59e8deca032b26131208
|
@ -0,0 +1,338 @@
|
|||||||
|
#include "df/viewscreen_adopt_regionst.h"
|
||||||
|
#include "df/viewscreen_adventure_logst.h"
|
||||||
|
#include "df/viewscreen_announcelistst.h"
|
||||||
|
#include "df/viewscreen_assign_display_itemst.h"
|
||||||
|
#include "df/viewscreen_barterst.h"
|
||||||
|
#include "df/viewscreen_buildinglistst.h"
|
||||||
|
#include "df/viewscreen_buildingst.h"
|
||||||
|
#include "df/viewscreen_choose_start_sitest.h"
|
||||||
|
#include "df/viewscreen_civlistst.h"
|
||||||
|
#include "df/viewscreen_counterintelligencest.h"
|
||||||
|
#include "df/viewscreen_createquotast.h"
|
||||||
|
#include "df/viewscreen_customize_unitst.h"
|
||||||
|
#include "df/viewscreen_dungeonmodest.h"
|
||||||
|
#include "df/viewscreen_dungeon_monsterstatusst.h"
|
||||||
|
#include "df/viewscreen_dungeon_wrestlest.h"
|
||||||
|
#include "df/viewscreen_dwarfmodest.h"
|
||||||
|
#include "df/viewscreen_entityst.h"
|
||||||
|
#include "df/viewscreen_export_graphical_mapst.h"
|
||||||
|
#include "df/viewscreen_export_regionst.h"
|
||||||
|
#include "df/viewscreen_game_cleanerst.h"
|
||||||
|
#include "df/viewscreen_image_creator_mode.h"
|
||||||
|
#include "df/viewscreen_image_creatorst.h"
|
||||||
|
#include "df/viewscreen_itemst.h"
|
||||||
|
#include "df/viewscreen_joblistst.h"
|
||||||
|
#include "df/viewscreen_jobmanagementst.h"
|
||||||
|
#include "df/viewscreen_jobst.h"
|
||||||
|
#include "df/viewscreen_justicest.h"
|
||||||
|
#include "df/viewscreen_kitchenpref_page.h"
|
||||||
|
#include "df/viewscreen_kitchenprefst.h"
|
||||||
|
#include "df/viewscreen_layer_arena_creaturest.h"
|
||||||
|
#include "df/viewscreen_layer_assigntradest.h"
|
||||||
|
#include "df/viewscreen_layer_choose_language_namest.h"
|
||||||
|
#include "df/viewscreen_layer_currencyst.h"
|
||||||
|
#include "df/viewscreen_layer_export_play_mapst.h"
|
||||||
|
#include "df/viewscreen_layer.h"
|
||||||
|
#include "df/viewscreen_layer_militaryst.h"
|
||||||
|
#include "df/viewscreen_layer_musicsoundst.h"
|
||||||
|
#include "df/viewscreen_layer_noblelistst.h"
|
||||||
|
#include "df/viewscreen_layer_overall_healthst.h"
|
||||||
|
#include "df/viewscreen_layer_reactionst.h"
|
||||||
|
#include "df/viewscreen_layer_squad_schedulest.h"
|
||||||
|
#include "df/viewscreen_layer_stockpilest.h"
|
||||||
|
#include "df/viewscreen_layer_stone_restrictionst.h"
|
||||||
|
#include "df/viewscreen_layer_unit_actionst.h"
|
||||||
|
#include "df/viewscreen_layer_unit_healthst.h"
|
||||||
|
#include "df/viewscreen_layer_unit_relationshipst.h"
|
||||||
|
#include "df/viewscreen_layer_world_gen_param_presetst.h"
|
||||||
|
#include "df/viewscreen_layer_world_gen_paramst.h"
|
||||||
|
#include "df/viewscreen_legendsst.h"
|
||||||
|
#include "df/viewscreen_loadgamest.h"
|
||||||
|
#include "df/viewscreen_locationsst.h"
|
||||||
|
#include "df/viewscreen_meetingst.h"
|
||||||
|
#include "df/viewscreen_movieplayerst.h"
|
||||||
|
#include "df/viewscreen_noblest.h"
|
||||||
|
#include "df/viewscreen_optionst.h"
|
||||||
|
#include "df/viewscreen_overallstatusst.h"
|
||||||
|
#include "df/viewscreen_petitionsst.h"
|
||||||
|
#include "df/viewscreen_petst.h"
|
||||||
|
#include "df/viewscreen_pricest.h"
|
||||||
|
#include "df/viewscreen_reportlistst.h"
|
||||||
|
#include "df/viewscreen_requestagreementst.h"
|
||||||
|
#include "df/viewscreen_savegamest.h"
|
||||||
|
#include "df/viewscreen_selectitemst.h"
|
||||||
|
#include "df/viewscreen_setupadventurest.h"
|
||||||
|
#include "df/viewscreen_setupdwarfgamest.h"
|
||||||
|
#include "df/viewscreen_storesst.h"
|
||||||
|
#include "df/viewscreen_textviewerst.h"
|
||||||
|
#include "df/viewscreen_titlest.h"
|
||||||
|
#include "df/viewscreen_topicmeeting_fill_land_holder_positionsst.h"
|
||||||
|
#include "df/viewscreen_topicmeetingst.h"
|
||||||
|
#include "df/viewscreen_topicmeeting_takerequestsst.h"
|
||||||
|
#include "df/viewscreen_tradeagreementst.h"
|
||||||
|
#include "df/viewscreen_tradegoodsst.h"
|
||||||
|
#include "df/viewscreen_tradelistst.h"
|
||||||
|
#include "df/viewscreen_treasurelistst.h"
|
||||||
|
#include "df/viewscreen_unitlist_page.h"
|
||||||
|
#include "df/viewscreen_unitlistst.h"
|
||||||
|
#include "df/viewscreen_unitst.h"
|
||||||
|
#include "df/viewscreen_update_regionst.h"
|
||||||
|
#include "df/viewscreen_wagesst.h"
|
||||||
|
#include "df/viewscreen_workquota_conditionst.h"
|
||||||
|
#include "df/viewscreen_workquota_detailsst.h"
|
||||||
|
#include "df/viewscreen_workshop_profilest.h"
|
||||||
|
|
||||||
|
#include "Debug.h"
|
||||||
|
#include "PluginManager.h"
|
||||||
|
#include "VTableInterpose.h"
|
||||||
|
#include "uicommon.h"
|
||||||
|
|
||||||
|
#include "modules/Screen.h"
|
||||||
|
|
||||||
|
using namespace DFHack;
|
||||||
|
|
||||||
|
DFHACK_PLUGIN("overlay");
|
||||||
|
DFHACK_PLUGIN_IS_ENABLED(is_enabled);
|
||||||
|
REQUIRE_GLOBAL(enabler);
|
||||||
|
REQUIRE_GLOBAL(gps);
|
||||||
|
|
||||||
|
namespace DFHack {
|
||||||
|
DBG_DECLARE(overlay, log, DebugCategory::LINFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string button_text = "[ DFHack Launcher ]";
|
||||||
|
static bool clicked = false;
|
||||||
|
|
||||||
|
static bool handle_click() {
|
||||||
|
if (!enabler->mouse_lbut_down || clicked) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
df::coord2d pos = Screen::getMousePos();
|
||||||
|
DEBUG(log).print("clicked at screen coordinates (%d, %d)\n", pos.x, pos.y);
|
||||||
|
if (pos.y == gps->dimy - 1 && pos.x >= 1 && (size_t)pos.x <= button_text.size()) {
|
||||||
|
clicked = true;
|
||||||
|
Core::getInstance().setHotkeyCmd("gui/launcher");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_widgets() {
|
||||||
|
int x = 1;
|
||||||
|
int y = gps->dimy - 1;
|
||||||
|
OutputString(COLOR_GREEN, x, y, button_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct viewscreen_overlay : T {
|
||||||
|
typedef T interpose_base;
|
||||||
|
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(void, feed, (set<df::interface_key> *input)) {
|
||||||
|
if (!handle_click())
|
||||||
|
INTERPOSE_NEXT(feed)(input);
|
||||||
|
}
|
||||||
|
DEFINE_VMETHOD_INTERPOSE(void, render, ()) {
|
||||||
|
INTERPOSE_NEXT(render)();
|
||||||
|
draw_widgets();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IMPLEMENT_HOOKS(screen) \
|
||||||
|
typedef viewscreen_overlay<df::viewscreen_##screen##st> screen##_overlay; \
|
||||||
|
template<> IMPLEMENT_VMETHOD_INTERPOSE(screen##_overlay, feed); \
|
||||||
|
template<> IMPLEMENT_VMETHOD_INTERPOSE(screen##_overlay, render);
|
||||||
|
|
||||||
|
IMPLEMENT_HOOKS(adopt_region)
|
||||||
|
IMPLEMENT_HOOKS(adventure_log)
|
||||||
|
IMPLEMENT_HOOKS(announcelist)
|
||||||
|
IMPLEMENT_HOOKS(assign_display_item)
|
||||||
|
IMPLEMENT_HOOKS(barter)
|
||||||
|
IMPLEMENT_HOOKS(buildinglist)
|
||||||
|
IMPLEMENT_HOOKS(building)
|
||||||
|
IMPLEMENT_HOOKS(choose_start_site)
|
||||||
|
IMPLEMENT_HOOKS(civlist)
|
||||||
|
IMPLEMENT_HOOKS(counterintelligence)
|
||||||
|
IMPLEMENT_HOOKS(createquota)
|
||||||
|
IMPLEMENT_HOOKS(customize_unit)
|
||||||
|
IMPLEMENT_HOOKS(dungeonmode)
|
||||||
|
IMPLEMENT_HOOKS(dungeon_monsterstatus)
|
||||||
|
IMPLEMENT_HOOKS(dungeon_wrestle)
|
||||||
|
IMPLEMENT_HOOKS(dwarfmode)
|
||||||
|
IMPLEMENT_HOOKS(entity)
|
||||||
|
IMPLEMENT_HOOKS(export_graphical_map)
|
||||||
|
IMPLEMENT_HOOKS(export_region)
|
||||||
|
IMPLEMENT_HOOKS(game_cleaner)
|
||||||
|
IMPLEMENT_HOOKS(image_creator)
|
||||||
|
IMPLEMENT_HOOKS(item)
|
||||||
|
IMPLEMENT_HOOKS(joblist)
|
||||||
|
IMPLEMENT_HOOKS(jobmanagement)
|
||||||
|
IMPLEMENT_HOOKS(job)
|
||||||
|
IMPLEMENT_HOOKS(justice)
|
||||||
|
IMPLEMENT_HOOKS(kitchenpref)
|
||||||
|
IMPLEMENT_HOOKS(layer_arena_creature)
|
||||||
|
IMPLEMENT_HOOKS(layer_assigntrade)
|
||||||
|
IMPLEMENT_HOOKS(layer_choose_language_name)
|
||||||
|
IMPLEMENT_HOOKS(layer_currency)
|
||||||
|
IMPLEMENT_HOOKS(layer_export_play_map)
|
||||||
|
IMPLEMENT_HOOKS(layer_military)
|
||||||
|
IMPLEMENT_HOOKS(layer_musicsound)
|
||||||
|
IMPLEMENT_HOOKS(layer_noblelist)
|
||||||
|
IMPLEMENT_HOOKS(layer_overall_health)
|
||||||
|
IMPLEMENT_HOOKS(layer_reaction)
|
||||||
|
IMPLEMENT_HOOKS(layer_squad_schedule)
|
||||||
|
IMPLEMENT_HOOKS(layer_stockpile)
|
||||||
|
IMPLEMENT_HOOKS(layer_stone_restriction)
|
||||||
|
IMPLEMENT_HOOKS(layer_unit_action)
|
||||||
|
IMPLEMENT_HOOKS(layer_unit_health)
|
||||||
|
IMPLEMENT_HOOKS(layer_unit_relationship)
|
||||||
|
IMPLEMENT_HOOKS(layer_world_gen_param_preset)
|
||||||
|
IMPLEMENT_HOOKS(layer_world_gen_param)
|
||||||
|
IMPLEMENT_HOOKS(legends)
|
||||||
|
IMPLEMENT_HOOKS(loadgame)
|
||||||
|
IMPLEMENT_HOOKS(locations)
|
||||||
|
IMPLEMENT_HOOKS(meeting)
|
||||||
|
IMPLEMENT_HOOKS(movieplayer)
|
||||||
|
IMPLEMENT_HOOKS(noble)
|
||||||
|
IMPLEMENT_HOOKS(option)
|
||||||
|
IMPLEMENT_HOOKS(overallstatus)
|
||||||
|
IMPLEMENT_HOOKS(petitions)
|
||||||
|
IMPLEMENT_HOOKS(pet)
|
||||||
|
IMPLEMENT_HOOKS(price)
|
||||||
|
IMPLEMENT_HOOKS(reportlist)
|
||||||
|
IMPLEMENT_HOOKS(requestagreement)
|
||||||
|
IMPLEMENT_HOOKS(savegame)
|
||||||
|
IMPLEMENT_HOOKS(selectitem)
|
||||||
|
IMPLEMENT_HOOKS(setupadventure)
|
||||||
|
IMPLEMENT_HOOKS(setupdwarfgame)
|
||||||
|
IMPLEMENT_HOOKS(stores)
|
||||||
|
IMPLEMENT_HOOKS(textviewer)
|
||||||
|
IMPLEMENT_HOOKS(title)
|
||||||
|
IMPLEMENT_HOOKS(topicmeeting_fill_land_holder_positions)
|
||||||
|
IMPLEMENT_HOOKS(topicmeeting)
|
||||||
|
IMPLEMENT_HOOKS(topicmeeting_takerequests)
|
||||||
|
IMPLEMENT_HOOKS(tradeagreement)
|
||||||
|
IMPLEMENT_HOOKS(tradegoods)
|
||||||
|
IMPLEMENT_HOOKS(tradelist)
|
||||||
|
IMPLEMENT_HOOKS(treasurelist)
|
||||||
|
IMPLEMENT_HOOKS(unitlist)
|
||||||
|
IMPLEMENT_HOOKS(unit)
|
||||||
|
IMPLEMENT_HOOKS(update_region)
|
||||||
|
IMPLEMENT_HOOKS(wages)
|
||||||
|
IMPLEMENT_HOOKS(workquota_condition)
|
||||||
|
IMPLEMENT_HOOKS(workquota_details)
|
||||||
|
IMPLEMENT_HOOKS(workshop_profile)
|
||||||
|
|
||||||
|
#undef IMPLEMENT_HOOKS
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_onstatechange(color_ostream &,
|
||||||
|
state_change_event evt) {
|
||||||
|
if (evt == SC_VIEWSCREEN_CHANGED) {
|
||||||
|
clicked = false;
|
||||||
|
}
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INTERPOSE_HOOKS_FAILED(screen) \
|
||||||
|
!INTERPOSE_HOOK(screen##_overlay, feed).apply(enable) || \
|
||||||
|
!INTERPOSE_HOOK(screen##_overlay, render).apply(enable)
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_enable(color_ostream &, bool enable) {
|
||||||
|
if (is_enabled == enable)
|
||||||
|
return CR_OK;
|
||||||
|
|
||||||
|
if (enable != is_enabled) {
|
||||||
|
if (INTERPOSE_HOOKS_FAILED(adopt_region) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(adventure_log) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(announcelist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(assign_display_item) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(barter) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(buildinglist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(building) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(choose_start_site) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(civlist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(counterintelligence) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(createquota) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(customize_unit) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(dungeonmode) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(dungeon_monsterstatus) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(dungeon_wrestle) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(dwarfmode) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(entity) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(export_graphical_map) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(export_region) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(game_cleaner) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(image_creator) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(item) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(joblist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(jobmanagement) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(job) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(justice) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(kitchenpref) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_arena_creature) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_assigntrade) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_choose_language_name) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_currency) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_export_play_map) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_military) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_musicsound) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_noblelist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_overall_health) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_reaction) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_squad_schedule) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_stockpile) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_stone_restriction) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_unit_action) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_unit_health) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_unit_relationship) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_world_gen_param_preset) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(layer_world_gen_param) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(legends) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(loadgame) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(locations) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(meeting) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(movieplayer) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(noble) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(option) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(overallstatus) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(petitions) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(pet) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(price) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(reportlist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(requestagreement) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(savegame) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(selectitem) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(setupadventure) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(setupdwarfgame) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(stores) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(textviewer) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(title) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(topicmeeting_fill_land_holder_positions) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(topicmeeting) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(topicmeeting_takerequests) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(tradeagreement) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(tradegoods) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(tradelist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(treasurelist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(unitlist) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(unit) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(update_region) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(wages) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(workquota_condition) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(workquota_details) ||
|
||||||
|
INTERPOSE_HOOKS_FAILED(workshop_profile))
|
||||||
|
return CR_FAILURE;
|
||||||
|
|
||||||
|
is_enabled = enable;
|
||||||
|
}
|
||||||
|
return CR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef INTERPOSE_HOOKS_FAILED
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &) {
|
||||||
|
return plugin_enable(out, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
DFhackCExport command_result plugin_shutdown(color_ostream &out) {
|
||||||
|
return plugin_enable(out, false);
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
Subproject commit 7bae77756f974aad765251096516a89d9d035977
|
Subproject commit 484988bb3d64f7aed9442af3b226faa77d8fd00d
|
Loading…
Reference in New Issue