Files
bryanthaboi dee935a4ea CLOSES #2430
2026-09-23 11:58:32 -04:00

196 lines
8.6 KiB
Lua

local U = require("tests.drivers.util")
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/game3_fly_bird"
local failures = 0
local function result(ok, label)
print((ok and "PASS " or "FAIL ") .. label)
if not ok then failures = failures + 1 end
return ok
end
local function finish()
if failures == 0 then
print("PASS fly_bird")
love.event.quit(0)
else
print("FAIL fly_bird failures=" .. failures)
love.event.quit(1)
end
end
return function(game)
for _ = 1, 900 do
if game.phase == "boot" and game.boot then break end
U.wait(1)
end
game:_handleBootAction({ action = "new_game", name = "RED" })
U.wait(240)
local Runtime = require("src.core.game3.runtime")
local FieldEffects = require("src.core.game3.field_effects")
local Field = require("src.core.game3.field")
local session = Runtime.getSession()
if not result(session ~= nil, "new game reached the game3 field") then return finish() end
local function anim(kind)
for _, a in ipairs(FieldEffects._anims or {}) do
if a.kind == kind then return a end
end
return nil
end
-- pokefirered/src/field_effect.c:1065 ReturnToFieldFromFlyMapSelect
local Fade = require("src.ui.game3.fade")
local Map = require("src.core.game3.map")
local Player = require("src.core.game3.player")
Map.load(nil, game, "FR_PALLET_TOWN", { x = 5, y = 8, facing = "down" })
session.x, session.y, session.facing = 5, 8, "down"
Player.cellX, Player.cellY = 5, 8
Player.px, Player.py = 5 * 16, 8 * 16
Player.targetX, Player.targetY = 5, 8
Player.facing = "down"
U.wait(60)
local events = {}
local realBegin, realLoad, realLanding = Fade.begin, Map.load, FieldEffects.startFlyIn
Fade.begin = function(mode, ...)
events[#events + 1] = "fade" .. tostring(mode)
return realBegin(mode, ...)
end
Map.load = function(mod, g, mapId, ...)
events[#events + 1] = "load:" .. tostring(mapId)
return realLoad(mod, g, mapId, ...)
end
FieldEffects.startFlyIn = function(...)
events[#events + 1] = Fade.active and "landing-during-fade" or "landing"
return realLanding(...)
end
local function sequence(mapId)
local want = { "fade0", "fade1", "load:" .. mapId, "fade0", "landing" }
local got = table.concat(events, ",")
local i = 1
for _, e in ipairs(events) do
if e == want[i] then i = i + 1 end
if e == "landing-during-fade" then return false, got end
end
return i > #want, got
end
local function waitFor(pred, frames)
for _ = 1, frames do
if pred() then return true end
U.wait(1)
end
return pred() and true or false
end
local gender = tonumber(session.gender) or 0
local function attachedTo(b)
return Player.spriteXOffset == b.x + b.x2 - 120
and Player.spriteYOffset == b.y + b.y2 - 8 - 72
end
result(Field.flyTo("MAPSEC_PEWTER_CITY") == true, "the fly map select took PEWTER CITY")
-- pokefirered/src/field_effect.c:1073 FieldCallback_UseFly
result(Fade.active and Fade.mode == Fade.MODE.FROM_BLACK and anim("fly_out") == nil,
"the field fades in from black before the takeoff")
waitFor(function() return anim("fly_out") ~= nil end, 600)
local out = anim("fly_out")
if not result(out ~= nil, "the ShowMon cut-in hands over to FldEff_FlyOut") then return finish() end
local sheet = FieldEffects._sheets and FieldEffects._sheets["fly_bird"]
if not result(type(sheet) == "table", "the fly_bird sheet loaded from the cache") then return finish() end
local iw, ih = sheet.image:getDimensions()
result(iw == 64 and ih == 320,
string.format("fly_bird sheet is 64x320, got %dx%d", iw, ih))
result(sheet.quads[4] ~= nil, "the sheet has all five frames")
-- pokefirered/src/field_effect.c:3398 SpriteCB_FlyBirdLeaveBall
waitFor(function() return out.bird and out.bird.d1 >= 96 end, 120)
local b = out.bird
result(b and b.cb == "leave" and b.anim == 0 and b.aff and b.aff.scale > 256,
"the bird leaves the ball alone, growing on its arc (d1=" .. tostring(b and b.d1)
.. " scale=" .. tostring(b and b.aff and b.aff.scale) .. ")")
result(Player.isVisible() and (Player.fieldMoveAnim or 0) > 0,
"the player holds the field-move pose while the bird leaves")
U.still(game, DIR .. "/fly_bird_01_leave_ball.png")
-- pokefirered/src/field_effect.c:3278 FlyOutFieldEffect_BirdSwoopDown
waitFor(function() return out.state == "jump_on" and b.d2 >= 24 end, 200)
result(out.state == "jump_on" and b.cb == "swoop" and b.anim == 0 and Player.facing == "left"
and not Player.flyRide,
"the bird swoops in from the right while the player faces left (state=" .. tostring(out.state) .. ")")
U.still(game, DIR .. "/fly_bird_02_swoop_down.png")
-- pokefirered/src/field_effect.c:3289 FlyOutFieldEffect_JumpOnBird
waitFor(function() return out.state == "fly_off" and out.tTimer >= 4 end, 60)
result(Player.flyRide and Player.spriteYOffset < 0,
"the player jumps on in the ride sprite (y2=" .. tostring(Player.spriteYOffset) .. ")")
U.still(game, DIR .. "/fly_bird_03_jump_on.png")
-- pokefirered/src/field_effect.c:3677 SpriteCB_FlyBirdWithPlayer
waitFor(function() return out.state == "wait_off" and b.d2 >= 86 end, 120)
result(b.anim == gender * 2 + 1 and b.cb == "with_player" and attachedTo(b) and Player.isVisible(),
"the bird flies off with the player riding, anim=" .. tostring(b.anim)
.. " player at " .. tostring(Player.spriteXOffset) .. "," .. tostring(Player.spriteYOffset))
U.still(game, DIR .. "/fly_bird_04_fly_off_ridden.png")
waitFor(function() return anim("fly_out") == nil end, 300)
result(anim("fly_out") == nil and not Player.flyRide, "the takeoff animation ends")
-- pokefirered/src/field_effect.c:1104 FieldCallback_FlyIntoMap
waitFor(function() return anim("fly_in") ~= nil end, 900)
local okSeq, seq = sequence("FR_PEWTER_CITY")
result(okSeq, "fade in, fade out, load PEWTER CITY, fade in from black, then land: " .. seq)
local landing = anim("fly_in")
if not result(landing ~= nil, "the landing at PEWTER CITY queued FldEff_FlyIn") then
return finish()
end
result(session.map == "FR_PEWTER_CITY",
"and it dropped the player in PEWTER CITY, map=" .. tostring(session.map))
-- pokefirered/src/field_effect.c:3550
waitFor(function() return landing.state == "with_bird" and landing.tTimer <= 18 end, 60)
local lb = landing.bird
result(lb and lb.anim == gender * 2 + 2 and lb.attached and attachedTo(lb) and Player.flyRide,
"the landing bird carries the player and the player sprite rides it, anim="
.. tostring(lb and lb.anim) .. " player at "
.. tostring(Player.spriteXOffset) .. "," .. tostring(Player.spriteYOffset))
U.still(game, DIR .. "/fly_bird_05_landing_carried.png")
-- pokefirered/src/field_effect.c:3575 FlyInFieldEffect_JumpOffBird
waitFor(function() return landing.state == "jump_off" and landing.tTimer >= 7 end, 80)
result(landing.state == "jump_off" and not lb.attached and lb.anim == 0,
"the player jumps off and the bird swoops on alone (anim=" .. tostring(lb.anim) .. ")")
U.still(game, DIR .. "/fly_bird_06_jump_off.png")
-- pokefirered/src/field_effect.c:3450 SpriteCB_FlyBirdReturnToBall
waitFor(function() return landing.state == "wait_return" and lb.d3 >= 24 end, 200)
result(landing.state == "wait_return" and lb.cb == "return" and Player.spriteXOffset == 0
and Player.spriteYOffset == 0 and (Player.fieldMoveAnim or 0) > 0,
"the bird returns to the ball while the player holds the pose on the landing cell")
U.still(game, DIR .. "/fly_bird_07_bird_return.png")
waitFor(function() return anim("fly_in") == nil end, 300)
result(anim("fly_in") == nil and Player.facing == "down" and not Player.flyRide,
"the landing animation ends facing south")
waitFor(function() return not Field.locked end, 120)
result(not Field.locked, "the field is unlocked after landing")
local sevii = Field.flyDestination("MAPSEC_ONE_ISLAND")
if not result(sevii ~= nil, "ONE ISLAND has a fly destination") then return finish() end
events = {}
result(Field.flyTo("MAPSEC_ONE_ISLAND") == true, "the fly map select took ONE ISLAND")
waitFor(function()
local l = anim("fly_in")
return session.map == sevii.map and l and l.state == "jump_off"
end, 1500)
U.still(game, DIR .. "/fly_bird_08_one_island_landing.png")
local okSevii, seqSevii = sequence(sevii.map)
result(okSevii, "Kanto to ONE ISLAND fades in from black before the bird lands: " .. seqSevii)
waitFor(function() return anim("fly_in") == nil and not Field.locked end, 900)
result(session.map == sevii.map and not Field.locked,
"the Sevii landing finishes on " .. tostring(session.map))
Fade.begin, Map.load, FieldEffects.startFlyIn = realBegin, realLoad, realLanding
finish()
end