z_mirror/Krastorio2

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

Caleb HeuerFix teleporter deconstruction status display6889f983

real-fusion-reactor
17.0 KiB522 linesraw
1local flib_gui = require("__flib__.gui")
2local flib_math = require("__flib__.math")
3local flib_table = require("__flib__.table")
4
5local util = require("scripts.util")
6
7--- @class PlanetaryTeleporterGui
8--- @field elems table<string, LuaGuiElement>
9--- @field entity LuaEntity
10--- @field data PlanetaryTeleporterData
11--- @field player LuaPlayer
12--- @field search_open boolean
13--- @field search_query string
14
15--- @param player LuaPlayer
16--- @param from LuaEntity
17--- @param to LuaEntity
18local function teleport_player(player, from, to)
19  from.energy = 0
20  to.energy = 0
21  local position = to.position
22  position.y = position.y + 1.1
23  player.teleport(position, to.surface)
24  from.surface.play_sound({
25    path = "kr-planetary-teleporter-effect-sound",
26    volume_modifier = 0.8,
27    position = from.position,
28  })
29  to.surface.play_sound({
30    path = "kr-planetary-teleporter-effect-sound",
31    position = to.position,
32    volume_modifier = 1,
33  })
34end
35
36--- @param self PlanetaryTeleporterGui
37local function filter_destinations(self)
38  local search_query = self.search_query
39  for _, frame in pairs(self.elems.destinations_table.children) do
40    if search_query == "" then
41      frame.visible = true
42      goto continue
43    end
44    local unit_number = tonumber(frame.name) --[[@as uint]]
45    local data = storage.planetary_teleporter[unit_number]
46    if not data then
47      frame.destroy()
48      goto continue
49    end
50    if data.name and string.find(string.lower(data.name), search_query, nil, true) then
51      frame.visible = true
52    else
53      frame.visible = false
54    end
55    ::continue::
56  end
57end
58
59--- @param self PlanetaryTeleporterGui
60--- @param by_click boolean
61local function toggle_search(self, by_click)
62  local search_button = self.elems.search_button
63  if not by_click then
64    search_button.toggled = not search_button.toggled
65  end
66  self.search_open = search_button.toggled
67
68  local search_textfield = self.elems.search_textfield
69  search_textfield.visible = self.search_open
70  if self.search_open then
71    search_textfield.focus()
72    search_textfield.select_all()
73  else
74    self.search_query = ""
75  end
76
77  filter_destinations(self)
78end
79
80--- @param self PlanetaryTeleporterGui
81local function destroy_gui(self)
82  local window = self.elems.kr_planetary_teleporter_window
83  if window.valid then
84    window.destroy()
85    if self.player.opened == window then
86      self.player.opened = nil
87    end
88  end
89  storage.planetary_teleporter_gui[self.player.index] = nil
90end
91
92--- @param self PlanetaryTeleporterGui
93local function update_name_label(self)
94  self.elems.name_label.caption = self.data.name or { "gui.kr-planetary-teleporter-unnamed" }
95end
96
97--- @param e EventData.on_gui_closed|EventData.on_gui_click
98local function on_window_closed(e)
99  local self = storage.planetary_teleporter_gui[e.player_index]
100  if not self then
101    return
102  end
103  if self.search_open then
104    toggle_search(self, false)
105    self.player.opened = self.elems.kr_planetary_teleporter_window
106    return
107  end
108  destroy_gui(self)
109end
110
111--- @param e EventData.on_gui_text_changed
112local function on_search_textfield_text_changed(e)
113  local self = storage.planetary_teleporter_gui[e.player_index]
114  if not self then
115    return
116  end
117  self.search_query = e.element.text
118  filter_destinations(self)
119end
120
121--- @param e EventData.on_gui_click|EventData.CustomInputEvent
122local function on_search_button_clicked(e)
123  local self = storage.planetary_teleporter_gui[e.player_index]
124  if not self then
125    return
126  end
127  toggle_search(self, true)
128end
129
130--- @param e EventData.on_gui_click
131local function on_rename_button_clicked(e)
132  local self = storage.planetary_teleporter_gui[e.player_index]
133  if not self or not self.elems.kr_planetary_teleporter_window.valid then
134    return
135  end
136  local textfield = self.elems.name_textfield
137  local label = self.elems.name_label
138  if textfield.visible then
139    local text = textfield.text
140    if #text > 0 then
141      self.data.name = text
142    else
143      self.data.name = nil
144    end
145    update_name_label(self)
146    label.visible = true
147    textfield.visible = false
148  else
149    label.visible = false
150    textfield.text = self.data.name or ""
151    textfield.visible = true
152    textfield.select_all()
153    textfield.focus()
154  end
155end
156
157--- @param e EventData.on_gui_confirmed
158local function on_name_textfield_confirmed(e)
159  -- Just pass through the logic because it's all the same anyway.
160  on_rename_button_clicked(e --[[@as EventData.on_gui_click]])
161end
162
163--- @param e EventData.on_gui_click
164local function on_teleport_button_clicked(e)
165  local self = storage.planetary_teleporter_gui[e.player_index]
166  if not self or not self.elems.kr_planetary_teleporter_window.valid then
167    return
168  end
169  local target_unit_number = e.element.tags.unit_number --[[@as UnitNumber]]
170  assert(target_unit_number, "target_unit_number is nil")
171  local target_data = storage.planetary_teleporter[target_unit_number]
172  assert(target_data, "target_data is nil")
173  local players = storage.planetary_teleporter_players[
174    target_data.entities.turret.unit_number --[[@as uint]]
175  ]
176  if players then
177    util.flying_text_with_sound(self.player, { "gui.kr-planetary-teleporter-destination-blocked" })
178    return
179  end
180  local source_unit_number = self.entity.unit_number
181  assert(source_unit_number, "source_unit_number is nil")
182  local source_data = storage.planetary_teleporter[source_unit_number]
183  teleport_player(self.player, source_data.entities.base, target_data.entities.base)
184  destroy_gui(self)
185end
186
187--- @type table<defines.entity_status, { sprite: SpritePath, label: LocalisedString }>
188local status_sprites = {
189  [defines.entity_status.charging] = { label = { "entity-status.charging" }, sprite = "utility/status_yellow" },
190  [defines.entity_status.fully_charged] = {
191    label = { "entity-status.fully-charged" },
192    sprite = "utility/status_working",
193  },
194  [defines.entity_status.no_power] = { label = { "entity-status.no-power" }, sprite = "utility/status_not_working" },
195  [defines.entity_status.normal] = { label = { "entity-status.normal" }, sprite = "utility/status_working" },
196  [defines.entity_status.marked_for_deconstruction] = {
197    label = { "entity-status.marked-for-deconstruction" },
198    sprite = "utility/status_not_working",
199  },
200}
201
202--- @param self PlanetaryTeleporterGui
203local function update_gui(self)
204  if not self.entity.valid then
205    destroy_gui(self)
206    return
207  end
208  local buffer_capacity = self.entity.prototype.electric_energy_source_prototype.buffer_capacity
209  local status = self.entity.status
210  local status_properties = status_sprites[status] or {}
211  self.elems.status_icon.sprite = status_properties.sprite
212  self.elems.status_label.caption = status_properties.label
213
214  local charge = self.entity.energy / buffer_capacity
215  self.elems.charge_progressbar.value = charge
216  self.elems.charge_progressbar.caption = { "format-percent", flib_math.round(charge * 100) }
217
218  local players = storage.planetary_teleporter_players[self.data.entities.turret.unit_number]
219  local standing_on_teleporter = players and flib_table.find(players, self.player.index) or false
220  local too_many_players = players and #players > 1 or false
221  local source_fully_charged = flib_math.round(charge, 0.1) >= 1
222  local fully_operational = standing_on_teleporter and source_fully_charged and not too_many_players
223
224  if fully_operational then
225    self.elems.destinations_status_icon.sprite = "flib_indicator_green"
226    self.elems.destinations_status_label.caption = { "gui.kr-planetary-teleporter-ready" }
227  else
228    self.elems.destinations_status_icon.sprite = "flib_indicator_red"
229    if not standing_on_teleporter then
230      self.elems.destinations_status_label.caption = { "gui.kr-planetary-teleporter-not-on-teleporter" }
231    elseif too_many_players then
232      self.elems.destinations_status_label.caption = { "gui.kr-planetary-teleporter-too-many-players" }
233    else
234      self.elems.destinations_status_label.caption = { "gui.kr-planetary-teleporter-not-fully-charged" }
235    end
236  end
237
238  update_name_label(self)
239
240  for _, frame in pairs(self.elems.destinations_table.children) do
241    local unit_number = tonumber(frame.name) --[[@as uint]]
242    local dest_data = storage.planetary_teleporter[unit_number]
243    if not dest_data then
244      goto continue
245    end
246    local dest_entity = dest_data.entities.base
247    local dest_charge = dest_entity.energy / buffer_capacity
248    local dest_fully_charged = flib_math.round(dest_charge, 0.1) == 1
249    local minimap = frame.minimap_frame.minimap --[[@as LuaGuiElement]]
250    minimap.progressbar.value = dest_charge
251    minimap.progressbar.visible = not dest_fully_charged
252    minimap.minimap_button.enabled = fully_operational and dest_fully_charged
253    if not dest_fully_charged then
254      minimap.minimap_button.tooltip = { "format-percent", flib_math.round(dest_charge * 100) }
255    elseif fully_operational then
256      minimap.minimap_button.tooltip = { "gui.kr-planetary-teleporter-teleport" }
257    else
258      minimap.minimap_button.tooltip = nil
259    end
260    ::continue::
261  end
262end
263
264local function on_tick()
265  for _, gui in pairs(storage.planetary_teleporter_gui) do
266    update_gui(gui)
267  end
268end
269
270--- @param e EventData.on_gui_opened
271local function on_gui_opened(e)
272  local entity = e.entity
273  if not entity or not entity.valid or entity.name ~= "kr-planetary-teleporter" then
274    return
275  end
276
277  local player = game.get_player(e.player_index)
278  if not player then
279    return
280  end
281
282  local destination_buttons = {}
283  for unit_number, data in pairs(storage.planetary_teleporter) do
284    if unit_number == entity.unit_number or data.force ~= entity.force or data.surface ~= entity.surface then
285      goto continue
286    end
287    destination_buttons[#destination_buttons + 1] = {
288      type = "frame",
289      name = unit_number,
290      style = "train_with_minimap_frame",
291      direction = "vertical",
292      {
293        type = "frame",
294        name = "minimap_frame",
295        style = "deep_frame_in_shallow_frame",
296        {
297          type = "minimap",
298          name = "minimap",
299          style = "kr_planetary_teleporter_destination_minimap",
300          position = data.entities.base.position,
301          {
302            type = "button",
303            name = "minimap_button",
304            style = "kr_planetary_teleporter_destination_minimap_button",
305            tags = { unit_number = unit_number },
306            handler = { [defines.events.on_gui_click] = on_teleport_button_clicked },
307          },
308          {
309            type = "progressbar",
310            name = "progressbar",
311            style = "kr_planetary_teleporter_destination_charge_bar",
312            ignored_by_interaction = true,
313          },
314          {
315            type = "label",
316            style = "kr_planetary_teleporter_destination_label",
317            style_mods = { top_margin = 8, right_margin = 8 },
318            caption = data.name or { "gui.kr-planetary-teleporter-unnamed" },
319            ignored_by_interaction = true,
320          },
321        },
322      },
323    }
324    ::continue::
325  end
326
327  local elems = flib_gui.add(player.gui.screen, {
328    type = "frame",
329    name = "kr_planetary_teleporter_window",
330    direction = "vertical",
331    elem_mods = { auto_center = true },
332    handler = { [defines.events.on_gui_closed] = on_window_closed },
333    {
334      type = "flow",
335      style = "flib_titlebar_flow",
336      drag_target = "kr_planetary_teleporter_window",
337      {
338        type = "label",
339        style = "flib_frame_title",
340        caption = { "entity-name.kr-planetary-teleporter" },
341        ignored_by_interaction = true,
342      },
343      { type = "empty-widget", style = "flib_titlebar_drag_handle", ignored_by_interaction = true },
344      {
345        type = "textfield",
346        name = "search_textfield",
347        style = "flib_titlebar_search_textfield",
348        visible = false,
349        handler = { [defines.events.on_gui_text_changed] = on_search_textfield_text_changed },
350      },
351      {
352        type = "sprite-button",
353        name = "search_button",
354        style = "frame_action_button",
355        sprite = "utility/search",
356        tooltip = { "gui.flib-search-instruction" },
357        auto_toggle = true,
358        handler = { [defines.events.on_gui_click] = on_search_button_clicked },
359      },
360      {
361        type = "sprite-button",
362        style = "frame_action_button",
363        sprite = "utility/close",
364        tooltip = { "gui.close-instruction" },
365        handler = { [defines.events.on_gui_click] = on_window_closed },
366      },
367    },
368    {
369      type = "flow",
370      style_mods = { horizontal_spacing = 12 },
371      {
372        type = "frame",
373        style = "inside_shallow_frame",
374        direction = "vertical",
375        {
376          type = "frame",
377          style = "subheader_frame",
378          style_mods = { width = 424 },
379          {
380            type = "label",
381            name = "name_label",
382            style = "subheader_caption_label",
383            style_mods = { horizontally_squashable = true },
384          },
385          {
386            type = "textfield",
387            name = "name_textfield",
388            visible = false,
389            handler = { [defines.events.on_gui_confirmed] = on_name_textfield_confirmed },
390          },
391          {
392            type = "sprite-button",
393            style = "mini_button_aligned_to_text_vertically_when_centered",
394            sprite = "utility/rename_icon",
395            handler = { [defines.events.on_gui_click] = on_rename_button_clicked },
396          },
397        },
398        {
399          type = "flow",
400          style_mods = { padding = 12, top_padding = 8, vertical_spacing = 8 },
401          direction = "vertical",
402          {
403            type = "flow",
404            style_mods = { vertical_align = "center" },
405            { type = "sprite", name = "status_icon", style = "flib_indicator" },
406            { type = "label", name = "status_label" },
407          },
408          {
409            type = "frame",
410            style = "deep_frame_in_shallow_frame",
411            { type = "entity-preview", style = "wide_entity_button", elem_mods = { entity = entity } },
412          },
413          {
414            type = "progressbar",
415            name = "charge_progressbar",
416            style = "production_progressbar",
417            style_mods = { horizontally_stretchable = true },
418            caption = "foo",
419            value = 0.2,
420          },
421          { type = "empty-widget", style = "entity_frame_filler" },
422        },
423      },
424      {
425        type = "frame",
426        style = "inside_deep_frame",
427        direction = "vertical",
428        {
429          type = "frame",
430          style = "subheader_frame",
431          {
432            type = "label",
433            style = "subheader_caption_label",
434            caption = { "gui.kr-planetary-teleporter-destinations" },
435          },
436          { type = "empty-widget", style = "flib_horizontal_pusher" },
437          {
438            type = "flow",
439            name = "destinations_status_flow",
440            style_mods = { vertical_align = "center", right_margin = 8 },
441            {
442              type = "sprite",
443              name = "destinations_status_icon",
444              style = "flib_indicator",
445              sprite = "flib_indicator_red",
446            },
447            {
448              type = "label",
449              name = "destinations_status_label",
450              caption = { "gui.kr-planetary-teleporter-not-on-teleporter" },
451            },
452          },
453        },
454        {
455          type = "scroll-pane",
456          style = "kr_planetary_teleporter_destinations_scroll_pane",
457          vertical_scroll_policy = "auto-and-reserve-space",
458          {
459            type = "table",
460            name = "destinations_table",
461            style = "slot_table",
462            column_count = 3,
463            children = destination_buttons,
464          },
465        },
466      },
467    },
468  })
469
470  player.opened = elems.kr_planetary_teleporter_window
471
472  --- @type PlanetaryTeleporterGui
473  storage.planetary_teleporter_gui[e.player_index] = {
474    elems = elems,
475    data = storage.planetary_teleporter[entity.unit_number],
476    entity = entity,
477    player = player,
478    search_open = false,
479    search_query = "",
480  }
481end
482
483--- @param e EventData.CustomInputEvent
484local function on_focus_search(e)
485  local self = storage.planetary_teleporter_gui[e.player_index]
486  if self then
487    toggle_search(self, false)
488  end
489end
490
491local planetary_teleporter_gui = {}
492
493function planetary_teleporter_gui.on_init()
494  --- @type table<uint, PlanetaryTeleporterGui>
495  storage.planetary_teleporter_gui = {}
496end
497
498function planetary_teleporter_gui.on_configuration_changed()
499  for _, gui in pairs(storage.planetary_teleporter_gui or {}) do
500    if gui.elems.window.valid then
501      gui.elems.window.destroy()
502    end
503  end
504  storage.planetary_teleporter_gui = {}
505end
506
507planetary_teleporter_gui.events = {
508  [defines.events.on_gui_opened] = on_gui_opened,
509  [defines.events.on_tick] = on_tick,
510  ["kr-linked-focus-search"] = on_focus_search,
511}
512
513flib_gui.add_handlers({
514  planetary_teleporter_on_name_textfield_confirmed = on_name_textfield_confirmed,
515  planetary_teleporter_on_rename_button_clicked = on_rename_button_clicked,
516  planetary_teleporter_on_search_button_clicked = on_search_button_clicked,
517  planetary_teleporter_on_search_textfield_text_changed = on_search_textfield_text_changed,
518  planetary_teleporter_on_teleport_button_clicked = on_teleport_button_clicked,
519  planetary_teleporter_on_window_closed = on_window_closed,
520})
521
522return planetary_teleporter_gui