243 lines
7.6 KiB
Lua

local flib_bounding_box = require("__flib__/bounding-box")
local flib_math = require("__flib__/math")
local flib_position = require("__flib__/position")
local flib_queue = require("__flib__/queue")
--- @alias RenderObjectID uint64
local function reset()
rendering.clear(script.mod_name)
--- @type Queue<RenderObjectID>
global.render_objects = flib_queue.new()
end
--- @param id RenderObjectID
local function clear_sprite(id)
if not rendering.is_valid(id) then
return
end
rendering.set_visible(id, false)
flib_queue.push_back(global.render_objects, id)
end
--- @param args LuaRendering.draw_sprite_param
--- @return RenderObjectID
local function draw_sprite(args)
--- @type RenderObjectID?
local id
repeat
id = flib_queue.pop_front(global.render_objects)
until not id or rendering.get_surface(id).index == args.surface and rendering.is_valid(id)
if not id then
return rendering.draw_sprite(args)
end
rendering.set_sprite(id, args.sprite)
rendering.set_color(id, args.tint)
rendering.set_x_scale(id, args.x_scale or 1)
rendering.set_y_scale(id, args.y_scale or 1)
rendering.set_render_layer(id, args.render_layer)
rendering.set_orientation(id, args.orientation or 0)
rendering.set_target(id, args.target)
rendering.set_players(id, args.players)
rendering.set_visible(id, true)
return id
end
local layers = {
arrow = "195",
line = "194",
underground = "193",
entity = "192",
}
local pipe_types = {
["infinity-pipe"] = true,
["pipe-to-ground"] = true,
["pipe"] = true,
}
local encoded_directions = {
[defines.direction.north] = 1,
[defines.direction.east] = 2,
[defines.direction.south] = 4,
[defines.direction.west] = 8,
}
--- @type Color
local default_color = { r = 0.4, g = 0.4, b = 0.4 }
--- @type FluidSystemData
local default_fluid_system = { color = default_color, from_hover = false, order = flib_math.max_uint }
local renderer = {}
--- @param it Iterator
--- @param entity_data EntityData
function renderer.draw(it, entity_data)
local is_complex_type = not pipe_types[entity_data.entity.type]
if is_complex_type then
local box = flib_bounding_box.resize(entity_data.entity.selection_box, -0.1)
entity_data.shape = draw_sprite({
sprite = "pv-entity-box",
tint = default_color,
x_scale = flib_bounding_box.width(box),
y_scale = flib_bounding_box.height(box),
render_layer = layers.entity,
target = flib_bounding_box.center(box),
surface = entity_data.entity.surface_index,
players = { it.player_index },
})
else
local box = flib_bounding_box.ceil(entity_data.entity.selection_box)
entity_data.shape = draw_sprite({
sprite = "pv-pipe-connections-0",
tint = default_color,
x_scale = flib_bounding_box.width(box),
y_scale = flib_bounding_box.height(box),
render_layer = layers.line,
target = entity_data.entity.position,
surface = entity_data.entity.surface_index,
players = { it.player_index },
})
end
local shape_fluid_system = default_fluid_system
local encoded_connections = 0
for fluid_system_id, connections in pairs(entity_data.connections) do
local fluid_system_data = it.systems[fluid_system_id]
if not fluid_system_data then
goto continue
end
if fluid_system_data.order < shape_fluid_system.order then
shape_fluid_system = fluid_system_data
end
local objects = entity_data.connection_objects[fluid_system_id]
if not objects then
objects = {}
entity_data.connection_objects[fluid_system_id] = objects
end
for _, connection in pairs(connections) do
if not connection.target then
goto inner_continue
end
local direction = connection.direction
if is_complex_type then
if connection.flow_direction == "input" then
direction = (direction + 4) % 8 -- Opposite
end
local sprite = "pv-fluid-arrow-" .. connection.flow_direction
if connection.flow_direction ~= "input-output" and not pipe_types[connection.target_owner.type] then
sprite = "pv-fluid-arrow"
end
objects[#objects + 1] = draw_sprite({
sprite = sprite,
tint = fluid_system_data.color,
render_layer = layers.arrow,
orientation = direction / 8,
target = connection.shape_position,
surface = entity_data.entity.surface_index,
players = { it.player_index },
})
else
encoded_connections = bit32.bor(encoded_connections, encoded_directions[direction])
end
if connection.connection_type == "underground" then
local target_data = it.entities[
connection.target_owner.unit_number --[[@as uint]]
]
if not target_data then
goto inner_continue
end
local target_fluid_system_connections = target_data.connections[fluid_system_id]
if not target_fluid_system_connections then
goto inner_continue
end
local target_connection_data = target_fluid_system_connections[connection.target_pipe_connection_index]
local target_position = target_connection_data.position
local distance = flib_position.distance(connection.position, target_connection_data.position)
for i = 1, distance - 1 do
local target = flib_position.lerp(connection.position, target_position, i / distance)
objects[#objects + 1] = draw_sprite({
sprite = "pv-underground-connection",
tint = fluid_system_data.color,
render_layer = layers.underground,
orientation = direction / 8,
target = target,
surface = entity_data.entity.surface_index,
players = { it.player_index },
})
end
end
::inner_continue::
end
::continue::
end
if entity_data.shape then
rendering.set_color(entity_data.shape, shape_fluid_system.color)
if encoded_connections > 0 then
rendering.set_sprite(entity_data.shape, "pv-pipe-connections-" .. encoded_connections)
end
end
end
--- @param entity_data EntityData
function renderer.clear(entity_data)
clear_sprite(entity_data.shape)
entity_data.shape = nil
for _, objects in pairs(entity_data.connection_objects) do
for _, id in pairs(objects) do
clear_sprite(id)
end
end
entity_data.connection_objects = {}
end
--- @param iterator Iterator
--- @param entity_data EntityData
--- @param fluid_system_id FluidSystemID
--- @return boolean should_remove
function renderer.clear_system(iterator, entity_data, fluid_system_id)
local objects = entity_data.connection_objects[fluid_system_id]
if objects then
for _, id in pairs(objects) do
clear_sprite(id)
end
entity_data.connection_objects[fluid_system_id] = nil
end
local should_remove = not next(entity_data.connection_objects)
if should_remove then
clear_sprite(entity_data.shape)
entity_data.shape = nil
else
renderer.update_shape_color(iterator, entity_data)
end
return should_remove
end
--- @param iterator Iterator
--- @param entity_data EntityData
function renderer.update_shape_color(iterator, entity_data)
--- @type FluidSystemData
local shape_fluid_system = { color = default_color, order = flib_math.max_uint }
for fluid_system_id in pairs(entity_data.connection_objects) do
local fluid_system_data = iterator.systems[fluid_system_id]
if fluid_system_data and fluid_system_data.order < shape_fluid_system.order then
shape_fluid_system = fluid_system_data
end
end
rendering.set_color(entity_data.shape, shape_fluid_system.color)
end
renderer.on_init = reset
renderer.on_configuration_changed = reset
renderer.reset = reset
return renderer