-
Notifications
You must be signed in to change notification settings - Fork 0
/
control.lua
455 lines (368 loc) · 13.6 KB
/
control.lua
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
require("controller.FlangChip")
GlobalData = require("controller.GlobalData")
Flang.tick = 0
-- Mappings
local PLAYER_ID_TO_ENTITY_MAPPING = {}
-- Mapping from entity ID -> FlangChip
local CHIP_TABLE = {}
-- Some constants
local FLANG_CHIP_ENTITY_NAME = "flang-chip"
local INVIS_FLANG_CHIP_ENTITY_NAME = "invis-flang-chip"
function get_player_last_chip_entity(player_id)
-- create the table if needed
if PLAYER_ID_TO_ENTITY_MAPPING["player_last_chip_entity_mapping"] then
local entity = PLAYER_ID_TO_ENTITY_MAPPING["player_last_chip_entity_mapping"][player_id]
if entity and entity.valid then
return entity
else
-- get rid of this hot entity
PLAYER_ID_TO_ENTITY_MAPPING["player_last_chip_entity_mapping"][player_id] = nil
return nil
end
else
return nil
end
end
function set_player_last_chip_entity(player_id, entity)
if not PLAYER_ID_TO_ENTITY_MAPPING["player_last_chip_entity_mapping"] then
PLAYER_ID_TO_ENTITY_MAPPING["player_last_chip_entity_mapping"] = {}
end
PLAYER_ID_TO_ENTITY_MAPPING["player_last_chip_entity_mapping"][player_id] = entity
end
--[[
The source is the preloaded source from storage
]]
function create_editor_window(player, source)
-- Get rid of any window that's already present
if player.gui.left.flang_parent_window_flow then player.gui.left.flang_parent_window_flow.destroy() end
-- create the parent window for the whole thing
local flang_parent_window_flow = player.gui.left.flang_parent_window_flow
if not flang_parent_window_flow then
-- create the parent flow
flang_parent_window_flow = player.gui.left.add{type = "flow", name = "flang_parent_window_flow", direction = "vertical"}
end
-- create the menu
menu_flow = flang_parent_window_flow.add({type = "flow", name = "flang_menu_flow", direction = "horizontal"})
-- inside the menu we add the buttons and stuff
button_style = "slot_button_style"
close_button = menu_flow.add({
type = "sprite-button",
name = "flang_menu_close_button",
sprite = "close"})
play_button = menu_flow.add({
type = "sprite-button",
name = "flang_menu_play_button",
sprite = "play"})
stop_button = menu_flow.add({
type = "sprite-button",
name = "flang_menu_stop_button",
sprite = "stop"})
-- create the editor
editor_window = flang_parent_window_flow.add{type="text-box", name="flang_editor_window",
style="flang_editor_window_style", text=source}
-- create the info window
info_window = flang_parent_window_flow.add{
type="text-box", name="flang_info_window",
style="flang_info_window_style",
text="nothing here... yet"
}
end
function close_editor_window(player, flangchip_entity)
if player.gui.left.flang_parent_window_flow then
player.gui.left.flang_parent_window_flow.destroy()
end
end
--[[
Called when the chip dies or is mined.
]]
function delete_chip_controller(entity)
if is_entity_flang_chip(entity) then
local id = entity.unit_number
-- delete from global storage
GlobalData.delete_entity_data(id)
-- delete from local storage
local chip = CHIP_TABLE[id]
if (chip and chip.invis_chip) then
chip.invis_chip.destroy()
end
CHIP_TABLE[id] = nil
end
end
function create_chip_controller(entity, built_from_robot)
if is_entity_flang_chip(entity) then
local id = entity.unit_number
local sourceCode = ""
-- Detect if a chip already exists at the same position
local invis_chip
local existingInvisChip = get_existing_entity_name_at_parent(entity, INVIS_FLANG_CHIP_ENTITY_NAME)
if (existingInvisChip) then
-- TODO I don't think this case will ever be hit in real-world use
-- The invis chip is only made in the other block AFTER this chip already exists
invis_chip = existingInvisChip
-- We already have an encoded chip here, read the contents
sourceCode = decode_data_from_invis_chip(existingInvisChip)
else
-- Create an invis chip since none already exist
invis_chip = create_invis_chip(entity)
if (built_from_robot) then
-- This will be deconstructed and fulfill by the robot shortly
invis_chip.order_deconstruction(entity.force)
end
end
-- create the first record of the entity
local object_data = GlobalData.new_data_object()
object_data.entity = entity
object_data.invis_chip = invis_chip
GlobalData.write_entity_data(id, object_data)
-- create the local chip
local chip = FlangChip:new({
entity = entity,
printer = editor_window_print,
invis_chip = invis_chip,
source = sourceCode
})
CHIP_TABLE[id] = chip
elseif is_entity_invis_flang_chip(entity) then
local existingData = decode_data_from_invis_chip(entity)
-- Find the existing flang chip here
local existingFlangEntity = get_existing_entity_name_at_parent(entity, FLANG_CHIP_ENTITY_NAME)
if (not is_entity_flang_chip(existingFlangEntity)) then
return
end
-- Update our tables to reflect the chip relationship
-- Global table update
local chipEntityId = existingFlangEntity.unit_number
GlobalData.write_invis_chip(chipEntityId, entity)
-- Local chip update
local chip = CHIP_TABLE[chipEntityId]
chip.invis_chip = entity
-- Update the chip source code with the decoded value here
update_entity_source_code(chipEntityId, existingData)
end
end
-------------------------- Chip Handling ---------------------
--------------------------------------------------------------
script.on_event(defines.events.on_tick, function(event)
if (event.tick % (30*1) == 0) then
Flang.tick = Flang.tick + 1
for entity_id, chip in pairs(CHIP_TABLE) do
chip:execute()
end
end
end)
script.on_event(defines.events.on_built_entity, function(event)
create_chip_controller(event.created_entity, false)
end)
script.on_event(defines.events.on_robot_built_entity, function(event)
create_chip_controller(event.created_entity, true)
end)
script.on_event(defines.events.on_entity_settings_pasted, function(event)
--[[
on_entity_settings_pasted
Called after entity copy-paste is done.
Contains
player_index :: uint
source :: LuaEntity: The source entity settings have been copied from.
destination :: LuaEntity: The destination entity settings have been copied to.
]]
-- Copy over the source to the new chip
-- Get the entities
local sourceEntity = event.source
local destinationEntity = event.destination
if (is_entity_flang_chip(sourceEntity) and is_entity_flang_chip(destinationEntity)) then
local sourceId = sourceEntity.unit_number
local destinationId = destinationEntity.unit_number
-- Write to the global data
local sourceChipData = GlobalData.get_entity_data(sourceId)
local sourceCode = sourceChipData.source
update_entity_source_code(destinationId, sourceCode)
end
end)
-------------------------- GUI -------------------------------
--------------------------------------------------------------
script.on_event("flang-open-editor", function(event)
local player = game.players[event.player_index]
-- Make sure the entity is a flang chip
if player.selected and is_entity_flang_chip(player.selected) then
local entity = player.selected
local source = GlobalData.get_entity_data(entity.unit_number)["source"]
set_player_last_chip_entity(event.player_index, entity)
create_editor_window(player, source)
end
end)
script.on_event(defines.events.on_gui_click, function(event)
local player = game.players[event.player_index]
-- if the close button was clicked, close the parent window
if event.element.name == "flang_menu_close_button" then
close_editor_window(player)
elseif event.element.name == "flang_menu_play_button" then
local entity = get_player_last_chip_entity(event.player_index)
if entity then
local id = entity.unit_number
GlobalData.write_entity_is_running(id, true)
-- The chip should exist already
local chip = CHIP_TABLE[id]
chip:start_execution()
end
elseif event.element.name == "flang_menu_stop_button" then
local entity = get_player_last_chip_entity(event.player_index)
if entity then
local id = entity.unit_number
GlobalData.write_entity_is_running(id, false)
-- The chip should exist already
local chip = CHIP_TABLE[id]
chip:stop_execution()
end
end
end)
script.on_event(defines.events.on_gui_text_changed, function(event)
local player = game.players[event.player_index]
if event.element.name == "flang_editor_window" then
local text = event.element.text
local entity = get_player_last_chip_entity(event.player_index)
if entity then
local id = entity.unit_number
-- We add a newline since the gui editor apparently doesn't have EOF
update_entity_source_code(id, text .. "\n")
end
end
end)
-------------------------- Deletion --------------------------
--------------------------------------------------------------
script.on_event(defines.events.on_entity_died, function(event)
delete_chip_controller(event.entity)
end)
script.on_event(defines.events.on_player_mined_entity, function(event)
delete_chip_controller(event.entity)
end)
script.on_event(defines.events.on_robot_mined_entity, function(event)
delete_chip_controller(event.entity)
end)
-------------------------- Initialization --------------------
--------------------------------------------------------------
script.on_init(function()
-- recreate the controller table from the global table
-- player_log_print("on init")
end)
script.on_configuration_changed(function()
-- recreate the controller table from the global table
-- player_log_print("on config changed")
end)
script.on_load(function()
-- recreate the controller table from the global table
for entity_id, chip_data in pairs(GlobalData.get_all_entities()) do
local chip = FlangChip:new({
entity = chip_data["entity"],
source = chip_data["source"],
is_running = chip_data["is_running"],
invis_chip = chip_data["invis_chip"],
printer = editor_window_print
})
CHIP_TABLE[entity_id] = chip
end
end)
-------------------------- Chip Data Updates -----------------
--------------------------------------------------------------
function update_entity_source_code(destinationEntityId, sourceCode)
-- Update the global data
GlobalData.write_entity_source(destinationEntityId, sourceCode)
-- Write to the local data
local chip = CHIP_TABLE[destinationEntityId]
chip:update_source(sourceCode)
-- Write to the invis_chip
local invis_chip = chip.invis_chip
if (is_entity_invis_flang_chip(invis_chip)) then
encode_data_onto_invis_chip(invis_chip, sourceCode)
end
end
----------------- Invis Chip ---------------------------------
--------------------------------------------------------------
function create_invis_chip(entity)
local surface = entity.surface
local invis_chip = surface.create_entity({
name = INVIS_FLANG_CHIP_ENTITY_NAME,
position = entity.position,
direction = entity.direction,
force = entity.force
})
invis_chip.destructible = false
return invis_chip
end
function encode_data_onto_invis_chip(entity, stringData)
-- See https://lua-api.factorio.com/latest/LuaEntity.html#LuaEntity.alert_parameters
-- https://lua-api.factorio.com/latest/Concepts.html#ProgrammableSpeakerAlertParameters
local speakerAlertParameters = {
alert_message = stringData,
show_alert = false,
show_on_map = false,
-- signal ID is not required
}
if (is_entity_invis_flang_chip(entity)) then
entity.alert_parameters = speakerAlertParameters
end
end
function decode_data_from_invis_chip(entity)
if (is_entity_invis_flang_chip(entity)) then
return entity.alert_parameters.alert_message
end
end
function get_existing_entity_name_at_parent(parentEntity, targetName)
-- Any invis chips will exist at the same position
local entitiesAtSamePosition = parentEntity.surface.find_entities_filtered({
position = parentEntity.position,
name = targetName
})
for _,matchingEntity in pairs(entitiesAtSamePosition) do
if (matchingEntity ~= parentEntity) then
return matchingEntity
end
end
player_log_print("Couldn't find existing entity with target name " .. targetName)
return nil
end
-------------------------- Misc ------------------------------
--------------------------------------------------------------
function is_entity_flang_chip(entity)
return entity and entity.name == FLANG_CHIP_ENTITY_NAME and entity.valid
end
function is_entity_invis_flang_chip(entity)
return entity and entity.name == INVIS_FLANG_CHIP_ENTITY_NAME and entity.valid
end
function player_log_print(msg)
if game == nil then
return
end
for index,player in pairs(game.connected_players) do
player.print(msg)
end
end
function editor_window_print(spawningEntity, msg, should_clear)
if game == nil then
return
end
for playerIndex,player in pairs(game.connected_players) do
local lastPlayerChipEntity = get_player_last_chip_entity(player.index)
if (lastPlayerChipEntity == nil) then
return
end
if (spawningEntity.unit_number ~= lastPlayerChipEntity.unit_number) then
return
end
if player.gui.left.flang_parent_window_flow and player.gui.left.flang_parent_window_flow.flang_info_window then
info_window = player.gui.left.flang_parent_window_flow.flang_info_window
if (should_clear) then
info_window.text = ""
end
if (info_window.text == "") then
info_window.text = msg
else
info_window.text = info_window.text .. "\n" .. msg
end
end
end
end
function print_pairs(table)
for k,v in pairs(table) do
player_log_print("key" .. k .. " val " .. v)
end
end