mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
41935aa2a2
Second batch of the Gen 3 review work: the review lanes closed their queues and the test gate grew with them. Review census: 139 routed findings -- 118 fixed, 14 invalid (the report had Gen 4+ semantics in more than one place), 6 struck as stale after re-verification, 1 re-routed. Every fix carries its own pret citation in docs/game3/review-v3-triage.md; the E10 opcode closure is itemised in docs/game3/e10-opcode-spec.md (18 ops wired, 2 reclassified to the seam class). Representative ROM-grounded changes: - The POKéMON start-menu entry is now gated on FLAG_SYS_POKEMON_GET the way retail does it (pokefirered/src/start_menu.c:217-218, flag 0x828). - Money ops read their amount raw and gate the change on the disable byte (pokefirered/src/scrcmd.c:1798-1830, pokefirered/asm/macros/event.inc:1166-1186); random and the warp family VarGet theirs (pokefirered/src/scrcmd.c:455-461, :719-731). - The HM table matches FRLG: there is no Whirlpool HM (pokefirered/include/constants/items.h:411-418). - The day-care party-full guard follows src/daycare.c:525, :1081. - Knock Off keeps its battle-scoped send-out mask (pokefirered/src/battle_script_commands.c:2730-2752, :4489), carried from the first batch. Structural work: the I6/I9/J1 architecture items landed as seams (profile selector, capability flags, font provider, virtual-object layer), the adopted footprint register keeps a KEEP verdict, the quantizer target is met, and the 30 drain items plus carves 1-4 are folded in. Tests: T6 now runs 282 top-level suites (273 + 9 new), the re-sweep ends at 267 pass / 0 fail, the engine tier is 634 suites, modkit 37, and the full gate ran green twice with T3 active on an imported Red cache. Documented skips are the lua5.4 oversize-save oracle where lua5.4 is absent and the config-partials listed in docs/game3/game3-artifact-conversions-v3.md. Docs shipped: review-v3-triage.md, e10-opcode-spec.md, rse-seams.md, game3-suite-sweep-v113.md, game3-artifact-conversions-v3.md, test-baseline-v3.md (plus the first-batch docs already on the branch).
109 lines
4.8 KiB
Lua
109 lines
4.8 KiB
Lua
-- Sections 5.3/5.4 of docs/game3/e10-opcode-spec.md: the virtual-object
|
|
-- registry behind `createvobject` / `turnvobject`. Unwired (the dispatch cases
|
|
-- are the Finisher's), so this suite pins the registry contract they will call.
|
|
-- luajit tests/engine/game3_virtual_objects_test.lua
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
|
|
local VirtualObjects = require("src.core.game3.virtual_objects")
|
|
|
|
VirtualObjects.reset()
|
|
|
|
-- ------------------------------------------------------------ module boundary
|
|
|
|
check(package.loaded["src.core.game3.objects"] == nil,
|
|
"the registry does not drag in objects.lua")
|
|
check(package.loaded["src.core.game3.collision"] == nil,
|
|
"the registry does not drag in collision (virtual objects never collide)")
|
|
eq(VirtualObjects.DIR_SOUTH, 1, "DIR_SOUTH matches pret include/constants/global.h:110")
|
|
|
|
-- ----------------------------------------------------------------- lifecycle
|
|
|
|
eq(VirtualObjects.count(), 0, "starts empty")
|
|
|
|
local rec = VirtualObjects.spawn(1, 42, 6, 8, 3, 2)
|
|
check(type(rec) == "table", "spawn returns a record")
|
|
eq(rec.id, 1, "id stored")
|
|
eq(rec.graphicsId, 42, "graphicsId stored")
|
|
eq(rec.x, 6, "x stored")
|
|
eq(rec.y, 8, "y stored")
|
|
eq(rec.elevation, 3, "elevation stored")
|
|
eq(rec.direction, 2, "direction stored")
|
|
eq(VirtualObjects.count(), 1, "one live object")
|
|
|
|
-- event.inc:1346 defaults (elevation=3, direction=DIR_SOUTH)
|
|
local dflt = VirtualObjects.spawn(2, 7, 0, 0, nil, nil)
|
|
eq(dflt.elevation, 3, "default elevation is 3")
|
|
eq(dflt.direction, VirtualObjects.DIR_SOUTH, "default direction is DIR_SOUTH")
|
|
eq(VirtualObjects.count(), 2, "two live objects")
|
|
|
|
check(VirtualObjects.get(1) == rec, "get finds the record")
|
|
check(VirtualObjects.get(99) == nil, "get misses cleanly")
|
|
|
|
local list = VirtualObjects.list()
|
|
eq(#list, 2, "list returns both records")
|
|
eq(list[1].id, 1, "list preserves spawn order (first)")
|
|
eq(list[2].id, 2, "list preserves spawn order (second)")
|
|
|
|
-- --------------------------------------------------------------- turn (5.4)
|
|
|
|
check(VirtualObjects.turn(1, 5) == true, "turn on a live id succeeds")
|
|
eq(VirtualObjects.get(1).direction, 5, "turn updates direction")
|
|
eq(VirtualObjects.get(2).direction, VirtualObjects.DIR_SOUTH,
|
|
"turn on one id leaves the other alone")
|
|
|
|
-- Missing id: logged no-op returning false (pret GetVirtualObjectSpriteId's
|
|
-- MAX_SPRITES miss, src/event_object_movement.c:9248-9257).
|
|
check(VirtualObjects.turn(99, 1) == false, "turn on a missing id is a no-op")
|
|
check(VirtualObjects.turn(99, 1) == false, "still a no-op on a repeat")
|
|
check(VirtualObjects.turn(nil, 1) == false, "turn(nil) is a no-op")
|
|
eq(VirtualObjects.get(99), nil, "a missed turn creates nothing")
|
|
|
|
-- A non-numeric direction leaves the stored direction untouched (tonumber miss).
|
|
check(VirtualObjects.turn(2, "sideways") == true, "non-numeric direction is accepted")
|
|
eq(VirtualObjects.get(2).direction, VirtualObjects.DIR_SOUTH,
|
|
"a non-numeric direction does not clobber the stored one")
|
|
|
|
-- ------------------------------------------------- replace + numeric coercion
|
|
|
|
local replaced = VirtualObjects.spawn(1, 99, 12, 14, 5, 6)
|
|
check(replaced == VirtualObjects.get(1), "re-spawning an id replaces the entry")
|
|
eq(VirtualObjects.count(), 2, "a replace does not duplicate the id")
|
|
eq(VirtualObjects.get(1).graphicsId, 99, "the replacement's graphics wins")
|
|
eq(#VirtualObjects.list(), 2, "list has no duplicate id")
|
|
|
|
local coerced = VirtualObjects.spawn("3", 1, 1, 1, 1, 1)
|
|
check(coerced ~= nil, "a numeric-string id is accepted")
|
|
check(VirtualObjects.get(3) == coerced, "and resolves to the same record")
|
|
check(VirtualObjects.turn("3", 4) == true, "turn accepts the same coercion")
|
|
|
|
-- --------------------------------------------------------------- bad input
|
|
|
|
eq(VirtualObjects.spawn(nil, 1, 0, 0, 1, 1), nil, "a nil id is refused")
|
|
eq(VirtualObjects.spawn("badge", 1, 0, 0, 1, 1), nil, "a non-numeric id is refused")
|
|
eq(VirtualObjects.count(), 3, "refused spawns add nothing")
|
|
|
|
-- ----------------------------------------------------------- map unload clear
|
|
|
|
VirtualObjects.clear()
|
|
eq(VirtualObjects.count(), 0, "clear empties the registry (map unload)")
|
|
eq(#VirtualObjects.list(), 0, "list is empty after clear")
|
|
check(VirtualObjects.get(1) == nil, "records are gone")
|
|
check(VirtualObjects.turn(1, 1) == false, "turn after clear is a no-op")
|
|
|
|
-- An id is reusable after clear (a fresh map may spawn the same ids).
|
|
local fresh = VirtualObjects.spawn(1, 5, 2, 2, 3, 1)
|
|
check(fresh ~= nil, "ids are reusable after clear")
|
|
eq(VirtualObjects.count(), 1, "one object after re-spawn")
|
|
eq(#VirtualObjects.list(), 1, "no ghost entries from the pre-clear spawn")
|
|
|
|
VirtualObjects.reset()
|
|
eq(VirtualObjects.count(), 0, "reset clears everything")
|
|
VirtualObjects.reset()
|
|
eq(VirtualObjects.count(), 0, "reset is safe to repeat")
|
|
|
|
T.finish("game3_virtual_objects_test")
|