Files
gen1recomp/tests/engine/gen2_residual_timing_bug2257.lua

200 lines
6.8 KiB
Lua

-- engine/battle/core.asm:869, engine/battle/core.asm:912, engine/battle/core.asm:1005
package.path = "./?.lua;./?/init.lua;" .. package.path
local T = require("tests.harness")
local Battle = require("src.battle.gen2.Battle")
local Mon = require("src.battle.gen2.Mon")
local TYPES = {
NORMAL = { id = "NORMAL", index = 0, category = "physical" },
}
local MOVES = {
TACKLE = { id = "TACKLE", name = "TACKLE", power = 35, type = "NORMAL",
accuracy = 100, pp = 35, effect = "EFFECT_NORMAL_HIT" },
}
local POKEMON = {
growthRates = {
GROWTH_MEDIUM_FAST = { numerator = 1, denominator = 1, squared = 0,
linear = 0, constant = 0 },
},
MACHOP = { id = "MACHOP", index = 66, name = "MACHOP",
baseStats = { hp = 70, attack = 80, defense = 50, speed = 35,
specialAttack = 35, specialDefense = 35 },
types = { "NORMAL", "NORMAL" }, catchRate = 180, baseExp = 75,
growthRate = "GROWTH_MEDIUM_FAST", genderRatio = 63,
levelMoves = {}, evolutions = {} },
}
local DATA = { pokemon = POKEMON, moves = MOVES,
type_chart = { types = TYPES, matchups = {} }, items = {} }
local perfect = { attack = 15, defense = 15, speed = 15, special = 15 }
perfect.hp = Mon.hpDV(perfect)
local function rng() return 0 end
local function mon(level)
local m = Mon.new(DATA, "MACHOP", level or 50, { dvs = perfect })
m.moves = { { id = "TACKLE", pp = 35, maxPp = 35 } }
return m
end
local TACKLE = { kind = "move", move = "TACKLE" }
local function indexOf(events, pred, from)
for i = from or 1, #events do
if pred(events[i]) then return i end
end
end
local function moveBy(side)
return function(e) return e.kind == "move" and e.side == side end
end
local function tickOf(side, anim)
return function(e)
return e.kind == "damage" and e.side == side and e.anim == anim
end
end
local function count(events, pred)
local n = 0
for _, e in ipairs(events) do if pred(e) then n = n + 1 end end
return n
end
local function hasText(events, sub)
for _, e in ipairs(events) do
if e.text and e.text:find(sub, 1, true) then return true end
end
return false
end
do
local lead, bench = mon(), mon()
local player = mon()
local battle = Battle.new({ data = DATA, party = { player },
trainer = { class = "YOUNGSTER", name = "JOEY", party = { lead, bench } },
random = rng })
player.status = "poison"
player.stats.speed, lead.stats.speed = 200, 1
lead.hp = 1
local hpBefore = player.hp
local events = battle:takeTurn(TACKLE)
T.eq(battle.enemy, bench, "a: the trainer's second mon is sent out")
T.check(not hasText(events, "hurt by poison"),
"a: an OHKO jumps past the attacker's own poison tick")
T.eq(player.hp, hpBefore, "a: the poisoned player loses no HP that round")
end
do
local p1, p2 = mon(), mon()
local lead = mon()
local battle = Battle.new({ data = DATA, party = { p1, p2 },
trainer = { class = "YOUNGSTER", name = "JOEY", party = { lead, mon() } },
random = rng })
lead.status = "poison"
lead.stats.speed, p1.stats.speed = 200, 1
p1.hp = 1
local hpBefore = lead.hp
local events = battle:takeTurn(TACKLE)
T.check((p1.hp or 0) <= 0, "b: the player's lead fainted")
T.check(not hasText(events, "hurt by poison"),
"b: the enemy that KOd skips its poison tick")
T.eq(lead.hp, hpBefore, "b: the poisoned enemy loses no HP that round")
end
do
local player, wild = mon(), mon()
local battle = Battle.new({ data = DATA, party = { player }, wild = wild,
random = rng })
player.status, wild.status = "poison", "poison"
player.stats.speed, wild.stats.speed = 1, 200
local events = battle:takeTurn(TACKLE)
local em = indexOf(events, moveBy("enemy"))
local et = indexOf(events, tickOf("enemy", "ANIM_PSN"))
local pm = indexOf(events, moveBy("player"))
local pt = indexOf(events, tickOf("player", "ANIM_PSN"))
T.check(em and et and pm and pt, "c: both moves and both poison ticks happen")
T.check(em and et and pm and pt and em < et and et < pm and pm < pt,
"c: enemy move, enemy poison, player move, player poison")
end
do
local player, wild = mon(), mon()
local battle = Battle.new({ data = DATA, party = { player }, wild = wild,
random = rng })
player.status = "poison"
player.stats.speed, wild.stats.speed = 200, 1
player.hp = 1
local events = battle:takeTurn(TACKLE)
T.eq(player.hp, 0, "d: the player faints to its own poison")
T.eq(indexOf(events, moveBy("enemy")), nil,
"d: the slower enemy never gets its move")
end
do
local player, wild = mon(), mon()
local battle = Battle.new({ data = DATA, party = { player }, wild = wild,
random = rng })
player.stats.speed, wild.stats.speed = 200, 1
battle:volatile(player).leechSeed = true
local events = battle:takeTurn(TACKLE)
local pm = indexOf(events, moveBy("player"))
local sap = indexOf(events, tickOf("player", "ANIM_SAP"))
local em = indexOf(events, moveBy("enemy"))
T.check(pm and sap and em and pm < sap and sap < em,
"e: Leech Seed drains between the faster mon's move and the slower one's")
end
do
local p1, p2 = mon(), mon()
local wild = mon()
local battle = Battle.new({ data = DATA, party = { p1, p2 }, wild = wild,
random = rng })
p2.status = "poison"
local events = battle:takeTurn({ kind = "switch", index = 2 })
local pt = indexOf(events, tickOf("player", "ANIM_PSN"))
local em = indexOf(events, moveBy("enemy"))
T.eq(count(events, tickOf("player", "ANIM_PSN")), 1,
"f: the switched-in mon's poison ticks exactly once")
T.check(pt and em and pt < em,
"f: after a switch the player's tick lands before the enemy's move")
end
do
local player, wild = mon(), mon()
local battle = Battle.new({ data = DATA, party = { player }, wild = wild,
random = rng })
player.status = "poison"
local events = battle:takeTurn({ kind = "item", item = "POTION" })
local pt = indexOf(events, tickOf("player", "ANIM_PSN"))
local em = indexOf(events, moveBy("enemy"))
T.eq(count(events, tickOf("player", "ANIM_PSN")), 1,
"f: an item turn ticks the player's poison exactly once")
T.check(pt and em and pt < em,
"f: after an item the player's tick lands before the enemy's move")
end
do
local player, wild = mon(), mon()
local battle = Battle.new({ data = DATA, party = { player }, wild = wild,
random = rng })
player.status, wild.status = "poison", "poison"
player.stats.speed, wild.stats.speed = 200, 1
battle.weather, battle.weatherTurns = "sandstorm", 5
local events = battle:takeTurn(TACKLE)
local pt = indexOf(events, tickOf("player", "ANIM_PSN"))
local et = indexOf(events, tickOf("enemy", "ANIM_PSN"))
local sand = indexOf(events, function(e)
return e.kind == "damage" and e.anim == "ANIM_IN_SANDSTORM"
end)
T.check(pt and et and sand and pt < sand and et < sand,
"g: HandleBetweenTurnEffects' sandstorm lands after both poison ticks")
end
T.finish("gen2 residual timing bug 2257")