Commit Graph

1500 Commits

Author SHA1 Message Date
github-actions 37b3a2e591 chore(ios): update app-repo.json [skip ci] 2026-09-02 09:54:52 -04:00
bryanthaboi a86f349e64 Merge pull request #2110 from bryanthaboi/dev
CLOSES #2080, CLOSES #2082, CLOSES #2084, CLOSES #2085, CLOSES #2086,…
v0.2.50
2026-09-02 09:33:22 -04:00
bryanthaboi d6c74bb7bb CLOSES #2080, CLOSES #2082, CLOSES #2084, CLOSES #2085, CLOSES #2086, CLOSES #2090, CLOSES #2091, CLOSES #2093, CLOSES #2094, CLOSES #2095, CLOSES #2099, CLOSES #2100, CLOSES #2101, CLOSES #2102 2026-09-02 09:18:54 -04:00
github-actions fde3ce3771 chore(ios): update app-repo.json [skip ci] 2026-09-02 07:01:57 -04:00
bryanthaboi 70db453f6a Merge pull request #2105 from bryanthaboi/dev v0.2.49 2026-09-02 06:48:16 -04:00
bryanthaboi 8ca1e08a86 Merge pull request #2097 from thibautbus/fix/gen2-move-effects-validation 2026-09-02 06:30:08 -04:00
bryanthaboi 50da140c02 Merge pull request #2056 from thibautbus/fix/complete-translation-coverage-v0241 2026-09-02 06:29:57 -04:00
bryanthaboi 51801a27bc CLOSES #2092, CLOSES #2098 2026-09-02 05:58:45 -04:00
thibautbus 56a9a3f0b4 Widen Gen 2 move_effects registration to the movedex's own effect ids
Battle.MOVE_EFFECT_RECORDS only lists effects with a standalone handler --
by design, per its own comment: a move whose effect is just "deal damage"
(EFFECT_NORMAL_HIT and the multi-hit/recoil/drain families) has none and
falls through to the generic damage path. But src/mods/Schemas.lua's
`moves.effect = f.id("move_effects")` cross-check treats move_effects as
the complete id space for the field regardless of generation, so every
"full" effect the real Gold/Silver movedex uses read as a dangling
reference the instant any mod's `moves` patch touched the registry --
caught building the Gold/Silver translation mod, whose move_names patch
(name only, never effect) was enough to trigger the scan on all ~130
moves.

registerMoveEffectsInto now also registers a bare `{kind="primary"}`
marker for every effect id data.moves actually uses that MOVE_EFFECT_RECORDS
doesn't already cover. Both of moveEffectRecordFor's call sites already
treat a handler-less record exactly like a missing one (nil-checking
.run/.status before use), so this is a validation-only change with no
battle behavior difference -- a real typo in a mod's own effect patch is
still caught, since the widened set is seeded from the pre-merge data.moves,
not from whatever a mod patches in afterward.
2026-09-02 11:43:30 +02:00
github-actions 74763043d4 chore(ios): update app-repo.json [skip ci] 2026-09-01 20:23:08 -04:00
bryanthaboi 5a3a9cba3b Merge pull request #2081 from bryanthaboi/dev v0.2.48 2026-09-01 20:08:52 -04:00
bryanthaboi 93faf0cb81 CLOSES #2076, CLOSES #2077 2026-09-01 19:54:00 -04:00
bryanthaboi 700331cb8f CLOSES #2057, CLOSES #2058, CLOSES #2059, CLOSES #2060, CLOSES #2061, CLOSES #2062, CLOSES #2063, CLOSES #2064, CLOSES #2066, CLOSES #2067, CLOSES #2069, CLOSES #2070, CLOSES #2072 2026-09-01 19:03:13 -04:00
github-actions f90b4bb699 chore(ios): update app-repo.json [skip ci] 2026-09-01 16:12:58 -04:00
bryanthaboi cd3caca9a7 Merge pull request #2074 from bryanthaboi/dev
goia android
v0.2.47
2026-09-01 15:57:08 -04:00
thibautbus 277a705375 Route the RBY OPTION screen and remaining type-name draws through translations
TypeChart.displayName(typeId, data) already resolved a merged type_chart
translation for the Gen 2 Summary screen, but the Gen 1/RBY SummaryMenu and
HallOfFame call sites never passed their data table, so opening either
screen before the first battle fell back to the untranslated TypeChart.TYPES
table even with a translation mod loaded.

WideBattle.lua's and this file's own in-battle move-details panels get the
same explicit data argument for the same call convention, though it is a
no-op there today: battle.data/self.data is game.data by reference, the
same table TypeChart.load(game.data) already caches from at battle start,
so both paths already agreed before this change.

The RBY OPTION screen's ZOOM, MAX FPS and the three GameSpeed rows also drew
their dynamic value labels straight from Zoom.offsetLabel/FrameCap.label/
GameSpeed.levelLabel, bypassing Strings() entirely, unlike the Gen 2 OPTION
screen's equivalent rows added earlier in this branch.
2026-09-01 21:52:55 +02:00
thibautbus f07ebfe423 Route remaining Gen2 UI text through translations
CELADON_TM/MON and GOLDENROD_TM/MON stored each Game Corner row as one
hand-padded "NAME    COST" literal; a translated name of a different length
than the English original shifted or overlapped the price that used to be
right-aligned by the padding alone. Split each row into a translatable name
and the existing numeric cost field, print them as two separate calls (the
cost right-aligned against a fixed priceRight column per counter, the same
column-aware approach MartMenu.printPriceOpaque already uses), and clamp the
name to the tile budget before the price column with Font.split (glyph-aware,
so a <PK><MN> macro or multi-byte UTF-8 character -- including one whose
expansion straddles the clamp boundary -- is never cut mid-sequence).

TEXTS (SlotMachine.lua) kept a hand-typed English line array next to each
entry's Strings.source() template; derive the array from source once at load
instead, and cache localizedLines()'s parsed split per source table so the
bet/result screens do not re-run the same gmatch split every draw() call.

ContestMenu.TEXT.alreadyCaught's line split now uses the same gmatch loop
the rest of this file's line-parsing already uses (an anchored ^(.-)\n(.*)$
match assumed exactly one \n and left a nil hole when a translation merges
the two lines into one clause).

Four of PrizeMenu.TEXTS' Game Corner vendor messages (Celadon's and
Goldenrod's prize-vendor intros, Goldenrod's quit line, and the coin
vendor's no-COIN-CASE refusal) used \f where the real cart text
(poke-corpus GoldSilver, e.g. gs.CeladonGameCornerPrizeRoom.
CeladonPrizeRoom_PrizeVendorIntroText) ends in \v: a plain page clear
instead of a scroll, so the vendor's last line appeared alone with no
lead-in instead of continuing under the previous one. Found and confirmed
against the corpus while auditing this branch for the same class of bug as
CenterPcMenu.lua's \n-vs-\v fix.
2026-09-01 21:52:55 +02:00
thibautbus 50b29ad83e Localize Gen2 PC and storage text
messagePages() only split on \n (line) and \f (paragraph), so a translated
message needing a \v scroll-continue break -- the same marker PackMenu and
PrizeMenu's messages already rely on via CommonText.pages -- rendered wrong
here: no line ever scrolled. Delegate to CommonText.pages, the same shared
page-break implementation PackMenu and PrizeMenu already use for their own
messages, instead of a second, incomplete reimplementation local to this
file.

CommonText.pages() also only treats \n as the box's second row, not a page
break, unlike the old local messagePages(), which grouped every two
\n-separated lines into a page regardless of \f. The one BOX_FAILURE_SOURCES
literal with a third line via a second bare \n ("You'll need a\nPOKéMON to
call\nwith.") needed \f instead, the marker every other multi-page message
in this file already uses for the same transition (see RELEASED just
above); pinned with a test.

CenterPcMenu.lua's own TEXT.noMon had the identical \n-vs-\v bug: the
Pokecenter PC's empty-party refusal (_PokecenterPCCantUseText, "ends in
cont" per the comment already on this line) needs \v to scroll "have a #MON
to" up and land "use this!" under it, not a third bare \n line, which
pagesOf() (this screen's own \f/\v-aware paginator, unaffected by the
messagePages() bug above) renders as a lone one-line page instead -- caught
by gen1recomp/dev's own independent fix to the same line while rebasing this
branch onto dev, and confirmed against tests/gen2_pc_screens_test.lua's
scroll assertions. The French/German/Spanish/Italian/Japanese/Korean
overrides already carry the correct \v in their translated values; only the
lookup key needed the same fix, made in gen1recomp-translation-mods
alongside this commit.
2026-09-01 21:52:55 +02:00
thibautbus 62e13556f5 Localize Gen2 options text 2026-09-01 21:52:55 +02:00
thibautbus c5008afd5d Localize Gen2 battle text 2026-09-01 21:52:50 +02:00
thibautbus 23aeda4871 Expose localized Gen2 content registries
GEN2_STATUS_IDS (psn/brn/frz/par/paralysis/slp -> Gen 2's own registry ids)
was declared verbatim in both PartyMenu.lua and SummaryMenu.lua; move it to
Status.GEN2_ID_ALIASES so a future status alias fix only has one copy to
update.

SummaryMenu.TYPE_NAMES was left behind after this branch switched its two
former internal uses to the shared TypeChart.displayName/DISPLAY_NAMES; it
has no remaining callers anywhere in src/ or tests/.
2026-09-01 21:52:42 +02:00
bryanthaboi f036197fd3 Merge pull request #2073 from 1Jamie/android-xbox-vsync
disables adaptive vsync as an option since few platforms support it and it causes issues on ones that incorrectly detect it as a supported
2026-09-01 15:49:21 -04:00
1jamie 5342fcbede disables adaptive vsync as an option since few platforms support it and
it causes issues on them
2026-09-01 14:40:50 -05:00
thibautbus 46c386b19e Route remaining RBY text through translations 2026-09-01 21:08:31 +02:00
github-actions 4cc736c4ee chore(ios): update app-repo.json [skip ci] 2026-09-01 14:18:15 -04:00
bryanthaboi a495093f14 Merge pull request #2065 from bryanthaboi/dev
big ol buggies
v0.2.46
2026-09-01 14:02:27 -04:00
bryanthaboi 2f9a920f4d Merge pull request #1825 from BountyHunterKanden/encounter-table-preview
Add API seam for mods to read/give information about altered encounter tables
2026-09-01 13:50:11 -04:00
bryanthaboi 23f59b7868 Merge pull request #2036 from 1Jamie/android-xbox-vsync
fix(present): android + xbox uwp nests that dont fake being desktop windows
2026-09-01 13:49:17 -04:00
bryanthaboi 45742481bf CLOSES #2025, CLOSES #2029, CLOSES #2030, CLOSES #2031, CLOSES #2033, CLOSES #2034, CLOSES #2035, CLOSES #2038, CLOSES #2040, CLOSES #2041, CLOSES #2042, CLOSES #2043, CLOSES #2044, CLOSES #2045, CLOSES #2046, CLOSES #2048, CLOSES #2049, CLOSES #2050, CLOSES #2051, CLOSES #2052, CLOSES #2053, CLOSES #2054 2026-09-01 13:45:00 -04:00
1jamie 19b2b688a8 fixed present sync so android/uwp dont get treated like desktop windows, android is bufferqueue backpressure and xbox uwp is angle under the console compositor; leaving vsync on while we soft-cap was stopping the swapchain from ever locking clean. now they get their own nests, DISPLAY by default so the probe can actually engage, and fail-closed silences the driver so framecap can pace alone. 2026-08-31 16:41:42 -05:00
bryanthaboi 1c8cf6c927 Merge pull request #1973 from mleo2003/fix/checkpoint-poison-steps
fix(checkpoint): keep poisonSteps across a restore (#1971)
2026-08-31 14:34:34 -04:00
github-actions 76a63a4ea1 chore(ios): update app-repo.json [skip ci] 2026-08-31 12:47:09 -04:00
mleo2003 25073d9ebe fix(checkpoint): keep poisonSteps across a restore (#1971)
`Checkpoint.restore()` rejected most valid checkpoints:

    Checkpoint restoration failed: restored state differed at $.save.poisonSteps

poisonSteps is a plain step counter -- (poisonSteps + 1) % 4 on EVERY step,
not only while a mon is poisoned (OverworldController:applyFieldPoison) -- so
it is non-zero three steps out of four in ordinary play.

Installing the restored world re-enters the map, and the map-enter path zeroes
the counter, correctly mirroring ClearVariablesOnEnterMap. Checkpoint.restore
then re-captures the applied state and compares it against the checkpoint, so
the field it had just discarded failed the comparison and the whole restore
rolled back.

A restore is not a map entry from the player's point of view: the counter
belongs to the state being restored. Carry it across the push.

Why this stayed hidden: the two autosave triggers a checkpoint consumer
naturally uses, map.entered and player.warped, are emitted from inside the
very paths that zero the counter (OverworldController 377/572 and 4664/4783),
so those captures hold 0 and restore cleanly. Only a capture taken at an
arbitrary step -- a manual quicksave, or one tied to the ordinary SAVE --
carries a non-zero value. The T4 title-checkpoint tier misses it for the same
reason: its save is fresh, so the counter is already 0.

The comment above the push claimed Checkpoint.resume was this method's only
caller. Both callers arrive through Checkpoint.apply, which serves resume from
the title session and restore from a settled runtime; that is exactly why the
map-entry side effects matter here. Corrected.

tests/engine/restore_poison_steps_bug1971.lua covers 1..3 and 0 across a
restore, and pins the two behaviours that must NOT change: a plain map entry
still zeroes the counter, and a seamless connection crossing still carries it.
It fails on main (3/6, "got 0, want 3") and passes with this change.
2026-08-31 09:46:45 -07:00
bryanthaboi 9e0a573375 Merge pull request #2027 from bryanthaboi/dev v0.2.45 2026-08-31 12:33:14 -04:00
bryanthaboi 998cb03d4c CLOSES #1947, CLOSES #1987, CLOSES #1989, CLOSES #1994, CLOSES #1996, CLOSES #1998, CLOSES #2010, CLOSES #2011, CLOSES #2012, CLOSES #2013, CLOSES #2014, CLOSES #2015, CLOSES #2017, CLOSES #2018, CLOSES #2019, CLOSES #2020, CLOSES #2021, CLOSES #2022 2026-08-31 12:05:42 -04:00
github-actions 48804306eb chore(ios): update app-repo.json [skip ci] 2026-08-31 08:57:10 -04:00
bryanthaboi aa87b2f6fa Merge pull request #2023 from bryanthaboi/dev
requested hooks and online connection fix
v0.2.44
2026-08-31 08:42:24 -04:00
bryanthaboi b9d14e425c hooks 2026-08-31 08:41:02 -04:00
bryanthaboi f4424ea4a0 rquesated hooks 2026-08-31 07:56:27 -04:00
bryanthaboi 22f3b61c6b online connectionc fix 2026-08-31 07:25:10 -04:00
github-actions a845140727 chore(ios): update app-repo.json [skip ci] 2026-08-30 21:59:18 -04:00
bryanthaboi b0011492c9 Merge pull request #2009 from bryanthaboi/dev v0.2.43 2026-08-30 21:45:41 -04:00
bryanthaboi d18aab2086 Merge pull request #2008 from 1Jamie/low-power-fps-fix 2026-08-30 21:39:13 -04:00
bryanthaboi f600677874 Merge pull request #2007 from laaledesiempre/fix/main-thread-jitter
fix(core): cut per-frame allocation churn and GC pressure on the main loop
2026-08-30 21:38:09 -04:00
bryanthaboi b7c342ab28 Merge pull request #2006 from laaledesiempre/fix/pokeport-audio-rate
fix(audio): honor POKEPORT_AUDIO_RATE in chip synth
2026-08-30 21:38:06 -04:00
1jamie 7984fe296c Implement idle render governor and update vsync handling for handheld builds
- Introduced the `POKEPORT_IDLE_AFTER` and `POKEPORT_IDLE_FPS` environment variables to manage presentation rates on static screens, allowing game logic and audio to maintain full speed.
- Updated the vsync handling to keep it enabled across all platforms, including KMSDRM handhelds, to leverage PresentSync for improved cadence and pacing.
- Enhanced FrameCap logic to ensure proper handling of performance caps in handheld environments, preventing unnecessary software pacing when hardware capabilities are sufficient.
- Adjusted documentation to reflect these changes and their impact on power efficiency and performance.
2026-08-30 19:47:23 -05:00
github-actions e59bd41bcb chore(ios): update app-repo.json [skip ci] 2026-08-30 20:01:59 -04:00
laaledesiempre 3659252a88 fix(audio): honor POKEPORT_AUDIO_RATE in chip synth
The sbc/portmaster launcher exports POKEPORT_AUDIO_RATE=22050 and
docs/linux-arm-sbc.md describes it as halving synthesis CPU on
Cortex-A53 handhelds, but nothing in the engine read it: ChipSynth
always rendered at 44100 Hz, and snapTicks baked that rate in as the
integer rational 1470/512 (so once the rate does change, music plays
at half tempo/pitch unless snapTicks follows).

Read the env var at module load (validated to 8000-48000, 44100
fallback) and make snapTicks rate-aware.  At 44100 the new form is
arithmetically identical to the old one
((ticks*44100 + 7680)/15360 == (ticks*1470 + 256)/512), so desktop
behavior is unchanged.

Tested on an RG351MP (RK3326, dArkOS): correct music tempo and pitch
at 22050 Hz, halved synthesis work on the audio worker.
2026-08-30 20:57:02 -03:00
laaledesiempre a4bbcc10ab fix(core): cut per-frame allocation churn and GC pressure on the main loop
On weak single-core handhelds (RK3326/RG351MP) the main thread sits at
100% CPU, so every millisecond of jitter drops a frame: constant
stuttering while walking even at PERFORMANCE LOW with all mods off,
while average CPU usage stays unchanged.  Three sources of per-frame
garbage and pause on the hot path:

- Game:step / Game:logicSpeed passed inline closures to ModRuntime.call,
  allocating a fresh function 60 times per second.  Hoist both to
  module-level locals; behavior is identical.
- Game:update advanced the incremental collector every rendered frame.
  Space it to every 4th frame: the explicit frees the comment refers
  to still do the heavy lifting, collection stays incremental, and the
  stepping itself stops competing with the 16.6ms frame budget.
- checkEmergencyQuit called love.joystick.getJoysticks() (a fresh
  table) twice per frame to guard a 5-second hold combo.  Cache the
  joystick list and refresh it once per second; a 1s hotplug delay is
  irrelevant against a 5s hold.

Tested on an RG351MP (RK3326, dArkOS, v0.2.41): eliminates the
constant walking stutter with all mods off, and with 11 mods
re-enabled.  Average CPU is unchanged (the cost was variance, not
load), which matches the diagnosis.
2026-08-30 20:56:51 -03:00
bryanthaboi 17f36f48e4 Merge pull request #2000 from bryanthaboi/dev v0.2.42 2026-08-30 19:47:12 -04:00