mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 08:11:11 +02:00
bump version
This commit is contained in:
+202
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user