mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
178 lines
7.6 KiB
Lua
178 lines
7.6 KiB
Lua
-- Manual check of the Rocket Hideout B4F Jessie & James ambush: James walks
|
|
-- the full four tiles to the player's side (#865) and their loss line prints
|
|
-- on the battle screen before the prize money (#866).
|
|
-- pokeyellow scripts/RocketHideoutB4F.asm (MovementData_45605 falls through
|
|
-- into _45606) and data/maps/objects/RocketHideoutB4F.asm. No fast-forward:
|
|
-- POKEPORT_DRIVER=tests/drivers/jessie_james_bug866_test.lua POKEPORT_VERSION=yellow love .
|
|
return function(game)
|
|
local U = dofile("tests/drivers/util.lua")
|
|
local Pokemon = require("src.pokemon.Pokemon")
|
|
local BattleState = require("src.battle.BattleState")
|
|
local Commands = require("src.script.Commands")
|
|
local GameVersion = require("src.core.GameVersion")
|
|
local mapScripts = require("data.scripts.init")
|
|
local SHOT_DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
|
|
|
local MAP = "ROCKET_HIDEOUT_B4F"
|
|
local BEAT = "EVENT_BEAT_ROCKET_HIDEOUT_4_JESSIE_JAMES"
|
|
local JAMES, JESSIE = "ROCKETHIDEOUTB4F_JAMES", "ROCKETHIDEOUTB4F_JESSIE"
|
|
-- RocketHideoutB4FScript_455a5 fires on wYCoord $e with wXCoord $18 or $19.
|
|
-- x=24 leaves EVENT_ROCKET_HIDEOUT_4_JESSIE_JAMES_ON_LEFT clear, which is
|
|
-- the branch that hands the four-step blob to James (object 2, spawned at
|
|
-- 25,10) and the three-step one to Jessie (object 3, at 24,10).
|
|
local TRIGGER = { x = 24, y = 14 }
|
|
local EXPECT = {
|
|
[JAMES] = { x = 25, y = 14, facing = "left" },
|
|
[JESSIE] = { x = 24, y = 13, facing = "down" },
|
|
}
|
|
|
|
local function check(label, ok)
|
|
U.log(ok and "PASS" or "FAIL", label)
|
|
return ok
|
|
end
|
|
|
|
check("running the Yellow cache (the duo exists nowhere else)",
|
|
GameVersion.isYellow())
|
|
if not GameVersion.isYellow() then
|
|
U.log("re-run with POKEPORT_VERSION=yellow; nothing below will be true")
|
|
end
|
|
|
|
local hooks = mapScripts.get(MAP)
|
|
check("yellow_jessie_james registered an onStep for " .. MAP,
|
|
type(hooks) == "table" and type(hooks.onStep) == "function")
|
|
check("their talk entries are registered too",
|
|
type(hooks) == "table" and type(hooks.talk) == "table"
|
|
and hooks.talk.TEXT_ROCKETHIDEOUTB4F_JAMES ~= nil
|
|
and hooks.talk.TEXT_ROCKETHIDEOUTB4F_JESSIE ~= nil)
|
|
|
|
-- the #866 fix is a new script verb; if a mod shadowed it or the registry
|
|
-- never picked it up, the row would silently no-op and the line would come
|
|
-- back after the money instead of before it
|
|
local verb = Commands.resolve(game.data, "save_end_battle_text")
|
|
check("save_end_battle_text resolves as a script verb", type(verb) == "function")
|
|
|
|
local texts = {}
|
|
for i = 1, 4 do
|
|
local key = "_RocketHideoutJessieJamesText" .. i
|
|
texts[i] = game.data.text[key]
|
|
check(key .. " resolves to a string",
|
|
type(texts[i]) == "string" and texts[i] ~= "")
|
|
end
|
|
if type(texts[3]) == "string" then
|
|
U.log("the armed loss line reads:", (texts[3]:gsub("\n", " / ")))
|
|
end
|
|
|
|
local objs = (game.data.maps[MAP] or {}).objects or {}
|
|
local defs = {}
|
|
for _, o in ipairs(objs) do
|
|
if o.name == JAMES or o.name == JESSIE then defs[o.name] = o end
|
|
end
|
|
check("James is object 2 of " .. MAP .. ", hidden at (25,10)",
|
|
defs[JAMES] ~= nil and defs[JAMES].index == 2
|
|
and defs[JAMES].x == 25 and defs[JAMES].y == 10
|
|
and defs[JAMES].hidden == true)
|
|
check("Jessie is object 3, hidden at (24,10)",
|
|
defs[JESSIE] ~= nil and defs[JESSIE].index == 3
|
|
and defs[JESSIE].x == 24 and defs[JESSIE].y == 10
|
|
and defs[JESSIE].hidden == true)
|
|
|
|
local rocket = game.data.trainers.OPP_ROCKET
|
|
check("OPP_ROCKET party 43 (the duo's shared team) exists",
|
|
rocket ~= nil and rocket.parties ~= nil and rocket.parties[43] ~= nil)
|
|
|
|
-- arm the site: the ambush is gated only on its beat flag, so no story
|
|
-- progress is needed to make it live
|
|
game.save.flags[BEAT] = nil
|
|
game.save.flags.EVENT_ROCKET_HIDEOUT_4_JESSIE_JAMES_ON_LEFT = nil
|
|
check(BEAT .. " cleared, so the trigger is live",
|
|
game.save.flags[BEAT] == nil)
|
|
|
|
-- a real party, because the human has to win the battle for the loss line
|
|
-- to print at all
|
|
game.save.party = {
|
|
Pokemon.new(game.data, "CHARIZARD", 60),
|
|
Pokemon.new(game.data, "NIDOKING", 58),
|
|
Pokemon.new(game.data, "STARMIE", 58),
|
|
}
|
|
game.save.player.name = "RED"
|
|
|
|
-- walk in from the north; the two elevator warps sit on row 15, so the
|
|
-- approach cannot come from below
|
|
U.teleport(game, MAP, TRIGGER.x, TRIGGER.y - 1, "down")
|
|
local ow = game.overworld
|
|
if not ow.map:isWalkableCell(TRIGGER.x, TRIGGER.y - 1) then
|
|
-- a map edit moved the free cell: any walkable neighbour of the trigger
|
|
-- works, the script only reads the tile the player lands on
|
|
local sides = { { 0, -1, "down" }, { -1, 0, "right" }, { 1, 0, "left" } }
|
|
for _, s in ipairs(sides) do
|
|
local cx, cy = TRIGGER.x + s[1], TRIGGER.y + s[2]
|
|
if ow.map:isWalkableCell(cx, cy) then
|
|
U.log("standing on", cx, cy, "facing", s[3], "instead")
|
|
U.teleport(game, MAP, cx, cy, s[3])
|
|
ow = game.overworld
|
|
U.hold(game, s[3] == "down" and "down" or (s[3] == "right" and "right" or "left"), 20)
|
|
break
|
|
end
|
|
end
|
|
else
|
|
U.hold(game, "down", 20)
|
|
end
|
|
U.wait(10)
|
|
check("player stepped onto the trigger tile (24,14)",
|
|
ow.player.cellX == TRIGGER.x and ow.player.cellY == TRIGGER.y)
|
|
check("the ambush script is running", ow.runner:isRunning())
|
|
|
|
U.log("The cutscene is yours now: press A to read, then fight and win.")
|
|
U.log("Right looks like both Rockets closing in -- Jessie stopping one tile")
|
|
U.log("above you, James coming all the way down to stand at your right -- and")
|
|
U.log("after you win, \"ROCKET: Such a dreadful twerp!\" appearing on the")
|
|
U.log("battle screen just before the money line. The near-miss to watch for")
|
|
U.log("is James halting three tiles up by the wall, or that line showing up")
|
|
U.log("in the overworld box after the payout with no ROCKET: tag on it.")
|
|
U.log("Two more checks print below as you get to them.")
|
|
|
|
local function npcNamed(name)
|
|
for _, n in ipairs(game.overworld and game.overworld.npcs or {}) do
|
|
if n.def and n.def.name == name then return n end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local approachDone, battleSeen = false, false
|
|
while true do
|
|
if not approachDone then
|
|
local j, s = npcNamed(JAMES), npcNamed(JESSIE)
|
|
local ow2 = game.overworld
|
|
if j and s and not j.moving and not s.moving and ow2
|
|
and #(ow2.scriptMoves or {}) == 0
|
|
and (j.cellY > 10 or s.cellY > 10) then
|
|
approachDone = true
|
|
check("James walked the full four tiles to (25,14) facing left",
|
|
j.cellX == EXPECT[JAMES].x and j.cellY == EXPECT[JAMES].y
|
|
and j.facing == EXPECT[JAMES].facing)
|
|
check("Jessie stopped three down at (24,13) facing the player",
|
|
s.cellX == EXPECT[JESSIE].x and s.cellY == EXPECT[JESSIE].y
|
|
and s.facing == EXPECT[JESSIE].facing)
|
|
U.log("James at", j.cellX, j.cellY, j.facing,
|
|
"Jessie at", s.cellX, s.cellY, s.facing)
|
|
U.shot(game, SHOT_DIR .. "/jj866_approach.png")
|
|
end
|
|
end
|
|
if not battleSeen then
|
|
local top = game.stack:top()
|
|
if getmetatable(top) == BattleState then
|
|
battleSeen = true
|
|
-- BattleState prints endBattleText between _TrainerDefeatedText and
|
|
-- _MoneyForWinningText, so an armed field IS the ordering fix
|
|
check("the battle carries the loss line as its end-battle text",
|
|
type(top.endBattleText) == "string" and top.endBattleText ~= ""
|
|
and top.endBattleText == texts[3])
|
|
if type(top.endBattleText) == "string" then
|
|
U.log("armed:", (top.endBattleText:gsub("\n", " / ")))
|
|
end
|
|
end
|
|
end
|
|
coroutine.yield()
|
|
end
|
|
end
|