mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
170 lines
6.6 KiB
Lua
170 lines
6.6 KiB
Lua
#!/usr/bin/env luajit
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
require("tests.game3_cache").requireData("game3_battle_baton_pass_test")
|
|
|
|
package.loaded["src.core.game3.audio"] = setmetatable({}, {
|
|
__index = function() return function() end end,
|
|
})
|
|
|
|
local Moves = require("src.core.game3.battle.moves")
|
|
local ROM = {
|
|
[16] = { effect = 149, power = 40, type = 2, accuracy = 100, pp = 35, secondaryChance = 0, target = 0, priority = 0, flags = 50 },
|
|
[33] = { effect = 0, power = 35, type = 0, accuracy = 95, pp = 35, secondaryChance = 0, target = 0, priority = 0, flags = 51 },
|
|
[93] = { effect = 76, power = 50, type = 14, accuracy = 100, pp = 25, secondaryChance = 10, target = 0, priority = 0, flags = 18 },
|
|
[226] = { effect = 127, power = 0, type = 0, accuracy = 0, pp = 40, secondaryChance = 0, target = 16, priority = 0, flags = 0 },
|
|
}
|
|
Moves._romLoaded = true
|
|
Moves._rom = ROM
|
|
Moves.loadRomPack = function()
|
|
Moves._romLoaded = true
|
|
Moves._rom = ROM
|
|
return true
|
|
end
|
|
|
|
local State = require("src.core.game3.battle.state")
|
|
local Engine = require("src.core.game3.battle.engine")
|
|
local Adapter = require("src.core.game3.battle.adapter")
|
|
local Types = require("src.core.game3.battle.types")
|
|
|
|
local failed, passed = 0, 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
passed = passed + 1
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function mon(o)
|
|
return {
|
|
species = o.species or 1, level = 50, hp = o.hp or 100, maxHp = 100,
|
|
attack = 50, defense = 50, spAtk = 50, spDef = 50, speed = 50,
|
|
ability = 0, nickname = o.nickname, moves = o.moves or { 33 }, pp = { 20, 20, 20, 20 },
|
|
}
|
|
end
|
|
|
|
local function battle(party, foeParty)
|
|
local st = State.new({ wild = false, playerParty = party, foeParty = foeParty })
|
|
st.trainerClassName, st.trainerName = "YOUNGSTER", "BEN"
|
|
st.rng = function(lo, hi) if lo == 1 and hi == 100 then return 1 end return hi end
|
|
return st, Adapter.new(st)
|
|
end
|
|
|
|
local function kinds(out)
|
|
local k = {}
|
|
for _, ev in ipairs(out._anim.events or {}) do k[#k + 1] = ev.kind end
|
|
return table.concat(k, ",")
|
|
end
|
|
|
|
print("=== player Baton Pass pauses for the party menu ===")
|
|
do
|
|
local st, ad = battle({
|
|
mon({ nickname = "ALPHA", moves = { 226 } }),
|
|
mon({ nickname = "BETA" }),
|
|
mon({ nickname = "GAMMA" }),
|
|
}, { mon({ nickname = "FOE" }) })
|
|
st.player.stages.attack = 2
|
|
st.player.substituteHP = 25
|
|
st.player.confusionTurns = 3
|
|
st.interactiveChoices = true
|
|
local out = {}
|
|
Engine.resolveMove(st.player, st.enemy, 226, 1, ad, st, out)
|
|
check(out.pendingChoice and out.pendingChoice.kind == "baton_pass", "resolve returns a baton_pass choice")
|
|
check(#out.pendingChoice.candidates == 2, "two eligible bench mons")
|
|
check(st.player.partyIndex == 1, "no switch before the choice")
|
|
check(kinds(out):find("move", 1, true) ~= nil and kinds(out):find("switch", 1, true) == nil,
|
|
"first half is attack string + anim only")
|
|
check(st.player.mon.pp[1] == 19, "PP spent before the party screen")
|
|
|
|
local out2 = Engine.resumeChoice(st, ad, 3)
|
|
check(st.pendingChoice == nil and out2.pendingChoice == nil, "choice consumed")
|
|
check(st.player.partyIndex == 3 and State.displayName(st.player) == "GAMMA", "picked slot 3 switched in")
|
|
check(st.player.stages.attack == 2 and st.player.substituteHP == 25 and st.player.confusionTurns == 2,
|
|
"stat stages, substitute and confusion passed")
|
|
local goMsg
|
|
for _, ev in ipairs(out2._anim.events) do
|
|
if ev.kind == "msg" and ev.text == "Go! GAMMA!" then goMsg = ev end
|
|
end
|
|
check(goMsg and goMsg.wait == 0, "Go! text before switchinanim without waitmessage")
|
|
local sw
|
|
for _, ev in ipairs(out2._anim.events) do
|
|
if ev.kind == "switch" and ev.reason == "baton_pass" then sw = ev end
|
|
end
|
|
check(sw and sw.from == 1 and sw.to == 3, "switch event 1 -> 3")
|
|
end
|
|
|
|
print("=== non-interactive keeps first eligible ===")
|
|
do
|
|
local st, ad = battle({
|
|
mon({ nickname = "ALPHA", moves = { 226 } }),
|
|
mon({ nickname = "BETA" }),
|
|
mon({ nickname = "GAMMA" }),
|
|
}, { mon({ nickname = "FOE" }) })
|
|
local out = {}
|
|
Engine.resolveMove(st.player, st.enemy, 226, 1, ad, st, out)
|
|
check(out.pendingChoice == nil and st.player.partyIndex == 2, "headless picks slot 2")
|
|
end
|
|
|
|
print("=== enemy Baton Pass uses GetMostSuitableMonToSwitchInto ===")
|
|
do
|
|
local function run(rattataMoves)
|
|
local st, ad = battle({ mon({ nickname = "ALPHA" }) }, {
|
|
mon({ nickname = "PASSER", moves = { 226 } }),
|
|
mon({ species = 16, nickname = "PIDGEY", moves = { 16 } }),
|
|
mon({ species = 19, nickname = "RATTATA", moves = rattataMoves }),
|
|
})
|
|
st.player.type1, st.player.type2 = Types.ID.FIGHTING, nil
|
|
return st, ad
|
|
end
|
|
local st, ad = run({ 33 })
|
|
check(Engine.mostSuitableMon(st, ad, "enemy") == 2,
|
|
"weakest-typed RATTATA has no SE move, falls to PIDGEY with GUST")
|
|
local st2, ad2 = run({ 93 })
|
|
check(Engine.mostSuitableMon(st2, ad2, "enemy") == 3, "RATTATA with CONFUSION wins on typing")
|
|
local out = {}
|
|
st2.interactiveChoices = true
|
|
Engine.resolveMove(st2.enemy, st2.player, 226, 1, ad2, st2, out)
|
|
check(out.pendingChoice == nil and st2.enemy.partyIndex == 3, "enemy never pauses; AI pick switched in")
|
|
end
|
|
|
|
print("=== Battle flow opens the party menu (can't cancel) ===")
|
|
do
|
|
local shown
|
|
package.loaded["src.ui.game3.party_menu"] = {
|
|
show = function(party, overlay, opts) shown = opts end,
|
|
isOpen = function() return false end,
|
|
}
|
|
local Battle = require("src.core.game3.battle.init")
|
|
local Ui = require("src.core.game3.battle.ui")
|
|
local Anim = require("src.core.game3.battle.anim")
|
|
local st, ad = battle({
|
|
mon({ nickname = "ALPHA", moves = { 226 } }),
|
|
mon({ nickname = "BETA" }),
|
|
mon({ nickname = "GAMMA" }),
|
|
}, { mon({ nickname = "FOE" }) })
|
|
Ui.reset({ headless = true })
|
|
Anim.reset({ headless = true })
|
|
Battle._active = true
|
|
Battle._st, Battle._adapter = st, ad
|
|
Battle._headless, Battle._auto = false, false
|
|
Battle._actions = { { user = st.player, target = st.enemy, move = 226, slot = 1 } }
|
|
Battle._actionI = 1
|
|
Battle._metaAct = nil
|
|
Battle._phase = "actions"
|
|
Battle.update(0, nil)
|
|
check(Battle._pendingChoice and Battle._pendingChoice.kind == "baton_pass", "step_action keeps the pending choice")
|
|
Battle._openPendingChoiceForTests()
|
|
check(shown and shown.mode == "battle_faint", "party menu in forced-send-out mode")
|
|
check(shown.validate(1) ~= nil and shown.validate(2) == nil, "active mon rejected, bench accepted")
|
|
shown.onSelect(3)
|
|
check(st.player.partyIndex == 3 and Battle._phase == "animating", "selection resumes the move")
|
|
Battle._active = false
|
|
Battle._st = nil
|
|
end
|
|
|
|
print(string.format("\n%d passed, %d failed", passed, failed))
|
|
os.exit(failed == 0 and 0 or 1)
|