mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 11:30:51 +02:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
+687
-213
File diff suppressed because it is too large
Load Diff
+15
-1
@@ -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
|
||||
|
||||
+56
-1
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user