mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
225 lines
8.9 KiB
Lua
225 lines
8.9 KiB
Lua
-- Giovanni's Silph Co 11F coordinate trigger speaks BEFORE he walks (#869).
|
|
-- pokered scripts/SilphCo11F.asm SilphCo11FDefaultScript: DisplayTextID
|
|
-- TEXT_SILPHCO11F_GIOVANNI first, then MoveSprite .GiovanniMovement (3x down)
|
|
-- and EngageMapTrainer with no second box. Do not set POKEPORT_SPEED: the
|
|
-- box-vs-walk ordering is exactly the moment under test.
|
|
-- POKEPORT_DRIVER=tests/drivers/giovanni_silph11f_bug869_test.lua POKEPORT_IDENTITY=bug869 POKEPORT_TOUCH=0 POKEPORT_VERSION=red love .
|
|
return function(game)
|
|
local U = dofile("tests/drivers/util.lua")
|
|
local Pokemon = require("src.pokemon.Pokemon")
|
|
local TextBox = require("src.render.TextBox")
|
|
local BattleTransition = require("src.render.BattleTransition")
|
|
local BattleState = require("src.battle.BattleState")
|
|
local SHOT_DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
|
|
|
-- Positions from ../pokered/data/maps/objects/SilphCo11F.asm: Giovanni
|
|
-- object_event (6, 9), SILPHCO11F_ROCKET1 (3, 16). Trigger tiles from
|
|
-- ../pokered/scripts/SilphCo11F.asm .PlayerCoordsArray: (6, 13) and
|
|
-- (7, 12). data/generated/maps.lua stores the same cells 1:1.
|
|
local MAP = "SILPH_CO_11F"
|
|
local GIO_HOME = { x = 6, y = 9 }
|
|
local GIO_STOP = { x = 6, y = 12 } -- home + 3x NPC_MOVEMENT_DOWN
|
|
|
|
local failed = 0
|
|
local function check(label, ok)
|
|
U.log(ok and "PASS" or "FAIL", label)
|
|
if not ok then failed = failed + 1 end
|
|
return ok
|
|
end
|
|
|
|
-- party strong enough that the human can win the OPP_GIOVANNI#2 fight
|
|
-- and watch the unchanged aftermath (victories.lua "Arrgh!!", then the
|
|
-- "Blast it all!" speech and the rockets leaving)
|
|
game.save.party = {
|
|
Pokemon.new(game.data, "MEWTWO", 80),
|
|
Pokemon.new(game.data, "SNORLAX", 77),
|
|
Pokemon.new(game.data, "CHARIZARD", 70),
|
|
}
|
|
game.save.player.name = "RED"
|
|
|
|
-- (6,13) and (7,13) sit in the card-key doorway of the boss room: block
|
|
-- (3,6) stays the closed id 32 until EVENT_SILPH_CO_11_UNLOCKED_DOOR is
|
|
-- set (stampClosedDoors, mirroring pokered engine/events/card_key.asm),
|
|
-- and a closed door refuses the step this test needs. A real player has
|
|
-- opened it before the trigger can fire, so open it here too.
|
|
game.save.flags.EVENT_SILPH_CO_11_UNLOCKED_DOOR = true
|
|
|
|
check("EVENT_BEAT_SILPH_CO_GIOVANNI starts unset -- trigger is armed",
|
|
not game.save.flags.EVENT_BEAT_SILPH_CO_GIOVANNI)
|
|
local opts = game.save.options or {}
|
|
if (opts.sfxVol or 0) == 0 then
|
|
U.log("sfxVol is 0 -- the evil-trainer sting will be inaudible")
|
|
end
|
|
if (opts.musicVol or 0) == 0 then
|
|
U.log("musicVol is 0 -- the encounter sting and battle theme are muted")
|
|
end
|
|
|
|
local function findNpc(ow, name)
|
|
for _, n in ipairs(ow.npcs or {}) do
|
|
if n.def and n.def.name == name then return n end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function topBox()
|
|
local t = game.stack:top()
|
|
if getmetatable(t) == TextBox then return t end
|
|
return nil
|
|
end
|
|
|
|
local function boxText(box)
|
|
local shown = {}
|
|
for _, page in ipairs(box.pages or {}) do
|
|
for _, line in ipairs(page) do shown[#shown + 1] = line end
|
|
end
|
|
return table.concat(shown, " / ")
|
|
end
|
|
|
|
-- Stand next to (tx, ty) and take one real walking step onto it; onStep
|
|
-- hooks fire on a finished step, so a bare teleport onto the tile would
|
|
-- prove nothing. `sides` are {dx, dy, facing} in preference order, each
|
|
-- checked for walkability so a map edit only degrades to the next side.
|
|
local function stepOnto(tx, ty, sides)
|
|
U.teleport(game, MAP, tx + sides[1][1], ty + sides[1][2], sides[1][3])
|
|
U.wait(10)
|
|
local ow = game.overworld
|
|
for _, s in ipairs(sides) do
|
|
local cx, cy = tx + s[1], ty + s[2]
|
|
if ow.map:isWalkableCell(cx, cy) and not ow:npcAtCell(cx, cy) then
|
|
if ow.player.cellX ~= cx or ow.player.cellY ~= cy then
|
|
U.teleport(game, MAP, cx, cy, s[3])
|
|
U.wait(10)
|
|
end
|
|
U.hold(game, s[3], 24)
|
|
U.wait(10)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Regression on the shared engageTrainer path first: an ordinary trainer
|
|
-- with no skipBattleText must still get its normal pre-battle box.
|
|
-- ROCKET1 faces up, so talk to him from above.
|
|
check("regression: reached ROCKET1's cell",
|
|
stepOnto(3, 15, { { 0, 1, "up" }, { 0, -1, "down" },
|
|
{ 1, 0, "left" }, { -1, 0, "right" } }))
|
|
do
|
|
local ow = game.overworld
|
|
local rocket = findNpc(ow, "SILPHCO11F_ROCKET1")
|
|
check("regression: ROCKET1 object loaded", rocket ~= nil)
|
|
if rocket then
|
|
-- walk down one so we face him from (3, 15)
|
|
local fx, fy = ow.player:facingCell()
|
|
if ow:npcAtCell(fx, fy) ~= rocket then
|
|
-- stepOnto left us adjacent to (3, 15); face the rocket directly
|
|
local dx = rocket.cellX - ow.player.cellX
|
|
local dy = rocket.cellY - ow.player.cellY
|
|
local face = (dy > 0 and "down") or (dy < 0 and "up")
|
|
or (dx > 0 and "right") or "left"
|
|
U.tap(game, face)
|
|
U.wait(10)
|
|
end
|
|
U.tap(game, "a")
|
|
U.wait(30)
|
|
local box = topBox()
|
|
check("regression: talking to ROCKET1 still opens the pre-battle box",
|
|
box ~= nil)
|
|
if box then
|
|
local t = boxText(box)
|
|
U.log("rocket box reads:", t)
|
|
check("regression: it is his battle line (\"Stop right there!\")",
|
|
t:find("Stop right there", 1, true) ~= nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Both trigger tiles must fire with Giovanni still at his desk. The
|
|
-- (7, 12) probe is abandoned before the box is dismissed (the teleport
|
|
-- rebuilds the map state), so the trigger re-arms for the main run --
|
|
-- vanilla re-arms too, since only a win sets the event flag.
|
|
do
|
|
check("(7,12) approach: stepped onto the trigger from the right",
|
|
stepOnto(7, 12, { { 1, 0, "left" }, { 0, 1, "up" },
|
|
{ -1, 0, "right" } }))
|
|
local box = topBox()
|
|
check("(7,12) approach: intro box opened", box ~= nil)
|
|
local gio = findNpc(game.overworld, "SILPHCO11F_GIOVANNI")
|
|
check("(7,12) approach: Giovanni is still at his desk (6,9)",
|
|
gio ~= nil and gio.cellX == GIO_HOME.x and gio.cellY == GIO_HOME.y)
|
|
end
|
|
|
|
-- Main run, the route the issue screenshots show: (6,15) facing up, two
|
|
-- steps onto (6,13).
|
|
U.teleport(game, MAP, 6, 15, "up")
|
|
U.wait(10)
|
|
U.hold(game, "up", 24)
|
|
U.hold(game, "up", 24)
|
|
U.wait(15)
|
|
if not topBox() then
|
|
-- blocked approach fallback: one step onto (6,13) from any free side
|
|
stepOnto(6, 13, { { 0, 1, "up" }, { -1, 0, "right" }, { 1, 0, "left" } })
|
|
end
|
|
|
|
local ow = game.overworld
|
|
local gio = findNpc(ow, "SILPHCO11F_GIOVANNI")
|
|
check("Giovanni object loaded on " .. MAP, gio ~= nil)
|
|
local box = topBox()
|
|
check("stepping onto (6,13) opened a text box", box ~= nil)
|
|
if box then
|
|
local t = boxText(box)
|
|
U.log("intro box reads:", t)
|
|
check("it is the Giovanni intro (\"So we meet again!\")",
|
|
t:find("So we meet again", 1, true) ~= nil)
|
|
end
|
|
check("the box opened with Giovanni STILL at his desk (6,9) -- the fix",
|
|
gio ~= nil and gio.cellX == GIO_HOME.x and gio.cellY == GIO_HOME.y)
|
|
U.shot(game, SHOT_DIR .. "/bug869_box_before_walk.png")
|
|
U.wait(30)
|
|
check("he holds the desk for the whole box, not just its first frame",
|
|
gio ~= nil and gio.cellX == GIO_HOME.x and gio.cellY == GIO_HOME.y)
|
|
|
|
-- dismiss every page; the walk and the battle must follow with NO
|
|
-- further dialogue box in between (EngageMapTrainer runs bare in the
|
|
-- original, so engageTrainer is called with skipBattleText here)
|
|
for _ = 1, 300 do
|
|
if not topBox() then break end
|
|
U.tap(game, "a")
|
|
U.wait(5)
|
|
end
|
|
check("intro box dismissed", topBox() == nil)
|
|
local sawSecondBox, battleReached = false, false
|
|
for _ = 1, 900 do
|
|
local t = game.stack:top()
|
|
local mt = getmetatable(t)
|
|
if mt == TextBox and not sawSecondBox then
|
|
sawSecondBox = true
|
|
U.log("unexpected box reads:", boxText(t))
|
|
end
|
|
if mt == BattleTransition or mt == BattleState then
|
|
battleReached = true
|
|
break
|
|
end
|
|
U.wait(1)
|
|
end
|
|
check("battle wipe started after the box", battleReached)
|
|
check("no second dialogue box between the walk and the battle", not sawSecondBox)
|
|
check("Giovanni walked the three tiles down to (6,12) first",
|
|
gio ~= nil and gio.cellX == GIO_STOP.x and gio.cellY == GIO_STOP.y)
|
|
|
|
U.log(failed == 0 and "PASS all machine checks clean"
|
|
or ("FAIL " .. failed .. " machine check(s) above"))
|
|
|
|
U.log("The battle wipe is running now; take the pad and win the fight.")
|
|
U.log("Right looks like what just played: his speech opened while he was")
|
|
U.log("behind the desk, then he walked down and the fight began straight")
|
|
U.log("away, with the evil-trainer sting and no extra dialogue. After the")
|
|
U.log("win you should get \"Arrgh!!\" on the battle screen, the \"Blast it")
|
|
U.log("all!\" speech, a fade, and every rocket gone. Wrong is the old bug:")
|
|
U.log("he crosses the room in silence and only then talks, point-blank.")
|
|
|
|
while true do
|
|
coroutine.yield()
|
|
end
|
|
end
|