z_mirror/EditorExtensions

git clone https://git.raiguard.me/z_mirror/EditorExtensions

Caleb HeuerReturn normal force in get_player_proper_forcef564b4d

hidden-items
9.2 KiB321 linesraw
1--- @class LabState
2--- @field lab StateData
3--- @field normal StateData
4--- @field player LuaPlayer
5--- @field refresh boolean?
6
7--- @class StateData
8--- @field force LuaForce
9--- @field position MapPosition
10--- @field surface LuaSurface
11
12--- @alias LabSetting
13--- | "force"
14--- | "off"
15--- | "personal"
16
17local empty_map_gen_settings = {
18  default_enable_all_autoplace_controls = false,
19  property_expression_names = { cliffiness = 0 },
20  autoplace_settings = {
21    tile = { settings = { ["out-of-map"] = { frequency = "normal", size = "normal", richness = "normal" } } },
22  },
23  starting_area = "none",
24}
25
26--- @param player LuaPlayer
27--- @param lab_setting LabSetting
28--- @return LabState?
29local function create_lab(player, lab_setting)
30  local key
31  if lab_setting == "personal" then
32    key = player.index
33  elseif game.forces["EE_TESTSURFACE_shared"] then
34    -- For versions prior to 1.13.0, all forces used the "shared" lab
35    key = "shared"
36  else
37    -- Use the actual force name, not the testing lab force name
38    key = string.gsub(player.force.name, "EE_TESTFORCE_", "")
39  end
40  local surface_name = "EE_TESTSURFACE_" .. key
41  local force_name = "EE_TESTFORCE_" .. key
42
43  local surface = game.get_surface(surface_name)
44  if not surface then
45    surface = game.create_surface(surface_name, empty_map_gen_settings)
46    if not surface then
47      player.print("Could not create test surface")
48      return
49    end
50    -- Lab conditions
51    surface.generate_with_lab_tiles = true
52    surface.freeze_daytime = true
53    surface.show_clouds = false
54    surface.daytime = 0
55    -- Warn the player about the passage of time
56    player.print({ "message.ee-time-passes-in-lab" })
57  end
58
59  local force = game.forces[force_name]
60  if not game.forces[force_name] then
61    if table_size(game.forces) == 64 then
62      player.print(
63        "Cannot create a testing lab force. Factorio only supports up to 64 forces at once. Please use a shared lab."
64      )
65      return
66    end
67
68    force = game.create_force(force_name)
69    if settings.global["ee-testing-lab-match-research"].value then
70      -- Sync research techs with the parent force
71      for name, tech in pairs(player.force.technologies) do
72        force.technologies[name].researched = tech.researched
73      end
74    else
75      force.research_all_technologies()
76    end
77  end
78
79  return {
80    lab = { force = force, position = { x = 0, y = 0 }, surface = surface },
81    normal = { force = player.force, position = player.position, surface = player.surface },
82    player = player,
83  }
84end
85
86--- @param player LuaPlayer
87--- @param to_state StateData
88local function transfer_player(player, to_state)
89  if not to_state.force.valid or not to_state.surface.valid then
90    return
91  end
92  player.teleport(to_state.position, to_state.surface)
93  player.force = to_state.force
94end
95
96--- @param player LuaPlayer
97--- @param lab_setting LabSetting
98local function enter_lab(player, lab_setting)
99  local lab_state = global.testing_lab_state[player.index]
100  if not lab_state or lab_state.refresh then
101    lab_state = create_lab(player, lab_setting)
102    global.testing_lab_state[player.index] = lab_state
103  end
104  if not lab_state then
105    return
106  end
107
108  local normal_data = lab_state.normal
109  normal_data.force = player.force --[[@as LuaForce]]
110  normal_data.position = player.position
111  normal_data.surface = player.surface
112
113  transfer_player(player, lab_state.lab)
114end
115
116--- @param player LuaPlayer
117local function exit_lab(player)
118  local lab_state = global.testing_lab_state[player.index]
119  if not lab_state then
120    return
121  end
122
123  local lab_data = lab_state.lab
124  if player.surface == lab_data.surface then
125    lab_data.position = player.position
126  end
127
128  transfer_player(player, lab_state.normal)
129end
130
131--- @param player LuaPlayer
132--- @return LabSetting
133local function get_lab_setting(player)
134  return player.mod_settings["ee-testing-lab"].value --[[@as LabSetting]]
135end
136
137--- @param e EventData.on_pre_player_toggled_map_editor
138local function on_pre_player_toggled_map_editor(e)
139  local player = game.get_player(e.player_index)
140  if not player then
141    return
142  end
143
144  local lab_setting = get_lab_setting(player)
145  if lab_setting == "off" then
146    return
147  end
148
149  if player.controller_type == defines.controllers.editor then
150    exit_lab(player)
151  end
152end
153
154--- @param e EventData.on_player_toggled_map_editor
155local function on_player_toggled_map_editor(e)
156  local player = game.get_player(e.player_index)
157  if not player then
158    return
159  end
160
161  local lab_setting = get_lab_setting(player)
162  if lab_setting == "off" then
163    return
164  end
165
166  if player.controller_type == defines.controllers.editor then
167    enter_lab(player, lab_setting)
168  end
169end
170
171--- @param e EventData.on_force_reset
172local function on_force_reset(e)
173  local parent_force = e.force
174  if string.find(parent_force.name, "EE_TESTFORCE_") then
175    return
176  end
177  if not settings.global["ee-testing-lab-match-research"].value then
178    return
179  end
180
181  local testing_force = game.forces["EE_TESTFORCE_" .. parent_force.name]
182  if not testing_force then
183    return
184  end
185
186  for name, tech in pairs(parent_force.technologies) do
187    testing_force.technologies[name].researched = tech.researched
188  end
189  testing_force.reset_technology_effects()
190end
191
192--- @param e EventData.on_research_reversed
193local function on_research_reversed(e)
194  local parent_force = e.research.force
195  if string.find(parent_force.name, "EE_TESTFORCE_") then
196    return
197  end
198  if not settings.global["ee-testing-lab-match-research"].value then
199    return
200  end
201
202  local testing_force = game.forces["EE_TESTFORCE_" .. parent_force.name]
203  if testing_force then
204    testing_force.technologies[e.research.name].researched = false
205  end
206
207  for i in pairs(parent_force.players) do
208    local testing_force = game.forces["EE_TESTFORCE_" .. i]
209    if testing_force then
210      testing_force.technologies[e.research.name].researched = false
211    end
212  end
213end
214
215--- @param e EventData.on_research_finished
216local function on_research_finished(e)
217  local parent_force = e.research.force
218  if string.find(parent_force.name, "EE_TESTFORCE_") then
219    return
220  end
221  if not settings.global["ee-testing-lab-match-research"].value then
222    return
223  end
224
225  local testing_force = game.forces["EE_TESTFORCE_" .. parent_force.name]
226  if testing_force then
227    testing_force.technologies[e.research.name].researched = true
228  end
229
230  for i in pairs(parent_force.players) do
231    local testing_force = game.forces["EE_TESTFORCE_" .. i]
232    if testing_force then
233      testing_force.technologies[e.research.name].researched = true
234    end
235  end
236end
237
238local function on_match_research_setting_changed()
239  for _, force in pairs(game.forces) do
240    local _, _, force_key = string.find(force.name, "EE_TESTFORCE_(.*)")
241    if not force_key then
242      goto continue
243    end
244    if not settings.global["ee-testing-lab-match-research"].value then
245      force.research_all_technologies()
246      goto continue
247    end
248
249    local parent_force
250    local force_key_num = tonumber(force_key) --- @cast force_key_num uint?
251    if force_key_num then
252      local player = game.get_player(force_key_num) --[[@as LuaPlayer]]
253      parent_force = remote.call("EditorExtensions", "get_player_proper_force", player)
254    else
255      parent_force = game.forces[force_key]
256    end
257    if parent_force then
258      -- Sync research techs with the parent force
259      for name, tech in pairs(parent_force.technologies) do
260        force.technologies[name].researched = tech.researched
261      end
262    end
263    force.reset_technology_effects()
264    ::continue::
265  end
266end
267
268--- @param e EventData.on_runtime_mod_setting_changed
269local function on_testing_lab_setting_changed(e)
270  local lab_state = global.testing_lab_state[e.player_index]
271  if lab_state then
272    lab_state.refresh = true
273  end
274end
275
276--- @param e EventData.on_runtime_mod_setting_changed
277local function on_runtime_mod_setting_changed(e)
278  if e.setting == "ee-testing-lab-match-research" then
279    on_match_research_setting_changed()
280  elseif e.setting == "ee-testing-lab" then
281    on_testing_lab_setting_changed(e)
282  end
283end
284
285local testing_lab = {}
286
287testing_lab.on_init = function()
288  --- @type table<uint, LabState?>
289  global.testing_lab_state = {}
290end
291
292testing_lab.events = {
293  [defines.events.on_force_reset] = on_force_reset,
294  [defines.events.on_player_toggled_map_editor] = on_player_toggled_map_editor,
295  [defines.events.on_pre_player_toggled_map_editor] = on_pre_player_toggled_map_editor,
296  [defines.events.on_research_finished] = on_research_finished,
297  [defines.events.on_research_reversed] = on_research_reversed,
298  [defines.events.on_runtime_mod_setting_changed] = on_runtime_mod_setting_changed,
299}
300
301testing_lab.add_remote_interface = function()
302  remote.add_interface("EditorExtensions", {
303    --- Get the force that the player is actually on, ignoring the testing lab force.
304    --- @param player LuaPlayer
305    --- @return ForceIdentification
306    get_player_proper_force = function(player)
307      if not player or not player.valid then
308        error("Did not pass a valid LuaPlayer")
309      end
310      local in_editor = player.controller_type == defines.controllers.editor
311      local ts_setting = player.mod_settings["ee-testing-lab"].value
312      if ts_setting == "off" or not in_editor then
313        return player.force
314      else
315        return global.testing_lab_state[player.index].normal.force
316      end
317    end,
318  })
319end
320
321return testing_lab