Commit Graph

206 Commits

Author SHA1 Message Date
DramaticShape 47369c24fe fixed tops of walls in houses 2026-08-08 22:12:07 -04:00
DramaticShape 787d93a0e4 iterate version 2026-08-08 19:57:49 -04:00
DramaticShape c681167475 add doors to s/w/e sides of gate houses 2026-08-08 19:57:22 -04:00
DramaticShape 99de8c2760 update 2d shiny sprites and shiny animation 2026-08-08 19:55:32 -04:00
DramaticShape 34e7da5c12 v-grid toggle on battles 2026-08-08 19:01:53 -04:00
DramaticShape 9ddb940bc8 update 2d shiny sprites and shiny animation 2026-08-08 18:51:01 -04:00
DramaticShape c05c2cebd9 Merge pull request #125 from Code-Grub/fix/flat-top-rim-repeat
stop a flat top stamping its rim down the plateau
2026-08-08 16:49:29 -04:00
DramaticShape d6a9f10352 x
Removed restriction on redistribution of non-derivative code.
2026-08-08 16:27:30 -04:00
DramaticShape 4aeb0fc675 Update version from 1.7.2 to 1.8.0 2026-08-08 14:32:14 -04:00
DramaticShape 9be6888290 Merge pull request #143 from DramaticShape/shadow-toggle
Shadow toggle
2026-08-08 14:31:11 -04:00
DramaticShape 6482325e26 remove fireflies 2026-08-08 14:21:41 -04:00
DramaticShape b8b2e0cd33 shiny capture 2026-08-08 14:02:29 -04:00
DramaticShape bee22507b2 @
capture: open the shot 15% wider

BattleCam.zoom is a multiple of the rig frame height, so above one is
zoomed OUT. 1.15 is fifteen percent wider -- exactly one notch of the
player wheel (ZOOM_STEP).

Applied AFTER pushBattle, because OverworldBattle begin calls
BattleCam.reset() and that puts zoom and zoomGoal back to 1. Both are
set, not just the goal, so the shot opens wide instead of gliding
outward over the first fifth of a second of the recording.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@
2026-08-08 13:59:46 -04:00
DramaticShape 1f9a720625 lineup: shiny Lapras on the open sea (ROUTE_20 mid-water)
Checked rather than assumed: the arena stages fine out there (noArena=0),
which was the open question -- STADIUM A wants clear GROUND and there is none
for fifty tiles in either direction. The water reads as the floor, the
shoreline sits on the horizon, and the sparkle ring lands clear of the shell.
2026-08-08 13:54:31 -04:00
DramaticShape 55145e3faf cave lineup: Mewtwo and Articuno, each where it lives
Cerulean Cave B1F and Seafoam Islands B4F. The RUNS rows gain a rung column
because a cave is where the choice actually matters: STADIUM A stages the
fight on the map and wants clear ground, and a cave floor often has none,
which is what STADIUM B's carried discs exist for. Both are on A -- checked
rather than assumed, and Cerulean Cave stages fine (noArena=0), with the
walls framing the model better than open ground does.

Worth noting for anyone reading the shot: shiny Mewtwo does NOT come out lime
green here. The Stadium values rotate its purple toward blue instead, which
is the fidelity:poor case the guide flags -- the model is right, the
divergence is Stadium's own.
2026-08-08 13:49:26 -04:00
DramaticShape 0091e6d93b the sparkle was playing behind the transition wipe
Armed on the occupant-change edge, which happens while the screen is still
mid-wipe -- so the burst spent its whole three-quarter-second life underneath
it. The instrumentation is what settled it: armed=1, quads=450, which is
45 frames times 10 stars, exactly LIFE. It was never missing. It was drawing
where nobody could see it.

Two gates, because the first one was still wrong. Holding the clock until the
scene DREW the side does not help: the battle renders under the wipe for
about a second before the wipe is gone. The burst now waits until the battle
is the top of the stack -- the wipe popped, somebody watching -- and only
then starts. Armed and released are separate moments now, which is what they
always were.

Also, a shiny no longer discolours a PERSON. Both sides can be holding a
trainer pic rather than a Pokemon (the foe's portrait before the send-out,
the player's own back until "Go!"), and the tint was going straight through
it. Shininess is a fact about a Pokemon, not its owner. sideTexture asks the
same two questions it already used to label the finished texture.

And the capture drivers build the party BEFORE pinning the odds. Pokemon.new
is where shininess is decided, so setting odds to 1 first made the player's
own Pikachu shiny too -- which tinted the player's side, which during the
intro is the trainer sprite. That is what "the player trainer sprite seems
discolored" was.

Lineup is now Charizard, Electrode, Vaporeon, Dratini.

Verified by strip: the burst lands in the frames right after the wipe clears,
and the trainer back sprite is its own colour again.
2026-08-08 13:35:01 -04:00
DramaticShape 120f9716b6 capture drivers: real 1x, one window at a time
A POKEPORT_DRIVER run is deliberately unpaced -- main.lua's pacingEnabled()
returns false whenever the variable is set, so love.run spins as fast as the
machine allows and takes one Game:update(1/60) per turn of the loop. Right
for a screenshot script, wrong for a recording: "wait 180 frames" is three
seconds only by coincidence of hardware.

No engine change needed. The loop is blocked while the driver coroutine is
running, so sleeping inside it before each yield paces the whole thing -- one
logic step per one sixtieth of real time, 1x by construction. Measured with
DS_AUTOCLOSE: asked for 6.0s, took 6.31s, and it errs slow rather than fast.

shiny_one.lua does ONE encounter and then never finishes, which is how the
window stays up: main.lua quits the moment a driver coroutine goes dead. So
closing the window by hand is what ends the take. shiny_run.sh launches the
five in turn and blocks on each, so the next never opens over the top of one
still being recorded, and a bad take can just be closed and re-run.

It also puts options.lua back afterwards. The game persists the whole options
table mid-run, so every capture leaves its display settings on disk.
2026-08-08 13:22:52 -04:00
DramaticShape 322defbbd0 size the sparkle to the Pokemon it belongs to
Flat numbers cannot work here and both previous attempts proved it in
opposite directions. The first ring was 7 world units across, which is inside
anything bigger than a Rattata -- and additive drawing keeps the depth test,
so all sixty quads a frame were rejected and nobody saw them. Correcting by
eye gave a ring 24 across starting 20 units up, which is taller than the
tallest Pokemon in the game: it hung in the sky above a Ponyta with nothing
underneath it.

A mon on the map is 5 to 18 world pixels tall (StadiumMon REF_HEIGHT 14). One
ring cannot fit a Diglett and a Gyarados, so every distance is now a fraction
of the mon: Stadium pushes each side's worldHeight and worldRadius into
ShinyFx every frame -- worldRadius exists precisely so "a caller can size
something to its footprint" -- and the burst is measured off those.

Two more things the pictures showed. The ring is an ELLIPSE with its axes
measured separately: flattened enough to read as a ring from the battle's low
seat, a single radius reaches only a third of the body's height, so the top
and bottom stars sit on the Pokemon. And it OPENS from clear of the body
rather than from a point -- springing out of nothing means every star spends
the first frames stacked at the centre, which is the middle of the mon and
looks exactly like the sparkles being stuck inside it.

Shot against Ponyta and Gyarados, an order of magnitude apart in size: the
ring hugs each silhouette from outside and no star lands on either body.
2026-08-08 13:01:09 -04:00
DramaticShape eb69bc7db8 a rev bump has to actually reach the machines it was bumped for
It did not. pending() short-circuited on available(), and available() was
true for any checkout carrying assets/stadium -- so when REV went to 3 for
the shiny variants, such a machine was never pending, was never asked to
rebuild, and quietly went on serving the old set. ready() was false, so
readPack skipped the save-dir cache and read the shipped normal-only packs:
every shiny Pokemon drawn in its ordinary colours, with nothing on screen
saying why. A driver run sitting at "idle 0/151" for twelve thousand frames
is what surfaced it.

pending() is now keyed on ready(), not available(). A checkout with a ROM
spends one loading screen rebuilding a set it had files for; after that it is
current and never pending again. That was the cost the old short-circuit was
avoiding, and it is worth paying once to make a rev bump mean something.

The other half is the players who imported a ROM through the picker rather
than dropping it in baseroms/. beginFrom builds from the bytes and never
keeps them, so on a rev bump there is no ROM to rebuild from -- and with
available() keyed on ready(), the STADIUM rungs would have vanished off the
options row entirely. usable() now separates "these packs are readable" from
"these packs are current": format and count, deliberately not rev. Stale
packs keep the mode working and keep the rungs offered; only the recolour
waits for a rebuild. Losing the shinies until then is a blemish, losing the
mode is not.

readPack orders the two accordingly: a current cache always wins, a stale one
wins only when there is no shipped set to prefer instead -- so a cache from
an extractor rev we have since fixed cannot shadow good files, while a player
whose only copy IS that cache still gets Pokemon on the field.

Verified by rolling the marker back to rev 2 and launching: ready=false,
usable=true, available=true, pending=true, and the build runs unprompted.
2026-08-08 12:53:59 -04:00
DramaticShape b05c7265d6 a palette transform, and the sprite sheet it is for
The texel transform is wrong for palettes, and silently so. A lookup table
answers only the colours it contains -- the ones its MODEL is painted with --
and the engine's ADVANCED palettes are a different set entirely: BLUEMON's
blue is not any blue on the Gyarados model. Asked to shift a palette, the
table returned it unchanged, so the five table species produced no sprite
shift at all and the most dramatic shiny in the game came out identical.

paletteTransform picks the right tool per species: the slide where there is
one, the tint multiplier (which IS derived from the table) where there is
not. 149 of 151 palettes now move; the two that do not are Jigglypuff and
Wigglytuff, whose shiny genuinely leaves the body almost where it was.

Plus the two tools that make the comparison sheet. Worth saying why it is a
palette job at all: Gen 1 battle pics carry no colour -- they are four-shade
DMG grey, and every bit of colour is the palette laid over them. So a shiny
sprite is the same pixels under a shifted palette, and a sheet built any
other way would be showing something the game never draws.

Sheet at .claude/shiny_update/7_sprites_all151.png, every species beside its
own control.
2026-08-08 12:47:54 -04:00
DramaticShape 6b4dfe6b9b shiny: tint from the body colour, and the fixes the screenshots found
Four things, all found by shooting the feature rather than by reading it.

The flat-pic tint was a no-op for the five colour-table species. tintFor ran
synthetic reference colours through the lookup, none of them were IN it, so
every ratio came back 1 and the tint was discarded -- a shiny Gyarados drew
an ordinary blue pic. Those species now measure the tint from the table's own
entries.

It was also a no-op for most SLIDE species, for a better-hidden reason: a hue
rotation moves red toward cyan and cyan toward red, so averaged over a
balanced set of references the ratios cancel and every species reports no
tint. Charizard and Ponyta both came back neutral. The tint is now measured
against the colour each species is actually MADE of -- a modal body colour,
generated into data/shiny_colors.lua as `dom`. 132 of 151 now carry a usable
tint; the rest genuinely shift too little for one to mean anything.

The sparkle drew sixty quads a frame that nobody could see. It was built to
numbers a tenth of the scale of a mon card -- a ring seven units across,
inside a Gyarados -- and additive drawing keeps the depth test, so all of it
was rejected. Sized against the card now, and pulled toward the camera the
way the move-animation card is.

And the summary PIC cannot be recoloured by touching pixels: the art is
four-shade DMG grey and the colour is applied afterwards by the palette pass.
That attempt is reverted, with the reason left where the next person will
look for it. The star is the designation that works there.

Also: tools/shiny_colors.py now resolves its own paths instead of hardcoding
a worktree, and the extract test gained a hard failure when NOTHING
recolours -- which is what a missing colour table looks like from the
outside, and it passed through it once already.

Evidence in .claude/shiny_update, every case beside its own control.
2026-08-08 12:37:57 -04:00
DramaticShape de54e4ea26 fix canopy issue 2026-08-08 12:27:19 -04:00
DramaticShape 0d245c4ccb Merge branch 'worktree-shiny-system' into shadow-toggle
Shiny Pokemon: derived from DVs via the engine's own Stats.isShiny, models
recoloured as part of the Stadium extraction, flat art tinted, a sparkle on
arrival and a star on the status page.

Verified on the merged tree rather than only on the branch, since
shadow-toggle moved underneath it (the new menus work): the mod loads clean,
tests/shiny_test passes 49, and the extraction still reproduces all 151
packs byte-for-byte against the Python oracle with 151 shiny variants
alongside.
2026-08-08 12:22:24 -04:00
DramaticShape 2907aba6ff new menus 2026-08-08 12:18:35 -04:00
DramaticShape c07aed2449 find the colour table relative to the mod, not the cwd
ShinyPalette loaded data/shiny_colors.lua by guessing cwd-relative paths
when V.data was absent, which is the headless case. Run from the project
root, none of the guesses hit -- so the extraction built all 151 species,
recoloured none of them, and reported PASS. The packs were correct; there
simply were no shiny variants in them, and nothing in the output said so.

Two fixes, because either alone leaves the trap open:

  V.path is now consulted first, so the file is found relative to the MOD
  the way every other resource is. The extract test's stub sets it to
  whatever --mod it was given.

  A count of zero recoloured is now a failure. That is precisely what a
  missing colour table looks like from the outside, and a test that passes
  on it is not testing the feature. It was caught by noticing the number
  change between two runs that differed only in where they were started
  from -- which is too thin a thread to hang it on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 12:14:16 -04:00
DramaticShape a3fb18a589 shiny Pokemon, on by default
Gen 1 has no shininess of its own, but it has the four DVs Gen 2 reads to
decide it -- and the engine already ships that reading (Stats.isShiny, its
own comment calling it "the RBY virtual shiny", allowlisted for mods
precisely so an indicator mod can call it). Nothing new is stored on a
Pokemon and nothing migrates: every save already contains the answer, and
this starts drawing it. Random DVs land on the pattern 1 in 8192, which is
the classic rate and the default the odds dial ships at.

Deriving rather than storing is what makes it survive a save, a box, a
trade and an evolution with no second copy of the truth to drift. mon.shiny
is a cache written from the DVs, never read as the source.

The roll goes in Pokemon.new -- every wild, gift, starter and traded mon is
built there, and it is before the battle bakes its sprite, which
battle.started is already too late for. It draws from the mod's own random
stream so installing this does not shift the sequence damage rolls and
encounter slots come out of. Trainers stay ordinary by themselves: the
engine pins their DVs, as the real games do.

The models are genuinely recoloured, as part of the extraction. Each
species is decoded once, packed as usual, then recoloured and packed again
as NNNs.dsm. The colours are Stadium's own HSL slide (hue in degrees,
saturation and lightness on a -8..+8 scale at 12.5% a step); five species
carry an explicit colour table instead, because Stadium gives them a real
alternate texture that no single slide reproduces -- Jigglypuff's body must
stay pink while its irises rotate to green.

Extraction is the right moment because StadiumFx's generated frames are
still marked there and the packer drops the marker: it is the last point a
flame is distinguishable from a hide. A shiny Charizard has a shiny hide
and an ordinary fire. The normal packs are written BEFORE the recolour, so
they come out byte-identical and stadium_extract_test still diffs all 151
against the Python oracle unchanged -- no format change, no DSM4, no second
implementation to keep in step. REV goes to 3 so an existing cache rebuilds.

Flat art is tinted instead, because the engine bakes a species palette into
a cache with no notion of which individual is drawn. The tint comes from
that species' own slide rather than a generic gold. A multiply can only
darken, so species whose shiny is lighter read quieter there than on the
model; the status page's star is the mode-proof mark.

Tests: 58 assertions in tests/shiny_test.lua, including the colour
transform against 640 real colour pairs lifted from the verified texture
set, the DV model, the read side, and the end-to-end through the engine's
own constructor. stadium_extract_test gains --mod (worktrees have neither
the ROM nor the packs, both gitignored) and now also checks that every
shiny pack is the same length as its twin and actually differs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 12:12:25 -04:00
DramaticShape 66da6cbc4b fix catch regression + add new menus 2026-08-08 12:08:27 -04:00
DramaticShape 121c87b629 fix back facade of buildings 2026-08-08 11:34:04 -04:00
DramaticShape dfe2f1ae1a fix regressions with freemove 2026-08-08 10:42:28 -04:00
DramaticShape 41f1f07342 grass sway during battles, fix south screen culling 2026-08-08 10:11:00 -04:00
DramaticShape 848c9cb29f Merge pull request #142 from DramaticShape/dramatic_go_to_the_polls
Dramatic go to the polls
2026-08-08 09:46:39 -04:00
DramaticShape 87a6c03017 add ball throw mechanic 2026-08-08 09:44:47 -04:00
DramaticShape f0d5ca570a lets go 2026-08-08 09:06:51 -04:00
DramaticShape d5542f9518 Merge pull request #138 from daviDarthemis/dev
Grass improvements
2026-08-08 08:27:19 -04:00
DramaticShape 063ce6e328 Bump version from 1.7.1 to 1.7.2 2026-08-07 21:20:47 -04:00
DramaticShape c9c5a8901a Merge pull request #119 from Elementalzx14/fix/ios-battle-hud
Fix iOS battle HUD
2026-08-07 21:16:31 -04:00
DramaticShape d86d387243 Merge pull request #139 from DramaticShape/stadium_fixes
1.7.1
2026-08-07 21:05:19 -04:00
DramaticShape 89fe722a62 iterate version 2026-08-07 20:54:25 -04:00
DramaticShape 180dd393ea add render distance options 2026-08-07 20:52:43 -04:00
DramaticShape 67bbb3d44b 3DS port boots :) 2026-08-07 20:01:37 -04:00
DramaticShape 4ee15c4f32 Update README to include redistribution policy
Added a restriction on code redistribution after v1.6.0.
2026-08-07 19:03:37 -04:00
Davi D'artemis 5a4979303b visual improvements to the grass 2026-08-07 05:25:37 -03:00
Davi D'artemis a46f194009 Visual improvements: grass movement 2026-08-07 04:38:41 -03:00
DramaticShape 301bd4c1f9 Merge pull request #130 from DramaticShape/stadium_fixes
Stadium fixes
2026-08-06 20:40:38 -04:00
DramaticShape 55bc993ed7 fix battle rotations in vr 2026-08-06 20:31:01 -04:00
DramaticShape 6080531b08 add diorama vr modes 2026-08-06 19:57:11 -04:00
DramaticShape 53fff766a4 revert battle locations temporarily 2026-08-06 19:56:39 -04:00
DramaticShape 00f2b0bb9a Merge pull request #128 from DramaticShape/stadium_fixes
Stadium fixes
2026-08-06 18:24:30 -04:00
DramaticShape c8555e6820 update CUT tree voxelization and mansion computer desks 2026-08-06 18:15:28 -04:00
DramaticShape 91d9a37e8f update voxelizations of celadon diner stools and tables. Update square table voxelization in celadon mansion. Update mansion computer desks 2026-08-06 17:52:59 -04:00