forked from jamesofarrell/TheFatController
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathTrainList.lua
More file actions
360 lines (342 loc) · 12.7 KB
/
TrainList.lua
File metadata and controls
360 lines (342 loc) · 12.7 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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
--- TrainList module
-- @module TrainList
--- Train info
-- @type TrainInfo
local TrainInfo = {
name = "", -- name of the train
current_station = false, --name of current station
depart_at = 0, --tick when train will depart the station
--main_index = 0, -- always equal to global.trainsByForce[force.name] index
train = false, -- ref to lua_train
mainIndex = false, --
last_update = 0, --tick of last inventory update
previous_state = 0,
previous_state_tick = 0,
last_state = 0, -- last trainstate
last_state_tick = 0,
unfiltered_state = {
previous_state = 0,
previous_tick = 0,
last_state = 0,
last_tick = 0
},
first_carriage = false,
last_carriage = false,
force = false,
follower_index = 0, -- player_index of following player
opened_guis = {}, -- contains references to opened player guis, indexed by player_index
stations = {}, --boolean table, indexed by stations in the schedule, new global trains_by_station?? should speedup filtered display
automated = false,
alarm = {
active = false,
type = false,
message = "",
last_message = 0, --tick
arrived_at_signal = false, -- tick
left_station = false --tick
}
}
--- foo
-- @type TrainList
TrainList = {}--luacheck: allow defined top
--- add a train to the list
-- @return #TrainInfo description
TrainList.add_train = function(train, no_alarm)
local force = train.carriages[1].force
local ti = TrainList.createTrainInfo(train, no_alarm)
if ti then
table.insert(global.trainsByForce[force.name], ti)
end
TrainList.remove_invalid(force, true)
return ti
end
TrainList.remove_invalid = function(force, show)
local removed = 0
TrainList.removeAlarms()
local decoupled = false
for i = #global.trainsByForce[force.name], 1, -1 do
local ti = global.trainsByForce[force.name][i]
if ti then
if not ti.train or not ti.train.valid then
local first_train = (ti.first_carriage and ti.first_carriage.valid and ti.first_carriage.train.valid) and ti.first_carriage.train or false
local last_train = (ti.last_carriage and ti.last_carriage.valid and ti.last_carriage.train.valid) and ti.last_carriage.train or false
if first_train then
local tmp = TrainList.createTrainInfo(first_train, true)
if tmp then
tmp.mainIndex = i
global.trainsByForce[force.name][i] = tmp
else
table.remove(global.trainsByForce[force.name], i)
end
end
if last_train then
local tmp = TrainList.createTrainInfo(last_train, true)
if tmp then
if first_train then
decoupled = tmp
else
tmp.mainIndex = i
global.trainsByForce[force.name][i] = tmp
end
else
table.remove(global.trainsByForce[force.name], i)
end
end
end
if ti.train and ti.train.valid and #ti.train.locomotives.front_movers == 0 and #ti.train.locomotives.back_movers == 0 then
table.remove(global.trainsByForce[force.name], i)
removed = removed + 1
end
else
debugDump("oops", true)
table.remove(global.trainsByForce[force.name], i)
end
end
if decoupled then
table.insert(global.trainsByForce[force.name], decoupled)
end
TrainList.remove_duplicates(force)
for i, ti in pairs(global.trainsByForce[force.name]) do
ti.mainIndex = i
ti.opened_guis = {}
if ti.train.valid and ti.train.state == defines.train_state.manual_control and ti.train.speed == 0 then
TrainList.add_manual(ti)
end
end
if removed > 0 then
if show or debug then --removed > 0 and show then
debugDump(game.tick .. " Removed " .. removed .. " invalid trains", true)
--flyingText("Removed "..removed.." invalid trains", RED, false, true)
end
end
GUI.refreshAllTrainInfoGuis(force)
return removed
end
TrainList.remove_duplicates = function(force)
for i = #global.trainsByForce[force.name], 1, -1 do
if global.trainsByForce[force.name][i] then
local trainA = global.trainsByForce[force.name][i].train
for j = #global.trainsByForce[force.name], 1, -1 do
if j ~= i and trainA and trainA == global.trainsByForce[force.name][j].train then
--debugDump("Duplicate: "..i.."=="..j,true)
table.remove(global.trainsByForce[force.name], i)
end
end
end
end
end
--- empty traininfo
-- @return #TrainInfo description
TrainList.createTrainInfo = function(train, no_alarm)
if #train.locomotives.front_movers == 0 and #train.locomotives.back_movers == 0 then
return false
end
local ti = table.deepcopy(TrainInfo)
ti.train = train
ti.first_carriage = train.carriages[1]
ti.last_carriage = train.carriages[#train.carriages]
ti.force = ti.first_carriage.force
if no_alarm then
ti.alarm.last_message = game.tick + 60 * 60
end
if ti.first_carriage == ti.last_carriage then
ti.last_carriage = false
end
if #train.locomotives.front_movers > 0 then
ti.name = train.locomotives.front_movers[1].backer_name
else
ti.name = train.locomotives.back_movers[1].backer_name
end
if ti.name:len() > 20 then
ti.name = ti.name:sub(1, 20) .. "..."
end
ti.last_state = ti.train.state
ti.last_update = 0
ti.inventory = getHighestInventoryCount(ti)--luacheck: ignore
ti.automated = train.state ~= defines.train_state.manual_control and train.state ~= defines.train_state.manual_control_stop
TrainList.update_stations(ti)
local record = (train.schedule and train.schedule.records and #train.schedule.records > 0) and train.schedule.records and train.schedule.records[train.schedule.current]
local station = record and record.station or "Temporary"
ti.current_station = station
if ti.train.state == defines.train_state.wait_station and train.schedule and #train.schedule.records > 1 then
ti.depart_at = game.tick
end
return ti
end
TrainList.remove_train = function(train)
local force = train.carriages[1].force
local trains = global.trainsByForce[force.name]
for i = #trains, 1, -1 do
if trains[i].train == train then
table.remove(trains, i)
break
end
end
TrainList.removeAlarms(train)
for i, ti in pairs(global.trainsByForce[force.name]) do
ti.mainIndex = i
ti.opened_guis = {}
end
GUI.refreshAllTrainInfoGuis(force)
end
--- Get traininfo by LuaTrain
-- @return #TrainInfo
TrainList.get_traininfo = function(force, train)
local trains = global.trainsByForce[force.name]
if trains then
for i, ti in pairs(trains) do
if ti.train and ti.train.valid and ti.train == train then
ti.mainIndex = i
return ti
end
end
else
return false
end
end
TrainList.update_stations = function(ti)
if not ti.train or not ti.train.valid then return end
local records = (ti.train.schedule and ti.train.schedule.records and #ti.train.schedule.records > 0) and ti.train.schedule.records or false
ti.stations = {}
if not records then return end
for _, record in pairs(records) do
ti.stations[record.station or "Temporary"] = true
end
end
TrainList.matchStationFilter = function(trainInfo, activeFilterList, alarm, modeOR)
if trainInfo ~= nil then
if alarm then
return trainInfo.alarm.active
end
if not activeFilterList then
return true
end
for filter, _ in pairs(activeFilterList) do
if modeOR and trainInfo.stations[filter] then
return true
end
if not modeOR and not trainInfo.stations[filter] then
return false
end
end
if not modeOR then
return true
else
return false
end
end
return false
end
TrainList.get_filtered_trains = function(force, guiSettings)
local trains = global.trainsByForce[force.name]
local alarm_only = guiSettings.filter_alarms
local filterList = guiSettings.activeFilterList
local mode = guiSettings.filterModeOr
local filtered = {}
if trains then
guiSettings.automatedCount = 0
guiSettings.filteredIndex = {}
for i, ti in pairs(trains) do
TrainList.update_stations(ti)
if TrainList.matchStationFilter(ti, filterList, alarm_only, mode) then
ti.mainIndex = i
table.insert(filtered, ti)
guiSettings.filteredIndex[i] = true
if ti.automated then
guiSettings.automatedCount = guiSettings.automatedCount + 1
end
end
end
end
return filtered
end
TrainList.removeAlarms = function(train)
local remove = {}
for tick, trains in pairs(global.updateAlarms) do
for i = #trains, 1, -1 do
local ti = trains[i]
if ti then
if not ti.train or not ti.train.valid then
trains[i] = nil
elseif ti.train == train then
trains[i] = nil
end
else
trains[i] = nil
end
end
if #trains == 0 then
--debugDump("no alarms"..tick,true)
table.insert(remove, tick)
end
end
for _, tick in pairs(remove) do
global.updateAlarms[tick] = nil
end
end
TrainList.add_manual = function(ti, player)
if ti and ti.train and ti.train.valid then
local state = ti.train.state
if state == defines.train_state.manual_control
or state == defines.train_state.no_path
then
if player or ti.train.speed == 0 then
ti.passenger = player
--debugDump("added to manual",true)
TickTable.insert_unique(game.tick + update_rate_manual, "updateManual", ti)
end
end
end
end
TrainList.reset_manual = function(train)
if not train then
global.updateManual = {}
for _, p in pairs(game.players) do
if p.vehicle and (p.vehicle.type == "locomotive" or p.vehicle.type == "cargo-wagon") then
local ti = TrainList.get_traininfo(p.force, p.vehicle.train)
if ti and ti.train and ti.train.valid then
local state = ti.train.state
if state == defines.train_state.manual_control
or state == defines.train_state.manual_control_stop
or state == defines.train_state.no_path
then
TickTable.insert(game.tick + update_rate_manual + p.index, "updateManual", ti)
end
global.gui[p.index].vehicle = ti.train
end
end
end
elseif train.valid then
for _, trains in pairs(global.updateManual) do
for i = #trains, 1, -1 do
local ti = trains[i]
if ti and ti.train.valid and ti.train == train and train.speed ~= 0 then
trains[i] = nil
elseif not ti or not ti.train.valid then
trains[i] = nil
end
end
end
end
end
TrainList.count = function(force)
if force then
return #global.trainsByForce[force.name]
else
local c = 0
for _, trains in pairs(global.trainsByForce) do
c = c + #trains
end
return c
end
end
TrainList.automatedCount = function(force)
local c = 0
for _, t in pairs(global.trainsByForce[force.name]) do
if t.train.valid and (t.train.state ~= defines.train_state.manual_control and t.train.state ~= defines.train_state.manual_control_stop) then
c = c + 1
end
end
global.automatedCount[force.name] = c
return c
end