z_mirror/Krastorio2

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

Caleb HeuerChange to four-space indentation07409ca6

four-space-indentation
8.3 KiB243 linesraw
1local flib_bounding_box = require("__flib__.bounding-box")
2
3--- @alias k2.ResourceRestrictions.Forces table<ForceIndex, k2.ResourceRestrictions.ForceData?>
4
5--- @class k2.ResourceRestrictions.ForceData
6--- @field disabled_mining_drills table<UnitNumber, k2.ResourceRestrictions.DrillData>
7--- @field force LuaForce
8--- @field restricted_resources Set<string>
9
10--- @class k2.ResourceRestrictions.DrillData
11--- @field drill LuaEntity
12--- @field icon LuaRenderObject
13
14--- @return k2.ResourceRestrictions.Forces
15local function get_forces()
16    if not storage.resource_restrictions then
17        --- @type k2.ResourceRestrictions.Forces
18        storage.resource_restrictions = {}
19    end
20    return storage.resource_restrictions
21end
22
23--- @param force LuaForce
24local function get_force_data(force)
25    local forces = get_forces()
26    local force_index = force.index
27    local force_data = forces[force_index]
28    if not force_data then
29        force_data = {
30            disabled_mining_drills = {},
31            force = force,
32            restricted_resources = {},
33        }
34        forces[force_index] = force_data
35    end
36    return force_data
37end
38
39--- @param force_data k2.ResourceRestrictions.ForceData
40--- @param drill LuaEntity
41--- @return LuaEntityPrototype? restricted_resource
42local function get_first_restricted_resource(force_data, drill)
43    local radius = drill.prototype.get_mining_drill_radius(drill.quality)
44    --- @cast radius -?
45    local area = flib_bounding_box.from_dimensions(drill.position, radius * 2, radius * 2)
46    local targets = drill.surface.find_entities_filtered({ type = "resource", area = area })
47    local drill_categories = drill.prototype.resource_categories or {}
48    local restrictions = force_data.restricted_resources
49    for i = 1, #targets do
50        local target = targets[i]
51        if drill_categories[target.prototype.resource_category] and restrictions[target.name] then
52            return target.prototype
53        end
54    end
55end
56
57--- @param force_data k2.ResourceRestrictions.ForceData
58--- @param drill LuaEntity
59local function update_drill(force_data, drill)
60    local disabled_mining_drills = force_data.disabled_mining_drills
61
62    local unit_number = drill.unit_number
63    --- @cast unit_number -?
64
65    local drill_data = disabled_mining_drills[unit_number]
66
67    local restriction = get_first_restricted_resource(force_data, drill)
68    if restriction then
69        if not drill_data then
70            drill.active = false
71            drill.custom_status = {
72                diode = defines.entity_status_diode.red,
73                label = { "entity-status.kr-unable-to-mine", restriction.localised_name },
74            }
75            disabled_mining_drills[unit_number] = {
76                drill = drill,
77                icon = rendering.draw_sprite({
78                    sprite = "utility/resources_depleted_icon",
79                    target = drill,
80                    surface = drill.surface,
81                    x_scale = 0.5,
82                    y_scale = 0.5,
83                    blink_interval = 30,
84                }),
85            }
86        end
87    else
88        if drill_data then
89            drill.active = true
90            drill.custom_status = nil
91            if drill_data.icon.valid then
92                drill_data.icon.destroy()
93            end
94            disabled_mining_drills[unit_number] = nil
95        end
96    end
97end
98
99--- @param force_data k2.ResourceRestrictions.ForceData
100local function refresh_all_drills(force_data)
101    for _, drill_data in pairs(force_data.disabled_mining_drills) do
102        if drill_data.icon.valid then
103            drill_data.icon.destroy()
104        end
105    end
106    force_data.disabled_mining_drills = {}
107    for _, surface in pairs(game.surfaces) do
108        for _, drill in pairs(surface.find_entities_filtered({ type = "mining-drill", force = force_data.force })) do
109            update_drill(force_data, drill)
110        end
111    end
112end
113
114local unlocks = prototypes.mod_data["kr-resource-restrictions"].data --[[ @as table<data.TechnologyID, data.EntityID[]?> ]]
115
116--- @param force_data k2.ResourceRestrictions.ForceData
117--- @param technology LuaTechnology
118--- @param restrict boolean
119local function process_effects(force_data, technology, restrict)
120    local technology_unlocks = unlocks[technology.name]
121    if not technology_unlocks then
122        return
123    end
124
125    local restricted_resources = force_data.restricted_resources
126    for _, resource_name in pairs(technology_unlocks) do
127        restricted_resources[resource_name] = restrict or nil
128    end
129
130    if restrict then
131        -- The on_research_reversed event is very rare, and never happens during regular gameplay, so it is acceptable to have a lag spike in this case.
132        refresh_all_drills(force_data)
133    else
134        for _, drill_data in pairs(force_data.disabled_mining_drills) do
135            update_drill(force_data, drill_data.drill)
136        end
137    end
138end
139
140--- @param force LuaForce
141local function refresh_force(force)
142    force.mining_with_fluid = true
143    local force_data = get_force_data(force)
144    for _, technology in pairs(force.technologies) do
145        if not technology.researched then
146            -- TODO: This is doing a full scan of all surfaces every time!
147            process_effects(force_data, technology, true)
148        end
149    end
150end
151
152--- @param e EntityBuiltEvent
153local function on_entity_built(e)
154    local entity = e.entity or e.destination
155    if not entity.valid or entity.type ~= "mining-drill" then
156        return
157    end
158    update_drill(get_force_data(entity.force --[[@as LuaForce]]), entity)
159end
160
161--- @param e EntityDestroyedEvent
162local function on_entity_destroyed(e)
163    local entity = e.entity
164    if not entity.valid or entity.type ~= "mining-drill" then
165        return
166    end
167
168    -- The icon will get destroyed automatically because its target is the mining drill.
169    get_force_data(e.entity.force --[[@as LuaForce]]).disabled_mining_drills[entity.unit_number] = nil
170end
171
172--- @param e EventData.on_research_finished
173local function on_research_finished(e)
174    process_effects(get_force_data(e.research.force), e.research, false)
175end
176
177--- @param e EventData.on_research_finished
178local function on_research_reversed(e)
179    process_effects(get_force_data(e.research.force), e.research, true)
180end
181
182--- @param e EventData.on_force_created
183local function on_force_created(e)
184    refresh_force(e.force)
185end
186
187--- @param e EventData.on_force_reset
188local function on_force_reset(e)
189    refresh_force(e.force)
190end
191
192--- @param e EventData.on_forces_merging
193local function on_forces_merging(e)
194    local source_data = get_force_data(e.source)
195    for _, drill_data in pairs(source_data.disabled_mining_drills) do
196        if drill_data.icon.valid then
197            drill_data.icon.destroy()
198        end
199    end
200    get_forces()[e.source.index] = nil
201    refresh_force(e.destination)
202end
203
204--- @param e EventData.on_technology_effects_reset
205local function on_technology_effects_reset(e)
206    refresh_force(e.force)
207end
208
209local function refresh()
210    storage.resource_restrictions = nil
211    for _, force in pairs(game.forces) do
212        refresh_force(force)
213        refresh_all_drills(get_force_data(force))
214    end
215end
216
217--- @class MiningDrillRestrictionsHandler: event_handler
218local handler = {}
219
220handler.on_init = refresh
221handler.on_configuration_changed = refresh
222
223handler.events = {
224    [defines.events.on_built_entity] = on_entity_built,
225    [defines.events.on_entity_cloned] = on_entity_built,
226    [defines.events.on_entity_died] = on_entity_destroyed,
227    [defines.events.on_force_created] = on_force_created,
228    [defines.events.on_force_reset] = on_force_reset,
229    [defines.events.on_forces_merging] = on_forces_merging,
230    [defines.events.on_player_mined_entity] = on_entity_destroyed,
231    [defines.events.on_research_finished] = on_research_finished,
232    [defines.events.on_research_reversed] = on_research_reversed,
233    [defines.events.on_robot_built_entity] = on_entity_built,
234    [defines.events.on_robot_mined_entity] = on_entity_destroyed,
235    [defines.events.on_space_platform_built_entity] = on_entity_built,
236    [defines.events.on_space_platform_mined_entity] = on_entity_destroyed,
237    [defines.events.on_technology_effects_reset] = on_technology_effects_reset,
238    [defines.events.script_raised_built] = on_entity_built,
239    [defines.events.script_raised_destroy] = on_entity_destroyed,
240    [defines.events.script_raised_revive] = on_entity_built,
241}
242
243return handler