z_mirror/Krastorio2

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

Caleb HeuerBiter virus refactor (#497)ad91ece5

discrete-steel-pipes
3.6 KiB133 linesraw
1local flib_position = require("__flib__.position")
2
3--- @class BiterVirusData
4--- @field active BiterVirusActiveData[]
5--- @field active_len integer
6--- @field to_iterate ChunkPositionAndAreaAndDistance[]
7--- @field to_iterate_len integer
8--- @field surface LuaSurface
9
10--- @class BiterVirusActiveData
11--- @field entities LuaEntity[]
12--- @field entities_len integer
13--- @field to_kill integer
14
15--- @class ChunkPositionAndAreaAndDistance : ChunkPositionAndArea
16--- @field distance_squared number
17
18--- @param e EventData.on_player_used_capsule
19local function on_player_used_capsule(e)
20  local item = e.item
21  if not item or not item.valid or item.name ~= "kr-biter-virus" then
22    return
23  end
24
25  local player = game.get_player(e.player_index)
26  if not player then
27    return
28  end
29
30  local surface = player.surface
31  if storage.biter_viruses[surface.index] then
32    return
33  end
34
35  local origin = flib_position.to_chunk(e.position)
36  --- @type ChunkPositionAndAreaAndDistance[]
37  local chunks = {}
38  for chunk in surface.get_chunks() do
39    if surface.is_chunk_generated(chunk) then
40      --- @cast chunk ChunkPositionAndAreaAndDistance
41      chunk.distance_squared = flib_position.distance_squared(chunk --[[@as ChunkPosition]], origin)
42      chunks[#chunks + 1] = chunk
43    end
44  end
45  table.sort(chunks, function(pos_a, pos_b)
46    -- Sort backwards so that we can remove items from the end.
47    return pos_a.distance_squared > pos_b.distance_squared
48  end)
49
50  storage.biter_viruses[surface.index] = {
51    active = {},
52    active_len = 0,
53    to_iterate = chunks,
54    to_iterate_len = #chunks,
55    surface = surface,
56  }
57end
58
59local function on_tick()
60  if not storage.biter_viruses then
61    return
62  end
63  --- @type uint[]
64  local to_remove = {}
65  for surface_index, virus in pairs(storage.biter_viruses) do
66    if not virus.surface.valid or (virus.active_len == 0 and virus.to_iterate_len == 0) then
67      to_remove[#to_remove + 1] = surface_index
68      goto continue
69    end
70
71    for _ = 1, 5 - virus.active_len do
72      if virus.to_iterate_len > 0 then
73        local chunk = table.remove(virus.to_iterate, virus.to_iterate_len)
74        virus.to_iterate_len = virus.to_iterate_len - 1
75
76        local entities = virus.surface.find_entities_filtered({
77          area = chunk.area,
78          force = "enemy",
79          type = { "unit", "unit-spawner" },
80        })
81        local entities_len = #entities
82        if entities_len > 0 then
83          virus.active_len = virus.active_len + 1
84          virus.active[virus.active_len] = {
85            entities = entities,
86            entities_len = entities_len,
87            to_kill = math.ceil(entities_len / 3),
88          }
89        end
90      end
91    end
92
93    local active_to_remove = {}
94    for i = 1, virus.active_len do
95      local active = virus.active[i]
96      if active then
97        local entity = table.remove(active.entities, active.entities_len)
98        active.entities_len = active.entities_len - 1
99        if entity and entity.valid then
100          entity.die()
101          active.to_kill = active.to_kill - 1
102        end
103        if active.to_kill == 0 then
104          active_to_remove[#active_to_remove + 1] = i
105        end
106      end
107    end
108    for i = #active_to_remove, 1, -1 do
109      table.remove(virus.active, active_to_remove[i])
110      virus.active_len = virus.active_len - 1
111    end
112
113    ::continue::
114  end
115
116  for i = 1, #to_remove do
117    storage.biter_viruses[to_remove[i]] = nil
118  end
119end
120
121local virus = {}
122
123function virus.on_init()
124  --- @type table<uint, BiterVirusData>
125  storage.biter_viruses = {}
126end
127
128virus.events = {
129  [defines.events.on_player_used_capsule] = on_player_used_capsule,
130  [defines.events.on_tick] = on_tick,
131}
132
133return virus