mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
308 lines
11 KiB
Lua
308 lines
11 KiB
Lua
-- scripts/SilphCo7F.asm:206-252
|
|
-- POKEPORT_DRIVER=tests/drivers/silph7f_rival_exit_bug2242_test.lua \
|
|
-- POKEPORT_IDENTITY=bug2242 POKEPORT_TOUCH=0 POKEPORT_VERSION=red \
|
|
-- POKEPORT_SHOT_DIR=/tmp/shots love . (BUG2242_TILE=2 runs the upper tile)
|
|
return function(game)
|
|
local U = dofile("tests/drivers/util.lua")
|
|
local DIR = os.getenv("POKEPORT_SHOT_DIR") or os.getenv("SHOT_DIR") or "/tmp/shots"
|
|
local Pokemon = require("src.pokemon.Pokemon")
|
|
local Flags = require("src.script.Flags")
|
|
local TextBox = require("src.render.TextBox")
|
|
local mapScripts = require("data.scripts.init")
|
|
|
|
local MAP = "SILPH_CO_7F"
|
|
local TILE_Y = tonumber(os.getenv("BUG2242_TILE") or "3")
|
|
if TILE_Y ~= 2 and TILE_Y ~= 3 then TILE_Y = 3 end
|
|
local TRIG_X = 3
|
|
local WANT = (TILE_Y == 2)
|
|
and { rx = 3, ry = 3,
|
|
dirs = { "right", "right" },
|
|
cells = { "(4,3)", "(5,3)" },
|
|
shotCell = "(4,3)" }
|
|
or { rx = 3, ry = 4,
|
|
dirs = { "left", "up", "up", "right", "right", "right", "down" },
|
|
cells = { "(2,4)", "(2,3)", "(2,2)", "(3,2)", "(4,2)", "(5,2)", "(5,3)" },
|
|
shotCell = "(4,2)" }
|
|
|
|
local failures = 0
|
|
local function check(label, ok)
|
|
U.log(ok and "PASS" or "FAIL", label)
|
|
if not ok then failures = failures + 1 end
|
|
return ok
|
|
end
|
|
local function finish()
|
|
U.log(("%s: %d failure(s)"):format(failures == 0 and "DONE" or "DONE WITH FAILURES",
|
|
failures))
|
|
love.event.quit(failures == 0 and 0 or 1)
|
|
while true do coroutine.yield() end
|
|
end
|
|
|
|
local tank = Pokemon.new(game.data, "MEWTWO", 100)
|
|
tank.moves = {
|
|
{ id = "PSYCHIC_M", pp = 99 },
|
|
{ id = "THUNDERBOLT", pp = 99 },
|
|
{ id = "ICE_BEAM", pp = 99 },
|
|
{ id = "RECOVER", pp = 99 },
|
|
}
|
|
game.save.party = { tank }
|
|
game.save.player = game.save.player or {}
|
|
game.save.player.name = game.save.player.name or "RED"
|
|
game.save.player.rival = game.save.player.rival or "BLUE"
|
|
game.save.defeatedTrainers = {}
|
|
game.save.objectToggles = game.save.objectToggles or {}
|
|
game.save.objectToggles[MAP] = nil
|
|
Flags.set(game.save, "EVENT_CHOSE_SQUIRTLE")
|
|
Flags.clear(game.save, "EVENT_BEAT_SILPH_CO_RIVAL")
|
|
|
|
U.teleport(game, MAP, TRIG_X + 1, TILE_Y, "left")
|
|
U.wait(10)
|
|
local ow = game.overworld
|
|
|
|
local t = game.data.text
|
|
for _, key in ipairs({ "_SilphCo7FRivalText", "_SilphCo7FRivalWaitedHereText",
|
|
"_SilphCo7FRivalDefeatedText",
|
|
"_SilphCo7FRivalGoodLuckToYouText" }) do
|
|
check(key .. " resolves to a string",
|
|
type(t[key]) == "string" and t[key] ~= "")
|
|
end
|
|
local defeated = t._SilphCo7FRivalDefeatedText or ""
|
|
local needle = defeated:match("Oh ho") and "Oh ho" or "BOSS"
|
|
|
|
local spawn
|
|
for _, o in ipairs(game.data.maps[MAP].objects or {}) do
|
|
if o.name == "SILPHCO7F_RIVAL" then spawn = o end
|
|
end
|
|
check("SILPHCO7F_RIVAL has an object_event", spawn ~= nil)
|
|
check("it spawns on (3,7) as in data/maps/objects/SilphCo7F.asm",
|
|
spawn ~= nil and spawn.x == 3 and spawn.y == 7)
|
|
check("SILPH_CO_7F (5,4) is the desk, not floor", not ow.map:isWalkableCell(5, 4))
|
|
|
|
local Music = require("src.core.Music")
|
|
local realPlay = Music.play
|
|
Music.play = function() end
|
|
local rows, probeFacing
|
|
do
|
|
local probe = {
|
|
runner = { isRunning = function() return false end,
|
|
run = function(_, r) rows = r end },
|
|
player = { facing = "left" },
|
|
npcByIndex = function() return { def = { name = "X" } } end,
|
|
}
|
|
local script = mapScripts.get(MAP)
|
|
check("SILPH_CO_7F has an onStep hook", script ~= nil and script.onStep ~= nil)
|
|
if script and script.onStep then
|
|
check(("onStep fires on the ambush tile (%d,%d)"):format(TRIG_X, TILE_Y),
|
|
script.onStep(game, probe, TRIG_X, TILE_Y) == true)
|
|
end
|
|
probeFacing = probe.player.facing
|
|
end
|
|
Music.play = realPlay
|
|
|
|
local moveTo, walk, arm, battle, mapDefeat
|
|
for i, r in ipairs(rows or {}) do
|
|
if r[1] == "move_npc_to" then moveTo = r end
|
|
if r[1] == "walk_npc" then walk = r end
|
|
if r[1] == "save_end_battle_text" then arm = i end
|
|
if r[1] == "rival_battle" then battle = i end
|
|
if r[1] == "show_text" and r[2] == "_SilphCo7FRivalDefeatedText" then mapDefeat = i end
|
|
end
|
|
check("the scene carries move/walk rows", moveTo ~= nil and walk ~= nil)
|
|
check("player is turned down at the rival", probeFacing == "down")
|
|
check("_SilphCo7FRivalDefeatedText is armed right before rival_battle",
|
|
arm ~= nil and battle ~= nil and arm == battle - 1
|
|
and rows[arm][2] == "_SilphCo7FRivalDefeatedText")
|
|
check("the loss line is not printed on the map afterwards", mapDefeat == nil)
|
|
if moveTo and walk then
|
|
U.log(("plan: rival to (%d,%d), exit %s"):format(
|
|
moveTo[3], moveTo[4], table.concat(walk[3], ", ")))
|
|
check(("rival parks on (%d,%d)"):format(WANT.rx, WANT.ry),
|
|
moveTo[3] == WANT.rx and moveTo[4] == WANT.ry)
|
|
local same = #walk[3] == #WANT.dirs
|
|
for i = 1, #WANT.dirs do
|
|
if walk[3][i] ~= WANT.dirs[i] then same = false end
|
|
end
|
|
check("exit list is " .. table.concat(WANT.dirs, ", "), same)
|
|
local D = { up = { 0, -1 }, down = { 0, 1 },
|
|
left = { -1, 0 }, right = { 1, 0 } }
|
|
local x, y, clean = moveTo[3], moveTo[4], true
|
|
for i, d in ipairs(walk[3]) do
|
|
x, y = x + D[d][1], y + D[d][2]
|
|
if not ow.map:isWalkableCell(x, y) then
|
|
clean = false
|
|
U.log((" exit step %d (%s) walks into solid ground at (%d,%d)")
|
|
:format(i, d, x, y))
|
|
end
|
|
if x == TRIG_X and y == TILE_Y then
|
|
clean = false
|
|
U.log((" exit step %d (%s) walks through the player on (%d,%d)")
|
|
:format(i, d, x, y))
|
|
end
|
|
end
|
|
check("every exit cell is walkable and none is the player's", clean)
|
|
check("the exit ends on the (5,3) teleporter", x == 5 and y == 3)
|
|
end
|
|
|
|
U.hold(game, "left", 24)
|
|
|
|
local function boxText()
|
|
local top = game.stack:top()
|
|
if getmetatable(top) ~= TextBox then return "" end
|
|
local parts = {}
|
|
for _, page in ipairs(top.pages or {}) do
|
|
if type(page) == "table" then
|
|
for _, line in ipairs(page) do parts[#parts + 1] = tostring(line) end
|
|
end
|
|
end
|
|
return table.concat(parts, " ")
|
|
end
|
|
|
|
local function rivalNpc()
|
|
for _, n in ipairs(game.overworld and game.overworld.npcs or {}) do
|
|
if n.def and n.def.name == "SILPHCO7F_RIVAL" then return n end
|
|
end
|
|
end
|
|
|
|
local waited = t._SilphCo7FRivalWaitedHereText or ""
|
|
local waitedNeedle = waited:match("waited") and "waited" or "here"
|
|
local parked
|
|
for _ = 1, 600 do
|
|
local r = rivalNpc()
|
|
local s = boxText()
|
|
if r and #ow.scriptMoves == 0 and not r.moving
|
|
and s:find(waitedNeedle, 1, true) then
|
|
parked = r
|
|
break
|
|
end
|
|
if s ~= "" then U.tap(game, "a") end
|
|
U.wait(2)
|
|
end
|
|
check("the rival showed up and stopped walking", parked ~= nil)
|
|
if parked then
|
|
U.log(("rival parked on (%d,%d) facing %s; player on (%d,%d) facing %s")
|
|
:format(parked.cellX, parked.cellY, tostring(parked.facing),
|
|
ow.player.cellX, ow.player.cellY, tostring(ow.player.facing)))
|
|
check(("he is on (%d,%d) live"):format(WANT.rx, WANT.ry),
|
|
parked.cellX == WANT.rx and parked.cellY == WANT.ry)
|
|
end
|
|
|
|
local sawBattle, defeatInBattle, moneyAfter, shotDefeat = false, false, false, false
|
|
for f = 1, 4000 do
|
|
local top = game.stack:top()
|
|
if top and top.phase then
|
|
if not sawBattle then U.log("battle started at loop " .. f) end
|
|
sawBattle = true
|
|
local cur = top.current
|
|
local text = cur and type(cur.text) == "string" and cur.text or ""
|
|
if text:find(needle, 1, true) then
|
|
defeatInBattle = true
|
|
if not shotDefeat then
|
|
shotDefeat = true
|
|
U.log("in-battle loss line: " .. text:gsub("\n", " / "))
|
|
check("the loss line opens with the rival's tag",
|
|
text:sub(1, #game.save.player.rival + 2) == game.save.player.rival .. ": ")
|
|
U.wait(45)
|
|
if U.shot(game, DIR .. "/2241_01_defeat_text_in_battle.png") then
|
|
U.log("captured", DIR .. "/2241_01_defeat_text_in_battle.png")
|
|
end
|
|
end
|
|
elseif defeatInBattle and text:find("for winning", 1, true) then
|
|
moneyAfter = true
|
|
end
|
|
if top.phase == "menu" then top.menuIndex = 1
|
|
elseif top.phase == "moveSelect" then top.moveIndex = 1 end
|
|
U.tap(game, "a")
|
|
if f > 2400 and top.onFinish then
|
|
U.log("force-finishing a stalled battle")
|
|
top.onFinish("win")
|
|
if game.stack:top() == top then game.stack:pop() end
|
|
end
|
|
elseif sawBattle then
|
|
break
|
|
elseif top ~= ow then
|
|
U.tap(game, "a")
|
|
end
|
|
U.wait(2)
|
|
end
|
|
check("the battle ran and ended", sawBattle)
|
|
check("the loss line printed inside the battle state", defeatInBattle)
|
|
check("the money line followed it", moneyAfter)
|
|
|
|
local mapBoxes = {}
|
|
local sawLuck = false
|
|
local luck = t._SilphCo7FRivalGoodLuckToYouText or ""
|
|
local luckNeedle = luck:match("moving on up") and "moving on up" or "luck"
|
|
for _ = 1, 600 do
|
|
local s = boxText()
|
|
if s ~= "" and mapBoxes[#mapBoxes] ~= s then mapBoxes[#mapBoxes + 1] = s end
|
|
if s:find(luckNeedle, 1, true) then sawLuck = true break end
|
|
if game.stack:top() ~= ow then U.tap(game, "a") end
|
|
U.wait(3)
|
|
end
|
|
check("GoodLuckToYou is on screen after the battle", sawLuck)
|
|
check("EVENT_BEAT_SILPH_CO_RIVAL is set",
|
|
game.save.flags.EVENT_BEAT_SILPH_CO_RIVAL == true)
|
|
local leaked = false
|
|
for _, s in ipairs(mapBoxes) do
|
|
if s:find(needle, 1, true) then leaked = true end
|
|
end
|
|
check("no map box carried the loss line", not leaked)
|
|
|
|
local visited, last = {}, nil
|
|
local function sample()
|
|
local r = rivalNpc()
|
|
if not r then return false end
|
|
local function note(x, y)
|
|
local key = ("(%d,%d)"):format(x, y)
|
|
if key ~= last then
|
|
visited[#visited + 1] = key
|
|
last = key
|
|
end
|
|
end
|
|
if r.moving and r.targetX then
|
|
note(r.targetX, r.targetY)
|
|
else
|
|
note(r.cellX, r.cellY)
|
|
end
|
|
return true
|
|
end
|
|
sample()
|
|
local shotWalk = false
|
|
local gone = false
|
|
U.tap(game, "a")
|
|
for _ = 1, 1200 do
|
|
if game.stack:top() ~= ow and getmetatable(game.stack:top()) == TextBox then
|
|
U.tap(game, "a")
|
|
end
|
|
if not sample() then gone = true break end
|
|
if not shotWalk and last == WANT.shotCell then
|
|
shotWalk = true
|
|
U.wait(4)
|
|
if U.shot(game, DIR .. "/2242_02_walk_around_player.png") then
|
|
U.log("captured", DIR .. "/2242_02_walk_around_player.png")
|
|
end
|
|
sample()
|
|
end
|
|
U.wait(1)
|
|
end
|
|
U.log("rival visited: " .. table.concat(visited, " "))
|
|
check("the rival was hidden at the end of the walk", gone)
|
|
local start = ("(%d,%d)"):format(WANT.rx, WANT.ry)
|
|
check("the walk starts from his parked cell", visited[1] == start)
|
|
local path = {}
|
|
for i = 2, #visited do path[#path + 1] = visited[i] end
|
|
check("visited cells are " .. table.concat(WANT.cells, " "),
|
|
table.concat(path, " ") == table.concat(WANT.cells, " "))
|
|
check("he ends on (5,3)", visited[#visited] == "(5,3)")
|
|
if TILE_Y == 3 then
|
|
check("he arrives on (5,3) from (5,2)", visited[#visited - 1] == "(5,2)")
|
|
local bad = false
|
|
for _, c in ipairs(visited) do
|
|
if c == "(4,3)" or c == "(5,4)" then bad = true end
|
|
end
|
|
check("he never steps on (4,3) or the (5,4) desk", not bad)
|
|
end
|
|
check("the rival is gone from the map", rivalNpc() == nil)
|
|
|
|
finish()
|
|
end
|