-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontrol.lua
More file actions
506 lines (439 loc) · 17.6 KB
/
Copy pathcontrol.lua
File metadata and controls
506 lines (439 loc) · 17.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
-- Platform configuration
local PLATFORM_SIZE = 100
local PLATFORM_CENTER = {x = 0, y = 0}
local PLATFORM_AREA = {
left_top = {x = PLATFORM_CENTER.x - PLATFORM_SIZE/2, y = PLATFORM_CENTER.y - PLATFORM_SIZE/2},
right_bottom = {x = PLATFORM_CENTER.x + PLATFORM_SIZE/2, y = PLATFORM_CENTER.y + PLATFORM_SIZE/2}
}
-- Generic function to capture all entity data
local function capture_entity_data(entity)
local entity_data = {
name = entity.name,
position = entity.position,
direction = entity.direction,
force = entity.force.name,
health = entity.health,
type = entity.type
}
-- Save all inventories for the entity
entity_data.inventories = {}
-- Handle different entity types that might have items on them
if entity.type == "transport-belt" or entity.type == "underground-belt" or entity.type == "splitter" then
entity_data.belt_items = {}
local max_line = entity.get_max_transport_line_index()
for line = 1, max_line do
local tl = entity.get_transport_line(line)
if tl then
local contents = tl.get_detailed_contents()
if next(contents) then
entity_data.belt_items[line] = contents
end
end
end
else
-- Regular inventory handling for other entities
for inv_index = 1, 10 do -- Check common inventory indices
local inventory = entity.get_inventory(inv_index)
if inventory and inventory.valid then
-- Only save non-empty inventories
if not inventory.is_empty() then
-- Save as simple array of {name, count} pairs
local contents = {}
for i = 1, #inventory do
local stack = inventory[i]
if stack and stack.valid_for_read then
table.insert(contents, {
name = stack.name,
count = stack.count,
health = stack.health
})
end
end
if #contents > 0 then
entity_data.inventories[inv_index] = contents
end
end
end
end
end
-- Save additional metadata
-- Energy
if entity.energy then
entity_data.energy = entity.energy
end
-- Orientation (for some entities)
if entity.orientation then
entity_data.orientation = entity.orientation
end
-- Fluids (for tanks, pipes, etc.)
if entity.fluidbox then
entity_data.fluids = {}
for i = 1, #entity.fluidbox do
local fluid = entity.fluidbox[i]
if fluid then
entity_data.fluids[i] = {
name = fluid.name,
amount = fluid.amount,
temperature = fluid.temperature
}
end
end
end
-- Capture specific configurable properties that are known to be important for recipes/inputs
entity_data.properties = {}
-- Use pcall to safely access properties that may not exist on all entity types
local safe_get_property = function(obj, key)
local success, value = pcall(function()
return obj[key]
end)
if success then
return value
else
return nil
end
end
-- Recipe selection (for assemblers, furnaces)
local recipe = safe_get_property(entity, "recipe")
if recipe ~= nil then
entity_data.properties.recipe = recipe
end
-- For entities that support recipe setting, also store recipe name for restoration
if entity.get_recipe then
local success, result = pcall(function()
local current_recipe = entity.get_recipe()
if current_recipe then
return current_recipe.name
end
return nil
end)
if success and result then
entity_data.properties.current_recipe = result
end
end
-- Filter settings (for inserters, splitters)
local filters = safe_get_property(entity, "filters")
if filters ~= nil then
entity_data.properties.filters = filters
end
-- Requester chest settings
local request_filters = safe_get_property(entity, "request_filters")
if request_filters ~= nil then
entity_data.properties.request_filters = request_filters
end
-- Logistic chest settings
local logistic_mode = safe_get_property(entity, "logistic_mode")
if logistic_mode ~= nil then
entity_data.properties.logistic_mode = logistic_mode
end
-- Other specific configurable properties
local config_properties = {
"active",
"enabled",
"infinity_settings",
"request_from_buffers",
"control_behavior",
"circuit_opened",
"circuit_condition",
"circuit_connectable"
}
-- Additional properties that may be important for recipes and inventories
local additional_properties = {
"recipe",
"selected_recipe",
"current_recipe",
"set_recipe"
}
for _, prop in ipairs(additional_properties) do
local value = safe_get_property(entity, prop)
if value ~= nil then
-- Skip functions and problematic types
if type(value) ~= "function" then
entity_data.properties[prop] = value
end
end
end
for _, prop in ipairs(config_properties) do
local value = safe_get_property(entity, prop)
if value ~= nil then
-- Skip functions and problematic types
if type(value) ~= "function" then
entity_data.properties[prop] = value
end
end
end
return entity_data
end
-- Generic function to restore entity data
local function restore_entity_data(entity_data, surface)
local new_entity = surface.create_entity{
name = entity_data.name,
position = entity_data.position,
direction = entity_data.direction,
force = entity_data.force
}
if not new_entity or not new_entity.valid then
return
end
-- Restore all captured properties
for key, value in pairs(entity_data.properties or {}) do
if type(value) ~= "function" then
pcall(function()
new_entity[key] = value
end)
end
end
-- Special handling for recipe restoration
if entity_data.properties.current_recipe then
local success, result = pcall(function()
new_entity.set_recipe(entity_data.properties.current_recipe)
end)
if not success then
game.print("Warning: Failed to set recipe " .. entity_data.properties.current_recipe .. " on entity " .. entity_data.name)
end
end
-- Restore health
if entity_data.health then
new_entity.health = entity_data.health
end
-- Restore all inventories
if entity_data.inventories then
for inv_index, contents in pairs(entity_data.inventories) do
local inventory = new_entity.get_inventory(inv_index)
if inventory and inventory.valid then
-- Clear inventory first
inventory.clear()
-- Restore each item stack
for slot_index, stack_data in ipairs(contents) do
if stack_data.name and stack_data.count and stack_data.count > 0 then
-- Use pcall to safely insert items with health/durability parameters
local success, result = pcall(function()
-- Only include health and durability if they are valid properties for the item type
local insert_params = {
name = stack_data.name,
count = stack_data.count
}
-- Check if this is a tool-type item that can have health/durability
local success, item_prototype = pcall(function()
return game.get_prototype("item", stack_data.name)
end)
if success and item_prototype then
if item_prototype.type == "tool" then
-- Add optional parameters only if they exist and are valid
if stack_data.health ~= nil and stack_data.health > 0 then
insert_params.health = stack_data.health
end
if stack_data.durability ~= nil then
insert_params.durability = stack_data.durability
end
elseif item_prototype.type == "repair-tool" then
-- Repair tools also can have durability
if stack_data.durability ~= nil then
insert_params.durability = stack_data.durability
end
end
end
return inventory.insert(insert_params)
end)
if not success then
game.print("Warning: Failed to insert " .. stack_data.name .. " into inventory - " .. tostring(result))
elseif result == 0 then
game.print("Warning: Failed to insert " .. stack_data.name .. " into inventory (inserted 0 items)")
end
end
end
end
end
end
-- Handle belt items specifically (items that are on transport belts)
if entity_data.belt_items then
if new_entity.type == "transport-belt" or new_entity.type == "underground-belt" or new_entity.type == "splitter" then
for line, detailed_contents_array in pairs(entity_data.belt_items) do
local tl = new_entity.get_transport_line(line)
if tl then
for _, detailed_contents in pairs(detailed_contents_array) do
tl.insert_at(
detailed_contents.position,
detailed_contents.stack
)
end
end
end
end
end
-- Restore additional metadata
-- Energy
if entity_data.energy and new_entity.energy then
new_entity.energy = entity_data.energy
end
-- Orientation
if entity_data.orientation and new_entity.orientation ~= nil then
new_entity.orientation = entity_data.orientation
end
-- Fluids
if entity_data.fluids and new_entity.fluidbox then
for i, fluid_data in pairs(entity_data.fluids) do
pcall(function()
new_entity.fluidbox[i] = fluid_data
end)
end
end
end
-- Initialize the mod on first load
local function on_init()
-- Create the refined concrete platform on Nauvis
local nauvis = game.surfaces["nauvis"]
if not nauvis then return end
local tiles = {}
for x = PLATFORM_AREA.left_top.x, PLATFORM_AREA.right_bottom.x - 1 do
for y = PLATFORM_AREA.left_top.y, PLATFORM_AREA.right_bottom.y - 1 do
table.insert(tiles, {name = "refined-concrete", position = {x, y}})
end
end
nauvis.set_tiles(tiles)
-- Create or ensure Vulcanus surface exists
local vulcanus = game.surfaces["vulcanus"]
if not vulcanus then
vulcanus = game.create_surface("vulcanus", {
property_expression_names = {
elevation = "vulcanus_elevation",
temperature = "vulcanus_temperature",
moisture = "vulcanus_moisture",
aux = "vulcanus_aux",
cliffiness = "vulcanus_cliffiness",
cliff_elevation = "cliff_elevation_from_elevation"
}
})
game.print("Created Vulcanus surface.")
end
-- Create void area on Vulcanus at 0,0
vulcanus.request_to_generate_chunks({x = 0, y = 0}, 2)
vulcanus.force_generate_chunk_requests()
local void_tiles = {}
for x = PLATFORM_AREA.left_top.x, PLATFORM_AREA.right_bottom.x - 1 do
for y = PLATFORM_AREA.left_top.y, PLATFORM_AREA.right_bottom.y - 1 do
table.insert(void_tiles, {name = "out-of-map", position = {x, y}})
end
end
vulcanus.set_tiles(void_tiles)
-- Initialize global storage
storage.elevator = {
platform_area = PLATFORM_AREA,
nauvis_has_platform = true
}
end
-- Create GUI button for each player
local function create_gui(player)
if player.gui.top.elevator_teleport_button then
return -- Already exists
end
player.gui.top.add{
type = "button",
name = "elevator_teleport_button",
caption = "Teleport Platform"
}
end
-- Handle player joining
local function on_player_created(event)
local player = game.get_player(event.player_index)
if player then
create_gui(player)
end
end
-- Teleport platform between Nauvis and Vulcanus
local function teleport_platform()
local nauvis = game.surfaces["nauvis"]
local vulcanus = game.surfaces["vulcanus"]
if not nauvis or not vulcanus then
game.print("Error: Required surfaces not found!")
return
end
local area = storage.elevator.platform_area
-- Determine source and destination surfaces
local source_surface, dest_surface, direction_name
if storage.elevator.nauvis_has_platform then
source_surface = nauvis
dest_surface = vulcanus
direction_name = "to Vulcanus"
else
source_surface = vulcanus
dest_surface = nauvis
direction_name = "to Nauvis"
end
-- Step 1: Find players on the platform to teleport them
local players_to_teleport = {}
for _, player in pairs(game.players) do
if player.valid and player.character and player.surface == source_surface then
local pos = player.position
if pos.x >= area.left_top.x and pos.x < area.right_bottom.x and
pos.y >= area.left_top.y and pos.y < area.right_bottom.y then
table.insert(players_to_teleport, player)
end
end
end
-- Step 2: Find and clone all entities on source platform
local entities_data = {}
local entities = source_surface.find_entities(area)
for _, entity in pairs(entities) do
-- Skip player characters (we'll teleport them separately)
if entity.valid and entity.name ~= "character" then
local entity_data = capture_entity_data(entity)
table.insert(entities_data, entity_data)
end
end
-- Step 3: Ensure chunks are generated on destination at the target location
dest_surface.request_to_generate_chunks({x = 0, y = 0}, 2)
dest_surface.force_generate_chunk_requests()
-- Step 4: Create refined concrete on destination
local concrete_tiles = {}
for x = area.left_top.x, area.right_bottom.x - 1 do
for y = area.left_top.y, area.right_bottom.y - 1 do
table.insert(concrete_tiles, {name = "refined-concrete", position = {x, y}})
end
end
dest_surface.set_tiles(concrete_tiles)
-- Step 5: Recreate entities on destination
for _, entity_data in pairs(entities_data) do
restore_entity_data(entity_data, dest_surface)
end
-- Step 6: Teleport players to destination (BEFORE replacing with void)
for _, player in pairs(players_to_teleport) do
if player.valid then
player.teleport(player.position, dest_surface)
end
end
-- Step 7: NOW remove entities and replace with void on source
for _, entity in pairs(entities) do
if entity.valid and entity.name ~= "player" and entity.name ~= "character" then
entity.destroy()
end
end
-- Replace refined concrete with out-of-map tiles (void)
local void_tiles = {}
for x = area.left_top.x, area.right_bottom.x - 1 do
for y = area.left_top.y, area.right_bottom.y - 1 do
table.insert(void_tiles, {name = "out-of-map", position = {x, y}})
end
end
source_surface.set_tiles(void_tiles)
local player_count = #players_to_teleport
local player_msg = player_count > 0 and (" " .. player_count .. " player(s) teleported.") or ""
game.print("Platform teleported " .. direction_name .. "! " .. #entities_data .. " entities moved." .. player_msg)
-- Toggle platform location
storage.elevator.nauvis_has_platform = not storage.elevator.nauvis_has_platform
end
-- Handle button clicks
local function on_gui_click(event)
if event.element.name == "elevator_teleport_button" then
teleport_platform()
end
end
-- Event handlers
script.on_init(on_init)
script.on_event(defines.events.on_player_created, on_player_created)
script.on_event(defines.events.on_gui_click, on_gui_click)
-- Create GUI for existing players on configuration change
script.on_configuration_changed(function()
for _, player in pairs(game.players) do
create_gui(player)
end
end)