z_mirror/EditorExtensions

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

Patrick 'Quezler' MounierMerge branch 'master' into patch-1e65a4b6

Quezler/patch-1
9.3 KiB320 linesraw
1local flib_direction = require("__flib__.direction")
2local flib_migration = require("__flib__.migration")
3local position = require("__flib__.position")
4
5local transport_belt_connectables = {
6  "transport-belt",
7  "underground-belt",
8  "splitter",
9  "loader",
10  "loader-1x1",
11  "linked-belt",
12  "lane-splitter",
13}
14
15--- @param entity LuaEntity
16local function snap(entity)
17  local offset_direction = entity.direction
18  if entity.loader_type == "input" then
19    offset_direction = flib_direction.opposite(offset_direction)
20  end
21  local belt_position = position.add(entity.position, flib_direction.to_vector(offset_direction))
22  local belt =
23    entity.surface.find_entities_filtered({ position = belt_position, type = transport_belt_connectables })[1]
24  if not belt then
25    belt =
26      entity.surface.find_entities_filtered({ position = belt_position, ghost_type = transport_belt_connectables })[1]
27  end
28  if not belt then
29    return
30  end
31  if belt.direction == flib_direction.opposite(entity.direction) then
32    entity.loader_type = entity.loader_type == "output" and "input" or "output"
33  end
34end
35
36--- @param entity LuaEntity
37--- @param chest LuaEntity?
38local function sync_chest_filter(entity, chest)
39  if not chest then
40    chest = entity.surface.find_entity("ee-infinity-loader-chest", entity.position)
41  end
42  if not chest then
43    entity.destroy()
44    return
45  end
46  for i = 1, 2 do
47    local filter = entity.get_filter(i)
48    if filter then
49      chest.set_infinity_container_filter(i, {
50        index = i,
51        name = filter.name --[[@as string]],
52        quality = filter.quality,
53        count = prototypes.item[filter.name].stack_size * 5,
54        mode = "exactly",
55      })
56    else
57      chest.set_infinity_container_filter(i, nil)
58    end
59  end
60end
61
62--- @param loader LuaEntity
63--- @param combinator LuaEntity
64local function copy_from_loader_to_combinator(loader, combinator)
65  local cb = combinator.get_or_create_control_behavior() --[[@as LuaConstantCombinatorControlBehavior]]
66  --- @type LuaLogisticSection?
67  local section
68  for _, sec in pairs(cb.sections) do
69    if sec.group == "" then
70      section = sec
71      break
72    end
73  end
74  if not section then
75    section = cb.add_section()
76  end
77  if not section then
78    return -- When will this ever happen?
79  end
80  --- @type ItemFilter?
81  local first_filter
82  for i = 1, 2 do
83    local filter = loader.get_filter(i)
84    if not filter then
85      goto continue
86    end
87    if i == 1 then
88      first_filter = filter
89    elseif first_filter and filter.name == first_filter.name and filter.quality == first_filter.quality then
90      return
91    end
92    section.set_slot(i, {
93      value = {
94        type = "item",
95        name = filter.name --[[@as string]],
96        quality = filter.quality,
97      },
98      min = 1,
99    })
100    ::continue::
101  end
102end
103
104--- @param combinator LuaEntity
105--- @param loader LuaEntity
106local function copy_from_combinator_to_loader(combinator, loader)
107  local cb = combinator.get_control_behavior() --[[@as LuaConstantCombinatorControlBehavior?]]
108  if not cb then
109    return
110  end
111  local section = cb.get_section(1)
112  if not section then
113    return
114  end
115  for i = 1, 2 do
116    local filter = section.filters[i]
117    if filter then
118      local value = filter.value
119      if value and prototypes[value.type or "item"][value.name] then
120        loader.set_filter(i, { name = value.name, quality = value.quality })
121      else
122        loader.set_filter(i, nil)
123      end
124    end
125  end
126  sync_chest_filter(loader)
127end
128
129--- @param e BuiltEvent
130local function on_entity_built(e)
131  local entity = e.entity or e.destination
132  if not entity.valid then
133    return
134  end
135
136  if entity.name ~= "ee-infinity-loader" then
137    return
138  end
139
140  -- Create chest
141  local chest = entity.surface.create_entity({
142    name = "ee-infinity-loader-chest",
143    position = entity.position,
144    force = entity.force,
145    create_build_effect_smoke = false,
146    raise_built = true,
147  })
148
149  if not chest then
150    entity.destroy()
151    return
152  end
153
154  chest.remove_unfiltered_items = true
155  sync_chest_filter(entity, chest)
156  snap(entity)
157end
158
159--- @param e DestroyedEvent
160local function on_entity_destroyed(e)
161  local entity = e.entity
162  if not entity.valid or entity.name ~= "ee-infinity-loader" then
163    return
164  end
165  local chest = entity.surface.find_entity("ee-infinity-loader-chest", entity.position)
166  if chest then
167    chest.destroy({ raise_destroy = true })
168  end
169end
170
171--- @param e EventData.on_player_rotated_entity
172local function on_entity_rotated(e)
173  local entity = e.entity
174  if not entity.valid or entity.name ~= "ee-infinity-loader" then
175    return
176  end
177  sync_chest_filter(entity)
178end
179
180--- @param e EventData.on_entity_settings_pasted
181local function on_entity_settings_pasted(e)
182  local source, destination = e.source, e.destination
183  if not source.valid or not destination.valid then
184    return
185  end
186  local source_is_loader, destination_is_loader =
187    source.name == "ee-infinity-loader", destination.name == "ee-infinity-loader"
188  if source_is_loader and destination.name == "constant-combinator" then
189    copy_from_loader_to_combinator(source, destination)
190  elseif source.name == "constant-combinator" and destination_is_loader then
191    copy_from_combinator_to_loader(source, destination)
192  elseif destination_is_loader then
193    sync_chest_filter(destination)
194  end
195end
196
197--- @param e EventData.on_gui_opened
198local function on_gui_opened(e)
199  if e.gui_type ~= defines.gui_type.entity then
200    return
201  end
202  local entity = e.entity
203  if not entity or not entity.valid then
204    return
205  end
206  if entity.name == "ee-infinity-loader" then
207    storage.infinity_loader_open[e.player_index] = entity
208  end
209end
210
211--- @param e EventData.on_gui_closed
212local function on_gui_closed(e)
213  if e.gui_type ~= defines.gui_type.entity then
214    return
215  end
216  local loader = storage.infinity_loader_open[e.player_index]
217  if loader and loader.valid then
218    sync_chest_filter(loader)
219    storage.infinity_loader_open[e.player_index] = nil
220  end
221end
222
223local infinity_loader = {}
224
225function infinity_loader.on_init()
226  --- @type table<uint, LuaEntity>
227  storage.infinity_loader_open = {}
228end
229
230--- @param e ConfigurationChangedData
231function infinity_loader.on_configuration_changed(e)
232  flib_migration.on_config_changed(
233    e,
234    infinity_loader.migrations,
235    script.mod_name,
236    e.mod_changes.EditorExtensions and e.mod_changes.EditorExtensions.old_version
237  )
238end
239
240infinity_loader.migrations = {
241  ["2.0.0"] = function()
242    for _, surface in pairs(game.surfaces) do
243      for _, combinator in pairs(surface.find_entities_filtered({ name = "ee-infinity-loader-dummy-combinator" })) do
244        local cb = combinator.get_control_behavior() --[[@as LuaConstantCombinatorControlBehavior?]]
245        if not cb then
246          goto continue
247        end
248        local section = cb.get_section(1)
249        if not section then
250          goto continue
251        end
252        local filter_1, filter_2 = section.filters[1], section.filters[2]
253        local loader = combinator.surface.create_entity({
254          name = "ee-infinity-loader",
255          direction = combinator.direction,
256          position = combinator.position,
257          force = combinator.force,
258          last_user = combinator.last_user,
259          fast_replace = true,
260          create_build_effect_smoke = false,
261        })
262        if not loader then
263          error("Failed to create infinity loader replacement.")
264        end
265        if filter_1 and filter_1.value then
266          loader.set_filter(1, { name = filter_1.value.name, quality = filter_1.value.quality, amount = filter_1.min })
267        end
268        if filter_2 and filter_2.value then
269          loader.set_filter(2, { name = filter_2.value.name, quality = filter_2.value.quality, amount = filter_2.min })
270        end
271        snap(loader)
272        combinator.destroy()
273        ::continue::
274      end
275    end
276  end,
277  --- @param old_version string
278  ["2.4.0"] = function(old_version)
279    if not flib_migration.is_newer_version("2.0.0", old_version) then
280      return
281    end
282    for _, surface in pairs(game.surfaces) do
283      for _, loader in pairs(surface.find_entities_filtered({ name = "ee-infinity-loader" })) do
284        loader.set_filter(2, loader.get_filter(1))
285      end
286    end
287  end,
288}
289
290infinity_loader.events = {
291  [defines.events.on_built_entity] = on_entity_built,
292  [defines.events.on_entity_cloned] = on_entity_built,
293  [defines.events.on_entity_died] = on_entity_destroyed,
294  [defines.events.on_entity_settings_pasted] = on_entity_settings_pasted,
295  [defines.events.on_gui_closed] = on_gui_closed,
296  [defines.events.on_gui_opened] = on_gui_opened,
297  [defines.events.on_player_mined_entity] = on_entity_destroyed,
298  [defines.events.on_player_rotated_entity] = on_entity_rotated,
299  [defines.events.on_robot_built_entity] = on_entity_built,
300  [defines.events.on_robot_mined_entity] = on_entity_destroyed,
301  [defines.events.script_raised_built] = on_entity_built,
302  [defines.events.script_raised_destroy] = on_entity_destroyed,
303  [defines.events.script_raised_revive] = on_entity_built,
304  [defines.events.on_space_platform_built_entity] = on_entity_built,
305  [defines.events.on_space_platform_mined_entity] = on_entity_destroyed,
306}
307
308infinity_loader.on_nth_tick = {
309  [15] = function()
310    for unit_number, loader in pairs(storage.infinity_loader_open) do
311      if loader.valid then
312        sync_chest_filter(loader)
313      else
314        storage.infinity_loader_open[unit_number] = nil
315      end
316    end
317  end,
318}
319
320return infinity_loader