z_mirror/Krastorio2

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

Patrick 'Quezler' MounierFix roboport mode switching not preserving quality (#672)579dd07e

k2so-fixes
7.3 KiB272 linesraw
1local flib_gui = require("__flib__.gui")
2
3local util = require("scripts.util")
4
5--- @type table<string, {color: Color?, suffix: string, text: string}
6local mode_data = {
7  normal = { suffix = "", text = "normal" },
8  construction = { color = { r = 0.5, g = 1, b = 0.4 }, suffix = "-construction-mode", text = "construction" },
9  logistic = { color = { r = 1, g = 0.5, b = 0.25 }, suffix = "-logistic-mode", text = "logistic" },
10}
11
12local next_roboport_mode = {
13  [""] = "construction",
14  ["-construction-mode"] = "logistic",
15  ["-logistic-mode"] = "normal",
16}
17
18--- @param player LuaPlayer
19--- @param entity LuaEntity
20local function update_gui(player, entity)
21  local player_gui = storage.roboport_guis[player.index]
22  if not player_gui or not player_gui.valid then
23    return
24  end
25  local mode = string.match(entity.name, "^.*%-(%w*)%-mode") or "normal"
26
27  for _, button in pairs(player_gui.inner_frame.children) do
28    if button.name == mode then
29      button.state = true
30    else
31      button.state = false
32    end
33  end
34end
35
36--- @param entity LuaEntity
37--- @param player LuaPlayer
38--- @param to_mode string?
39local function change_mode(entity, player, to_mode)
40  local matched, _, base_name, suffix = string.find(entity.name, "^(.-)(%-%w-%-mode)$")
41  if not matched then
42    base_name = entity.name
43    suffix = ""
44  end
45
46  local new_mode_data
47  if to_mode then
48    new_mode_data = mode_data[to_mode]
49  else
50    new_mode_data = mode_data[next_roboport_mode[suffix]]
51  end
52
53  local new_name = base_name .. new_mode_data.suffix
54
55  if new_name == entity.name then
56    return
57  end
58
59  if not prototypes.entity[new_name] then
60    util.flying_text_with_sound(player, { "message.kr-roboport-modes-not-supported" }, { position = entity.position })
61    return
62  end
63
64  local energy = entity.energy
65  local health = entity.health
66
67  -- Find all players that have this roboport open
68  -- This must be done before the roboport is swapped
69  local guis_to_update = {}
70  for player_index in pairs(storage.roboport_guis) do
71    local gui_player = game.get_player(player_index)
72    if gui_player and gui_player.opened_gui_type == defines.gui_type.entity then
73      local opened = gui_player.opened
74      if opened and opened == entity then
75        table.insert(guis_to_update, gui_player)
76      end
77    end
78  end
79
80  local new_entity = entity.surface.create_entity({
81    name = new_name,
82    position = entity.position,
83    force = entity.force,
84    direction = entity.direction,
85    quality = entity.quality,
86    fast_replace = true,
87    raise_built = true,
88    spill = false,
89    create_build_effect_smoke = false,
90  })
91
92  if not new_entity then
93    return
94  end
95  new_entity.energy = energy
96  new_entity.health = health
97  new_entity.last_user = player
98
99  util.entity_flying_text(new_entity, { "message.kr-" .. new_mode_data.text .. "-mode" }, new_mode_data.color)
100  game.play_sound({
101    path = "kr-welding",
102    position = new_entity.position,
103    volume_modifier = 1.0,
104  })
105  new_entity.surface.create_particle({
106    name = "kr-welding-particle",
107    position = { new_entity.position.x, new_entity.position.y + 1 },
108    movement = { 0.0, -0.05 },
109    height = 1.0,
110    vertical_speed = 0.015,
111    frame_speed = 1,
112  })
113
114  -- Swapping the entity will close any open GUIs, so we must re-open them.
115  for _, gui_player in pairs(guis_to_update) do
116    gui_player.opened = new_entity
117    update_gui(gui_player, new_entity)
118  end
119end
120
121--- @param e EventData.on_gui_checked_state_changed
122local function on_mode_radio_button_checked(e)
123  local player = game.get_player(e.player_index)
124  if not player or player.opened_gui_type ~= defines.gui_type.entity then
125    return
126  end
127  local entity = player.opened --[[@as LuaEntity]]
128  if not entity.valid or entity.type ~= "roboport" then
129    return
130  end
131  change_mode(entity, player, e.element.name)
132end
133
134--- @param mode string
135local function radio_button(mode)
136  return {
137    type = "radiobutton",
138    name = mode,
139    caption = { "message.kr-" .. mode .. "-mode" },
140    state = false,
141    handler = { [defines.events.on_gui_checked_state_changed] = on_mode_radio_button_checked },
142  }
143end
144
145--- @param player_index uint
146local function destroy_gui(player_index)
147  local player_gui = storage.roboport_guis[player_index]
148  if not player_gui then
149    return
150  end
151  if player_gui.valid then
152    player_gui.destroy()
153  end
154  storage.roboport_guis[player_index] = nil
155end
156
157--- @param player LuaPlayer
158local function refresh_gui(player)
159  destroy_gui(player.index)
160
161  _, storage.roboport_guis[player.index] = flib_gui.add(player.gui.relative, {
162    type = "frame",
163    name = "kr_roboport_mode_window",
164    caption = { "gui.kr-change-mode" },
165    anchor = {
166      gui = defines.relative_gui_type.roboport_gui,
167      position = defines.relative_gui_position.right,
168      names = storage.roboport_has_variants_gui,
169    },
170    {
171      type = "frame",
172      name = "inner_frame",
173      style = "inside_shallow_frame_with_padding",
174      direction = "vertical",
175      radio_button("normal"),
176      radio_button("construction"),
177      radio_button("logistic"),
178    },
179  })
180end
181
182local function find_variants()
183  -- Find all compatible roboports
184  local has_variants_gui = {}
185  for _, entity in pairs(prototypes.get_entity_filtered({ { filter = "type", type = "roboport" } })) do
186    local matched, _, base_name = string.find(entity.name, "^(.-)%-%w*%-mode")
187    if matched then
188      has_variants_gui[base_name] = true
189      has_variants_gui[entity.name] = true
190    end
191  end
192
193  -- Convert to a format usable by GuiAnchor
194  local output = {}
195  local i = 0
196  for entity in pairs(has_variants_gui) do
197    i = i + 1
198    output[i] = entity
199  end
200
201  storage.roboport_has_variants_gui = output
202end
203
204--- @param e EventData.on_gui_opened
205local function on_gui_opened(e)
206  local entity = e.entity
207  if not entity or not entity.valid then
208    return
209  end
210  local player = game.get_player(e.player_index)
211  if not player then
212    return
213  end
214  if entity.type == "roboport" then
215    update_gui(player, entity)
216  end
217end
218
219--- @param e EventData.on_player_created
220local function on_player_created(e)
221  local player = game.get_player(e.player_index)
222  if player then
223    refresh_gui(player)
224  end
225end
226
227--- @param e EventData.on_player_removed
228local function on_player_removed(e)
229  destroy_gui(e.player_index)
230end
231
232--- @param e EventData.CustomInputEvent
233local function on_change_roboport_mode_hotkey(e)
234  local player = game.get_player(e.player_index)
235  if not player then
236    return
237  end
238  local selected = player.selected
239  if selected and selected.valid and selected.type == "roboport" then
240    change_mode(selected, player)
241  end
242end
243
244local function on_configuration_changed()
245  find_variants()
246  for _, player in pairs(game.players) do
247    refresh_gui(player)
248  end
249end
250
251local function on_init()
252  --- @type table<uint, LuaGuiElement>
253  storage.roboport_guis = {}
254  on_configuration_changed()
255end
256
257--- @class RoboportHandler : event_handler
258local roboport = {}
259
260roboport.on_init = on_init
261roboport.on_configuration_changed = on_configuration_changed
262
263roboport.events = {
264  [defines.events.on_gui_opened] = on_gui_opened,
265  [defines.events.on_player_created] = on_player_created,
266  [defines.events.on_player_removed] = on_player_removed,
267  ["kr-change-roboport-state"] = on_change_roboport_mode_hotkey,
268}
269
270flib_gui.add_handlers({ roboport_on_mode_radio_button_checked = on_mode_radio_button_checked })
271
272return roboport