206 Commits

Author SHA1 Message Date
bryanthaboi af33c6e810 Merge pull request #1078 from MaxTomahawk/feat/scripted-battle-checkpoints
feat(mods): checkpoint scripted battle decisions
2026-08-11 21:22:40 -04:00
bryanthaboi e8eccfd4df Merge pull request #1080 from MaxTomahawk/feat/device-date-time
feat(mods): add shared local date and time formatting
2026-08-11 21:21:51 -04:00
bryanthaboi 241c3345bd Merge pull request #1086 from thibautbus/fix/yellow-pallet-oak-music
Bring Oak's Pallet Town cutscene in line with the original Yellow game
2026-08-11 21:21:36 -04:00
bryanthaboi 20e0692486 Merge pull request #1087 from MaxTomahawk/fix/battle-decision-settling
fix: settle real battle checkpoint decisions
2026-08-11 21:21:14 -04:00
bryanthaboi ae6cac89e1 G2 support 2026-08-11 11:53:30 -04:00
MaxTomahawk 41f02ecfbc fix: settle real battle checkpoint decisions 2026-08-11 17:13:29 +02:00
thibautbus cef286b170 Add unit tests
Regression coverage for the four Yellow Pallet Town fixes on this
branch:

- tests/parity_J.lua: the old-man-style demo bag shows x1 in Yellow
  (SimulatedInputBattleItemList), not just the pre-existing x50
  (pokered's OldManItemList) case.
- tests/engine/push_battle_transition.lua: Commands.pushBattle calls
  ctx.overworld:pushBattle when available and falls back with a
  logged warning otherwise. Logger.warn is spied (pcall-safe, always
  restored) rather than read off the shared Logger.history ring
  buffer, so the fallback checks don't leave noise behind for
  whatever else runs in the same process.
- tests/parity_yellow_pallet_pikachu.lua: drives the real onStep
  closure through the real StateStack/OverworldState, with no mocked
  battle -- Red never plays Music_MuseumGuy for this escort (Blue
  shares the same code path: onStep only branches on
  GameVersion.isYellow(), never isBlue(), so a separate Blue run
  would exercise nothing new), and Yellow's hold before the Pikachu
  battle is armed for exactly 2 frames before handing off to
  BattleTransition rather than a bare stack push. Stops there rather
  than also driving the demo battle to completion just to assert
  Music_MuseumGuy fires in Yellow (that fix's own coverage): the
  extra coupling to unrelated battle menu/bag/throw frame budgets
  wasn't worth it for one more assertion. That side stays manually
  verified. scenario() restores Game/GameVersion and re-inits
  StateStack on the way out.

All three pass standalone (luajit tests/<path>). The Yellow E2E test
hits the same pre-existing "Music.playMap is nil" gap three other
map-warping parity tests already hit inside the aggregated
tests/run_tests.lua run (missing data/generated/audio.lua in this dev
environment) -- confirmed by diffing the identical error text against
parity_warp_after_warp_step.lua, which also passes clean standalone.
2026-08-11 16:55:06 +02:00
MaxTomahawk 2c8c800584 feat: add shared date and time formatting 2026-08-11 11:17:32 +02:00
MaxTomahawk 882763cfe1 feat: checkpoint scripted battle decisions 2026-08-11 08:58:33 +02:00
bryanthaboi abf0f9e98a Merge pull request #1057 from ShaneMcGovernIE/shanemcgovernie-fix-index-unexpected-character
Handle non-JSON update responses
2026-08-10 17:07:43 -04:00
bryanthaboi c3855cebec more buggies CLOSES #960, CLOSES #961, CLOSES #968, CLOSES #995, CLOSES #1006, CLOSES #1009, CLOSES #1013, CLOSES #1021, CLOSES #1031, CLOSES #1044, CLOSES #1049, CLOSES #1050, CLOSES #1045,, 2026-08-10 17:06:24 -04:00
Shane McGovern f606840216 Handle non-JSON update responses
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-10 20:51:57 +01:00
bryanthaboi 9d73ff4110 yellow copyright diff 2026-08-10 14:27:00 -04:00
bryanthaboi 9234f12ed0 Merge remote-tracking branch 'origin/dev' into fix1038
# Conflicts:
#	docs/modding.md
2026-08-10 14:14:39 -04:00
bryanthaboi 9302bbcbf3 Merge branch 'fix1037' into fix1038
# Conflicts:
#	docs/modding.md
2026-08-10 14:12:21 -04:00
bryanthaboi 11af06a752 Merge remote-tracking branch 'origin/dev' into fix1023
# Conflicts:
#	docs/modding.md
2026-08-10 14:10:52 -04:00
bryanthaboi ca6bee64b9 Merge pull request #1042 from thibautbus/fix/android-ttf-dpi
Fix custom TTF rendering on Android
2026-08-10 14:09:07 -04:00
bryanthaboi b52d183dba Merge pull request #1024 from AverageConsumer/feature/semantic-pc-list-kinds
Mod API: expose semantic PC list kinds
2026-08-10 14:09:01 -04:00
bryanthaboi 23e1877a62 Merge pull request #1025 from AverageConsumer/feature/map-overview-detail-markers
Mod API: add detailed map overview data and POIs
2026-08-10 14:08:58 -04:00
bryanthaboi 12c2677dc2 title screen issues, audio issues, and replacing gf c 2026-08-10 14:00:26 -04:00
thibautbus 3c6242717e Use 1x TTF rasterizer on Android 2026-08-10 08:43:46 +02:00
david 3c71afb9fa Per-category GAME SPEED: overworld/battle/menu + core.logic_speed hook (RFC 0007)
GameSpeed is a single fast-forward multiplier applied uniformly to the
whole logic clock -- overworld walking, menu navigation and battle turns
all scale together. A player who wants 4X battles but 1X overworld (so a
cutscene or NPC dialogue doesn't blur past) has no way to get both.

Splits save.options.speed into speedOverworld/speedBattle/speedMenu, each
cycling independently, with an automatic migration so an existing save's
speed choice carries over. Game.speedCategoryInStack resolves which
category is active by walking the state stack (the same idiom
wideBattleInStack/fillScaleInStack already use), so a menu opened mid-
battle inherits battle speed rather than resetting to whatever "menu"
defaults to. Adds a new core.logic_speed hook so a mod can read or
override the resolved multiplier for the current frame regardless of
which category produced it, sitting after the link-play and run-argument
overrides so neither is a seam a mod can defeat.

RFC 0007 status: Proposed.
2026-08-09 20:13:21 -07:00
AverageConsumer d881997acd feat(mods): add detailed map overview POIs 2026-08-09 15:57:17 +02:00
AverageConsumer 8906133b93 feat(ui): expose semantic PC list kinds 2026-08-09 15:49:28 +02:00
AverageConsumer b9e3c4a689 feat(mods): expose battle UI visibility hooks 2026-08-09 15:46:33 +02:00
AverageConsumer 371edd1a02 feat(mods): expose map tile shading 2026-08-08 11:59:57 +02:00
bryanthaboi 983bea61aa Merge pull request #986 from MaxTomahawk/feat/mod-battle-checkpoints
feat: add persistent battle safe-point checkpoints
2026-08-08 05:46:34 -04:00
bryanthaboi af183d95ad Merge pull request #962 from AverageConsumer/feature/mod-world-map-overview
Mod API: expose a read-only active map overview
2026-08-08 05:42:55 -04:00
bryanthaboi 653b72d86a Merge pull request #925 from Bortlesboat/multiplayer-session-foundation
Add a reusable multiplayer session layer
2026-08-08 05:42:23 -04:00
MaxTomahawk c979685a1f test: cover switched battle party fidelity 2026-08-08 09:53:41 +02:00
MaxTomahawk 2ce612d5b1 test: prove deterministic battle checkpoint fidelity 2026-08-08 09:53:41 +02:00
MaxTomahawk 44a7910c69 feat: reconstruct standard battle continuations 2026-08-08 09:53:41 +02:00
MaxTomahawk 85c6fde443 feat: reconstruct battle checkpoints from data 2026-08-08 09:53:41 +02:00
MaxTomahawk 67b6dcc293 feat: capture data-only battle checkpoints 2026-08-08 09:53:41 +02:00
MaxTomahawk 24d9f279ec feat: expose settled battle checkpoint boundary 2026-08-08 09:53:41 +02:00
AverageConsumer c1e0685e97 feat(mods): expose read-only map overview 2026-08-07 20:20:57 +02:00
bryanthaboi 91a85a065b Merge pull request #927 from AverageConsumer/feature/second-screen-touch
Mod API: expose Android second-screen touch input
2026-08-07 12:41:48 -04:00
bryanthaboi 32e2c51365 Merge pull request #957 from ShaneMcGovernIE/shanemcgovernie-issue-916-trainer-sprite-at-end-of-fly-animation-9e5f77
Hide trainer sprite through the Fly/Dig warp fade (#916)
2026-08-07 12:41:05 -04:00
bryanthaboi 1992fdf398 Merge pull request #955 from ShaneMcGovernIE/shanemcgovernie-issue-945-cannot-edit-trainer-class-s-battle-theme-ad701b
Honor trainer battleTheme override (fixes #945)
2026-08-07 12:40:55 -04:00
bryanthaboi 59ac27971a Merge pull request #953 from ShaneMcGovernIE/shanemcgovernie-fix-reset-settings-bugs
saveOptions three-way merge: stop partial writes dropping launcher keys (#932)
2026-08-07 12:39:56 -04:00
bryanthaboi cd0ace2a39 Merge pull request #952 from MaxTomahawk/feat/mod-state-checkpoints
Add playthrough-scoped storage and stable overworld checkpoints
2026-08-07 12:39:32 -04:00
Shane McGovern 0d4a518a08 saveOptions three-way merge: stop partial writes dropping launcher keys (#932)
options.lua is a whole-file rewrite, so a caller handing saveOptions a
partial table (only the keys it changed) silently dropped every key it did
not mention: launcher-only keys like lastVersion, and keys the launcher set
(battleBg, tilt) all fell back to defaults.

saveOptions now reads the on-disk file first and folds caller-absent
values underneath before mergeOptions backfills defaults.  A table holding
every defaultOptions key is a full snapshot and stays authoritative, so the
fold is inert for all in-repo writers (every one passes loadOptions-ed
tables) and cannot resurrect the bindings/activeProfile deletions the
RESET REBINDS and mod-manager paths make on full tables.

Adds a regression suite (options_partial_write_bug932.lua) pinning the
merge, and updates the #828 suite's partial-write assertion, which now
expects lastVersion to survive a delta write.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-07 14:55:55 +01:00
MaxTomahawk 49954ec4ad fix: allocate playthrough identity only on demand 2026-08-07 15:01:18 +02:00
MaxTomahawk 726ed1102e feat: add opaque playthrough identity 2026-08-07 14:43:04 +02:00
Shane McGovern ce204aaf16 CLOSES #916: hide trainer sprite through the Fly/Dig warp fade
After the Fly departure animation finished (bird off-screen) and during
Dig/teleport, the trainer sprite popped back in standing at the old cell
for the whole 32-frame black fade-out before the transition.  The
player-hide guard only held while a departure animation was live:
flyAnim went nil the instant path2 completed and the teleportOut
countdown cleared the spin fields at 0, but startWarpTo's Transition
(not isOpaque) keeps the overworld drawing beneath the veil, and the
arrival animation is not armed until setMap's midpoint.

Add a playerHidden flag on OverworldState that bridges the gap:
- set when each departure completes (flyAnim path2 / teleportOut hit 0),
  immediately before the warp starts;
- cleared in startWarpTo's Transition enter callback, synchronously
  after setMap and before the arrival arms flyArrive / spinDrop, so the
  player is never drawable mid-fade and never bare on the landing frame;
- folded into both player-draw guards.

ROM-free regression test (tests/engine/warp_sprite_hidden_bug916.lua)
drives the REAL Transition + setMap headlessly for Dig and Fly and
asserts zero fade frames leave the player drawable bare (would have
observed 31/32 gap frames before the fix).  Runs in the CI headless T2
tier.

Dig spin timing/lift and the black fade color are left as-is (fade is
intentional per #607).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-07 11:14:20 +01:00
Shane McGovern ed8a89c5ce Honor trainer battleTheme override (fixes #945)
trainers.battleTheme validated and merged onto the trainer record but was
never read: battle music came solely from data.audio.battle[kind] where
kind is computeMusicKind()'s final/gym/trainer/wild.  Route both battle-
theme start sites through a single choke point:

- BattleState:playBattleTheme() cues Music.playBattle with the override
  (self.trainer.battleTheme via battleTheme()), defaulting to the kind
  when unset, so vanilla fights and #782's non-gym Giovanni are unchanged.
- BattleState:enter() and OverworldController:pushBattle() both call it.
- Music.playBattle gains an optional 4th song arg that overrides the kind
  default, and real call sites now populate the music.select trainerId.
- Victory jingles stay kind-based: a custom battle theme has no derivable
  win-variant.

New ROM-free T2 suite tests/engine/trainer_battle_theme_bug945.lua covers
mod load, override resolution, the choke point, and the nil-override
parity gate.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-07 10:53:42 +01:00
Shane McGovern d59a9522ee Fix mod update checks failing on non-JSON responses (#931)
The mod update and Find Mods feeds feed the raw HTTP body straight to
Json.decode. When the endpoint hands back something that is not JSON
(an HTML error page, a proxy/captive prompt, or a plain-text outage
message like "Exceeded secondary rate limit" -- usually still HTTP 200),
the decoder's "unexpected character 'E'" assert escaped through the
pcall and became the error message, blaming the parser instead of the
response.

Add Json.describeUnexpected() as a pre-decode content-type guard: it
returns nil for body shapes the endpoints actually publish (JSON object
or array) and otherwise a short message naming what the server sent
(HTML page / plain text / empty, with a preview). Wire it into
ModUpdate.parseReleases and ModIndex.parse, so both the sync and async
update-check paths surface the real answer instead of the parse error.
HTTP status was already checked upstream by HostShell.httpGet (non-2xx
becomes "HTTP <code> from <url> (...)"); this closes the remaining
"2xx but not JSON" gap everywhere, including bridge platforms that
expose no status or headers.

Add regression tests for plain-text, HTML, and empty bodies; strengthen
the ModIndex HTML soft-fail test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-07 10:49:10 +01:00
AverageConsumer a98562f46b feat(android): expose secondary-screen touch events 2026-08-06 22:56:13 +02:00
Andrew Barnes d50ea4d067 Route tournaments through multiplayer sessions 2026-08-06 16:50:30 -04:00
Andrew Barnes 52c96437fa Route link play through multiplayer sessions 2026-08-06 16:50:29 -04:00