z_mirror/EditorExtensions

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

Caleb HeuerFix crash when placing an infinity loader55451ea

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