Files
gen1recomp/tests/game3_battle_ghost_test.lua
bryanthaboi dee935a4ea CLOSES #2430
2026-09-23 11:58:32 -04:00

179 lines
7.0 KiB
Lua

#!/usr/bin/env luajit
package.path = "./?.lua;./?/init.lua;" .. package.path
require("tests.game3_cache").requireData("game3_battle_ghost_test")
package.loaded["src.core.game3.audio"] = setmetatable({}, {
__index = function() return function() end end,
})
local Moves = require("src.core.game3.battle.moves")
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 Abilities = require("src.core.game3.battle.abilities")
local Residuals = require("src.core.game3.battle.residual_handlers")
local CatchSeq = require("src.core.game3.battle.catch_seq")
local Ui = require("src.core.game3.battle.ui")
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 = o.speed or 50,
ability = o.ability or 0, nickname = o.nickname, moves = o.moves or { 33 }, pp = { 20, 20, 20, 20 },
}
end
local function battle(opts)
opts = opts or {}
local st = State.new({
wild = true,
playerParty = { mon({ nickname = "ALPHA", ability = opts.playerAbility }) },
foeParty = { mon({ species = opts.foeSpecies or 92, nickname = "GHOST" }) },
})
st.ghostBattle = true
st.ghostUnveiled = opts.unveiled
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 use(st, ad, user, move)
local target = (user == st.player) and st.enemy or st.player
local out = {}
Engine.resolveMove(user, target, move, 1, ad, st, out)
return table.concat(out, " || "), out
end
local function find_event(out, pred)
for _, ev in ipairs(out._anim.events or {}) do
if pred(ev) then return ev end
end
end
print("=== player too scared to move ===")
do
local st, ad = battle()
local txt, out = use(st, ad, st.player, 33)
check(txt:find("ALPHA is too scared to move!", 1, true) ~= nil, "too scared text")
check(txt:find("used", 1, true) == nil, "no attack string")
check(st.enemy.mon.hp == 100, "ghost takes no damage")
check(st.player.mon.pp[1] == 20, "no PP spent")
check(find_event(out, function(e) return e.kind == "anim" and e.name == "MON_SCARED" end) ~= nil,
"MON_SCARED general anim launched")
end
print("=== ghost Get out ===")
do
local st, ad = battle()
local txt, out = use(st, ad, st.enemy, 33)
check(txt:find("GHOST: Get out…… Get out……", 1, true) ~= nil, "Get out text")
check(st.player.mon.hp == 100, "player takes no damage")
local msg = find_event(out, function(e) return e.kind == "msg" and e.text:find("Get out", 1, true) end)
check(msg and msg.wait == 0, "Get out text has no waitmessage (anim plays under it)")
local gi = find_event(out, function(e) return e.kind == "anim" and e.name == "GHOST_GET_OUT" end)
check(gi ~= nil and gi.attacker == "enemy", "GHOST_GET_OUT general anim on the ghost")
end
print("=== silph scope unveiled battles fight normally ===")
do
local st, ad = battle({ unveiled = true, foeSpecies = 105 })
local txt = use(st, ad, st.player, 33)
check(txt:find("used", 1, true) ~= nil and st.enemy.mon.hp < 100, "unveiled ghost can be hit")
end
print("=== run / intimidate / weather ===")
do
local st, ad = battle()
st.player.mon.speed = 1
st.enemy.mon.speed = 255
check(Engine.tryFlee(st, ad, st.player) == true, "player always escapes a ghost")
local st2, ad2 = battle({ playerAbility = "INTIMIDATE" })
st2.player.ability = "INTIMIDATE"
check(Abilities.switchIn(ad2, st2.player) == false and not st2.player.expIntimidatePending,
"INTIMIDATE does not activate in a ghost battle")
check(Abilities.runIntimidate(ad2) == false and st2.enemy.stages.attack == 0, "ghost attack not cut")
local st3, ad3 = battle()
st3.weather, st3.weatherTurns = "SAND", 3
local mark = ad3:eventMark()
ad3._say = function() end
Residuals.tickWeather(ad3)
local buffetedGhost, buffetedPlayer = false, false
for _, e in ipairs(ad3:eventsSince(mark)) do
if e.kind == "msg" and e.text:find("buffeted", 1, true) then
if e.text:find("GHOST", 1, true) then buffetedGhost = true else buffetedPlayer = true end
end
end
check(not buffetedGhost and buffetedPlayer, "sandstorm skips the ghost only")
end
print("=== ball dodge ===")
do
local st = battle()
local msgs = {}
CatchSeq.begin(st, 4, false, 0, {
headless = true, ghostDodge = true, pushMsg = function(t) msgs[#msgs + 1] = t end,
session = { name = "RED" },
})
check(msgs[#msgs] == "It dodged the thrown BALL!\nThis POKéMON can't be caught!", "dodge text")
check(CatchSeq.result() == "fail_catch", "no catch")
end
print("=== headless battle flow ===")
do
local Battle = require("src.core.game3.battle.init")
local Damage = require("src.core.game3.battle.damage")
local pMon = Damage.ensureStats({ species = 1, level = 30, hp = 90, maxHp = 90, moves = { 33 }, pp = { 35 } })
local foe = { species = 92, level = 20, hp = 50, maxHp = 50, moves = { 33 }, pp = { 35 }, ghost = true }
Battle.start({ playerName = "RED", headless = true, autoFight = false, playerParty = { pMon }, foe = foe, wild = true })
local st = Battle.getState()
check(st.ghostBattle == true and st.enemy.mon.nickname == "GHOST", "foe.ghost starts a GHOST battle")
local log = Ui.log()
check(log[1] == "The GHOST appeared!\\pDarn!\nThe GHOST can't be ID'd!\\p", "can't be ID'd intro")
check(log[2] == "Go! BULBASAUR!", "then Go!")
Battle._phase = "command"
Ui._pendingCommand = { kind = "bag", itemId = 4, user = "player" }
Battle.update(0, nil)
for _ = 1, 6 do Battle.update(0, nil) end
log = Ui.log()
local dodged, getOut = false, false
for _, t in ipairs(log) do
if t:find("dodged the thrown BALL", 1, true) then dodged = true end
if t:find("Get out", 1, true) then getOut = true end
end
check(dodged and getOut and Battle.isActive(), "ball dodged, ghost acts, battle continues")
Battle.abort()
local foe2 = { species = 105, level = 30, hp = 60, maxHp = 60, moves = { 33 }, pp = { 35 }, ghost = true, ghostUnveiled = true }
Battle.start({ playerName = "RED", headless = true, autoFight = false, playerParty = { pMon }, foe = foe2, wild = true })
st = Battle.getState()
log = Ui.log()
check(log[1] == "The GHOST appeared!\\p" and log[2] == "SILPH SCOPE unveiled the GHOST's\nidentity!"
and log[3] == "The GHOST was MAROWAK!\\p", "silph scope reveal text")
check(st.enemy.mon.nickname == nil and State.displayName(st.enemy) ~= "GHOST", "unveiled name restored")
Battle.abort()
end
print("=== pic placement ===")
do
local base = { x = 176, y = 40 }
local _, gy = Ui.battlerSpriteCenter("enemy", 92, base, 0, true)
check(gy == 40, "GHOST pic sits at BATTLER_COORD_Y")
local _, ny = Ui.battlerSpriteCenter("enemy", 92, base, 0, false)
check(ny ~= 40, "real mon uses pic offset")
end
print(string.format("\n%d passed, %d failed", passed, failed))
os.exit(failed == 0 and 0 or 1)