@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,807 @@
1
+ --[[-- Control Module - vlayer
2
+ - Adds a virtual layer to store power to save space.
3
+ @control vlayer
4
+ @alias vlayer
5
+ ]]
6
+
7
+ local ExpUtil = require("modules/exp_util")
8
+ local Storage = require("modules/exp_util/storage")
9
+ local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
10
+ local config = require("modules.exp_legacy.config.vlayer") --- @dep config.vlayer
11
+
12
+ local mega = 1000000
13
+
14
+ local vlayer = {}
15
+ local vlayer_data = {
16
+ entity_interfaces = {
17
+ energy = {}, --- @type LuaEntity[]
18
+ circuit = {}, --- @type LuaEntity[]
19
+ storage_input = {}, --- @type LuaEntity[]
20
+ storage_output = {}, --- @type LuaEntity[]
21
+ },
22
+ properties = {
23
+ total_surface_area = 0,
24
+ used_surface_area = 0,
25
+ total_production = 0,
26
+ production = 0,
27
+ discharge = 0,
28
+ capacity = 0,
29
+ },
30
+ storage = {
31
+ items = {}, --- @type table<string, number>
32
+ power_items = {}, --- @type table<string, { value: number, count: number }>
33
+ energy = 0,
34
+ unallocated = {}, --- @type table<string, number>
35
+ },
36
+ surface = table.deep_copy(config.surface),
37
+ }
38
+
39
+ Storage.register(vlayer_data, function(tbl)
40
+ vlayer_data = tbl
41
+ end)
42
+
43
+ for name, properties in pairs(config.allowed_items) do
44
+ properties.modded = false
45
+
46
+ if properties.power then
47
+ vlayer_data.storage.power_items[name] = {
48
+ value = assert(properties.fuel_value) * 1000000,
49
+ count = 0,
50
+ }
51
+ end
52
+ end
53
+
54
+ -- For all modded items, create a config for them
55
+ for item_name, properties in pairs(config.modded_items) do
56
+ local base_properties = config.allowed_items[properties.base_game_equivalent]
57
+ local m = properties.multiplier
58
+
59
+ config.allowed_items[item_name] = {
60
+ starting_value = properties.starting_value or 0,
61
+ required_area = base_properties.required_area or 0,
62
+ surface_area = (base_properties.surface_area or 0) * m,
63
+ production = (base_properties.production or 0) * m,
64
+ capacity = (base_properties.capacity or 0) * m,
65
+ modded = true,
66
+ }
67
+ end
68
+
69
+ --- Get all items in storage, do not modify
70
+ -- @treturn table a dictionary of all items stored in the vlayer
71
+ function vlayer.get_items()
72
+ return vlayer_data.storage.items
73
+ end
74
+
75
+ --- Get all unallocated items in storage
76
+ -- @treturn table a dictionary of all unallocated items stored in the vlayer
77
+ function vlayer.get_unallocated_items()
78
+ return vlayer_data.storage.unallocated
79
+ end
80
+
81
+ --- Get all allocated items in storage
82
+ -- @treturn table a dictionary of all allocated items stored in the vlayer
83
+ function vlayer.get_allocated_items()
84
+ local r = {}
85
+
86
+ for k, v in pairs(vlayer_data.storage.items) do
87
+ r[k] = v
88
+
89
+ if vlayer_data.storage.unallocated[k] then
90
+ r[k] = r[k] - vlayer_data.storage.unallocated[k]
91
+ end
92
+ end
93
+
94
+ return r
95
+ end
96
+
97
+ --- Get the actual defecit of land
98
+ local function get_actual_land_defecit()
99
+ local n = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
100
+
101
+ for k, v in pairs(vlayer.get_unallocated_items()) do
102
+ n = n - (config.allowed_items[k].required_area * v)
103
+ end
104
+
105
+ return n
106
+ end
107
+
108
+ --- Get interface counts
109
+ -- @treturn table a dictionary of the vlayer interface counts
110
+ function vlayer.get_interface_counts()
111
+ local interfaces = vlayer_data.entity_interfaces
112
+
113
+ return {
114
+ energy = #interfaces.energy,
115
+ circuit = #interfaces.circuit,
116
+ storage_input = #interfaces.storage_input,
117
+ storage_output = #interfaces.storage_output,
118
+ }
119
+ end
120
+
121
+ --- Get interfaces
122
+ -- @treturn table a dictionary of the vlayer interfaces
123
+ function vlayer.get_interfaces()
124
+ local interfaces = vlayer_data.entity_interfaces
125
+
126
+ return {
127
+ energy = interfaces.energy,
128
+ circuit = interfaces.circuit,
129
+ storage_input = interfaces.storage_input,
130
+ storage_output = interfaces.storage_output,
131
+ }
132
+ end
133
+
134
+ --[[
135
+ 25,200 / 420 s
136
+ 昼 210秒 ソーラー効率100%
137
+ 夕方 84秒 1秒ごとにソーラー発電量が約1.2%ずつ下がり、やがて0%になる
138
+ 夜 42秒 ソーラー発電量が0%になる
139
+ 朝方 84秒 1秒ごとにソーラー発電量が約1.2%ずつ上がり、やがて100%になる
140
+
141
+ (surface.dawn) 0.75 18,900 Day 12,600 210s
142
+ 0.00 0 Noon
143
+ (surface.dusk) 0.25 6,300 Sunset 5,040 84s
144
+ (surface.evening) 0.45 11,340 Night 2,520 42s
145
+ (surface.morning) 0.55 13,860 Sunrise 5,040 84s
146
+ ]]
147
+
148
+ --- Get the power multiplier based on the surface time
149
+ local function get_production_multiplier()
150
+ local mul = vlayer_data.surface.solar_power_multiplier
151
+ local surface = vlayer_data.surface
152
+
153
+ if surface.always_day then
154
+ -- Surface is always day, so full production is used
155
+ return mul
156
+ end
157
+
158
+ if surface.darkness then
159
+ -- We are using a real surface, our config does not contain "darkness"
160
+ local brightness = 1 - surface.darkness
161
+
162
+ if brightness >= surface.min_brightness then
163
+ return mul * (brightness - surface.min_brightness) / (1 - surface.min_brightness)
164
+ else
165
+ return 0
166
+ end
167
+ end
168
+
169
+ -- Caused by using a set config rather than a surface
170
+ local tick = game.tick % surface.ticks_per_day
171
+ local daytime = tick / surface.ticks_per_day
172
+ surface.daytime = daytime
173
+
174
+ if daytime <= surface.dusk then -- Noon to Sunset
175
+ return mul
176
+ elseif daytime <= surface.evening then -- Sunset to Night
177
+ return mul * (1 - ((daytime - surface.dusk) / (surface.evening - surface.dusk)))
178
+ elseif daytime <= surface.morning then -- Night to Sunrise
179
+ return 0
180
+ elseif daytime <= surface.dawn then -- Sunrise to Morning
181
+ return mul * ((surface.daytime - surface.morning) / (surface.dawn - surface.morning))
182
+ else -- Morning to Noon
183
+ return mul
184
+ end
185
+ end
186
+
187
+ --- Get the sustained power multiplier, this needs improving
188
+ local function get_sustained_multiplier()
189
+ local mul = vlayer_data.surface.solar_power_multiplier
190
+ local surface = vlayer_data.surface
191
+
192
+ if surface.always_day then
193
+ -- Surface is always day, so full production is used
194
+ return mul
195
+ end
196
+
197
+ -- For nauvis vanilla: 210s + (1/2 x (84s + 84s))
198
+ local day_duration = 1 - surface.dawn + surface.dusk
199
+ local sunset_duration = surface.evening - surface.dusk
200
+ local sunrise_duration = surface.dawn - surface.morning
201
+
202
+ return mul * (day_duration + (0.5 * (sunset_duration + sunrise_duration)))
203
+ end
204
+
205
+ --- Internal, Allocate items in the vlayer, this will increase the property values of the vlayer such as production and capacity
206
+ -- Does not increment item storage, so should not be called before insert_item unless during init
207
+ -- Does not validate area requirements, so checks must be performed before calling this function
208
+ -- Accepts negative count for deallocating items
209
+ -- @tparam string item_name The name of the item to allocate
210
+ -- @tparam number count The count of the item to allocate
211
+ function vlayer.allocate_item(item_name, count)
212
+ local item_properties = config.allowed_items[item_name]
213
+ assert(item_properties, "Item not allowed in vlayer: " .. tostring(item_name))
214
+
215
+ if item_properties.production then
216
+ local nc = item_properties.production * count
217
+ vlayer_data.properties.production = vlayer_data.properties.production + nc
218
+ vlayer_data.properties.total_production = vlayer_data.properties.total_production + nc
219
+ end
220
+
221
+ if item_properties.capacity then
222
+ vlayer_data.properties.capacity = vlayer_data.properties.capacity + item_properties.capacity * count
223
+ end
224
+
225
+ if item_properties.discharge then
226
+ vlayer_data.properties.discharge = vlayer_data.properties.discharge + item_properties.discharge * count
227
+ end
228
+
229
+ if item_properties.surface_area then
230
+ vlayer_data.properties.total_surface_area = vlayer_data.properties.total_surface_area + item_properties.surface_area * count
231
+ end
232
+
233
+ if item_properties.required_area and item_properties.required_area > 0 then
234
+ vlayer_data.properties.used_surface_area = vlayer_data.properties.used_surface_area + item_properties.required_area * count
235
+ end
236
+ end
237
+
238
+ function vlayer.unable_alloc_item_pwr_calc(item_name, count)
239
+ local item_properties = config.allowed_items[item_name]
240
+
241
+ if item_properties.production then
242
+ vlayer_data.properties.total_production = vlayer_data.properties.total_production + item_properties.production * count
243
+ end
244
+ end
245
+
246
+ -- For all allowed items, setup their starting values, default 0
247
+ for item_name, properties in pairs(config.allowed_items) do
248
+ vlayer_data.storage.items[item_name] = properties.starting_value or 0
249
+
250
+ if properties.required_area and properties.required_area > 0 then
251
+ vlayer_data.storage.unallocated[item_name] = 0
252
+ end
253
+
254
+ vlayer.allocate_item(item_name, properties.starting_value)
255
+ end
256
+
257
+ --- Insert an item into the vlayer, this will increment its count in storage and allocate it if possible
258
+ -- @tparam string item_name The name of the item to insert
259
+ -- @tparam number count The count of the item to insert
260
+ function vlayer.insert_item(item_name, count)
261
+ local item_properties = config.allowed_items[item_name]
262
+ assert(item_properties, "Item not allowed in vlayer: " .. tostring(item_name))
263
+ vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] + count
264
+
265
+ if not config.unlimited_surface_area and item_properties.required_area and item_properties.required_area > 0 then
266
+ -- Calculate how many can be allocated
267
+ local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
268
+ local allocate_count = math.min(count, math.floor(surplus_area / item_properties.required_area))
269
+
270
+ if allocate_count > 0 then
271
+ vlayer.allocate_item(item_name, allocate_count)
272
+ end
273
+
274
+ local unallocated = count - allocate_count
275
+ vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] + unallocated
276
+ vlayer.unable_alloc_item_pwr_calc(item_name, unallocated)
277
+ else
278
+ vlayer.allocate_item(item_name, count)
279
+ end
280
+ end
281
+
282
+ --- Remove an item from the vlayer, this will decrement its count in storage and prioritise unallocated items over deallocation
283
+ -- Can not always fulfil the remove request for items which provide surface area, therefore returns the amount actually removed
284
+ -- @tparam string item_name The name of the item to remove
285
+ -- @tparam number count The count of the item to remove
286
+ -- @treturn number The count of the item actually removed
287
+ function vlayer.remove_item(item_name, count)
288
+ local item_properties = config.allowed_items[item_name]
289
+ assert(item_properties, "Item not allowed in vlayer: " .. tostring(item_name))
290
+ local remove_unallocated = 0
291
+
292
+ if not config.unlimited_surface_area and item_properties.required_area and item_properties.required_area > 0 then
293
+ -- Remove from the unallocated storage first
294
+ remove_unallocated = math.min(count, vlayer_data.storage.unallocated[item_name])
295
+
296
+ if remove_unallocated > 0 then
297
+ vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] - count
298
+ vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] - count
299
+ end
300
+
301
+ -- Check if any more items need to be removed
302
+ count = count - remove_unallocated
303
+
304
+ if count == 0 then
305
+ return remove_unallocated
306
+ end
307
+ end
308
+
309
+ -- Calculate the amount to remove based on items in storage
310
+ local remove_count = math.min(count, vlayer_data.storage.items[item_name])
311
+
312
+ if item_properties.surface_area and item_properties.surface_area > 0 then
313
+ -- If the item provides surface area then it has additional limitations
314
+ local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
315
+ remove_count = math.min(remove_count, math.floor(surplus_area / item_properties.surface_area))
316
+
317
+ if remove_count <= 0 then
318
+ return remove_unallocated
319
+ end
320
+ end
321
+
322
+ -- Remove the item from allocated storage
323
+ vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] - remove_count
324
+ vlayer.allocate_item(item_name, -remove_count)
325
+
326
+ return remove_unallocated + remove_count
327
+ end
328
+
329
+ --- Create a new storage input interface
330
+ -- @tparam LuaSurface surface The surface to place the interface onto
331
+ -- @tparam MapPosition position The position on the surface to place the interface at
332
+ -- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
333
+ -- @treturn LuaEntity The entity that was created for the interface
334
+ function vlayer.create_input_interface(surface, position, circuit, last_user)
335
+ local interface = surface.create_entity{ name = "storage-chest", position = position, force = "neutral" }
336
+ interface.storage_filter = { name = "deconstruction-planner", quality = "normal" }
337
+ table.insert(vlayer_data.entity_interfaces.storage_input, interface)
338
+
339
+ if last_user then
340
+ interface.last_user = last_user
341
+ end
342
+
343
+ if circuit then
344
+ for k, _ in pairs(circuit) do
345
+ for _, v in pairs(circuit[k]) do
346
+ interface.connect_neighbour{ wire = defines.wire_type[k], target_entity = v }
347
+ end
348
+ end
349
+ end
350
+
351
+ interface.destructible = false
352
+ interface.minable_flag = false
353
+ interface.operable = true
354
+
355
+ return interface
356
+ end
357
+
358
+ --- Handle all input interfaces, will take their contents and insert it into the vlayer storage
359
+ local function handle_input_interfaces()
360
+ for index, interface in pairs(vlayer_data.entity_interfaces.storage_input) do
361
+ if not interface.valid then
362
+ vlayer_data.entity_interfaces.storage_input[index] = nil
363
+ else
364
+ local inventory = assert(interface.get_inventory(defines.inventory.chest))
365
+
366
+ for _, v in pairs(inventory.get_contents()) do
367
+ if config.allowed_items[v.name] then
368
+ --[[
369
+ there are no quality support currently.
370
+ so instead, using the stats projection value for higher quality.
371
+ ]]
372
+ local count_deduct
373
+ local count_add
374
+
375
+ if prototypes.quality[v.quality].level == 0 then
376
+ count_deduct = v.count
377
+ count_add = v.count
378
+
379
+ elseif prototypes.quality[v.quality].level > 0 and v.count >= 10 then
380
+ local batch = math.floor(v.count / 10)
381
+ count_deduct = batch * 10
382
+ count_add = batch * (10 + (prototypes.quality[v.quality].level * 3))
383
+ end
384
+
385
+ if count_deduct and count_add then
386
+ if config.allowed_items[v.name].modded then
387
+ local modded_item = assert(config.modded_items[v.name])
388
+ if config.modded_auto_downgrade then
389
+ vlayer.insert_item(modded_item.base_game_equivalent, count_add * modded_item.multiplier)
390
+ else
391
+ vlayer.insert_item(v.name, count_add)
392
+ end
393
+ else
394
+ if vlayer_data.storage.power_items[v.name] then
395
+ vlayer_data.storage.power_items[v.name].count = vlayer_data.storage.power_items[v.name].count + count_add
396
+ else
397
+ vlayer.insert_item(v.name, count_add)
398
+ end
399
+ end
400
+
401
+ inventory.remove{ name = v.name, count = count_deduct, quality = v.quality }
402
+ end
403
+ end
404
+ end
405
+ end
406
+ end
407
+ end
408
+
409
+ --- Create a new storage output interface
410
+ -- @tparam LuaSurface surface The surface to place the interface onto
411
+ -- @tparam MapPosition position The position on the surface to place the interface at
412
+ -- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
413
+ -- @treturn LuaEntity The entity that was created for the interface
414
+ function vlayer.create_output_interface(surface, position, circuit, last_user)
415
+ local interface = surface.create_entity{ name = "requester-chest", position = position, force = "neutral" }
416
+ table.insert(vlayer_data.entity_interfaces.storage_output, interface)
417
+
418
+ if last_user then
419
+ interface.last_user = last_user
420
+ end
421
+
422
+ if circuit then
423
+ for k, _ in pairs(circuit) do
424
+ for _, v in pairs(circuit[k]) do
425
+ interface.connect_neighbour{ wire = defines.wire_type[k], target_entity = v }
426
+ end
427
+ end
428
+ end
429
+
430
+ interface.destructible = false
431
+ interface.minable_flag = false
432
+ interface.operable = true
433
+
434
+ return interface
435
+ end
436
+
437
+ --- Handle all output interfaces, will take their requests and remove it from the vlayer storage
438
+ local function handle_output_interfaces()
439
+ for index, interface in pairs(vlayer_data.entity_interfaces.storage_output) do
440
+ if not interface.valid then
441
+ vlayer_data.entity_interfaces.storage_output[index] = nil
442
+ else
443
+ local inventory = assert(interface.get_inventory(defines.inventory.chest))
444
+ local inventory_request_sections = assert(interface.get_logistic_sections()).sections
445
+
446
+ for i = 1, #inventory_request_sections do
447
+ for _, v in pairs(inventory_request_sections[i].filters) do
448
+ if v.value and config.allowed_items[v.value.name] then
449
+ local current_amount = inventory.get_item_count(v.value.name)
450
+ local request_amount = math.min(assert(v.min) - current_amount, vlayer_data.storage.items[v.value.name])
451
+
452
+ if request_amount > 0 and inventory.can_insert{ name = v.value.name, count = request_amount } then
453
+ local removed_item_count = vlayer.remove_item(v.value.name, request_amount)
454
+
455
+ if removed_item_count > 0 then
456
+ inventory.insert{ name = v.value.name, count = removed_item_count, quality = "normal" }
457
+ end
458
+ end
459
+ end
460
+ end
461
+ end
462
+ end
463
+ end
464
+ end
465
+
466
+ --- Handle the unallocated items because more surface area may have been added
467
+ local function handle_unallocated()
468
+ -- unallocated cant happen when its unlimited
469
+ if config.unlimited_surface_area then
470
+ return
471
+ end
472
+
473
+ -- Get the total unallocated area so items can be allocated in equal amounts
474
+ local unallocated_area = 0
475
+
476
+ for item_name, count in pairs(vlayer_data.storage.unallocated) do
477
+ local item_properties = config.allowed_items[item_name]
478
+ unallocated_area = unallocated_area + item_properties.required_area * count
479
+ end
480
+
481
+ if unallocated_area == 0 then
482
+ return
483
+ end
484
+
485
+ -- Allocate items in an equal distribution
486
+ local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
487
+
488
+ for item_name, count in pairs(vlayer_data.storage.unallocated) do
489
+ local allocation_count = math.min(count, math.floor(count * surplus_area / unallocated_area))
490
+
491
+ if allocation_count > 0 then
492
+ vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] - allocation_count
493
+ vlayer.allocate_item(item_name, allocation_count)
494
+ vlayer.unable_alloc_item_pwr_calc(item_name, -allocation_count)
495
+ end
496
+ end
497
+ end
498
+
499
+ --- Get the statistics for the vlayer
500
+ function vlayer.get_statistics()
501
+ local vdp = vlayer_data.properties.production * mega
502
+ local gdm = get_production_multiplier()
503
+ local gsm = get_sustained_multiplier()
504
+ local gald = get_actual_land_defecit()
505
+
506
+ return {
507
+ total_surface_area = vlayer_data.properties.total_surface_area,
508
+ used_surface_area = vlayer_data.properties.used_surface_area,
509
+ remaining_surface_area = gald,
510
+ surface_area = vlayer_data.properties.total_surface_area - gald,
511
+ production_multiplier = gdm,
512
+ energy_max = vdp,
513
+ energy_production = vdp * gdm,
514
+ energy_total_production = vlayer_data.properties.total_production * gsm * mega,
515
+ energy_sustained = vdp * gsm,
516
+ energy_capacity = vlayer_data.properties.capacity * mega,
517
+ energy_storage = vlayer_data.storage.energy,
518
+ }
519
+ end
520
+
521
+ --- add or reduce vlayer power
522
+ --- @param power number
523
+ --- @return number
524
+ function vlayer.energy_changed(power)
525
+ local new_value = vlayer_data.storage.energy + power
526
+ vlayer_data.storage.energy = new_value
527
+ return new_value
528
+ end
529
+
530
+ --- Circuit signals used for the statistics
531
+ function vlayer.get_circuits()
532
+ return {
533
+ total_surface_area = "signal-A",
534
+ used_surface_area = "signal-U",
535
+ remaining_surface_area = "signal-R",
536
+ production_multiplier = "signal-M",
537
+ energy_production = "signal-P",
538
+ energy_sustained = "signal-S",
539
+ energy_capacity = "signal-C",
540
+ energy_storage = "signal-E",
541
+ }
542
+ end
543
+
544
+ local vlayer_circuits_string = ""
545
+
546
+ for key, value in pairs(vlayer.get_circuits()) do
547
+ vlayer_circuits_string = vlayer_circuits_string .. string.format("[virtual-signal=%s] = %s\n", value, key:gsub("_", " "))
548
+ end
549
+
550
+ --- Create a new circuit interface
551
+ -- @tparam LuaSurface surface The surface to place the interface onto
552
+ -- @tparam MapPosition position The position on the surface to place the interface at
553
+ -- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
554
+ -- @treturn LuaEntity The entity that was created for the interface
555
+ function vlayer.create_circuit_interface(surface, position, circuit, last_user)
556
+ local interface = surface.create_entity{ name = "constant-combinator", position = position, force = "neutral" }
557
+ table.insert(vlayer_data.entity_interfaces.circuit, interface)
558
+
559
+ if last_user then
560
+ interface.last_user = last_user
561
+ end
562
+
563
+ if circuit then
564
+ for k, _ in pairs(circuit) do
565
+ for _, v in pairs(circuit[k]) do
566
+ interface.connect_neighbour{ wire = defines.wire_type[k], target_entity = v }
567
+ end
568
+ end
569
+ end
570
+
571
+ interface.destructible = false
572
+ interface.minable_flag = false
573
+ interface.operable = true
574
+
575
+ return interface
576
+ end
577
+
578
+ --- Handle all circuit interfaces, updating their signals to match the vlayer statistics
579
+ local function handle_circuit_interfaces()
580
+ local stats = vlayer.get_statistics()
581
+
582
+ for index, interface in pairs(vlayer_data.entity_interfaces.circuit) do
583
+ if not interface.valid then
584
+ vlayer_data.entity_interfaces.circuit[index] = nil
585
+ else
586
+ local control = interface.get_or_create_control_behavior() --[[@as LuaConstantCombinatorControlBehavior]]
587
+ if control.sections_count == 0 then
588
+ control.add_section()
589
+ end
590
+ local circuit_oc = assert(control.sections[1])
591
+ local signal_index = 1
592
+ local circuit = vlayer.get_circuits()
593
+
594
+ -- Set the virtual signals based on the vlayer stats
595
+ for stat_name, signal_name in pairs(circuit) do
596
+ if stat_name:find("energy") then
597
+ circuit_oc.set_slot(signal_index, { value = { type = "virtual", name = signal_name, quality = "normal" }, min = math.floor(stats[stat_name] / mega) })
598
+ elseif stat_name == "production_multiplier" then
599
+ circuit_oc.set_slot(signal_index, { value = { type = "virtual", name = signal_name, quality = "normal" }, min = math.floor(stats[stat_name] * 10000) })
600
+ else
601
+ circuit_oc.set_slot(signal_index, { value = { type = "virtual", name = signal_name, quality = "normal" }, min = math.floor(stats[stat_name]) })
602
+ end
603
+
604
+ signal_index = signal_index + 1
605
+ end
606
+
607
+ -- Set the item signals based on stored items
608
+ for item_name, count in pairs(vlayer_data.storage.items) do
609
+ if prototypes.item[item_name] and count > 0 then
610
+ local existing_index = circuit_oc.set_slot(signal_index, { value = { type = "item", name = item_name, quality = "normal" }, min = count })
611
+ if existing_index then
612
+ circuit_oc.clear_slot(existing_index)
613
+ circuit_oc.set_slot(signal_index, { value = { type = "item", name = item_name, quality = "normal" }, min = count })
614
+ end
615
+ signal_index = signal_index + 1
616
+ end
617
+ end
618
+
619
+ -- Clear remaining signals to prevent outdated values being present (caused by count > 0 check)
620
+ for clear_index = signal_index, #circuit do
621
+ if not circuit_oc.get_slot(clear_index).value then
622
+ break -- There are no more signals to clear
623
+ end
624
+
625
+ circuit_oc.clear_slot(clear_index)
626
+ end
627
+
628
+ interface.combinator_description = vlayer_circuits_string
629
+ end
630
+ end
631
+ end
632
+
633
+ --- Create a new energy interface
634
+ -- @tparam LuaSurface surface The surface to place the interface onto
635
+ -- @tparam MapPosition position The position on the surface to place the interface at
636
+ -- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
637
+ -- @treturn LuaEntity The entity that was created for the interface, or nil if it could not be created
638
+ function vlayer.create_energy_interface(surface, position, last_user)
639
+ if not surface.can_place_entity{ name = "electric-energy-interface", position = position } then
640
+ return nil
641
+ end
642
+
643
+ local interface = surface.create_entity{ name = "electric-energy-interface", position = position, force = "neutral" }
644
+ table.insert(vlayer_data.entity_interfaces.energy, interface)
645
+
646
+ if last_user then
647
+ interface.last_user = last_user
648
+ end
649
+
650
+ interface.destructible = false
651
+ interface.minable_flag = false
652
+ interface.operable = false
653
+ interface.electric_buffer_size = 0
654
+ interface.power_production = 0
655
+ interface.power_usage = 0
656
+ interface.energy = 0
657
+
658
+ return interface
659
+ end
660
+
661
+ --- Handle all energy interfaces as well as the energy storage
662
+ local function handle_energy_interfaces()
663
+ -- Add the newly produced power
664
+ local production = vlayer_data.properties.production * mega * (config.update_tick_energy / 60)
665
+ vlayer_data.storage.energy = vlayer_data.storage.energy + math.floor(production * get_production_multiplier())
666
+
667
+ -- Calculate how much power is present in the network, that is storage + all interfaces
668
+ if #vlayer_data.entity_interfaces.energy > 0 then
669
+ local available_energy = vlayer_data.storage.energy
670
+
671
+ for index, interface in pairs(vlayer_data.entity_interfaces.energy) do
672
+ if not interface.valid then
673
+ vlayer_data.entity_interfaces.energy[index] = nil
674
+ else
675
+ available_energy = available_energy + interface.energy
676
+ end
677
+ end
678
+
679
+ -- Distribute the energy between all interfaces
680
+ local discharge_rate = 2 * (production + vlayer_data.properties.discharge * mega) / #vlayer_data.entity_interfaces.energy
681
+ local fill_to = math.min(discharge_rate, math.floor(available_energy / #vlayer_data.entity_interfaces.energy))
682
+
683
+ for _, interface in pairs(vlayer_data.entity_interfaces.energy) do
684
+ interface.electric_buffer_size = math.max(discharge_rate, interface.energy) -- prevent energy loss
685
+ local delta = fill_to - interface.energy -- positive means storage to interface
686
+ vlayer_data.storage.energy = vlayer_data.storage.energy - delta
687
+ interface.energy = interface.energy + delta
688
+ end
689
+ end
690
+
691
+ -- Cap the stored energy to the allowed capacity
692
+ if not config.unlimited_capacity and vlayer_data.storage.energy > vlayer_data.properties.capacity * mega then
693
+ vlayer_data.storage.energy = vlayer_data.properties.capacity * mega
694
+
695
+ -- burn the trash to produce power
696
+ elseif vlayer_data.storage.power_items then
697
+ for k, v in pairs(vlayer_data.storage.power_items) do
698
+ local max_burning = (vlayer_data.properties.capacity * mega / 2) - vlayer_data.storage.energy
699
+
700
+ if v.count > 0 and max_burning > 0 then
701
+ local to_burn = math.min(v.count, math.floor(max_burning / v.value))
702
+
703
+ vlayer_data.storage.energy = vlayer_data.storage.energy + (to_burn * v.value)
704
+ vlayer_data.storage.power_items[k].count = vlayer_data.storage.power_items[k].count - to_burn
705
+ end
706
+ end
707
+ end
708
+ end
709
+
710
+ --- Remove the entity interface using the given position
711
+ -- @tparam LuaSurface surface The surface to search for an interface on
712
+ -- @tparam MapPosition position The position of the item
713
+ -- @treturn string The type of interface that was removed, or nil if no interface was found
714
+ -- @treturn MapPosition The position the interface was at, or nil if no interface was found
715
+ function vlayer.remove_interface(surface, position)
716
+ local entities = surface.find_entities_filtered{
717
+ name = { "storage-chest", "requester-chest", "constant-combinator", "electric-energy-interface" },
718
+ force = "neutral",
719
+ position = position,
720
+ radius = 2,
721
+ limit = 1,
722
+ }
723
+
724
+ -- Get the details which will be returned
725
+ if #entities == 0 then
726
+ return nil, nil, nil
727
+ end
728
+
729
+ local interface = entities[1]
730
+ local name = interface.name
731
+ local sur = interface.surface
732
+ local pos = interface.position
733
+
734
+ -- Return the type of interface removed and do some clean up
735
+ if name == "storage-chest" then
736
+ local inventory = assert(interface.get_inventory(defines.inventory.chest))
737
+ ExpUtil.transfer_inventory_to_surface{
738
+ inventory = inventory,
739
+ surface = interface.surface,
740
+ name = "iron-chest",
741
+ allow_creation = true,
742
+ }
743
+ table.remove_element(vlayer_data.entity_interfaces.storage_input, interface)
744
+ interface.destroy()
745
+
746
+ return "storage-input", sur, pos
747
+ elseif name == "requester-chest" then
748
+ local inventory = assert(interface.get_inventory(defines.inventory.chest))
749
+ ExpUtil.transfer_inventory_to_surface{
750
+ inventory = inventory,
751
+ surface = interface.surface,
752
+ name = "iron-chest",
753
+ allow_creation = true,
754
+ }
755
+ table.remove_element(vlayer_data.entity_interfaces.storage_output, interface)
756
+ interface.destroy()
757
+
758
+ return "storage-output", sur, pos
759
+ elseif name == "constant-combinator" then
760
+ table.remove_element(vlayer_data.entity_interfaces.circuit, interface)
761
+ interface.destroy()
762
+
763
+ return "circuit", sur, pos
764
+ elseif name == "electric-energy-interface" then
765
+ vlayer_data.storage.energy = vlayer_data.storage.energy + interface.energy
766
+ table.remove_element(vlayer_data.entity_interfaces.energy, interface)
767
+ interface.destroy()
768
+
769
+ return "energy", sur, pos
770
+ end
771
+ end
772
+
773
+ local function on_surface_event()
774
+ if config.mimic_surface then
775
+ local surface = game.get_surface(config.mimic_surface)
776
+
777
+ if surface then
778
+ vlayer_data.surface = surface
779
+ return
780
+ end
781
+ end
782
+
783
+ if not vlayer_data.surface.index then
784
+ -- Our fake surface data never has an index, we test for this to avoid unneeded copies from the config
785
+ vlayer_data.surface = table.deep_copy(config.surface)
786
+ end
787
+ end
788
+
789
+ --- Handle all storage IO and attempt allocation of unallocated items
790
+ Event.on_nth_tick(config.update_tick_storage, function(_)
791
+ handle_input_interfaces()
792
+ handle_output_interfaces()
793
+ handle_unallocated()
794
+ end)
795
+
796
+ --- Handle all energy and circuit updates
797
+ Event.on_nth_tick(config.update_tick_energy, function(_)
798
+ handle_circuit_interfaces()
799
+ handle_energy_interfaces()
800
+ end)
801
+
802
+ Event.add(defines.events.on_surface_created, on_surface_event)
803
+ Event.add(defines.events.on_surface_renamed, on_surface_event)
804
+ Event.add(defines.events.on_surface_imported, on_surface_event)
805
+ Event.on_init(on_surface_event) -- Default surface always exists, does not trigger on_surface_created
806
+
807
+ return vlayer