I was asked to put some comments in the script for this mod so that others could use it to make pre-determined random encounters and custom sieges. Well, I started on that, but realized that it made the code file too much of a mess, so I instead wrote a read me tutorial that I will include in the next update to this mod. Here is a copy of it.
I have learned a lot from Roses and Fleeting Frames. They have made this possible.
This script counts down to a specific date, displays a pop-up window and then finds
a random surface location and spawns a creature at that location a set number of times.
I originally planned for this to be done in one event, as one function
"function wp_moon_hunters()" that displayed a pop-up window and spawned 100 creatures
at random locations around the surface of the map. However, running multiple lines of
code (in this case thousands) at the same time crashes the game. So, instead this script
is done using multiple spawning wave functions. Each wave is separated by a day (1200 ticks)
and consists of 20 creatures; however, these numbers could be manipulated for more variety.
The countdown function is the countdown to the event. Note that it is not called until the
last line of the script file (after everything is read). It calls each event wave at the date
specified by finding the difference between the current date and the specified event date
and then using "dfhack.timeout(ticks+1,'ticks',FUNCTION_NAME)", like so:
local ticks = date_moon_hunters_START - current_tick
The total ticks in a year is calculated with the following equation. This variable never changes:
local year_tick_total = 1200*28*12
This is equivalent to 403200 (1200 ticks in a day * 28 days in a month * 12 months in a year)
The current tick is calculated using:
local current_tick = df.global.cur_year_tick
The specific event date is calculated using:
local date_moon_hunters_START = 1200*(7*28+9)
This is (1200 ticks in a day * (# of months * 28 days in a month * # of days)
The # of months starts at Granite (not January) and the # of days is up to the date (the day
before the day of the event)
Knowing this, we can do some interesting things here. Using a random number, we can now choose
a random date (within a date range) and not just use a specific date. Lets say that we want this
event to happen between June to September. We can now roll a four sided die and set the result
to the month, like so:
local m = math.random(4) + 3
local date_of_event = 1200*(m*28+9)
This will choose a random number between 1 and 4 and then add 3 to it. Assuming we got a
random number of 2, this event would happen on the 10th of July. We can further complicate
this date like so:
local m = math.random(4) + 3
local d = math.random(28) - 1
local date_of_event = 1200*(m*28+d)
This event will now happen sometime between the 1st of June and the 28th of September.
Remember that the random number will be between 1 and 28 and the # of days is the day before
the event. Assuming we got the random numbers 3 and 12, this event would happen on August 12th.
Note that these variables would change everytime the game was loaded.
You can do the same thing with years in the future as well. This code here says that if the
difference between the current date and the specified event date is less than 0, the event
has already passed this year and will take place next year.
if ticks < 0 then
ticks = ticks + year_tick_total
end
By multiplying another random number to year_tick_total, you can have this event happen
multiple years in the future. Once again, this would change everytime the game was loaded.
local y = math.random(5)
if ticks < 0 then
ticks = ticks + year_tick_total * y
end
The wave functions make up the actual actions of the events. For this script, they display a
pop-up notification or spawn a creature at a random location, but could do anything that you
want to happen in the event.
The pop-up notification is as follows. Note that this does not pause the game. I also read
that it should come after a creature is spawned, but I have not noticed any issue with that.
If you are using a pop-up notification and a creature spawn at the same time, I suggest
that you put the pop-up notification at the end:
dfhack.gui.showPopupAnnouncement("announcement", COLOR_WHITE , true )
The creature spawn is a simple dfhack.run_command(). Note that this only accepts a string.
If you want to pass a variable that isn't a string, you have to convert it into a string
first. The "modtools/create-unit" has ambush tags, but you will get a pop-up notification
for every creature that is spotted. So, if you are spawning 100 creatures, that is 100
notifications. I suggest that you use your own pop-up notification if you want to announce
an ambush and are using multiple creatures.
The actual spawn loop could be done in a for loop (and is probably the correct format), but
I like while loops, so that is what I used. You can spawn a random number of creatures here:
local n = 0
local t = math.random (11) + 9
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
This will spawn anywhere from 10 to 20 creatures. You can also set a percentage chance trigger
here for anything to even happen:
local event_chance = math.random(10)
if event_chance == 1 then
local n = 0
local t = math.random (11) + 9
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
end
This means that the event only has a 10% chance of happening. You can change this percentage
chance by changing the equation:
local event_chance = math.random(10)
if event_chance < 5 then
local n = 0
local t = math.random (11) + 9
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
end
This means that the event now has a 40% chance of happening. You can further complicate
this by setting different chances to different amount of creature spawns.
local event_chance = math.random(10)
if event_chance < 5 then
local n = 0
local t = math.random (11) + 9
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
else
local n = 0
local t = math.random (5)
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
end
Now this means that the event will happen. However, there is a 40% chance that 10 to
20 creatures will spawn and a 60% chance that 1 to 5 creatures will spawn. To complicate
this further, let's remove the event guarantee:
local event_chance = math.random(10)
if event_chance < 5 then
local n = 0
local t = math.random (11) + 9
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
elseif event_chance == 5 or event_chance == 6
local n = 0
local t = math.random (5)
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
else
-- do nothing
end
Now there is a 40% chance that 10 to 20 creatures will spawn, a 20% chance that 1 to 5
creatures will spawn and a 40% chance that the event will not happen.
You can also add in population requirements, like so:
local k = 0
local event_chance = math.random(10)
for i,unit in pairs(df.global.world.units.active) do
if dfhack.units.isDwarf(unit) then
k = k + 1
end
end
if k > 79 then
if event_chance < 5 then
local n = 0
local t = math.random (11) + 9
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
else
local n = 0
local t = math.random (5)
while n < t do
dfhack.run_command("modtools/create-unit")
n = n + 1
end
end
end
This code counts all of the dwarves on the map and then requires at least a count of 80
for the event to happen. If there are 80 dwaves, there is a 40% chance that 10 to 20
creatures will spawn, a 60% chance that 1 to 5 creatures will spawn and a 40% chance that
the event will not happen.
Once again, you can add an else/elseif to the population minimum if statement to make
a fewer number (or a different creature(s)) spawn depending on the current population:
local k = 0
for i,unit in pairs(df.global.world.units.active) do
if dfhack.units.isDwarf(unit) then
k = k + 1
end
end
if k > 79 then
-- spawn 50 to 100 creatures
else if k > 49 then
-- spawn 20 to 50 creatures
else if k > 19 then
-- spawn 5 to 10 creatures
else
-- do nothing
end
I hope this helps with your own events, pre-determined random encounters and custom sieges!
Let me know if you have any questions.
EDIT: I wanted to add one more tip to this write up:
I highly suggest using the print output while testing your script. You should put a
print output at the start of each function and for each important variable.
For example, this line of code will tell you the date of the event (in ticks), the current
tick and the difference between the two. This is a good way to find out when the event
should happen (e.g. 100 ticks from now or 10,000):
print("event tick:", date_event, "current tick:", current_tick, "ticks to event:", ticks)
This is also a good way to see what random number was chosen so that you can make
sure that the code is working as intended:
local n = math.random(5) + 3
printf(n)