-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathtesting-lab.lua
363 lines (315 loc) · 10.5 KB
/
testing-lab.lua
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
361
362
363
--- @class LabState
--- @field lab StateData
--- @field normal StateData
--- @field player LuaPlayer
--- @field refresh boolean?
--- @class StateData
--- @field force LuaForce
--- @field position MapPosition
--- @field surface LuaSurface
--- @field vehicle LuaEntity?
--- @field is_driver boolean?
--- @alias LabSetting
--- | "force"
--- | "off"
--- | "personal"
local empty_map_gen_settings = {
default_enable_all_autoplace_controls = false,
property_expression_names = { cliffiness = 0 },
autoplace_settings = {
tile = { settings = { ["out-of-map"] = { frequency = "normal", size = "normal", richness = "normal" } } },
},
starting_area = "none",
}
--- @param player LuaPlayer
--- @param lab_setting LabSetting
--- @return LabState?
local function create_lab(player, lab_setting)
local key
if lab_setting == "personal" then
key = player.index
elseif game.forces["EE_TESTSURFACE_shared"] then
-- For versions prior to 1.13.0, all forces used the "shared" lab
key = "shared"
else
-- Use the actual force name, not the testing lab force name
key = string.gsub(player.force.name, "EE_TESTFORCE_", "")
end
local surface_name = "EE_TESTSURFACE_" .. key
local force_name = "EE_TESTFORCE_" .. key
local surface = game.get_surface(surface_name)
if not surface then
surface = game.create_surface(surface_name, empty_map_gen_settings)
for force_name, force in pairs(game.forces) do
if not string.find(force_name, "EE_TESTFORCE_") then
force.set_surface_hidden(surface, true)
end
end
player.force.set_surface_hidden(surface, true)
if not surface then
player.print("Could not create test surface")
return
end
-- Lab conditions
surface.generate_with_lab_tiles = true
surface.freeze_daytime = true
surface.show_clouds = false
surface.daytime = 0
-- Warn the player about the passage of time
player.print({ "message.ee-time-passes-in-lab" })
end
local force = game.forces[force_name]
if not game.forces[force_name] then
if #game.forces == 64 then
player.print(
"Cannot create a testing lab force. Factorio only supports up to 64 forces at once. Please use a shared lab."
)
return
end
force = game.create_force(force_name)
if settings.global["ee-testing-lab-match-research"].value then
-- Sync research techs with the parent force
for name, tech in pairs(player.force.technologies) do
force.technologies[name].researched = tech.researched
end
else
force.research_all_technologies()
end
end
return {
lab = { force = force, position = { x = 0, y = 0 }, surface = surface },
normal = { force = player.force, position = player.position, surface = player.surface },
player = player,
}
end
--- @param player LuaPlayer
--- @param to_state StateData
local function transfer_player(player, to_state)
if not to_state.force.valid or not to_state.surface.valid then
return
end
-- Change force first to avoid spilling items into the real world on inventory size change - see #143
player.force = to_state.force
player.teleport(to_state.position, to_state.surface)
end
--- @param player LuaPlayer
local function enter_lab(player)
local lab_state = storage.testing_lab_state[player.index]
if not lab_state then
return
end
local normal_data = lab_state.normal
normal_data.force = player.force --[[@as LuaForce]]
normal_data.position = player.position
normal_data.surface = player.surface
transfer_player(player, lab_state.lab)
end
--- @param lab_state LabState
local function exit_lab(lab_state)
local lab_data = lab_state.lab
if lab_state.player.surface == lab_data.surface then
lab_data.position = lab_state.player.position
end
transfer_player(lab_state.player, lab_state.normal)
end
--- @param player LuaPlayer
--- @return LabSetting
local function get_lab_setting(player)
return player.mod_settings["ee-testing-lab"].value --[[@as LabSetting]]
end
--- @param e EventData.on_pre_player_toggled_map_editor
local function on_pre_player_toggled_map_editor(e)
local player = game.get_player(e.player_index)
if not player then
return
end
local lab_setting = get_lab_setting(player)
if lab_setting == "off" then
return
end
local in_editor = player.physical_controller_type == defines.controllers.editor
local lab_state = storage.testing_lab_state[e.player_index]
if not lab_state and not in_editor then
lab_state = create_lab(player, lab_setting)
storage.testing_lab_state[e.player_index] = lab_state
end
if not lab_state then
return
end
local current_state = in_editor and lab_state.lab or lab_state.normal
current_state.vehicle = player.vehicle
current_state.is_driver = player.driving
if in_editor then
exit_lab(lab_state)
end
end
--- @param player LuaPlayer
local function sync_vehicle_state(player)
local lab_state = storage.testing_lab_state[player.index]
if not lab_state then
return
end
local in_editor = player.physical_controller_type == defines.controllers.editor
local new_state = in_editor and lab_state.lab or lab_state.normal
local vehicle = new_state.vehicle
if not vehicle or not vehicle.valid then
return
end
if new_state.is_driver and not vehicle.get_driver() then
new_state.vehicle.set_driver(player)
elseif not new_state.is_driver and not vehicle.get_passenger() then
new_state.vehicle.set_passenger(player)
end
end
--- @param e EventData.on_player_toggled_map_editor
local function on_player_toggled_map_editor(e)
local player = game.get_player(e.player_index)
if not player then
return
end
if get_lab_setting(player) == "off" then
return
end
if player.physical_controller_type == defines.controllers.editor then
enter_lab(player)
end
sync_vehicle_state(player)
end
--- @param e EventData.on_force_reset
local function on_force_reset(e)
local parent_force = e.force
if string.find(parent_force.name, "EE_TESTFORCE_") then
return
end
if not settings.global["ee-testing-lab-match-research"].value then
return
end
local testing_force = game.forces["EE_TESTFORCE_" .. parent_force.name]
if not testing_force then
return
end
for name, tech in pairs(parent_force.technologies) do
testing_force.technologies[name].researched = tech.researched
end
testing_force.reset_technology_effects()
end
--- @param e EventData.on_research_reversed
local function on_research_reversed(e)
local parent_force = e.research.force
if string.find(parent_force.name, "EE_TESTFORCE_") then
return
end
if not settings.global["ee-testing-lab-match-research"].value then
return
end
local testing_force = game.forces["EE_TESTFORCE_" .. parent_force.name]
if testing_force then
testing_force.technologies[e.research.name].researched = false
end
for i in pairs(parent_force.players) do
local testing_force = game.forces["EE_TESTFORCE_" .. i]
if testing_force then
testing_force.technologies[e.research.name].researched = false
end
end
end
--- @param e EventData.on_research_finished
local function on_research_finished(e)
local parent_force = e.research.force
if string.find(parent_force.name, "EE_TESTFORCE_") then
return
end
if not settings.global["ee-testing-lab-match-research"].value then
return
end
local testing_force = game.forces["EE_TESTFORCE_" .. parent_force.name]
if testing_force then
testing_force.technologies[e.research.name].researched = true
end
for i in pairs(parent_force.players) do
local testing_force = game.forces["EE_TESTFORCE_" .. i]
if testing_force then
testing_force.technologies[e.research.name].researched = true
end
end
end
local function on_match_research_setting_changed()
for _, force in pairs(game.forces) do
local _, _, force_key = string.find(force.name, "EE_TESTFORCE_(.*)")
if not force_key then
goto continue
end
if not settings.global["ee-testing-lab-match-research"].value then
force.research_all_technologies()
goto continue
end
local parent_force
local force_key_num = tonumber(force_key) --- @cast force_key_num uint?
if force_key_num then
local player = game.get_player(force_key_num) --[[@as LuaPlayer]]
parent_force = remote.call("EditorExtensions", "get_player_proper_force", player)
else
parent_force = game.forces[force_key]
end
if parent_force then
-- Sync research techs with the parent force
for name, tech in pairs(parent_force.technologies) do
force.technologies[name].researched = tech.researched
end
end
force.reset_technology_effects()
::continue::
end
end
--- @param e EventData.on_runtime_mod_setting_changed
local function on_testing_lab_setting_changed(e)
local lab_state = storage.testing_lab_state[e.player_index]
if lab_state then
lab_state.refresh = true
end
end
--- @param e EventData.on_runtime_mod_setting_changed
local function on_runtime_mod_setting_changed(e)
if e.setting == "ee-testing-lab-match-research" then
on_match_research_setting_changed()
elseif e.setting == "ee-testing-lab" then
on_testing_lab_setting_changed(e)
end
end
local testing_lab = {}
testing_lab.on_init = function()
--- @type table<uint, LabState?>
storage.testing_lab_state = {}
end
testing_lab.events = {
[defines.events.on_force_reset] = on_force_reset,
[defines.events.on_player_toggled_map_editor] = on_player_toggled_map_editor,
[defines.events.on_pre_player_toggled_map_editor] = on_pre_player_toggled_map_editor,
[defines.events.on_research_finished] = on_research_finished,
[defines.events.on_research_reversed] = on_research_reversed,
[defines.events.on_runtime_mod_setting_changed] = on_runtime_mod_setting_changed,
}
testing_lab.add_remote_interface = function()
remote.add_interface("EditorExtensions", {
--- Get the force that the player is actually on, ignoring the testing lab force.
--- @param player LuaPlayer
--- @return ForceID
get_player_proper_force = function(player)
if not player or not player.valid then
error("Did not pass a valid LuaPlayer")
end
local in_editor = player.physical_controller_type == defines.controllers.editor
local ts_setting = player.mod_settings["ee-testing-lab"].value
if
ts_setting == "off"
or not in_editor
or not storage.testing_lab_state
or not storage.testing_lab_state[player.index]
then
return player.force
end
return storage.testing_lab_state[player.index].normal.force
end,
})
end
return testing_lab