From 89b6c8513f9416fc422e17e55d5e09e97908f248 Mon Sep 17 00:00:00 2001 From: DramaticShape Date: Mon, 27 Jul 2026 03:54:33 -0400 Subject: [PATCH] 1.0.6: conditional pins, Pokemon Tower, plateau statues, cave elevation Adds `when_above` conditional pins, resolved per position in TileShape.at: one graphic can mean two things (the gates' $32 is both wall base course and counter front), and a flat pin has to pick one. Gates get half-height counters and level walls. Pokemon Tower gains a buildings entry -- it is the drawing the map edge cuts off, sealed on the north -- and the Indigo Plateau statues are built like the gym statues. Fixes: grass stands one full-height clump per tile instead of two half-cut stubs at different depths; ledge pillars drop to ledge height; a prop only rides furniture when its own cell is blocked (chairs were being lifted onto tables); and the caves had water and rock pinned backwards, which stood the Seafoam sea up as rock and cut trenches through Mt Moon. No cave tile is below the datum now. --- CHANGELOG.md | 89 ++++ data/voxel_heights.lua | 900 +++++++++++++++++++++++++++++++---------- lib/Structures.lua | 16 +- lib/TileShape.lua | 57 ++- main.lua | 2 +- manifest.json | 2 +- 6 files changed, 849 insertions(+), 217 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b04f9b0..7b081a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,94 @@ # Changelog +## 1.0.6 + +### Added + +- **Conditional pins.** A profile entry may now carry `when_above`: + tile id -> rules keyed on the tile drawn directly north of it, + resolved per POSITION in `TileShape.at`. A pin is per tile id and one + graphic can mean two things -- the route gates' `$32` is both the + wall's dark base course and every service counter's front, and it is + the bottom row of its cell either way. Pinned `wall` the counters + stood a full 16px; pinned `counter` the deep wall banks corrugated + 16/8 for sixteen rows and the room read as crates. What separates the + two uses is what sits on top, so that is what the rule reads. The + gates now have half-height counters AND level walls. + +- **The Pokemon Tower has an exterior.** It is the one catalogued + building drawing the map edge cuts off (no roof band is on the map at + all), and it had no `buildings` entry, so it fell to the volume path + -- which tops a run by repeating its first two rows, laying window + courses flat across the plateau. Sealing the silhouette on the north + alone closes it (88% fill, one piece, against 37% and 126 pieces + unsealed), and one row of roof band spent on the drawing's top margin + costs no window course. It stands as a real tower, panes recessed, + door on the ground. + +- **The Indigo Plateau statues stand up**, built exactly like the gym + statues: plinth a solid 16px block, figure a per-pixel cutout riding + it. On the avenue the statues stack with no gap, so the flood joined + six of them into one 24-row region and the volume builder raised + ridges of boxes with the statue art folded on the front. The same + bird is drawn at the foot of every badge-check pillar, so those are + crowned too. + +### Fixed + +- **Tall grass: one clump per tile.** Each 8x8 tile is a whole clump, + but the template split every tile AGAIN into its top and bottom four + art rows and stood those at two different depths -- so any blade + running down a tile was cut in half, into two 4px stubs 4px apart. + One tile is now one full-height standing slab at its own depth; a + cell's 2x2 tiles still stand independently, so the player walks + between the north and south rows. + +- **Ledge lines are continuous.** `$34` is the cliff slope's foot and + also the pillar between hop-down segments; pinned `wall` with the + rest of the slope chain (the Diglett's Cave fix) it stood those + pillars 16px beside a 6px lip. At ledge height the run reads as one + lip, and the mound is unchanged -- its foot row reads as the talus it + is drawn as. + +- **A prop only stands on furniture when its own cell is blocked.** + "Is something drawn above me" is not "am I standing on it": a chair + drawn against the north side of a table is above the table's trim row + too, and was being lifted onto the tabletop, with its claimed cells + re-tiled as tabletop so the table marched two rows north. Three + chairs in Cinnabar's trade room and Fuchsia's meeting room, and the + Celadon diner's stools. The world already knows the difference: a + thing that sits ON furniture occupies a blocked cell, a seat you walk + up to is in a walkable one. + +- **Caves: nothing below sea level, and the water is water.** Two tiles + were identified backwards in the first pass. `$14` -- the tile the + engine animates, that `Map.WATER_TILES` names and that Surf runs on + -- was pinned `wall`, so Cerulean Cave's lake and the Seafoam sea + stood up as rock slabs. And the pale dithered rock fill was pinned + `water`, cutting 612 tiles of two-cell-wide trench through four maps. + Both corrected; a sweep of all 19 cave maps now reports zero tiles + below the datum. The elevation scheme is documented in the entry and + derived from the game's own `tilePairs`: dark floor, water and drop + holes at 0, the lit shelf a 6px step above, rock at 16. + +- **Cave ladders climb.** Which ladder graphic goes up and which goes + down is unanimous in the warp table -- 37 cells of one always warp + down, 40 of the other always up -- so they are real stepped flights + now, not painted plates. + +- **Poke Mart's register stands on the counter.** The pin was on the + wrong tile: `$08` is the counter's own top band, not the register, so + the standee flood ate everything but two black lines. The register is + the keypad-and-receipt drawing one row up. + +- **Celadon's televisions**, which were solid 16px boxes wearing the TV + art on one face, and the **Pokemon Tower reception desk** and the + **gate counters**, which stood at wall height, are all their drawn + heights now. The **Fan Club and Silph boardroom statues** were read + as seated chairmen and painted onto the tabletop; they are cutouts + standing on their pedestals, and the tables are cut to a true + octagonal footprint. + ## 1.0.5 ### Added diff --git a/data/voxel_heights.lua b/data/voxel_heights.lua index 77e2b21..e171f27 100644 --- a/data/voxel_heights.lua +++ b/data/voxel_heights.lua @@ -291,14 +291,39 @@ return { -- The caves: one blockset serves nineteen maps -- Mt Moon's three -- floors, Rock Tunnel's two, Seafoam Islands' five, Victory Road's -- three, Cerulean Cave's three, and Diglett's Cave with its two - -- route-side stubs. A Gen 1 cave is drawn as TWO floor levels: a - -- dark lower floor ($20 rough, $21 bright checker, $2A fine checker) - -- and LIT shelves ($05) raised above it. data.field.tilePairs - -- forbids stepping between the two ($20/$21/$2A/$41 <-> $05), which - -- is what the shipped `ledge` pin on $05 records: the lit floor is a - -- step up off the dark floor, reached only by the drawn ramps. - -- Everything else in here is rock, and in a dungeon the rock IS the - -- room -- so every rock tile is pinned to ONE 16px course. + -- route-side stubs. + -- + -- THE CAVE'S VERTICAL SCHEME. A Gen 1 cave is drawn on two floor + -- levels, and the game's own collision data says which is which. + -- data.field.tilePairs forbids stepping between $20/$21/$2A/$41 and + -- $05 on land, and between $14 and $05 while SURFING -- but nothing + -- forbids $14 <-> $20. Read that back and the elevation falls out: + -- the dark floor and the water are one plane, the lit floor is a + -- step above both, and the rock is the room around them. + -- + -- h what tiles + -- 0 DARK LOWER FLOOR -- the datum $20 $21 $2A + -- 0 CAVE WATER, the surfable pool $14 + -- 0 fall-through drop hole $2F $22 + -- 0 unlit black (the void rule, no pin) $3C + -- 3 Victory Road's boulder switch plate $2B $2C $2D $2E + -- 6 LIT SHELF, one step up (`ledge`) $05 $29 + -- 6 the stair plate down off that shelf $15 $16 + -- 16 ROCK -- one band for the masses, the everything else + -- faces, the caps and the ceiling mass + -- 0 up 16 ladder shaft UP a floor $0A $0B $1A $1B + -- 0 dn 16 ladder shaft DOWN a floor $08 $09 $18 $19 + -- + -- NO CAVE SURFACE SITS BELOW 0. The datum is the dark floor; the + -- water is level with it (you surf off a pool straight onto the dark + -- floor, which is exactly what the missing $14/$20 tile pair says); + -- every other surface is above it. A cave is underground, not + -- underwater, and the only class in this file that goes negative is + -- `water` (-2), which exists to cut the overworld's shoreline lip. + -- It is deliberately NOT used anywhere in this entry. The one thing + -- that does reach below the datum is the DOWN ladder's stairwell, + -- which is a hole on purpose: it is the shaft to the floor below, + -- and no walkable surface is drawn in it. -- -- What the detector made of it: the rock EDGES towered. A shelf's -- side is drawn as a cap over a run of face tiles over a corner @@ -319,84 +344,165 @@ return { -- volume builder, so pinning the rock is the whole fix: there is no -- run left to misread. CAVERN = { - -- The rock, one 16px course. Reading across the blockset: - -- $02/$03 over $12/$13 is the speckled rock mass and $0C/$0D over - -- $1C/$1D the boulder-cluster mass, both solid wall-to-wall - -- fills; $10/$11 is the cobble course facing the south side of - -- every lit shelf, $31 its west face and $17 its east; $04/$07/ - -- $28 are the caps and $25/$26 the inner corners where two faces - -- meet. $0E/$0F over $1E/$1F is the barred rock shelf that - -- stands alone on the dark floor (MT_MOON_1F, ROCK_TUNNEL_1F and - -- twice in SEAFOAM_ISLANDS_B4F): one cell of face-on drawing, so - -- one 16px face -- never a standee, its black surround touches - -- all four rims and would segment into a solid slab. + -- ---- 16: the rock, one band ---- -- - -- $14 is the bright rubble mass that fills Cerulean Cave and the - -- lower Seafoam floors, and it is the $14 WATER-FALLBACK TRAP: - -- unpinned it hits the engine's stale-cache water set, and every - -- cell it floors RECESSED -- probed `14w-2` over whole blocks of - -- SEAFOAM_ISLANDS_B4F and CERULEAN_CAVE, a cavern of ponds. + -- Reading across the blockset: $02/$03 over $12/$13 is the + -- speckled rock mass and $0C/$0D over $1C/$1D the boulder-cluster + -- mass, both solid wall-to-wall fills; $10/$11 is the cobble + -- course facing the south side of every lit shelf, $31 its west + -- face and $17 its east; $04/$07/$28 are the caps and $25/$26 the + -- inner corners where two faces meet. $0E/$0F over $1E/$1F is the + -- barred rock shelf that stands alone on the dark floor + -- (MT_MOON_1F, ROCK_TUNNEL_1F and twice in SEAFOAM_ISLANDS_B4F): + -- one cell of face-on drawing, so one 16px face -- never a + -- standee, its black surround touches all four rims and would + -- segment into a solid slab. + -- + -- $06/$27 over $24/$01 is the CEILING MASS: a pale dithered rock + -- fill with a black rim dithered along its north and south edges, + -- so a run of it tiles into the grid of light blocks that walls + -- off most of MT_MOON_B2F (37 cells) and stands as one lump in + -- each of VICTORY_ROAD_1F/2F/3F. It is blocked in every map that + -- places it ($24 is not on the walkable list) and it is NOT in the + -- engine's water set, so it is rock and belongs in the band with + -- the rest of the rock. The first pass read the checker as a pond + -- and pinned it `water`: that recessed 612 tiles to -2 and cut + -- open trenches down the middle of Mt Moon B2F -- 06w-2 27w-2 / + -- 24w-2 01w-2 for ten cells straight, hard against 31w16 rock and + -- the 05l06 shelf. Its unpinned reading was 48px (eight rows of + -- alternating $06/$24 read as one drawing); 16 is the answer. wall = { 2, 3, 18, 19, -- $02/$03 over $12/$13, speckled mass 12, 13, 28, 29, -- $0C/$0D over $1C/$1D, boulder mass 16, 17, -- $10/$11, the shelf's south cobbles 49, 23, -- $31 west face, $17 east face 4, 7, 40, -- $04 NW cap, $07 NE cap, $28 SW cap 37, 38, -- $25/$26, the inner corners - 20, -- $14 rubble mass -- the water trap - 14, 15, 30, 31 }, -- $0E/$0F over $1E/$1F, barred shelf - -- the lit floor and the shading row along its north rim: one 6px - -- shelf. $05 is the shipped pin (the tilePairs step). $29 is the - -- SAME surface -- the artist's shadow row where the shelf runs up - -- against the rock above it -- but it sits in walkable cells, so + 14, 15, 30, 31, -- $0E/$0F over $1E/$1F, barred shelf + 6, 39, 36, 1 }, -- $06/$27 over $24/$01, ceiling mass + -- ---- 6: the lit shelf, and the stairs off it ---- + -- + -- $05 is the lit floor -- the shipped pin, and the step + -- data.field.tilePairs encodes ($20/$21/$2A/$41 <-> $05). $29 is + -- the SAME surface: the artist's shadow row where the shelf runs + -- up against the rock above it. It sits in walkable cells, so -- unpinned it resolved to flat ground and cut a 6px trench along -- the north edge of every lit room (`29g00` with `05l06` one tile -- south, all the way across MT_MOON_B2F). - ledge = { 5, 41 }, - -- the cave ponds: $06/$27 over $24/$01, the tileset's - -- TILEANIM_WATER cell -- Mt Moon B2F's lake, Victory Road's - -- pools, the Seafoam channels you Surf. The engine's water set - -- does not name them (it carries the stale $14/$32/$48 ids - -- instead), so unpinned they fell to the wall fallback and the - -- volume builder read the alternating $06/$24 rows as a drawing - -- eight rows tall: `27w48`/`01w48`, a 48px wall of water standing - -- down the middle of MT_MOON_B2F. - water = { 6, 39, 36, 1 }, - -- Victory Road's boulder switches ($2B/$2C over $2D/$2E, the four - -- plates on 1F, 2F and 3F). Round art, but NOT a round object: - -- the Strength boulders themselves are overworld SPRITES, and - -- this is the plate they get pushed onto -- drawn from straight - -- above as a black ring, a white highlight rim and a grey disc. - -- `cylinder` was tried here first, because a circle is what the - -- overworld's round canopies take, and it came out wrong: the - -- plate's own dithered background carries stray black pixels the - -- darkest-outline flood cannot reach, so they joined the mask, - -- inflated the top and bottom row spans to the full 16, and the - -- hull rendered as a clutch of white shards standing where the - -- plate should be (VICTORY_ROAD_1F cell 17,13). `relief` is the - -- reading anything drawn from above wants: the disc stays flat - -- and the pixels inside its black ring lift a few voxels, so it - -- reads as a plate -- and the cell is walkable, so you step on it. - relief = { 43, 44, 45, 46 }, - -- the flat things, pinned so nothing can raise them: both ladder - -- graphics ($08/$09 over $18/$19, $0A/$0B over $1A/$1B), drawn - -- from ABOVE and carrying the tileset's warp tiles; the ramp - -- treads that walk you off a lit shelf down to the dark floor - -- ($15/$16, four steps seen from above); and the drop holes of - -- Seafoam and Victory Road ($2F rim over $22). All of them - -- already resolved to ground through their walkable cells -- the - -- pins keep that true whatever cell a block puts them in, and - -- keep the hole's lit rim out of the wall fallback. - ground = { 8, 9, 24, 25, -- $08/$09 over $18/$19, ladder - 10, 11, 26, 27, -- $0A/$0B over $1A/$1B, ladder - 21, 22, -- $15/$16, the ramp treads + -- + -- $15/$16 is the STAIR down off that shelf, and the elevation it + -- spans is 6px, not 16. Its art is four treads seen from above, + -- each a light tread over a black riser line, stacked NORTH-SOUTH: + -- every one of the 54 stair cells in the tileset has the lit floor + -- $05 to its NORTH and the low ground to its SOUTH (dark floor x35, + -- water x14, lit floor x5 where two flights meet), so the flight + -- climbs NORTHWARD, one cell deep, 0 -> 6. + -- + -- It is NOT pinned `stair_e`/`stair_w`, and that is deliberate. + -- Those classes build a flight that marches along X -- 16px tall, + -- rising toward the named side -- so either of them here would + -- throw a 16px staircase sideways across a 6px north-south step, + -- blocking the passage it is supposed to open and climbing at + -- right angles to the drawn risers. A wrong-way flight is worse + -- than a flat one. `ledge` is the honest reading available: the + -- stair cell joins the shelf it belongs to, wears its four treads + -- on the TOP face (which is how they are drawn -- from above), and + -- puts its 6px riser face at the FOOT of the flight where the + -- player actually steps down onto the dark floor. See the report: + -- a real sloped cave stair wants `stair_n`/`stair_s` in + -- Structures.stairCell, which is an engine change, not a pin. + ledge = { 5, 41, -- $05 lit floor, $29 its north shading + 21, 22 }, -- $15/$16, the stair plate off it + -- ---- 0: the floor plane ---- + -- + -- The dark lower floor, pinned rather than left derived so the + -- datum this whole entry measures from is stated in it. $20 is + -- the rough floor, $21 the bright checker, $2A the fine checker; + -- all three are on the tileset's walkable list and all three + -- already resolved here -- the pins restate the level, they do not + -- move it. + -- + -- $14 IS THE CAVE WATER: the tileset animates by TILEANIM_WATER, + -- and TileRenderer's WATER_TILE is $14 -- it is the tile that + -- h-shifts to ripple. Map's water set names it too, so its cells + -- are the ones Surf works on: Cerulean Cave's lake (352 + 272 + -- tiles on 1F/B1F) and the Seafoam channels (304 on B3F, 816 on + -- B4F). The first pass read its blocky white dither as "bright + -- rubble mass" and pinned it `wall`, which stood both lakes up as + -- 16px rock -- two thirds of SEAFOAM_ISLANDS_B4F was a rock slab + -- you were meant to surf across. It is water, and it lies at the + -- floor plane: `ground` keeps the drawn (and animated) water art + -- exactly as it is and puts its surface at 0, level with the dark + -- floor you step off onto, one 6px step below the lit shelf you + -- cannot surf onto. NOT the `water` class -- that is -2, a + -- shoreline lip for the overworld sea, and a cave pool must never + -- read as a trough sunk into the floor you are walking on. + -- + -- $2F over $22 is Seafoam's and Victory Road's fall-through drop + -- hole (Map's warpPadTiles calls $22 a "hole"). Its art is a lit + -- rim over solid black and its cell is walkable -- you step on it + -- and drop a floor -- so it stays flat at the floor plane; the pin + -- keeps the rim out of the wall fallback. + ground = { 32, 33, 42, -- $20/$21/$2A, the dark lower floor + 20, -- $14, the surfable cave water 47, 34 }, -- $2F over $22, the drop hole - -- Left to the derived defaults on purpose. The dark lower floor - -- ($20, $21, $2A) is in the tileset's walkable list and resolves - -- to flat ground at level 0 -- the level the tilePairs step above - -- is measured FROM -- so pinning it would only restate the - -- default. $3C is solid black and the void rule flattens it - -- (MT_MOON_B1F's unlit north band probes `3Cv00`); pinning it - -- would BREAK that, since the void rule skips authored tiles. + -- ---- 0 to 16: the ladders, the caves' real staircases ---- + -- + -- Both ladder graphics are drawn from above as a shaft with two + -- rails and rungs between them, and which one is which is not a + -- guess -- it is in the warp table. Across all nineteen maps, + -- every one of the 37 $08 cells warps to a LOWER floor + -- (MT_MOON_1F->B1F, B1F->B2F, ROCK_TUNNEL_1F->B1F, each + -- SEAFOAM_ISLANDS floor to the next one down, CERULEAN_CAVE_2F->1F + -- and 1F->B1F, the Diglett's route stubs down into the cave) and + -- every one of the 40 $0A cells warps to a HIGHER one (the exact + -- reverse, plus MT_MOON_B1F's and ROCK_TUNNEL_1F's exits back out + -- to daylight). $08 is the ladder DOWN, $0A the ladder UP: 77 + -- cells, no exceptions. + -- + -- So they take the two classes that MOVE between floors. $0A + -- becomes a rising flight -- four real steps climbing 0 -> 16, the + -- height of the rock band it disappears into -- and $08 becomes an + -- excavated stairwell, four steps descending below the floor into + -- a dark opening. East for both: the ladder art is symmetric + -- about its own centre line so the drawing does not choose a side, + -- the cell's east neighbour is the closed (rock) side more often + -- than its west, and the rest of this profile's staircases + -- (REDS_HOUSE_1, UNDERGROUND) climb east too. The flight's treads + -- land one rung apiece, which is as close to a drawn ladder as + -- stepped geometry gets. + -- + -- All four tiles of each cell are listed, the way REDS_HOUSE_1 + -- lists its flight: buildStairs anchors on the cell's TOP-LEFT + -- tile ($08 / $0A) and claims the other three, but pinning them + -- too keeps them out of the region flood and the door fold. + stair_e = { 10, 11, 26, 27 }, -- $0A/$0B over $1A/$1B, UP + stair_down_e = { 8, 9, 24, 25 }, -- $08/$09 over $18/$19, DOWN + -- ---- 3: the boulder switches ---- + -- + -- Victory Road's four plates ($2B/$2C over $2D/$2E, on 1F, 2F and + -- 3F). Round art, but NOT a round object: the Strength boulders + -- themselves are overworld SPRITES, and this is the plate they get + -- pushed onto -- drawn from straight above as a black ring, a + -- white highlight rim and a grey disc. `cylinder` was tried here + -- first, because a circle is what the overworld's round canopies + -- take, and it came out wrong: the plate's own dithered background + -- carries stray black pixels the darkest-outline flood cannot + -- reach, so they joined the mask, inflated the top and bottom row + -- spans to the full 16, and the hull rendered as a clutch of white + -- shards standing where the plate should be (VICTORY_ROAD_1F cell + -- 17,13). `relief` is the reading anything drawn from above + -- wants: the disc stays flat and the pixels inside its black ring + -- lift a few voxels, so it reads as a plate -- and the cell is + -- walkable, so you step on it. + relief = { 43, 44, 45, 46 }, + -- Left to the derived default on purpose: $3C is solid black -- + -- the unlit rock that is most of MT_MOON_B1F -- and the void rule + -- flattens it (`3Cv00`). Pinning it would BREAK that, since the + -- void rule skips authored tiles; a 16px black band there would + -- also wall the corridors in with something the 2D art draws as + -- nothing at all. $30 and $41 are on the walkable list and in + -- tilePairs but no block in this tileset places either, so there + -- is nothing for a pin to catch. }, -- Viridian Forest. Nearly everything drawn here is ROUND, and the -- detector was boxing all of it: the big trees came out as ragged @@ -627,21 +733,46 @@ return { -- healing consoles wall = { 23, 29, 40, 44, 45, 46, 47, 62, 63, 78, 79, 90, 91 }, -- the clerk's counter, half a cell high like every service - -- counter: the south front (24/25), the east-facing display run - -- (16/41/89 panels, 14/15/30/31 the juice-poster case whose art - -- rides the top like the nurse's tray), and the dark section - -- beside the register (56, near-black -- pinned or the void rule - -- flattens it) - counter = { 14, 15, 16, 24, 25, 30, 31, 41, 56, 89 }, - -- the cash register stands ON the pinned counter through the - -- authored-box support rule, black-outline segmented - billboard = { 8 }, + -- counter. It is a C wrapping the alcove the clerk stands in: the + -- south arm's drawn front (24/25) under its top band (8/56), the + -- light work surface the east arm and the rest of the south arm + -- share (16/41), and the north arm's end panel (89). 8 and 56 are + -- the two halves of one top band -- the first pass read 8 as the + -- cash register, pinned it `billboard`, and got a bare black + -- outline standing on the counter with the tile's own art replaced + -- by its neighbour's. + counter = { 8, 16, 24, 25, 41, 56, 89 }, + -- The CASH REGISTER (14/15 over 30/31), a keypad and a curl of + -- receipt paper drawn face-on across two tile rows in the middle of + -- the counter's east arm. Pinned `counter` it was just paint on + -- the work surface. In the `billboard` pool it is a standing + -- per-pixel cutout ten voxels deep -- the flower-and-vase treatment + -- of Red's house, but with a machine's body rather than a stem -- + -- and the support rule lifts it onto the counter automatically, + -- because the tile drawn directly BELOW it (16/41) is a pinned 8px + -- box. Its own pool, so the counter can never absorb it. The art + -- has a clean light margin on three sides and a black outline all + -- round, so the shade flood drains the work surface away and leaves + -- the machine whole (verified pixel by pixel before shooting). + billboard = { 14, 15, 30, 31 }, -- the free-standing shelf racks: TALL drawings, not deep ones -- -- each rank collapses onto a one-cell-deep shelf at its drawn -- height (the Dojo/Red's-house treatment). 64/65/67 and 80/81/83 -- are the bottle rows the clerk's booth also wears as its top -- display; 68/69/71 and 84/85/87 the goods rows below bookcase = { 64, 65, 67, 68, 69, 71, 80, 81, 83, 84, 85, 87 }, + -- Left to the derived default on purpose: the floor checker and + -- its shadowed variants (1/11/17/26/27/54) and the exit mat + -- (12/28) all sit in cells the ROM marks walkable, so the cell + -- rule lays them flat unaided; 76/77 is the SALE case's black + -- interior, which the volume path recesses half a course into the + -- band -- a dark display niche, which is what it is drawn as. + -- NOT covered: INDIGO_PLATEAU_LOBBY, the ninth map on this id and + -- the only one that is not the 4x4 shop. It draws its own hall, + -- lift bank and rope stanchions from tiles no Mart places, three + -- of which fall in the engine's stale water set -- see + -- reports/COUNTERS_pass2.md. Pinning them is a tileset pass of + -- its own and is deliberately not attempted here. }, -- The route gates and their upstairs lounges, the four Underground @@ -662,7 +793,8 @@ return { -- walkable. GATE = { -- The wall band stays ONE 16px face. Three wall drawings share - -- it: the route gates' panelled course (32/33 over 50/51), the + -- it: the route gates' panelled course (32/33 -- its base course + -- 50/51 is `counter`, see below), the -- Underground Path huts' and Route 22's plain stripes (0), and -- Route 2's speckled plaster (72 over its dark skirting 74). 0 -- and 74 need no height correction but are pinned anyway, so the @@ -675,30 +807,54 @@ return { -- the floor -- the Center's warp-tile treatment, and pins are -- look-only so the cell stays walkable. -- - -- 50/51 is the one compromise in this entry. The same two tiles - -- draw the wall's base course and every counter's front, and a pin - -- cannot tell the two uses apart. Pinned `counter` (8) the - -- counters come out perfect and the wall band castellates: it - -- stacks 32/33 over 50/51 for up to sixteen tile rows down the - -- east and west sides, so a 16px ridge and an 8px trench alternate - -- every 8px of depth and the room ends up built out of crates. - -- Pinned `wall` the band is one clean face everywhere and the - -- price is that a counter's FRONT row stands 16 instead of 8 -- - -- which is cheap, because the mesher's authored-top rule still - -- lays the drawn counter surface (7/8/9) on that block's top face - -- and folds the drawn front panel up its face. It reads as a - -- full-height service counter, not as a slab. The wall is the - -- room's frame and it wins. (FOREST_GATE never places 32/33, so - -- there the counter keeps its half cell.) - wall = { 0, 32, 33, 41, 43, 45, 50, 51, 58, 61, 62, 72, 74 }, - -- the counter, half a cell high: its north rim and end caps (7/8), - -- the long run between them (9), and the top surface a counter - -- drawn running north-south wears (23/24). 8px is one clean band, - -- so the surface stays on top and the guard leans on it rather - -- than standing behind a wall stub. The counters that run wall to - -- wall (Route 12's pair) are pure 8px; the ones that end in the - -- open carry the 16px front row above. - counter = { 7, 8, 9, 23, 24 }, + -- 50/51 is NOT in that list, and that is the one deliberate cost in + -- this entry. The artist draws one panel twice: as the wall cell's + -- base course (32/33 over 50/51) and as the front of every counter + -- (7/9/8 or 17/18 over 50/51). A pin is per tile id and a class + -- carries one height, so the two uses cannot be separated here at + -- all -- only in `lib`, by letting a pin be conditional on the tile + -- drawn ABOVE (see reports/COUNTERS_pass2.md). Pinned `wall` (16) + -- the band is one clean face and the counter the guard stands + -- behind is a full 16px slab that hides him to the shoulders. + -- Pinned `counter` (8) every counter is the half cell it is drawn + -- as -- which is what the room is FOR -- and the price is that the + -- wall cell's front 8px of depth drops with it: a shallow wall + -- gains an 8px plinth (reads as a wainscot), and the deep margin + -- masses that run two to eight cells north-south down the east and + -- west sides of the fourteen maps that place both drawings (Routes + -- 5/6/7/8, the five gate 1Fs, the Safari gate and its four rest + -- houses) corrugate, their exposed flank stepping 16/8/16/8 every + -- 8px of depth. The other six -- Route 2, Route 22 and the four + -- Underground Path huts -- place 50/51 and never 32/33, so they + -- take the half cell for nothing. The counters win where it costs: + -- they are the object the player walks up to and talks over, they + -- sit mid-room, and the margin masses are pure border filler seen + -- edge-on. This also puts GATE and FOREST_GATE on the same footing + -- at last -- ROUTE_2_GATE draws the forest gates' room tile for + -- tile and used to be the only one of the three with a 16px front. + wall = { 0, 32, 33, 41, 43, 45, 58, 61, 62, 72, 74 }, + -- the counter, half a cell high everywhere it is drawn: its north + -- rim and end caps (7/8), the long run between them (9), the top + -- surface a counter drawn running north-south wears (23/24), and + -- the drawn front panel (50/51) standing up as one clean 8px band + -- with the surface riding the top face. The guard leans on it + -- instead of standing behind a wall stub, and a counter that runs + -- wall to wall (Route 12's pair) matches one that ends in the open. + counter = { 7, 8, 9, 23, 24, 50, 51 }, + -- 50/51 draws BOTH the counter's front and the wall's base course, + -- and it is the bottom row of its cell either way -- so a flat pin + -- has to pick one and be wrong about the other. Pinned `wall` the + -- counters stood a full 16px; pinned `counter` the deep border + -- banks corrugated 16/8 for sixteen rows and the room read as + -- crates. What tells the two uses apart is what is drawn ABOVE: + -- the wall stacks its panelled course (32/33) over the base, while + -- a counter carries its own top (7/8/9). So the counter pin above + -- is the default and this puts the wall back wherever the panel + -- sits on top of it -- resolved per position, in TileShape.at. + when_above = { + [50] = { { above = { 32 }, class = "wall" } }, + [51] = { { above = { 33 }, class = "wall" } }, + }, -- the tall glass-fronted cabinets: three shelf ranks (34/35) -- folded up a 24px box whose top face adopts the drawn top rim -- (7/8, pinned `counter` directly above it) through the mesher's @@ -723,7 +879,8 @@ return { -- the potted palm that stands in every hut and rest-house corner -- (5/6 crown, 21/22 fronds, 37/38 over 53/54 the pot), and Route -- 22's wall plant (14/15 crown, 30/31 pot), which stands on the - -- 16px planter its base course (50/51) makes. + -- half-cell planter its base course (50/51) makes -- a planter box + -- now rather than the 16px pedestal it used to perch on. prop = { 5, 6, 14, 15, 21, 22, 30, 31, 37, 38, 53, 54 }, -- the exit doormat (4 over 20): drawn from straight above, so it -- has to lie flat -- and 20 is the $14 water-fallback trap, which @@ -748,14 +905,17 @@ return { -- Viridian Forest's north and south gates. Tile for tile they are -- the same drawing as ROUTE_2_GATE, but they are their own tileset - -- id, so they need their own copy of the pins -- and they get the - -- one thing GATE cannot have. These two maps never place the - -- panelled wall course (32/33), so nothing else claims 50/51 and the - -- counter can be the half cell it is drawn as. Before this entry - -- the room was a pond with an island: 72 ($48) sank the north wall's - -- top course, 50 ($32) sank all four counter fronts, and 20 ($14) - -- sank the exit doormat -- the three stale-cache water ids, all - -- three placed in one small room. + -- id, so they need their own copy of the pins. These two maps never + -- place the panelled wall course (32/33), so 50/51 is a counter + -- front here and nothing else -- the half cell it is drawn as costs + -- this id nothing at all, where GATE pays for it in the wall (see + -- the note there). Both ids run 50/51 `counter` now, so the three + -- rooms that share this drawing -- the two forest gates and + -- ROUTE_2_GATE -- finally match. Before this entry the room was a + -- pond with an island: 72 ($48) sank the north wall's top course, 50 + -- ($32) sank all four counter fronts, and 20 ($14) sank the exit + -- doormat -- the three stale-cache water ids, all three placed in + -- one small room. FOREST_GATE = { -- the wall band, one 16px face: the speckled plaster course (72) -- over its dark skirting (74), plus the door pane (41/43) drawn @@ -814,9 +974,6 @@ return { -- end of 1F's counter top, where 16px reads as a sign -- board standing on the counter -- the one placement -- that is not wall, and the least bad of the two) - -- 14/15/30/31 the TV set: on 3F's wall band it stands over the - -- display case below it, and on 3F's goods counter the - -- same 16px box reads as a floor-standing display unit -- 62/63 the lift call-button panel in both 2x2 cabins -- 72/73/88/89 the framed picture (72 is $48, a water-set id, so -- unpinned its top half sank) @@ -829,9 +986,33 @@ return { -- 91 the dithered wall course above the Prize Room counter -- and the roof stairhead -- 92/93 the Rocket lift's horizontal cabin frame - wall = { 1, 2, 3, 6, 14, 15, 18, 19, 22, 30, 31, 33, 46, 47, - 62, 63, 68, 72, 73, 75, 76, 77, 78, 79, 84, 88, 89, - 91, 92, 93 }, + -- 70/71 NOT wall panelling: the round tables' lower-left and + -- lower-right rim ($46/$47). They are here to lift the + -- terrace and diner tabletops to the one height their + -- middle can be built at -- see the long round-table + -- note under `counter`. The tileset places them in the + -- two table blocks (29, 49) and nowhere else, so 16px + -- costs nothing anywhere in the group. + wall = { 1, 2, 3, 6, 18, 19, 22, 33, 46, 47, + 62, 63, 68, 70, 71, 72, 73, 75, 76, 77, 78, 79, 84, + 88, 89, 91, 92, 93 }, + -- 3F's television sets ($0E/$0F/$1E/$1F): the one drawing in this + -- tileset that is a deliberate object with a body -- a black-framed + -- cabinet, a bezel and a lit screen, drawn face-on -- and the same + -- thing Red's living room stands up. Six placements: four on the + -- two goods counters, two against the east wall band. As `wall` + -- each was a 16px CUBE with the TV art folded onto one face, so the + -- counters wore grey filing cabinets and the TV shop read as a + -- stockroom. The standee pool cuts the drawing per pixel at 10 + -- voxels of body instead, and the support rule does the rest: the + -- four counter sets are drawn directly above an authored 8px + -- `counter` row, so they STAND ON the counter and the tiles they + -- claim keep rendering as counter top rather than holing it. (The + -- art carries a full black frame with light margin inside it, so + -- the segmentation flood has nothing to drain and the whole set + -- survives.) Its own pool, away from `stool`, so a TV never stacks + -- with a chair drawn beside it. + billboard = { 14, 15, 30, 31 }, -- Every service counter in the group, half a cell high like the -- Center's and the Mart's. One 8px band means the drawn front row -- stands up and everything above it rides the TOP face in drawn @@ -842,18 +1023,53 @@ return { -- 38/39/41 the top surface and its end caps -- 21/48/49 the south front panel and its end caps -- 54/57 the arms' left and right side edges - -- 36/37/52/53 the goods laid out on 3F's counter (a boxed set - -- and a Poke Ball) -- they ride the top face, drawn - -- once, the way the nurse's tray does - -- 9/25/70/71/85/86/87 the round tables of the diner and the - -- roof terrace, drawn from above with a pedestal - -- row at the south: the same half-cell treatment - -- puts the tabletop on top and the pedestal on the - -- front. (Their four INTERIOR tiles are $37, which - -- is also the checkerboard floor and the Prize - -- Room's upper wall -- see the note below.) + -- 36/37/52/53 the games console and its controller laid out on + -- 3F's counter. This is pixel for pixel the drawing + -- Red's house pins `relief` -- a prop seen from + -- ABOVE -- and `relief` is the class it wants. It + -- cannot have it here: Structures.buildRelief anchors + -- its extrusion at y=0 and has no support lookup, + -- while every placement of this drawing in the + -- tileset is on a counter TOP. Pinned `relief` the + -- cell is claimed, dropped to floor level and punched + -- a 16x16 hole clean through the counter (verified + -- in-engine, pass-2 report). `counter` leaves the + -- console exactly where relief would put it -- flat, + -- flush with the surface, art on the top face, drawn + -- once -- and gives up only relief's three voxels of + -- lift. The report carries the one-place engine + -- change that would free it. + -- 9/25/85/86/87 the round tables of the diner and the roof + -- terrace: their north rim (9/25 = $09/$19) and the + -- pedestal course at the south (85/86/87). + -- + -- THE ROUND TABLES in full, because the shape is a compromise. The + -- drawing (block 29, and the same four rows split across blocks 45 + -- and 49 in the diner) is + -- $09 $27 $27 $19 an octagonal top seen from above, with + -- $36 $37 $37 $39 a pedestal drawn below its southern + -- $46 $37 $37 $47 rim + -- $55 $56 $57 $37 + -- Its four INTERIOR tiles are $37, which is ALSO the light half of + -- the checkerboard floor and the plain upper wall of the Prize Room + -- and the roof stairhead, so $37 cannot be pinned at all (see the + -- closing note). Left to the detector those four are a two-row + -- column, which buildVolume reads as a repeat, floors at unit 2 and + -- builds at 16px -- and with the whole rim pinned `counter` at 8px + -- the terrace tables came out as grey CUBES sitting on trays. + -- + -- So stop fighting the 16px and let it BE the tabletop: 70/71 + -- ($46/$47, the lower-left and lower-right rim) move to `wall`, and + -- the table's whole front course then stands at the same 16px its + -- middle already does -- one flat disc, the drawn octagon on its + -- top face and the lower arc folded down its front. What stays at + -- 8px is the FAR rim (9/25, and the shared 39/54/57) and the + -- pedestal (85/86/87): the far rim is occluded by the 16px top in + -- front of it, and the pedestal is meant to sit low. 16px is also + -- the right height against the 8px `stool` chairs drawn around it + -- -- a terrace table you sit at, not a footstool. counter = { 9, 21, 25, 36, 37, 38, 39, 41, 48, 49, 52, 53, 54, - 57, 70, 71, 85, 86, 87 }, + 57, 85, 86, 87 }, -- Free-standing merchandise racks and glass cases: TALL drawings, -- not deep ones. The four-row rack (42-45 / 58-61 / 64-67 / -- 80-83) stands two racks abreast on floors 2F-5F and along the @@ -885,15 +1101,14 @@ return { -- $37 (55) is three different things -- the light half of the -- checkerboard floor, the interior of the round tables, and -- the plain upper wall of the Prize Room and the roof - -- stairhead. The cell rules already answer all three - -- correctly (the floor's cells are walkable through their - -- $45 bottom-left tile; the wall's are not), and pinning it - -- to any one class breaks the other two. The cost is the - -- four $37 tiles inside each round table, which stay a - -- detected 16px box in the middle of the 8px top -- read as - -- a centrepiece. Fixing it properly needs the PaletteFX - -- alias ($37 -> $5a on the roof and the diner) carried into - -- the shape lookup, which is engine work. + -- stairhead -- and every class that suits one ruins the + -- others: `ground` holes the wall and the tabletop, `counter` + -- turns every second floor tile into an 8px lump, `wall` turns + -- it into a 16px pillar. The cell rules already answer the + -- floor (its cells are walkable through their $45 bottom-left + -- tile) and the wall (its cells are not), and the tabletop is + -- answered above by raising 70/71 to meet the 16px the + -- detector builds there. Left unpinned on purpose. -- $10 (16) is pure black and the void rule already flattens it -- to the interior darkness above the Game Corner's wall. -- The stair openings (10/11/26/27, 40/56) and the lift doors @@ -1023,27 +1238,52 @@ return { -- 10/11 the dial faces, 74/75 + 90/91 the narrow filler columns, -- 26/27 the plinth with its feet, 59 the black end pilaster; -- 40/41 are the metronome room's and the meeting room's shelf - -- ranks. Where the drawing's own top row is the shared table - -- trim (64/66, pinned `table` below) the bookcase builder adopts - -- it as the rank's CAP -- one more band of height and the art the - -- top face wears -- so those units come out 32px too. - bookcase = { 10, 11, 26, 27, 40, 41, 59, 69, 70, 74, 75, 85, 86, - 90, 91 }, + -- ranks. + -- + -- 64/65/66 -- the TOP-TRIM row (one black line, one white + -- highlight, then grey) that every flat-topped unit in this + -- tileset wears -- are here too, and this is the load-bearing + -- decision in the entry. They used to be `table`. A `table` is + -- an authored upright BOX, and the support rule stands a pinned + -- standee drawn directly above an authored box ON it: every chair + -- on the FAR side of a table in this tileset is drawn directly + -- above that table's trim row, so the trade room's two north + -- chairs and the meeting room's spare chair were lifted onto the + -- tabletops -- and their cells were re-tiled as tabletop, marching + -- each table two tile rows north to meet them. A `bookcase` is + -- authored but is NOT a box (its art mode is the rank collapse), + -- so the support rule does not fire and those three chairs stand + -- on the floor where they are drawn. + -- Nothing else about the trim changes for the worse. Where it + -- caps a shelf rank (64/66 over 40/41 or 10/11, in the metronome + -- room, the meeting room, the fossil room and the secret house) + -- it simply becomes the rank's fourth row: the same 32px height + -- and the same top-face art the cap-adoption rule used to hand it, + -- minus the 12px stub the trim used to render as BEHIND the shelf. + -- Where it tops a table or the Warden's bench it becomes that + -- unit's own 8px back rim, tucked behind the 12px top. + -- (The one thing it costs is named in the report: the meeting + -- room's two partitions used to hand this trim to the 16px band's + -- TOP face -- the mesher gives a full-height course the authored + -- UPRIGHT row above it -- and now top with their own band art.) + bookcase = { 10, 11, 26, 27, 40, 41, 59, 64, 65, 66, 69, 70, + 74, 75, 85, 86, 90, 91 }, -- the lab furniture, at table height. The benches in the fossil -- and metronome rooms (2/3 the specimen tray, 4/5 + 20/21 the -- microscope, 18/19 the tray's rack row) and the big lab tables - -- (64/65/66 top edge, 80/81/82 body, 83/58/84 the front rail and - -- its legs). 72/73 -- the monitor and the ball lying on the - -- table -- ride the table's authored height rather than standing - -- as separate cutouts, exactly as Oak's starter balls do; pinning - -- them also stops the display frame's black corner brackets from + -- (80/81/82 the top, 83/58/84 the front rail and its legs). + -- 72/73 -- the monitor and the ball lying on the table -- ride the + -- table's authored height rather than standing as separate + -- cutouts, exactly as Oak's starter balls do; pinning them also + -- stops the display frame's black corner brackets from -- auto-extracting into standing prisms. Four of these ids -- ($04/$05/$14/$15) plus $48 are the tileset's water-fallback -- traps: unpinned they sink into a pond lip in the middle of the - -- room. 65 doubles as the meeting room's long partition top, - -- which therefore reads as a 12px shelf behind its 16px base - -- band instead of joining the 48px tower it used to make. - table = { 2, 3, 4, 5, 18, 19, 20, 21, 58, 64, 65, 66, 72, 73, + -- room. With the trim row moved to `bookcase` the top face of + -- each table now wears its own drawn surface instead of the trim + -- -- which is how the Warden's bench finally shows the monitor and + -- the Pokeball the artist drew on it. + table = { 2, 3, 4, 5, 18, 19, 20, 21, 58, 72, 73, 80, 81, 82, 83, 84 }, -- seats, the 8px standee pool, all of them in WALKABLE cells and -- therefore flat floor until pinned: the four chairs round the @@ -1053,6 +1293,9 @@ return { -- carry the bench's apron along their top edge -- the standee -- wears a thin dark cap for it, which is cheaper than dropping -- the stool to a half-cell drawing with no floor margin. + -- The chairs on the far side of a table stay on the FLOOR only + -- because the trim row above them is no longer a box; see the + -- bookcase group. stool = { 6, 7, 14, 15, 22, 23, 30, 31 }, -- the tall corner plants: two cells of drawing (44/45 + 60/61 the -- frond crown, 46/47 + 62/63 the stem and pot), mostly silhouette, @@ -1208,37 +1451,64 @@ return { -- footing. bookcase = { 7, 8, 9, 10, 23, 24, 25, 26, 39, 40, 41, 42, 55, 56, 57, 58 }, - -- the big octagonal table of the Fan Club and Silph's boardroom, - -- half a cell high -- and half a cell ON PURPOSE. A `counter` is - -- ONE band: exactly the drawing's bottom row (79/63/64/81/82, the - -- table's front bevel) stands up as the front, and every row above - -- it rides the top face IN DRAWN ORDER. For a table drawn in plan - -- that is the only class that reproduces the plan: the octagon's - -- bevelled corners (72/73 north, 77/78 the flanks, 63/81 and - -- 79/82 south) land where they are drawn instead of being smeared - -- by a taller box, which wears its NORTH row's art across the - -- whole top face. - -- The chairman's seat is drawn INTO the table -- 17/18 the sitter, - -- 33/34 the chair back, 91/92 the seat -- across three tile rows, - -- and this is the Pokemon Center couch problem exactly: folding - -- two rows upright makes the box two tiles deep and a folded box - -- repeats its north row across the whole top, so every upright - -- arrangement draws the sitter's head two or three times. Riding - -- the counter's top face draws him once, in the right place, at - -- the right depth. Standing him as a cutout is not an option - -- either: the drawing has no floor margin -- it is surrounded on - -- all four sides by the tabletop's own mid grey -- so the mask - -- would drain from every edge. - -- 91/92 double as the wall band's base blocks where a side wall - -- meets the south course in Silph; those six tiles therefore sit - -- 8px instead of 16px. It is a notch at the foot of a wall the - -- player never faces, and the alternative is a 16px stub standing - -- in the middle of the boardroom table. + -- The big OCTAGONAL boardroom table -- the Fan Club's and Silph + -- 11F's, the same drawing in both -- half a cell high, and half a + -- cell on purpose. A `counter` is ONE band: exactly the drawing's + -- bottom row stands up as the front and every row above it rides + -- the top face IN DRAWN ORDER. For a table drawn in PLAN that is + -- the only class that reproduces the plan; a taller box wears its + -- NORTH row's art across the whole top face and smears it. + -- + -- The octagon itself is cut at TILE granularity, which is the + -- finest the mesher has: an authored tile is its own 8x8 column, + -- so the bevel is drawn by choosing which tiles rise. Reading the + -- Fan Club rows (Silph is the identical shape eight rows further + -- down the map): + -- row 4 72 74 74 74 74 73 6 tiles wide + -- rows 5-8 72 <----- 8 tiles -----> 73 8 tiles wide + -- row 9 63 64 64 64 64 81 6 tiles wide + -- row 10 75 76 76 75 4 tiles wide + -- 72/73 are the north chamfer, 63/81 the south one, 77/78 the + -- straight flanks, 74 the north rim and 64 the top itself. 75/76 + -- -- the front apron with its two drawn legs -- is pinned so the + -- octagon's south edge closes and the legs stand on the front face + -- instead of lying painted on the floor; it sits in the walkable + -- cell in front of the table, which is the cell a player stands in + -- to talk across it, and an 8px apron there reads as standing AT + -- the table (the same arrangement as the Center's counter). + -- 79 and 82, the OUTER corner of the south bevel, are `ground` -- + -- see the ground group. + -- + -- 91/92, the statue's pedestal, ride the top face here rather than + -- standing with the statue: those two ids are ALSO the wall band's + -- base blocks where a side wall meets the south course in Silph, + -- so six tiles at the foot of Silph's walls sit 8px instead of + -- 16px. It is a notch at the foot of a wall the player never + -- faces, and the alternatives are worse in both directions -- a + -- 16px stub standing in the middle of the boardroom table, or a + -- paper cutout standing at the foot of every south wall. -- 40 and 48 are water-fallback traps: unpinned, the table's whole -- north-west cell was a pond, and it dragged the floor tile $1F -- sharing that cell down with it. - counter = { 17, 18, 33, 34, 63, 64, 72, 73, 74, 77, 78, 79, - 81, 82, 91, 92 }, + counter = { 63, 64, 72, 73, 74, 75, 76, 77, 78, 81, 91, 92 }, + -- The Pokemon statue standing in the middle of both boardroom + -- tables: a per-pixel cutout, ONE voxel deep, standing ON the + -- table through the authored-box support rule -- a standee drawn + -- directly above an authored box rises from that box's top, which + -- is how the gym's bird statues stand on their plinths and the + -- potted plant stands on Red's table. 17/18 is the crown and the + -- head, 33/34 the face and the top rim of the pedestal it stands + -- in; the pedestal proper (91/92) stays `counter` under its feet. + -- The four claimed tiles keep rendering as the tabletop they were + -- cut out of, so the table is whole underneath. + -- `cutout` and not `prop`: the drawing has NO floor margin -- it + -- is surrounded on all four sides by the tabletop's own mid grey + -- -- and the cutout pool's stricter contract makes mid shades + -- background unconditionally, so the tabletop drains away cleanly + -- while the black outline, the paint whites and everything they + -- enclose survive. Pinned `counter` with the rest of the table + -- (what it was) the statue was painted FLAT on the tabletop. + cutout = { 17, 18, 33, 34 }, -- Bill's desk and the Silph president's, at table height: 11-14 -- the surface with the terminal and the ball on it, 27-30 the -- front edge. Their aprons (43/44 + 91/92 on the row below) are @@ -1260,13 +1530,20 @@ return { -- the pond with it. A pin is the only rule that outranks it. -- 1/2 are the curve of Bill's drums where they meet the floor, -- drawn below the pinned barrel and flat where they lie. - ground = { 1, 2, 31 }, + -- 79/82 are the OUTER corner of the octagon's south bevel, one + -- tile beyond 63/81 on each side. Raised with the rest of the + -- table they squared both bottom corners off and left an 8px stub + -- standing in the floor a tile away from the table on each side; + -- flat they are what they are drawn as -- the table's cast shadow + -- where the bevel meets the floor -- and the octagon closes with a + -- clean 45 degree step on all four corners. + ground = { 1, 2, 31, 79, 82 }, -- Deliberately NOT pinned: the doormats (70/71) and Silph's warp -- pads, stairwells and elevator mouth (3/4, 5/6/21/22, 83-86) are -- walkable and already flat; 47 is the black surround outside the -- building's footprint and the void rule authors it; 69 is the - -- shadow course under Bill's drums and 75/76 the octagon's front - -- apron, both drawn into walkable cells and both correct flat. + -- shadow course under Bill's drums, drawn into a walkable cell and + -- correct flat. }, -- The Cable Club rooms and the Bike Shop (one tileset, three maps). @@ -1708,14 +1985,25 @@ return { -- One 16px course: the chamber's wall ring ($09/$0A over -- $19/$1A), the grey mass beyond it ($11) so the room reads as a -- chamber cut into solid stock rather than a fence standing on a - -- plain, the roped-off corner of 1F ($02 over $12 running - -- east-west, seen face-on; $1D/$1E running north-south, seen from - -- above -- 8 rows of it, which the volume path would have towered - -- to 64px), and Agatha's north band ($20 over $30). + -- plain, and Agatha's north band ($20 over $30). wall = { 9, 10, 25, 26, 17, - 2, 18, 29, 30, 32, 48 }, + -- The reception counter of POKEMON_TOWER_1F -- the only counter in + -- the group, and the ROM's own counterTiles for this tileset name + -- $12 as exactly that. It is an L that fences the receptionist + -- into the south-east corner: the east-west arm drawn face-on + -- ($02 the surface over $12 the front panel, eight cells of it) + -- meeting the north-south arm drawn from above ($1D/$1E, eight + -- tile rows, which the volume path would have towered to 64px). + -- The first pass put all four in `wall` and the barrier came out + -- a full-height partition with the receptionist hidden behind it + -- to the shoulders. Half a cell is what a counter is: she leans + -- on it, the drawn surface ($02) rides the top face and the drawn + -- panel ($12) folds up the front, and both arms come out the same + -- height so the corner turns cleanly instead of stepping. The + -- cells stay blocked either way -- pins are look-only. + counter = { 2, 18, 29, 30 }, -- The gravestones ($05/$06 over $15/$16): a headstone drawn -- face-on, one per cell, 314 of them across the seven floors and -- Agatha's room. The volume path measured the drawing as its @@ -2443,6 +2731,72 @@ return { roofRows = 64, roofBack = 8, roofFront = 8, roofCycle = { 8, 31 }, slab = 4, frontEave = 4, ledge = nil, seal = "s", }, + -- assets/docs/buildings/B30: the POKEMON TOWER, and the one drawing + -- in the catalogue the map edge cuts off. It stands at + -- LAVENDER_TOWN (12,0), tile row 0, so the four rows of roof band + -- every other block of this family wears are simply not on the map: + -- rows 0..55 are three window courses over brick, 56..62 the base + -- course, and that is the whole drawing. + -- + -- REMAINING.md files it unvoxelizable on two counts. The first is + -- real and `seal` answers it; the second is a misreading of it. + -- + -- Unbounded silhouette. Every sibling is closed on the north by + -- the black outline along the top of its roof band. Here the top + -- row is the 1px light margin over the first window course, so the + -- flood seeds the whole of y=0, drops down the 2px light channel + -- between the side outline (x=5, x=90) and the window band (x=8), + -- and from the first brick course eats the wall out: 37% fill in + -- 126 pieces. North is the ONLY way in -- the base course seals + -- the south, the side outlines seal east and west -- so `seal = + -- "n"` alone restores it: 88% fill, ONE piece, largest 100%, in + -- the shipped band. The 728px left outside are exactly the 5px + -- grass margin down each flank plus the terrain row under the base + -- course, the same margins every sibling keeps. (REMAINING.md + -- tests "s" and "nswe" and concludes the box cannot be sealed + -- because a quarter of it is ground -- but that quarter is the + -- unsealed flood's own damage measured back: its 1552px is + -- precisely what the flood ate. North alone is never tried.) + -- + -- No roof drawn. True, and not fixable: `tiles` is the matcher as + -- well as the art, so the roof band cannot be borrowed from B14 + -- without asking the map for tiles at ty=-4. One row of band is + -- the least the pipeline can be given, and roofRows = 1 spends it + -- on that light top margin alone -- no window course is eaten, the + -- whole 63px of drawn facade stays standing, and the 4px slab caps + -- it flat. 67 voxels: nearly twice the Center and the Mart beside + -- it, and one voxel under its own twin B14 -- which is right, + -- because B14's facade IS this drawing. + -- + -- The last row carries a THIRTEENTH tile id. `matches` walks each + -- row to its own length while `read` composites only the first + -- #tiles[1] columns, so that id constrains where the template may + -- land without entering the drawing -- and it must not enter it, + -- because the only tile beside the tower is the $11 filler, whose + -- hatch carries isolated dark pixels the flood cannot reach: drawn, + -- each becomes a 64-deep rod of speckle hanging beside the tower. + -- The constraint is needed because these 8 rows ARE rows 5..12 of + -- flat_block_6x6: the bare grid matches four times, the tower plus + -- the lower two thirds of B14 at CELADON_CITY (14,4), ROUTE_16 + -- (18,6) and ROUTE_18 (34,4), and nothing stops a second model being + -- stamped inside the first. $11 sits east of the tower's south-east + -- corner; the three B14 plots carry grass, path and a plant there + -- ($23, $30, $55), so with the extra id the grid resolves once. + { + id = "pokemon_tower", + tiles = { + { 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 31 }, + { 15, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 31 }, + { 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 31 }, + { 15, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 31 }, + { 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 31 }, + { 15, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 31 }, + { 15, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 31 }, + { 78, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 79, 17 }, + }, + roofRows = 1, roofBack = 1, roofFront = 0, roofCycle = { 0, 0 }, + slab = 4, frontEave = 4, ledge = nil, seal = "n", + }, }, FOREST = { @@ -2463,25 +2817,145 @@ return { }, }, + -- The approach to the Pokemon League: two maps, INDIGO_PLATEAU (the + -- forecourt the League itself stands on) and ROUTE_23 (the long + -- climb up to it, with its badge-check gates). Everything this + -- blockset draws is one piece of architecture -- striated rock + -- walls, white pillars, and the bird STATUES that line both the + -- avenue and the plaza. 29 of its 73 blocks are never placed, so + -- every tile named below really is one of these two maps'. + -- + -- A statue is built exactly like the badge gyms' (see GYM above): + -- one cell of FIGURE ($10/$12 over $28/$29) standing on one cell of + -- PLINTH ($15/$16 the cap over $30/$31 the plaque). 47 of them -- + -- 12 on INDIGO_PLATEAU, six a side down the avenue, and 35 more + -- across ROUTE_23's plaza (blocks $42/$43; the $25/$26 twins that + -- stand the same statue on grass are never placed). + -- + -- What the detector made of them is the bug this entry exists for. + -- On the avenue the statues stack with NO gap: the plinth's plaque + -- row is drawn directly above the next figure's head, so the + -- flood-fill joined all six of a column into ONE region 24 tile rows + -- tall, and the volume builder's repeat scan read 32px down one half + -- of the drawing and 24px down the other. Each row came out as a + -- continuous stepped RIDGE of boxes wearing the statue art folded + -- onto its south face -- probed INDIGO_PLATEAU tiles (16,12)-(17,35) + -- at 32/24 and (22,12)-(23,35) mirrored. ROUTE_23's plaza statues + -- stand alone and fared no better: 24px boxes with the figure's top + -- row skipped outright. + -- + -- Pinned the gyms' way the ridge becomes statues. The plinth is a + -- SOLID 16px `wall` block; the figure is a per-pixel cutout 5 voxels + -- deep (the thin `prop` pool) that rides the plinth's top face + -- through the authored-box support rule and collapses to the + -- plinth's SINGLE cell of footprint -- Structures' wall-support case, + -- so the base never marches backwards. + -- + -- The one thing the gyms did not have to deal with: $28/$29 is + -- SHARED. The same bird is drawn again at the foot of every white + -- pillar, framed there by the pillar's black edge ($25/$26 over that + -- same $28/$29, blocks $18/$1B) -- 80 of them, 76 down ROUTE_23 and + -- four on INDIGO_PLATEAU (its two outer corners and the pair + -- flanking the League's recess). So $25/$26 joins the same pool: + -- pinning half a cell would have stood a half-height bird under a + -- wall. That in turn is why the pillar itself is pinned -- see the + -- last paragraph of `wall`. PLATEAU = { - -- assets/docs/buildings/B23: the Victory Road entrance on - -- Route 23: a long rock face with two barred doors cut into - -- it. Not a house -- what the roof band reads is the pale top - -- of the cliff seen from above, and the facade is the - -- striated rock below it. - { - id = "victory_road_gate", - tiles = { - { 37, 38, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 37, 38 }, - { 40, 41, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 40, 41 }, - { 21, 22, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 46, 47, 3, 3, 3, 3, 3, 3, 3, 3, 46, 47, 15, 15, 15, 15, 15, 15, 21, 22 }, - { 5, 6, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 46, 47, 3, 3, 3, 3, 3, 3, 3, 3, 46, 47, 15, 15, 15, 15, 15, 15, 5, 6 }, - { 5, 6, 15, 15, 15, 15, 15, 15, 11, 12, 15, 15, 15, 15, 15, 15, 46, 47, 3, 3, 3, 3, 3, 3, 3, 3, 46, 47, 11, 12, 15, 15, 15, 15, 5, 6 }, - { 21, 22, 14, 14, 14, 14, 14, 14, 27, 28, 14, 14, 14, 14, 14, 14, 46, 47, 3, 3, 3, 3, 3, 3, 3, 3, 46, 47, 27, 28, 14, 14, 14, 14, 21, 22 }, - }, - roofRows = 16, roofBack = 7, roofFront = 8, roofCycle = { 9, 12 }, - slab = 4, frontEave = 4, ledge = nil, - }, + -- ONE 16px course for every piece of masonry here. + -- + -- $03 is the striated rock face, 2436 placements and the bulk of + -- both maps. It already read 16 nearly everywhere, but in the + -- columns of INDIGO_PLATEAU's rim that stand over a corner pillar + -- the repeat scan came out 24 -- a stagger in the plateau's + -- skyline (probed `03w24` at tiles (8,0)-(9,2), (12,0)-(13,2) and + -- their two mirrors). Authored, the rim is one course. + -- + -- $0D/$0F/$0E are the League's outer wall -- top band, face and + -- base. The same three tiles draw the Pokemon League's own + -- facade, the long walls flanking the avenue, and every + -- badge-check gate down ROUTE_23. + -- + -- $15/$16 + $05/$06 + $30/$31 are the pilaster: cap, shaft, and + -- the plaque base. $15/$16 over $30/$31 IS the statue's plinth -- + -- the artist drew the same stone twice -- which is why one pin + -- serves the gate corners and the statues alike. + -- + -- $2E/$2F the white pillar shaft and $20/$21 its cap change no + -- HEIGHT: they derive 16 already. What the pin buys is + -- `authored`, which is exactly what the prop support rule tests. + -- Without it a pillar-foot bird finds no support, drops to ground + -- level, and leaves a hole punched clean through the pillar + -- (probed, shot, then fixed). + wall = { 3, + 13, 14, 15, + 21, 22, 48, 49, 5, 6, + 32, 33, 46, 47 }, + -- The statues, and the same bird at the pillar feet. Black-outline + -- segmented: the outline and everything it encloses stay, the sky + -- and the paving around them flood away. + prop = { 16, 18, 40, 41, 37, 38 }, + -- Round drawings, one voxel ball per 16x16 cell -- the treatment + -- the overworld's canopies and Celadon's hedge take. + -- + -- $07/$08 over $17/$18: the seven canopies planted on pillar tops + -- along ROUTE_23 (blocks $44/$45; the $0F block that tiles four of + -- them together is never placed). Boxed, the canopy art smeared + -- down the whole pillar column beneath it. + -- + -- $2A/$2B over $22/$1D: the boulders strewn across ROUTE_23's + -- middle terrace (blocks $02/$13/$16), one per cell and NOT + -- walkable. As boxes they sat flat enough to look painted onto + -- the path; as balls they read as the obstacles they are. + cylinder = { 7, 8, 23, 24, + 29, 34, 42, 43 }, + -- The Route 23 sign, one cell, block $48's only placement. + -- Unpinned it probed `09w00 0Aw00 / 19w08 1Aw08` -- an 8px stub + -- with its board skipped. Same thin plate on a stick every other + -- outdoor sign gets. + signpost = { 9, 10, 25, 26 }, + -- The Route 22 gate's roof, drawn from ABOVE and filling + -- ROUTE_23's last two block rows ($3D the tiling, $3E/$44 its + -- edges, $40/$41 the corners). Art on the TOP face -- the way + -- SHIP_PORT's hull is pinned -- rather than folded up a 16px kerb. + -- It stands past the map's last walkable row (you warp to + -- ROUTE_22_GATE before you reach it), so this is a tidy-up rather + -- than a fix. + roof = { 61, 62, 64, 65, 68 }, + -- The ground painted where a pinned figure's cell used to be: + -- $23, the pale paving both maps are floored with. It is also the + -- white the pillars are drawn in, so the cell a pillar-foot bird + -- vacates reads as more pillar -- where the neighbour vote left a + -- BLACK hole, having nothing to elect (all four neighbours of that + -- cell are wall). On the avenue and the plaza the statues' own + -- cells simply keep the paving they stand on. + prop_ground = { [16] = 35, [18] = 35, [40] = 35, [41] = 35, + [37] = 35, [38] = 35 }, + -- Deliberately NOT pinned: + -- + -- $14, the water -- 1446 placements, the pond on ROUTE_23's middle + -- terrace -- and its bank shading $32/$33/$1F. $14 and $32 are + -- two of the three stale-cache water ids the trap is named for, + -- but here they are honestly water: TILEANIM_WATER animates $14, + -- the pond is real, and $32/$33/$1F are drawn in the TOP half of + -- water cells as the waterline itself, so the cell rule dropping + -- them to -2 is what the art means. Left to fall through to the + -- engine's water set. ($48, the third trap id, is not in this + -- atlas at all -- it stops at $45.) + -- + -- $0B/$0C over $1B/$1C, the barred doors: the Pokemon League's two + -- and Victory Road's two. They are the tileset's own doorTiles, + -- so the door fold already stands them upright in the facade; + -- probed 16px identically before and after this entry. + -- + -- $23/$2C/$2D are in the walkable list outright, and $45 is the + -- tileset's grassTile, which derives its own standing-tuft pin. + -- + -- The Victory Road entrance is a `buildings` template + -- (victory_road_gate, at the bottom of this file): 36x6 tiles at + -- ROUTE_23 (0,58), and its ends are built out of $25/$26/$28/$29 + -- and $15/$16/$05/$06. Buildings claim their tiles before any of + -- the above can reach them -- probed `b` class over all 216 of + -- them, unchanged by this entry. }, }, } diff --git a/lib/Structures.lua b/lib/Structures.lua index 6ff85f7..3baf693 100644 --- a/lib/Structures.lua +++ b/lib/Structures.lua @@ -1919,10 +1919,24 @@ function Structures.buildObject(S, map, region, cluster, -- the box's top with its feet on the box's north row, and the claimed -- tiles keep rendering as that box (wearing its plain art) instead of -- punching a floor-level hole through it. + -- + -- Only when the prop's OWN CELL IS BLOCKED, though. "Is something + -- drawn above me?" is not the same question as "am I standing on it": + -- a chair drawn against the north side of a table is above the table's + -- trim row too, and it was being lifted onto the tabletop -- three + -- chairs standing on the furniture in Cinnabar's trade room and + -- Fuchsia's meeting room, with the claimed cells re-tiled as tabletop + -- so the table marched two rows north with them. The world already + -- knows which is which: a thing that sits ON furniture occupies a + -- blocked cell (you cannot walk through the gym statue, Red's plant, + -- the PC), while a seat you walk up to is in a walkable one. local baseY, support = 0, nil if force then local bs = S.shapeAt[keyOf(cluster.minX, cluster.maxY + 1)] - if bs and bs.authored and bs.art == "upright" and (bs.h or 0) > 0 then + local blocked = not map:isWalkableCell(math.floor(cluster.minX / 2), + math.floor(cluster.maxY / 2)) + if blocked and bs and bs.authored and bs.art == "upright" + and (bs.h or 0) > 0 then baseY, support = bs.h, bs end end diff --git a/lib/TileShape.lua b/lib/TileShape.lua index c9b15fa..1e11c59 100644 --- a/lib/TileShape.lua +++ b/lib/TileShape.lua @@ -202,6 +202,48 @@ local function authoredGroups(tilesetId, heights) return out end +-- Conditional pins: tile id -> list of { above = {tile ids}, class }. +-- +-- A pin is per TILE ID, and one graphic can mean two things. The route +-- gates' $32/$33 is the case that forced this: the artist reuses it for +-- the wall's dark base course AND for every service counter's front, and +-- it is the bottom row of its cell either way. Pinned `wall` the counter +-- stands a full 16px; pinned `counter` the wall bank corrugates 16/8 for +-- sixteen rows. Neither is right, and no per-tile pin can be, because +-- forMap resolves an id to ONE shape. +-- +-- What separates the two uses is what is drawn ABOVE: the wall's upper +-- course over a wall base, the counter's top over a counter front. So a +-- profile entry may carry `when_above = { [tile] = { { above = {...}, +-- class = "..." } } }`, evaluated per POSITION in TileShape.at, where +-- the map and coordinates are in hand. First match wins; no match keeps +-- the tile's ordinary pin. +local function authoredConditions(tilesetId, heights) + local s = load() + local entry = s and s.tilesets and s.tilesets[tilesetId] + local spec = entry and entry.when_above + if type(spec) ~= "table" then return nil end + local out, any = {}, false + for tile, rules in pairs(spec) do + if type(tile) == "number" and type(rules) == "table" then + local list = {} + for _, rule in ipairs(rules) do + if type(rule) == "table" and heights[rule.class] + and type(rule.above) == "table" then + local set = {} + for _, t in ipairs(rule.above) do set[t] = true end + list[#list + 1] = { above = set, class = rule.class } + end + end + if #list > 0 then + out[tile] = list + any = true + end + end + end + return any and out or nil +end + local function shapeFor(class, heights, authored) return { class = class, h = heights[class] or 0, art = ART[class] or "upright", @@ -251,7 +293,7 @@ function TileShape.forMap(map) end end - local shapes = { classes = {} } + local shapes = { classes = {}, cond = authoredConditions(id, heights) } for class in pairs(FALLBACK_HEIGHTS) do shapes.classes[class] = shapeFor(class, heights) end @@ -284,6 +326,19 @@ end -- map:tileAt(tx, ty), passed in because every caller already has it. function TileShape.at(map, shapes, tile, tx, ty) local s = shapes[tile] + -- conditional pins first: they are authored answers that need the + -- POSITION to resolve, so they outrank both the flat pin on the same + -- tile and the cell rules below (see authoredConditions) + local rules = shapes.cond and shapes.cond[tile] + if rules then + local above = map:tileAt(tx, ty - 1) + for _, rule in ipairs(rules) do + if above and rule.above[above] then + shapes.classes[rule.class].authored = true + return shapes.classes[rule.class] + end + end + end if not s or s.authored then return s end local cx = math.floor(tx / 2) local cy = math.floor(ty / 2) diff --git a/main.lua b/main.lua index a1f2b8c..fd9a5a7 100644 --- a/main.lua +++ b/main.lua @@ -376,7 +376,7 @@ mod.events:on("map.reloaded", function(payload) if mapId then ChunkMesher.invalidate(mapId) end end) -mod.exports.version = "1.0.5" +mod.exports.version = "1.0.6" -- exposed so a companion mod can pin its own tiles' shapes or read the -- camera without reaching into this mod's file layout mod.exports.lib = V diff --git a/manifest.json b/manifest.json index 9fa4490..89de570 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "DRAMATIC_SHAPE", "name": "Dramatic Shape Voxel Mod", - "version": "1.0.5", + "version": "1.0.6", "api": 2, "entry": "main.lua", "profile": "content",