@@ -5,6 +5,8 @@ local argparse = require('argparse')
5
5
local repeatutil = require (" repeat-util" )
6
6
local utils = require (' utils' )
7
7
8
+ DEBUG = DEBUG or false
9
+
8
10
---- --------------------------------
9
11
-- state management
10
12
@@ -91,62 +93,71 @@ local function clamp_timeskip(timeskip)
91
93
return math.min (timeskip , get_next_trigger_year_tick (next_tick )- next_tick )
92
94
end
93
95
94
- local function has_caste_flag (unit , flag )
95
- if unit .curse .rem_tags1 [flag ] then return false end
96
- if unit .curse .add_tags1 [flag ] then return true end
97
- return dfhack .units .casteFlagSet (unit .race , unit .caste , df .caste_raw_flags [flag ])
96
+ local function increment_counter (obj , counter_name , timeskip )
97
+ if obj [counter_name ] <= 0 then return end
98
+ obj [counter_name ] = obj [counter_name ] + timeskip
99
+ end
100
+
101
+ local function decrement_counter (obj , counter_name , timeskip )
102
+ if obj [counter_name ] <= 0 then return end
103
+ obj [counter_name ] = math.max (1 , obj [counter_name ] - timeskip )
104
+ end
105
+
106
+ local function adjust_unit_counters (unit , timeskip )
107
+ local c1 = unit .counters
108
+ decrement_counter (c1 , ' think_counter' , timeskip )
109
+ decrement_counter (c1 , ' job_counter' , timeskip )
110
+ decrement_counter (c1 , ' swap_counter' , timeskip )
111
+ decrement_counter (c1 , ' winded' , timeskip )
112
+ decrement_counter (c1 , ' stunned' , timeskip )
113
+ decrement_counter (c1 , ' unconscious' , timeskip )
114
+ decrement_counter (c1 , ' suffocation' , timeskip )
115
+ decrement_counter (c1 , ' webbed' , timeskip )
116
+ decrement_counter (c1 , ' soldier_mood_countdown' , timeskip )
117
+ decrement_counter (c1 , ' pain' , timeskip )
118
+ decrement_counter (c1 , ' nausea' , timeskip )
119
+ decrement_counter (c1 , ' dizziness' , timeskip )
120
+ local c2 = unit .counters2
121
+ decrement_counter (c2 , ' paralysis' , timeskip )
122
+ decrement_counter (c2 , ' numbness' , timeskip )
123
+ decrement_counter (c2 , ' fever' , timeskip )
124
+ decrement_counter (c2 , ' exhaustion' , timeskip * 3 )
125
+ increment_counter (c2 , ' hunger_timer' , timeskip )
126
+ increment_counter (c2 , ' thirst_timer' , timeskip )
127
+ local job = unit .job .current_job
128
+ if job and job .job_type == df .job_type .Rest then
129
+ decrement_counter (c2 , ' sleepiness_timer' , timeskip * 200 )
130
+ elseif job and job .job_type == df .job_type .Sleep then
131
+ decrement_counter (c2 , ' sleepiness_timer' , timeskip * 19 )
132
+ else
133
+ increment_counter (c2 , ' sleepiness_timer' , timeskip )
134
+ end
135
+ decrement_counter (c2 , ' stomach_content' , timeskip * 5 )
136
+ decrement_counter (c2 , ' stomach_food' , timeskip * 5 )
137
+ decrement_counter (c2 , ' vomit_timeout' , timeskip )
138
+ -- stored_fat wanders about based on other state; we can probably leave it alone
98
139
end
99
140
141
+ -- unit needs appear to be incremented on season ticks, so we don't need to worry about those
100
142
local function adjust_units (timeskip )
101
143
for _ , unit in ipairs (df .global .world .units .active ) do
102
144
if not dfhack .units .isActive (unit ) then goto continue end
103
- if unit .sex == df .pronoun_type .she then
104
- if unit .pregnancy_timer > 0 then
105
- unit .pregnancy_timer = math.max (1 , unit .pregnancy_timer - timeskip )
106
- end
107
- end
145
+ decrement_counter (unit , ' pregnancy_timer' , timeskip )
108
146
dfhack .units .subtractGroupActionTimers (unit , timeskip , df .unit_action_type_group .All )
109
- local job = unit .job .current_job
110
- local c2 = unit .counters2
111
- if job and job .job_type == df .job_type .Rest then
112
- c2 .sleepiness_timer = math.max (0 , c2 .sleepiness_timer - timeskip * 200 )
113
- end
114
- if not dfhack .units .isCitizen (unit , true ) then goto continue end
115
- if not has_caste_flag (unit , ' NO_EAT' ) then
116
- c2 .hunger_timer = c2 .hunger_timer + timeskip
117
- end
118
- if not has_caste_flag (unit , ' NO_DRINK' ) then
119
- c2 .thirst_timer = c2 .thirst_timer + timeskip
120
- end
121
- if not has_caste_flag (unit , ' NO_SLEEP' ) then
122
- if job and job .job_type == df .job_type .Sleep then
123
- c2 .sleepiness_timer = math.max (0 , c2 .sleepiness_timer - timeskip * 19 )
124
- else
125
- c2 .sleepiness_timer = c2 .sleepiness_timer + timeskip
126
- end
127
- end
128
- -- TODO: c2.stomach_content, c2.stomach_food, and c2.stored_fat
129
- -- TODO: needs
147
+ if not dfhack .units .isOwnGroup (unit ) then goto continue end
148
+ adjust_unit_counters (unit , timeskip )
130
149
:: continue::
131
150
end
132
151
end
133
152
134
- local function increment_counter (ev , counter_name , timeskip )
135
- if ev [counter_name ] <= 0 then return end
136
- ev [counter_name ] = ev [counter_name ] + timeskip
137
- end
138
-
139
- local function decrement_counter (ev , counter_name , timeskip )
140
- if ev [counter_name ] <= 0 then return end
141
- ev [counter_name ] = math.max (1 , ev [counter_name ] - timeskip )
142
- end
143
-
153
+ -- behavior ascertained from in-game observation
144
154
local function adjust_activities (timeskip )
145
155
for i , act in ipairs (df .global .world .activities .all ) do
146
156
for _ , ev in ipairs (act .events ) do
147
157
if df .activity_event_training_sessionst :is_instance (ev ) then
148
- -- noop
158
+ -- no counters
149
159
elseif df .activity_event_combat_trainingst :is_instance (ev ) then
160
+ -- has organize_counter at a non-zero value, but it doesn't seem to move
150
161
elseif df .activity_event_skill_demonstrationst :is_instance (ev ) then
151
162
-- can be negative or positive, but always counts towards 0
152
163
if ev .organize_counter < 0 then
@@ -156,46 +167,62 @@ local function adjust_activities(timeskip)
156
167
end
157
168
decrement_counter (ev , ' train_countdown' , timeskip )
158
169
elseif df .activity_event_fill_service_orderst :is_instance (ev ) then
170
+ -- no counters
159
171
elseif df .activity_event_individual_skill_drillst :is_instance (ev ) then
160
172
-- only counts down on season ticks, nothing to do here
161
173
elseif df .activity_event_sparringst :is_instance (ev ) then
174
+ decrement_counter (ev , ' countdown' , timeskip * 2 )
162
175
elseif df .activity_event_ranged_practicest :is_instance (ev ) then
176
+ -- countdown appears to never move from 0
163
177
decrement_counter (ev , ' countdown' , timeskip )
164
178
elseif df .activity_event_harassmentst :is_instance (ev ) then
179
+ -- TODO: counter behavior not yet analyzed
180
+ -- print(i)
165
181
elseif df .activity_event_encounterst :is_instance (ev ) then
182
+ -- TODO: counter behavior not yet analyzed
183
+ -- print(i)
166
184
elseif df .activity_event_reunionst :is_instance (ev ) then
185
+ -- TODO: counter behavior not yet analyzed
186
+ -- print(i)
167
187
elseif df .activity_event_conversationst :is_instance (ev ) then
168
188
increment_counter (ev , ' pause' , timeskip )
169
189
elseif df .activity_event_guardst :is_instance (ev ) then
190
+ -- no counters
170
191
elseif df .activity_event_conflictst :is_instance (ev ) then
171
192
increment_counter (ev , ' inactivity_timer' , timeskip )
172
193
increment_counter (ev , ' attack_inactivity_timer' , timeskip )
173
194
increment_counter (ev , ' stop_fort_fights_timer' , timeskip )
174
195
elseif df .activity_event_prayerst :is_instance (ev ) then
175
196
decrement_counter (ev , ' timer' , timeskip )
176
197
elseif df .activity_event_researchst :is_instance (ev ) then
177
- -- noop
198
+ -- no counters
178
199
elseif df .activity_event_playst :is_instance (ev ) then
179
200
increment_counter (ev , ' down_time_counter' , timeskip )
180
201
elseif df .activity_event_worshipst :is_instance (ev ) then
181
202
increment_counter (ev , ' down_time_counter' , timeskip )
182
203
elseif df .activity_event_socializest :is_instance (ev ) then
183
204
increment_counter (ev , ' down_time_counter' , timeskip )
184
205
elseif df .activity_event_ponder_topicst :is_instance (ev ) then
206
+ decrement_counter (ev , ' timer' , timeskip )
185
207
elseif df .activity_event_discuss_topicst :is_instance (ev ) then
186
208
decrement_counter (ev , ' timer' , timeskip )
187
209
elseif df .activity_event_teach_topicst :is_instance (ev ) then
210
+ decrement_counter (ev , ' time_left' , timeskip )
188
211
elseif df .activity_event_readst :is_instance (ev ) then
189
212
decrement_counter (ev , ' timer' , timeskip )
190
213
elseif df .activity_event_writest :is_instance (ev ) then
191
214
decrement_counter (ev , ' timer' , timeskip )
192
215
elseif df .activity_event_copy_written_contentst :is_instance (ev ) then
216
+ decrement_counter (ev , ' time_left' , timeskip )
193
217
elseif df .activity_event_make_believest :is_instance (ev ) then
194
218
decrement_counter (ev , ' time_left' , timeskip )
195
219
elseif df .activity_event_play_with_toyst :is_instance (ev ) then
220
+ decrement_counter (ev , ' time_left' , timeskip )
196
221
elseif df .activity_event_performancest :is_instance (ev ) then
197
222
increment_counter (ev , ' current_position' , timeskip )
198
223
elseif df .activity_event_store_objectst :is_instance (ev ) then
224
+ -- TODO: counter behavior not yet analyzed
225
+ -- print(i)
199
226
end
200
227
end
201
228
end
@@ -214,18 +241,19 @@ local function on_tick()
214
241
-- add some jitter so we don't fall into a constant pattern
215
242
-- this reduces the risk of repeatedly missing an unknown threshold
216
243
-- also keeps the game from looking robotic at lower frame rates
217
- local jitter_category = math.random (1 , 10 )
218
- if jitter_category <= 1 then
244
+ local jitter_strategy = math.random (1 , 10 )
245
+ if jitter_strategy <= 1 then
219
246
timeskip = math.random (0 , timeskip )
220
- elseif jitter_category <= 3 then
247
+ elseif jitter_strategy <= 3 then
221
248
timeskip = math.random (math.max (0 , timeskip - 2 ), timeskip )
222
- elseif jitter_category <= 5 then
249
+ elseif jitter_strategy <= 5 then
223
250
timeskip = math.random (math.max (0 , timeskip - 4 ), timeskip )
224
251
end
225
252
226
- -- no need to let our deficit grow unbounded
253
+ -- don't let our deficit grow unbounded if we can never catch up
227
254
timeskip_deficit = math.min (desired_timeskip - timeskip , 100.0 )
228
255
256
+ if DEBUG then print ((' timeskip (%d, +%.2f)' ):format (timeskip , timeskip_deficit )) end
229
257
if timeskip <= 0 then return end
230
258
231
259
local desired_calendar_timeskip = (timeskip * state .settings .calendar_rate ) + calendar_timeskip_deficit
0 commit comments