mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
187 lines
7.7 KiB
Lua
187 lines
7.7 KiB
Lua
-- Look at the two boxes a test cannot judge: the bike shop's BICYCLE/CANCEL
|
|
-- price window (#568) and a Pokemon Center bench guy answering A (#488).
|
|
-- Oracle: scripts/BikeShop.asm BikeShopClerkText (TextBoxBorder 0,0 b=4 c=15
|
|
-- over BikeShopMenuText/BikeShopMenuPrice) and data/events/bench_guys.asm.
|
|
-- The logic halves are asserted in tests/parity_bike_shop_clerk_bug568.lua
|
|
-- and tests/parity_bench_guy_facing_bug488.lua.
|
|
-- POKEPORT_DRIVER=tests/drivers/bike_shop_bug568_test.lua POKEPORT_IDENTITY=bug568 POKEPORT_TOUCH=0 POKEPORT_VERSION=red love .
|
|
return function(game)
|
|
local U = dofile("tests/drivers/util.lua")
|
|
local TextBox = require("src.render.TextBox")
|
|
local SHOT_DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
|
|
|
local failures = 0
|
|
local function check(label, ok)
|
|
if not ok then failures = failures + 1 end
|
|
U.log(ok and "PASS" or "FAIL", label)
|
|
return ok
|
|
end
|
|
|
|
local function boxOnScreen()
|
|
local top = game.stack:top()
|
|
if getmetatable(top) ~= TextBox then return nil end
|
|
local lines = {}
|
|
for _, page in ipairs(top.pages or {}) do
|
|
for _, line in ipairs(page) do lines[#lines + 1] = line end
|
|
end
|
|
return top, table.concat(lines, " / ")
|
|
end
|
|
|
|
-- A missing string or a renamed label goes quiet in exactly the way both
|
|
-- bugs did, so resolve everything first and say so before anyone looks.
|
|
local t = game.data.text
|
|
for _, label in ipairs({ "_BikeShopClerkWelcomeText",
|
|
"_BikeShopClerkDoYouLikeItText", "_BikeShopCantAffordText",
|
|
"_BikeShopComeAgainText", "_VermilionPokecenterGuyText" }) do
|
|
check(label .. " resolves", type(t[label]) == "string" and t[label] ~= "")
|
|
end
|
|
check("BICYCLE is an item the window can name",
|
|
game.data.items.BICYCLE ~= nil)
|
|
|
|
local seat = (game.data.field.hiddenExtras.benchGuys.VERMILION_POKECENTER
|
|
or {})[1]
|
|
check("the Vermilion bench guy is in the generated data", seat ~= nil)
|
|
check("his seat is faced from the left (BenchGuyTextPointers)",
|
|
seat and seat.textFacing == "left")
|
|
|
|
if failures > 0 then
|
|
U.log(failures, "check(s) failed above; the screens below will not tell")
|
|
U.log("you anything the log has not already said.")
|
|
end
|
|
|
|
-- ------------------------------------------------------------- #488
|
|
-- data/events/hidden_events.asm parks the seat at (0,4); it is the bench
|
|
-- wall itself, so the player stands on the floor tile east of it. The
|
|
-- cell north of the seat is walkable too and is the control: in the
|
|
-- original, PrintBenchGuyText only answers a LEFT facing, so an A press
|
|
-- from up there does nothing, and that is correct.
|
|
local BENCH = { map = "VERMILION_POKECENTER", x = seat and seat.x or 0,
|
|
y = seat and seat.y or 4 }
|
|
|
|
U.teleport(game, BENCH.map, BENCH.x, BENCH.y - 1, "down")
|
|
U.wait(10)
|
|
U.tap(game, "a")
|
|
U.wait(20)
|
|
check("facing the seat from the north stays silent, like the original",
|
|
boxOnScreen() == nil)
|
|
|
|
-- the seat's own approach, with a fallback in case a map edit walls it in
|
|
local stand = { x = BENCH.x + 1, y = BENCH.y, facing = "left" }
|
|
if not game.overworld.map:isWalkableCell(stand.x, stand.y) then
|
|
for _, side in ipairs({ { -1, 0, "right" }, { 0, 1, "up" }, { 0, -1, "down" } }) do
|
|
local cx, cy = BENCH.x + side[1], BENCH.y + side[2]
|
|
if game.overworld.map:isWalkableCell(cx, cy) then
|
|
U.log("the floor east of the seat is gone; standing at", cx, cy)
|
|
stand = { x = cx, y = cy, facing = side[3] }
|
|
break
|
|
end
|
|
end
|
|
end
|
|
U.teleport(game, BENCH.map, stand.x, stand.y, stand.facing)
|
|
U.wait(10)
|
|
U.tap(game, "a")
|
|
U.wait(90) -- let the first page finish typing before the shot
|
|
local box, text = boxOnScreen()
|
|
check("A from the east opens the bench guy's box (#488)", box ~= nil)
|
|
if box then
|
|
U.log("bench guy reads:", text)
|
|
check("it is his line about no universally strong POKeMON",
|
|
text:find("universally", 1, true) ~= nil)
|
|
check("screenshot on disk",
|
|
U.shot(game, SHOT_DIR .. "/bug488_bench_guy.png"))
|
|
U.log("captured", SHOT_DIR .. "/bug488_bench_guy.png")
|
|
U.tap(game, "a")
|
|
U.wait(20)
|
|
end
|
|
|
|
-- ------------------------------------------------------------- #568
|
|
-- pokered data/maps/objects/BikeShop.asm: the clerk stands at (6,2)
|
|
-- behind the counter row, so the player talks across it from (6,4).
|
|
-- Clear the bike and the voucher so the talk takes the sales pitch.
|
|
game.save.inventory.BICYCLE = nil
|
|
game.save.inventory.BIKE_VOUCHER = nil
|
|
game.save.flags.EVENT_GOT_BICYCLE = nil
|
|
|
|
U.teleport(game, "BIKE_SHOP", 6, 4, "up")
|
|
U.wait(10)
|
|
|
|
local ow = game.overworld
|
|
local clerk
|
|
for _, n in ipairs(ow.npcs or {}) do
|
|
if n.def and n.def.name == "BIKESHOP_CLERK" then clerk = n end
|
|
-- the middle-aged woman paces; park her so she cannot wander into shot
|
|
if n.def and n.def.name == "BIKESHOP_MIDDLE_AGED_WOMAN" then
|
|
n.frozen = true
|
|
end
|
|
end
|
|
check("the clerk is on the map", clerk ~= nil)
|
|
|
|
-- talking across a counter reads one cell further, so the stand cell is
|
|
-- two south of him; fall back to any free neighbour if the shop is redrawn
|
|
local function facingTheClerk()
|
|
local w = game.overworld
|
|
local fx, fy = w.player:facingCell()
|
|
if w:npcAtCell(fx, fy) == clerk then return true end
|
|
if not w.map:isCounterCell(fx, fy) then return false end
|
|
local Collision = require("src.world.Collision")
|
|
local bx, by = Collision.target(fx, fy, w.player.facing)
|
|
return w:npcAtCell(bx, by) == clerk
|
|
end
|
|
if clerk and not facingTheClerk() then
|
|
for _, side in ipairs({ { 0, 2, "up" }, { 0, 1, "up" }, { 0, -1, "down" },
|
|
{ 1, 0, "left" }, { -1, 0, "right" } }) do
|
|
local cx, cy = clerk.cellX + side[1], clerk.cellY + side[2]
|
|
if ow.map:isWalkableCell(cx, cy) and not ow:npcAtCell(cx, cy) then
|
|
U.log("(6,4) no longer reaches the counter; standing at", cx, cy)
|
|
U.teleport(game, "BIKE_SHOP", cx, cy, side[3])
|
|
U.wait(10)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
check("the player is at the counter", facingTheClerk())
|
|
|
|
U.tap(game, "a")
|
|
U.wait(20)
|
|
local _, welcome = boxOnScreen()
|
|
check("A opens the welcome line", welcome ~= nil)
|
|
if welcome then U.log("clerk reads:", welcome) end
|
|
|
|
-- mash through the welcome box; the pitch box then types itself out and
|
|
-- pops on its own, handing the screen to the price window
|
|
local window
|
|
for _ = 1, 200 do
|
|
local top = game.stack:top()
|
|
if top ~= game.overworld and getmetatable(top) ~= TextBox then
|
|
window = top
|
|
break
|
|
end
|
|
U.tap(game, "a")
|
|
U.wait(4)
|
|
end
|
|
check("the price window is up and holding input (#568)", window ~= nil)
|
|
if window then
|
|
check("the cursor starts on BICYCLE", window.index == 1)
|
|
check("it kept the pitch line for the bottom box",
|
|
type(window.footer) == "table" and #window.footer > 0)
|
|
check("screenshot on disk",
|
|
U.shot(game, SHOT_DIR .. "/bug568_price_window.png"))
|
|
U.log("captured", SHOT_DIR .. "/bug568_price_window.png")
|
|
end
|
|
|
|
U.log("The window on screen is the shop menu: BICYCLE over CANCEL at the")
|
|
U.log("top left, \194\1651000000 beside the BICYCLE row, cursor on BICYCLE, and")
|
|
U.log("\"It's a cool BIKE! Do you want it?\" still in the bottom box under it.")
|
|
U.log("Input is live. Up and down move between the two rows and clamp -- the")
|
|
U.log("cursor must not wrap. A buys, B or CANCEL backs out; either way the")
|
|
U.log("window has to stay on screen behind the closing lines and only")
|
|
U.log("disappear with \"Come back again some time!\". A window that vanishes")
|
|
U.log("the moment you answer, or a bottom box that blanks while the menu is")
|
|
U.log("up, is the near miss to watch for. Talk to him again to replay it.")
|
|
U.log("The boy by the door is the control: he has no menu, just a line.")
|
|
|
|
while true do
|
|
coroutine.yield()
|
|
end
|
|
end
|