fix victory road statues

This commit is contained in:
DramaticShape
2026-08-09 00:01:47 -04:00
parent 47369c24fe
commit 2af88d07cf
7 changed files with 501 additions and 44 deletions
+14 -7
View File
@@ -304,10 +304,15 @@ local function runGeometry(map, bodyOnly, masks, sink, waterSink)
-- The capping course an interior wall wears on its TOP face (see
-- TileShape.wallTop). A wall band folds entirely onto its own face, so
-- the top had nothing left to lay flat and repeated the face -- a house's
-- town-map poster and window, and a Center's pokeball poster, came out
-- lying across the top of the wall as well as hanging on it. Only the top
-- is redirected: the face still draws what the map draws.
-- town-map poster and window, a Center's pokeball poster and the Rocket
-- lift's doors came out lying across the top of the wall as well as
-- standing in it. Only the top is redirected: the face still draws what
-- the map draws.
local wallTop = TileShape.wallTop(tileset.id)
local function capOf(s, tile)
if not (wallTop and s.class == "wall") then return nil end
return wallTop(tile)
end
-- one atlas-rect UV, optionally cropped to art rows [vTop, vBot] of 8
local function uvRect(tile, vTop, vBot)
@@ -594,6 +599,11 @@ local function runGeometry(map, bodyOnly, masks, sink, waterSink)
{ { u0, v1 }, { u1, v1 }, { u1, v0 }, { u0, v0 } }, 0.95)
elseif run then
local topTile = map:tileAt(tx, ChunkMesher.flatTopRow(run, ty))
-- a DETECTED wall volume caps the same way a pinned one does:
-- the Rocket lift's cabin doors are found rather than pinned,
-- and their drawing lay across the top of the wall they are set
-- into (see wallTop)
topTile = capOf(s, tile) or topTile
topQuad(x0, z0, h, topTile, VOLUME_TOP_SHADE)
else
local topTile = tile
@@ -657,10 +667,7 @@ local function runGeometry(map, bodyOnly, masks, sink, waterSink)
row = (above and above.authored and above.art == "upright")
and (north - 1) or north
end
topTile = S.tileAt[keyOf(tx, row)]
-- ...unless the tileset names the course its walls cap with,
-- which every wall cell wears whatever it draws on its face
if wallTop and s.class == "wall" then topTile = wallTop end
topTile = capOf(s, tile) or S.tileAt[keyOf(tx, row)]
end
-- water's surface, and only water's: the recessed sheet itself,
-- never the ground's shoreline bands around it. A cell an object
+45 -5
View File
@@ -227,7 +227,11 @@ function Structures.forMap(map)
S = { shapeAt = shapeAt, tileAt = tileAt, outdoor = Map.isOutdoor(def),
hideBareRing = hullRingOnly or nil,
runs = {}, skip = {}, ground = {}, doorFold = {}, objectQuads = {},
grassQuads = {}, flowerQuads = {}, roundStamps = {}, figures = {} }
grassQuads = {}, flowerQuads = {}, roundStamps = {}, figures = {},
-- tile key -> the row a collapsed bookcase rank's box actually
-- stands on, so a standee supported by one lands on it rather than
-- where the drawing put it (see buildBookcases)
bookcaseBox = {} }
Buildings.build(S, map, pixels(tileset), perRow)
-- Fold doors into their buildings. A door cell is WALKABLE (the player
@@ -1882,6 +1886,12 @@ local function bookcaseRank(S, map, perRow, run, i, j, k, pane, srcU, srcV,
end
end
-- The arts a `bookcase_backfill = "above"` row may inherit: terrain and
-- solid bodies only (see the note at the backfill itself). Everything
-- absent here -- billboard, post, cylinder, grass, flower -- is a per-pixel
-- object STANDING on terrain rather than terrain.
local BACKFILL_ART = { flat = true, top = true, upright = true }
function Structures.buildBookcases(S, map, x0, x1, y0, y1, data, perRow)
perRow = perRow or map.tileset.tilesPerRow or 16
-- What to do with the rows a rank VACATES (see TileShape.bookcaseBackfill).
@@ -1928,11 +1938,32 @@ function Structures.buildBookcases(S, map, x0, x1, y0, y1, data, perRow)
-- shelf standing in a room. `bookcase_backfill = "above"` hands it
-- the cell above the run instead, shape and art, so a wall cut into
-- a terrace has more terrace behind it rather than a trench.
--
-- Only BODY above backfills: a vacated row wants more of the
-- terrace the wall is cut into, and the terrace is whatever lies
-- flat, tops out or stands as a solid face. A per-pixel STANDEE
-- above -- a statue, a sign, a bush -- is an object standing ON
-- that terrace, and copying it northward builds a second and a
-- third of it: Indigo Plateau's avenue statues sit directly on
-- the pilasters that collapse here, so every bird came out
-- duplicated twice down the shaft behind itself. A standee
-- above means the row has no terrace to inherit, so it takes the
-- default and is painted with synthesized ground.
local covered = math.min(2, front - top + 1)
local srcK = keyOf(tx, top - 1)
local src = backfill == "above" and S.shapeAt[srcK] or nil
if src and not BACKFILL_ART[src.art] then src = nil end
-- Where the box ACTUALLY ends up, remembered for every row of the
-- rank: the collapse walks the whole drawn run onto its southmost
-- cell, so anything that has to stand ON the box has to be told
-- where the box went. A statue keys off the cell below its own
-- drawing, which is the run's NORTH end -- two rows away from the
-- box on a two-cell pilaster, which is exactly the distance the
-- Plateau's birds floated by.
local boxTop = front - covered + 1
for cy = top, front do
local tk = keyOf(tx, cy)
S.bookcaseBox[tk] = boxTop
if src and cy <= front - covered then
S.shapeAt[tk] = src
S.tileAt[tk] = S.tileAt[srcK]
@@ -2840,9 +2871,10 @@ function Structures.buildObject(S, map, region, cluster,
-- Town is where it showed: pinning the cliff's slope chain gave the
-- posts along the cliff edge an authored 16px box to their south, and
-- they were hoisted to stand on the clifftop instead of the path.
local baseY, support = 0, nil
local baseY, support, supportRow = 0, nil, nil
if force and force ~= "opaque" then
local bs = S.shapeAt[keyOf(cluster.minX, cluster.maxY + 1)]
local belowK = keyOf(cluster.minX, cluster.maxY + 1)
local bs = S.shapeAt[belowK]
local blocked = not map:isWalkableCell(math.floor(cluster.minX / 2),
math.floor(cluster.maxY / 2))
-- `bookcase` supports as well as `upright`. A prop drawn above an
@@ -2859,6 +2891,13 @@ function Structures.buildObject(S, map, region, cluster,
and (bs.art == "upright" or bs.art == "bookcase"
or bs.class == "building") then
baseY, support = bs.h, bs
-- A bookcase support has MOVED: the collapse walks the whole drawn
-- run onto its southmost cell, and the cell tested above is the run's
-- north end. On the Plateau's two-cell pilasters that is a full cell
-- away, and the bird stood at the right HEIGHT over open ground with
-- its pillar behind it -- floating. Stand it on the box's own north
-- row instead of one row south of its drawing.
supportRow = S.bookcaseBox[belowK]
end
end
local atlasW = map.tileset.imageWidth or 128
@@ -2910,8 +2949,9 @@ function Structures.buildObject(S, map, region, cluster,
end
end
for _, c in ipairs(comps) do
c.z0 = cluster.minY * 8 + math.floor(c.lowY / 8) * 8
+ (support and 8 or 0) + (8 - depth) / 2
c.z0 = supportRow and (supportRow * 8 + (8 - depth) / 2)
or (cluster.minY * 8 + math.floor(c.lowY / 8) * 8
+ (support and 8 or 0) + (8 - depth) / 2)
c.z1 = c.z0 + depth
end
+31 -9
View File
@@ -764,8 +764,9 @@ function TileShape.bookcaseRelief(tilesetId)
return not (entry and entry.bookcase_relief == false)
end
--- What every `wall` cell's TOP face wears in this tileset (a tileset
--- entry's wall_top). Returns a tile id, or nil to leave the top alone.
--- What a `wall` cell's TOP face wears in this tileset (a tileset entry's
--- wall_top). Returns a function tile -> cap tile id (nil for "leave it
--- alone"), or nil when the tileset says nothing at all.
---
--- A wall band is 16px of art folded upright over a run two drawn rows
--- deep, so it folds ENTIRELY onto its face and has no row left to lay
@@ -773,17 +774,38 @@ end
--- poster and window came out lying across the top of the wall as well as
--- hanging on it. What is up there is the wall's capping course, which is
--- the plain panel the decorated column's own neighbours draw; naming it
--- per tileset is the whole fix, because "plain" is a fact about the
--- drawing that nothing in the geometry can measure.
--- is the whole fix, because "plain" is a fact about the drawing that
--- nothing in the geometry can measure.
---
--- Per tileset rather than per tile: one room caps with one course, and a
--- list keyed by the decorated tiles would have to be extended every time
--- a map hung something new on a wall already covered.
--- Two forms, because tilesets differ in how far one answer reaches:
---
--- wall_top = <id> EVERY wall cell caps with this course.
--- Right where one atlas dresses one kind of
--- room -- the town house, the Centers, Red's
--- two floors all cap with their own blank
--- panel, and a list keyed by the decorated
--- tiles would need extending every time a
--- map hung something new on the same wall.
--- wall_top = { [tile] = id } only these tiles are redirected. Right
--- where one atlas dresses several rooms:
--- LOBBY is the department store, the Game
--- Corner, Silph's floors, the roof AND the
--- Rocket lift, and the lift's cabin frame is
--- not what a shop wall caps with.
function TileShape.wallTop(tilesetId)
local s = load()
local entry = s and s.tilesets and s.tilesets[tilesetId]
local tile = entry and entry.wall_top
return type(tile) == "number" and tile or nil
local spec = entry and entry.wall_top
if type(spec) == "number" then
return function() return spec end
end
if type(spec) == "table" then
return function(tile)
local cap = spec[tile]
return type(cap) == "number" and cap or nil
end
end
return nil
end
-- Drop the cache: a mod that shadows data/voxel_heights.lua or a tileset