z_mirror/Krastorio2

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

Caleb HeuerClean up legacy event parameter041f29c4

santichu95/patch-1
12.3 KiB434 linesraw
1local charging_rate = 3000000 -- 3 MW
2local cooldown = 10
3local loss_multiplier = 1.8
4local range = 20
5local required_energy = 10000000 -- 10 MW
6
7local absorber_buffer_capacity = prototypes.equipment["energy-absorber-equipment"].energy_source.buffer_capacity
8
9-- TOWER
10-- The entity that is interacted with
11
12--- @param e EntityBuiltEvent
13local function on_entity_built(e)
14  local entity = e.entity or e.destination
15  if not entity or not entity.valid then
16    return
17  end
18  local entity_name = entity.name
19
20  -- Internal entities should not be cloned
21  if
22    e.name == defines.events.on_entity_cloned
23    and (entity_name == "kr-tesla-coil-turret" or entity_name == "kr-tesla-coil-collision")
24  then
25    entity.destroy()
26    return
27  end
28
29  if entity_name ~= "kr-tesla-coil" then
30    return
31  end
32
33  local surface = entity.surface
34  local unit_number = entity.unit_number --[[@as uint]]
35
36  local turret = surface.create_entity({
37    name = "kr-tesla-coil-turret",
38    position = entity.position,
39    force = game.forces["kr-internal-turrets"],
40    create_build_effect_smoke = false,
41    raise_built = true,
42  })
43  if not turret or not turret.valid then
44    game.print(
45      "Building tesla failed due to AAI Programmable Vehicles; This tesla coil will not function. To fix this, change the 'Deadzone construction denial range' map setting to zero."
46    )
47    entity.active = false
48    return
49  end
50  turret.destructible = false
51
52  local collision_entity = surface.create_entity({
53    name = "kr-tesla-coil-collision",
54    position = entity.position,
55    force = entity.force,
56    create_build_effect_smoke = false,
57    raise_built = true,
58  })
59  collision_entity.destructible = false
60
61  --- @class TowerData
62  local data = {
63    entities = {
64      collision = collision_entity,
65      tower = entity,
66      turret = turret,
67    },
68    tower_unit_number = unit_number,
69    turret_unit_number = turret.unit_number,
70  }
71
72  storage.tesla_coil.turrets[data.turret_unit_number] = data
73  storage.tesla_coil.towers[unit_number] = data
74end
75
76--- @param entity LuaEntity
77local function destroy_if_valid(entity)
78  if entity.valid then
79    entity.destroy()
80  end
81end
82
83--- @param e EntityDestroyedEvent
84local function on_entity_destroyed(e)
85  local entity = e.entity
86  if not entity or not entity.valid or entity.name ~= "kr-tesla-coil" then
87    return
88  end
89
90  local unit_number = entity.unit_number
91  assert(unit_number)
92  local tower_data = storage.tesla_coil.towers[unit_number]
93  if not tower_data then
94    return
95  end
96
97  destroy_if_valid(tower_data.entities.collision)
98  destroy_if_valid(tower_data.entities.turret)
99
100  storage.tesla_coil.turrets[tower_data.turret_unit_number] = nil
101  storage.tesla_coil.towers[unit_number] = nil
102end
103
104-- TARGET
105-- An entity that will receive energy from a tesla coil
106
107--- @param target LuaEntity
108--- @return GridData?
109local function get_grid_data(target)
110  --- @type LuaEquipmentGrid
111  local grid
112  if target.type == "character" then
113    local armor_inventory = target.get_inventory(defines.inventory.character_armor)
114    if armor_inventory and armor_inventory.valid then
115      local armor = armor_inventory[1]
116      if armor and armor.valid_for_read then
117        grid = armor.grid
118      end
119    end
120  else
121    grid = target.grid
122  end
123
124  if grid then
125    --- @class GridData
126    local data = {
127      absorber = grid.find("energy-absorber-equipment"),
128      grid = grid,
129    }
130
131    return data
132  end
133end
134
135--- Updates the absorber object in a target's equipment grid
136--- @param grid LuaEquipmentGrid
137local function update_target_grid(grid)
138  for _, target_data in pairs(storage.tesla_coil.targets) do
139    local grid_data = target_data.grid_data
140    if grid_data.grid.valid and grid_data.grid == grid then
141      grid_data.absorber = grid.find("energy-absorber-equipment")
142    end
143  end
144end
145
146--- @param target LuaEntity
147--- @param tower_data TowerData
148local function add_target(target, tower_data)
149  local target_unit_number = target.unit_number --[[@as uint]]
150  local tower = tower_data.entities.tower
151  if not tower.valid or tower.energy < required_energy then
152    return
153  end
154  -- Check the target's equipment grid for an energy absorber
155  local grid_data = get_grid_data(target)
156  if grid_data and grid_data.absorber and grid_data.absorber.valid then
157    --- @class TargetData
158    local target_data = {
159      connections = {
160        --- @type table<number, ConnectionData>
161        by_beam = {},
162        --- @type table<number, ConnectionData>
163        by_tower = {},
164      },
165      entity = target,
166      --- @type number?
167      full_tick = nil,
168      grid_data = grid_data,
169      unit_number = target_unit_number,
170    }
171    storage.tesla_coil.targets[target_unit_number] = target_data
172
173    return target_data
174  end
175end
176
177--- @param target_unit_number number
178local function remove_target(target_unit_number)
179  storage.tesla_coil.targets[target_unit_number] = nil
180end
181
182-- CONNECTION
183-- A connection between a tower and a target, comprising of a beam
184-- There can be unlimited connections per target
185
186--- @param target_data TargetData
187--- @param tower_data TowerData
188--- @return boolean
189local function add_connection(target_data, tower_data)
190  -- Check if the absorber has space
191  local absorber = target_data.grid_data.absorber
192  if not absorber or not absorber.valid then
193    remove_target(target_data.unit_number)
194    return false
195  end
196
197  local tower = tower_data.entities.tower
198  if not tower.valid then
199    remove_target(target_data.unit_number)
200    return false
201  end
202
203  if absorber.energy >= absorber_buffer_capacity then
204    return false
205  end
206
207  -- Create beam entity
208  local beam = tower_data.entities.tower.surface.create_entity({
209    name = "kr-tesla-coil-electric-beam",
210    source = tower_data.entities.tower,
211    source_offset = { 0, -2.2 },
212    position = tower_data.entities.tower.position,
213    target = target_data.entity,
214    duration = 0,
215    max_length = range,
216    force = tower_data.entities.tower.force,
217    raise_built = true,
218  })
219  if not beam then
220    return false
221  end
222  local beam_number = script.register_on_object_destroyed(beam)
223
224  --- @class BeamData
225  storage.tesla_coil.beams[beam_number] = {
226    beam = beam,
227    beam_number = beam_number,
228    target_data = target_data,
229    tower_data = tower_data,
230  }
231
232  --- @class ConnectionData
233  local connection_data = {
234    beam = beam,
235    beam_number = beam_number,
236    tower_data = tower_data,
237  }
238  target_data.connections.by_beam[beam_number] = connection_data
239  target_data.connections.by_tower[tower_data.tower_unit_number] = connection_data
240
241  return true
242end
243
244--- @param target_data TargetData
245--- @param tower_data TowerData
246local function remove_connection(target_data, tower_data)
247  local connection_data = target_data.connections.by_tower[tower_data.tower_unit_number]
248
249  -- Destroy beam if it still exists
250  if connection_data.beam.valid then
251    connection_data.beam.destroy()
252  end
253
254  local beam_number = connection_data.beam_number
255  storage.tesla_coil.beams[beam_number] = nil
256
257  target_data.connections.by_beam[beam_number] = nil
258  target_data.connections.by_tower[tower_data.tower_unit_number] = nil
259
260  if table_size(target_data.connections.by_beam) == 0 then
261    remove_target(target_data.unit_number)
262  end
263end
264
265--- @param target_data TargetData
266--- @param tower_data TowerData
267local function update_connection(target_data, tower_data)
268  local absorber = target_data.grid_data.absorber
269
270  -- Check if the tower is powered
271  if not absorber or not absorber.valid or tower_data.entities.tower.energy < required_energy then
272    remove_connection(target_data, tower_data)
273    return
274  end
275
276  local energy = absorber.energy
277  if energy < absorber_buffer_capacity then
278    -- Calculate how much to add
279    local to_add = charging_rate / 60 * cooldown
280    local result = energy + to_add
281    local tower = tower_data.entities.tower
282
283    if result >= absorber_buffer_capacity then
284      absorber.energy = absorber_buffer_capacity
285      target_data.full_tick = game.tick
286    else
287      absorber.energy = result
288      target_data.full_tick = nil
289    end
290
291    tower.energy = tower.energy - (to_add * loss_multiplier)
292  elseif target_data.full_tick and target_data.full_tick + cooldown <= game.tick then
293    remove_connection(target_data, tower_data)
294  end
295end
296
297--- @param target LuaEntity
298--- @param turret LuaEntity
299local function process_turret_fire(target, turret)
300  local tower_data = storage.tesla_coil.turrets[turret.unit_number]
301  if not tower_data then
302    return
303  end
304
305  local target_data = storage.tesla_coil.targets[target.unit_number]
306  if not target_data then
307    target_data = add_target(target, tower_data)
308  end
309
310  if target_data then
311    -- Just in case (#182)
312    if not target_data.entity.valid then
313      target_data.entity = target
314    end
315    local connection = target_data.connections.by_tower[tower_data.tower_unit_number]
316    if connection or add_connection(target_data, tower_data) then
317      update_connection(target_data, tower_data)
318    end
319  end
320end
321
322-- EVENTS
323
324--- @param e EventData.on_object_destroyed
325local function on_object_destroyed(e)
326  local beam_data = storage.tesla_coil.beams[e.registration_number]
327  if beam_data then
328    remove_connection(beam_data.target_data, beam_data.tower_data)
329  end
330end
331
332--- @param e EventData.on_equipment_inserted
333local function on_equipment_inserted(e)
334  if not e.equipment.valid or e.equipment.name ~= "energy-absorber-equipment" then
335    return
336  end
337  update_target_grid(e.grid)
338end
339
340--- @param e EventData.on_equipment_removed
341local function on_equipment_removed(e)
342  if e.equipment == "energy-absorber-equipment" then
343    update_target_grid(e.grid)
344  end
345end
346
347--- @param e EventData.on_script_trigger_effect
348local function on_script_trigger_effect(e)
349  if e.effect_id == "kr-tesla-coil-trigger" then
350    process_turret_fire(e.target_entity, e.source_entity)
351  end
352end
353
354local tesla_coil = {}
355
356function tesla_coil.on_init()
357  storage.tesla_coil = {
358    --- @type table<number, BeamData>
359    beams = {},
360    --- @type table<number, TowerData>
361    turrets = {},
362    --- @type table<number, TowerData>
363    towers = {},
364    --- @type table<number, TargetData>
365    targets = {},
366  }
367end
368
369tesla_coil.events = {
370  [defines.events.on_built_entity] = on_entity_built,
371  [defines.events.on_entity_cloned] = on_entity_built,
372  [defines.events.on_entity_died] = on_entity_destroyed,
373  [defines.events.on_equipment_inserted] = on_equipment_inserted,
374  [defines.events.on_equipment_removed] = on_equipment_removed,
375  [defines.events.on_object_destroyed] = on_object_destroyed,
376  [defines.events.on_player_mined_entity] = on_entity_destroyed,
377  [defines.events.on_robot_built_entity] = on_entity_built,
378  [defines.events.on_robot_mined_entity] = on_entity_destroyed,
379  [defines.events.on_script_trigger_effect] = on_script_trigger_effect,
380  [defines.events.script_raised_built] = on_entity_built,
381  [defines.events.script_raised_destroy] = on_entity_destroyed,
382  [defines.events.script_raised_revive] = on_entity_built,
383  [defines.events.on_space_platform_built_entity] = on_entity_built,
384  [defines.events.on_space_platform_mined_entity] = on_entity_destroyed,
385}
386
387return tesla_coil
388
389--[[
390  -- Quick testing code
391  event.register(remote.call("EditorExtensions", "debug_world_ready_event"), function()
392    local surface = game.surfaces.nauvis
393
394    local coils = {
395      { x = -9.5, y = -9.5 },
396      { x = 10.5, y = -9.5 },
397      { x = -9.5, y = 10.5 },
398      { x = 10.5, y = 10.5 },
399    }
400    for _, position in pairs(coils) do
401      surface.create_entity({
402        name = "kr-tesla-coil",
403        direction = defines.direction.north,
404        position = position,
405        force = game.forces.player,
406        raise_built = true,
407        create_build_effect_smoke = false,
408      })
409    end
410    surface.create_entity({
411      name = "ee-infinity-accumulator-primary-output",
412      position = { x = -1, y = -9 },
413      force = game.forces.player,
414      raise_built = true,
415      create_build_effect_smoke = false,
416    })
417    surface.create_entity({
418      name = "ee-super-substation",
419      position = { x = 1, y = -9 },
420      force = game.forces.player,
421      raise_built = true,
422      create_build_effect_smoke = false,
423    })
424
425    local character = game.get_player(1).character
426    local armor_inventory = character.get_inventory(defines.inventory.character_armor)
427    if armor_inventory and armor_inventory.valid then
428      local armor = armor_inventory[1]
429      if armor and armor.valid_for_read then
430        armor.grid.put({ name = "energy-absorber-equipment", position = { 5, 5 } })
431      end
432    end
433  end)
434]]