mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 08:01:09 +02:00
level the cliff mounds: pin the east slope chain, region-adopt doorways
The cliff drawing's dark east slope ($02/$24/$34) broke the repeat scan at its corners and rose to 32px towers; a folded doorway column read its own drawn extent and towered a block over the 16px mound around Diglett's Cave. The slope chain is pinned to one 16px course, and a doorway column now takes height AND top from its region: roofed among drawn facades (houses), flat among flat repeats (the mound). Adds the voxel_mound_probe driver that prints per-column detector decisions.
This commit is contained in:
@@ -138,6 +138,25 @@
|
||||
construction: they now carry an `own` flag the edge keep-rules never
|
||||
touch.
|
||||
|
||||
- **Diglett's Cave mounds (and cliffs everywhere) stop sprouting
|
||||
towers.** Two detector misreadings stacked up on the cave-entrance
|
||||
mound. The dark east slope of the cliff drawing ($02/$24/$34) is one
|
||||
texture repeated over the mound's whole height, but its corner tiles
|
||||
break the repeat scan, so those columns rose to 32px -- the rock
|
||||
pillar beside the entrance. And a folded doorway column reads its own
|
||||
drawn extent (the door plus everything above it), which is a house's
|
||||
real height when the door is a house's, but a 32px tower over a 16px
|
||||
plateau when the door is a cave mouth -- the entrance jumped a block
|
||||
above the mound around it. The slope chain is now profile-pinned to
|
||||
one 16px course, and a doorway column answers to its REGION entirely:
|
||||
height from the region's dominant column, top flat when those columns
|
||||
are flat repeats (the mound) and roofed when they are drawn facades
|
||||
(a house). Both cave entrances -- and every cliff built from the same
|
||||
slope tiles -- now read as one level mesa with the cave mouth at
|
||||
ground level. A new `voxel_mound_probe` driver prints the detector's
|
||||
per-column class and height over any rectangle, which is how this was
|
||||
diagnosed.
|
||||
|
||||
## 1.0.3
|
||||
|
||||
### Added
|
||||
|
||||
@@ -129,6 +129,15 @@ return {
|
||||
-- fence-textured tower. `post` extracts each cell alone, so these
|
||||
-- render as the same thin posts, marching north
|
||||
post = { 14, 85 },
|
||||
-- the cliff-mound's dark east slope and its corners ($02 the NE
|
||||
-- corner, $24 the slope column, $34 the slope-to-rim seat). Their
|
||||
-- drawn runs span the whole mound drawing, so the detector raised
|
||||
-- them to 32px towers -- the rock pillar beside Diglett's Cave --
|
||||
-- and the doorway column, which adopts its REGION's height,
|
||||
-- inherited the same 32 and put the cave entrance a block above
|
||||
-- the mound around it. Pinned to one 16px course they match the
|
||||
-- plateau body, and the doorway drops with them.
|
||||
wall = { 2, 36, 52 },
|
||||
-- the cuttable bush ($2D/$2E/$3D/$3E, the four tiles Cut deletes
|
||||
-- -- across the whole tileset they appear only in the five
|
||||
-- cut-tree blocks): a standing per-pixel cutout 5 voxels deep,
|
||||
|
||||
+36
-5
@@ -192,7 +192,7 @@ function Structures.forMap(map)
|
||||
-- still overdraws a walker's feet even though characters stamp over
|
||||
-- terrain.)
|
||||
S = { shapeAt = shapeAt, tileAt = tileAt, outdoor = Map.isOutdoor(def),
|
||||
runs = {}, skip = {}, ground = {}, objectQuads = {},
|
||||
runs = {}, skip = {}, ground = {}, doorFold = {}, objectQuads = {},
|
||||
grassQuads = {}, flowerQuads = {}, roundStamps = {} }
|
||||
Buildings.build(S, map, pixels(tileset), perRow)
|
||||
|
||||
@@ -213,7 +213,12 @@ function Structures.forMap(map)
|
||||
if ns and ns.art == "upright" then
|
||||
for dy = 0, 1 do
|
||||
for dx = 0, 1 do
|
||||
shapeAt[keyOf(cx * 2 + dx, cy * 2 + dy)] = shapes.classes.wall
|
||||
local dk = keyOf(cx * 2 + dx, cy * 2 + dy)
|
||||
shapeAt[dk] = shapes.classes.wall
|
||||
-- remembered for buildVolume: a folded doorway column
|
||||
-- answers to its REGION for height and top, not to its
|
||||
-- own drawn extent (see the door adoption there)
|
||||
S.doorFold[dk] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1314,6 +1319,7 @@ function Structures.buildVolume(S, map, tiles)
|
||||
|
||||
local runs = {}
|
||||
local heightVotes = {}
|
||||
local repeatVotes = {}
|
||||
for tx, ys in pairs(cols) do
|
||||
-- visit each contiguous vertical run in this column
|
||||
local sorted = {}
|
||||
@@ -1345,11 +1351,19 @@ function Structures.buildVolume(S, map, tiles)
|
||||
end
|
||||
end
|
||||
end
|
||||
local isDoor = false
|
||||
for ty = north, front do
|
||||
if S.doorFold[keyOf(tx, ty)] then
|
||||
isDoor = true
|
||||
break
|
||||
end
|
||||
end
|
||||
local run = { front = front, north = north, extent = extent,
|
||||
unit = unit, fromRepeat = repeatRead }
|
||||
unit = unit, fromRepeat = repeatRead, door = isDoor }
|
||||
runs[#runs + 1] = { tx = tx, run = run }
|
||||
local h = unit * 8
|
||||
heightVotes[h] = (heightVotes[h] or 0) + 1
|
||||
if repeatRead then repeatVotes[h] = (repeatVotes[h] or 0) + 1 end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1361,11 +1375,27 @@ function Structures.buildVolume(S, map, tiles)
|
||||
for h, n in pairs(heightVotes) do
|
||||
if n > modeN or (n == modeN and h > modeH) then modeH, modeN = h, n end
|
||||
end
|
||||
-- whether the region's dominant columns are flat repeats (a cliff
|
||||
-- mound's plateau) rather than drawn facades (a house's front)
|
||||
local modeRepeat = (repeatVotes[modeH] or 0) * 2 > modeN
|
||||
for _, r in ipairs(runs) do
|
||||
local run = r.run
|
||||
local h = run.unit * 8
|
||||
local adopted = false
|
||||
if run.fromRepeat and modeH > h then
|
||||
local flatDoor = false
|
||||
if run.door then
|
||||
-- A folded doorway column answers to its region ENTIRELY. Its own
|
||||
-- reading spans the door plus everything drawn above it -- a
|
||||
-- house's full height when the door is a house's, but a 32px
|
||||
-- tower over a 16px plateau when the door is a cave mouth cut
|
||||
-- into a cliff mound (Diglett's Cave: the entrance jumped a block
|
||||
-- above the mound around it). Height and top both come from the
|
||||
-- region: the mode height, roofed like a facade when the mode
|
||||
-- columns are drawn facades, flat when they are flat repeats.
|
||||
h = modeH
|
||||
adopted = not modeRepeat
|
||||
flatDoor = modeRepeat
|
||||
elseif run.fromRepeat and modeH > h then
|
||||
h = modeH
|
||||
adopted = true
|
||||
end
|
||||
@@ -1383,7 +1413,8 @@ function Structures.buildVolume(S, map, tiles)
|
||||
-- whole roof area -- and a rooftop tilted into a 48px ramp reads
|
||||
-- wrong instantly. Distinct top rows -> slope; repeated -> level top.
|
||||
local roofRows = 0
|
||||
if S.outdoor and (not run.fromRepeat or adopted) and h >= 16 then
|
||||
if S.outdoor and (not run.fromRepeat or adopted) and h >= 16
|
||||
and not flatDoor then
|
||||
roofRows = math.min(2, math.floor(h / 8) - 1)
|
||||
if roofRows > 0 and map:tileAt(r.tx, run.north)
|
||||
== map:tileAt(r.tx, run.north + 1) then
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
-- Driver: what did the detector decide over a rectangle of tiles?
|
||||
--
|
||||
-- Prints, for every tile of MOUND_RECT on MOUND_MAP, the resolved shape
|
||||
-- class and the volume run height Structures gave it -- the ground truth
|
||||
-- for diagnosing cliff/mound columns that extrude to the wrong height
|
||||
-- (Diglett's Cave entrance ridge, bug of 2026-07-27). Then a shot.
|
||||
--
|
||||
-- POKEPORT_DRIVER=mods/DRAMATIC_SHAPE/tests/voxel_mound_probe.lua lovec .
|
||||
--
|
||||
-- knobs (env):
|
||||
-- MOUND_MAP map id (default ROUTE_2)
|
||||
-- MOUND_RECT "tx0,ty0,tx1,ty1" in tiles (default 16,12,35,23)
|
||||
-- MOUND_SPOT "x,y[,facing]" player cell (default 12,12,up)
|
||||
-- SHOT_DIR output directory, must exist (default "shots")
|
||||
return function(game)
|
||||
local U = dofile("tests/drivers/util.lua")
|
||||
local Pipelines = require("src.render.Pipelines")
|
||||
|
||||
local DIR = os.getenv("SHOT_DIR") or "shots"
|
||||
local mapId = os.getenv("MOUND_MAP") or "ROUTE_2"
|
||||
local rx0, ry0, rx1, ry1 = (os.getenv("MOUND_RECT") or "16,12,35,23")
|
||||
:match("^(%-?%d+),(%-?%d+),(%-?%d+),(%-?%d+)$")
|
||||
rx0, ry0, rx1, ry1 = tonumber(rx0), tonumber(ry0), tonumber(rx1), tonumber(ry1)
|
||||
local sx, sy, facing = (os.getenv("MOUND_SPOT") or "12,12,up")
|
||||
:match("^%s*(%d+)%s*,%s*(%d+)%s*,?%s*(%a*)")
|
||||
facing = (facing ~= "" and facing) or "up"
|
||||
|
||||
local Zoom = require("src.render.Zoom")
|
||||
Zoom.reset()
|
||||
Pipelines.setLevel("tiltshift", 0)
|
||||
U.teleport(game, mapId, tonumber(sx), tonumber(sy), facing)
|
||||
U.wait(20)
|
||||
Pipelines.setLevel("voxel", 3)
|
||||
U.wait(30)
|
||||
|
||||
local V = game.mods.exports["DRAMATIC_SHAPE"]
|
||||
V = V and V.lib
|
||||
local Structures = V and V.require("Structures")
|
||||
local ow = game.overworld
|
||||
if Structures and ow and ow.map then
|
||||
local S = Structures.forMap(ow.map)
|
||||
local function keyOf(tx, ty) return (ty + 64) * 4096 + (tx + 64) end
|
||||
print(("[mound] %s tiles (%d,%d)-(%d,%d): tile/class/h per column")
|
||||
:format(mapId, rx0, ry0, rx1, ry1))
|
||||
for ty = ry0, ry1 do
|
||||
local row = {}
|
||||
for tx = rx0, rx1 do
|
||||
local k = keyOf(tx, ty)
|
||||
local s = S.shapeAt[k]
|
||||
local run = S.runs[k]
|
||||
local h = run and run.h or (s and s.h) or 0
|
||||
if S.skip[k] then h = 0 end
|
||||
row[#row + 1] = ("%02X%s%02d"):format(S.tileAt[k] or 0xFF,
|
||||
s and s.class:sub(1, 1) or "?", h)
|
||||
end
|
||||
print("[mound] " .. table.concat(row, " "))
|
||||
end
|
||||
end
|
||||
|
||||
game.capturePath = ("%s/mound_%s.png"):format(DIR, mapId)
|
||||
U.wait(5)
|
||||
Pipelines.setLevel("voxel", 0)
|
||||
U.wait(5)
|
||||
print("[mound] done")
|
||||
end
|
||||
Reference in New Issue
Block a user