Events and You!
Today I will be talking about the Event System
Have you ever thought to yourself, "There aren't enough random events that occur while I play, I wish I could get a double mega-beast attack, or meteors could fall from the sky"? If so, then this is for you!
The Event System allows you to program customizable events to randomly occur while playing. Anything that is do-able with DFHack scripts is able to be triggered by this systems. Events are triggered randomly depending on specified requirements and checked at various intervals.
This means that you can have a lot of customization in your game! So let's start by going over what you will need.
- Dwarf Fortress
- DFHack
- My Script Collection
The files in my script collection that are related to the Civilization System:
- hack/lua/events/requirement-check.lua
- hack/lua/events/findunit.lua
- hack/lua/events/finditem.lua
- hack/lua/events/findlocation.lua
- hack/lua/events/findbuilding.lua
- hack/scripts/events/trigger.lua
- hack/scripts/base/events.lua
- raw/objects/event.txt
So now that we know what it does, and we know what we need. How do we get started? Well for virtually everything you want to do, the only file you will need to modify is the event.txt file.
So let's take a look at event.txt
[EVENT:SAMPLE_EVENT]
[NAME:this is a sample event]
[CHECK:MONTHLY]
[CHANCE:10]
[DELAY:RANDOM:12000]
[REQUIREMENT:BUILDING:SAMPLE_WORKSHOP:1]
[REQUIREMENT:COUNTER:SAMPLE_COUNTER:10]
[REQUIREMENT:TIME:10000]
[REQUIREMENT:POPULATION:50]
[REQUIREMENT:WEALTH:TOTAL:10000]
[REQUIREMENT:CLASS:SAMPLE_CLASS:3]
[REQUIREMENT:SKILL:MINER:15]
[REQUIREMENT:KILLS:GOBLIN:10]
[REQUIREMENT:DEATHS:ALL:50]
[REQUIREMENT:TRADES:PLAINS:5]
[REQUIREMENT:SIEGES:EVIL:5]
[EFFECT:1]
[EFFECT_NAME:first sample effect of the event]
[EFFECT_CHANCE:100]
[EFFECT_DELAY:STATIC:100]
[EFFECT_CONTINGENT:0]
[EFFECT_ARGUMENT:1]
*EFFECT_REQUIREMENT:* <- same as for just normal REQUIREMENT
[ARGUMENT_WEIGHTING:100,100,100,10,10,10,1]
[ARGUMENT_VARIABLE:HUMAN_MERCHANT,ELF_MERCHANT,DWARF_MERCHANT,TRAVELING_MERCHANT.GOBLIN_MERCHANT,KOBOLD_MERCHANT,EXOTIC_MERCHANT]
[EFFECT_SCRIPT:"building/change -from EMPTY_MERCHANTS_STALL -to !ARG_1 -dur 25200"]
That includes all of the currently supported tokens for each event. Note that the X in [EVENT:X] must be the unique. Now let's talk about what each one does.
Starting with the event declaration itself;
Event TokensBase TokensThese tokens are mandatory for each event, and should only occur once. If they are repeated, they will be overwritten.
- [NAME] - what the event is called, not currently used for anything, but will be included later
- [CHECK] - how often to check if they event should be triggered. Valid entries inlude
- DAILY
- WEEKLY
- MONTHLY
- SEASON
- YEARLY
- [CHANCE] - the percentage chance that the event will be triggered
- [DELAY] - the amount of time, in in-game ticks, after the check that the event should be triggered. Can be either a STATIC delay or a RANDOM delay.
Requirement TokensThese tokens specify what requirements must be met in order for the event to be triggered. They can be combined in any number of ways.
- COUNTER - for use with the counters script
- TIME - checks the age of the fortress, in in-game ticks
- POPULATION - checks the population of the fortress
- WEALTH - checks the wealth of the fortress, Valid secondary tokens include;
- TOTAL - checks total wealth
- IMPORTED - checks imported wealth
- EXPORTED - checks exported wealth
- WEAPONS - checks cumulative value of all weapons
- ARMOR - checks cumulative value of all armor
- FURNITURE - checks cumulative value of all furniture
- DISPLAYED - checks cumulative value of all displayed items
- HELD - checks cumulative value of all held items
- ARCHITECTURE - checks cumulative wealth of all architecture
- OTHER - no idea, everything else I guess?
- BUILDING - checks for a number of specified custom workshops and furnaces
- SKILL - checks for any units that have a specified skill at a specified level
- CLASS - for use with the Class System, checks for a number of units with the specified class
- KILLS - checks for a specific amount of kills
- DEATHS - checks for a specific amount of deaths
- TRADES - checks for a specific number of trades
- SIEGES - checks for a specific number of sieges
With the event tokens specified we can move on to the effect tokens, which is where the magic actually happens.
Effect TokensBase TokensThe same as the base event tokens, these tokens must occur once for each effect.
- [EFFECT_NAME] - name of the effect, not currently used
- [EFFECT_CHANCE] - chance the effect will be triggered after the event has already been triggered
- [EFFECT_DELAY] - delay of effect triggering, cumulative with event [DELAY]
Requirement TokensThe exact same as the event requirement tokens, just change [REQUIREMENT] to [EFFECT_REQUIREMENT], with one addition.
[EFFECT_CONTINGENT] specifies that a previous effect must have triggered in order for the current effect to trigger.
Script TokensThe effects are where the scripts are actually run, and can contain as many scripts as you would like. The scripts are specified just as they would be on the command line with a few differences for special inputs. The following special inputs are used to modify the scripts.
- !UNIT - for use with [EFFECT_UNIT]
- !LOCATION - for use with [EFFECT_LOCATION]
- !BUILDING - for use with [EFFECT_BUILDING]
- !ITEM - for use with [EFFECT_ITEM]
- !ARG_X - for use with [EFFECT_ARGUMENT:X]
[EFFECT_UNIT]This is how the effect identifies a unit to send to the script being run. Valid arguments include
- RANDOM - picks any active unit
- RANDOM:CIVILIZATION - picks any active unit that is a member of your forts civilzation
- RANDOM:POPULATION - picks any active unit that is a member of your fort
- RANDOM:INVADER - picks any unit that is currently invading your fort
- RANDOM:MALE - picks any male on the map
- RANDOM:FEMALE - picks any female on the map
- RANDOM:PROFESSION:PROFESSION_NAME - picks any unit of the given profession
- RANDOM:CLASS:CLASS_NAME - for use with the Class System, picks any unit of the given class
- RANDOM:SKILL:SKILL_NAME:VALUE - picks any unit with the specified skill/value combination
- RANDOM:CREATURE:CASTE - picks any unit of the specified creature/caste combination
[EFFECT_LOCATION]Use this when you need to pass a location to a script
- RANDOM - picks any location on the map
- RANDOM:SURFACE - picks any location on the surface
- RANDOM:SURFACE:EDGE - picks any location on the surface on an edge
- RANDOM:SURFACE:CENTER:X - picks any location on the surface within X tiles of the x,y center
- RANDOM:UNDERGROUND - picks any location underground (this includes inside stone walls and the like)
- RANDOM:UNDERGROUND:CAVERN:X - picks any open location in a specified cavern level
- RANDOM:SKY - picks any location above the surface
- RANDOM:SKY:EDGE - picks any location above the surface on an edge
- RANDOM:SKY:CENTER:X - picks any location above the surface within X tiles of the x,y center
[EFFECT_ITEM]When you need an item ID for a script you can use this. Valid arguments include
- RANDOM - picks any item on the map at random
- RANDOM:WEAPON - picks any weapon on the map, can include an optional :SUBTYPE
- RANDOM:ARMOR - picks any armor on the map, can include an optional :SUBTYPE
- RANDOM:HELM - picks any helm on the map, can include an optional :SUBTYPE
- RANDOM:PANTS - picks any pants on the map, can include an optional :SUBTYPE
- RANDOM:GLOVE - picks any glove on the map, can include an optional :SUBTYPE
- RANDOM:SHOE - picks any shoe on the map, can include an optional :SUBTYPE
- RANDOM:SHIELD - picks any shield on the map,, can include an optional :SUBTYPE
- RANDOM:AMMO - picks any piece of ammo on the map, can include an optional :SUBTYPE
- RANDOM:MATERIAL - picks any item made of a specific material, currently only supports INORGANIC materials
[EFFECT_BUILDING]For when you need to pass a building ID to a script
- RANDOM - picks any building on the map
- RANDOM:WORKSHOP - picks any workshop on the map
- RANDOM:FURNACE - picks any furnace on the map
- RANDOM:CUSTOM:CUSTOM_BUILDING - picks any custom building
- RANDOM:TRADE_DEPOT - picks any trade depot on the map
- RANDOM:STOCKPILE - picks any stockpile on the map
- RANDOM:ZONE - picks any civ zone on the map
[EFFECT_ARGUMENT]This allows for even more customization in an individual script call. The way it works is, any time the script sees !ARG_X it will replace it with whatever [EFFECT_ARGUMENT:X] is. In order for this too work new tokens must be defined.
Argument TokensEach [EFFECT_ARGUMENT] must have a corresponding weighting and value so that it can randomly pick what to replace.
- [ARGUMENT_WEIGHTING] - this is mandatory, it tells the script how to handle multiple options
- [ARGUMENT_VALUE] - this tells the script the various values to choose for an effect (note these can be numeric values or strings)
- [ARGUMENT_EQUATION] - this is a special case of [ARGUMENT_VALUE] and is more complex, see the wrapper documentation to see how equations work
And that is all the tokens currently available to use in event.txt. Let us take a look at an example in order to provide some clarity.
[EVENT:MERCHANT_ARRIVAL]
[NAME:a new merchant arrives]
[REQUIREMENT:BUILDING:EMPTY_MERCHANTS_STALL:1]
[CHECK:MONTHLY]
[CHANCE:100]
[DELAY:STATIC:0]
[EFFECT:1]
[EFFECT_NAME:merchants arrive for a short time]
[EFFECT_CHANCE:100]
[EFFECT_DELAY:STATIC:0]
[EFFECT_BUILDING:RANDOM:CUSTOM:EMPTY_MERCHANTS_STALL]
[EFFECT_ARGUMENT:1]
[ARGUMENT_WEIGHTING:100,100,100,10,10,10,1]
[ARGUMENT_VARIABLE:HUMAN_MERCHANT,ELF_MERCHANT,DWARF_MERCHANT,TRAVELING_MERCHANT.GOBLIN_MERCHANT,KOBOLD_MERCHANT,EXOTIC_MERCHANT]
[EFFECT_SCRIPT:"building/change -building !BUILDING -to !ARG_1 -dur 25200"]
In words, this event changes the building EMPTY_MERCHANTS_STALL into a different building at the start of every month. The building it changes in to is chosen randomly from those found in [ARGUMENT_VALUE] with the weighting found in [ARGUMENT_WEIGHTING].
And that's everything you need to know. I hope to be able to create some of my own custom events to share with all of you, but please, if you create any events for yourself, or for your mod, please post them here so that others can see more examples!
Addendum 1:You can force an event to trigger by using the dfhack command
events/trigger -event EVENT_ID
This will still run the requirement checks, but will by-pass the [CHECK] and [CHANCE] tokens. In order to by-pass the requirements as well, you can use the command
events/trigger -event EVENT_ID -force
Which will by-pass the event requirements, or
events/trigger -event EVENT_ID -forceAll
Which will by-pass both the event and effect requirements