@expcluster/legacy 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE.md +21 -0
  2. package/dist/browser/package.json +40 -0
  3. package/dist/browser/tsconfig.browser.tsbuildinfo +1 -0
  4. package/dist/browser/web/index.d.ts +1 -0
  5. package/dist/browser/web/index.d.ts.map +1 -0
  6. package/dist/browser/web/index.js +2 -0
  7. package/dist/browser/web/index.js.map +1 -0
  8. package/dist/node/index.d.ts +3 -0
  9. package/dist/node/index.d.ts.map +1 -0
  10. package/dist/node/index.js +10 -0
  11. package/dist/node/index.js.map +1 -0
  12. package/dist/node/instance.d.ts +4 -0
  13. package/dist/node/instance.d.ts.map +1 -0
  14. package/dist/node/instance.js +8 -0
  15. package/dist/node/instance.js.map +1 -0
  16. package/dist/node/tsconfig.node.tsbuildinfo +1 -0
  17. package/dist/web/manifest.json +7 -0
  18. package/dist/web/static/exp_legacy.9b6538559ed5e689539c.js +355 -0
  19. package/dist/web/static/index_ts.8f9d451c8d3f7f293da6.js +22 -0
  20. package/dist/web/static/main.94e1b48804112ca8bdfc.js +118 -0
  21. package/dist/web/static/package_json.a83729efdb0697b12396.js +22 -0
  22. package/dist/web/static/web_index_tsx.e5a01ac0e4b0a6f6517f.js +21 -0
  23. package/index.ts +8 -0
  24. package/instance.ts +7 -0
  25. package/module/config/_file_loader.lua +29 -0
  26. package/module/config/advanced_start.lua +112 -0
  27. package/module/config/afk_kick.lua +14 -0
  28. package/module/config/bonus.lua +105 -0
  29. package/module/config/chat_reply.lua +136 -0
  30. package/module/config/compilatron.lua +25 -0
  31. package/module/config/death_logger.lua +15 -0
  32. package/module/config/deconlog.lua +11 -0
  33. package/module/config/discord_alerts.lua +25 -0
  34. package/module/config/expcore/permission_groups.lua +143 -0
  35. package/module/config/graftorio.lua +7 -0
  36. package/module/config/gui/autofill.lua +129 -0
  37. package/module/config/gui/player_list_actions.lua +201 -0
  38. package/module/config/gui/rockets.lua +47 -0
  39. package/module/config/gui/science.lua +21 -0
  40. package/module/config/gui/tasks.lua +13 -0
  41. package/module/config/gui/warps.lua +52 -0
  42. package/module/config/inventory_clear.lua +9 -0
  43. package/module/config/join_messages.lua +8 -0
  44. package/module/config/lawnmower.lua +6 -0
  45. package/module/config/logging.lua +30 -0
  46. package/module/config/miner.lua +8 -0
  47. package/module/config/module.lua +86 -0
  48. package/module/config/nukeprotect.lua +30 -0
  49. package/module/config/pollution_grading.lua +9 -0
  50. package/module/config/popup_messages.lua +10 -0
  51. package/module/config/preset_player_colours.lua +33 -0
  52. package/module/config/preset_player_quickbar.lua +6 -0
  53. package/module/config/protection.lua +17 -0
  54. package/module/config/repair.lua +8 -0
  55. package/module/config/research.lua +179 -0
  56. package/module/config/scorched_earth.lua +116 -0
  57. package/module/config/spawn_area.lua +255 -0
  58. package/module/config/station_auto_name.lua +11 -0
  59. package/module/config/statistics.lua +46 -0
  60. package/module/config/vlayer.lua +180 -0
  61. package/module/config/warnings.lua +38 -0
  62. package/module/control.lua +46 -0
  63. package/module/expcore/datastore.lua +884 -0
  64. package/module/expcore/external.lua +146 -0
  65. package/module/expcore/permission_groups.lua +355 -0
  66. package/module/expcore/player_data.lua +152 -0
  67. package/module/locale/en/addons.cfg +31 -0
  68. package/module/locale/en/data.cfg +114 -0
  69. package/module/locale/en/expcore.cfg +16 -0
  70. package/module/locale/en/gui.cfg +114 -0
  71. package/module/locale/zh-CN/addons.cfg +31 -0
  72. package/module/locale/zh-CN/data.cfg +114 -0
  73. package/module/locale/zh-CN/expcore.cfg +16 -0
  74. package/module/locale/zh-CN/gui.cfg +122 -0
  75. package/module/locale/zh-TW/addons.cfg +31 -0
  76. package/module/locale/zh-TW/data.cfg +114 -0
  77. package/module/locale/zh-TW/expcore.cfg +16 -0
  78. package/module/locale/zh-TW/gui.cfg +122 -0
  79. package/module/module.json +14 -0
  80. package/module/module_exports.lua +3 -0
  81. package/module/modules/control/jail.lua +119 -0
  82. package/module/modules/control/protection.lua +204 -0
  83. package/module/modules/control/reports.lua +225 -0
  84. package/module/modules/control/spectate.lua +173 -0
  85. package/module/modules/control/vlayer.lua +807 -0
  86. package/module/modules/control/warnings.lua +331 -0
  87. package/module/modules/control/warps.lua +488 -0
  88. package/module/modules/data/alt-view.lua +26 -0
  89. package/module/modules/data/greetings.lua +45 -0
  90. package/module/modules/data/language.lua +21 -0
  91. package/module/modules/data/player-colours.lua +63 -0
  92. package/module/modules/data/quickbar.lua +72 -0
  93. package/module/modules/data/statistics.lua +181 -0
  94. package/module/modules/data/tag.lua +82 -0
  95. package/module/modules/data/toolbar.lua +31 -0
  96. package/module/modules/graftorio/forcestats.lua +196 -0
  97. package/module/modules/graftorio/general.lua +63 -0
  98. package/module/modules/graftorio/require.lua +28 -0
  99. package/module/modules/graftorio/statics.lua +36 -0
  100. package/module/modules/gui/vlayer.lua +547 -0
  101. package/module/modules/gui/warp-list.lua +909 -0
  102. package/module/utils/event.lua +86 -0
  103. package/package.json +38 -0
  104. package/tsconfig.browser.json +4 -0
  105. package/tsconfig.json +7 -0
  106. package/tsconfig.node.json +5 -0
  107. package/web/index.tsx +0 -0
  108. package/webpack.config.js +29 -0
@@ -0,0 +1,909 @@
1
+ --[[-- Gui Module - Warp List
2
+ - Adds a warp list gui which allows players to add and remove warp points
3
+ @gui Warps-List
4
+ @alias warp_list
5
+ ]]
6
+
7
+ local ExpUtil = require("modules/exp_util")
8
+ local Gui = require("modules/exp_gui")
9
+ local Datastore = require("modules.exp_legacy.expcore.datastore") --- @dep expcore.datastore
10
+ local Storage = require("modules/exp_util/storage")
11
+ local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
12
+ local Roles = require("modules/exp_roles")
13
+ local Colors = require("modules/exp_util/include/color")
14
+ local config = require("modules.exp_legacy.config.gui.warps") --- @dep config.gui.warps
15
+ local Warps = require("modules.exp_legacy.modules.control.warps") --- @dep modules.control.warps
16
+
17
+ local format_time = ExpUtil.format_time_factory_locale{ format = "short", hours = true, minutes = true }
18
+
19
+ --- Stores all data for the warp gui
20
+ local WrapGuiData = Datastore.connect("WrapGuiData")
21
+ WrapGuiData:set_serializer(Datastore.name_serializer)
22
+ local PlayerInRange = WrapGuiData:combine("PlayerInRange")
23
+ PlayerInRange:set_default(false)
24
+ local PlayerCooldown = WrapGuiData:combine("PlayerCooldown")
25
+ PlayerCooldown:set_default(0)
26
+
27
+ --- Table that stores a boolean value of weather to keep the warp gui open
28
+ local keep_gui_open = {}
29
+ Storage.register(keep_gui_open, function(tbl)
30
+ keep_gui_open = tbl
31
+ end)
32
+
33
+ --- Styles used for sprite buttons
34
+ local Styles = {
35
+ sprite22 = { height = 22, width = 22, padding = -2 },
36
+ sprite32 = { height = 32, width = 32, left_margin = 1 },
37
+ }
38
+ --- Status icon of a warp
39
+ local warp_status_icons = {
40
+ cooldown = "[img=utility/multiplayer_waiting_icon]",
41
+ not_available = "[img=utility/set_bar_slot]",
42
+ bypass = "[img=utility/side_menu_bonus_icon]",
43
+ current = "[img=utility/side_menu_map_icon]",
44
+ connected = "[img=utility/logistic_network_panel_white]",
45
+ different = "[img=utility/warning_white]",
46
+ }
47
+
48
+ --- Returns if a player is allowed to edit the given warp
49
+ --- If a player is allowed to use the edit buttons
50
+ local function check_player_permissions(player, action, warp)
51
+ -- Check if the action is allow edit and then check bypass settings
52
+ if action == "allow_edit_warp" then
53
+ -- Check if the warp is the spawn then it cant be edited
54
+ local spawn_id = Warps.get_spawn_warp_id(player.force.name)
55
+ if spawn_id == warp.warp_id then
56
+ return false
57
+ end
58
+
59
+ -- Check if the player being the last to edit will override existing permisisons
60
+ if config.user_can_edit_own_warps and warp.last_edit_name == player.name then
61
+ return true
62
+ end
63
+ end
64
+
65
+ -- Check player has permission based on value in the config
66
+ local action_config = config[action]
67
+ if action_config == "all" then
68
+ return true
69
+ elseif action_config == "admin" then
70
+ return player.admin
71
+ elseif action_config == "exp_roles" then
72
+ return Roles.player_has_permission(player, config["exp_roles_" .. action])
73
+ end
74
+
75
+ -- Return false as all other conditions have not been met
76
+ return false
77
+ end
78
+
79
+ --- Will add a new warp to the list, checks if the player is too close to an existing one
80
+ -- @element add_new_warp
81
+ local add_new_warp = Gui.define("add_new_warp")
82
+ :draw{
83
+ type = "sprite-button",
84
+ sprite = "utility/add",
85
+ tooltip = { "warp-list.add-tooltip" },
86
+ style = "shortcut_bar_button",
87
+ name = Gui.from_name,
88
+ }
89
+ :style(Styles.sprite22)
90
+ :on_click(function(def, player, element)
91
+ if player.controller_type ~= defines.controllers.character then return end
92
+ -- Add the new warp
93
+ local force_name = player.force.name
94
+ local surface = player.physical_surface
95
+ local position = player.physical_position
96
+
97
+ -- Check if the warp is too close to water
98
+ local water_tiles = surface.find_tiles_filtered{ collision_mask = "water_tile", radius = config.standard_proximity_radius + 1, position = position }
99
+ if #water_tiles > 0 then
100
+ player.print(
101
+ { "expcore-commands.command-fail", { "warp-list.too-close-to-water", config.standard_proximity_radius + 1 } },
102
+ { color = Colors.orange_red }
103
+ )
104
+ if game.player then game.player.play_sound{ path = "utility/wire_pickup" } end
105
+ for _, tile in pairs(water_tiles) do
106
+ rendering.draw_sprite{
107
+ sprite = "utility/rail_path_not_possible",
108
+ x_scale = 0.5,
109
+ y_scale = 0.5,
110
+ target = tile.position,
111
+ surface = surface,
112
+ players = { player },
113
+ time_to_live = 60,
114
+ }
115
+ end
116
+
117
+ return
118
+ end
119
+
120
+ -- Check if there are player entities in the way (has a bigger radius because the enities that can be placed by a player are larger)
121
+ local entities = surface.find_entities_filtered{
122
+ radius = config.standard_proximity_radius + 2.5,
123
+ position = position,
124
+ collision_mask = {
125
+ "item", "object", "player", "water_tile",
126
+ },
127
+ }
128
+ -- Remove 1 because that is the current player
129
+ if #entities > 1 then
130
+ player.print(
131
+ { "expcore-commands.command-fail", { "warp-list.too-close-to-entities", config.standard_proximity_radius + 2.5 } },
132
+ { color = Colors.orange_red }
133
+ )
134
+ if game.player then game.player.play_sound{ path = "utility/wire_pickup" } end
135
+ local character = player.character
136
+ for _, entity in pairs(entities) do
137
+ if entity ~= character then
138
+ rendering.draw_sprite{
139
+ sprite = "utility/rail_path_not_possible",
140
+ x_scale = 0.5,
141
+ y_scale = 0.5,
142
+ target = entity,
143
+ surface = surface,
144
+ players = { player },
145
+ time_to_live = 60,
146
+ }
147
+ end
148
+ end
149
+
150
+ return
151
+ end
152
+
153
+ -- Create the warp
154
+ local warp_id = Warps.add_warp(force_name, surface, position, player.name)
155
+ Warps.make_warp_tag(warp_id)
156
+ Warps.make_warp_area(warp_id)
157
+ end)
158
+
159
+ --- Warp icon button, this will trigger a warp when the player is able to
160
+ -- @element warp_icon_button
161
+ local warp_icon_button = Gui.define("warp_icon_button")
162
+ :draw(function(def, parent, warp)
163
+ local warp_position = warp.position
164
+
165
+ -- The SpritePath type is not the same as the SignalID type
166
+ local sprite = warp.icon.type .. "/" .. warp.icon.name
167
+ if warp.icon.type == "virtual" then
168
+ sprite = "virtual-signal/" .. warp.icon.name
169
+ end
170
+
171
+ -- Draw the element
172
+ return parent.add{
173
+ type = "sprite-button",
174
+ sprite = sprite,
175
+ name = def.name,
176
+ tooltip = { "warp-list.goto-tooltip", warp_position.x, warp_position.y },
177
+ style = "slot_button",
178
+ }
179
+ end)
180
+ :style(Styles.sprite32)
181
+ :on_click(function(def, player, element)
182
+ if element.type == "choose-elem-button" then return end
183
+ local warp_id = assert(element.parent).caption
184
+ Warps.teleport_player(warp_id, player)
185
+
186
+ -- Reset the warp cooldown if the player does not have unlimited warps
187
+ if not check_player_permissions(player, "bypass_warp_cooldown") then
188
+ PlayerCooldown:set(player, config.update_smoothing * config.cooldown_duration)
189
+ end
190
+
191
+ PlayerInRange:set(player, warp_id)
192
+ end)
193
+
194
+ --- The button that is visible when the warp is in edit state
195
+ -- @element warp_icon_editing
196
+ local warp_icon_editing = Gui.define("warp_icon_editing")
197
+ :draw(function(def, parent, warp)
198
+ return parent.add{
199
+ name = def.name,
200
+ type = "choose-elem-button",
201
+ elem_type = "signal",
202
+ signal = { type = warp.icon.type, name = warp.icon.name },
203
+ tooltip = { "warp-list.goto-edit" },
204
+ }
205
+ end)
206
+ :style(Styles.sprite32)
207
+
208
+ --- Warp label, visible if the player is not in edit state
209
+ -- @element warp_label
210
+ local warp_label = Gui.define("warp_label")
211
+ :draw(function(def, parent, warp)
212
+ local last_edit_name = warp.last_edit_name
213
+ local last_edit_time = warp.last_edit_time
214
+ -- Draw the element
215
+ return parent.add{
216
+ type = "label",
217
+ caption = warp.name,
218
+ tooltip = { "warp-list.last-edit", last_edit_name, format_time(last_edit_time) },
219
+ name = def.name,
220
+ }
221
+ end)
222
+ :style{
223
+ single_line = true,
224
+ left_padding = 2,
225
+ right_padding = 2,
226
+ horizontally_stretchable = true,
227
+ }
228
+ :on_click(function(def, player, element)
229
+ local warp_id = assert(element.parent).caption
230
+ local warp = Warps.get_warp(warp_id)
231
+ player.set_controller{ type = defines.controllers.remote, position = warp.position, surface = warp.surface }
232
+ end)
233
+
234
+ --- Warp status, visible if the player is not in edit state
235
+ --- This will show if the warp is connected or not
236
+ -- @element warp_status
237
+ local warp_status = Gui.define("warp_status")
238
+ :draw{
239
+ type = "label",
240
+ caption = "[img=utility/electricity_icon_unplugged]", -- Temporary icon
241
+ name = Gui.from_name,
242
+ }
243
+ :style{
244
+ -- When editing mode because textbox is larger the icon would move up.
245
+ top_padding = 1,
246
+ single_line = false,
247
+ }
248
+
249
+ --- Warp textfield, visible if the player is in edit state
250
+ -- @element warp_textfield
251
+ local warp_textfield = Gui.define("warp_textfield")
252
+ :draw(function(def, parent, warp)
253
+ -- Draw the element
254
+ return parent.add{
255
+ type = "textfield",
256
+ text = warp.name,
257
+ clear_and_focus_on_right_click = true,
258
+ name = def.name,
259
+ }
260
+ end)
261
+ :style{
262
+ -- Required fields to make it squashable and strechable.
263
+ minimal_width = 10,
264
+ maximal_width = 300,
265
+ horizontally_squashable = true,
266
+ horizontally_stretchable = true,
267
+ -- Other styling
268
+ height = 22,
269
+ padding = -2,
270
+ left_margin = 2,
271
+ right_margin = 2,
272
+ }
273
+ :on_confirmed(function(def, player, element)
274
+ local warp_id = assert(element.parent).caption
275
+ local warp_name = element.text
276
+ local warp_icon = assert(element.parent).parent["icon-" .. warp_id][warp_icon_editing.name].elem_value --[[@as SignalID]]
277
+ if warp_icon.type == nil then warp_icon.type = "item" end
278
+ Warps.set_editing(warp_id, player.name)
279
+ Warps.update_warp(warp_id, warp_name, warp_icon, player.name)
280
+ end)
281
+
282
+ --- Confirms the edit to name or icon of the warp
283
+ -- @element confirm_edit_button
284
+ local confirm_edit_button = Gui.define("confirm_edit_button")
285
+ :draw{
286
+ type = "sprite-button",
287
+ sprite = "utility/confirm_slot",
288
+ tooltip = { "warp-list.confirm-tooltip" },
289
+ style = "shortcut_bar_button_green",
290
+ name = Gui.from_name,
291
+ }
292
+ :style(Styles.sprite22)
293
+ :on_click(function(def, player, element)
294
+ local parent = assert(element.parent)
295
+ local grandparent = assert(parent.parent)
296
+ local warp_id = parent.caption --[[@as string]]
297
+ local name_flow = grandparent["name-" .. warp_id]
298
+ local icon_flow = grandparent["icon-" .. warp_id]
299
+ local warp_name = name_flow[warp_textfield.name].text
300
+ local warp_icon = icon_flow[warp_icon_editing.name].elem_value --[[@as SignalID]]
301
+ if warp_icon.type == nil then warp_icon.type = "item" end
302
+ Warps.set_editing(warp_id, player.name)
303
+ Warps.update_warp(warp_id, warp_name, warp_icon, player.name)
304
+ end)
305
+
306
+ --- Cancels the editing changes of the selected warp name or icon
307
+ -- @element cancel_edit_button
308
+ local cancel_edit_button = Gui.define("cancel_edit_button")
309
+ :draw{
310
+ type = "sprite-button",
311
+ sprite = "utility/close_black",
312
+ tooltip = { "warp-list.cancel-tooltip" },
313
+ style = "shortcut_bar_button_red",
314
+ name = Gui.from_name,
315
+ }
316
+ :style(Styles.sprite22)
317
+ :on_click(function(def, player, element)
318
+ local warp_id = assert(element.parent).caption
319
+ -- Check if this is the first edit, if so remove the warp.
320
+ local warp = Warps.get_warp(warp_id)
321
+ if warp.updates == 1 then
322
+ Warps.remove_warp(warp_id)
323
+ return
324
+ end
325
+ Warps.set_editing(warp_id, player.name)
326
+ end)
327
+
328
+ --- Removes a warp from the list, including the physical area and map tag
329
+ -- @element remove_warp_button
330
+ local remove_warp_button = Gui.define("remove_warp_button")
331
+ :draw{
332
+ type = "sprite-button",
333
+ sprite = "utility/trash",
334
+ tooltip = { "warp-list.remove-tooltip" },
335
+ style = "shortcut_bar_button_red",
336
+ name = Gui.from_name,
337
+ }
338
+ :style(Styles.sprite22)
339
+ :on_click(function(def, player, element)
340
+ local warp_id = assert(element.parent).caption
341
+ Warps.remove_warp(warp_id)
342
+ end)
343
+
344
+ --- Opens edit mode for the warp
345
+ -- @element edit_warp_button
346
+ local edit_warp_button = Gui.define("edit_warp_button")
347
+ :draw{
348
+ type = "sprite-button",
349
+ sprite = "utility/rename_icon",
350
+ tooltip = { "warp-list.edit-tooltip-none" },
351
+ style = "shortcut_bar_button",
352
+ name = Gui.from_name,
353
+ }
354
+ :style(Styles.sprite22)
355
+ :on_click(function(def, player, element)
356
+ local warp_id = assert(element.parent).caption
357
+ Warps.set_editing(warp_id, player.name, true)
358
+ end)
359
+
360
+ local update_all_warp_elements
361
+ --- Set of three elements which make up each row of the warp table
362
+ -- @element add_warp_elements
363
+ local add_warp_elements = Gui.define("add_warp_elements")
364
+ :draw(function(_, parent, warp)
365
+ -- Add icon flow, this will contain the warp button and warp icon edit button
366
+ local icon_flow = parent.add{
367
+ name = "icon-" .. warp.warp_id,
368
+ type = "flow",
369
+ caption = warp.warp_id,
370
+ }
371
+ icon_flow.style.padding = 0
372
+
373
+ -- Add the button and the icon edit button
374
+ warp_icon_button(icon_flow, warp)
375
+ warp_icon_editing(icon_flow, warp)
376
+
377
+ -- Add name flow, this will contain the warp label and textbox
378
+ local name_flow = parent.add{
379
+ type = "flow",
380
+ name = "name-" .. warp.warp_id,
381
+ caption = warp.warp_id,
382
+ }
383
+ name_flow.style.padding = 0
384
+
385
+ -- Add the label and textfield of the warp
386
+ warp_status(name_flow)
387
+ warp_label(name_flow, warp)
388
+ warp_textfield(name_flow, warp)
389
+
390
+ -- Add button flow, this will contain buttons to manage this specific warp
391
+ local button_flow = parent.add{
392
+ type = "flow",
393
+ name = "button-" .. warp.warp_id,
394
+ caption = warp.warp_id,
395
+ }
396
+ button_flow.style.padding = 0
397
+
398
+ -- Add both edit state buttons
399
+ confirm_edit_button(button_flow)
400
+ cancel_edit_button(button_flow)
401
+ edit_warp_button(button_flow)
402
+ remove_warp_button(button_flow)
403
+
404
+ return Gui.no_return()
405
+ end)
406
+
407
+ -- Removes the three elements that are added as part of the warp base
408
+ local function remove_warp_elements(parent, warp_id)
409
+ Gui.destroy_if_valid(parent["icon-" .. warp_id])
410
+ Gui.destroy_if_valid(parent["name-" .. warp_id])
411
+ Gui.destroy_if_valid(parent["button-" .. warp_id])
412
+ end
413
+
414
+ --- This timer controls when a player is able to warp, eg every 60 seconds
415
+ -- @element warp_timer
416
+ local warp_timer = Gui.define("warp_timer")
417
+ :draw{
418
+ type = "progressbar",
419
+ name = Gui.from_name,
420
+ tooltip = { "warp-list.timer-tooltip-zero", config.cooldown_duration },
421
+ minimum_value = 0,
422
+ maximum_value = config.cooldown_duration * config.update_smoothing,
423
+ }
424
+ :style{
425
+ horizontally_stretchable = true,
426
+ color = Colors.light_blue,
427
+ }
428
+
429
+ local warp_list_container
430
+
431
+ -- Helper function to style and enable or disable a button element
432
+ local function update_warp_elements(element, warp, warp_player_is_on, on_cooldown, bypass_warp_proximity)
433
+ -- Check if button element is valid
434
+ if not element or not element.valid then return end
435
+
436
+ local label_style = assert(element.parent).parent["name-" .. warp.warp_id][warp_label.name].style
437
+ local warp_status_element = assert(element.parent).parent["name-" .. warp.warp_id][warp_status.name]
438
+
439
+ -- If player is not on a warp
440
+ if not warp_player_is_on then
441
+ -- If player is allowed to warp without being on a warp. If not then disable the warp location
442
+ if bypass_warp_proximity then
443
+ local position = warp.position
444
+ element.tooltip = { "warp-list.goto-bypass", position.x, position.y }
445
+ element.enabled = true
446
+ warp_status_element.tooltip = { "warp-list.goto-bypass", position.x, position.y }
447
+ warp_status_element.caption = warp_status_icons.bypass
448
+ label_style.font = "default-semibold"
449
+ else
450
+ element.tooltip = { "warp-list.goto-disabled" }
451
+ element.enabled = false
452
+ warp_status_element.tooltip = { "warp-list.goto-disabled" }
453
+ warp_status_element.caption = warp_status_icons.not_available
454
+ label_style.font = "default"
455
+ end
456
+ -- If player is on the warp that is being updated
457
+ elseif warp_player_is_on.warp_id == warp.warp_id then
458
+ element.tooltip = { "warp-list.goto-same-warp" }
459
+ element.enabled = false
460
+ warp_status_element.tooltip = { "warp-list.goto-same-warp" }
461
+ warp_status_element.caption = warp_status_icons.current
462
+ label_style.font = "default"
463
+ -- If player is on cooldown
464
+ elseif on_cooldown then
465
+ element.tooltip = { "warp-list.goto-cooldown" }
466
+ element.enabled = false
467
+ warp_status_element.tooltip = { "warp-list.goto-cooldown" }
468
+ warp_status_element.caption = warp_status_icons.cooldown
469
+ label_style.font = "default"
470
+ else
471
+ -- If the warp the player is standing on is the same as the warp that is being updated
472
+ local warp_electric_network_id = warp.electric_pole and warp.electric_pole.electric_network_id or -1
473
+ local player_warp_electric_network_id = warp_player_is_on.electric_pole and warp_player_is_on.electric_pole.electric_network_id or -2
474
+ if warp_electric_network_id == player_warp_electric_network_id then
475
+ local position = warp.position
476
+ element.tooltip = { "warp-list.goto-tooltip", position.x, position.y }
477
+ element.enabled = true
478
+ warp_status_element.tooltip = { "warp-list.goto-tooltip", position.x, position.y }
479
+ warp_status_element.caption = warp_status_icons.connected
480
+ label_style.font = "default-semibold"
481
+ -- If the warp is not on the same network but the player is allowed to warp without being on a warp
482
+ elseif bypass_warp_proximity then
483
+ local position = warp.position
484
+ element.tooltip = { "warp-list.goto-bypass-different-network", position.x, position.y }
485
+ element.enabled = true
486
+ warp_status_element.tooltip = { "warp-list.goto-bypass-different-network", position.x, position.y }
487
+ warp_status_element.caption = warp_status_icons.bypass
488
+ label_style.font = "default-semibold"
489
+ -- If the warp is on a different network than the one the player is standing on
490
+ else
491
+ element.tooltip = { "warp-list.goto-different-network" }
492
+ element.enabled = false
493
+ warp_status_element.tooltip = { "warp-list.goto-different-network" }
494
+ warp_status_element.caption = warp_status_icons.different
495
+ label_style.font = "default"
496
+ end
497
+ end
498
+ end
499
+
500
+ --- Update the warp buttons for a player
501
+ function update_all_warp_elements(player, timer, warp_id)
502
+ -- Get the warp table
503
+ local container = Gui.get_left_element(warp_list_container, player)
504
+ local scroll_table = container.frame.scroll.table
505
+
506
+ -- Check if the player is currently on cooldown
507
+ timer = timer or PlayerCooldown:get(player)
508
+ local on_cooldown = timer > 0
509
+ -- Get the warp the player is on
510
+ warp_id = warp_id or PlayerInRange:get(player)
511
+ local warp_player_is_on = warp_id and Warps.get_warp(warp_id) or nil
512
+ -- Check player permission
513
+ local bypass_warp_proximity = check_player_permissions(player, "bypass_warp_proximity")
514
+
515
+ -- Change the enabled state of the warp buttons
516
+ local warp_ids = Warps.get_force_warp_ids(player.force.name)
517
+ for _, next_warp_id in pairs(warp_ids) do
518
+ local element = scroll_table["icon-" .. next_warp_id][warp_icon_button.name]
519
+ local next_warp = Warps.get_warp(next_warp_id)
520
+ update_warp_elements(element, next_warp, warp_player_is_on, on_cooldown, bypass_warp_proximity)
521
+ end
522
+ end
523
+
524
+ --- Updates a warp for a player
525
+ local function update_warp(player, warp_table, warp_id)
526
+ local warp = Warps.get_warp(warp_id)
527
+
528
+ -- If the warp no longer exists then remove the warp elements from the warp table
529
+ if not warp then
530
+ remove_warp_elements(warp_table, warp_id)
531
+ return
532
+ end
533
+
534
+ -- Create the warp elements if they do not already exist
535
+ if not warp_table["icon-" .. warp_id] then
536
+ add_warp_elements(warp_table, warp)
537
+ end
538
+ local icon_flow = warp_table["icon-" .. warp_id]
539
+ local name_flow = warp_table["name-" .. warp_id]
540
+ local button_flow = warp_table["button-" .. warp_id]
541
+
542
+ -- Create local references to the elements for this warp
543
+ local warp_icon_element = icon_flow[warp_icon_button.name]
544
+ local warp_icon_edit_element = icon_flow[warp_icon_editing.name]
545
+
546
+ local label_element = name_flow[warp_label.name]
547
+ local textfield_element = name_flow[warp_textfield.name]
548
+
549
+ local cancel_edit_element = button_flow[cancel_edit_button.name]
550
+ local confirm_edit_element = button_flow[confirm_edit_button.name]
551
+
552
+ local edit_warp_element = button_flow[edit_warp_button.name]
553
+ local remove_warp_element = button_flow[remove_warp_button.name]
554
+
555
+ -- Hide the edit button if the player is not allowed to edit the warp
556
+ local player_allowed_edit = check_player_permissions(player, "allow_edit_warp", warp)
557
+ local players_editing = table.get_keys(warp.currently_editing)
558
+ edit_warp_element.visible = player_allowed_edit
559
+
560
+ -- Set the tooltip of the edit button
561
+ if #players_editing > 0 then
562
+ edit_warp_element.hovered_sprite = "utility/warning_icon"
563
+ edit_warp_element.tooltip = { "warp-list.edit-tooltip", table.concat(players_editing, ", ") }
564
+ else
565
+ edit_warp_element.hovered_sprite = edit_warp_element.sprite
566
+ edit_warp_element.tooltip = { "warp-list.edit-tooltip-none" }
567
+ end
568
+
569
+ -- Set the visibility of the warp elements based on whether the user is editing or not
570
+ local player_is_editing = warp.currently_editing[player.name]
571
+ if player_is_editing then
572
+ -- Set the icon elements visibility
573
+ warp_icon_element.visible = false
574
+ warp_icon_edit_element.visible = true
575
+ -- Set the name elements visibility
576
+ label_element.visible = false
577
+ textfield_element.visible = true
578
+ textfield_element.focus()
579
+ warp_table.parent.scroll_to_element(textfield_element, "top-third")
580
+ -- Set the edit buttons
581
+ cancel_edit_element.visible = true
582
+ confirm_edit_element.visible = true
583
+ -- Set the warp buttons
584
+ edit_warp_element.visible = false
585
+ remove_warp_element.visible = false
586
+ else
587
+ -- Set the icon elements visibility
588
+ warp_icon_element.visible = true
589
+ -- The SpritePath type is not the same as the SignalID type
590
+ local sprite = warp.icon.type .. "/" .. warp.icon.name
591
+ if warp.icon.type == "virtual" then
592
+ sprite = "virtual-signal/" .. warp.icon.name
593
+ end
594
+ warp_icon_element.sprite = sprite
595
+ -- Set icon edit to the warps icon
596
+ warp_icon_edit_element.elem_value = warp.icon
597
+ warp_icon_edit_element.visible = false
598
+ -- Set the name elements visibility
599
+ label_element.visible = true
600
+ label_element.caption = warp.name
601
+ textfield_element.visible = false
602
+ textfield_element.text = warp.name
603
+ -- Set the edit buttons
604
+ cancel_edit_element.visible = false
605
+ confirm_edit_element.visible = false
606
+ -- Set the warp buttons
607
+ edit_warp_element.visible = true and player_allowed_edit
608
+ remove_warp_element.visible = true and player_allowed_edit
609
+ end
610
+
611
+ local timer = PlayerCooldown:get(player)
612
+ local current_warp_id = PlayerInRange:get(player)
613
+ local to_warp = current_warp_id and Warps.get_warp(current_warp_id) or nil
614
+ local bypass_warp_proximity = check_player_permissions(player, "bypass_warp_proximity")
615
+ update_warp_elements(warp_icon_element, warp, to_warp, timer > 0, bypass_warp_proximity)
616
+ end
617
+
618
+ -- Update all the warps for a player
619
+ local function update_all_warps(player, warp_table)
620
+ local warp_ids = Warps.get_force_warp_ids(player.force.name)
621
+ warp_table.clear()
622
+ for _, warp_id in ipairs(warp_ids) do
623
+ update_warp(player, warp_table, warp_id)
624
+ end
625
+ end
626
+
627
+ -- Update all warps for all players on a force
628
+ local function update_all_warp_force(force)
629
+ local warp_ids = Warps.get_force_warp_ids(force.name)
630
+ for _, player in pairs(force.connected_players) do
631
+ local container = Gui.get_left_element(warp_list_container, player)
632
+ local warp_table = container.frame.scroll.table
633
+
634
+ warp_table.clear() -- Needed to re-sort the warps
635
+ for _, warp_id in ipairs(warp_ids) do
636
+ update_warp(player, warp_table, warp_id)
637
+ end
638
+ end
639
+ end
640
+
641
+ --- Main warp list container for the left flow
642
+ -- @element warp_list_container
643
+ warp_list_container = Gui.define("warp_list_container")
644
+ :draw(function(def, parent)
645
+ local player = Gui.get_player(parent)
646
+ -- Check if user has permission to add warps
647
+ local allow_add_warp = check_player_permissions(player, "allow_add_warp")
648
+
649
+ -- Draw the internal container
650
+ local container = Gui.elements.container(parent, allow_add_warp and 268 or 220)
651
+
652
+ -- Draw the header
653
+ local header = Gui.elements.header(container, {
654
+ name = "header",
655
+ caption = { "warp-list.main-caption" },
656
+ tooltip = {
657
+ "warp-list.sub-tooltip",
658
+ config.cooldown_duration,
659
+ config.standard_proximity_radius,
660
+ { "warp-list.sub-tooltip-current", warp_status_icons.current },
661
+ { "warp-list.sub-tooltip-connected", warp_status_icons.connected },
662
+ { "warp-list.sub-tooltip-different", warp_status_icons.different },
663
+ { "warp-list.sub-tooltip-cooldown", warp_status_icons.cooldown },
664
+ { "warp-list.sub-tooltip-not_available", warp_status_icons.not_available },
665
+ { "warp-list.sub-tooltip-bypass", warp_status_icons.bypass },
666
+ },
667
+ })
668
+
669
+ -- Draw the new warp button
670
+ local add_new_warp_element = add_new_warp(header)
671
+ add_new_warp_element.visible = allow_add_warp
672
+
673
+ -- Draw the scroll table for the warps
674
+ local scroll_table = Gui.elements.scroll_table(container, 250, 3, "scroll")
675
+ -- Set the scroll panel to always show the scrollbar (not doing this will result in a changing gui size)
676
+ assert(scroll_table.parent).vertical_scroll_policy = "always"
677
+
678
+ -- Change the style of the scroll table
679
+ local scroll_table_style = scroll_table.style
680
+ scroll_table_style.top_cell_padding = 3
681
+ scroll_table_style.bottom_cell_padding = 3
682
+
683
+ -- Draw the warp cooldown progress bar
684
+ local warp_timer_element = warp_timer(container)
685
+
686
+ -- Change the progress of the warp timer
687
+ local timer = PlayerCooldown:get(player)
688
+ if timer > 0 then
689
+ warp_timer_element.tooltip = { "warp-list.timer-tooltip", math.floor(timer / config.update_smoothing) }
690
+ warp_timer_element.value = 1 - (timer / config.update_smoothing / config.cooldown_duration)
691
+ else
692
+ warp_timer_element.tooltip = { "warp-list.timer-tooltip-zero", config.cooldown_duration }
693
+ warp_timer_element.value = 1
694
+ end
695
+
696
+ -- Add any existing warps
697
+ update_all_warps(player, scroll_table)
698
+
699
+ -- Return the external container
700
+ return container.parent
701
+ end)
702
+
703
+ --- Add the element to the left flow with a toolbar button
704
+ Gui.add_left_element(warp_list_container, false)
705
+ Gui.toolbar.create_button{
706
+ name = "warp_list_toggle",
707
+ left_element = warp_list_container,
708
+ sprite = config.default_icon.type .. "/" .. config.default_icon.name,
709
+ tooltip = { "warp-list.main-tooltip" },
710
+ visible = function(player, element)
711
+ return Roles.player_has_permission(player, "exp_scenario.gui.warp_list")
712
+ end
713
+ }:on_click(function(def, player, element)
714
+ -- Set gui keep open state for player that clicked the button: true if visible, false if invisible
715
+ keep_gui_open[player.name] = Gui.toolbar.get_button_toggled_state(def, player)
716
+ end)
717
+
718
+ --- When the name of a warp is updated this is triggered
719
+ Warps.on_update(function(_, warp, old_warp)
720
+ -- Get the force to update, warp is nil when removed
721
+ if warp then
722
+ update_all_warp_force(game.forces[warp.force_name])
723
+ else
724
+ update_all_warp_force(game.forces[old_warp.force_name])
725
+ end
726
+ end)
727
+
728
+ --- When the player leaves or enters range of a warp this is triggered
729
+ PlayerInRange:on_update(function(player_name, warp_id)
730
+ local player = game.players[player_name]
731
+
732
+ -- Change if the frame is visible based on if the player is in range
733
+ if not keep_gui_open[player.name] then
734
+ Gui.toolbar.set_left_element_visible_state(warp_list_container, player, warp_id ~= nil)
735
+ end
736
+
737
+ update_all_warp_elements(player, nil, warp_id)
738
+ end)
739
+
740
+ --- Update the warp cooldown progress bars to match the current cooldown
741
+ PlayerCooldown:on_update(function(player_name, player_cooldown)
742
+ -- Get the progress bar element
743
+ local player = game.players[player_name]
744
+ local container = Gui.get_left_element(warp_list_container, player)
745
+ local warp_timer_element = container.frame[warp_timer.name]
746
+
747
+ -- Set the progress
748
+ if player_cooldown and player_cooldown > 0 then
749
+ warp_timer_element.tooltip = { "warp-list.timer-tooltip", math.floor(player_cooldown / config.update_smoothing) }
750
+ warp_timer_element.value = 1 - (player_cooldown / config.update_smoothing / config.cooldown_duration)
751
+ else
752
+ warp_timer_element.tooltip = { "warp-list.timer-tooltip-zero", config.cooldown_duration }
753
+ warp_timer_element.value = 1
754
+ end
755
+
756
+ -- Trigger update of buttons if cooldown is now 0
757
+ if player_cooldown == 0 then
758
+ update_all_warp_elements(player, player_cooldown, nil)
759
+ end
760
+ end)
761
+
762
+ --- Handles updating the timer and checking distance from a warp
763
+ local r2 = config.standard_proximity_radius ^ 2
764
+ local rs2 = config.spawn_proximity_radius ^ 2
765
+ local mr2 = config.minimum_distance ^ 2
766
+ Event.on_nth_tick(math.floor(60 / config.update_smoothing), function()
767
+ PlayerCooldown:update_all(function(_, player_cooldown)
768
+ if player_cooldown > 0 then return player_cooldown - 1 end
769
+ end)
770
+
771
+ local force_warps = {}
772
+ local warps = {}
773
+ for _, player in pairs(game.connected_players) do
774
+ local was_in_range = PlayerInRange:get(player)
775
+
776
+ -- Get the ids of all the warps on the players force
777
+ local force_name = player.force.name
778
+ local warp_ids = force_warps[force_name]
779
+ if not warp_ids then
780
+ warp_ids = Warps.get_force_warp_ids(force_name)
781
+ force_warps[force_name] = warp_ids
782
+ end
783
+
784
+ -- Check if the force has any warps
785
+ local closest_warp = nil --- @type { warp_id: string, name: string }?
786
+ local closest_distance = nil
787
+ if #warp_ids > 0 then
788
+ local surface = player.surface
789
+ local pos = player.physical_position
790
+ local px, py = pos.x, pos.y
791
+
792
+ -- Loop over each warp
793
+ for _, warp_id in ipairs(warp_ids) do
794
+ -- Check if warp id is cached
795
+ local warp = warps[warp_id]
796
+ if not warp then
797
+ warp = Warps.get_warp(warp_id)
798
+ warps[warp_id] = warp
799
+ end
800
+
801
+ -- Check if the player is within range
802
+ local warp_pos = warp.position
803
+ if warp.surface == surface then
804
+ local dx, dy = px - warp_pos.x, py - warp_pos.y
805
+ local dist = (dx * dx) + (dy * dy)
806
+ if closest_distance == nil or dist < closest_distance then
807
+ closest_warp = warp
808
+ closest_distance = dist
809
+ if dist < r2 then break end
810
+ end
811
+ end
812
+ end
813
+
814
+ -- Check the dist to the closest warp
815
+ local in_range = closest_warp ~= nil and (closest_warp.warp_id == warp_ids.spawn and closest_distance < rs2 or closest_distance < r2)
816
+ if was_in_range and not in_range then
817
+ PlayerInRange:set(player, nil)
818
+ elseif not was_in_range and in_range then
819
+ --- @cast closest_warp -nil
820
+ PlayerInRange:set(player, closest_warp.warp_id)
821
+ end
822
+
823
+ -- Change the enabled state of the add warp button
824
+ local container = Gui.get_left_element(warp_list_container, player)
825
+ local add_warp_element = container.frame.header[add_new_warp.name]
826
+ local old_closest_warp_name = add_warp_element.tooltip[2] or closest_warp and closest_warp.name
827
+ local was_able_to_make_warp = add_warp_element.enabled
828
+ local can_make_warp = closest_distance == nil or closest_distance > mr2
829
+ if can_make_warp and not was_able_to_make_warp then
830
+ add_warp_element.enabled = true
831
+ add_warp_element.tooltip = { "warp-list.add-tooltip" }
832
+ elseif not can_make_warp and was_able_to_make_warp or closest_warp and (old_closest_warp_name ~= closest_warp.name) then
833
+ --- @cast closest_warp -nil
834
+ add_warp_element.enabled = false
835
+ add_warp_element.tooltip = { "warp-list.too-close", closest_warp.name }
836
+ end
837
+ end
838
+ end
839
+ end)
840
+
841
+ --- When a player is created make sure that there is a spawn warp created
842
+ Event.add(defines.events.on_player_created, function(event)
843
+ -- If the force has no spawn then make a spawn warp
844
+ local player = game.players[event.player_index]
845
+ local force = player.force --[[@as LuaForce]]
846
+ local spawn_id = Warps.get_spawn_warp_id(force.name)
847
+ if not spawn_id then
848
+ local spawn_position = force.get_spawn_position(player.surface)
849
+ spawn_id = Warps.add_warp(force.name, player.surface, spawn_position, nil, "Spawn")
850
+ Warps.set_spawn_warp(spawn_id, force)
851
+ Warps.make_warp_tag(spawn_id)
852
+
853
+ local entities = player.surface.find_entities_filtered{ type = "electric-pole", position = spawn_position, radius = 20, limit = 1 }
854
+ if entities and entities[1] then
855
+ local warp = Warps.get_warp(spawn_id)
856
+ warp.electric_pole = entities[1]
857
+ end
858
+ end
859
+ end)
860
+
861
+ --- Update the warps when the player joins
862
+ Event.add(defines.events.on_player_joined_game, function(event)
863
+ local player = game.players[event.player_index]
864
+ local container = Gui.get_left_element(warp_list_container, player)
865
+ local scroll_table = container.frame.scroll.table
866
+ update_all_warps(player, scroll_table)
867
+ end)
868
+
869
+ --- Makes sure the right buttons are present when roles change
870
+ local function role_update_event(event)
871
+ local player = game.players[event.player_index]
872
+ local frame = Gui.get_left_element(warp_list_container, player).frame
873
+
874
+ -- Check if user has permission to add warps
875
+ local allow_add_warp = check_player_permissions(player, "allow_add_warp")
876
+ -- Update container size depending on whether the player is allowed to add warps
877
+ assert(frame.parent).style.width = allow_add_warp and 268 or 220
878
+
879
+ -- Update the warps, in case the user can now edit them
880
+ local scroll_table = frame.scroll.table
881
+ update_all_warps(player, scroll_table)
882
+
883
+ -- Update the new warp button in case the user can now add them
884
+ local add_new_warp_element = frame.header[add_new_warp.name]
885
+ add_new_warp_element.visible = allow_add_warp
886
+ end
887
+
888
+ Event.add(Roles.events.on_player_roles_changed, role_update_event)
889
+
890
+ --- When a chart tag is removed or edited make sure it is not one that belongs to a warp
891
+ local function maintain_tag(event)
892
+ if not event.player_index then return end
893
+ local tag = event.tag
894
+ local force_name = event.force.name
895
+ local warp_ids = Warps.get_force_warp_ids(force_name)
896
+ for _, warp_id in pairs(warp_ids) do
897
+ local warp = Warps.get_warp(warp_id)
898
+ local warp_tag = warp.tag
899
+ if not warp_tag or not warp_tag.valid or warp_tag == tag then
900
+ if event.name == defines.events.on_chart_tag_removed then
901
+ warp.tag = nil
902
+ end
903
+ Warps.make_warp_tag(warp_id)
904
+ end
905
+ end
906
+ end
907
+
908
+ Event.add(defines.events.on_chart_tag_modified, maintain_tag)
909
+ Event.add(defines.events.on_chart_tag_removed, maintain_tag)