mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-30 23:37:23 -04:00
154 lines
6.0 KiB
Lua
154 lines
6.0 KiB
Lua
-- Gen 1 Leech Seed drains right after the SEEDED mon's move, not in an
|
|
-- end-of-round sweep (#784): MainInBattleLoop calls
|
|
-- HandlePoisonBurnLeechSeed after every Execute*Move (core.asm:426-464),
|
|
-- and the drain plays the ABSORB animation from the healing side
|
|
-- (core.asm:506-517). The port ran the whole residual sweep in
|
|
-- endOfTurn, after both sides had acted, and showed no drain animation.
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.modkit")
|
|
local Data = T.fixtures.fresh()
|
|
local Font = require("src.render.Font")
|
|
Font.load(Data)
|
|
local BattleState = require("src.battle.BattleState")
|
|
local Pokemon = require("src.pokemon.Pokemon")
|
|
local SaveData = require("src.core.SaveData")
|
|
local TypeChart = require("src.battle.TypeChart")
|
|
TypeChart.load(Data)
|
|
|
|
local function newBattle()
|
|
local save = SaveData.newGame()
|
|
save.party = { Pokemon.new(Data, "FIXMON_A", 30) }
|
|
local game = { data = Data, save = save,
|
|
stack = { top = function() return nil end, push = function() end } }
|
|
local battle = BattleState.newWild(game, "FIXMON_C", 30)
|
|
battle.rng = function() return 0 end -- every roll lands: moves always hit
|
|
battle.enemyAction = function() return { id = "FIX_SCRATCH", pp = 35 } end
|
|
-- the seeded foe moves first, so its drain is observable mid-turn
|
|
battle.enemy.curStats.speed = 200
|
|
battle.player.curStats.speed = 1
|
|
return battle
|
|
end
|
|
|
|
-- consume the queue the way updateQueue does, minus the presentation;
|
|
-- keeps the text and anim rows in order
|
|
local function drain(battle)
|
|
local rows = {}
|
|
for _ = 1, 400 do
|
|
local item = table.remove(battle.queue, 1)
|
|
if not item then return rows end
|
|
if item.text then rows[#rows + 1] = { text = item.text } end
|
|
if item.anim then
|
|
rows[#rows + 1] = { anim = item.anim,
|
|
attackerIsPlayer = item.attackerIsPlayer }
|
|
end
|
|
if item.fn then
|
|
battle.nextInsert = 0
|
|
item.fn()
|
|
end
|
|
end
|
|
error("the turn queue never drained")
|
|
end
|
|
|
|
local function indexOf(rows, pred)
|
|
for i, row in ipairs(rows) do
|
|
if pred(row) then return i end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function saidWith(rows, needle)
|
|
return indexOf(rows, function(r)
|
|
return r.text and r.text:find(needle, 1, true) ~= nil
|
|
end)
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- the seeded foe is faster: its drain lands between the two moves
|
|
-- ---------------------------------------------------------------------
|
|
do
|
|
local battle = newBattle()
|
|
battle.enemy.leechSeeded = true
|
|
battle:resolveTurn({ id = "FIX_TACKLE", pp = 35 })
|
|
local rows = drain(battle)
|
|
|
|
local scratchIdx = saidWith(rows, "FIX SCRATCH")
|
|
local seedIdx = saidWith(rows, "LEECH SEED")
|
|
local tackleIdx = saidWith(rows, "FIX TACKLE")
|
|
T.check(scratchIdx ~= nil, "the foe's move announces")
|
|
T.check(seedIdx ~= nil, "the drain announces")
|
|
T.check(tackleIdx ~= nil, "the player's move announces")
|
|
T.check(scratchIdx < seedIdx,
|
|
"the drain comes right after the seeded mon's move")
|
|
T.check(seedIdx < tackleIdx,
|
|
"and before the slower mon acts, not at end of round")
|
|
|
|
local animIdx = indexOf(rows, function(r) return r.anim == "ABSORB" end)
|
|
T.check(animIdx ~= nil, "the drain plays the ABSORB animation")
|
|
T.check(animIdx ~= nil and rows[animIdx].attackerIsPlayer == true,
|
|
"played from the healing side (hWhoseTurn flipped)")
|
|
T.check(animIdx ~= nil and animIdx > scratchIdx and animIdx < seedIdx,
|
|
"and it plays before the seed text (core.asm:506-523)")
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- a seeded foe at 1 HP faints to its own drain right after moving:
|
|
-- the slower player never gets a move that turn (core.asm:426-429)
|
|
-- ---------------------------------------------------------------------
|
|
do
|
|
local battle = newBattle()
|
|
battle.enemy.leechSeeded = true
|
|
battle.enemy.mon.hp = 1
|
|
battle:resolveTurn({ id = "FIX_TACKLE", pp = 35 })
|
|
local rows = drain(battle)
|
|
|
|
T.check(saidWith(rows, "FIX SCRATCH") ~= nil, "the foe still moves first")
|
|
T.check(saidWith(rows, "LEECH SEED") ~= nil, "the drain still runs")
|
|
T.eq(battle.enemy.mon.hp, 0, "the drain faints the seeded foe")
|
|
T.check(saidWith(rows, "FIX TACKLE") == nil,
|
|
"the slower mon never moves after the residual faint")
|
|
T.eq(battle.result, "win", "and the battle is decided there and then")
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- the modern ruleset keeps the Gen 3+ end-of-round sweep, with no
|
|
-- mid-turn drain animation
|
|
-- ---------------------------------------------------------------------
|
|
do
|
|
local battle = newBattle()
|
|
battle.ruleset = require("src.battle.rulesets.modern_clean")
|
|
battle.enemy.leechSeeded = true
|
|
battle:resolveTurn({ id = "FIX_TACKLE", pp = 35 })
|
|
local rows = drain(battle)
|
|
|
|
local scratchIdx = saidWith(rows, "FIX SCRATCH")
|
|
local seedIdx = saidWith(rows, "LEECH SEED")
|
|
local tackleIdx = saidWith(rows, "FIX TACKLE")
|
|
T.check(scratchIdx ~= nil and tackleIdx ~= nil and seedIdx ~= nil,
|
|
"all three beats still happen under the modern ruleset")
|
|
T.check(scratchIdx < tackleIdx and tackleIdx < seedIdx,
|
|
"but the drain waits for the end of the round")
|
|
T.check(indexOf(rows, function(r) return r.anim == "ABSORB" end) == nil,
|
|
"and no mid-turn drain animation is queued")
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------
|
|
-- an item turn spends the player's move but its residual still ticks
|
|
-- (ExecutePlayerMove rets early on wActionResultOrTookBattleTurn and
|
|
-- MainInBattleLoop calls HandlePoisonBurnLeechSeed anyway)
|
|
-- ---------------------------------------------------------------------
|
|
do
|
|
local battle = newBattle()
|
|
battle.player.mon.status = "PSN"
|
|
battle:itemUsed({})
|
|
local rows = drain(battle)
|
|
|
|
local scratchIdx = saidWith(rows, "FIX SCRATCH")
|
|
local poisonIdx = saidWith(rows, "hurt by poison")
|
|
T.check(scratchIdx ~= nil and poisonIdx ~= nil,
|
|
"the foe moves and the poison ticks on an item turn")
|
|
T.check(scratchIdx < poisonIdx, "the tick lands right after the foe's move")
|
|
end
|
|
|
|
T.finish("leech seed drain timing (#784)")
|