Files
gen1recomp/tests/drivers/bide_silent_bug2338.lua

221 lines
7.6 KiB
Lua

-- engine/battle/effects.asm:764-789
-- engine/battle/core.asm:3481-3506
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pokemon = require("src.pokemon.Pokemon")
local BattleState = require("src.battle.BattleState")
local DIR = os.getenv("POKEPORT_SHOT_DIR") or os.getenv("SHOT_DIR") or "/tmp/shots"
local fails = 0
local function check(label, ok)
if ok then
print("PASS " .. label)
else
print("FAIL " .. label)
fails = fails + 1
end
return ok
end
local function flat(s) return (tostring(s or ""):gsub("[\n\v\f]", " ")) end
game.save.options = game.save.options or {}
game.save.options.animations = true
local lead = Pokemon.new(game.data, "CHARMANDER", 20)
lead.moves = { { id = "BIDE", pp = 10 } }
game.save.party = { lead }
U.teleport(game, "ROUTE_1", 5, 5, "down")
U.wait(15)
local ow = game.overworld
check("standing on ROUTE_1", ow and ow.map and ow.map.id == "ROUTE_1")
local function watcher(battle)
local w = { shown = {}, anims = {}, queued = {} }
local markQ, lastCur = {}, nil
function w.poll()
for _, row in ipairs(battle.queue) do
if not markQ[row] then
markQ[row] = true
if row.text then w.queued[#w.queued + 1] = flat(row.text) end
if row.anim then w.anims[#w.anims + 1] = row.anim end
end
end
local cur = battle.current
if cur ~= lastCur then
lastCur = cur
if cur and cur.text then
w.shown[#w.shown + 1] = flat(cur.text)
return flat(cur.text)
end
end
return nil
end
return w
end
local function has(list, needle)
for _, s in ipairs(list) do
if s:find(needle, 1, true) then return true end
end
return false
end
local function waitPhase(battle, phase, tries, w)
for _ = 1, tries do
if battle.phase == phase then return true end
U.tap(game, "a")
for _ = 1, 6 do w.poll() U.wait(1) end
end
return battle.phase == phase
end
local function pickFirstMove(battle, w)
U.tap(game, "a")
if not waitPhase(battle, "moveSelect", 10, w) then return false end
U.tap(game, "a")
return true
end
local function settledShot(watch, path)
for _ = 1, 100 do watch.poll() U.wait(1) end
return U.shot(game, path)
end
local function leaveBattle()
for _ = 1, 10 do
if game.stack:top() == ow then break end
game.stack:pop()
end
end
local battle = BattleState.newWild(game, "RATTATA", 8)
battle.onFinish = function() end
battle.enemy.mon.moves = { { id = "TACKLE", pp = 35 } }
battle.enemy.curMoves = battle.enemy.mon.moves
ow:pushBattle(battle)
local w = watcher(battle)
check("player battle reached the menu", waitPhase(battle, "menu", 80, w))
local sinceShown, sinceQueued = #w.shown, #w.queued
check("BIDE picked", pickFirstMove(battle, w))
local tackles, unleashed = 0, false
local lockedSeen, sawSpiral = false, false
local shots = {}
for _ = 1, 3000 do
local line = w.poll()
if has(w.anims, "XSTATITEM_ANIM") then sawSpiral = true end
if line then
if line:find("TACKLE", 1, true) then
tackles = tackles + 1
if tackles == 1 and not shots.store then
shots.store = settledShot(w, DIR .. "/2338_01_store_turn_spiral_then_foe_tackle.png")
elseif tackles >= 2 and not shots.locked then
lockedSeen = true
shots.locked = settledShot(w, DIR .. "/2338_02_locked_turn_only_foe_text.png")
end
elseif line:find("unleashed energy", 1, true) then
unleashed = true
shots.release = settledShot(w, DIR .. "/2338_03_release_unleashed_energy.png")
break
end
end
if game.stack:top() ~= battle then
if not game.stack:top() or battle.player.mon.hp <= 0 then break end
end
if U.frame() % 6 == 0 then U.tap(game, "a") else U.wait(1) end
end
local pShown, pQueued = {}, {}
for i = sinceShown + 1, #w.shown do pShown[#pShown + 1] = w.shown[i] end
for i = sinceQueued + 1, #w.queued do pQueued[#pQueued + 1] = w.queued[i] end
local stray = {}
for _, s in ipairs(pShown) do
if s:find("CHARMANDER", 1, true) and not s:find("used BIDE", 1, true)
and not s:find("unleashed energy", 1, true) then
stray[#stray + 1] = s
end
if s:find("unleashed energy", 1, true) then break end
end
U.log("player battle shown: " .. table.concat(pShown, " | "))
check("storing turn played the X-item spiral", sawSpiral)
check("no storing-energy line shown on the player's side",
not has(pShown, "storing energy"))
check("no storing-energy line queued on the player's side",
not has(pQueued, "storing energy"))
check("foe acted on at least one locked turn (" .. tackles .. " TACKLE lines)",
lockedSeen)
check("no CHARMANDER line besides used BIDE before the release: "
.. (#stray > 0 and table.concat(stray, " | ") or "(none)"), #stray == 0)
check("release printed unleashed energy", unleashed)
for _ = 1, 30 do w.poll() U.wait(1) end
check("release played BIDE's own animation", has(w.anims, "BIDE"))
leaveBattle()
lead.hp = lead.stats.hp
lead.status = nil
lead.moves = { { id = "SCRATCH", pp = 35 } }
local foeBattle = BattleState.newWild(game, "RATTATA", 25)
foeBattle.onFinish = function() end
foeBattle.enemy.mon.moves = { { id = "BIDE", pp = 10 } }
foeBattle.enemy.curMoves = foeBattle.enemy.mon.moves
ow:pushBattle(foeBattle)
local fw = watcher(foeBattle)
check("foe battle reached the menu", waitPhase(foeBattle, "menu", 80, fw))
local fSince = #fw.shown
local fQSince = #fw.queued
check("SCRATCH picked", pickFirstMove(foeBattle, fw))
local scratches, foeUnleashed, foeSpiral = 0, false, false
local foeStoreShot = false
for _ = 1, 3000 do
local line = fw.poll()
if has(fw.anims, "XSTATITEM_DUPLICATE_ANIM") then foeSpiral = true end
if line then
if line:find("SCRATCH", 1, true) then
scratches = scratches + 1
if foeSpiral and not foeStoreShot then
foeStoreShot = true
settledShot(fw, DIR .. "/2338_04_foe_store_turn_then_player_move.png")
end
elseif line:find("unleashed energy", 1, true) then
foeUnleashed = true
break
end
end
if game.stack:top() ~= foeBattle then
if not game.stack:top() or foeBattle.enemy.mon.hp <= 0 then break end
end
if U.frame() % 6 == 0 then U.tap(game, "a") else U.wait(1) end
end
local fShown, fQueued = {}, {}
for i = fSince + 1, #fw.shown do fShown[#fShown + 1] = fw.shown[i] end
for i = fQSince + 1, #fw.queued do fQueued[#fQueued + 1] = fw.queued[i] end
local fStray = {}
for _, s in ipairs(fShown) do
if s:find("RATTATA", 1, true) and not s:find("used BIDE", 1, true)
and not s:find("unleashed energy", 1, true) then
fStray[#fStray + 1] = s
end
if s:find("unleashed energy", 1, true) then break end
end
U.log("foe battle shown: " .. table.concat(fShown, " | "))
check("foe's storing turn played XSTATITEM_DUPLICATE_ANIM", foeSpiral)
check("no storing-energy line shown on the foe's side",
not has(fShown, "storing energy"))
check("no storing-energy line queued on the foe's side",
not has(fQueued, "storing energy"))
check("player moved through the foe's locked turns (" .. scratches
.. " SCRATCH lines)", scratches >= 2)
check("no RATTATA line besides used BIDE before the release: "
.. (#fStray > 0 and table.concat(fStray, " | ") or "(none)"), #fStray == 0)
check("foe's release printed unleashed energy", foeUnleashed)
leaveBattle()
print(fails == 0 and "PASS bide_silent_bug2338" or "FAIL bide_silent_bug2338")
love.event.quit(fails == 0 and 0 or 1)
end