Commit Graph

437 Commits

Author SHA1 Message Date
Andrew Quenehen 7e0c64cb78 docs(specs): AD-012 NX save .sav inbox
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 08:06:16 -03:00
Andrew Quenehen 4afb54c54f docs(nx): save .sav inbox and exports paths
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 08:05:53 -03:00
Andrew Quenehen 74f6b68034 feat(nx): Export save shows MTP exports path
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 08:02:38 -03:00
Andrew Quenehen 5d1e7ff3c1 feat(nx): Import save uses imports/saves inbox
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 08:02:04 -03:00
Andrew Quenehen 3c628140f7 feat(nx): saves inbox scan with AppleDouble/retain guards
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 08:01:14 -03:00
Andrew Quenehen 8bf99c3318 fix(switch): restore Save Editor pad and touch input on NX
Route gamepad/touch into the editor instead of dropping them in editorMode,
and hit-test clicks at event coords so a finger tap is not lost under the
Joy-Con virtual cursor.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 23:04:37 -03:00
Andrew Quenehen f0829ed54f Merge branch 'dev' into feat/switch-nx
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 22:30:18 -03:00
bryanthaboi 21276e20d1 Merge pull request #707 from ShaneMcGovernIE/fix/champion-theme-timing 2026-08-02 21:26:03 -04:00
bryanthaboi 9fb9648e4c Merge pull request #708 from ShaneMcGovernIE/fix/picker-path-sanitizing 2026-08-02 21:25:43 -04:00
bryanthaboi af7689a1bc Merge pull request #710 from ShaneMcGovernIE/fix/faint-cry-parity 2026-08-02 21:25:20 -04:00
Andrew Quenehen 59dba325b9 docs(switch): finalize Switch docs for PR review
Drop WIP status language, document Joy-Con controls and shortcuts,
credit the port and V1 testing help, and record community V1 boot evidence.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 22:23:29 -03:00
Andrew Quenehen d13064ee64 ci(switch): run fused build only after offline selftest
Avoid burning the self-hosted Mac when the ubuntu packaging gate fails.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 22:08:49 -03:00
Andrew Quenehen 669c9f4d8b ci(switch): skip fused build on fork pull requests
Keep ubuntu selftest for fork→canonical PRs; leave iOS build policy alone.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 22:08:36 -03:00
Andrew Quenehen 228306f883 ci(switch): run workflow content gate in headless suite
Path-gate the Lua gate file and invoke it from scripts/test.sh T0.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 22:08:15 -03:00
Andrew Quenehen 0a2ef854e6 test(switch): harden CI workflow content gate
Forbid soft-fail on Switch jobs and require the sha256 sidecar path.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 22:07:58 -03:00
Andrew Quenehen 61c471343a ci: stop artifact commenters from deleting each other
Upsert via comment-tag only so iOS and Switch PR comments coexist.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 22:07:50 -03:00
Shane McGovern 175bff4b29 Match the original faint sound sequence per side (#709)
pokered plays no 'pitched-down faint cry': the player mon's faint is its
ordinary species cry (RemoveFaintedPlayerMon -> PlayCry) with no
Faint_Fall, and the enemy faint plays no species cry at all -- trainer
battles get SFX_FAINT_FALL then SFX_FAINT_THUD, wild battles go straight
to the victory music (FaintEnemyPokemon core.asm:732-796).

The port played the species cry AND Faint_Fall on every faint, so a
fainted enemy sounded its full battle cry and a fainted player mon got
the fall whistle the hardware never plays.

BattleState.onFaint now:
- player: Sound.playCry only
- enemy trainer: Faint_Fall then Faint_Thud (after the slide)
- enemy wild: no faint sfx (victory music already queued)

Adds tests/parity_faint_cry_bug709.lua asserting the per-side sequence.

Fixes #709
2026-08-03 01:55:52 +01:00
Andrew Quenehen 95eb1c3a35 ci(release): clarify Switch hard-fail vs path-gated PR CI
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 21:53:19 -03:00
Andrew Quenehen cf50976d65 docs(switch): document CI selftest, fused PR builds, release hard-fail
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 21:52:58 -03:00
Andrew Quenehen 3fcee7f951 ci(switch): comment NRO artifacts on pull requests
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 21:52:36 -03:00
Andrew Quenehen 17a4c83970 ci(switch): add canonical fused NRO artifact job
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 21:52:24 -03:00
Andrew Quenehen 9712fb1e9b ci(switch): path-gate offline selftest like iOS
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 21:52:01 -03:00
Andrew Quenehen cbf622f4aa Merge branch 'dev' into feat/switch-nx
Bring feat/switch-nx up to date with origin/dev (72 commits). Resolve
Input/RomImporter conflicts by keeping GamepadMap (NX face remap + dual-path
gate) while adopting upstream joyBindings rebinds (#632) and Enable-all mods
(#647). Gate shoulder GAME SPEED hotkeys when Select is held so Select+L
display chords still work.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-02 21:36:56 -03:00
Shane McGovern 7c1cdea2eb Sanitize picker file paths (spaces / non-ANSI) and shell prompts
Issue #665: the Windows ROM and save pickers returned the raw chosen
path. io.open on Windows needs ANSI bytes, so a path with accented
characters (Pokemon -> Pok\x82mon, or a folder like 'Pokemon Gen1')
could never be opened -- the same bug #325 already fixed for mod zips
by copying to a plain-ASCII temp name.

Apply that fix to the ROM and .sav pickers: each copies its pick to an
ASCII temp name (pokeport_rom_pick.gb / pokeport_sav_pick.sav) before
answering, exactly like chooseZip does.

Also sanitize the prompt strings interpolated into the picker shell
commands: '%' would be eaten as a string.format directive, and quotes
would break the AppleScript/zenity argument or the surrounding shell
string.

Fixes #665
2026-08-03 01:04:59 +01:00
Shane McGovern 2bfa491b6a Start champion theme when rival dialogue ends, not at battle
The Elite Four champion script showed the rival's intro text and then
jumped straight into the battle; the wipe-time playBattle('final')
started Music_FinalBattle only when the battle began. The original
(ChampionsRoomRivalReadyToBattleScript) plays MUSIC_FINAL_BATTLE right
after the dialogue ends, before the battle.

Add a play_music Music_FinalBattle row between the intro text and the
battle, and bump the two past-the-end jump targets 25 -> 26 for the new
row count. pushBattle's wipe-time playBattle('final') no-ops on the
already-playing song, so the theme stays continuous into the fight.

Fixes #706
2026-08-03 00:52:46 +01:00
bryanthaboi 839b238e74 Merge pull request #699 from ShaneMcGovernIE/feature/3x-speed-and-shoulder-hotkeys 2026-08-02 19:08:25 -04:00
Shane McGovern ec22d57cd1 Document GAME SPEED hotkeys in README
Maintainer review on #699 asked to document the hotkeys. Added key 1
(cycle speed up) and controller R2/L2 to the README Hotkeys table, and
GAME SPEED to the Options-menu note.
2026-08-03 00:07:31 +01:00
bryanthaboi 23ef283272 Merge pull request #701 from ShaneMcGovernIE/fix/revive-exp-participants 2026-08-02 19:02:16 -04:00
bryanthaboi 327579f9b6 Merge pull request #700 from ShaneMcGovernIE/fix/pp-up-stats-display 2026-08-02 19:02:06 -04:00
Shane McGovern e5f9d2903d Fix revived Pokemon not receiving experience share
When a Pokemon faints in battle, onFaint() clears it from the battle
participant set. The revive item restored HP but never re-added the
mon to self.participants, so at awardExp() time the revived mon passed
the HP check but failed the participant gate -- getting no exp.

Fix: re-add the revived mon to battle.participants in the revive item
effect, so it's counted as a participant and receives its share of
experience at battle end.

Fixes #648
2026-08-02 23:22:34 +01:00
Shane McGovern 1f3aa4e111 Fix PP UP max PP not shown on stats/summary screen
The SummaryMenu (pause menu stats screen and in-battle stats screen)
displayed the base PP from the move definition as the max value,
ignoring PP Up bonuses. After using a PP UP, the screen would show
e.g. 6/5 instead of 6/6.

Fix: calculate maxPP with the PP Up bonus (basePP + ppUps * basePP/5),
matching the formula used everywhere else -- battle fight menus,
ETHER restore, Pokemon Center heal, link protocol, and save editor.

Fixes #641
2026-08-02 23:16:10 +01:00
Shane McGovern 3f88d1c49a Add 3X game speed and R2/L2 shoulder button speed hotkeys
Add 3X as a speed option between 2X and 4X in GameSpeed.LEVELS (#677).

Add controller hotkeys: rightshoulder (R2) cycles speed up through the
level list, leftshoulder (L2) cycles speed down. Keyboard equivalent
is hotkey 1 (cycles up). All hotkeys are gated during transitions,
scripted cutscenes, and link play (same guards as the color hotkey
at key 2).

The _cycleSpeed helper wraps the save-options update with the same
busy/overworld guard used by the existing color-cycle hotkey.

Fixes #677
2026-08-02 23:10:06 +01:00
bryanthaboi 5348ba1c65 Merge pull request #690 from spiritsnails/fix/faithful-ratio-mobile 2026-08-02 17:58:41 -04:00
bryanthaboi 2f35fd44a1 Merge pull request #693 from ShaneMcGovernIE/fix/celadon-diner-table-alias 2026-08-02 17:58:17 -04:00
bryanthaboi 470fe70f07 Merge pull request #696 from ShaneMcGovernIE/fix/oaks-lab-rival-exit-music 2026-08-02 17:57:58 -04:00
bryanthaboi 399bf557f3 Merge pull request #698 from ShaneMcGovernIE/fix/pc-back-and-hole-sfx 2026-08-02 17:57:44 -04:00
Shane McGovern fc2ca6cc26 Fix PC B-button navigation and hole-fall sound effect
Issue #695: Pressing B in PC submenus (BoxMenu, PlayerPC) was exiting
the entire PC session instead of returning to the main PC menu. The
three main-menu items (Bill's PC, player's PC, Prof. Oak's PC) were
missing keepOpen=true, so selecting one popped the main menu off the
stack. Added keepOpen to all three, matching the pattern already used
by BoxMenu and PlayerPC's own rows.

Issue #694: Falling through boulder holes in Seafoam Islands, Victory
Road, and Pokemon Mansion played no sound effect. Added Faint_Fall sfx
before every hole warp -- the scripted onStep holes in seafoam.lua,
story.lua, and story6.lua, plus the warp-tile-based hole detection in
OverworldController takeWarp. Faint_Fall is the companion to Faint_Thud
(already played when boulders fall into holes).

Fixes #695
Fixes #694
2026-08-02 22:11:15 +01:00
Shane McGovern d9d42ec956 Play rival encounter music when rival leaves Oak's Lab after battle
The parcel scene in Oak's Lab plays Music_MeetRival on both the rival's
arrival and departure (lines 144-146 in oaks_lab.lua), but the post-battle
onStep exit sequence only played the fanfare when the rival approached
(fixed in #596). It was missing when the rival walks out after the battle.

Add stop_music + play_music Music_MeetRival before the rival's exit
walk-out in both oaks_lab.lua and oaks_lab_yellow.lua, matching the
parcel scene's double-fanfare pattern from the original ROM.

Fixes #683
2026-08-02 21:49:56 +01:00
Shane McGovern 5e41c74682 Add tile aliases for LOBBY table blocks 45 and 49
The Celadon Diner uses three LOBBY table blocks that share tile 0x37 on
their flat surfaces. Only block 29 had the 0x37->0x5a BROWN alias; blocks
45 and 49 showed raw tile 0x37 in ROOF (blue-gray), creating a blue
square on the second/third tables with the Advanced Colors preset.

Add alias entries for blocks 45 (cells 13/14) and 49 (cells 1/2).

Fixes #689
2026-08-02 21:20:28 +01:00
spiritsnails 02ad846dfa fix: FAITHFUL RATIO works on Android and iOS
apply() returned false on its first line for mobile, so the option did
nothing there. A phone has no window to resize, so the lock caps the
render scale instead: the largest whole multiple of 160x144 the display
holds, centred, black around it.

Two parts beyond that. The scale is read off the display rather than from
the desktop's 1X-4X ladder, which named a different fraction of every
device and left the useful levels off the list; mobile shows ON or OFF.
And the world pass, which expands to cover the whole display so letterbox
becomes more map, is now sized against the locked viewport, so the lock
reaches the overworld instead of showing more of it.

Pixel perfect throughout, whole multiples only. Desktop and OFF are
unchanged. Renames the row to FAITHFUL RATIO on both platforms; the saved
key stays faithfulRes so existing settings carry over.
2026-08-02 12:44:38 -06:00
bryanthaboi 4e7eda65ed Merge pull request #685 from castdrian/metal 2026-08-02 14:34:08 -04:00
Adrian Castro 3fabe4f591 fix(ios): use square mobile icon in AltSource 2026-08-02 19:27:55 +02:00
bryanthaboi fa886899fc Merge pull request #653 from jherediagu/fix/oak-intro-name-confirmation 2026-08-02 13:11:51 -04:00
bryanthaboi a81126a03a Merge pull request #667 from castdrian/metal 2026-08-02 13:11:27 -04:00
bryanthaboi 164c555bb4 Merge pull request #669 from jherediagu/fix/battle-messages-use-rom-text 2026-08-02 13:10:15 -04:00
bryanthaboi e5926893a9 Merge pull request #670 from ShaneMcGovernIE/fix/oak-starter-jingle-668 2026-08-02 13:07:38 -04:00
bryanthaboi dbcaf705c2 Merge pull request #672 from ShaneMcGovernIE/fix/faint-animation-671 2026-08-02 13:05:23 -04:00
bryanthaboi f6809be81d Merge pull request #678 from spiritsnails/feat/ui-layout-option 2026-08-02 13:03:29 -04:00
spiritsnails 3ddf70888e test: ui_layout_option runs ROM-free
The row assertions called Data:load(), which needs data/generated/. The
T1/T2 tier runs without a ROM in CI, so the suite died on the import
rather than failing an assertion. Use T.fixtures.load() like the other
engine suites do.

Verified by moving data/generated aside and re-running: 20/20 with no
imported data present.
2026-08-02 10:54:04 -06:00
spiritsnails fe7dcf33ec feat: UI LAYOUT option, centered by default
Edge docking and zoom-linked UI scaling shipped as unconditional
behaviour. Both are departures from how the port composed the screen, so
they become a setting instead: UI LAYOUT = CENTERED (the default) or
DYNAMIC.

CENTERED is a fixed letterbox. Elements stay where they were drawn in the
160x144 canvas, and the UI does not follow the survey zoom, so screen
furniture neither moves nor resizes under the player. That is what the
pre-anchoring builds did. DYNAMIC is the current behaviour, unchanged.

Both halves matter together: gating only the anchoring would stop the
dialogue box moving but leave it resizing with the zoom, which is the same
complaint in a different form.

Gated at Renderer:setUIAnchor and Renderer:uiScale rather than at each
caller, so one switch covers the dialogue box, its YES/NO, the START menu
and anything anchored later, and no caller knows the option exists.
Game.dynamicUI answers true only for an explicit "dynamic", so a save
written before this keeps the layout it already had.

Independent of it, deliberately: BATTLE SIZE still works under either mode
(uiFill overrides the scale later, in endFrame), and a battle still holds
its own prompts inside its screen under DYNAMIC.

Also includes the Oak intro fix (previously #674): the speech fills white
over the UI canvas while its dialogue box docks to the window edge, so
under DYNAMIC black showed between the two. letterboxWhite closes it, and
the shrink beat's replica box rides the same anchor as the real box it
stands in for.
2026-08-02 10:47:27 -06:00