bump version

This commit is contained in:
DramaticShape
2026-08-02 00:14:13 -04:00
parent a7c9541ac4
commit 1a283d6771
19 changed files with 1486 additions and 2 deletions
+202
View File
@@ -1,5 +1,207 @@
# Changelog
## 1.4.3
### Added
- **The furniture of the whole game goes through the building
pipeline.** 1.4.1 put four drawings through it; this is the rest of
the rooms. Every one of them is the same read -- the drawing's own
bands say what is a top seen from above, what is a face seen head-on,
and where the thing ends on the floor -- and every one of them
replaces a pinned box that wore its drawing as a decal. The pins all
stay as the degradation path, neutralized wherever a template stamps.
- **The bookcase, the commonest piece of furniture in the game** --
58 placements across two drawings on the town-house atlas (books
and a bowl on each shelf at the west end of eighteen homes, books
on both at the east), plus Red's and the Copycat's pair. Pinned
`desk` it was a 24px box with the books painted on its flat front.
Modelled it is 23 voxels of cabinet with its top seen from above,
and every book, bowl and door panel sunk a voxel behind the frame
the drawing seals it in.
- **Celadon's display cabinets** -- the tall one with the trophy
behind its glass and the short one beside it, band for band the
same object as the town house's on another atlas, which is what
makes the pair read as one line of furniture: 23 voxels and 15,
exactly the 8 rows of drawing between them.
- **The dining table, everywhere it is drawn** -- the generic town
house's at 18 placements, Red's and the Copycat's, and the chief's
long table at four cells wide. All of them the lab table's read at
a different width, all of them 6 voxels, all of them standing on
the ground line their legs are drawn stopping at rather than on
the grid's floor.
- **The stool at every one of those tables** -- 94 placements on the
house atlas alone, ten more in Red's and the Copycat's, and the Fan
Club's four members' chairs, a different drawing that is
pixel-identical from the seat down. The first template with no base
piece at all: a stool is drawn mid-cell over its own floor, so it
is a desk-set of exactly one part, seat lid over legs with the
floor showing between them.
- **The Pokemon Center's healing machine** -- two variants, 24
placements, plus the Indigo Plateau lobby's pair. A wall-height
cabinet with its monitor perched on the front of its top face,
drawn across two map rows because it towers over the 16px band
behind it, which the volume path could only read as more wall. The
hoses leaving its side are modelled as hoses, at the elevation and
the depth the two stacked motifs put them; the west machine's
keyboard is a shelf at counter height wearing its own top-view art.
- **Bill's desk, and the Silph president's** -- the same drawing in
both rooms. Its terminal is drawn in 2:1 isometric, turned 45
degrees to the map, and builds as a cube rather than the slab a 2:1
reading gives; the kinked dark run between keyboard and computer is
raised to the keyboard's height and reads as the cable it is. The
desk stops at its own two cells because the artist drew its apron
into the walkable cell in front, sharing tiles with the chair
pushed up to it -- so the chair is modelled as a part of the desk.
- **The Bike Shop's open toolbox.** The drawing looks down INTO the
tray, which is why every solid treatment failed it -- as a
`billboard` the whole cell went up as one 10-voxel slab wearing the
drawing as a decal. `tray` builds four walls, a floor and air
between them, with the lid standing open on its hinge.
What the template language grew to carry them: `tray`; a `desk` band
that lays its top face flat as a lid; the `box`, `flat` and `iso`
part kinds; `stretch` for a band mapped over a deeper plot than it
was drawn on; `inset` for a pane sunk by hand; `panes = false` where
the global recess pass has the polarity backwards; a `wall` element
so a template can keep the band behind it solid; `plane` for a height
the drawing states elsewhere; and `scrub`/`keep`/`support`, which let
a template model a surface while leaving an object standing on it to
its own standee -- Red's potted plant on the dining table.
- **Round bins: the `can` class.** Vermilion Gym's switch puzzle stands
fifteen galvanised trash cans in a row, and the S.S. Anne redraws the
same object pixel for pixel as its galley barrels. Left to the thin
standee pool they were flat discs on edge -- fifteen coins standing
in a row; pinned a plain `cylinder` the drawing's base arc revolves
too and they came out as barrels balanced on a three-voxel stem.
`can` is the round hull cut at both ends, hollowed and tapered: the
drawn mouth ellipse projects across the top and down the well so you
look into the bin, the drawn base ellipse is ground contact rather
than body, and the plan narrows toward the floor. The two ellipses
are measured off the pixels; the height, the well and the taper are
authored, and the entry says why.
- **The rock gyms' boulders are round.** 87 placements over Pewter's
walls and maze and Bruno's clusters, and every one of them was a
square bar wearing a boulder texture in relief -- the repeat-aware
scenery path extruding the whole drawing as one course. Each cell is
now a hull whose plan is its own drawn width profile turned in depth:
a dome full-width from the drawn shoulder down, tapering over the top
five rows exactly where the art tapers, with the floor's corner
diamonds opening between them the way the drawing has them. Still
16px, so nothing standing on or beside a rock moves.
- **The potted plant stands as a plant.** The most repeated interior
prop in the game -- 78 placements over 13 maps, six per Pokemon
Center -- and its urn was rendering as a hollow black frame, because
the drawing's foot lies flush on the block's bottom edge and the
background vote took the plant's own darks away with the floor. Named
outright as light and white instead, it stands as one organic
silhouette 32px tall over its two stacked cells, crown overhanging
the stem. `planter` carries the same reading for a round drawing
stacked two cells high on one cell of plot.
- **Bicycles, in both places the Bike Shop draws them.** The six on the
showroom floor get their own pool at two voxels rather than the thin
pool's five: a bike is a line drawing, and at five voxels every
stroke closes the gap to its neighbour with its own side faces, so
from any angle but dead-on the air inside the frames filled in and
the six came out as one dark lump. And the two against the north wall
get `mounted`, a new authored-mask escape for a thing drawn INTO a
wall band: it holds the wall's plane as a thin per-pixel slab instead
of standing up as a sprite card, and it keeps its drawn elevation, so
a bicycle hung clear of the floor stays hung. Its mask is measured
rather than hand-drawn -- the plain panel tile composited across the
same grid and the background flooded in through the pixels that still
match it, which separates bicycle from stripe exactly.
- **The Marts' cash register is a machine, not a decal.** An authored
figure may now state a `depth`, which makes it an object rather than
a person: a per-pixel solid standing on the counter instead of the
flat card that turned edge-on with the camera. And the drawing is not
a box -- its black linework packs two facings, an L of base and arm
around a keypad that is the machine's deck seen from above. `flat`
lays that rect horizontal in the notch of the L, and `thin` gives the
receipt curl a paper's thickness where the body's would have made it
a wedge.
- **Shelf fronts have relief.** Everything the `bookcase` collapse is
used for is a shelf, a rack or a display case, and all of them seal
their contents behind the drawing's own black frame -- so those
regions now sink a voxel, the same rule a facade's window panes are
recessed by, and the books stand in the shelf instead of being
painted on it. A tileset that borrows the collapse for something that
is not a shelf says `bookcase_relief = false`: the League's masonry
and pilasters, whose courses are the wall itself, and Bill's
transporter drums, whose light regions are a lit barrel.
### Changed
- **Class heights now follow the models under them.** A tileset's
`heights` gets stools at 5 and tables at 6 in the houses, Bill's desk
at 8, and cans at 9 -- each of them the drawn elevation the new
template or hull stands at, so whoever sits on a stool sits on the
seat, and whatever object sprite stands on a table lands on the
modelled top rather than three voxels over it or under it.
- The healing machines' two flanks leave the `wall` pin for the thin
standee pool. They are equipment standing beside the console -- a
pair of pipes and a keyboard -- and as wall each was boxed into a
solid 16px half-cell wearing its drawing in relief.
### Fixed
- **Water no longer hides behind water.** The reflective pass writes no
depth -- the depth canvas is detached for the length of it so the
shader can read it -- so nothing put a lake in the buffer and no lake
could occlude another; the sheets were simply painted in mesh order.
Flat water never showed it, one plane, a farther sheet always landing
farther down the screen. The world curve ends that: it 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 -- water and terrain "from the
other side of the map", not reflected but there. The water meshes now
go down flat first, through the ordinary scene shader with depth
writes on, and the reflective pass draws over what survived. The
buffer holds the 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 a frame
that cannot run the pass at all is unchanged, because the flat draw
is the fallback that was already there.
- **Reflections under the world curve.** The bend tips the world away
and the things standing on it do not lean with it -- and a lake is
one of those things. Reflected off the bowl the bend makes, the far
half of a pond was a mirror tilted twenty degrees: it threw the ray
past the vertical, where the sky ramp's own measure swings from one
end to the other across a single column, and hard-edged patches of
the wrong sky stamped into the water; the same tilt sent the
screen-space march grazing along the bank rather than over it, which
is what smeared the dock and the roofs across the harbour. What the
water reflects is now worked out in the flat world, exactly as it
would be with the curve off, and every marched sample is bent on its
way to the screen by the vertex stage's own displacement -- so the
ray is straight where it should be and lands where the geometry did.
The wave columns are read on the flat sheet too: the relief walk is
built on an even slab over a level plane, and in the curved world
that slab is a bowl, which handed back a column a pixel or three off
per fragment -- a patch of noise in the middle of a pond.
- **Merged runs tore open under the curve.** A quad's interior is the
chord of a parabola its neighbours draw the arc of, so a long run
hangs below the short quads butted against it. Nothing bounded a
run's length, and the ones that ran away were those wearing a
constant texel -- a roof's black eave outline, its fascia, its shaded
underside -- because a flat run has no art to break it. At 102px
across a gym the eave tore off the roof and the slot showed the
building's dark interior through it. Runs now stop at the next 8px
lattice line, which is the lattice buildings are stamped on and the
one every other quad in the scene already ends on, so every join is
vertex-for-vertex and the bend carries them together. It costs quads
whether the curve is on or not -- Cerulean's object stream goes from
35.7k to 41.6k -- and that is deliberate: the mesh is cached per map
and built over seconds, so meshing for the curve's sake only when the
curve is on would mean rebuilding every live map on a keypress.
## 1.4.1
### Added
+70
View File
@@ -4198,6 +4198,60 @@ return {
roofRows = 28, roofBack = 24, roofFront = 0, roofCycle = { 2, 23 },
slab = 3, frontEave = 0, ledge = nil,
},
-- F10: the town house's BOOKCASE -- the commonest piece of
-- furniture in the game at 58 placements across three drawings,
-- and the piece this family was really for: pinned `desk` it was a
-- 24px box with a flat front and the books PAINTED on it. Band for
-- band it is the Celadon display cabinet (F05/F06 under MANSION)
-- on a different atlas, and it takes those numbers unchanged: rows
-- 0-8 the top seen from above (black rim, white highlight courses
-- along the north and west, grey field, the front corner shaded at
-- row 8); row 9 the top's own black front edge -- which is what the
-- rim treatment paints, so slab = 1 and that row folds into the
-- roof band instead of extruding under it; 10-31 the front: two
-- shelves in their dark openings over the cupboard's two panelled
-- doors. Every book, bowl and door panel is a non-black region the
-- drawing seals behind its own black frame, so the measured pane
-- pass sinks each one a voxel and the frames stand proud of them --
-- the relief the `bookcase` class now carries too, arrived at the
-- same way.
--
-- Nine drawn top rows over a 32px plot: the rims map 1:1 (the drawn
-- front-corner shading lands one voxel behind the front edge) and
-- the uniform field cycles between. BOTH cell rows of the plot are
-- blocked and both are cabinet drawing, so D is the whole grid --
-- the case is built into the room's north wall. 23 voxels tall,
-- one under the `desk` pin it replaces and exactly the Celadon
-- trophy case's stand; the `desk` pins stay as the degradation
-- path, neutralized wherever this stamps.
--
-- This drawing carries books and a bowl on each shelf: 36
-- placements, the west end of eighteen homes.
{
id = "house_bookcase_bowls",
tiles = {
{ 38, 41 },
{ 14, 15 },
{ 14, 15 },
{ 30, 31 },
},
roofRows = 10, roofBack = 8, roofFront = 2, roofCycle = { 2, 7 },
slab = 1, frontEave = 0, ledge = nil,
},
-- F10 again: its twin at the east end, books on both shelves -- 22
-- placements, and the only one of the two that Cerulean's trashed
-- house also puts at the west end.
{
id = "house_bookcase_books",
tiles = {
{ 38, 41 },
{ 48, 49 },
{ 48, 49 },
{ 30, 31 },
},
roofRows = 10, roofBack = 8, roofFront = 2, roofCycle = { 2, 7 },
slab = 1, frontEave = 0, ledge = nil,
},
-- F09: the stool at every one of those tables (94 placements on
-- this tileset) -- the first template with NO base piece. The
-- drawing is one object, a round seat on legs, drawn MID-CELL over
@@ -4268,6 +4322,22 @@ return {
keep = { 40, 54, 55, 56, 57 },
support = 6,
},
-- F10 again: Red's and the Copycat's bookcase pair, cells (0,0)
-- and (1,0) of both maps (4 placements). The same object as the
-- town house's, one shelf of each of its two drawings -- 48/49
-- books above, 34/35 below -- and the same band numbers; see HOUSE
-- above for the read.
{
id = "reds_bookcase",
tiles = {
{ 38, 41 },
{ 48, 49 },
{ 34, 35 },
{ 50, 51 },
},
roofRows = 10, roofBack = 8, roofFront = 2, roofCycle = { 2, 7 },
slab = 1, frontEave = 0, ledge = nil,
},
-- F09 again: the stools around it (10 placements across Red's and
-- the Copycat's ground floors), pixel for pixel the house
-- tileset's drawing. Same part table; see HOUSE above.
+1 -1
View File
@@ -890,7 +890,7 @@ mod.hooks:wrap("world.tod", function(next, tod, ctx)
return DayNight.tod()
end)
mod.exports.version = "1.4.1"
mod.exports.version = "1.4.3"
-- 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
View File
@@ -1,7 +1,7 @@
{
"id": "DRAMATIC_SHAPE",
"name": "Dramatic Shape Voxel Mod",
"version": "1.4.1",
"version": "1.4.3",
"api": 2,
"entry": "main.lua",
"profile": "content",
+79
View File
@@ -0,0 +1,79 @@
-- Scratch driver: shots of the Bike Shop showroom, for the bicycle
-- voxelization. Two viewpoints -- the north wall (the two bikes drawn
-- INTO the wall band) and the showroom floor (the six standing bikes).
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/bike_shop_shots.lua \
-- SHOT_DIR=.scratchpad/bikes AB_TAG=before lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/bikes")
.. "/" .. (os.getenv("AB_TAG") or "before")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[bike] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
-- cells: wall bikes ride cell row 0 (tile cols 1-3 and 6-8); the six
-- floor bikes stand in cell columns 0 and 2, rows 1-2 and 4-5
local SCENES = {
{ x = 2, y = 2, face = "up", label = "wall" },
{ x = 3, y = 3, face = "up", label = "room" },
{ x = 2, y = 4, face = "left", label = "floor" },
{ x = 3, y = 6, face = "up", label = "wide" },
-- the two toolboxes, cells (6,6) and (7,7)
{ x = 5, y = 6, face = "right", label = "tools" },
{ x = 6, y = 4, face = "down", label = "tools2" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, "BIKE_SHOP", s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[bike] capture missed: " .. path) end
end
end
print(("[bike] %d shots into %s"):format(shots, ROOT))
end
+77
View File
@@ -0,0 +1,77 @@
-- Scratch driver: shots of Bill's desk, for the `bills_desk`
-- voxelization. The desk fills cells (1,4) and (2,4) of Bill's house
-- with its chair in the walkable cell (1,5) below it, so these are the
-- angles you can actually stand at: head-on from the floor two cells
-- south, and from either flank.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/bills_desk_shots.lua \
-- SHOT_DIR=.scratchpad/billsdesk AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/billsdesk")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[bills] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
{ x = 1, y = 6, face = "up", label = "headon" },
{ x = 2, y = 6, face = "up", label = "headon_e" },
{ x = 4, y = 5, face = "left", label = "east" },
{ x = 0, y = 5, face = "right", label = "west" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, "BILLS_HOUSE", s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[bills] capture missed: " .. path) end
end
end
print(("[bills] %d shots into %s"):format(shots, ROOT))
love.event.quit()
end
+81
View File
@@ -0,0 +1,81 @@
-- Scratch driver: shots of the Celadon chief's house, for the display
-- cabinet and long table voxelizations. Three viewpoints -- the
-- cabinet rank along the north wall, the long table in the middle of
-- the room, and a wide shot with both in frame.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/chief_house_shots.lua \
-- SHOT_DIR=.scratchpad/chief AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/chief")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[chief] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
-- the cabinets occupy cells 2..5 of rows 0-1; the long table cells
-- 2..5 of rows 3-4; the player walks rows 2 and 5
local SCENES = {
{ x = 3, y = 2, face = "up", label = "cabinets" },
{ x = 5, y = 2, face = "up", label = "bookcase" },
{ x = 3, y = 5, face = "up", label = "table" },
{ x = 1, y = 5, face = "right", label = "wide" },
-- the same rank on CELADON_MANSION_1F, where it stands against the
-- interior partition and the grids start on an ODD tile row
{ map = "CELADON_MANSION_1F", x = 2, y = 4, face = "up",
label = "mansion1f" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, s.map or "CELADON_CHIEF_HOUSE", s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[chief] capture missed: " .. path) end
end
end
print(("[chief] %d shots into %s"):format(shots, ROOT))
end
+78
View File
@@ -0,0 +1,78 @@
-- Scratch driver: shots of the Pokemon Center healing machines behind
-- the counter, for the center_heal_machine voxelization. The pair
-- stands at cells (1,0):(2,1) and (6,0):(7,1) of every Center; the
-- nurse aisle (row 2) is the row you can actually face them from, and
-- the public floor south of the counter gives the wide view.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/heal_machine_shots.lua \
-- SHOT_DIR=.scratchpad/healshots AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/healmachine")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[heal] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
{ x = 1, y = 2, face = "up", label = "west_head" },
{ x = 2, y = 2, face = "up", label = "west_keyboard" },
{ x = 3, y = 2, face = "left", label = "west_side" },
{ x = 6, y = 2, face = "up", label = "east_head" },
{ x = 3, y = 4, face = "up", label = "wide" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, "VIRIDIAN_POKECENTER", s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[heal] capture missed: " .. path) end
end
end
print(("[heal] %d shots into %s"):format(shots, ROOT))
love.event.quit()
end
+98
View File
@@ -0,0 +1,98 @@
-- Scratch driver: shots of the house dining tables and stools, for the
-- band-table + no-desk-part voxelization. Every generic home places the
-- table at cells (3,3):(4,4) with four stools around it (Blue's house has
-- Daisy seated at hers); Red's and the Copycat's ground floors place the
-- same furniture one cell lower, with the potted plant CUTOUT standing on
-- the tabletop -- the standee the table template must support, not
-- swallow.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/house_furniture_shots.lua \
-- SHOT_DIR=.scratchpad/housefurn AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/housefurn")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[housefurn] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
-- head on: below Blue's table looking north over a stool at it,
-- Daisy seated at the left one
{ map = "BLUES_HOUSE", x = 3, y = 5, face = "up", label = "blues_front" },
-- from the east, table and both east stools in profile
{ map = "BLUES_HOUSE", x = 6, y = 3, face = "left", label = "blues_side" },
-- from the north wall looking south down over the tabletop
{ map = "BLUES_HOUSE", x = 4, y = 2, face = "down", label = "blues_over" },
-- close beside a stool: seat top, legs and the gap between them
{ map = "BLUES_HOUSE", x = 2, y = 5, face = "up", label = "stool_close" },
-- Red's table head on from the south: the plant cutout standing on
-- the modelled tabletop
{ map = "REDS_HOUSE_1F", x = 4, y = 6, face = "up", label = "reds_front" },
-- and from the east along the stool row, plant in profile
{ map = "REDS_HOUSE_1F", x = 6, y = 4, face = "left", label = "reds_side" },
-- the Fan Club's four members' chairs round the boardroom table:
-- from the south of the west pair, both stools stacked in profile
{ map = "POKEMON_FAN_CLUB", x = 1, y = 5, face = "up",
label = "club_west_pair" },
-- across the table from the west, both pairs and the octagon between
{ map = "POKEMON_FAN_CLUB", x = 0, y = 3, face = "right",
label = "club_across" },
-- close on the east pair from the north, looking down over the seats
{ map = "POKEMON_FAN_CLUB", x = 6, y = 2, face = "down",
label = "club_over" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, s.map, s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[housefurn] capture missed: " .. path) end
end
end
print(("[housefurn] %d shots into %s"):format(shots, ROOT))
end
+76
View File
@@ -0,0 +1,76 @@
-- Scratch driver: shots of a Poke Mart's clerk counter, for the cash
-- register voxelization. The register is drawn at cell (1,5) of the 4x4
-- shop layout every Mart shares, in the middle of the counter's east arm,
-- so these are the three angles you can actually stand at: head-on from
-- the aisle, side-on from the east, and over the counter's south arm.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/mart_shots.lua \
-- SHOT_DIR=.scratchpad/register AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/register")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[mart] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
{ x = 1, y = 7, face = "up", label = "aisle" },
{ x = 2, y = 5, face = "left", label = "side" },
{ x = 2, y = 6, face = "left", label = "over" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, "VIRIDIAN_MART", s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[mart] capture missed: " .. path) end
end
end
print(("[mart] %d shots into %s"):format(shots, ROOT))
love.event.quit()
end
+91
View File
@@ -0,0 +1,91 @@
-- Scratch driver: shots of the potted plants, for the plant standee
-- voxelization. Every Center places three side-by-side pairs on its
-- bottom row -- crowns at cells (0,6)/(1,6), (6,6)/(7,6), (12,6)/(13,6),
-- pots below at y=7 -- and INDIGO_PLATEAU_LOBBY (the MART tileset id,
-- same atlas) lines four of them along its hall at cells (12,10)..(15,10).
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/potted_plant_shots.lua \
-- SHOT_DIR=.scratchpad/plants AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/plants")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[plant] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
-- east of the left pair, looking west along the bottom row: both
-- plants in profile, crown overhang and pot silhouette side-on
{ map = "VIRIDIAN_POKECENTER", x = 3, y = 6, face = "left",
label = "pair_side" },
-- north of the left pair, looking south down over the crowns
{ map = "VIRIDIAN_POKECENTER", x = 1, y = 5, face = "down",
label = "pair_over" },
-- head on: standing below the middle pair looking north at it
{ map = "VIRIDIAN_POKECENTER", x = 6, y = 7, face = "up",
label = "pair_front" },
-- close up beside the east pair's pot
{ map = "VIRIDIAN_POKECENTER", x = 11, y = 7, face = "right",
label = "close" },
-- the Plateau lobby's row of four (MART tileset id), along the row
{ map = "INDIGO_PLATEAU_LOBBY", x = 11, y = 10, face = "right",
label = "lobby_row" },
-- and head on from the hall below
{ map = "INDIGO_PLATEAU_LOBBY", x = 13, y = 12, face = "up",
label = "lobby_front" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, s.map, s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[plant] capture missed: " .. path) end
end
end
print(("[plant] %d shots into %s"):format(shots, ROOT))
end
+80
View File
@@ -0,0 +1,80 @@
-- Scratch driver: the Cerulean gym and the houses beside it, shot at
-- several camera rungs with V-CURVE walked OFF..3, to see what the world
-- bend does to a building's roof.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/roof_curve_shots.lua \
-- SHOT_DIR=.scratchpad/roofcurve AB_TAG=before lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/roofcurve")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[roof] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
local WorldCurve = V.require("WorldCurve")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(30)
end
-- the gym door is CERULEAN_CITY (30,19); the bike shop and the row of
-- houses along the west side give a second, smaller roof in frame
local SCENES = {
{ map = "CERULEAN_CITY", x = 30, y = 20, face = "up", label = "gym" },
{ map = "CERULEAN_CITY", x = 27, y = 21, face = "up", label = "gymwide" },
{ map = "PALLET_TOWN", x = 5, y = 6, face = "up", label = "house" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
for _, curve in ipairs({ 0, 3 }) do
U.teleport(game, s.map, s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
WorldCurve.setting:setIndex(curve + 1, game)
settle()
local path = ("%s/%s_v%d_c%d.png"):format(ROOT, s.label, rung, curve)
game.capturePath = path
U.wait(8)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[roof] capture missed: " .. path) end
end
end
end
print(("[roof] %d shots into %s"):format(shots, ROOT))
love.event.quit()
end
+47
View File
@@ -0,0 +1,47 @@
-- Scratch probe: how long do a building model's merged quads get?
--
-- A quad's longest world-space edge is what decides how far its CHORD
-- falls below the world curve's parabola, so this is the number that says
-- whether the bend can crack the mesh open.
--
-- BUILD_MAP=CERULEAN_CITY POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/roof_span_probe.lua lovec .
return function(game)
local U = dofile("tests/drivers/util.lua")
local mapId = os.getenv("BUILD_MAP") or "CERULEAN_CITY"
U.teleport(game, mapId, tonumber(os.getenv("BUILD_X") or "30"),
tonumber(os.getenv("BUILD_Y") or "20"), "up")
U.wait(30)
local V = game.mods.exports["DRAMATIC_SHAPE"]
V = V and V.lib
local Structures = V and V.require("Structures")
local ow = game.overworld
if not (Structures and ow and ow.map) then
print("[span] mod or map unavailable")
love.event.quit()
return
end
local S = Structures.forMap(ow.map)
local hist, worst = {}, 0
for _, q in ipairs(S.objectQuads) do
local dx = math.max(q[1][1], q[2][1], q[3][1], q[4][1])
- math.min(q[1][1], q[2][1], q[3][1], q[4][1])
local dz = math.max(q[1][3], q[2][3], q[3][3], q[4][3])
- math.min(q[1][3], q[2][3], q[3][3], q[4][3])
local dy = math.max(q[1][2], q[2][2], q[3][2], q[4][2])
- math.min(q[1][2], q[2][2], q[3][2], q[4][2])
local span = math.max(dx, dz, dy)
local bucket = span <= 8 and "<=8" or (span <= 16 and "<=16"
or (span <= 32 and "<=32" or (span <= 64 and "<=64" or ">64")))
bucket = bucket .. (q.own and " bld" or " prop")
hist[bucket] = (hist[bucket] or 0) + 1
if span > worst then worst = span end
end
print(("[span] %d object quads, longest edge %d px"):format(#S.objectQuads, worst))
for _, b in ipairs({ "<=8", "<=16", "<=32", "<=64", ">64" }) do
for _, kind in ipairs({ " bld", " prop" }) do
print(("[span] %-10s %d"):format(b .. kind, hist[b .. kind] or 0))
end
end
love.event.quit()
end
+75
View File
@@ -0,0 +1,75 @@
-- Scratch driver: one shot of every OTHER user of the round-hull builder
-- (tree canopies, boulders, hedges, stumps, the Center planter), to check
-- that the `can` class's base cut is the identity it is supposed to be for
-- everything that does not ask for it.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/round_regress_shots.lua \
-- SHOT_DIR=.scratchpad/round AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/round")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then return end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
{ map = "VIRIDIAN_FOREST", x = 16, y = 20, face = "up", label = "forest" },
{ map = "PEWTER_GYM", x = 4, y = 10, face = "up", label = "boulders" },
{ map = "CELADON_GYM", x = 4, y = 8, face = "up", label = "hedges" },
{ map = "VIRIDIAN_POKECENTER", x = 6, y = 5, face = "up", label = "planter" },
{ map = "PALLET_TOWN", x = 5, y = 8, face = "up", label = "trees" },
}
local shots = 0
for _, s in ipairs(SCENES) do
local ok = pcall(U.teleport, game, s.map, s.x, s.y, s.face)
if ok then
Pipelines.setLevel("voxel", 5)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s.png"):format(ROOT, s.label)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[round] capture missed: " .. path) end
else
print("[round] teleport failed: " .. s.map)
end
end
print(("[round] %d shots into %s"):format(shots, ROOT))
end
+92
View File
@@ -0,0 +1,92 @@
-- Scratch driver: shots of the `bookcase` class across the tilesets that
-- pin it, for the shelf-front relief. Two of them are NOT shelves --
-- the League's gate walls and the terraces on PLATEAU -- and are here as
-- the control: their courses run edge to edge, so nothing should sink.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/shelf_relief_shots.lua \
-- SHOT_DIR=.scratchpad/shelves AB_TAG=before lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/shelves")
.. "/" .. (os.getenv("AB_TAG") or "before")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[shelf] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
{ map = "OAKS_LAB", x = 7, y = 2, face = "up", label = "dojo_lab" },
{ map = "CELADON_MANSION_2F", x = 2, y = 4, face = "up", label = "mansion_2f" },
{ map = "CELADON_MART_2F", x = 5, y = 5, face = "up", label = "lobby_mart" },
{ map = "MUSEUM_1F", x = 2, y = 4, face = "up", label = "museum" },
{ map = "VIRIDIAN_MART", x = 3, y = 5, face = "up", label = "mart" },
{ map = "SS_ANNE_CAPTAINS_ROOM", x = 5, y = 2, face = "up", label = "ship" },
-- the controls, both of them tilesets that borrow the collapse for
-- something that is NOT a shelf and say so with `bookcase_relief =
-- false`: the League's masonry gate walls, and Bill's transporter
-- drums. Nothing in either may move.
{ map = "INDIGO_PLATEAU", x = 2, y = 5, face = "up", label = "plateau" },
{ map = "BILLS_HOUSE", x = 2, y = 3, face = "up", label = "bills" },
-- the house shelves: pinned `desk`, NOT `bookcase`, so they go
-- through the world mesher's box fold and this relief never reaches
-- them. Here to show the gap.
{ map = "REDS_HOUSE_1F", x = 1, y = 2, face = "up", label = "reds" },
{ map = "BLUES_HOUSE", x = 1, y = 2, face = "up", label = "blues" },
}
local shots = 0
for _, s in ipairs(SCENES) do
local ok = pcall(U.teleport, game, s.map, s.x, s.y, s.face)
-- twice: the first load of the session has no mesh to settle against
pcall(U.teleport, game, s.map, s.x, s.y, s.face)
if ok then
Pipelines.setLevel("voxel", 5)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s.png"):format(ROOT, s.label)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[shelf] capture missed: " .. path) end
else
print("[shelf] teleport failed: " .. s.map)
end
end
print(("[shelf] %d shots into %s"):format(shots, ROOT))
end
+78
View File
@@ -0,0 +1,78 @@
-- Scratch driver: shots of the SS Anne's galley barrels, which are Lt.
-- Surge's trash can redrawn on the ship atlas. Three down the kitchen's
-- east wall at cells (13,5)/(13,7)/(13,9), one in the captain's room at
-- (4,1), and one each in the two ship-interior houses at (7,7).
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/ship_can_shots.lua \
-- SHOT_DIR=.scratchpad/ssanne AB_TAG=after lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/ssanne")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[ship] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
{ map = "SS_ANNE_KITCHEN", x = 12, y = 11, face = "up", label = "galley_up" },
{ map = "SS_ANNE_KITCHEN", x = 12, y = 3, face = "down", label = "galley_down" },
{ map = "SS_ANNE_KITCHEN", x = 11, y = 7, face = "right", label = "galley_side" },
{ map = "SS_ANNE_CAPTAINS_ROOM", x = 4, y = 4, face = "up", label = "captain" },
{ map = "CERULEAN_BADGE_HOUSE", x = 6, y = 7, face = "right", label = "house" },
}
local shots = 0
for _, s in ipairs(SCENES) do
local ok = pcall(U.teleport, game, s.map, s.x, s.y, s.face)
if ok then
Pipelines.setLevel("voxel", 5)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s.png"):format(ROOT, s.label)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[ship] capture missed: " .. path) end
else
print("[ship] teleport failed: " .. s.map)
end
end
print(("[ship] %d shots into %s"):format(shots, ROOT))
end
+80
View File
@@ -0,0 +1,80 @@
-- Scratch driver: shots of Vermilion Gym's trash cans, for the trash can
-- voxelization. The fifteen cans stand on odd cell columns 1..9 in cell
-- rows 7, 9 and 11; the sixteenth is up at cell (6,1) beside the leader's
-- platform. Even columns are open floor, so the player can be parked
-- between two cans and look along a row.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/trash_can_shots.lua \
-- SHOT_DIR=.scratchpad/cans AB_TAG=before lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/cans")
.. "/" .. (os.getenv("AB_TAG") or "before")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[can] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync("day")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
local SCENES = {
-- head on into the middle of the field, cans left, right and ahead
{ x = 4, y = 12, face = "up", label = "field" },
-- close up: standing between two cans of the bottom row
{ x = 2, y = 11, face = "left", label = "close" },
-- along the row, so the cans line up in depth
{ x = 4, y = 13, face = "up", label = "row" },
-- from the north, looking back down over all three rows
{ x = 4, y = 6, face = "down", label = "over" },
}
local shots = 0
for _, s in ipairs(SCENES) do
for _, rung in ipairs({ 3, 5 }) do
U.teleport(game, "VERMILION_GYM", s.x, s.y, s.face)
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
settle()
local path = ("%s/%s_v%d.png"):format(ROOT, s.label, rung)
game.capturePath = path
U.wait(6)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[can] capture missed: " .. path) end
end
end
print(("[can] %d shots into %s"):format(shots, ROOT))
end
+121
View File
@@ -0,0 +1,121 @@
-- Scratch driver: water shot with V-CURVE walked OFF..3, to see what the
-- world bend does to the reflective pass.
--
-- POKEPORT_DRIVER=mods/DramaticShapeVoxelMod/tests/water_curve_shots.lua \
-- SHOT_DIR=.scratchpad/watercurve AB_TAG=before lovec.exe .
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pipelines = require("src.render.Pipelines")
local ROOT = (os.getenv("SHOT_DIR") or "shots/watercurve")
.. "/" .. (os.getenv("AB_TAG") or "after")
local handle = game.mods.exports["DRAMATIC_SHAPE"]
if not (handle and handle.lib) then
print("[water] DRAMATIC_SHAPE mod not loaded")
return
end
local V = handle.lib
local DayNight = V.require("DayNight")
local ChunkMesher = V.require("ChunkMesher")
local Voxel = V.require("VoxelState")
local WorldCurve = V.require("WorldCurve")
local Water = V.require("Water")
-- WATER_RUNG=sky drops the screen-space march and leaves the sky path, to
-- tell an artefact of the one from an artefact of the other
if os.getenv("WATER_RUNG") then Water.setting:sync(os.getenv("WATER_RUNG")) end
require("src.world.OverworldController").rollEncounter = function() return nil end
local TileRenderer = require("src.render.TileRenderer")
TileRenderer.tick = function() end
TileRenderer.animFrame = function() return 0 end
DayNight.setting:sync(os.getenv("WATER_TIME") or "night")
pcall(os.execute, 'mkdir -p "' .. ROOT .. '" 2>/dev/null')
pcall(os.execute, 'mkdir "' .. ROOT:gsub("/", "\\") .. '" 2>nul')
local Zoom = require("src.render.Zoom")
pcall(function()
game.save.options.zoom = 1
Zoom.applyOptions(game.save.options)
end)
local function settle()
for _ = 1, 900 do
if ChunkMesher.pending() == 0 then break end
U.wait(1)
end
for _ = 1, 300 do
if Voxel.t >= 1 and Voxel.ready and ChunkMesher.pending() == 0 then break end
U.wait(1)
end
U.wait(40)
end
-- Stand on the walkable cell just north of the widest run of water on the
-- map, so a scene is picked by where the water actually is rather than by
-- a coordinate guessed off the block list.
local TileShape = V.require("TileShape")
local function shore(map)
local def = map.def
local shapes = TileShape.forMap(map)
local function classAt(cx, cy)
local tx, ty = cx * 2, cy * 2
local s = TileShape.at(map, shapes, map:tileAt(tx, ty), tx, ty)
return s and s.class
end
local best, bestRun = nil, 0
for cy = 1, def.height * 2 - 1 do
local run, start = 0, nil
for cx = 0, def.width * 2 - 1 do
if classAt(cx, cy) == "water" then
start = start or cx
run = run + 1
if run > bestRun and classAt(cx, cy - 1) ~= "water" then
bestRun, best = run, { x = start + math.floor(run / 2), y = cy - 1 }
end
else
run, start = 0, nil
end
end
end
return best
end
local SCENES = {
{ map = "PALLET_TOWN", label = "pallet" },
{ map = "CERULEAN_CITY", label = "cerulean" },
{ map = "VERMILION_CITY", x = 18, y = 27, label = "vermilion" },
{ map = "ROUTE_24", label = "route24" },
{ map = "VIRIDIAN_CITY", label = "viridian" },
}
local shots = 0
for _, s in ipairs(SCENES) do
if not s.x then
-- teleport once so the map is loaded, then let the scan place us
U.teleport(game, s.map, 1, 1, "down")
U.wait(4)
local spot = game.overworld and game.overworld.map and shore(game.overworld.map)
if spot then s.x, s.y = spot.x, spot.y else s.x, s.y = 1, 1 end
print(("[water] %s shore at (%d,%d)"):format(s.label, s.x, s.y))
end
for _, rung in ipairs({ 3, 4 }) do
for _, curve in ipairs({ 0, 3 }) do
U.teleport(game, s.map, s.x, s.y, s.face or "down")
Pipelines.setLevel("voxel", rung)
Pipelines.setLevel("tiltshift", 0)
WorldCurve.setting:setIndex(curve + 1, game)
settle()
local path = ("%s/%s_v%d_c%d.png"):format(ROOT, s.label, rung, curve)
game.capturePath = path
U.wait(8)
local f = io.open(path, "rb")
if f then f:close() shots = shots + 1
else print("[water] capture missed: " .. path) end
end
end
end
print(("[water] %d shots into %s"):format(shots, ROOT))
love.event.quit()
end
+59
View File
@@ -723,6 +723,65 @@ TEMPLATES = {
slab=3, front_eave=0, ledge=None, tileset="reds_house",
scrub=[(10, 5, 21, 15)],
),
# F10: the town house's bookcase -- the commonest piece of furniture
# in the game at 58 placements across three drawings, and the reason
# this family exists: the `desk` box it used to be had a flat front
# with the books PAINTED on it. Band for band it is the Celadon
# cabinet (F05/F06) on a different atlas, and it takes those numbers
# unchanged: 0-8 the top seen from above (black rim, white highlight
# courses along the north and west, grey field, the front corner
# shaded at row 8), row 9 the top's own black front edge -- so
# slab = 1 and that row folds into the roof band -- and 10-31 the
# front: two shelves in their dark openings over the cupboard's two
# panelled doors. Every one of those is a non-black region the
# drawing seals behind its own black frame, so the measured pane
# pass sinks each a voxel and the frames stand proud of them.
# Nine drawn top rows over a 32px plot: the rims map 1:1 and the
# uniform field cycles between. BOTH cell rows of the plot are
# blocked and both are cabinet drawing, so D is the whole grid --
# the case is built into the room's north wall. 23 voxels tall,
# one under the `desk` pin it replaces, and the same 23 the Celadon
# trophy case stands.
#
# This one carries books and a bowl on each shelf; 36 placements,
# the west end of eighteen homes.
"house_bookcase_bowls": dict(
tiles=[
[38, 41],
[14, 15],
[14, 15],
[30, 31],
],
roof_rows=10, roof_back=8, roof_front=2, roof_cycle=(2, 7),
slab=1, front_eave=0, ledge=None, tileset="house",
),
# F10 again: its twin at the east end, books on both shelves --
# 22 placements, and the only drawing of the two that the trashed
# house in Cerulean puts at the west end as well.
"house_bookcase_books": dict(
tiles=[
[38, 41],
[48, 49],
[48, 49],
[30, 31],
],
roof_rows=10, roof_back=8, roof_front=2, roof_cycle=(2, 7),
slab=1, front_eave=0, ledge=None, tileset="house",
),
# F10 once more, on the reds_house atlas: Red's and the Copycat's
# ground-floor pair (4 placements). The same object, one shelf of
# each drawing -- 48/49 books above, 34/35 below -- and the same
# band numbers.
"reds_bookcase": dict(
tiles=[
[38, 41],
[48, 49],
[34, 35],
[50, 51],
],
roof_rows=10, roof_back=8, roof_front=2, roof_cycle=(2, 7),
slab=1, front_eave=0, ledge=None, tileset="reds_house",
),
# F09: the stool at every one of those tables -- the first template
# with NO base piece. The drawing is one object, a round seat on
# legs, drawn MID-CELL over its own floor (rows 0-4 are the room