- v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
- Use this page to report any issues related to the migration.
Lua scripting
![]() |
Research Pending! This article or section is incomplete/under construction (likely due to recent changes) and may still be outdated or missing details. Feel free to do some testing and expand it. |
Modding |
---|
Tokens |
Audio · Biome · Graphics · Tile page · Interaction · Mod info · Plant · Speech · Sphere · Syndrome · World |
Body tokens |
Body · Body detail plan · Bodygloss · Tissue |
Creature tokens |
Creature · Creature mannerism · Personality facet · Creature variation · Procedural graphics layer |
Descriptor tokens |
Descriptor color · Color · Descriptor pattern · Descriptor shape |
Entity tokens |
Entity · Ethic · Language · Value · Position |
Job tokens |
Building · Labor · Reaction · Skill · Unit type |
Item tokens |
Item type · Item definition · Ammo · Armor · Instrument · Tool · Trap component · Weapon |
Material tokens |
Material type · Material definition · Inorganic material definition |
Lua |
Scripting · Examples · Functions |
This article is about procedural raw generation. Information on Utility:DFHack scripting can be found at https://docs.dfhack.org/en/stable/.
Lua scripting is an experimental featurev51.06. It is used to create custom procedurally-generated objects that were previously created by hardcoded methods. It was announced in a video, with the stated goal of "supporting future magical endeavors."
Inorganic materials, languages, creatures, interactions, items (currently excluding instruments), reactions, entities, and plants are open to this system.
Scripts are loaded from a mod's scripts/init.lua
file, and can require()
other files.
Structure[edit]
As of right now, Lua scripting is confined to generation of procedural objects. This is done by running the generate
function, a global function loaded in data/init/generators.lua
. It runs unit tests, preprocess, do_once, materials, items, languages, creatures, interactions, entities and postprocessing, in that order.
When random objects are first generated, the game populates two global tables, world
and random_object_parameters
. world
contains info about the world currently being generated (or, in the future, played in), while random_object_parameters
contains what the game expects to be generated. The most important thing between these is random_object_parameters.main_world_randoms
, which is true
for exactly one generation at the start of worldgen; it's what you want to check for if you're generating your own objects outside of do_once
and the standard generation contexts.
Debugging[edit]
You can set the global debug_level
variable to print some debug info. It's a number, but what numbers are there are completely arbitrary. If it's >0, it'll run unit tests; if it's >=0.5, it'll display what step of generation it's at, at every step. You can use get_debug_logger(x)
to return a function that logs to lualog.txt
if the debug level is at least x
.
Unit tests are functions that return a table, containing good
, which, if truthy, is considered passed, and info
, which is a string that contains information on said pass or fail. These unit tests should have no side effects, i.e. they shouldn't muck with global state any. Here's an example unit test, one that was used during development (but had no reason to be removed):
get_random_creature |
---|
get_random_creature=function()
local cr=world.creature.get_random_creature()
local res={}
res.good=type(cr)=='table'
res.info=res.good and ("Got a random creature: "..cr.token) or "No random creature could be gotten, even at most permissive!"
return res
end
|
Object generation[edit]
The first steps after debugging are preprocess
and do_once
. These are tables of functions, running each one at a time. This is where you want your side effects.
If you're registering an entirely new procedural object type, you can use these steps. do_once
only runs in the "main world randoms" generation call and is the safest option for adding new objects. The "adamantine alloys" example is done through these steps. You can also add functions to postprocess
, which is like preprocessing but run after all the other generation steps.
You can also mess around with random_object_parameters
in preprocessing. Vanilla demon types are assigned here, and you can change the proportions as an end user if you want.
Generation from list[edit]
The game then generates all of the individual objects; the general procedure for this is that the game calls the generate_from_list()
function on a table of functions, which calls every function and picks one of the resulting values at random depending on their weights.
For example, the interactions.secrets
table contains one entry, that for necromancers; it returns a table containing three entries: {interaction=tbl,weight=1,spheres=spheres}
.
interaction
is the full raw text of the interactionweight
is the random weight for the interaction, i.e. if you add another function which returns a table containingweight=2
, that will be twice as likely as necromancers.spheres
is some extra data the generator might be able to use. It actually doesn't, at this point, but one could overridegenerate_random_interactions()
with their own version that takes into accountspheres
and, say, tries to evenly distribute generated secrets over available spheres. (This didn't end up in vanilla primarily out of concerns of bug-like behavior cropping up).
Languages[edit]
Languages are special, though; as can be seen in the Divine language/script or identity language. The languages
table just expects to return table containing translations, e.g. tbl["ABBEY"]="abbey"
. If you want to procedurally add words or symbols (and yes, these are both doable), you can do so with raws.register_languages()
in another function table.
Creatures[edit]
Creatures have a lot more to them than other procedural objects. Forgotten beasts are, in a sense, the simplest of them:
creatures.fb.default |
---|
creatures.fb.default=function(layer_type,tok)
local tbl={}
local options={
strong_attack_tweak=true,
spheres={CAVERNS=true},
is_evil=true,
sickness_name="beast sickness",
token=tok
}
tbl=split_to_lines(tbl,[[
[FEATURE_BEAST]
[ATTACK_TRIGGER:0:0:2]
[NAME:forgotten beast:forgotten beasts:forgotten beast]
[CASTE_NAME:forgotten beast:forgotten beasts:forgotten beast]
[NO_GENDER]
[CARNIVORE]
[DIFFICULTY:10]
[NATURAL_SKILL:WRESTLING:6]
[NATURAL_SKILL:BITE:6]
[NATURAL_SKILL:GRASP_STRIKE:6]
[NATURAL_SKILL:STANCE_STRIKE:6]
[NATURAL_SKILL:MELEE_COMBAT:6]
[NATURAL_SKILL:DODGING:6]
[NATURAL_SKILL:SITUATIONAL_AWARENESS:6]
[LARGE_PREDATOR]
]])
add_regular_tokens(tbl,options)
tbl[#tbl+1]=layer_type==0 and "[BIOME:SUBTERRANEAN_WATER]" or "[BIOME:SUBTERRANEAN_CHASM]"
if layer_type==0 then options.spheres.WATER=true end
options.spheres[pick_random(evil_spheres)]=true
options.do_water=layer_type==0
populate_sphere_info(tbl,options)
local rcp=get_random_creature_profile(options)
add_body_size(tbl,math.max(10000000,rcp.min_size),options)
tbl[#tbl+1]="[CREATURE_TILE:"..tile_string(rcp.tile).."]"
build_procgen_creature(rcp,tbl,options)
return {creature=tbl,weight=1}
end
|
This is a lot of info! First, you build an options
table; it's possible to make a full list of options used in vanilla, but other mods can also use arbitrary options. It then adds all the usual special-to-forgotten-beast tokens, in a big string, followed by calling add_regular_tokens(tbl,options)
, which adds some stuff common to all (vanilla) procedural creatures, based on the options given.
It sets do_water
and the WATER sphere if the FB is in a water cavern, an option which whitelists certain random creature profiles, as well as adding a random evil sphere.
populate_sphere_info()
is similar to add_regular_tokens()
; it adds all of the spheres in options.spheres
to the creature, using the [SPHERE]
token, then, if certain options are set, does more.
Then, it gets a random creature profile using get_random_creature_profile()
and the options, uses add_body_size()
to set the BODY_SIZE tokens and attendant things that come with it, sets the creature tile, and finally runs the Big Function, build_procgen_creature()
, which creates the description, body, tissues, et cetera.
Random Creature Profiles[edit]
A random creature profile is a type of "thing" a generated creature can be. For example:
random_creature_types.GENERAL_QUADRUPED |
---|
GENERAL_QUADRUPED={
name_string="quadruped",
tile='Q',
body_base="QUADRUPED",
c_class="UNIFORM",
cannot_have_get_more_legs=true,
min_size=70000,
weight=1000
},
|
Of these, only cannot_have_get_more_legs
is optional. build_procgen_creature()
has direct access to the RCP, as the first argument, and thus extra table entries can be used however you like.
Logic insertion[edit]
While SELECT_CREATURE
cannot target generated objects, btc1_tweaks
is a global table of functions. build_procgen_creature()
calls every function in it before determining the first tweak. The arguments supplied are lines,options,add_to_body,add_to_body_unique,add_tweak_candidate
, where lines
and options
are the creature's raw lines and options, and the latter three pass local functions that add the chosen body part to the creature's definition or allow a certain tweak to be chosen.
Other stuff[edit]
TODO: Tweaks, random creature materials, random creature classes, color pickers, function that build_procgen_creature()
calls in the process of building that can be used to inject your own logic into creature building (e.g. btc1_tweaks
), etc.