Commit Graph

771 Commits

Author SHA1 Message Date
1jamie e97def0c4f revereted attaching game spe3ed to sound.setRate so that audio plays the
same reguardless of speed
2026-08-30 16:51:18 -05:00
bryanthaboi c18fe7db50 Merge pull request #1918 from colsonrice/feat/gen2-export-map-objects 2026-08-30 14:05:46 -04:00
bryanthaboi ccd3abb3b3 Merge pull request #1984 from bryanthaboi/feat/uri-launch-args 2026-08-30 14:04:38 -04:00
1jamie a1adeb4676 Enhance PresentProbe and PresentSync for improved frame pacing and sync detection
- Updated PresentProbe to measure inter-present cadence accurately, ensuring that ambiguous signals default to FrameCap.
- Refined classification logic to prioritize stable cadence over unreliable hardware gating, enhancing the robustness of frame pacing.
- Adjusted PresentSync documentation to clarify probe isolation and its implications for sync confirmation.
- Improved test cases to validate the new cadence-based gating logic across various platforms, ensuring consistent behavior in frame pacing.
2026-08-30 12:11:56 -05:00
Adrian Castro 7795a6fd3e refactor(ios): rename app clips to web clips 2026-08-30 18:45:42 +02:00
Adrian Castro 18cf2257a8 fix(ios): capture launch options before scene creation 2026-08-30 18:45:42 +02:00
Adrian Castro 00a2486555 feat: add URI launch options and unified platform builds 2026-08-30 18:45:42 +02:00
1jamie 07071e334c Refine VSync detection and FixedStep logic for improved frame pacing
- Updated PresentSync to accurately measure present() block time, ensuring that the probe does not misclassify sync status due to FrameCap sleep artifacts.
- Enhanced FixedStep to implement a hard ceiling on catch-up debt, preventing input starvation during high-speed scenarios.
- Adjusted logic in Game and Game2 to utilize the new catchupLimit function for setting maxAccum, ensuring consistent behavior across speed multipliers.
- Improved test coverage for PresentProbe and PresentSync to validate the new logic and edge cases.
2026-08-30 11:29:43 -05:00
1jamie f82f565cae Fix to vsync measuring and detection and fixes fixestep so it snaps wallclock dt before applying speed
the display sync stuff i added was probing whether vsync was actually working, but it was measuring the wrong thing. during the probe we software-cap at 60 for safety, and the probe was looking at the gap between frames... which includes the sleep 😅 .....  so it always looked like sync was fine even when the driver was ignoring it. on something like the ally x on windows thats a real problem. vsync says on, probe says gated, we lift the cap and snap logic to the panel hz, then youre basically uncapped. at 2–4x that turns into hitching, dropped frames, dropped input, that weird half second freeze. speed swapping wasnt desyncing the driver, it was just making the bad path hurt more.
the solution is just we time present() itself now, not the gap after it, this way the warmup sleep cant fake a pass. if sync is unclear or broken we just stay on a capped 60 and dont snap logic. Also fixed fixedstep so it snaps wall clock dt before applying speed, so in general the 2-4x speed swaps dont screw the pacing math anymore
2026-08-30 11:12:19 -05:00
bryanthaboi 308605fd61 CLOSES #1944, CLOSES #1952, CLOSES #1967, CLOSES #1968, CLOSES #1969, CLOSES #1970, CLOSES #1972, CLOSES #1974, CLOSES #1975, CLOSES #1976, CLOSES #1977, CLOSES #1978, CLOSES #1979, CLOSES #1980, CLOSES #1981, CLOSES #1982, CLOSES #1983, CLOSES #1985, CLOSES #1986 2026-08-30 10:09:50 -04:00
bryanthaboi 5c850d670e CLOSES #1938, CLOSES #1940, CLOSES #1941, CLOSES #1943, CLOSES #1945, CLOSES #1946, CLOSES #1948, CLOSES #1949, CLOSES #1950, CLOSES #1953, CLOSES #1955, CLOSES #1957, CLOSES #1960, CLOSES #1961, CLOSES #1962, CLOSES #1963, CLOSES #1964 2026-08-30 08:04:35 -04:00
Colson Rice 532972de59 Carry the map's object state when a Gen 2 export moves maps 2026-08-29 21:27:25 -04:00
1jamie 2f6c5527f6 Add PresentSync integration for improved VSync handling
- Introduced PresentSync module to manage display synchronization.
- Updated love.run() to handle display changes and resizing events with PresentSync.
- Enhanced FrameCap logic to accommodate PresentSync requirements.
- Modified VSync to report effective states and handle driver quirks.
- Updated options menus to reflect PresentSync availability and restrictions.
- Added tests to validate PresentSync functionality and its interaction with VSync settings.

should address issue #1910 and issue #1958
2026-08-29 19:43:25 -05:00
bryanthaboi cba47b04e8 CLOSES #1928, CLOSES #1929, CLOSES #1930, CLOSES #1931, CLOSES #1933, CLOSES #1934, CLOSES #1935, CLOSES #1936 2026-08-29 09:00:17 -04:00
bryanthaboi 36ad59bdbb CLOSES #1590, CLOSES #1592, CLOSES #1625, CLOSES #1657, CLOSES #1698, CLOSES #1699, CLOSES #1700, CLOSES #1843, CLOSES #1875, CLOSES #1898, CLOSES #1902, CLOSES #1905, CLOSES #1906, CLOSES #1907, CLOSES #1908, CLOSES #1909, CLOSES #1912, CLOSES #1915, CLOSES #1917, CLOSES #1919, CLOSES #1920, CLOSES #1921, CLOSES #1922, CLOSES #1923, CLOSES #1924, CLOSES #1925, CLOSES #1926, CLOSES #1927 2026-08-29 06:59:38 -04:00
bryanthaboi 71200f21dc further online junk 2026-08-28 15:43:13 -04:00
bryanthaboi 2123bb007a Merge pull request #1903 from AverageConsumer/codex/gen2-deferred-caller-id-latest
fix(gen2): preserve caller identity for deferred phone calls
2026-08-28 14:18:10 -04:00
bryanthaboi 8ad755af06 online bugs 2026-08-28 14:15:51 -04:00
AverageConsumer 770e267ebe docs(gen2): describe stale caller state accurately 2026-08-28 18:29:54 +02:00
AverageConsumer 46ca4c819d fix(gen2): preserve deferred phone caller identity 2026-08-28 18:17:48 +02:00
bryanthaboi a9ce423bdc CLOSES #1275, CLOSES #1295, CLOSES #1384, CLOSES #1628, CLOSES #1677, CLOSES #1682, CLOSES #1691, CLOSES #1813, CLOSES #1822, CLOSES #1861, CLOSES #1867, CLOSES #1868, CLOSES #1869, CLOSES #1870, CLOSES #1871, CLOSES #1872, CLOSES #1874, CLOSES #1876, CLOSES #1878, CLOSES #1880, CLOSES #1881, CLOSES #1882, CLOSES #1884, CLOSES #1885, CLOSES #1886, CLOSES #1887, CLOSES #1888, CLOSES #1889, CLOSES #1890, CLOSES #1891, CLOSES #1892, CLOSES #1893, CLOSES #1894, CLOSES #1895, CLOSES #1896, CLOSES #1899, CLOSES #1900, CLOSES #1901
CLOSES #1275, CLOSES #1295, CLOSES #1384, CLOSES #1628, CLOSES #1677, CLOSES #1682, CLOSES #1691, CLOSES #1813, CLOSES #1822, CLOSES #1861, CLOSES #1867, CLOSES #1868, CLOSES #1869, CLOSES #1870, CLOSES #1871, CLOSES #1872, CLOSES #1874, CLOSES #1876, CLOSES #1878, CLOSES #1880, CLOSES #1881, CLOSES #1882, CLOSES #1884, CLOSES #1885, CLOSES #1886, CLOSES #1887, CLOSES #1888, CLOSES #1889, CLOSES #1890, CLOSES #1891, CLOSES #1892, CLOSES #1893, CLOSES #1894, CLOSES #1895, CLOSES #1896, CLOSES #1899, CLOSES #1900, CLOSES #1901
2026-08-28 12:15:36 -04:00
bryanthaboi 4849a5c9f6 Merge branch 'dev' of https://github.com/bryanthaboi/gen1recomp into dev 2026-08-28 10:14:46 -04:00
bryanthaboi 680db777a3 big online energy 2026-08-28 10:14:44 -04:00
bryanthaboi 8e9206b612 Merge pull request #1835 from campavao/rfc-0019-level-visible
engine: pokemon.level_visible, a level a mode can take off the screen (RFC 0019)
2026-08-27 13:38:59 -04:00
bryanthaboi dc99b1f8da Merge pull request #1852 from colsonrice/feat/gen2-save-export
Export Gen 2 cart saves
2026-08-27 13:37:43 -04:00
bryanthaboi 377f1f8a71 Merge pull request #1877 from 1Jamie/flatpak-lic-fix
update flatpak information that was left as template default
2026-08-27 13:36:27 -04:00
bryanthaboi 2441996d45 maybe better menu situation 2026-08-27 12:44:22 -04:00
1jamie 5e7dc556f5 update flatpak information that was left as template default
flatpak properly now marks as oss and points to site and gh
2026-08-27 10:30:44 -05:00
Colson Rice 71a5602b58 Export Gen 2 cart saves
Closes the round trip. Gold, Silver and Crystal progress writes back to a
cartridge save the real game boots.

Writes into the image the save came from. Gen 2 SRAM holds a great deal
this codec does not model and the real game trusts it on CONTINUE, so a
save with no cartridge behind it is refused rather than built from nothing.
That refusal is the known limitation: a game begun in this project has no
lineage to write back into yet.

The image lives BESIDE the slot as saves/<version>/<id>.cart, not in the
save table. 32 KB of binary in the serialized table is 40 KB of Lua source
reparsed on every save and load, for every imported slot, forever.

Only the primary copy is written. TryLoadSaveFile rewrites the backup from
the primary the moment VerifyChecksum passes, so a stale backup heals
itself on the first load and this does not need a second offset mapping.
Crystal arranges its backup differently from Gold and Silver, so that
matters.

What encode now reaches that it did not:

  * the bag, all four pockets, bucketed by each item's own pocket, plus
    wCurBox and the box names. It used to leave them at whatever the
    template carried, so a potion bought in a session never arrived.
  * the RTC footer. importToSlot truncates to 32768, so the image is kept
    at its full length and the footer is carried through. Dropping it
    resets the clock and costs the player daily events, the bug contest
    and a clock-adjustment penalty.
  * 0x1C-0x1E, pokerus and caught data. Left to the template they survive
    POSITIONALLY, so reordering the party gives slot 1 the previous
    occupant's caught level and location.

A bag that cannot be bucketed is refused rather than written short: without
the item table every item falls into ITEM, which holds 20, and a real bag
is bigger than that. Silently dropping the overflow would be worse than
the bug this fixes.

Two bugs in the text encoder that only real names caught: the cart's table
carries the ligature halves PO and KE, so a name containing "PO" became
0x70 where the cart had a plain P; and #glyph counts BYTES in Lua, so every
multi-byte glyph was dropped and came back as "?", which is NIDORAN and
every name with an accent.

Tests

The round trip CHANGES things first, in each place export has to reach, and
reads them back through a fresh decode. Exporting onto the buffer a save was
decoded from proves nothing, because every region encode does not write
matches by construction.

The fixture-gated audit exports a real cart save too, and pins that the
image keeps its size, RTC and all.

./scripts/test.sh passes end to end, and luacheck is clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 10:41:30 -04:00
Colson Rice ebd315b01e Import Gen 2 cart saves
Gold, Silver and Crystal battery saves import now. Export is still refused.

GenSave.lua is pokered's SRAM window and nothing else, which is why the
guard refusing Gen 2 was right to be there. This adds Gen2Save.lua beside
it, covering pokegold and pokecrystal.

Every offset is generated, not transcribed. tools/gen2_sram_offsets.py
reads pokegold.sym and pokecrystal.sym from a pret build and emits
Gen2Layout.lua, including the text table from constants/charmap.asm and
Crystal's backup-save layout. Gen 2 copies a contiguous WRAM block into
SRAM bank 1, so a field's file offset is sPlayerData + (wField -
wPlayerData); the generator asserts that relation against sPokemonData
rather than assuming it, and range-guards anything outside
sGameData..sGameDataEnd.

Gold and Crystal are separate tables because they disagree about nearly
every field. Reading a Crystal save with Gold's numbers gives a party
count of 133 and 13113 hours played, with a checksum that validates.

The cart stores numbers and the engine is keyed by name, so the codec
crosswalks species, moves and items through the generated tables the same
way GenSave.crosswalks does for Gen 1. Without that, an import looks
perfect and the engine cannot read a byte of it.

Shapes that have to match what the engine reads:
  * events is byte index -> packed byte, which Save.scrubEvents validates
    with tonumber. A set of booleans is silently emptied.
  * the bag is one flat save.inventory keyed by item id, which PackMenu
    buckets by each item's pocket. Nothing reads save.keyItems or
    save.balls, and the TM/HM pocket lands here too.
  * position carries the map id, or Save.summary falls through to
    save.spawn and the player resumes somewhere else at their old
    coordinates.
  * mon.status is an ItemEffects.STATUS_CLASS key with statusTurns beside
    it, nil when healthy. 0 is truthy in Lua.

A save the real cartridge would open is not refused: TryLoadSaveFile falls
back to VerifyBackupChecksum, so this does too. Crystal's backup is
contiguous and laid out like the primary; Gold and Silver split theirs
across three sections and have none to offer.

Three suites that pinned Gen 2 import being refused now pin what refuses
instead. Tests live in tests/engine so the ROM-free tier actually runs
them.

./scripts/test.sh passes end to end, and luacheck is clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 10:32:40 -04:00
bryanthaboi 8b56ec2d77 big egg energy 2026-08-27 07:40:02 -04:00
bryanthaboi f11724f08a Merge pull request #1841 from thibautbus/fix/gen2-pokedex-mod-text 2026-08-27 07:25:03 -04:00
bryanthaboi bd3e02986d test fix and iOS close hide 2026-08-27 07:11:43 -04:00
bryanthaboi eed246f645 CLOSES #1831, CLOSES #1834, CLOSES #1836, CLOSES #1860 2026-08-27 06:09:50 -04:00
bryanthaboi c9d06779d8 Merge pull request #1844 from colsonrice/fix/gen2-save-refusal-message
Refuse a Gen 2 cart save as a Gen 2 save, not as a broken Gen 1 one
2026-08-27 06:04:27 -04:00
bryanthaboi 48d8a4e922 CLOSES #1562, CLOSES #1804, CLOSES #1805, CLOSES #1810, CLOSES #1811, CLOSES #1814, CLOSES #1817, CLOSES #1818, CLOSES #1819, CLOSES #1821, CLOSES #1826, CLOSES #1827, CLOSES #1829, CLOSES #1833, CLOSES #1840, CLOSES #1842, CLOSES #1845, CLOSES #1846, CLOSES #1847, CLOSES #1848, CLOSES #1849, CLOSES #1853, CLOSES #1858, CLOSES #1862 2026-08-27 05:09:26 -04:00
Colson Rice 3a0a72102f Teach the bug420 SaveConvert double the method importToSlot now calls
CI caught this: tests/engine/save_import_retry_bug420.lua replaces
SaveConvert with a minimal double, and importToSlot now asks it
importSupported before it measures the bytes, so the double answered nil
and the call died.

The double stands in for the real module, so it grows with it. Answering
true keeps that case about the thing it is testing, which is that
importToSlot names the game whose cache to read.

./scripts/test.sh passes end to end locally now, every ROM-free tier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 15:47:08 -04:00
Colson Rice fe31293566 Refuse a Gen 2 cart save as a Gen 2 save, not as a broken Gen 1 one
#1832 reports a Crystal .sav failing to import with "save data checksum
invalid (main data checksum mismatch)". The save is fine. It is being
measured with the wrong generation's ruler.

SaveFileIO.importToSlot sends anything that is not exactly SAVE_SIZE into
mainChecksumValid, which is pokered's main-data checksum. Gen 2 carts are
MBC3+TIMER, so a real Gold, Silver or Crystal battery save carries an RTC
footer: 32786 bytes, never 32768. It misses the size test, falls into the
checksum branch, and is told it is damaged by a rule written for a
different game. Every real Gen 2 cart save takes that path, every time.

The generation check already exists, it just lives too late, inside
importSav and behind the size gate that has already rejected the save.
This lifts it into SaveConvert.importSupported and exportSupported and
asks before the bytes are measured. Both existing guards route through
the same predicates now, so the early caller and the late one cannot
describe the same game two different ways.

Nothing changes for Gen 1, and Gen 2 is still refused, for the real
reason and in a sentence a player can do something with.

tests/gen2_save_import_message_test.lua covers it. Against the code as it
stands it is 20/26, and the failures are the report itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 14:52:44 -04:00
thibautbus c8eeeaa1f6 Cover the #DEX registry projection fix with a targeted test 2026-08-26 19:44:00 +02:00
bryanthaboi 3e4772abf4 big palette energy 2026-08-26 12:04:06 -04:00
DESKTOP-8SRFDDM\cam95 49a5408c2d engine: pokemon.level_visible, a level a mode can take off the screen (RFC 0019)
A Pokémon's level is printed on four Gen 1 surfaces -- both battle
healthboxes, the party rows, and both status pages -- and every one of them
prints it unconditionally. There is no seam, so a mode that wants the number
gone has two options today and both are bad: paint over the engine's own
pixels from render.hud (four rectangles, a background shade to match, and
the palette flashes and healthbox slide to survive), or monkey-patch the
render modules from inside the sandbox, which works and is exactly what
CONTRIBUTING-mods.md tells mods not to do.

The motivating case is a battle royale that scales every party to a shared
rung rising with its fog: the number is the same for everyone, it changes on
a clock, and it reads as a threat it is not -- a Lv37 opponent looks
dangerous to a player who has not worked out that their own team is Lv37
too. A randomizer keeping an encounter unreadable, a challenge run that
forbids level-checking and a blind Nuzlocke want the same switch.

New hook `pokemon.level_visible`, taking the shape the presentation
predicates on the battle screen already use -- battle.status_hud_visible,
battle.bottom_ui_visible, battle.caught_marker_visible: consulted behind
Runtime.wantsHook, default visible, only an explicit false suppresses. It is
not named battle.* because a level is not a battle-only readout, and it
carries the surface that asked (battle.enemy / battle.player / party /
summary) so a mode can hide an opponent's level and keep its own.

src/ui/LevelDisplay.lua holds the one definition of "visible", so the four
call sites are a one-line guard each rather than four copies of the same
five lines that can drift apart.

No layout moves. Each site keeps its own hand-rolled PrintLevel rule
(home/pokemon.asm:335-345), it just asks first. Two details are deliberate:
a status condition still replaces the level on a healthbox exactly as in the
cart, so hiding a level never hides PSN or BRN (the guard is an elseif on
the existing status branch); and on status page 2 the <to> arrow is hidden
with the level it points at, because an arrow with nothing after it is half
a sentence.

Gen 1 only. The Gen 2 screens and the Gen 1 PC box list -- where the level
is part of a row label rather than a drawn field -- keep their own readouts
and do not consult the hook. Both are stated as follow-ups in the RFC and
beside the hook in docs/modding.md, so a mod author reads the limit before
depending on it.

Verification: tests/modkit/cases/pokemon_level_visible.lua covers the
contract through the public mod API; gate_hooks picks the hook up on its own
because it walks the live catalog; gate_meta_coverage is satisfied by the
change that introduces the seam, so it never enters the DEBT ledger.
tests/run_modkit.lua 33/33, tests/run_engine.lua 327/331 -- the same four
audio/hostshell suites fail unchanged on dev without this branch.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-26 10:53:09 -05:00
bryanthaboi 522d8cecf3 mega big fix for shaders 2026-08-26 10:29:16 -04:00
bryanthaboi fe338f47f2 Merge pull request #1820 from 1Jamie/app-image-fixes
feat(linux): ship raw x86 AppImage, Flatpak bundle, and dual-env curl
2026-08-26 07:40:48 -04:00
1jamie 0e8c93ec75 feat(linux): ship raw x86 AppImage, Flatpak bundle, and dual-env curl
Drop the pointless zip wrapper around the x86_64 AppImage, harden HostShell
so bundled curl keeps APPDIR libs while host curl scrubs LD_LIBRARY_PATH /
Steam LD_PRELOAD, and add a Flatpak channel with --device=all, bundled
curl, and AppStream releases metadata. Portable mode now probes writability
with a unique temp file and falls back soft on RO mounts / Flatpak.
2026-08-25 15:28:44 -05:00
bryanthaboi 1f12e4838f Merge pull request #1791 from HighDrexler/fix/mobile-required-import-streaming-v2
Fix/mobile required import streaming v2
2026-08-25 15:55:34 -04:00
bryanthaboi 7fa762871f Merge pull request #1798 from campavao/rfc-0015-battle-rule-hooks
engine: battle.style and catch.nickname rule hooks
2026-08-25 15:55:07 -04:00
bryanthaboi 4d805abb9c Merge pull request #1812 from 1Jamie/save-editor-safe-areas-move-search
fix(save-editor): mobile safe areas, move search picker, and release window titles
2026-08-25 15:54:19 -04:00
bryanthaboi 51908faabf fix(mods): stop the Gen 2 require facade answering the engine
bootGame's require("src.core.Game") was resolving to Gen2Compat's Game
facade after a Gold session, so the next Gen 1 boot got an empty proxy
whose rawget(load) is nil (iOS: "src.core.Game missing load after
reload"; Android: Game:load called on a nil value).

Two independent causes, both needed:

  * callerIsMod decided "not under src/" meant "a mod", and main.lua is
    not under src/, so the engine's own require was gated as a mod's.
    Root chunks main.lua and conf.lua are now matched exactly, which a
    mod's own mods/<id>/main.lua cannot collide with.

  * devShim.generation was only ever set, never cleared, so the
    generation Gold declared outlived Gold's session. Loader.endSession
    drops it from SessionLifecycle.endMountedSession, and the facade
    gate now requires generation == 2 rather than ~= 1.

Only platforms that return to the launcher in-process (Android, and iOS
since #1808) keep the shim alive across sessions; everywhere else the
process restart cleared it.
2026-08-25 15:14:47 -04:00
bryanthaboi 54c7a3aeae Merge pull request #1808 from 1Jamie/fix/mobile-lc
fix(mobile): in-process launcher return for Android and iOS
2026-08-25 15:14:05 -04:00
1jamie 93c7dff4d6 fix(tests): keep yellow-fixes suites ROM-free and dig-fade accurate
Drop Data:load from #1635/#1743 engine tests so headless CI without
data/generated/ can run them, and relax dig's #916 hide-ratio check for
white fade-in under spinDrop (#1644).
2026-08-25 12:58:03 -05:00