forked from DFHack/scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrandom-trigger.lua
More file actions
198 lines (171 loc) · 6.33 KB
/
random-trigger.lua
File metadata and controls
198 lines (171 loc) · 6.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
-- triggers random scripts
local usage = [====[
modtools/random-trigger
=======================
Trigger random dfhack commands with specified probabilities.
Register a few scripts, then tell it to "go" and it will pick one
based on the probability weights you specified.
Events are mutually-exclusive - register a list of scripts along with
relative weights, then tell the script to select and run one with the
specified probabilities. The weights must be positive integers, but
they do NOT have to sum to any particular number.
The outcomes are mutually exclusive: only one will be triggered. If you
want multiple independent random events, call the script multiple times.
99% of the time, you won't need to worry about this, but just in case,
you can specify a name of a list of outcomes to prevent interference from
other scripts that call this one. That also permits situations where you
don't know until runtime what outcomes you want. For example, you could
make a `modtools/reaction-trigger` that registers the worker as a mayor
candidate, then run this script to choose a random mayor from the list of
units that did the mayor reaction.
Arguments::
-outcomeListName name
specify the name of this list of outcomes to prevent interference
if two scripts are registering outcomes at the same time. If none
is specified, the default outcome list is selected automatically.
-command [ commandStrs ]
specify the command to be run if this outcome is selected
must be specified unless the -trigger argument is given
-weight n
the relative probability weight of this outcome
n must be a non-negative integer
if not specified, n=1 is used by default
-trigger
selects a random script based on the specified outcomeList
(or the default one if none is specified)
-preserveList
when combined with trigger, preserves the list of outcomes so you
don't have to register them again.
-withProbability p
p is a real number between 0 and 1 inclusive
triggers the command immediately with this probability
-seed s
sets the random seed for debugging purposes
(guarantees the same sequence of random numbers will be produced)
use
-listOutcomes
lists the currently registered list of outcomes of the outcomeList
along with their probability weights, for debugging purposes
-clear
unregister everything
.. note::
``-preserveList`` is something of a beta feature, which should be
avoided by users without a specific reason to use it.
It is highly recommended that you always specify ``-outcomeListName``
when you give this command to prevent almost certain interference.
If you want to trigger one of 5 outcomes three times, you might want
this option even without ``-outcomeListName``.
The list is NOT retained across game save/load, as nobody has yet had
a use for this feature. Contact expwnent if you would use it; it's
not that hard but if nobody wants it he won't bother.
]====]
-- performance will be slightly faster if you preserve the outcome lists
-- when possible and trigger them multiple times instead of reregistering
-- each time, but the effect should be small
local utils = require 'utils'
local eventful = require 'plugins.eventful'
outcomeLists = outcomeLists or {} --as:{_type:table,_array:{_type:table,total:number,outcomes:{_type:table,_array:{_type:table,weight:number,command:__arg}}}}
randomGen = randomGen or dfhack.random.new()
eventful.enableEvent(eventful.eventType.UNLOAD, 1)
eventful.onUnload.randomTrigger = function()
outcomeLists = {}
end
local validArgs = utils.invert({
'help',
'command',
'outcomeListName',
'weight',
'seed',
'trigger',
'preserveList',
'withProbability',
'listOutcomes',
'clear',
})
local function triggerEvent(outcomeListName)
local outcomeList = outcomeLists[outcomeListName]
local r = randomGen:random(outcomeList.total)
local sum = 0
--print ('r = ' .. r)
for i,outcome in ipairs(outcomeList.outcomes) do
sum = sum + outcome.weight
if sum > r then --luacheck: skip
local temp = outcome.command
--print('triggering outcome ' .. i .. ': "' .. table.concat(temp, ' ') .. '"')
--dfhack.run_command(table.unpack(temp))
dfhack.run_script(table.unpack(temp))
break
else
--print ('sum = ' .. sum .. ' <= r = ' .. r)
end
end
--print('Done.')
--dfhack.print('\n')
end
local args = utils.processArgs({...}, validArgs)
if args.help then
print(usage)
return
end
if args.clear then
outcomeLists = {}
end
if args.weight and not tonumber(args.weight) then
error ('Invalid weight: ' .. args.weight)
end
local weight = (args.weight and tonumber(args.weight)) or 1
if weight ~= math.floor(weight) then
error 'Noninteger weight.'
end
if weight < 0 then
error 'invalid weight: must be non-negative'
end
if args.seed then
randomGen:init(tonumber(args.seed), 37) --37 is probably excessive and definitely arbitrary
end
args.outcomeListName = args.outcomeListName or ''
args.outcomeListName = 'outcomeList ' .. args.outcomeListName
if args.withProbability then
local withProbability = tonumber(args.withProbability)
if not withProbability or withProbability < 0 or withProbability > 1 then
error('Invalid withProbability: ' .. (withProbability or 'nil'))
end
if randomGen:drandom() < withProbability then
dfhack.run_command(table.unpack(args.command))
end
end
if args.trigger then
triggerEvent(args.outcomeListName)
if not args.preserveList then
outcomeLists[args.outcomeListName] = nil
end
return
end
if args.listOutcomes then
local outcomeList = outcomeLists[args.outcomeListName]
if not outcomeList then
print ('No outcomes registered.')
return
end
print ('Total weight: ' .. outcomeList.total)
for _,outcome in ipairs(outcomeList.outcomes) do
print(' outcome weight ' .. outcome.weight .. ': ' .. table.concat(outcome.command, ' '))
end
print('\n')
return
end
if not args.command then
return
end
--actually register
local outcomeList = outcomeLists[args.outcomeListName]
if not outcomeList then
outcomeLists[args.outcomeListName] = {}
outcomeList = outcomeLists[args.outcomeListName]
end
outcomeList.total = weight + (outcomeList.total or 0)
local outcome = {}
outcome.weight = weight
outcome.command = args.command
outcomeList.outcomes = outcomeList.outcomes or {}
table.insert(outcomeList.outcomes, outcome)