-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontrol.lua
277 lines (236 loc) · 8.5 KB
/
control.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
-- Migrations
script.on_configuration_changed(function (data)
for _, force in pairs(game.forces) do
force.reset_technology_effects()
print("Migrations complete.")
end
end)
-- Rotatable chests
---@param bp_filters BlueprintLogisticFilter[]
---@return LogisticFilter[]
local function convert_bp_filters(bp_filters)
---@type LogisticFilter[]
local filters, last_index = {}, 0
for _, filter in pairs(bp_filters) do
last_index = math.max(last_index, filter.index)
filters[filter.index] = {
value = {
type = filter.type,
name = filter.name,
quality = filter.quality,
comparator = filter.comparator
},
min = filter.count,
max = filter.max_count,
minimum_delivery_count = filter.minimum_delivery_count,
-- Import_from??
}
end
for i = 1, last_index-1, 1 do
if not filters[i] then
filters[i] = {}
end
end
return filters
end
---@param chest_tags ChestTags
---@param requester LuaLogisticPoint
local function repair_logistic(requester, chest_tags)
requester.remove_section(requester.sections_count) -- Remove starting section
requester.trash_not_requested = chest_tags.request.trash_not_requested or false
for _, tag_section in pairs(chest_tags.request.sections) do
---@type LuaLogisticSection
local section
if tag_section.group then
section = requester.add_section(tag_section.group)--[[@as LuaLogisticSection]]
if section.filters_count == 0 and tag_section.filters then
section.filters = convert_bp_filters(tag_section.filters)
end
else
section = requester.add_section()--[[@as LuaLogisticSection]]
section.filters = convert_bp_filters(tag_section.filters or {})
end
section.multiplier = tag_section.multiplier or 1
section.active = tag_section.active ~= false
end
end
---@type table<string,fun(entity:LuaEntity, chest_tags:ChestTags, tags:Tags)>
local restore_from_bp = {
["container"] = function (entity, chest_tags, tags)
if not chest_tags.circuit then return end
local behavior = entity.get_or_create_control_behavior() --[[@as LuaContainerControlBehavior]]
behavior.read_contents = chest_tags.circuit.read_contents or false
end,
["logistic-container"] = function (entity, chest_tags, tags)
if chest_tags.circuit then
local circuit = chest_tags.circuit
---@cast circuit -?
local behavior = entity.get_or_create_control_behavior() --[[@as LuaLogisticContainerControlBehavior]]
if circuit.circuit_condition then
behavior.circuit_condition = {condition=circuit.circuit_condition}
end
if circuit.circuit_condition_enabled then
behavior.circuit_condition_enabled = circuit.circuit_condition_enabled
end
if circuit.circuit_mode_of_operation then
behavior.circuit_exclusive_mode_of_operation = circuit.circuit_mode_of_operation
end
chest_tags.circuit = nil
end
if chest_tags.request then
if chest_tags.request.request_from_buffers then
entity.request_from_buffers = chest_tags.request.request_from_buffers
end
local point = entity.get_logistic_point(defines.logistic_member_index.logistic_container)
---@cast point LuaLogisticPoint?
-- Workaround for not being able to affect ghost points
if not point then
chest_tags.request.request_from_buffers = nil
tags.wide_chest = chest_tags
goto skip_request
end
repair_logistic(point, chest_tags)
chest_tags.request = nil
end
::skip_request::
entity.tags = tags
end,
}
---@param entity LuaEntity
---@param full_name string
---@param tags Tags
---@param is_ghost boolean
local function chest_built(entity, full_name, tags, is_ghost)
local entity_name = full_name:sub(11)
local surface = entity.surface
local direction = entity.direction % 8 --[[@as int]]
if direction % 4 ~= 0 then
error("Entity starting with 'rotatable-' had a non-cardinal direction: "
..full_name..":\n"..serpent.block(entity)
)
end
if direction == 0 then
entity_name = "wide-"..entity_name
else
entity_name = "tall-"..entity_name
end
local player = entity.last_user
local chest_tags = tags.wide_chest or {} --[[@as ChestTags]]
tags.wide_chest = nil
---@type LuaSurface.create_entity_param
local create = {
name = entity_name,
inner_name = entity_name,
quality = entity.quality,
position = entity.position,
force = entity.force,
player = player,
create_build_effect_smoke = false,
bar = chest_tags.bar,
fast_replace = true,
}
chest_tags.bar = nil
if is_ghost then
create.name = "entity-ghost"
create.inner_name = entity_name
create.tags = tags
end
local new_entity = surface.create_entity(create)
if not new_entity then error("Replacement failed!") end
-- Remove the `removed-entity` action
-- This seems to also remove the paired `create-entity` action
if player then
local undo_stack = player.undo_redo_stack
local undo_actions = undo_stack.get_undo_item(1)
undo_stack.remove_undo_action(1, #undo_actions-1)
end
if chest_tags then
local func = restore_from_bp[is_ghost and new_entity.ghost_type or new_entity.type]
if func then func(new_entity, chest_tags, tags) end
end
end
local is_beta = settings.startup["enable-wide-containers-beta"].value --[[@as boolean]]
---@alias BuiltEventData
---| EventData.on_built_entity
---| EventData.on_robot_built_entity
---| EventData.on_space_platform_built_entity
---| EventData.script_raised_built
---@param EventData BuiltEventData
local function built(EventData)
local entity = EventData.entity
local type, name, tags = entity.type, "", EventData.tags or {}
local is_ghost = type == "entity-ghost"
if is_ghost then
type = entity.ghost_type
name = entity.ghost_name
tags = entity.tags or {}
else
name = entity.name
end
if type == "assembling-machine"
and name:sub(1, 10) == "rotatable-" then
chest_built(entity, name, tags, is_ghost)
elseif tags.wide_chest then
local chest_tags = tags.wide_chest--[[@as ChestTags]]
local point = entity.get_logistic_point(defines.logistic_member_index.logistic_container)
if not point then return log("Could not repair tags when reviving the entity") end
repair_logistic(point, chest_tags)
end
end
script.on_event(defines.events.on_built_entity, built)
script.on_event(defines.events.on_robot_built_entity, built)
script.on_event(defines.events.script_raised_built, built)
script.on_event(defines.events.script_raised_revive, built)
script.on_event(defines.events.on_space_platform_built_entity, built)
---@class ChestTags
---@field circuit? BlueprintControlBehavior.container|BlueprintControlBehavior.logistic_container
---@field bar? uint
---@field request? BlueprintFilters
---@class BlueprintControlBehavior.container
---@field read_contents? boolean
---@class BlueprintControlBehavior.logistic_container : BlueprintControlBehavior.container
---@field circuit_condition_enabled? boolean
---@field circuit_condition? CircuitCondition
---@field circuit_mode_of_operation? defines.control_behavior.logistic_container.exclusive_mode defaults to `defines.control_behavior.logistic_container.exclusive_mode.send_contents`
---@class BlueprintFilters
---@field request_from_buffers? boolean
---@field trash_not_requested? boolean
---@field sections BlueprintFilters.section[]
---@class BlueprintFilters.section
---@field index uint
---@field filters? BlueprintLogisticFilter[]
---@field group? string
---@field multiplier? int
---@field active? false
---@class BlueprintEntity.container : BlueprintEntity
---@field control_behavior BlueprintControlBehavior.container
---@field bar int
---@class BlueprintEntity.logistic_container : BlueprintEntity.container
---@field control_behavior BlueprintControlBehavior.logistic_container
---@field request_filters BlueprintFilters
script.on_event(defines.events.on_player_setup_blueprint, function (EventData)
local blueprint = EventData.stack
if not blueprint then return log("No entities in this blueprint") end
if not blueprint then error() end
local entities = blueprint.get_blueprint_entities()
if not entities then return log("No entities in this blueprint") end
for _, entity in pairs(entities) do
local type_str = entity.name:sub(1, 5)
local type = type_str == "wide-" and 0 or type_str == "tall-" and 4 or nil --[[@as defines.direction?]]
if type then
---@cast entity BlueprintEntity.container|BlueprintEntity.logistic_container
local chest = entity.name:sub(6)
entity.name = "rotatable-"..chest
entity.direction = type
entity.tags = entity.tags or {}
entity.tags.wide_chest = {
circuit = entity.control_behavior,
bar = entity.bar,
request = entity.request_filters
}--[[@as ChestTags]]
entity.control_behavior = nil
entity.bar = nil
end
end
blueprint.set_blueprint_entities(entities)
end)