The linux-arm64 AppImage, the SBC PortMaster zip and the RG34XXSP zip
shipped LuaJIT 2.1.0-beta3 (2017): Debian bullseye's libluajit-5.1-dev for
the AppImage, PortMaster's love_11.5 runtime library for the zips. With
the JIT enabled, beta3 segfaulted 3/3 runs on a TrimUI Brick; LuaJIT v2.1
commit c6ffc141 ran a 26-minute JIT-on soak without a crash.
- Build LuaJIT c6ffc141 from a sha256-pinned tarball in a digest-pinned
debian:bullseye (glibc 2.31) with the same apt sources as the AppImage
builder; compile with PREFIX=/usr so no build path is baked into
package.path, stage-install, and verify before use.
- AppImage: compile it inside the builder instead of libluajit-5.1-dev,
bundle it with jit/*.lua and its license; cache keys include the LuaJIT
commit, recipe hash and Dockerfile hash.
- PortMaster zips: replace libs.aarch64/libluajit-5.1.so.2 with the pinned
build, pin the PortMaster runtime to commit 715f50fd with sha256 checks
(rg34xxsp followed main), ship LuaJIT-COPYRIGHT, verify the finished zip.
- verify_luajit.sh gates every package: version string, not beta, aarch64
ELF, /usr module path, max GLIBC <= 2.31; offline selftests cover it and
the PortMaster helpers.
- CI/release build LuaJIT once on an arm64 runner and pass it to the zip
jobs via GEN1RECOMP_LUAJIT_LIB.
The JIT stays off by default (LÖVE's jitsetup disables it on arm/arm64).
POKEPORT_JIT=1 needs stability testing on H700, RK3566 and A133P devices
before it can be enabled by default.
* Add native Black and White battle sprite importer and shared provider
* Reduce Gen 5 import scheduling and duplicate atlas work
* Play long Gen 5 idle animations from independent part tracks
* Skip part tracks when a late frame cannot be composed
* Fix importer picker lifecycle, Xbox formats, and CI coverage
* Fix Linux Gen 5 test discovery and picker regression checks
* Preserve original Gen 5 sprite resolution in provider API 2
* Trim importer comments and documentation
The #DEX page prints the US cart's height in feet and inches, with the foot and inch tiles, and its weight in pounds; the localized carts print metres and kilograms ("TAI ???m", "PDS ???kg"). A mod can now give a species its metric height and weight the way it can on Red/Blue: mod.content.pokemon:patch(id, { dexEntry = { heightM = ..., weightKg = ... } }). src/core/gen2/PokedexText.lua copies them onto the #DEX entry beside the cart's own values, and the page then prints them with one decimal and a comma, right-aligned before the "m" and "kg" labels, "???" until the species is caught; the printout draws the same body, and the plain page drawn without the cache's dex sheet prints the metric values as it prints the imperial ones. The Gen 2 pokemon schema declares the dexEntry fields the #DEX page reads.
The Pokédex resolves the "m" and "kg" labels when it opens (PokedexMenu.new) rather than on every frame.
Ruby and Sapphire 1.0/1.1/1.2 import, play, save, convert and link. Gen 3 shaders, braille, weather and reflection parity, link room players, RS custom carts and launcher support, Gen 3 link fixes for Emerald and FireRed/LeafGreen.
build_ps4.sh mirrors build_switch.sh: --fetch downloads the pinned LÖVE for
PS4 runtime and checks its SHA-256, --loose packs a plain game.love, --fused
makes gen1recomp-<ver>-ps4.pkg with the runtime's fuse-pkg.sh.
mod.content.encounters:patch on a Gold boot keys by encounter KIND
(encounters.grass.ROUTE_29), but the id space was open: a key the
catalog did not describe was treated as a mod's own data and merged
as-is. A Gen 1 encounters mod ported unchanged writes the MAP where
Gold wants the kind, so the call was accepted, merged into
data.gen2Encounters.ROUTE_29 and read by nothing -- vanilla game, no
error, nothing in the Mod Manager (#2369).
That key cannot be a mod's own data the way an extra palette id can:
src/battle/gen2/Encounter.lua, src/core/gen2/Roamers.lua,
src/core/gen2/BugContest.lua and src/world/gen2/World.lua read this
table by name and the set of names is fixed, so an unknown id is a
write nothing reads. Add an opt-in `keysClosed` shape slot (folded by
Schemas.shapeFor like the other gen2* slots) that Schemas.check honours
in its keys/keyValue branch, and set it on encounters' Gen 2 spec. An
unknown id now names the ids that do exist instead of silently
no-opping; api 2 fails the mod, api 1 keeps the warning.
Also catalogue roamMons, which the extractor has always emitted
(RomExtractorGen2:readRoamMons) and Roamers.roster has always read, but
which was missing from gen2Keys.
Only encounters opts in: palettes, battle_anims and constants keep
their open id spaces, and Red's encounters id space is untouched.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Three follow-ups on the commit below.
statusBlockedId read the status record's optional `id` field. A mod that overrides a status rather than patching it replaces the record outright, and `id` is optional in the registry (src/mods/Schemas.lua), so the field can be nil there and full paralysis would quietly stop clearing bide, thrash, charge and trapping. It now falls back on the key the record was looked up under.
`msgs.failed` is the only thing that suppresses a primary effect's success animation now, so it is no longer an internal detail: the move_effects registry documents it, and docs/modding/reference/registries.md is regenerated. That regeneration also picks up rows that had drifted from the schema before this branch (missText, trueColor, the sprite cell fields).
The parity suite gains the case the commit below changes the rules in -- PAR's own roll clearing the volatiles, and a flinch on a paralysed battler leaving them alone -- and its translated-label block now restores the shared Data.text entries through pcall, so a failing check there cannot leak "pioupiou zzz" into every later parity suite.
The SBC and RG34XXSP ports hand LÖVE a source *directory*
(`love <dir>`), so `love.filesystem.isFused()` is false there and
`Boot.run` bailed on its first line. The launcher still offered
"Update vX.Y.Z" and "Restart to update", so an update downloaded,
verified, and then was silently ignored forever.
- Boot.canUpdateInPlace() replaces the fused-only gate: a packaged
build updates whether it is fused (AppImage, Flatpak game.love) or
unpacked, and only a dev / source checkout (engine "0.0.0-dev") is
excluded. Fails closed when the host cannot be established.
Prelaunch.updateAllowed now delegates to it, so the boot gate and the
`--update` gate cannot disagree.
- Check.hostPort() reads the release-target marker, with
POKEPORT_HANDHELD as a legacy fallback (the SBC launcher has always
exported it, so packs predating this change still identify
themselves). The marker was read from the environment but never
exported, so a full-package fallback on a handheld resolved to a
desktop AppImage that cannot run there.
- A handheld now fetches its own package ("Download port update",
reusing the worker's cross-platform download_full) instead of
offering a URL it has no browser to open, and reports "Update package
ready" once it is in the save directory for a manual re-extract.
- Launchers export POKEPORT_PORTMASTER / POKEPORT_RG34XXSP.
An in-place update only ever mounts a payload over the running source,
so relaxing the gate is less invasive than the fused path it joins.
A runtime bump still needs a full package, which the minShell gate
already reports as needs_full.
Tests: tests/engine/update_boot_host_gate.lua (new, 10 checks) pins the
gate; update_check_tests.lua pins both port asset names and hostPort().
scripts/test.sh --quick: all tiers passed; scripts/lint.sh: 0 errors.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Most map sections never showed a Location Preview because the preview
artwork and the region-map GUIDE text were hand-authored placeholders:
only a handful of sections had art and none of it came from the ROM.
Read the real tables out of the dump instead.
Extraction (new):
- src/import/gba/region_map_tables.lua locates and validates
sMapPreviewScreenData. The pinned offset from versions.lua is tried
first and a bounded +/-0x8000 scan is only a guard. Validation is
structural (mapsec in 88..196, type 0/1, three in-range ROM pointers,
and the shipped invariant that the palette block is the first 0x40
bytes of the tile block) so all 28 entries must hold.
- src/import/gba/map_preview_extract.lua bakes 21 deduplicated 240x160
RGBA artworks (28 entries reuse art: Pattern Bush and the six Tanoby
chambers), 109 mapsec names verified against sRegionMapSectionIdToName,
and the 19 sDungeonInfo entries. Palette banks 13/14; bank-0 tilemap
refs only occur in off-screen padding columns.
- src/import/gba/bg_bake.lua holds the shared 4bpp BG baking helpers;
berry_pouch_extract.lua is refactored onto them (-94 lines).
Runtime:
- src/ui/game3/map_preview_screen.lua ports
Task_RunMapPreviewScreenForest: 120-frame first-visit hold, 40-frame
revisit, 48-frame fade-out, artwork and name window composited into one
canvas so they fade together. Only MPS_TYPE_FOREST (8 sections) takes
over the screen; the 20 MPS_TYPE_CAVE sections only change the warp
fade colour, matching pret.
- src/core/game3/map.lua gives a changed section with a forest preview
precedence over the map name popup (overworld.c state 12).
- src/core/game3/warp.lua routes every fade through warpFadeModes,
applying WarpFadeOutScreen/WarpFadeInScreen: a section change into a
cave-preview map forces FADE_TO_BLACK, otherwise MapTransitionIsEnter/
IsExit decide white vs black on MAP_TYPE_UNDERGROUND.
- setworldmapflag feeds the transient visit flag; the HUD ticks and
dismisses the screen; the dataset installs the cache on hydrate.
- region_map.lua's GUIDE panel uses the retail geometry, tint ramp and
delayed text, and ROM names/dungeon text overlay the hand-authored
fallbacks via ensureGenerated().
Two retail quirks are tolerated: MPS_ROCKET_WAREHOUSE and Berry Forest
share flagId 2231 (as do the six Tanoby chambers), and the Tanoby mapsec
is spelled MAPSEC_DILFORD_CHAMBER.
CACHE_VERSION 99 -> 100.
Verified: tests/game3_map_preview_extract_test.lua (new; ROM-gated, all
28 entries, 8 forest / 20 cave, 21 artworks, 109 names, 19 dungeons, the
shared flag, Pattern Bush art reuse), tests/game3_town_map_test.lua,
tests/run_engine.lua 587/587, scripts/test.sh --quick all tiers,
luacheck src 0 warnings / 0 errors.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Collision.installWarps force-marked every map-header warp cell walkable. That
is right for the outdoor MB_WARP_DOOR tiles extract can leave solid, but it also
opened cells that only carry a dead warp event. PalletTown_PlayersHouse_1F has
one at (3,9): the wall tile directly left of the door mat, mid 26, coll 1,
behavior 0x00 MB_NORMAL. pret never lets the player stand there, so the warp
never fires; the forced COLL_DOOR let the player walk out of the house through
the wall and land in PalletTown.
The repair now fires only when the behavior is unreadable (tileset attrs stopped
before that mid, so Collision.behavior returns nil) or is a behavior pret would
actually warp on. Collision.isWarpMetatileBehavior covers 0x60-0x6F plus 0x71,
matching field_control_avatar.c: the arrow warps (TryArrowWarp), the directional
stair warps (IsDirectionalStairWarpMetatileBehavior), and everything
IsWarpMetatileBehavior accepts (cave door, ladder, fall warp, regular warp,
Lavaridge 1F warp, warp door, escalators, union-room warp).
Warp indexing is unchanged: a dead warp on a wall is still registered, only the
grid write is gated.
tests/engine/firered_house_wall_warp_bug2297.lua is ROM-free: it embeds the pret
13x10 map.bin grid and the `building` metatile attributes, translates them with
the real ScriptingCollision.fromCell, binds the real Versions.WARPS table, and
asserts the wall stays solid, the four warp cells still behave, the repair still
applies for unreadable attrs and live warp behaviors, and an exhaustive
0x00-0xFF table pins the predicate. It fails 6 checks against the pre-fix file.
luajit tests/run_engine.lua: 585/585 suites passed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>