mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-01 23:42:25 -04:00
e2454bf568
Second wave of fixes from the Gen 3 codebase review (210 findings), the
regressions the full-suite sweep surfaced, and a T6 tier so the top-level
tests/game3_*.lua suites run inside ./scripts/test.sh instead of outside it.
Battle: Knock Off and Thief/Trick item persistence follow the ROM, not the
review - the party slot is never written through during battle, and the
knocked-off mask suppresses the item on later send-outs
(pokefirered/src/battle_script_commands.c:2731 MOVE_EFFECT_KNOCK_OFF with the
STICKY_HOLD guard at :2732, opponent-steal guard at :2610-2622, mask use at
:4489). Rapid Spin keeps its one-per-use chain order (:8435-8474); Growl
keeps pret's target (src/data/battle_moves.h, include/battle.h:63).
Scripting: operand layouts match pret/asm/macros/event.inc - givemon 15 bytes
(:989-997), comparestat {B,W} (:1573-1576), setptr/loadbytefromptr/setptrbyte
take a word pointer (:118-137). 18 of the 20 E10 ops are wired per
src/scrcmd.c (per-op citations in docs/game3/e10-opcode-spec.md), money and
random and the warp family read their operands as VarGets
(src/scrcmd.c:1798-1830, :455-461, :719-731), and the day-care party-full
guard is in place (src/daycare.c:525,:1081).
Field and UI: fishing counts rounds as the ROM does
(src/field_player_avatar.c:1740-1765), an unresolved map section no longer
reports Pallet Town (src/region_map.c:3782), and a font provider sits behind
a FireRed capability/profile seam.
Save data: profile-driven Options.block, a save version round-trip, and slot
id validation that blocks path traversal (slotDiskPath("firered", "../evil")
returns nil).
Importer: the parallel import path now writes both completion markers,
object kind/clone bytes decode as pret defines them
(include/global.fieldmap.h:110-130), and the HM table drops Whirlpool
(include/constants/items.h:411-418).
Tests: six new scenario suites (battle_ai, capture, event, menu, move,
overworld) and eight engine suites (profile, capabilities, cache paths, font
provider, options block, save version round-trip, version dispatch,
versions_game). T6 runs every top-level game3 suite at GAME3_JOBS default 8
with per-suite logs, failure cause classification and a KNOWN_GAME3_FAILURES
ceiling of 23 as a shrinking guard.
Docs: the working notes under docs/game3 (triage ledger, pret citation audit,
e10 opcode spec, RSE seams, sweep v3/v113, test baseline, artifact
conversions, merge trial).
Full gate at this snapshot: exit 0, all tiers passed - engine 624/624,
T6 279/279 (0 known, 0 fresh failures), gen2 146/146, modkit 37/37, luacheck
clean, privacy gate 3/3 over a 9,711-file publication set.
58 lines
2.5 KiB
Lua
58 lines
2.5 KiB
Lua
-- T0.2 handoff (docs/game3/rse-seams.md section 4): the save's game identity
|
|
-- must round-trip instead of being a FireRed literal.
|
|
--
|
|
-- Regression: Schema.toSaveTable wrote `version = "firered"` and fromSaveTable
|
|
-- dropped the field entirely, so a future RSE save could not be told apart from
|
|
-- a FireRed one, and Game3._hasContinueSave / SaveData.slotSummary keyed Gen 3
|
|
-- off the `"FR_"` / `"firered"` literals. Every path here resolves to the
|
|
-- active profile, which fails closed to FireRed headless.
|
|
-- luajit tests/engine/game3_save_version_roundtrip_test.lua
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
love = love or require("tests.love_stub")
|
|
|
|
local Schema = require("src.core.game3.save_schema_firered")
|
|
local Profile = require("src.core.game3.profile")
|
|
local SaveData = require("src.core.SaveData")
|
|
|
|
-- 1. newGame stamps the active profile, not a literal.
|
|
local session = Schema.newGame({ gender = 0 })
|
|
eq(session.version, Profile.active().id, "newGame stamps the active profile id")
|
|
eq(session.version, "firered", "headless fails closed to the FireRed profile")
|
|
|
|
-- 2. toSaveTable writes the session's game.
|
|
local save = Schema.toSaveTable(session)
|
|
eq(save.version, "firered", "toSaveTable writes the session version, not a literal")
|
|
|
|
-- 3. fromSaveTable round-trips it (previously the field was dropped).
|
|
local back = Schema.fromSaveTable(save)
|
|
eq(back.version, "firered", "fromSaveTable round-trips version")
|
|
|
|
-- 4. A pre-T0.2 save with no version field falls back to the active profile.
|
|
local bare = Schema.toSaveTable(session)
|
|
bare.version = nil
|
|
eq(Schema.fromSaveTable(bare).version, Profile.active().id,
|
|
"a version-less save falls back to the active profile")
|
|
|
|
-- 5. A version the engine does not know fails closed without raising.
|
|
local unknown = Schema.toSaveTable(session)
|
|
unknown.version = "not-a-real-game"
|
|
eq(Schema.fromSaveTable(unknown).version, "not-a-real-game",
|
|
"fromSaveTable keeps the stored id; Profile.of is the fail-closed reader")
|
|
eq(Profile.of("not-a-real-game").id, Profile.active().id,
|
|
"Profile.of fails closed for an unknown id")
|
|
|
|
-- 6. slotSummary classifies Gen 3 from the engine tag, not the version literal.
|
|
local name, info = SaveData.slotSummary({
|
|
engine = "game3",
|
|
name = "RED",
|
|
dex = { owned = { [1] = true, [2] = true, [3] = true } },
|
|
})
|
|
eq(name, "RED", "slot summary reads the player name")
|
|
check(info and info.dexCount == 3, "an engine-tagged save without a version counts dex.owned as Gen 3")
|
|
|
|
T.finish("game3_save_version_roundtrip_test")
|