pokecenter machines, water occlusion

This commit is contained in:
DramaticShape
2026-08-02 00:01:39 -04:00
parent 91cc2d6f51
commit a7c9541ac4
6 changed files with 454 additions and 143 deletions
+255 -82
View File
@@ -842,12 +842,12 @@ return {
-- console face (76/77) and button panel (6/22) are ALSO wall:
-- drawn 16px tall against the band, so wall height is their drawn
-- height and they read as equipment jutting from it. (The
-- `center_heal_machine` template below claims those four tiles at
-- every placement and models the console properly; these pins are
-- the degradation path when the profile is absent.) The
-- near-black screen tiles must be pinned or the void rule flattens
-- them. What is NOT wall is the machines' two flanks -- see
-- `prop` below.
-- `center_heal_machine_w`/`_e` templates below claim the full
-- 4x4 machine grids at every placement and model the console
-- properly; these pins are the degradation path when the profile
-- is absent.) The near-black screen tiles must be pinned or the
-- void rule flattens them. What is NOT wall is the machines'
-- two flanks -- see `prop` below.
wall = { 2, 3, 4, 5, 6, 16, 18, 19, 20, 21, 22, 40, 41,
76, 77, 92, 93, 94, 95 },
-- the counters, half a cell high: top band (8) with the nurse's
@@ -899,7 +899,10 @@ return {
-- game -- 12 maps, the same two cells in each -- so this pin cannot
-- reach anything but the machines. 72 is still the $48
-- water-fallback trap and still has to carry SOME pin; it just
-- wanted the thin one, not the wall.
-- wanted the thin one, not the wall. (The machine templates now
-- claim the flank tiles too and model the hoses and keyboard as
-- attached 3D equipment; these standee pins are the degradation
-- path when the profile is absent.)
prop = { 7, 13, 72, 73,
-- ...and the potted plants -- see THE POTTED PLANTS below
32, 33, 34, 35, 48, 49, 50, 51 },
@@ -1968,13 +1971,24 @@ return {
-- on (see `buildings` below). The pin is only the degradation
-- path here -- the template claims both placements -- but the two
-- must not disagree about where the desk's top is.
heights = { table = 8 },
--
-- `stool` is 5 for the same reason: that is the drawn elevation of
-- BOTH seats in this tileset -- rows 11-15, the seat's front edge
-- over its legs -- and the height the `club_stool` template stands
-- them. Bill's chair and the Fan Club's are different drawings
-- above the seat but share tiles 59/60 below it, so they are drawn
-- at the same height pixel for pixel; the class default of 8 was
-- three voxels over both. Every one of these cells is WALKABLE,
-- so this is also where a character sitting on one ends up.
heights = { table = 8, stool = 5 },
-- the seats: Bill's desk stool (43/44 over 59/60) and the Fan
-- Club's four members' chairs (61/62 over 59/60), a whole cell
-- each. All of them sit in WALKABLE cells, so they were flat
-- floor until pinned; the 8px standee pool puts them at seat
-- height, which is also where a character standing on the cell
-- ends up.
-- floor until pinned; the standee pool puts them at seat height,
-- which is also where a character standing on the cell ends up.
-- The Fan Club's four are modelled in full by `club_stool` below
-- and these pins are their degradation path; Bill's chair stays a
-- standee, stood up as a part of `bills_desk`.
stool = { 43, 44, 59, 60, 61, 62 },
-- flat, and pinned flat on purpose. 31 is the rooms' floor tile
-- and needs no pin for its 980 walkable placements -- but the
@@ -3737,82 +3751,154 @@ return {
z = 13 }, -- keyboard
},
},
-- F05: the healing machine behind the counter -- the pair in every
-- Center (cells (1,0):(2,1) and (6,0):(7,1)), 22 placements, plus
-- the Indigo Plateau lobby's pair under MART below (scan
-- "58,59;74,75;76,77;6,22").
-- F05: the healing machine behind the counter -- two 4x4-tile
-- variants, 24 placements between them (a pair in every Center,
-- cells (1,0):(2,1) and (6,0):(7,1), plus the Indigo Plateau
-- lobby's pair under MART below). The variants differ only in
-- the east flank: the west machine has the control keyboard
-- there (7/13, scan "40,58,59,40;40,74,75,40;72,76,77,7;
-- 72,6,22,13" -- 12 placements), the east machine mirrored
-- hoses (73, scan "...;72,76,77,73;72,6,22,73" -- 12).
--
-- The drawing is 4x4 tiles but only its middle COLUMN is this
-- template's. The corners are the striped wall band (40), and
-- the outer tile columns are the flanking EQUIPMENT -- the hoses
-- (72 west, 73 east) and the control keyboard (7/13) -- which are
-- separate thin objects, not part of this console: they are
-- pinned into the THIN standee pool above and stay out of this
-- grid. What no class can split is the middle -- a wall-height
-- The FULL drawing is claimed now: the corners are the striped
-- wall band (40), the flanks the machine's own equipment --
-- hoses and a keyboard ATTACHED to the cabinet, not furniture
-- standing free (their standee pins above stay as the
-- degradation path). What no class can split is a wall-height
-- cabinet with a monitor perched on its front top edge, drawn
-- across two map rows because it towers over the 16px band
-- behind it, which the volume path can only read as more wall.
--
-- Read off the pixels (local x, the whole drawing's x8..x23):
-- rows 15-31 the CABINET's front face, 16 wide, 17 tall:
-- black front-top edge with vent notches (15),
-- white top rail (16), black panel in a white
-- frame (17-28), white rail / light plinth / black
-- ground line (29-31).
-- rows 6-14 the cabinet's TOP FACE seen from above -- not a
-- second-story facade: a white expanse with a lit
-- west strip (x1 white) and a shaded east strip
-- (x14 #555) wrapping the monitor, full-width in
-- front of it (13-14), strips beside it (7-12).
-- Its 9 rows plus the front edge row 15 ARE the
-- cabinet's depth: 10. `desk.top` lays the band
-- flat as the lid; where the monitor's drawing
-- occludes it, the lid continues the nearest strip
-- (the drawing's own pixels).
-- rows 1-13 the MONITOR, x2..x13: black back rim (1) and
-- white cap (2-3) seen from above -- 3 rows plus
-- the front edge, depth 4 -- then bezel, screen
-- and control strip (4-13) face-on, 10 tall,
-- standing on the cabinet top at its front: the
-- drawn base edge (13) is one band row up from the
-- front strip (14), so its front plane is one
-- voxel back from the cabinet's -- z 20..23. The
-- screen interior (x5..x10, rows 6-8) is sealed
-- behind its own black frame and sinks one voxel
-- Read off the pixels (absolute grid x0..x31):
-- rows 15-31, x8..x23 the CABINET's front face, 16 wide, 17
-- tall: black front-top edge with vent notches
-- (15), white top rail (16), black panel in a
-- white frame (17-28), white rail / light plinth /
-- black ground line (29-31). `desk.x` bounds the
-- desk to the middle columns.
-- rows 6-14, x8..x23 the cabinet's TOP FACE seen from above
-- -- not a second-story facade: a white expanse
-- with a lit west strip (x9) and a shaded east
-- strip (x22) wrapping the monitor. Its 9 rows
-- plus the front edge row 15 ARE the cabinet's
-- depth: 10, z 16..25 against the wall.
-- `desk.top` lays the band flat as the lid; where
-- the monitor's drawing occludes it, the lid
-- continues the nearest strip (the drawing's own
-- pixels).
-- rows 1-13, x10..x21 the MONITOR: black back rim (1) and
-- white cap (2-3) seen from above -- depth 4 --
-- then bezel, screen and control strip (4-13)
-- face-on, 10 tall, at the cabinet top's front
-- (the drawn base edge 13 is one band row up from
-- the front strip 14): z 20..23. The screen
-- interior (x13..x18, rows 6-8) sinks one voxel
-- via `inset` -- the pane rule applied by hand,
-- because `panes = false` blocks the global pass.
-- rows 0-5 at the flanks: wall stripes and the machine's
-- cast shadow on the wall east of it -- BACKGROUND,
-- the same standing as the potted plants' floor.
-- The `wall` element keeps the band solid over the
-- back map row: a 16-tall, 16-deep block cycling
-- the drawing's own stripe unit (x0, rows 0-3),
-- what the neighbouring cells' wall pins render.
-- rows 16-31, x0..x7 the HOSES (tile 72 stacked): one 8-row
-- motif per map row, and the two stacked motifs
-- are two hoses in DEPTH -- the drawn row band is
-- the depth band, and each motif's own rows are
-- its elevation WITHIN that band, exactly the
-- potted plants' stacked-cell rule. So both
-- hoses hug the floor: a horizontal run off the
-- cabinet's side (rows 24-26, x3..x7 -> heights
-- 5..7) over a shaded drop landing ON the floor
-- (rows 27-31, x2..x7 -> heights 0..4) -- all
-- measured, the drawn extent maps 1:1 with no
-- continuation. `box` parts at z 18 and z 24, 3
-- deep; both wear the LOWER motif's rows (the one
-- drawn at its true elevation; the upper motif is
-- the same atlas tile, pixel-identical).
-- rows 16-31, x24..x31 west variant: the KEYBOARD (7/13), a
-- key grid with its cable at the north end and a
-- bar down the east edge -- TOP-VIEW art, 16 rows
-- = 16 depth rows. A `flat` part mounted on the
-- cabinet's side at COUNTER level (`at` 8, the
-- counters' own 8px band -- the one number
-- top-view art cannot state, pinned to the room's
-- work surface), 3 thick, top face wearing the
-- drawn art. The dark dashes east of it (x30-31)
-- are its cast shadow on the floor: background.
-- East variant: mirrored hoses (runs x24..x28,
-- drops x24..x29), same rows and z slots.
-- rows 0-15 elsewhere wall stripes and the machine's cast
-- shadow -- BACKGROUND, the same standing as the
-- potted plants' floor. The `wall` element keeps
-- the band solid over the back map row, full grid
-- width: a 16-tall, 16-deep block cycling the
-- drawing's own stripe unit (x0, rows 0-3), what
-- the neighbouring cells' wall pins render.
--
-- `depth = 4` is BOTH map rows: the back row is the wall's plot,
-- the front row the machine's own, and the cabinet leans against
-- the wall at z 16..25. `panes = false`: the front panel seals
-- DARK behind LIGHT (a black panel in a white frame), the
-- opposite polarity to the pane rule, so the global recess pass
-- would sink the frame and leave the panel proud.
-- the front row the machine's own. `panes = false`: the front
-- panel seals DARK behind LIGHT (a black panel in a white
-- frame), the opposite polarity to the pane rule, so the global
-- recess pass would sink the frame and leave the panel proud.
{
id = "center_heal_machine",
id = "center_heal_machine_w",
tiles = {
{ 58, 59 },
{ 74, 75 },
{ 76, 77 },
{ 6, 22 },
{ 40, 58, 59, 40 },
{ 40, 74, 75, 40 },
{ 72, 76, 77, 7 },
{ 72, 6, 22, 13 },
},
roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
slab = 0, frontEave = 0, ledge = nil, depth = 4,
panes = false,
wall = { h = 16, depthPx = 16, x = 0, cycle = { 0, 3 } },
desk = { fascia = { 15, 16 }, base = { 17, 31 }, z = 16,
depthPx = 10, top = { 6, 14 } },
desk = { x = { 8, 23 }, fascia = { 15, 16 }, base = { 17, 31 },
z = 16, depthPx = 10, top = { 6, 14 } },
parts = {
{ kind = "upright", x = { 2, 13 }, top = { 1, 3 },
{ kind = "upright", x = { 10, 21 }, top = { 1, 3 },
facade = { 4, 13 }, z = 20, depth = 4,
inset = { x = { 5, 10 }, rows = { 6, 8 } } }, -- the monitor
inset = { x = { 13, 18 }, rows = { 6, 8 } } }, -- the monitor
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 18, depth = 3 }, -- N hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 18, depth = 3 }, -- N hose drop
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 24, depth = 3 }, -- S hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 24, depth = 3 }, -- S hose drop
{ kind = "flat", x = { 24, 29 }, rows = { 16, 31 },
z = 16, at = 8, thick = 3 }, -- keyboard shelf
},
},
{
id = "center_heal_machine_e",
tiles = {
{ 40, 58, 59, 40 },
{ 40, 74, 75, 40 },
{ 72, 76, 77, 73 },
{ 72, 6, 22, 73 },
},
roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
slab = 0, frontEave = 0, ledge = nil, depth = 4,
panes = false,
wall = { h = 16, depthPx = 16, x = 0, cycle = { 0, 3 } },
desk = { x = { 8, 23 }, fascia = { 15, 16 }, base = { 17, 31 },
z = 16, depthPx = 10, top = { 6, 14 } },
parts = {
{ kind = "upright", x = { 10, 21 }, top = { 1, 3 },
facade = { 4, 13 }, z = 20, depth = 4,
inset = { x = { 13, 18 }, rows = { 6, 8 } } }, -- the monitor
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 18, depth = 3 }, -- NW hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 18, depth = 3 }, -- NW hose drop
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 24, depth = 3 }, -- SW hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 24, depth = 3 }, -- SW hose drop
{ kind = "box", x = { 24, 28 }, rows = { 24, 26 },
z = 18, depth = 3 }, -- NE hose run
{ kind = "box", x = { 24, 29 }, rows = { 27, 31 },
z = 18, depth = 3 }, -- NE hose drop
{ kind = "box", x = { 24, 28 }, rows = { 24, 26 },
z = 24, depth = 3 }, -- SE hose run
{ kind = "box", x = { 24, 29 }, rows = { 27, 31 },
z = 24, depth = 3 }, -- SE hose drop
},
},
},
@@ -3840,28 +3926,75 @@ return {
},
},
-- F05 again: the Indigo Plateau lobby has the Centers' healing
-- machine pair too (cells (5,4):(6,5) and (10,4):(11,5)), and its
-- MART tileset shares the POKECENTER atlas, so this is the same
-- drawing tile for tile. Same part table as the POKECENTER entry
-- above.
-- machine pair too -- the west-variant grid at (5,4):(6,5) and
-- the east at (10,4):(11,5), verified by region dump -- and its
-- MART tileset shares the POKECENTER atlas, so these are the
-- same drawings tile for tile. Same part tables as the
-- POKECENTER entries above.
{
id = "center_heal_machine",
id = "center_heal_machine_w",
tiles = {
{ 58, 59 },
{ 74, 75 },
{ 76, 77 },
{ 6, 22 },
{ 40, 58, 59, 40 },
{ 40, 74, 75, 40 },
{ 72, 76, 77, 7 },
{ 72, 6, 22, 13 },
},
roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
slab = 0, frontEave = 0, ledge = nil, depth = 4,
panes = false,
wall = { h = 16, depthPx = 16, x = 0, cycle = { 0, 3 } },
desk = { fascia = { 15, 16 }, base = { 17, 31 }, z = 16,
depthPx = 10, top = { 6, 14 } },
desk = { x = { 8, 23 }, fascia = { 15, 16 }, base = { 17, 31 },
z = 16, depthPx = 10, top = { 6, 14 } },
parts = {
{ kind = "upright", x = { 2, 13 }, top = { 1, 3 },
{ kind = "upright", x = { 10, 21 }, top = { 1, 3 },
facade = { 4, 13 }, z = 20, depth = 4,
inset = { x = { 5, 10 }, rows = { 6, 8 } } }, -- the monitor
inset = { x = { 13, 18 }, rows = { 6, 8 } } }, -- the monitor
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 18, depth = 3 }, -- N hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 18, depth = 3 }, -- N hose drop
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 24, depth = 3 }, -- S hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 24, depth = 3 }, -- S hose drop
{ kind = "flat", x = { 24, 29 }, rows = { 16, 31 },
z = 16, at = 8, thick = 3 }, -- keyboard shelf
},
},
{
id = "center_heal_machine_e",
tiles = {
{ 40, 58, 59, 40 },
{ 40, 74, 75, 40 },
{ 72, 76, 77, 73 },
{ 72, 6, 22, 73 },
},
roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
slab = 0, frontEave = 0, ledge = nil, depth = 4,
panes = false,
wall = { h = 16, depthPx = 16, x = 0, cycle = { 0, 3 } },
desk = { x = { 8, 23 }, fascia = { 15, 16 }, base = { 17, 31 },
z = 16, depthPx = 10, top = { 6, 14 } },
parts = {
{ kind = "upright", x = { 10, 21 }, top = { 1, 3 },
facade = { 4, 13 }, z = 20, depth = 4,
inset = { x = { 13, 18 }, rows = { 6, 8 } } }, -- the monitor
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 18, depth = 3 }, -- NW hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 18, depth = 3 }, -- NW hose drop
{ kind = "box", x = { 3, 7 }, rows = { 24, 26 },
z = 24, depth = 3 }, -- SW hose run
{ kind = "box", x = { 2, 7 }, rows = { 27, 31 },
z = 24, depth = 3 }, -- SW hose drop
{ kind = "box", x = { 24, 28 }, rows = { 24, 26 },
z = 18, depth = 3 }, -- NE hose run
{ kind = "box", x = { 24, 29 }, rows = { 27, 31 },
z = 18, depth = 3 }, -- NE hose drop
{ kind = "box", x = { 24, 28 }, rows = { 24, 26 },
z = 24, depth = 3 }, -- SE hose run
{ kind = "box", x = { 24, 29 }, rows = { 27, 31 },
z = 24, depth = 3 }, -- SE hose drop
},
},
},
@@ -4236,6 +4369,46 @@ return {
facade = { 22, 31 }, rise = -8, z = 22, depth = 10 },
},
},
-- F09 once more: the Pokemon Fan Club's four members' chairs,
-- drawn round the boardroom table -- POKEMON_FAN_CLUB cells (1,3),
-- (1,4), (6,3) and (6,4), and nowhere else in the game.
--
-- A DIFFERENT drawing from the house stool -- its seat field
-- carries a one-pixel white margin where the house's carries two
-- -- but the same object band for band: rows 5-10 the seat seen
-- from above (its lid), row 11 the seat's own front edge, 12-15
-- the legs with the floor showing between them. Its elevation
-- rows are PIXEL-IDENTICAL to the house drawing's, so it takes
-- that part table unchanged, growth and all: z 3..13, `stretch`
-- mapping the seat band over the deeper lid, `panes = false` so
-- the pane rule does not sink the legs' lit faces.
--
-- Rows 11-15 come from tiles 59/60, which this drawing SHARES
-- with Bill's chair (43/44 over 59/60, modelled as a part of
-- `bills_desk` above). That is why the tileset's `stool = 5`
-- height override is right for both seats rather than a special
-- case for this one: they are drawn at the same height because
-- they are drawn with the same pixels.
--
-- Listed after `bills_desk` although neither grid is a subgrid of
-- the other (that grid has 43/44 over 59/60, this one 61/62), so
-- the order is free and the bigger, more specific grid keeps
-- first claim.
{
id = "club_stool",
tiles = {
{ 61, 62 },
{ 59, 60 },
},
roofRows = 0, roofBack = 0, roofFront = 0, roofCycle = { 0, 0 },
slab = 0, frontEave = 0, ledge = nil,
panes = false,
parts = {
{ kind = "upright", x = { 2, 13 }, top = { 5, 10 },
facade = { 11, 15 }, z = 3, depth = 11,
stretch = true }, -- the stool
},
},
},
},
}
+73 -13
View File
@@ -440,14 +440,68 @@ local function deskSetModel(sp, pr, t)
if p.kind == "flat" then
-- drawn row = depth row by default; `z` renames the origin when
-- the flat sits below the desk's own drawn top span (the Center
-- PC's keyboard)
-- PC's keyboard). `at` names the sheet's own height when it does
-- not lie on the desk plane (the healing machine's keyboard is a
-- shelf mounted on the cabinet's side); `thick` gives it a body
-- -- layers below the sheet repeating each column's own texel,
-- the same continuation rule every synthesized surface follows.
local r0 = p.rows[1]
local z0 = p.z or r0
local atY = p.at or plane
local thick = p.thick or 1
if atY > ytop then ytop = atY end
for sy = r0, p.rows[2] do
local z = z0 + (sy - r0)
if z >= 0 and z < D then
for sx = x0, x1 do
if inside[sy * W + sx] then put(sx, plane, z, sy * W + sx) end
if inside[sy * W + sx] then
for y = math.max(0, atY - thick + 1), atY do
put(sx, y, z, sy * W + sx)
end
end
end
end
end
elseif p.kind == "box" then
-- A BOX part is a drawn rect standing at its own drawn
-- elevation -- equipment attached to the machine rather than an
-- object on the desk plane. The rows are face-on art: the top
-- row's drawn height IS the box's top (ground - 1 - r0,
-- measured), and the box runs down to `base` (default the drawn
-- extent; 0 continues it to the floor, the legs-continue rule).
-- Height beyond the drawn rows fills the way a roof band does:
-- rows before `cycle` map 1:1 from the top, rows after it 1:1
-- from the bottom -- the healing machine hoses' foot lands ON
-- the floor -- and the cycle window repeats between.
local r0, r1 = p.rows[1], p.rows[2]
local c0 = p.cycle and p.cycle[1] or r1
local c1 = p.cycle and p.cycle[2] or r1
local pz = p.z or 0
local pd = p.depth
local top = pr.ground - 1 - r0
local bot = p.base or (pr.ground - 1 - r1)
local nTop, nBot = c0 - r0, r1 - c1
if top > ytop then ytop = top end
for y = bot, top do
local k, j = top - y, y - bot
local sy
if k < nTop then
sy = r0 + k
elseif j < nBot then
sy = r1 - j
else
sy = c0 + (k - nTop) % (c1 - c0 + 1)
end
for sx = x0, x1 do
local i = sy * W + sx
if inside[i] then
local ix = interiorAt(sx, sy, x0, x1)
for z = pz, pz + pd - 1 do
if z >= 0 and z < D then
local px = (z == pz or z == pz + pd - 1) and sx or ix
put(sx, y, z, sy * W + px)
end
end
end
end
end
@@ -700,6 +754,11 @@ local function deskSetModel(sp, pr, t)
local dz0 = t.desk.z or 0
local dz1 = dz0 + deskD - 1
local deskG = b1 + 1
-- ...and the desk's COLUMNS (`x`), when the grid is wider than the
-- desk: the healing machine's grid carries its flanking hoses and
-- keyboard, and the cabinet is only the middle 16 columns.
local dx0 = t.desk.x and t.desk.x[1] or 0
local dx1 = t.desk.x and t.desk.x[2] or W - 1
-- The WALL element: the band the machine backs onto, whose tiles this
-- grid claims. The drawing shows it only as the stripe background
@@ -727,9 +786,9 @@ local function deskSetModel(sp, pr, t)
for sy = b0, b1 do
Budget.tick()
local y = deskG - 1 - sy
for sx = 0, W - 1 do
for sx = dx0, dx1 do
if inside[sy * W + sx] then
local ix = interiorAt(sx, sy, 0, W - 1)
local ix = interiorAt(sx, sy, dx0, dx1)
for z = dz0, dz1 do
local px = (z == dz0 or z == dz1) and sx or ix
put(sx, y, z, sy * W + px)
@@ -739,8 +798,9 @@ local function deskSetModel(sp, pr, t)
end
for i in pairs(pr.recess) do
local sy = math.floor(i / W)
if sy >= b0 and sy <= b1 then
vox[key(i % W, deskG - 1 - sy, dz1)] = nil
local sx = i % W
if sy >= b0 and sy <= b1 and sx >= dx0 and sx <= dx1 then
vox[key(sx, deskG - 1 - sy, dz1)] = nil
end
end
@@ -748,7 +808,7 @@ local function deskSetModel(sp, pr, t)
for sy = f0, f1 do
Budget.tick()
local y = plane - 1 - (sy - f0)
for sx = 0, W - 1 do
for sx = dx0, dx1 do
for z = dz0, dz1 do put(sx, y, z, sy * W + sx) end
end
end
@@ -767,19 +827,19 @@ local function deskSetModel(sp, pr, t)
for z = dz0, dz1 do
Budget.tick()
local sy = z == dz1 and f0 or math.min(tr0 + (z - dz0), tr1)
for sx = 0, W - 1 do
for sx = dx0, dx1 do
local px = sx
for _, p in ipairs(t.parts) do
local px0, px1 = p.x[1], p.x[2]
local r0, r1
if p.kind == "flat" or p.kind == "iso" then
if p.kind == "flat" or p.kind == "iso" or p.kind == "box" then
r0, r1 = p.rows[1], p.rows[2]
else
r0, r1 = p.top[1], p.facade[2]
end
if sx >= px0 and sx <= px1 and sy >= r0 and sy <= r1 then
px = (sx - px0 < px1 - sx) and (px0 - 1) or (px1 + 1)
px = math.max(0, math.min(W - 1, px))
px = math.max(dx0, math.min(dx1, px))
break
end
end
@@ -790,12 +850,12 @@ local function deskSetModel(sp, pr, t)
-- the lid continues the sibling tables' top -- black rim, white
-- highlight courses along the north and west, grey field
local field = t.desk.lid == "white" and WHITE or GREY
for sx = 0, W - 1 do
for sx = dx0, dx1 do
for z = dz0, dz1 do
local shade = field
if sx == 0 or sx == W - 1 or z == dz0 or z == dz1 then
if sx == dx0 or sx == dx1 or z == dz0 or z == dz1 then
shade = BLACK
elseif sx == 1 or z == dz0 + 1 then
elseif sx == dx0 + 1 or z == dz0 + 1 then
shade = WHITE
end
put(sx, plane - 1, z, pr.shadeTexel[shade])
+36 -9
View File
@@ -583,8 +583,40 @@ end
-- The overworld's alone: the staged battle draws its water plain, always --
-- its placed camera reads this pass wrong, and a stage set wants painted
-- water anyway (see BattleScene, where the choice is argued).
-- ------- and why the flat draw happens FIRST, every time
--
-- The reflective pass writes no depth -- it cannot, the depth canvas is
-- detached for the length of it so the shader can READ it -- and it does its
-- own depth test against that texture instead. That test asks whether
-- something opaque is in front, and it answers correctly for every case but
-- one: WATER IN FRONT OF WATER. Nothing puts water in the depth buffer, so
-- no lake can hide another, and the pass simply paints them in mesh order.
--
-- On flat water that never mattered, because every surface lies in the one
-- plane at its own recessed height and a farther sheet always lands farther
-- down the screen. THE WORLD CURVE ENDS THAT. The bend drops the world by
-- the square of its distance, so the far side of the map swings down and
-- back up into the near field of view -- and a sheet of sea a hundred and
-- fifty tiles away, drawn later in the same mesh, paints straight over the
-- pond at the player's feet. That is the tall grass and the water "from the
-- other side of the map": not a reflection of them, THEM, rasterised on top
-- of the water in front of you.
--
-- So the meshes go down flat first, through the ordinary scene shader with
-- depth writes on, and the reflective pass draws over the top of what
-- survived. Three things fall out of it and all three are wanted: the depth
-- buffer now holds the water surface, so the pass's own test throws the far
-- sheet away; the reflection COPY holds it too, so a ray grazing another
-- part of the lake reads water rather than the void behind it; and the flat
-- draw is the same fallback this function already ended with, so a frame
-- that cannot run the reflective pass is unchanged.
--
-- It costs one more rasterisation of the water meshes. Water is a small
-- share of a frame's triangles and this is the cheapest shader in the mode.
function VoxelScene.drawWater(draws, cast)
local plain = true
for _, d in ipairs(draws) do
Voxel3D.draw(d[1], d[2], d[3])
end
if Water.enabled() and Voxel3D.depthReadable() then
local mirror, depth = Voxel3D.beginWater(cast)
local w, h = Voxel3D.size()
@@ -602,20 +634,15 @@ function VoxelScene.drawWater(draws, cast)
Water.draw(d[1], d[2], d[3])
end
Water.finish()
plain = false
end
-- Unconditionally, and OUTSIDE the success branch: beginWater unbinds
-- the shader and the depth mode BEFORE it can discover it cannot go on,
-- so a frame that bails halfway through has to be put back together
-- exactly like one that succeeded -- otherwise the plain draw below (and
-- every pass after it) runs with no shader and no depth test.
-- exactly like one that succeeded -- otherwise every pass after it runs
-- with no shader and no depth test. (What a bail leaves on screen is the
-- flat draw above, which is where this function used to end anyway.)
Voxel3D.endWater()
end
if plain then
for _, d in ipairs(draws) do
Voxel3D.draw(d[1], d[2], d[3])
end
end
end
-- A stamp of everything the sun pass depends on. Nothing in it moving
+8 -1
View File
@@ -956,8 +956,15 @@ vec4 effect(mediump vec4 color, Image tex, mediump vec2 tc, mediump vec2 sc) {
// the bound canvas's own pixel size, the same units sc is measured in, on
// every display. (`screen` stays in units: skyPos reads it against cell
// and skyEdge, which are unit-measured with it.)
//
// The buffer now holds THIS SURFACE too (VoxelScene draws the water flat
// before the pass that reflects it), which is what makes one lake able to
// hide another -- and it means every fragment here is testing against its
// own depth. The two draws reach it through different shader programs, so
// ask for a hair of slack rather than bit-equality: far less than the gap
// to anything genuinely in front, far more than two compilers disagree by.
vec2 uv = sc / love_ScreenSize.xy;
if (gl_FragCoord.z > Texel(depthTex, uv).r) discard;
if (gl_FragCoord.z > Texel(depthTex, uv).r + 1e-5) discard;
// THE COLUMN THIS FRAGMENT IS LOOKING AT. Every water pixel is a bar of
// its own standing a whole number of pixels tall, and the ray decides
+13
View File
@@ -1915,6 +1915,19 @@ T.check(plain:find("sc / love_ScreenSize.xy", 1, true) ~= nil,
.. "size -- `screen` counts canvas UNITS, and on a highdpi phone the two "
.. "differ by the density, which clamped the lookup and cut the water "
.. "into blocks")
-- ...and it tests against a buffer that now holds the water surface too,
-- because VoxelScene lays the meshes down flat before the reflective pass
-- runs over them. Nothing else can make one lake hide another: the pass
-- writes no depth (the buffer is detached so it can be READ), so before this
-- the sheets were simply painted in mesh order. Flat water never showed it --
-- one plane, and a farther sheet always lands farther down the screen -- but
-- the world curve drops the far side of the map into the near field of view,
-- and a sea a hundred and fifty tiles away came out rasterised on top of the
-- pond at the player's feet, tall grass and all.
T.check(plain:find("Texel(depthTex, uv).r + 1e-5", 1, true) ~= nil,
"with a hair of slack, because every water fragment is now testing "
.. "against its own depth through a second shader program")
T.check(VoxelScene.drawWater ~= nil, "and the flat draw that puts it there")
-- ------- the lift itself
--
+69 -38
View File
@@ -765,6 +765,35 @@ TEMPLATES = {
z=3, depth=11, stretch=True), # the stool
],
),
# F09 once more, on the interior atlas: the Pokemon Fan Club's four
# members' chairs, drawn round the boardroom table -- 4 placements
# (cells (1,3), (1,4), (6,3), (6,4)) and nowhere else in the game.
# A DIFFERENT drawing from the house stool -- its seat field carries
# a one-pixel white margin where the house's carries two -- but the
# same object band for band, and its elevation rows (11-15: the
# seat's front edge, then the legs) are pixel-identical to the house
# drawing's. So it takes the same part table unchanged, including
# the two-voxel growth north and south.
#
# Those elevation rows come from tiles 59/60, which this drawing
# SHARES with Bill's chair (43/44 over 59/60, modelled as a part of
# `bills_desk`) -- which is why one `stool = 5` height override in
# the tileset entry is right for both seats rather than a special
# case: they are drawn at the same height because they are drawn
# with the same pixels.
"club_stool": dict(
tiles=[
[61, 62],
[59, 60],
],
roof_rows=0, roof_back=0, roof_front=0, roof_cycle=(0, 0),
slab=0, front_eave=0, ledge=None, tileset="interior",
panes=False,
parts=[
dict(kind="upright", x=(2, 13), top=(5, 10), facade=(11, 15),
z=3, depth=11, stretch=True), # the stool
],
),
# F04: the Pokemon Center's PC -- every Center's northeast corner
# (11 placements) plus the Indigo Plateau lobby, whose MART tileset
# shares this atlas. The lab desk-set read again: a
@@ -829,29 +858,31 @@ TEMPLATES = {
# voxel via `inset` -- the pane rule by hand, since
# `panes=False` blocks the global pass.
# rows 16-31, x0..x7 the HOSES (tile 72 twice): one 8-row
# motif per map row -- a horizontal run high on the
# cabinet's side (16-18, x3..x7 -> heights 13..15,
# measured off the drawn rows) and a shaded drop
# (19-23, x2..x7) that bends down INTO THE FLOOR.
# The two stacked motifs are two hoses in DEPTH (the
# drawn row band is the depth band): `box` parts at
# z 18 and z 24, 3 deep, the drop cycling its two
# shading rows (19-20) down to the floor with the
# drawn foot rows (21-23) landing at the bottom --
# the roof-rim rule turned vertical. The second
# hose re-wears the first motif's rows: the atlas
# tile is the same, so the pixels are identical.
# motif per map row, and the two stacked motifs are
# two hoses in DEPTH -- the drawn row band is the
# depth band, and each motif's own rows are its
# elevation WITHIN that band, exactly the potted
# plants' stacked-cell rule. So both hoses hug the
# floor: a horizontal run off the cabinet's side
# (rows 24-26, x3..x7 -> heights 5..7) over a shaded
# drop landing ON the floor (rows 27-31, x2..x7 ->
# heights 0..4) -- all measured, the drawn extent
# maps 1:1 with no continuation. `box` parts at z 18
# and z 24, 3 deep; both wear the LOWER motif's rows
# (the one drawn at its true elevation; the upper
# motif is the same atlas tile, pixel-identical).
# rows 16-31, x24..x31 west variant: the KEYBOARD (7/13), a
# key grid with its cable at the north end and a bar
# down the east edge -- TOP-VIEW art, 16 rows = 16
# depth rows. A `flat` part mounted on the cabinet's
# side at working height (`at` 11, authored -- the
# one number top-view art cannot state), 3 thick,
# top face wearing the drawn art. The stray dark
# dashes east of it (x30-31) are its cast shadow on
# the floor: background. East variant: mirrored
# hoses (runs 16-18 x24..x28, drops 19-23 x24..x29),
# same z slots.
# side at COUNTER level (`at` 8, the counters' own
# 8px band -- the one number top-view art cannot
# state, pinned to the room's work surface), 3
# thick, top face wearing the drawn art. The stray
# dark dashes east of it (x30-31) are its cast
# shadow on the floor: background. East variant:
# mirrored hoses (runs x24..x28, drops x24..x29),
# same rows and z slots.
# rows 0-15 elsewhere wall stripes and cast shadow: BACKGROUND.
# The `wall` element keeps the band solid over the
# back map row, full grid width -- a 16-tall,
@@ -878,16 +909,16 @@ TEMPLATES = {
dict(kind="upright", x=(10, 21), top=(1, 3), facade=(4, 13),
z=20, depth=4,
inset=dict(x=(13, 18), rows=(6, 8))), # the monitor
dict(kind="box", x=(3, 7), rows=(16, 18),
dict(kind="box", x=(3, 7), rows=(24, 26),
z=18, depth=3), # north hose run
dict(kind="box", x=(2, 7), rows=(19, 23), cycle=(19, 20),
base=0, z=18, depth=3), # north hose drop
dict(kind="box", x=(3, 7), rows=(16, 18),
dict(kind="box", x=(2, 7), rows=(27, 31),
z=18, depth=3), # north hose drop
dict(kind="box", x=(3, 7), rows=(24, 26),
z=24, depth=3), # south hose run
dict(kind="box", x=(2, 7), rows=(19, 23), cycle=(19, 20),
base=0, z=24, depth=3), # south hose drop
dict(kind="box", x=(2, 7), rows=(27, 31),
z=24, depth=3), # south hose drop
dict(kind="flat", x=(24, 29), rows=(16, 31),
z=16, at=11, thick=3), # keyboard shelf
z=16, at=8, thick=3), # keyboard shelf
],
),
"center_heal_machine_e": dict(
@@ -907,22 +938,22 @@ TEMPLATES = {
dict(kind="upright", x=(10, 21), top=(1, 3), facade=(4, 13),
z=20, depth=4,
inset=dict(x=(13, 18), rows=(6, 8))), # the monitor
dict(kind="box", x=(3, 7), rows=(16, 18),
dict(kind="box", x=(3, 7), rows=(24, 26),
z=18, depth=3), # NW hose run
dict(kind="box", x=(2, 7), rows=(19, 23), cycle=(19, 20),
base=0, z=18, depth=3), # NW hose drop
dict(kind="box", x=(3, 7), rows=(16, 18),
dict(kind="box", x=(2, 7), rows=(27, 31),
z=18, depth=3), # NW hose drop
dict(kind="box", x=(3, 7), rows=(24, 26),
z=24, depth=3), # SW hose run
dict(kind="box", x=(2, 7), rows=(19, 23), cycle=(19, 20),
base=0, z=24, depth=3), # SW hose drop
dict(kind="box", x=(24, 28), rows=(16, 18),
dict(kind="box", x=(2, 7), rows=(27, 31),
z=24, depth=3), # SW hose drop
dict(kind="box", x=(24, 28), rows=(24, 26),
z=18, depth=3), # NE hose run
dict(kind="box", x=(24, 29), rows=(19, 23), cycle=(19, 20),
base=0, z=18, depth=3), # NE hose drop
dict(kind="box", x=(24, 28), rows=(16, 18),
dict(kind="box", x=(24, 29), rows=(27, 31),
z=18, depth=3), # NE hose drop
dict(kind="box", x=(24, 28), rows=(24, 26),
z=24, depth=3), # SE hose run
dict(kind="box", x=(24, 29), rows=(19, 23), cycle=(19, 20),
base=0, z=24, depth=3), # SE hose drop
dict(kind="box", x=(24, 29), rows=(27, 31),
z=24, depth=3), # SE hose drop
],
),
# F06: the Bike Shop's toolbox -- 2 placements, cells (6,6) and (7,7)