In pokered, MainInBattleLoop calls HandlePoisonBurnLeechSeed right
after every Execute*Move (core.asm:426-464), so a seeded, poisoned or
burned mon takes its residual before the slower side acts. The port
ran the whole sweep in endOfTurn, which made leech seed behave like
Gen 3+ and never showed the drain animation (#784).
The residual sweep now runs per action under the gen1_faithful
ruleset, gated by a new residualAfterMove flag; modern_clean keeps
the end of round sweep. The leech seed drain plays the ABSORB
animation from the healing side, the way the original flips
hWhoseTurn before PlayMoveAnimation. Item, ball, failed run and
ghost-fear turns still tick the player's residual, matching
ExecutePlayerMoveDone.
* Resolve Find Mods stats from each mod's GitHub repo when the feed lacks them
A FIND MODS row now shows download/date stats even when its feed publishes
none: the row fetches the mod's own GitHub releases through the same
cached ModUpdate.fetchReleases the MODS tab uses (six-hour options cache,
so an installed mod's repo is instant). Feed-published stats still win
when present; otherwise one repo is fetched per frame -- the thumbnail
budget pattern -- so opening the tab never stalls for the whole listing.
ModUpdate.statsForReleases is the shared resolver.
* Fix crash opening the Find Mods tab: rename the stats cache field
The resolver stored results in self._findStats, which collides with the
method of the same name: self._findStats resolves through the metatable to
the function, so the or {} guard never fired and indexing it crashed the
launcher the moment the panel built. State now lives in _findStatsCache.
* Fix Find Mods crash: require ModUpdate in the find panel
buildFindPanel called ModUpdate.statsLine without a local require --
only buildModsPanel had one -- so opening the tab indexed a nil global.
* Retry Find Mods stats after failed repo fetches
A failed repo fetch (hourly GitHub API rate limit, transient network error)
was memoized as resolved, so a rate-limited first visit left those rows
empty for the whole session. Failures now schedule a 60s retry; a 404 is
still permanent so a renamed or vanished repo is fetched once.
* Add the MODS tab sort options to the Find Mods tab
the launcher runs before Game:load, so #767's Strings hooks had nothing
filling the catalog and a restart could not help - the ordering is the same
every launch. reads lang/strings.lua from enabled mods only, data not the
entry chunk, sandboxed.
plus a plain pixel fallback on the ui faces, or the kana draw as tofu.
drawBox left the color white, which tiles ignore but ttf text does not, so
every label after a box drew white on white.
typechart only loaded on entering a battle, so the summary screen got raw
type ids back instead of display names.
lets a translation keep chosen characters on the rom tiles instead of the ttf.
needed for japanese: sizing the font for kana makes latin narrower, which
knocks the party menu numbers out of line.
A mod index can now publish three optional per-entry fields -- downloads
(total across every release), first_release and last_release (ISO days) --
which the FIND MODS listing shows in the same gold line the MODS tab uses.
The fields are additive by design: feeds that carry them stay readable by
every build that predates them (schema_version stays 1), and feeds that do
not render exactly as before. ModUpdate.statsLine builds the shared line;
the MODS tab reuses it. Parser, formatting, and parse coverage are tested.
The MODS tab now shows each installed mod's total GitHub downloads
(summed asset download_count across all releases), its first and latest
release dates, and a Sort row (Name / Popularity / Release date /
Last updated) persisted in options.modSort.
The launcher already fetched per-repo release lists for update checks, so
the data rides the same cached fetch: parseRelease keeps download_count
and published_at, writeCache persists them, and a cache entry written
before the fields existed is treated as stale and refetched once instead
of hiding the line behind an old cache.
Opening Touch Controls dropped the launcher virtual cursor and swallowed
gamepad input while touch still worked. Share PadCursor with the save
editor, forward pad events, and centralize overlay handoff/resume so both
hosts park and re-arm the pointer cleanly.
Co-authored-by: Cursor <cursoragent@cursor.com>
Source:getChannelCount could skip the #626 widen on love-nx, and keeping
8-bit depth into a stereo buffer still sounded wrong on audren. Decode the
file via newSoundData, always emit 16-bit stereo like ChipSynth, and write
fresh pika-cry WAVs as stereo at extract time so re-imports skip the hop.
Co-authored-by: Cursor <cursoragent@cursor.com>
The Yellow boot suite only recorded newSource, so a missing sound.newSoundData
wrap (the silent hole that motivated the full-surface overlay) could still go
green. Force mono on the Source stub, record newSoundData, and assert the cry
re-read lands on yellow/. Also pin the three NX suites in scripts/test.sh T0
and fix the seed_chunk global leak in the fallback suite.
Co-authored-by: Cursor <cursoragent@cursor.com>
The overlay only wrapped the five loaders the boot path needed, leaving a
silent-failure hole: any future state (or current code like Sound.lua's
widenMono, which re-reads the pika-cry WAV via love.sound.newSoundData
with the caller's bare path) could load a generated asset through an
unwrapped API and silently degrade on hardware.
NxAssetOverlay now wraps every read-side love function that accepts a
filesystem path (filesystem.read/load/lines/newFileData/getInfo,
graphics.newImage/newFont, image.newImageData, audio.newSource,
sound.newSoundData, font.newFontData), so new states and mods fall inside
the Blue/Yellow fallback with zero per-call-site work. Write-side
functions stay stock, proven by identity assertions in the fallback
suite. The static guard's forbidden-literal list covers the same APIs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Three layers, all running without a ROM:
- nx_yellow_boot_test.lua: drives the real Yellow and Blue boot states
(TitleState, YellowIntro + IntroMovie pre-roll for Yellow, IntroMovie
direct for Blue, Sound.playPikaCry) against a broken-mount filesystem
where generated art exists only under yellow|blue/, and asserts no bare
assets/generated path ever reaches the raw love loaders. This is the
runtime complement to the static literal guard: data-driven manifest
paths and formatted paths (cry_%02d.wav) are exactly what a source scan
cannot see. love_stub gains Image:setFilter/getFilter so IntroMovie
constructs headless.
- CI path gate: switch-changes now also triggers on the NX runtime
(NxAssetOverlay, Platform, GameVersion, CacheFs) and the NX engine
suites, so src-side NX regressions rebuild the fused NRO instead of
slipping through with green headless-only checks.
- switch-selftest runs the three NX engine suites headlessly on the
fork-safe ubuntu runner, giving PR feedback before the self-hosted Mac
build. The content gate and switch-build.md docs were updated in sync.
Co-authored-by: Cursor <cursoragent@cursor.com>
The scattered per-call-site prefix rewrites were a parallel track that any
future newImage("assets/generated/...") would silently bypass. Replace
them with NxAssetOverlay: installed once from love.load on NX only, it
wraps newImage / newImageData / newSource / filesystem.read / getInfo so a
missing assets/generated path falls back to the active version's
blue|yellow copy. Call sites return to plain love loader calls, and
Assets.resolve goes back to being the platform-free mod-override point.
Two deliberate exceptions remain: the chip-audio worker (separate Lua
state) keeps receiving the prefix explicitly via audio.programPrefix, and
data/generated module loads keep using CacheFs.readActive.
A new guard test (tests/engine/nx_generated_guard_test.lua) fails CI on
any direct love loader call with a literal assets/generated path, so the
class of bug cannot regress by accident. scripts/test.sh --quick is
green across all tiers.
Co-authored-by: Cursor <cursoragent@cursor.com>
Yellow music was still silent because the background worker thread loads
ChipSynth.lua in a fresh Lua state with no GameVersion/Platform context.
The main thread's prefix never reached it.
ChipAudio.slimAudio now resolves the versioned cache prefix on the main
thread and includes it in the audio payload as `programPrefix`.
ChipSynth.loadBanks prefers `audio.programPrefix` when present, falling
back to its own NX detection for the sync path. Blue and Yellow are
handled the same way.
Tests cover the worker prefix hand-off and Blue's programs.bin path.
Co-authored-by: Cursor <cursoragent@cursor.com>
The previous NX gate only rewrote image paths that go through Assets.resolve.
Pokemon Yellow still had no sound and a blank title screen because:
- ChipSynth reads programs.bin directly via love.filesystem.read, bypassing
Assets. On NX the unprefixed path is missing when the mount overlay fails,
so the engine never built and every song/SFX was silent.
- Sound.playPikaCry loads pika_cries WAVs with love.audio.newSource, also
bypassing Assets.resolve.
- TitleState, YellowIntro, and IntroMovie call love.graphics.newImage
directly on unprefixed assets/generated paths, so the Pikachu title and
intro atlases failed to load.
Fix: apply the same NX-only prefix rewrite in those four places.
Desktop/Android keep the existing mountVersion overlay behavior.
Also add ChipSynth._loadBanksForTest and tests covering the new paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
Capture resolve paths and newImage open results for Yellow/Blue art
triage without enabling switch-debug.txt.
Co-authored-by: Cursor <cursoragent@cursor.com>
Desktop and Android keep mountVersion as the overlay; only love-nx
resolves assets/generated to yellow|blue/ save-dir paths.
Co-authored-by: Cursor <cursoragent@cursor.com>
NX fused mount often cannot expose assets/generated; open the real
yellow|blue/assets/generated file with newImage instead of FileData.
Co-authored-by: Cursor <cursoragent@cursor.com>
Probe generated canaries after mountVersion and always readActive for
prefixed caches so sprites are not blanked by empty PhysFS stubs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Disable mouse-yield on NX where stick/touch moves the system pointer between sparse axis events, clamp pad dt, pixel-snap the overlay, and soften FlexLove GC so the launcher cursor stays steady.
Co-authored-by: Cursor <cursoragent@cursor.com>
Only resize when width/height change; love-nx flag mismatches were
recreating the EGL surface every frame.
Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror Data:load's versioned CacheFs read in Assets so Yellow-only NX
Play survives intro without needing a Red root cache mask.
Co-authored-by: Cursor <cursoragent@cursor.com>
Unlock love-nx SDL dock/undock resizing and sync via NxDisplay so
booting docked is not stuck on the conf 720p hint.
Co-authored-by: Cursor <cursoragent@cursor.com>
Skip per-frame mouse warps and FlexLove perf sampling on Switch, feed pad coords through a getPosition bridge, and park that shim before the save editor so desktop paths stay unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
A shared imports/ inbox with Red+Yellow was starting Red from the Yellow tab; match by GameVersion.forSha1 for the selected game and document the tab-scoped rescan.
Co-authored-by: Cursor <cursoragent@cursor.com>