From dae237f35904c4f7cad41cfe970572092113f96e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Sep 2026 20:32:49 +0000 Subject: [PATCH 1/3] Bump actions/cache from 4 to 6 Bumps [actions/cache](https://github.com/actions/cache) from 4 to 6. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7599bf5d..f3483727 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -162,7 +162,7 @@ jobs: name: shaderfx-bridge-linux-arm64 path: dist/native/linux-arm64 - name: Restore compiled arm64 dependencies - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: .bazinga/cache/linux-arm64 key: linux-arm64-deps-${{ hashFiles('scripts/linux-arm64/**', 'scripts/build_linux_arm64.sh') }} @@ -211,7 +211,7 @@ jobs: name: shaderfx-bridge-linux-x64 path: dist/native/linux-x64 - name: Restore the Flatpak runtime - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: ~/.local/share/flatpak key: flatpak-runtime-${{ runner.arch }}-${{ hashFiles('flatpak/*.yml', 'scripts/build_flatpak.sh') }} @@ -333,7 +333,7 @@ jobs: # Hash the build script too so changes to build flags invalidate the binary. - name: Restore finished TLS library id: tls-cache - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v6 with: path: dist/native/win-x64/gen1tls.dll key: native-tls-v2-${{ runner.os }}-${{ runner.arch }}-${{ steps.tls-env.outputs.image }}-dotnet8-${{ hashFiles('native/tls_dial/**', 'scripts/ci/build_gen1tls.ps1', '**/Directory.Build.*', '**/Directory.Packages.props', '**/NuGet.Config', '**/nuget.config', '**/global.json') }} @@ -353,7 +353,7 @@ jobs: if ($dll.Length -eq 0) { throw 'gen1tls.dll is empty' } - name: Cache finished TLS library if: steps.tls-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@v6 with: path: dist/native/win-x64/gen1tls.dll key: ${{ steps.tls-cache.outputs.cache-primary-key }} @@ -407,7 +407,7 @@ jobs: # inputs, build instructions, and the native environment affect reuse. - name: Restore finished bridge id: bridge-cache - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v6 with: path: dist/native/${{ matrix.plat }} key: shaderfx-binary-v2-${{ matrix.plat }}-${{ runner.arch }}-${{ matrix.glibc_triple }}-${{ steps.bridge-env.outputs.hash }}-${{ hashFiles('tools/shaderfx-bridge/**', '.cargo/**', 'rust-toolchain*', 'scripts/ci/shaderfx_bridge.sh') }} @@ -436,7 +436,7 @@ jobs: run: bash scripts/ci/shaderfx_bridge.sh verify - name: Cache finished bridge if: steps.bridge-cache.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@v6 with: path: dist/native/${{ matrix.plat }} key: ${{ steps.bridge-cache.outputs.cache-primary-key }} @@ -692,7 +692,7 @@ jobs: uses: actions/checkout@v7 - name: Restore pinned love-nx binaries - uses: actions/cache@v4 + uses: actions/cache@v6 with: path: .bazinga/love-nx/11.5-nx1 key: love-nx-11.5-nx1-${{ hashFiles('scripts/switch/love-nx-11.5-nx1.sha256') }} From 02e2f23f4afb3024b5f000b41b76b3c330187a44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Sep 2026 20:32:53 +0000 Subject: [PATCH 2/3] Bump signpath/github-action-submit-signing-request from 2 to 3 Bumps [signpath/github-action-submit-signing-request](https://github.com/signpath/github-action-submit-signing-request) from 2 to 3. - [Release notes](https://github.com/signpath/github-action-submit-signing-request/releases) - [Commits](https://github.com/signpath/github-action-submit-signing-request/compare/v2...v3) --- updated-dependencies: - dependency-name: signpath/github-action-submit-signing-request dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/windows-sign.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/windows-sign.yml b/.github/workflows/windows-sign.yml index 42d713ac..edfe4459 100644 --- a/.github/workflows/windows-sign.yml +++ b/.github/workflows/windows-sign.yml @@ -104,7 +104,7 @@ jobs: retention-days: 7 - name: Sign with SignPath - uses: signpath/github-action-submit-signing-request@v2 + uses: signpath/github-action-submit-signing-request@v3 with: api-token: ${{ secrets.SIGNPATH_API_TOKEN }} organization-id: a50673ca-cc8a-496c-a8bb-7313d9cadb8e From 93310885c75ceaf4813d511e39eac834ed33bb3b Mon Sep 17 00:00:00 2001 From: 1jamie Date: Sat, 26 Sep 2026 19:16:23 -0500 Subject: [PATCH 3/3] fix: enforce bike dismount on indoor maps and synchronize biking state across saves --- src/core/game3/item_use.lua | 53 ++++++++++----- src/core/game3/map.lua | 13 ++-- src/core/game3/player.lua | 16 ++++- src/core/game3/runtime.lua | 26 +++++++ src/core/game3/save_schema_firered.lua | 5 +- src/core/game3/scripting/natives_events.lua | 4 ++ src/world/gen2/Bike.lua | 6 +- tests/game3_cycling_road_bike_test.lua | 75 +++++++++++++++++++++ 8 files changed, 168 insertions(+), 30 deletions(-) diff --git a/src/core/game3/item_use.lua b/src/core/game3/item_use.lua index a8c12c24..d716e41f 100644 --- a/src/core/game3/item_use.lua +++ b/src/core/game3/item_use.lua @@ -431,28 +431,49 @@ end -- pokefirered/src/item_use.c:253 FieldUseFunc_Bike function ItemUse.useBike(session) local Player = require("src.core.game3.player") - -- pokefirered/src/item_use.c:261 - if Player.biking and Player.isOnCyclingRoad and Player.isOnCyclingRoad(session) then - return false, "bike", cant_dismount_bike_text() - end - - -- pokefirered/src/overworld.c:948 Overworld_IsBikingAllowed - local biking = map_header_flag(session, "bikingAllowed") - if biking == nil then biking = is_outdoor(session) end - if not biking then - return false, "bike", not_the_time(session) - end - -- pokefirered/src/item_use.c:276 ItemUseOnFieldCB_Bicycle - if not Player.biking then + if Player.biking then + -- pokefirered/src/item_use.c:261: If already on bike, cannot dismount on cycling road + if Player.isOnCyclingRoad and Player.isOnCyclingRoad(session) then + return false, "bike", cant_dismount_bike_text() + end + -- Dismounting is always allowed elsewhere (even if indoors) + Player.biking = false + if session then session.biking = false end + local Runtime = package.loaded["src.core.game3.runtime"] + local curSession = Runtime and Runtime.getSession and Runtime.getSession() + if curSession then curSession.biking = false end + local game = Runtime and Runtime.getGame and Runtime.getGame() + if game and game.save then + game.save.biking = false + if game.save.position then game.save.position.biking = false end + end + require("src.core.game3.audio").bikeMusic(false) + return true, "bike", nil + else + -- pokefirered/src/overworld.c:948 Overworld_IsBikingAllowed: Mounting only allowed where biking is permitted + local biking = map_header_flag(session, "bikingAllowed") + if biking == nil then biking = is_outdoor(session) end + if not biking then + return false, "bike", not_the_time(session) + end pcall(function() local Audio = require("src.core.game3.audio") local SE = require("src.core.game3.se_ids") if Audio and Audio.playSe then Audio.playSe(SE.SE_BIKE_BELL) end end) + Player.biking = true + if session then session.biking = true end + local Runtime = package.loaded["src.core.game3.runtime"] + local curSession = Runtime and Runtime.getSession and Runtime.getSession() + if curSession then curSession.biking = true end + local game = Runtime and Runtime.getGame and Runtime.getGame() + if game and game.save then + game.save.biking = true + if game.save.position then game.save.position.biking = true end + end + require("src.core.game3.audio").bikeMusic(true) + return true, "bike", nil end - Player.biking = not Player.biking - require("src.core.game3.audio").bikeMusic(Player.biking) - return true, "bike", nil end --- Check TM pre-flight compatibility and known moves matching retail FRLG. diff --git a/src/core/game3/map.lua b/src/core/game3/map.lua index b2c0b5c2..3e9aae9f 100644 --- a/src/core/game3/map.lua +++ b/src/core/game3/map.lua @@ -326,12 +326,14 @@ function Map.load(mod, game, mapId, opts) end end - local session = Runtime.getSession and Runtime.getSession() + local session = (Runtime.getSession and Runtime.getSession()) or (game and game.session) local save = game and game.save local Player = require("src.core.game3.player") local onCyclingRoad = Player.isOnCyclingRoad and Player.isOnCyclingRoad(session, x, y) - local wasBiking = Player.biking or (session and session.biking == true) - or (save and save.biking == true) + local wasBiking = (Player.biking == true) + if opts.initialLoad and not wasBiking then + wasBiking = (session and session.biking == true) or (save and save.biking == true) or false + end -- pokefirered/src/overworld.c:878 GetAdjustedInitialTransitionFlags local keepBike = false @@ -347,7 +349,6 @@ function Map.load(mod, game, mapId, opts) if onCyclingRoad and not (Player.surfing or Player.surfHopping) then keepBike = true end - Player.biking = keepBike end if session then @@ -365,6 +366,7 @@ function Map.load(mod, game, mapId, opts) save.position.x = x save.position.y = y save.position.facing = facing + save.position.biking = keepBike save.biking = keepBike end @@ -380,9 +382,6 @@ function Map.load(mod, game, mapId, opts) Player.reset(x, y, facing) end -- pokefirered/src/overworld.c:2145 SetPlayerAvatarTransitionFlags - if onCyclingRoad and not (Player.surfing or Player.surfHopping) then - keepBike = true - end Player.biking = keepBike Player.syncSavePosition(game) diff --git a/src/core/game3/player.lua b/src/core/game3/player.lua index 3f421f3e..e4f7b4c6 100644 --- a/src/core/game3/player.lua +++ b/src/core/game3/player.lua @@ -170,6 +170,9 @@ function Player.syncFromSession(session) if session.elevation ~= nil then Player.elevation = tonumber(session.elevation) or 3 end + if session.biking ~= nil then + Player.biking = (session.biking == true) + end end function Player.syncFromHost(game) @@ -180,6 +183,10 @@ function Player.syncFromHost(game) if p.elevation ~= nil then Player.elevation = tonumber(p.elevation) or 3 end + local save = game and game.save + if save and save.biking ~= nil then + Player.biking = (save.biking == true) + end end --- Write avatar coords into save.position (ferry / host save). No host entity mirror. @@ -189,10 +196,15 @@ function Player.syncSavePosition(game) save.position.x = Player.cellX save.position.y = Player.cellY save.position.facing = Player.facing + save.position.biking = (Player.biking == true) + save.biking = (Player.biking == true) local session = package.loaded["src.core.game3.runtime"] session = session and session.getSession and session.getSession() - if session and session.map then - save.position.map = session.map + if session then + session.biking = (Player.biking == true) + if session.map then + save.position.map = session.map + end end end diff --git a/src/core/game3/runtime.lua b/src/core/game3/runtime.lua index 641e9005..3574c624 100644 --- a/src/core/game3/runtime.lua +++ b/src/core/game3/runtime.lua @@ -193,9 +193,35 @@ function Runtime.start(mod, game, session, opts) if opts.alreadyOnMap then -- Stay on current host map; only ensure Space VM + depth-1 neighbors. local Map = require("src.core.game3.map") + local Player = require("src.core.game3.player") local def = game and game.data and game.data.maps and game.data.maps[session.map] Map.current = session.map Map.loadNeighborsDepth1(game, def) + + -- Enforce overworld biking permissions when adopting / resuming an existing map. + -- pokefirered/src/overworld.c:878 GetAdjustedInitialTransitionFlags + local onCyclingRoad = Player.isOnCyclingRoad and Player.isOnCyclingRoad(session, Player.cellX, Player.cellY) + local wasBiking = (Player.biking == true) or (session and session.biking == true) or (game and game.save and game.save.biking == true) + local keepBike = false + if wasBiking or onCyclingRoad then + local allowed = def and def.bikingAllowed + if allowed ~= nil then + keepBike = (tonumber(allowed) or 0) ~= 0 + else + local pair = def and (def.pair or (def.midLayout and def.midLayout.pair)) + keepBike = type(pair) == "string" and pair:find("outdoor", 1, true) ~= nil + end + if onCyclingRoad and not (Player.surfing or Player.surfHopping) then + keepBike = true + end + end + Player.biking = keepBike + if session then session.biking = keepBike end + if game and game.save then + game.save.biking = keepBike + if game.save.position then game.save.position.biking = keepBike end + end + Player.syncSavePosition(game) -- Space.onMapEnter already ran (or will run) from afterMap — don't double. log("adopted existing game3 map (no re-warp)") else diff --git a/src/core/game3/save_schema_firered.lua b/src/core/game3/save_schema_firered.lua index 726a6f09..0521b4ac 100644 --- a/src/core/game3/save_schema_firered.lua +++ b/src/core/game3/save_schema_firered.lua @@ -332,8 +332,9 @@ function Schema.toSaveTable(session) x = session.x, y = session.y, facing = session.facing, - biking = (session.biking == true) - or (package.loaded["src.core.game3.player"] and package.loaded["src.core.game3.player"].biking == true) + biking = (package.loaded["src.core.game3.player"] and package.loaded["src.core.game3.player"].biking ~= nil) + and (package.loaded["src.core.game3.player"].biking == true) + or (session and session.biking == true) or false, healMap = session.healMap, healX = session.healX, diff --git a/src/core/game3/scripting/natives_events.lua b/src/core/game3/scripting/natives_events.lua index 6e62e9a1..9b1f63d9 100644 --- a/src/core/game3/scripting/natives_events.lua +++ b/src/core/game3/scripting/natives_events.lua @@ -123,6 +123,8 @@ Events.HANDLERS = { if okP and Player and not Player.surfing then Player.biking = true Player.surfHopping = false + local game = package.loaded["src.core.game3.runtime"] and package.loaded["src.core.game3.runtime"]._game + Player.syncSavePosition(game) end require("src.core.game3.audio").bikeMusic(true, true) return false @@ -134,6 +136,8 @@ Events.HANDLERS = { Player.surfing = true Player.biking = false Player.surfHopping = false + local game = package.loaded["src.core.game3.runtime"] and package.loaded["src.core.game3.runtime"]._game + Player.syncSavePosition(game) end return false end, diff --git a/src/world/gen2/Bike.lua b/src/world/gen2/Bike.lua index 359ff4d3..ab2e258f 100644 --- a/src/world/gen2/Bike.lua +++ b/src/world/gen2/Bike.lua @@ -88,13 +88,13 @@ end -- is why nil covers PLAYER_SURF without a test of its own. function Bike.tryBike(ctx) ctx = ctx or {} - if not Bike.canUseHere(ctx.environment, ctx.collision) then return nil end local state = ctx.state or FieldMoves.PLAYER_NORMAL - if state == FieldMoves.PLAYER_NORMAL then return "mount" end - if state == FieldMoves.PLAYER_BIKE then + if FieldMoves.isBiking(state) or state == Bike.PLAYER_BIKE_ID or state == 1 then if ctx.alwaysOnBike then return "cant_get_off" end return "dismount" end + if not Bike.canUseHere(ctx.environment, ctx.collision) then return nil end + if state == FieldMoves.PLAYER_NORMAL or state == Bike.PLAYER_NORMAL_ID or state == 0 then return "mount" end return nil end diff --git a/tests/game3_cycling_road_bike_test.lua b/tests/game3_cycling_road_bike_test.lua index 5202416d..0d5838ed 100644 --- a/tests/game3_cycling_road_bike_test.lua +++ b/tests/game3_cycling_road_bike_test.lua @@ -147,6 +147,81 @@ check(saveTable.biking == true, "Schema.toSaveTable includes biking = true") local restoredSession = Schema.fromSaveTable(saveTable) check(restoredSession.biking == true, "Schema.fromSaveTable restores biking = true") +-- 10. Test dismounting on normal land and moving between outdoor maps keeps player on foot +Flags.setFlag(session.store, nil, 0x830, false) +session.flags[0x830] = false +Player.biking = true +ItemUse.useBike(session) +check(Player.biking == false, "dismounted bike on normal land") + +local palletDef = { + id = "FR_PALLET_TOWN", + bikingAllowed = 1, + pair = "outdoor", +} +local route1Def = { + id = "FR_ROUTE_1", + bikingAllowed = 1, + pair = "outdoor", +} +game.data.maps.FR_PALLET_TOWN = palletDef +game.data.maps.FR_ROUTE_1 = route1Def +Map.load(nil, game, "FR_ROUTE_1", { x = 10, y = 35, facing = "up" }) +check(Player.biking == false, "moving from Pallet Town to Route 1 while on foot leaves Player on foot") +Map.load(nil, game, "FR_PALLET_TOWN", { x = 10, y = 0, facing = "down" }) +check(Player.biking == false, "moving back to Pallet Town leaves Player on foot") + +-- 11. Test saving while on foot preserves biking = false +local onFootSave = Schema.toSaveTable(session) +check(onFootSave.biking == false, "Schema.toSaveTable records biking = false when on foot") +local restoredFoot = Schema.fromSaveTable(onFootSave) +check(restoredFoot.biking == false, "Schema.fromSaveTable restores biking = false when on foot") + +-- 12. Test entering indoor map (e.g. Silph Co) while biking forces dismount to foot +local silphDef = { + id = "FR_SILPH_CO_1F", + bikingAllowed = 0, + pair = "indoor", +} +local saffronDef = { + id = "FR_SAFFRON_CITY", + bikingAllowed = 1, + pair = "outdoor", +} +game.data.maps.FR_SILPH_CO_1F = silphDef +game.data.maps.FR_SAFFRON_CITY = saffronDef + +session = Runtime.getSession() or session +Player.biking = true +session.biking = true +Map.load(nil, game, "FR_SILPH_CO_1F", { x = 18, y = 21, facing = "up" }) +check(Player.biking == false, "entering Silph Co 1F forces Player.biking = false") +check(session.biking == false, "session.biking is false in Silph Co") + +-- Exiting Silph Co back to Saffron City keeps player on foot (not re-mounted) +Map.load(nil, game, "FR_SAFFRON_CITY", { x = 18, y = 22, facing = "down" }) +check(Player.biking == false, "exiting Silph Co to Saffron City keeps Player on foot") + +-- 13. Test loading directly into Silph Co via alreadyOnMap forces dismount to foot +Player.biking = true +session.biking = true +session.map = "FR_SILPH_CO_1F" +game.save = { biking = true, position = { map = "FR_SILPH_CO_1F", biking = true } } +Runtime.start(nil, game, session, { alreadyOnMap = true }) +check(Player.biking == false, "loading directly into Silph Co with alreadyOnMap clears Player.biking") +check(session.biking == false, "session.biking cleared to false on Silph Co load") + +-- 14. Test Gen 2 Bike.tryBike allows dismounting when in indoor environment +local Gen2Bike = require("src.world.gen2.Bike") +local dismountAction = Gen2Bike.tryBike({ + state = 1, -- PLAYER_BIKE + environment = "INDOOR", + collision = 0, + alwaysOnBike = false, +}) +check(dismountAction == "dismount", "Gen 2 Bike.tryBike allows dismount in INDOOR environment") + print(string.format("=== RESULTS: %d passed, %d failed ===", passed, failed)) if failed > 0 then os.exit(1) end +