z_mirror/Krastorio2

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

Caleb HeuerChange to four-space indentation07409ca6

four-space-indentation
4.1 KiB134 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
121--- @class VirusHandler : event_handler
122local virus = {}
123
124function virus.on_init()
125    --- @type table<uint, BiterVirusData>
126    storage.biter_viruses = {}
127end
128
129virus.events = {
130    [defines.events.on_player_used_capsule] = on_player_used_capsule,
131    [defines.events.on_tick] = on_tick,
132}
133
134return virus