mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
93 lines
3.3 KiB
Lua
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
|