344 lines
14 KiB
Lua

local const = require("scripts/constants")
local utils = require("scripts/utils")
local draw = require("scripts/rendering")
local get_belt_type = utils.get_belt_type
local empty_check = utils.empty_check
local check_entity = utils.check_entity
local lane_cycle = const.lane_cycle
local side_cycle = const.side_cycle
local straight = const.straight
local underground = const.underground
local dash = const.dash
local splitter = const.splitter
local loader = const.loader
local loader_1x1 = const.loader_1x1
local linked_belt = const.linked_belt
local function is_clockwise(entity, output)
return (output.direction - entity.direction) % 8 == 2
end
local function get_splitter_sides(entity, belt)
local direction = entity.direction
local position = entity.position
local belt_position = belt.position
local axis = direction % 4 == 0 and "x" or "y"
if position[axis] == belt_position[axis] then return side_cycle.both end
return (position[axis] > belt_position[axis]) ~= (direction >= 4) and side_cycle.left or side_cycle.right
end
local function get_filter_side(data, entity)
if not data.filter then return end
local splitter_filter = entity.splitter_filter
if not splitter_filter then return end
local output_priority = entity.splitter_output_priority
if output_priority == "none" then return end
return (output_priority == "left") == (data.filter == splitter_filter.name) and "left" or "right"
end
local function get_input_paths(check, lanes)
for lane, paths in pairs(check) do
if paths[1] then
lanes[lane] = true
end
end
end
local function get_input_lanes(data, entity, input)
local check = data.checked[input.unit_number]
local lanes = {}
if get_belt_type(input) == "splitter" then
for _, side_check in pairs(check) do
get_input_paths(side_check, lanes)
end
else
get_input_paths(check, lanes)
end
return lanes
end
local default_output = {"output", "output"}
local function get_output_lanes(data, entity, lanes, output)
if not output or entity.direction == output.direction then return lanes, default_output end
if not data.ghost and output.type == "entity-ghost" then return lanes, default_output end
local clockwise = is_clockwise(entity, output)
local next_lanes = {}
local offsets = {}
if get_belt_type(output) == "underground-belt" then
for lane in pairs(lanes) do
local type = output.belt_to_ground_type == "input"
if (clockwise == type) == (lane == 1) then
next_lanes[clockwise and 2 or 1] = true
offsets[lane] = "sideload"
else
offsets[lane] = "output"
end
end
else
if #output.belt_neighbours.inputs ~= 1 then
for lane in pairs(lanes) do
next_lanes[clockwise and 2 or 1] = true
offsets[lane] = "sideload"
end
else
return lanes, default_output
end
end
return next_lanes, offsets
end
local function get_prev_lanes(entity, lanes, input)
if entity.direction == input.direction then return lanes end
local clockwise = is_clockwise(input, entity)
for lane in pairs(lanes) do
if clockwise == (lane == 2) then
if get_belt_type(entity) == "underground-belt" then
local type = entity.belt_to_ground_type == "input"
return lane_cycle[clockwise == type and 2 or 3]
else
return lane_cycle[1]
end
end
end
end
local function add_to_queue(data, entity, lanes, path, old_entity)
if not entity then return end
local belt_type = entity.type
if belt_type == "entity-ghost" then
if data.ghost then
belt_type = entity.ghost_type
else return end
end
local is_splitter = belt_type == "splitter"
local sides
if is_splitter then
local const_sides = get_splitter_sides(entity, old_entity)
sides = {}
for side in pairs(const_sides) do
sides[side] = true
end
if path == 2 then
local filter_side = get_filter_side(data, entity)
if filter_side then
for side in pairs(const_sides) do
if filter_side ~= side then
sides[side] = nil
end
end
if not next(sides) then return end
end
end
end
local unit_number = entity.unit_number
local checked = data.checked
local new_lanes = {}
for lane in pairs(lanes) do
local check
if checked[unit_number] then
if is_splitter then
for side in pairs(sides) do
check = checked[unit_number][side][lane][path]
end
else
check = checked[unit_number][lane][path]
end
else
checked[unit_number] = empty_check(belt_type)
end
if not check then
new_lanes[lane] = true
check_entity(data, unit_number, lane, path, sides)
end
end
if next(new_lanes) then
local next_entities = data.next_entities
local next_len = data.next_len + 1
data.next_len = next_len
next_entities[next_len] = {entity = entity, lanes = new_lanes, path = path}
end
end
local highlight_entity = {}
highlight_entity["transport-belt"] = function(data, entity, lanes, path)
local direction = entity.direction
local belt_neighbours = entity.belt_neighbours
local inputs = belt_neighbours.inputs
local output = belt_neighbours.outputs[1]
local is_curved = (#inputs == 1) and (direction ~= inputs[1].direction)
local next_lanes, lane_offsets = get_output_lanes(data, entity, lanes, output)
for lane in pairs(lanes) do
local offsets = straight[lane][direction]
local lane_offset = lane_offsets[lane]
if not is_curved then
draw.line(data, entity, offsets.input, offsets[lane_offset])
else
draw.arc(data, entity, lane, is_clockwise(inputs[1], entity))
if lane_offset == "sideload" then
draw.line(data, entity, offsets.output, offsets[lane_offset])
end
end
end
if path == 1 then
add_to_queue(data, output, next_lanes, 1, entity)
else
for _, input in pairs(inputs) do
local prev_lanes = is_curved and lanes or get_prev_lanes(entity, lanes, input)
if prev_lanes then add_to_queue(data, input, prev_lanes, 2, entity) end
end
end
end
highlight_entity["underground-belt"] = function(data, entity, lanes, path)
local direction = entity.direction
local belt_neighbours = entity.belt_neighbours
local output = belt_neighbours.outputs[1]
local type = entity.belt_to_ground_type
local is_input = type == "input"
local next_lanes, lane_offsets = get_output_lanes(data, entity, lanes, output)
for lane in pairs(lanes) do
local lane_offset = is_input and "input" or lane_offsets[lane]
draw.line(data, entity, underground[lane][direction][type], straight[lane][direction][lane_offset])
end
local forward = path == 1
if forward then
add_to_queue(data, output, next_lanes, path, entity)
else
for _, input in pairs(belt_neighbours.inputs) do
local prev_lanes = get_prev_lanes(entity, lanes, input)
if prev_lanes then add_to_queue(data, input, prev_lanes, path, entity) end
end
end
local neighbour = entity.neighbours
if forward == is_input and neighbour then
local check = data.checked[entity.unit_number]
local neighbour_check = data.checked[neighbour.unit_number]
for lane in pairs(lanes) do
if not (neighbour_check and neighbour_check[lane].dash) then
local offsets = dash[lane][direction]
draw.dash(data, is_input and entity or neighbour, offsets.input, offsets.output)
end
check[lane].dash = true
end
add_to_queue(data, neighbour, lanes, path, entity)
end
end
highlight_entity["splitter"] = function(data, entity, lanes, path)
local direction = entity.direction
local belt_neighbours = entity.belt_neighbours
local forward = path == 1
local belts = {}
for _, belt in pairs(belt_neighbours[forward and "outputs" or "inputs"]) do
for side in pairs(get_splitter_sides(entity, belt)) do
if forward or get_belt_type(belt) ~= "splitter" or get_filter_side(data, belt) ~= side then
belts[side] = belt
end
end
end
local queued
local filter_side = get_filter_side(data, entity)
for side in pairs(forward and side_cycle[filter_side] or side_cycle.both) do
local next_lanes, lane_offsets = get_output_lanes(data, entity, lanes, belts[side])
for lane in pairs(lanes) do
local offsets = splitter[lane][direction]
local side_offsets = offsets[side]
local lane_offset = forward and lane_offsets[lane] or "input"
draw.line(data, entity, side_offsets.middle, side_offsets[lane_offset])
draw.line(data, entity, offsets.left.line, offsets.right.line)
end
if queued ~= belts[side] then
add_to_queue(data, belts[side], next_lanes, path, entity)
queued = belts[side]
end
end
for _, belt in pairs(belt_neighbours[forward and "inputs" or "outputs"]) do
local belt_check = data.checked[belt.unit_number]
if belt_check then
local checked_lanes = forward and get_input_lanes(data, entity, belt) or lanes
local _, lane_offsets = get_output_lanes(data, entity, lanes, belt)
local sides = not forward and side_cycle[filter_side] or get_splitter_sides(entity, belt)
for side in pairs(sides) do
for lane in pairs(checked_lanes) do
local belt_path
if get_belt_type(belt) == "splitter" then
belt_path = belt_check.left[lane][path] or belt_check.right[lane][path]
else
belt_path = belt_check[lane][path]
end
if belt_path then
local side_offsets = splitter[lane][direction][side]
local lane_offset = forward and "input" or lane_offsets[lane]
draw.line(data, entity, side_offsets.middle, side_offsets[lane_offset])
end
end
end
end
end
end
highlight_entity["linked-belt"] = function(data, entity, lanes, path)
local direction = entity.direction
local belt_neighbours = entity.belt_neighbours
local output = belt_neighbours.outputs[1]
local linked_belt_neighbour = entity.linked_belt_neighbour
local is_input = entity.linked_belt_type == "input"
local forward = path == 1
local next_lanes, lane_offsets = get_output_lanes(data, entity, lanes, output)
for lane in pairs(lanes) do
local offsets = linked_belt[lane][direction]
local middle = offsets.middle
local lane_offset = is_input and "input" or lane_offsets[lane]
draw.line(data, entity, middle, offsets[lane_offset])
draw.circle(data, entity, middle)
end
add_to_queue(data, forward and output or belt_neighbours.inputs[1], next_lanes, path, entity)
if is_input == forward then
add_to_queue(data, linked_belt_neighbour, lanes, path, entity)
end
end
---@return fun(data: table, entity: LuaEntity, lanes: table, path: 1|2)
local function highlight_loader(loader_const)
return function(data, entity, lanes, path)
local direction = entity.direction
local belt_neighbours = entity.belt_neighbours
local output = belt_neighbours.outputs[1]
local loader_type = entity.loader_type
local next_lanes, lane_offsets = get_output_lanes(data, entity, lanes, output)
for lane in pairs(lanes) do
local offsets = loader_const[lane][direction]
local lane_offset = lane_offsets[lane]
draw.line(data, entity, offsets.input, offsets[lane_offset])
draw.rectangle(data, entity, loader_const.rectangle[lane][direction][loader_type])
end
local forward = path == 1
local new_entity = forward and output or belt_neighbours.inputs[1]
add_to_queue(data, new_entity, next_lanes, path, entity)
if entity.type == "entity-ghost" then return end -- TODO: ACTUALLY FIX
if data.container_passthrough and forward == (loader_type == "input") then
local container = entity.loader_container
if container and (container.type == "container" or container.type == "logistic-container" or container.type == "infinity-container") then
local box = container.prototype.collision_box
local lt, rb = box.left_top, box.right_bottom
local pos = container.position
local loaders = container.surface.find_entities_filtered{
area = {{pos.x + lt.x - 1, pos.y + lt.y - 1}, {pos.x + rb.x + 1, pos.y + rb.y + 1}},
type = {"loader", "loader-1x1"},
}
for _, loader in pairs(loaders) do
local loader_container = loader.loader_container
if loader ~= entity and loader_container and loader_container == container and loader_type ~= loader.loader_type then
add_to_queue(data, loader, lane_cycle[1], path, entity)
end
end
end
end
end
end
highlight_entity["loader"] = highlight_loader(loader)
highlight_entity["loader-1x1"] = highlight_loader(loader_1x1)
return highlight_entity