Skip to content

Commit

Permalink
fluids: we now announce pipe shapes, for example horizontal, vertical…
Browse files Browse the repository at this point in the history
…, and corner south and east
  • Loading branch information
ahicks92 committed Nov 5, 2024
1 parent 0e5b111 commit f069a5b
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 4 deletions.
57 changes: 53 additions & 4 deletions scripts/fa-info.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ local Driving = require("scripts.driving")
local Electrical = require("scripts.electrical")
local Equipment = require("scripts.equipment")
local FaUtils = require("scripts.fa-utils")
local Fluids = require("scripts.fluids")
local Graphics = require("scripts.graphics")
local Localising = require("scripts.localising")
local MessageBuilder = require("scripts.message-builder")
Expand Down Expand Up @@ -399,9 +400,57 @@ local function ent_info_infinity_pipe(ctx)
end

---@param ctx fa.info.EntInfoContext
local function ent_info_is_pipe_end(ctx)
if ctx.ent.name == "pipe" and BuildingTools.is_a_pipe_end(ctx.ent) then
ctx.message:fragment({ "fa.ent-info-pipe-end" })
local function ent_info_pipe_shape(ctx)
if ctx.ent.type == "pipe" then
local shape_info = Fluids.get_pipe_shape(ctx.ent)
local s, d = shape_info.shape, shape_info.direction
local d_str = FaUtils.direction_lookup(d)
local conns = ctx.ent.fluidbox.get_pipe_connections(1)
local conn_count = 0
for _, c in pairs(conns) do
if c.target then conn_count = conn_count + 1 end
end

-- We must be careful. Pipe shapes do not account for other kinds of
-- connection, so we must compare with the expected count as well.
-- Otherwise we will say that a pipe is both connected and not connected
-- at the same time.

-- This is just a boring if table which appends fragments. no special
-- logic here.
if s == Fluids.PIPE_SHAPE.END and conn_count == 1 then
ctx.message:fragment({ "fa.ent-info-pipe-end", FaUtils.direction_lookup(FaUtils.rotate_180(d)) })
elseif s == Fluids.PIPE_SHAPE.ALONE and conn_count == 0 then
ctx.message:fragment({ "fa.ent-info-pipe-alone" })
elseif s == Fluids.PIPE_SHAPE.STRAIT and conn_count == 2 then
local key = d == defines.direction.north and "fa.ent-info-pipe-vertical" or "fa.ent-info-pipe-horizontal"
ctx.message:fragment({ key })
elseif s == Fluids.PIPE_SHAPE.CORNER and conn_count == 2 then
local c1, c2
if d == defines.direction.northwest then
c1 = defines.direction.south
c2 = defines.direction.east
elseif d == defines.direction.northeast then
c1 = defines.direction.south
c2 = defines.direction.west
elseif d == defines.direction.southwest then
c1 = defines.direction.north
c2 = defines.direction.east
elseif d == defines.direction.southeast then
c1 = defines.direction.north
c2 = defines.direction.west
else
error("unreachable! " .. serpent.line({ s = s, d = d }))
end

ctx.message:fragment({ "fa.ent-info-pipe-corner", FaUtils.direction_lookup(c1), FaUtils.direction_lookup(c2) })
elseif s == Fluids.PIPE_SHAPE.CROSS and conn_count == 4 then
ctx.message:fragment({ "fa.ent-info-pipe-cross" })
elseif s == Fluids.PIPE_SHAPE.T then
local key = "fa.ent-info-pipe-t-vertical"
if d == defines.direction.north or d == defines.direction.south then key = "fa.ent-info-pipe-t-horizontal" end
ctx.message:fragment({ key, FaUtils.direction_lookup(FaUtils.rotate_180(d)) })
end
end
end

Expand Down Expand Up @@ -646,7 +695,7 @@ function mod.ent_info(pindex, ent, is_scanner)
run_handler(ent_info_container)
run_handler(ent_info_logistic_network)
run_handler(ent_info_infinity_pipe)
run_handler(ent_info_is_pipe_end)
run_handler(ent_info_pipe_shape)
run_handler(ent_info_fluid_transport)

run_handler(ent_info_underground_belt_type)
Expand Down
104 changes: 104 additions & 0 deletions scripts/fluids.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Most of the time it's up to 1. It's unclear if or under what circumstances it
can be more than 1, but it is probably possible for that to happen. As of
2024-11-04, it is unclear to us just when that can happen in the new fluid
system.
Note that the shape computation logic is reused by heat. Heat isn't a fluid and
has a number of "interesting" special cases, but the corner shapes are the same.
]]
local FaUtils = require("scripts.fa-utils")
local TH = require("scripts.table-helpers")
Expand Down Expand Up @@ -104,4 +107,105 @@ function mod.get_connection_points(ent)
return res
end

---@enum fa.fluids.PipeShape
mod.PIPE_SHAPE = {
STRAIT = "straight",

-- 4-way, a cross
CROSS = "cross",
CORNER = "corner",

END = "end",

-- It's not connecting to anything.
ALONE = "alone",

T = "t",
}

---@alias fa.fluids.ShapeDef { shape: fa.fluids.PipeShape, direction: defines.direction }

-- north->east->south->west->shape, true if the direction is present, false if
-- it is not. Used below.
---@type table<boolean, table<boolean, table<boolean, table<boolean, fa.fluids.ShapeDef>>>>
local SHAPE_TABLE = {}

local function add_shape(n, e, s, w, shape, direction)
SHAPE_TABLE[n] = SHAPE_TABLE[n] or {}
SHAPE_TABLE[n][e] = SHAPE_TABLE[n][e] or {}
SHAPE_TABLE[n][e][s] = SHAPE_TABLE[n][e][s] or {}
SHAPE_TABLE[n][e][s][w] = { shape = shape, direction = direction }
end

add_shape(true, true, true, true, mod.PIPE_SHAPE.CROSS, defines.direction.north)
add_shape(true, false, true, false, mod.PIPE_SHAPE.STRAIT, defines.direction.north)
add_shape(false, true, false, true, mod.PIPE_SHAPE.STRAIT, defines.direction.east)
add_shape(true, true, false, false, mod.PIPE_SHAPE.CORNER, defines.direction.southwest)
add_shape(false, true, true, false, mod.PIPE_SHAPE.CORNER, defines.direction.northwest)
add_shape(false, false, true, true, mod.PIPE_SHAPE.CORNER, defines.direction.northeast)
add_shape(true, false, false, true, mod.PIPE_SHAPE.CORNER, defines.direction.southeast)
add_shape(true, false, false, false, mod.PIPE_SHAPE.END, defines.direction.north)
add_shape(false, true, false, false, mod.PIPE_SHAPE.END, defines.direction.east)
add_shape(false, false, true, false, mod.PIPE_SHAPE.END, defines.direction.south)
add_shape(false, false, false, true, mod.PIPE_SHAPE.END, defines.direction.west)
add_shape(true, true, true, false, mod.PIPE_SHAPE.T, defines.direction.west)
add_shape(true, true, false, true, mod.PIPE_SHAPE.T, defines.direction.south)
add_shape(true, false, true, true, mod.PIPE_SHAPE.T, defines.direction.east)
add_shape(false, true, true, true, mod.PIPE_SHAPE.T, defines.direction.north)
add_shape(false, false, false, false, mod.PIPE_SHAPE.ALONE)

--[[
Given a pipe entity, determine to which pipes it is connected and pass back
information on the shape. This comes back as two values, a kind and a
direction. Shape interpretations are as follows:
- straight: a line of 3 segments and this pipe is the middle. Direction is
either north or east, specifying if it is vertical or horizontal.
- end: has one connection. Direction is the way the end "points" e.g. 180 from
the connection.
- corner: an L formed of 3 pipe segments. The direction is the corner of the
imaginary box which would be formed of 4 corners. FOr example, northwest
means connecting south and east, because if you completed the box it'd be the
northwest corner.
- Cross: direction is returned as north. Looks like a cross, which is invariant
under rotation.
- alone: nothing connects, will just pass back north.
- T: the direction is the top of the T, e.g. the missing one.
This mismatches fa-info. The trouble is that the above directions make sense
for analysis and exposing them directly woiuld be the least verbose option, but
that relies on blind people knowing what a T is, and figuring out what
"northwest corner" means--we'd have to teach it. We probably do at some point,
but that's not now, so fa-info simplifies.
This function considers only pipe entities, and ignores undergrounds.
Undergrounds need to be announced separately.
]]
---@param ent LuaEntity
---@return { shape: fa.fluids.PipeShape, direction: defines.direction }
function mod.get_pipe_shape(ent)
local dirs = {}

assert(ent.type == "pipe")
local fb = ent.fluidbox
assert(#fb == 1)

local conns = fb.get_pipe_connections(1)
for _, conn in pairs(conns) do
if conn.target and conn.target.owner.type == "pipe" then
local v = { x = conn.target_position.x - conn.position.x, y = conn.target_position.y - conn.position.y }
local dir = FaUtils.direction_of_vector(v)
assert(dir)
dirs[dir] = true
end
end

local shape =
SHAPE_TABLE[dirs[defines.direction.north] ~= nil][dirs[defines.direction.east] ~= nil][dirs[defines.direction.south] ~= nil][dirs[defines.direction.west] ~= nil]
if not shape then error(string.format("No shape for %s", serpent.line(dirs))) end

-- Don't let callers modify what is otherwise a constant.
return table.deepcopy(shape)
end

return mod

0 comments on commit f069a5b

Please sign in to comment.