416 lines
13 KiB
Lua
416 lines
13 KiB
Lua
local util = {}
|
|
|
|
-- Position adjustments
|
|
|
|
function util.moveposition(position, offset)
|
|
return {x=position.x + offset.x, y=position.y + offset.y}
|
|
end
|
|
|
|
function util.offset(direction, longitudinal, orthogonal)
|
|
if direction == defines.direction.north then
|
|
return {x=orthogonal, y=-longitudinal}
|
|
end
|
|
|
|
if direction == defines.direction.south then
|
|
return {x=-orthogonal, y=longitudinal}
|
|
end
|
|
|
|
if direction == defines.direction.east then
|
|
return {x=longitudinal, y=orthogonal}
|
|
end
|
|
|
|
if direction == defines.direction.west then
|
|
return {x=-longitudinal, y=-orthogonal}
|
|
end
|
|
end
|
|
|
|
-- BoundingBox utilities
|
|
|
|
--[[
|
|
+----------------------+
|
|
| |
|
|
| |
|
|
| |
|
|
| O |
|
|
| |
|
|
+----------------------+
|
|
]]
|
|
function util.rotate_box(box, direction)
|
|
local left = box.left_top.x
|
|
local top = box.left_top.y
|
|
local right = box.right_bottom.x
|
|
local bottom = box.right_bottom.y
|
|
|
|
if direction == defines.direction.north then
|
|
return box
|
|
elseif direction == defines.direction.east then
|
|
-- 90 degree rotation
|
|
return {
|
|
left_top = {x=-bottom, y=left},
|
|
right_bottom = {x=-top, y=right},
|
|
}
|
|
elseif direction == defines.direction.south then
|
|
-- 180 degree rotation
|
|
return {
|
|
left_top = {x=-right, y=-bottom},
|
|
right_bottom = {x=-left, y=-top},
|
|
}
|
|
elseif direction == defines.direction.west then
|
|
-- 270 degree rotation
|
|
return {
|
|
left_top = {x=top, y=-right},
|
|
right_bottom = {x=bottom, y=-left},
|
|
}
|
|
else
|
|
error('invalid direction passed to rotate_box')
|
|
end
|
|
end
|
|
|
|
function util.move_box(box, offset)
|
|
return {
|
|
left_top = util.moveposition(box.left_top, offset),
|
|
right_bottom = util.moveposition(box.right_bottom, offset),
|
|
}
|
|
end
|
|
|
|
function util.expand_box(box, size)
|
|
return {
|
|
left_top = { x = box.left_top.x - size, y = box.left_top.y - size },
|
|
right_bottom = { x = box.right_bottom.x + size, y = box.right_bottom.y + size },
|
|
}
|
|
end
|
|
|
|
function util.entity_key(entity)
|
|
return entity.surface.name.."@"..entity.position.x..","..entity.position.y
|
|
end
|
|
|
|
-- Direction utilities
|
|
|
|
function util.is_ns(direction)
|
|
return direction == 0 or direction == 4
|
|
end
|
|
|
|
function util.is_ew(direction)
|
|
return direction == 2 or direction == 6
|
|
end
|
|
|
|
function util.opposite_direction(direction)
|
|
if direction >= 4 then
|
|
return direction - 4
|
|
end
|
|
return direction + 4
|
|
end
|
|
|
|
-- orientation utilities
|
|
|
|
-- hood_side returns the "back" or hood side of a loader or underground belt
|
|
function util.hood_side(entity)
|
|
if entity.type == "loader-1x1" and entity.loader_type == "output" then
|
|
return util.opposite_direction(entity.direction)
|
|
end
|
|
if entity.type == "underground-belt" and entity.belt_to_ground_type == "output" then
|
|
return util.opposite_direction(entity.direction)
|
|
end
|
|
return entity.direction
|
|
end
|
|
|
|
-- belt_side returns the "front" side of a loader or underground belt
|
|
function util.belt_side(entity)
|
|
if entity.type == "loader-1x1" and entity.loader_type == "input" then
|
|
return util.opposite_direction(entity.direction)
|
|
end
|
|
if entity.type == "underground-belt" and entity.belt_to_ground_type == "input" then
|
|
return util.opposite_direction(entity.direction)
|
|
end
|
|
return entity.direction
|
|
end
|
|
|
|
-- miniloader utilities
|
|
|
|
function util.find_miniloaders(params)
|
|
params.type = "loader-1x1"
|
|
local entities = params.surface.find_entities_filtered(params)
|
|
local out = {}
|
|
for i=1,#entities do
|
|
local ent = entities[i]
|
|
if util.is_miniloader(ent) then
|
|
out[#out+1] = ent
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
function util.is_miniloader(entity)
|
|
return string.find(entity.name, "miniloader%-loader$") ~= nil
|
|
end
|
|
|
|
function util.is_miniloader_inserter(entity)
|
|
return util.is_miniloader_inserter_name(entity.name)
|
|
end
|
|
|
|
function util.is_miniloader_inserter_name(name)
|
|
return name:find("miniloader%-inserter$") ~= nil
|
|
end
|
|
|
|
function util.is_output_miniloader_inserter(inserter)
|
|
local orientation = util.orientation_from_inserter(inserter)
|
|
return orientation and orientation.type == "output"
|
|
end
|
|
|
|
-- 60 items/second / 60 ticks/second / 8 items/tile = X tiles/tick
|
|
local BELT_SPEED_FOR_60_PER_SECOND = 60/60/8
|
|
function util.num_inserters(entity)
|
|
return math.ceil(entity.prototype.belt_speed / BELT_SPEED_FOR_60_PER_SECOND) * 2
|
|
end
|
|
|
|
function util.pickup_position(entity)
|
|
if entity.loader_type == "output" then
|
|
return util.moveposition(entity.position, util.offset(entity.direction, -0.8, 0))
|
|
end
|
|
return util.moveposition(entity.position, util.offset(entity.direction, -0.2, 0))
|
|
end
|
|
|
|
local moveposition = util.moveposition
|
|
local offset = util.offset
|
|
-- drop positions for input (belt->chest) = { 0.7, +-0.25}, { 0.9, +-0.25}, {1.1, +-0.25}, {1.3, +-0.25}
|
|
-- drop positions for output (chest->belt) = {-0.2, +-0.25}, {-0.0, +-0.25}, {0.1, +-0.25}, {0.3, +-0.25}
|
|
function util.drop_positions(entity)
|
|
local base_offset = 1.2
|
|
if entity.loader_type == "output" then
|
|
base_offset = base_offset - 1
|
|
end
|
|
local out = {}
|
|
local dir = entity.direction
|
|
local p1 = moveposition(entity.position, offset(dir, base_offset, -0.25))
|
|
local p2 = moveposition(p1, offset(dir, 0, 0.5))
|
|
out[1] = p1
|
|
out[2] = p2
|
|
for i=1,3 do
|
|
local j = i * 2 + 1
|
|
out[j ] = moveposition(p1, offset(dir, -0.20*i, 0))
|
|
out[j+1] = moveposition(p2, offset(dir, -0.20*i, 0))
|
|
end
|
|
for i=0,3 do
|
|
local j = i * 2 + 9
|
|
out[j ] = moveposition(p1, offset(dir, -0.20*i, 0))
|
|
out[j+1] = moveposition(p2, offset(dir, -0.20*i, 0))
|
|
end
|
|
return out
|
|
end
|
|
|
|
function util.get_loader_inserters(entity)
|
|
local out = {}
|
|
for _, e in pairs(entity.surface.find_entities_filtered{
|
|
position = entity.position,
|
|
type = "inserter",
|
|
}) do
|
|
if util.is_miniloader_inserter(e) then
|
|
out[#out+1] = e
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function update_miniloader_ghost(ghost, direction, type)
|
|
local position = ghost.position
|
|
-- We should normally destroy the ghost and recreate it facing the right direction,
|
|
-- but destroying an entity during its on_built_entity handler makes for compatibility
|
|
-- headaches.
|
|
|
|
-- add offset within tile to inform orientation_from_inserters that this ghost is preconfigured
|
|
if type == "input" then
|
|
ghost.pickup_position = moveposition(position, offset(direction, 0.25, 0.25))
|
|
ghost.drop_position = moveposition(position, offset(direction, 1, 0.25))
|
|
else
|
|
ghost.pickup_position = moveposition(position, offset(direction, -1, 0.25))
|
|
ghost.drop_position = moveposition(position, offset(direction, 0.25, 0.25))
|
|
end
|
|
end
|
|
|
|
function util.update_miniloader(entity, direction, type)
|
|
if entity.type == "entity-ghost" and entity.ghost_type == "inserter" then
|
|
return update_miniloader_ghost(entity, direction, type)
|
|
end
|
|
if entity.loader_type ~= type then
|
|
entity.rotate()
|
|
end
|
|
entity.direction = direction
|
|
util.update_inserters(entity)
|
|
end
|
|
|
|
local function dump_miniloader(entity)
|
|
local inserters = util.get_loader_inserters(entity)
|
|
local info = {}
|
|
for _, inserter in ipairs(inserters) do
|
|
info[#info+1] = inserter.unit_number..":"..serpent.line(inserter.get_or_create_control_behavior().circuit_condition)
|
|
end
|
|
return table.concat(info, "; ")
|
|
end
|
|
|
|
function util.update_inserters(entity)
|
|
local inserters = util.get_loader_inserters(entity)
|
|
local pickup = util.pickup_position(entity)
|
|
local drop = util.drop_positions(entity)
|
|
local direction = entity.direction
|
|
local loader_type = entity.loader_type
|
|
if loader_type == "input" then
|
|
direction = util.opposite_direction(direction)
|
|
end
|
|
|
|
for i=#inserters,1,-1 do
|
|
inserters[i].direction = direction
|
|
inserters[i].pickup_position = pickup
|
|
inserters[i].drop_position = drop[i]
|
|
if loader_type == "input" then
|
|
inserters[i].pickup_target = entity
|
|
else
|
|
inserters[i].drop_target = entity
|
|
end
|
|
end
|
|
end
|
|
|
|
function util.select_main_inserter(surface, position)
|
|
local inserters = surface.find_entities_filtered{type = "inserter", position = position}
|
|
if not next(inserters) then
|
|
inserters = surface.find_entities_filtered{ghost_type = "inserter", position = position}
|
|
end
|
|
|
|
if not next(inserters) then return nil end
|
|
for _, inserter in ipairs(inserters) do
|
|
if inserter.pickup_target == nil and inserter.drop_target == nil then
|
|
return inserter
|
|
end
|
|
end
|
|
return inserters[1]
|
|
end
|
|
|
|
function util.orientation_from_bp_inserter(bp_inserter)
|
|
local position_x = bp_inserter.position.x
|
|
local position_y = bp_inserter.position.y
|
|
local drop_position_x = bp_inserter.drop_position.x + position_x
|
|
local drop_position_y = bp_inserter.drop_position.y + position_y
|
|
local pickup_position_x = bp_inserter.pickup_position.x + position_x
|
|
local pickup_position_y = bp_inserter.pickup_position.y + position_y
|
|
if drop_position_x == position_x or drop_position_y == position_y then
|
|
return nil -- freshly placed with no inherited positions
|
|
elseif drop_position_x > position_x + 0.5 then
|
|
return {direction=defines.direction.east, type="input"}
|
|
elseif drop_position_x < position_x - 0.5 then
|
|
return {direction=defines.direction.west, type="input"}
|
|
elseif drop_position_y > position_y + 0.5 then
|
|
return {direction=defines.direction.south, type="input"}
|
|
elseif drop_position_y < position_y - 0.5 then
|
|
return {direction=defines.direction.north, type="input"}
|
|
elseif pickup_position_x > position_x + 0.5 then
|
|
return {direction=defines.direction.west, type="output"}
|
|
elseif pickup_position_x < position_x - 0.5 then
|
|
return {direction=defines.direction.east, type="output"}
|
|
elseif pickup_position_y > position_y + 0.5 then
|
|
return {direction=defines.direction.north, type="output"}
|
|
elseif pickup_position_y < position_y - 0.5 then
|
|
return {direction=defines.direction.south, type="output"}
|
|
end
|
|
end
|
|
|
|
function util.orientation_from_inserter(inserter)
|
|
if inserter.drop_position.x == inserter.position.x and inserter.drop_position.y == inserter.position.y then
|
|
return nil -- freshly placed with no inherited positions
|
|
elseif inserter.drop_position.x > inserter.position.x + 0.5 then
|
|
return {direction=defines.direction.east, type="input"}
|
|
elseif inserter.drop_position.x < inserter.position.x - 0.5 then
|
|
return {direction=defines.direction.west, type="input"}
|
|
elseif inserter.drop_position.y > inserter.position.y + 0.5 then
|
|
return {direction=defines.direction.south, type="input"}
|
|
elseif inserter.drop_position.y < inserter.position.y - 0.5 then
|
|
return {direction=defines.direction.north, type="input"}
|
|
elseif inserter.pickup_position.x > inserter.position.x + 0.5 then
|
|
return {direction=defines.direction.west, type="output", is_secondary=inserter.drop_position.y < inserter.position.y}
|
|
elseif inserter.pickup_position.x < inserter.position.x - 0.5 then
|
|
return {direction=defines.direction.east, type="output", is_secondary=inserter.drop_position.y > inserter.position.y}
|
|
elseif inserter.pickup_position.y > inserter.position.y + 0.5 then
|
|
return {direction=defines.direction.north, type="output", is_secondary=inserter.drop_position.x > inserter.position.x}
|
|
elseif inserter.pickup_position.y < inserter.position.y - 0.5 then
|
|
return {direction=defines.direction.south, type="output", is_secondary=inserter.drop_position.x < inserter.position.x}
|
|
end
|
|
end
|
|
|
|
function util.orientation_from_inserters(entity)
|
|
local inserter = util.select_main_inserter(entity.surface, entity.position)
|
|
return util.orientation_from_inserter(inserter)
|
|
end
|
|
|
|
function util.rebuild_belt(entity)
|
|
local surface = entity.surface
|
|
local name = entity.name
|
|
local protos = game.get_filtered_entity_prototypes{{filter="type", type=entity.type}}
|
|
local temporary_replacement
|
|
for proto_name in pairs(protos) do
|
|
if proto_name ~= name and proto_name ~= "__self" then
|
|
temporary_replacement = proto_name
|
|
break
|
|
end
|
|
end
|
|
if not temporary_replacement then return false end
|
|
|
|
local last_user = entity.last_user
|
|
local params = {
|
|
name = temporary_replacement,
|
|
position = entity.position,
|
|
direction = entity.direction,
|
|
force = entity.force,
|
|
fast_replace = true,
|
|
spill = false,
|
|
create_build_effect_smoke = false,
|
|
type = entity.type:find("loader") and entity.loader_type
|
|
or entity.type == "underground-belt" and entity.belt_to_ground_type,
|
|
}
|
|
|
|
surface.create_entity(params)
|
|
params.name = name
|
|
local belt = surface.create_entity(params)
|
|
|
|
if belt then
|
|
belt.last_user = last_user
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local control_behavior_keys = {
|
|
"circuit_condition", "logistic_condition", "connect_to_logistic_network",
|
|
"circuit_read_hand_contents", "circuit_mode_of_operation", "circuit_hand_read_mode", "circuit_set_stack_size", "circuit_stack_control_signal",
|
|
}
|
|
|
|
function util.capture_settings(ghost)
|
|
local control_behavior = ghost.get_or_create_control_behavior()
|
|
local control_behavior_state = {}
|
|
for _, key in pairs(control_behavior_keys) do
|
|
control_behavior_state[key] = control_behavior[key]
|
|
end
|
|
|
|
local filters = {}
|
|
for i=1,ghost.filter_slot_count do
|
|
filters[i] = ghost.get_filter(i)
|
|
end
|
|
|
|
return {
|
|
control_behavior = control_behavior_state,
|
|
filters = filters,
|
|
}
|
|
end
|
|
|
|
function util.apply_settings(settings, inserter)
|
|
local limit = math.min(inserter.filter_slot_count, #settings.filters)
|
|
for i = 1, limit do
|
|
inserter.set_filter(i, settings.filters[i])
|
|
end
|
|
local control_behavior = inserter.get_or_create_control_behavior()
|
|
for k, v in pairs(settings.control_behavior) do
|
|
control_behavior[k] = v
|
|
end
|
|
end
|
|
|
|
|
|
return util
|