Files
gen1recomp/tests/drivers/gold_battle_anim_shots.lua
bryanthaboi ae6cac89e1 G2 support
2026-08-11 11:53:30 -04:00

93 lines
3.3 KiB
Lua

-- Contact sheet: the Gen 2 battle-animation runtime, frame by frame.
--
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/gold_battle_anim_shots.lua love .
--
-- Shoots the 72-frame intro slide and then a run of move animations, one shot
-- every few frames, into /tmp/gold-anims. This is the only way to check the
-- object functions: they are pure arithmetic on byte fields and a test can say
-- "the struct moved", but only a picture says the flame went the right way.
--
-- POKEPORT_ANIM_MOVES=TACKLE,EMBER picks the moves; the default set covers one
-- animation from each family the runtime has to get right (a straight throw,
-- a spiral, a screen shake, a per-scanline sink, a palette cycle).
local U = require("tests.drivers.util")
local Mon = require("src.battle.gen2.Mon")
local DEFAULT_MOVES = {
"TACKLE", "EMBER", "WATER_GUN", "THUNDERSHOCK", "RAZOR_LEAF",
"EARTHQUAKE", "WITHDRAW", "DIG", "SING", "ABSORB",
-- The screen-wide deformations, which were no-ops until the sixth pass:
-- rolling water, a warp, an afterimage and a melt.
"SURF", "WHIRLPOOL", "PSYCHIC_M", "TELEPORT", "NIGHT_SHADE",
"DOUBLE_TEAM", "ACID_ARMOR",
}
return function(game)
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-anims"
local interval = tonumber(os.getenv("POKEPORT_SHOT_INTERVAL") or "4")
local moves = {}
local requested = os.getenv("POKEPORT_ANIM_MOVES")
if requested then
for name in requested:gmatch("[^,]+") do moves[#moves + 1] = name end
else
moves = DEFAULT_MOVES
end
U.wait(45)
local world = game.world
assert(world and world.map, "gold world did not boot")
local player = Mon.new(game.data, "CYNDAQUIL", 30)
assert(player, "could not build a CYNDAQUIL")
game.save.party = { player }
local wild = Mon.new(game.data, "PIDGEY", 30)
assert(world:startBattle({ wild = wild }), "startBattle failed")
-- DoBattleTransition owns the screen first now; wait it out.
local battle
for _ = 1, 600 do
local top = game.stack:top()
if top and top.battle then battle = top break end
U.wait(1)
end
assert(battle and battle.battle, "battle screen is not on the stack")
assert(battle.anims and battle.anims.scripts,
"battle_anims.lua has no scripts -- re-import Gold")
-- The intro slide, which runs before any input is read.
for frame = 0, 72, 6 do
U.shot(game, ("%s/00-slide-%02d.png"):format(out, frame))
U.wait(6)
end
-- Then each move's own animation, started directly rather than through the
-- menu so the shot numbering stays predictable.
local missing = {}
for index, move in ipairs(moves) do
battle.anim = nil
if not battle:animForMove(move, "player") then
missing[#missing + 1] = move
else
-- BattleState:update steps the runner itself, so the driver only waits
-- and shoots; stepping here too would run it at double speed.
local shot = 0
while battle.anim and shot < 400 do
if shot % interval == 0 then
U.shot(game, ("%s/%02d-%s-%03d.png"):format(out, index, move, shot))
end
shot = shot + 1
U.wait(1)
end
assert(shot < 400, move .. " never finished")
print(("[driver] %-14s %d frames"):format(move, shot))
end
end
if #missing > 0 then
print("[driver] no animation for: " .. table.concat(missing, ", "))
end
print("[driver] PASS gold battle anims in " .. out)
love.event.quit()
end