mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 05:32:07 -04:00
482 lines
24 KiB
Lua
482 lines
24 KiB
Lua
#!/usr/bin/env luajit
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
local GameCache = require("tests.game3_cache")
|
|
if not GameCache.bundle() then
|
|
print("[skip] game3 battle items + abilities: " .. tostring(GameCache.reason))
|
|
os.exit(0)
|
|
end
|
|
require("tests.fixture_data.game3_items").install()
|
|
|
|
local Moves = require("src.core.game3.battle.moves")
|
|
|
|
-- pokefirered/src/data/battle_moves.h:1
|
|
local ROM = {
|
|
[1] = { effect = 0, power = 40, type = 0, accuracy = 100, pp = 35, secondaryChance = 0, target = 0, priority = 0, flags = 51 },
|
|
[10] = { effect = 0, power = 40, type = 0, accuracy = 100, pp = 35, secondaryChance = 0, target = 0, priority = 0, flags = 51 },
|
|
[12] = { effect = 38, power = 1, type = 0, accuracy = 30, pp = 5, secondaryChance = 0, target = 0, priority = 0, flags = 19 },
|
|
[24] = { effect = 44, power = 30, type = 1, accuracy = 100, pp = 30, secondaryChance = 0, target = 0, priority = 0, flags = 51 },
|
|
[33] = { effect = 0, power = 35, type = 0, accuracy = 95, pp = 35, secondaryChance = 0, target = 0, priority = 0, flags = 51 },
|
|
[45] = { effect = 18, power = 0, type = 0, accuracy = 100, pp = 40, secondaryChance = 0, target = 8, priority = 0, flags = 22 },
|
|
[52] = { effect = 4, power = 40, type = 10, accuracy = 100, pp = 25, secondaryChance = 10, target = 0, priority = 0, flags = 18 },
|
|
[53] = { effect = 4, power = 95, type = 10, accuracy = 100, pp = 15, secondaryChance = 10, target = 0, priority = 0, flags = 18 },
|
|
[55] = { effect = 0, power = 40, type = 11, accuracy = 100, pp = 25, secondaryChance = 0, target = 0, priority = 0, flags = 50 },
|
|
[85] = { effect = 6, power = 95, type = 13, accuracy = 100, pp = 15, secondaryChance = 10, target = 0, priority = 0, flags = 18 },
|
|
[86] = { effect = 67, power = 0, type = 13, accuracy = 100, pp = 20, secondaryChance = 0, target = 0, priority = 0, flags = 22 },
|
|
[92] = { effect = 33, power = 0, type = 3, accuracy = 85, pp = 10, secondaryChance = 100, target = 0, priority = 0, flags = 22 },
|
|
[150] = { effect = 85, power = 0, type = 0, accuracy = 0, pp = 40, secondaryChance = 0, target = 16, priority = 0, flags = 0 },
|
|
[163] = { effect = 43, power = 70, type = 0, accuracy = 100, pp = 20, secondaryChance = 0, target = 0, priority = 0, flags = 51 },
|
|
[240] = { effect = 136, power = 0, type = 11, accuracy = 0, pp = 5, secondaryChance = 0, target = 16, priority = 0, flags = 0 },
|
|
}
|
|
for id, row in pairs(ROM) do row.numId = id end
|
|
Moves._romLoaded = true
|
|
Moves._rom = ROM
|
|
|
|
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 Damage = require("src.core.game3.battle.damage")
|
|
local Types = require("src.core.game3.battle.types")
|
|
local HeldItems = require("src.core.game3.battle.held_items")
|
|
local T = Types.ID
|
|
|
|
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 = o.level or 50, hp = o.hp or 100, maxHp = o.maxHp or 100,
|
|
attack = o.attack or 50, defense = o.defense or 50, spAtk = o.spAtk or 50, spDef = o.spDef or 50,
|
|
speed = o.speed or 50, ability = o.ability or 0, nickname = o.nickname,
|
|
moves = o.moves or { 33 }, pp = o.pp or { 20, 20, 20, 20 }, maxPp = o.maxPp, status = o.status,
|
|
item = o.item, heldItem = o.item, gender = o.gender, personality = o.personality or 0,
|
|
otId = o.otId, otName = o.otName,
|
|
}
|
|
end
|
|
|
|
local function mkRng(map)
|
|
map = map or {}
|
|
local cursor = {}
|
|
return function(lo, hi)
|
|
local key = tostring(lo) .. "," .. tostring(hi)
|
|
local v = map[key]
|
|
if type(v) == "table" then
|
|
local i = (cursor[key] or 0) + 1
|
|
cursor[key] = i
|
|
v = v[math.min(i, #v)]
|
|
end
|
|
if v ~= nil then return v end
|
|
if lo == 1 and hi == 100 then return 1 end
|
|
return hi
|
|
end
|
|
end
|
|
|
|
local function battle(p, e, extra)
|
|
extra = extra or {}
|
|
p.nickname = p.nickname or "ALPHA"
|
|
e.nickname = e.nickname or "BRAVO"
|
|
local party = { mon(p) }
|
|
for _, x in ipairs(extra.bench or {}) do party[#party + 1] = mon(x) end
|
|
local foeParty = { mon(e) }
|
|
for _, x in ipairs(extra.foeBench or {}) do foeParty[#foeParty + 1] = mon(x) end
|
|
local st = State.new({ wild = extra.wild ~= false, playerParty = party, foeParty = foeParty })
|
|
st.player.type1, st.player.type2 = p.type1 or T.NORMAL, p.type2
|
|
st.enemy.type1, st.enemy.type2 = e.type1 or T.NORMAL, e.type2
|
|
st.player.species = p.species or st.player.species
|
|
st.enemy.species = e.species or st.enemy.species
|
|
st.rng = mkRng(extra.rng)
|
|
st.badges = extra.badges
|
|
st.playerTrainerId = extra.playerTrainerId
|
|
st.playerName = extra.playerName
|
|
local ad = Adapter.new(st)
|
|
return st, ad
|
|
end
|
|
|
|
local function use(st, ad, user, move, slot)
|
|
local target = (user == st.player) and st.enemy or st.player
|
|
local out = {}
|
|
Engine.resolveMove(user, target, move, slot or 1, ad, st, out)
|
|
return table.concat(out, " || "), out
|
|
end
|
|
|
|
local function eot(st, ad)
|
|
local events = Engine.collectResidualEvents(st, ad)
|
|
local msgs, evs = {}, {}
|
|
for _, ev in ipairs(events) do
|
|
for _, m in ipairs(ev.msgs or {}) do msgs[#msgs + 1] = m end
|
|
for _, e in ipairs(ev.events or {}) do evs[#evs + 1] = e end
|
|
end
|
|
return table.concat(msgs, " || "), evs
|
|
end
|
|
|
|
local function has(s, needle) return s:find(needle, 1, true) ~= nil end
|
|
|
|
local function hasAnim(evs, name, side)
|
|
for _, e in ipairs(evs or {}) do
|
|
if e.kind == "anim" and e.name == name and (side == nil or e.target == side) then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function hpEvent(evs, side)
|
|
for _, e in ipairs(evs or {}) do
|
|
if e.kind == "hp" and e.side == side then return e end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
print("=== LEFTOVERS ===")
|
|
do
|
|
local st, ad = battle({ item = 200, hp = 50, maxHp = 160 }, {})
|
|
local txt, evs = eot(st, ad)
|
|
check(ad:hp(st.player) == 60, "Leftovers heals maxHP/16 at end of turn")
|
|
check(has(txt, "ALPHA's LEFTOVERS\nrestored its HP a little!") or has(txt, "restored its HP a little!"), "Leftovers message")
|
|
check(hasAnim(evs, "HELD_ITEM_EFFECT", "player") and hpEvent(evs, "player") ~= nil, "Leftovers plays HELD_ITEM_EFFECT and emits hp event")
|
|
check(st.player.item == 200, "Leftovers is not consumed")
|
|
end
|
|
|
|
print("=== ORAN / SITRUS (consumed, persisted, recyclable) ===")
|
|
do
|
|
local st, ad = battle({ item = 139, hp = 50, maxHp = 100 }, {})
|
|
eot(st, ad)
|
|
check(ad:hp(st.player) == 60, "Oran Berry restores 10 HP at <= 1/2")
|
|
check(st.player.item == 0 and st.playerParty[1].item == nil, "consumed berry is removed from the party mon")
|
|
check(st.playerSide.expUsedHeldItem == 139, "used held item recorded for Recycle")
|
|
|
|
local st2, ad2 = battle({ item = 142, hp = 51, maxHp = 100 }, {})
|
|
eot(st2, ad2)
|
|
check(ad2:hp(st2.player) == 51 and st2.player.item == 142, "Sitrus waits until HP <= 1/2")
|
|
|
|
local st3, ad3 = battle({ item = 139, hp = 10, maxHp = 100 }, { moves = { 33 } })
|
|
use(st3, ad3, st3.enemy, 33)
|
|
check(st3.player.item == 139, "HP berries do not trigger mid-turn (moveTurn)")
|
|
end
|
|
|
|
print("=== CHERI / LUM / PERSIM at move end ===")
|
|
do
|
|
local st, ad = battle({ item = 133 }, { moves = { 86 } })
|
|
local txt = use(st, ad, st.enemy, 86)
|
|
check(ad:status(st.player) == nil and st.player.item == 0, "Cheri Berry cures Thunder Wave paralysis right away")
|
|
check(has(txt, "ALPHA's CHERI BERRY\ncured paralysis!"), "Cheri message")
|
|
|
|
local st2, ad2 = battle({ item = 141, status = "PAR" }, {})
|
|
st2.player.confusionTurns = 3
|
|
local txt2 = eot(st2, ad2)
|
|
check(has(txt2, "LUM BERRY\nnormalized its status!") and ad2:status(st2.player) == nil
|
|
and (st2.player.confusionTurns or 0) == 0, "Lum Berry normalizes multiple problems at end of turn")
|
|
|
|
local st3, ad3 = battle({ item = 140 }, { moves = { 150 } })
|
|
st3.player.confusionTurns = 3
|
|
local txt3 = use(st3, ad3, st3.enemy, 150)
|
|
check(has(txt3, "snapped it out of confusion!") and (st3.player.confusionTurns or 0) == 0, "Persim Berry at move end")
|
|
end
|
|
|
|
print("=== CONFUSION BERRIES (flavor dislike) ===")
|
|
do
|
|
local st, ad = battle({ item = 143, hp = 40, maxHp = 80, personality = 15 }, {}, { rng = { ["0,3"] = 1 } })
|
|
local txt = eot(st, ad)
|
|
check(ad:hp(st.player) == 50, "Figy restores maxHP/8")
|
|
check(has(txt, "For ALPHA,\nFIGY BERRY was too spicy!") and (st.player.confusionTurns or 0) == 3,
|
|
"Modest nature dislikes spicy and gets confused")
|
|
local st2, ad2 = battle({ item = 143, hp = 40, maxHp = 80, personality = 3 }, {})
|
|
local txt2 = eot(st2, ad2)
|
|
check(not has(txt2, "too spicy") and (st2.player.confusionTurns or 0) == 0, "Adamant nature likes spicy")
|
|
end
|
|
|
|
print("=== PINCH STAT BERRIES ===")
|
|
do
|
|
local st, ad = battle({ item = 168, hp = 25, maxHp = 100 }, {})
|
|
local txt, evs = eot(st, ad)
|
|
check(st.player.stages.attack == 1 and st.player.item == 0, "Liechi raises Attack at 1/4 HP")
|
|
check(has(txt, "Using LIECHI BERRY, the ATTACK\nof ALPHA rose!"), "stat berry message")
|
|
check(hasAnim(evs, "HELD_ITEM_EFFECT") and hasAnim(evs, "STATS_CHANGE"), "stat berry anims")
|
|
|
|
local st2, ad2 = battle({ item = 174, hp = 20, maxHp = 100 }, {}, { rng = { ["0,4"] = 2 } })
|
|
local txt2 = eot(st2, ad2)
|
|
check(st2.player.stages.speed == 2 and has(txt2, "sharply rose!"), "Starf sharply raises a random stat")
|
|
|
|
local st3, ad3 = battle({ item = 173, hp = 20, maxHp = 100 }, {})
|
|
local txt3 = eot(st3, ad3)
|
|
check(st3.player.focusEnergy and has(txt3, "used\nLANSAT BERRY to hustle!"), "Lansat sets focus energy")
|
|
end
|
|
|
|
print("=== LEPPA ===")
|
|
do
|
|
local st, ad = battle({ item = 138, moves = { 33, 1 }, pp = { 0, 5 }, maxPp = { 35, 35 } }, {})
|
|
local txt = eot(st, ad)
|
|
check(st.player.mon.pp[1] == 10 and st.playerParty[1].pp[1] == 10, "Leppa restores 10 PP to the empty move")
|
|
check(has(txt, "restored TACKLE's PP!") or has(txt, "'s PP!"), "Leppa message")
|
|
end
|
|
|
|
print("=== WHITE HERB / MENTAL HERB ===")
|
|
do
|
|
local st, ad = battle({ item = 180 }, { moves = { 45 } })
|
|
local txt = use(st, ad, st.enemy, 45)
|
|
check(st.player.stages.attack == 0 and st.player.item == 0 and has(txt, "WHITE HERB\nrestored its status!"),
|
|
"White Herb restores lowered stats at move end")
|
|
local st2, ad2 = battle({ item = 185 }, { moves = { 150 } })
|
|
st2.player.expInfatuated = true
|
|
st2.player.expInfatuatedWith = st2.enemy
|
|
local txt2 = use(st2, ad2, st2.enemy, 150)
|
|
check(not st2.player.expInfatuated and has(txt2, "MENTAL HERB\ncured its love problem!"), "Mental Herb cures infatuation")
|
|
end
|
|
|
|
print("=== DAMAGE ITEMS ===")
|
|
do
|
|
local function base(p, e, move, extra)
|
|
local st, ad = battle(p, e, extra)
|
|
return Damage.base(st.player, st.enemy, ROM[move], { adapter = ad, power = ROM[move].power, moveType = ROM[move].type })
|
|
end
|
|
local plain = base({}, {}, 33)
|
|
check(base({ item = 186 }, {}, 33) > plain, "Choice Band boosts physical attack")
|
|
check(base({ item = 215 }, {}, 52) > base({}, {}, 52), "Charcoal boosts Fire moves")
|
|
check(base({ item = 215 }, {}, 33) == plain, "Charcoal does not boost Normal moves")
|
|
check(base({ item = 202, species = 25 }, {}, 85) > base({ species = 25 }, {}, 85), "Light Ball doubles Pikachu SpAtk")
|
|
check(base({ item = 224, species = 104 }, {}, 33) > base({ species = 104 }, {}, 33), "Thick Club doubles Cubone Attack")
|
|
check(base({}, { item = 223, species = 132 }, 33) < base({}, { species = 132 }, 33), "Metal Powder doubles Ditto Defense")
|
|
check(base({ item = 192, species = 373 }, {}, 55) > base({ species = 373 }, {}, 55), "DeepSeaTooth for Clamperl")
|
|
check(base({ item = 191, species = 407 }, {}, 55) > base({ species = 407 }, {}, 55), "Soul Dew for Latias")
|
|
check(base({}, {}, 33, { badges = { true } }) > plain, "Boulder Badge boosts player Attack")
|
|
check(base({ ability = "PLUS" }, { ability = "MINUS" }, 55) > base({ ability = "PLUS" }, {}, 55), "Plus with Minus on field")
|
|
end
|
|
|
|
print("=== CHOICE BAND lock ===")
|
|
do
|
|
local st, ad = battle({ item = 186, moves = { 33, 1 } }, { hp = 999, maxHp = 999 })
|
|
use(st, ad, st.player, 33, 1)
|
|
check(st.player.choicedMove == 33, "Choice Band records the locked move on the battler")
|
|
local acts = Engine.planTurnFromActions(st, ad, { kind = "move", move = 1, slot = 2 }, { kind = "move", move = 33, slot = 1 })
|
|
local pAct
|
|
for _, a in ipairs(acts) do if a.user == st.player then pAct = a end end
|
|
check(pAct and pAct.move == 33 and pAct.slot == 1, "planTurn enforces the Choice Band move")
|
|
local bad = Engine.moveLimitations(st.player, ad)
|
|
check(bad[2] and not bad[1], "moveLimitations greys out non-choiced moves")
|
|
end
|
|
|
|
print("=== QUICK CLAW / MACHO BRACE ===")
|
|
do
|
|
local st, ad = battle({ item = 183, speed = 10 }, { speed = 100 }, { rng = { ["0,65535"] = 100 } })
|
|
local acts = Engine.planTurnFromActions(st, ad, { kind = "move", move = 33, slot = 1 }, { kind = "move", move = 33, slot = 1 })
|
|
check(acts[1].user == st.player, "Quick Claw activates when randomTurnNumber < 0xFFFF*20/100")
|
|
local st2, ad2 = battle({ item = 183, speed = 10 }, { speed = 100 }, { rng = { ["0,65535"] = 20000 } })
|
|
local acts2 = Engine.planTurnFromActions(st2, ad2, { kind = "move", move = 33, slot = 1 }, { kind = "move", move = 33, slot = 1 })
|
|
check(acts2[1].user == st2.enemy, "Quick Claw does not activate on a high roll")
|
|
local st3, ad3 = battle({ item = 181, speed = 100 }, { speed = 60 })
|
|
check(Engine.speedOf(st3.player, st3, ad3) == 50, "Macho Brace halves speed")
|
|
local st4, ad4 = battle({ speed = 100 }, {}, { badges = { false, false, true } })
|
|
check(Engine.speedOf(st4.player, st4, ad4) == 110, "Thunder Badge speed boost")
|
|
end
|
|
|
|
print("=== BRIGHT POWDER / LAX INCENSE ===")
|
|
do
|
|
local st, ad = battle({ moves = { 1 } }, { item = 179, hp = 999, maxHp = 999 }, { rng = { ["1,100"] = 91 } })
|
|
local txt = use(st, ad, st.player, 1)
|
|
check(has(txt, "attack missed"), "Bright Powder: 100 acc -> 90")
|
|
local st2, ad2 = battle({ moves = { 1 } }, { item = 221, hp = 999, maxHp = 999 }, { rng = { ["1,100"] = 91 } })
|
|
local txt2 = use(st2, ad2, st2.player, 1)
|
|
check(not has(txt2, "attack missed"), "Lax Incense uses param 5 (95)")
|
|
end
|
|
|
|
print("=== FOCUS BAND ===")
|
|
do
|
|
local st, ad = battle({ moves = { 163 }, attack = 300 }, { item = 196, hp = 5, maxHp = 100 }, { rng = { ["0,99"] = 0 } })
|
|
local txt, out = use(st, ad, st.player, 163)
|
|
check(ad:hp(st.enemy) == 1, "Focus Band leaves 1 HP")
|
|
check(has(txt, "BRAVO hung on\nusing its FOCUS BAND!"), "Focus Band message")
|
|
check(hasAnim(out.events, "FOCUS_BAND", "enemy"), "FOCUS_BAND anim")
|
|
local st2, ad2 = battle({ moves = { 12 }, level = 60 }, { item = 196, level = 50 }, { rng = { ["0,99"] = 0, ["1,100"] = 1 } })
|
|
use(st2, ad2, st2.player, 12)
|
|
check(ad2:hp(st2.enemy) == 1, "Focus Band survives an OHKO move")
|
|
end
|
|
|
|
print("=== KING'S ROCK / SHELL BELL ===")
|
|
do
|
|
local st, ad = battle({ item = 187, moves = { 33 } }, { hp = 999, maxHp = 999 }, { rng = { ["0,99"] = 0 } })
|
|
use(st, ad, st.player, 33)
|
|
check(st.enemy.flinched == true, "King's Rock flinches on a contact move")
|
|
local st2, ad2 = battle({ item = 219, moves = { 33 }, hp = 50, maxHp = 100 }, { hp = 999, maxHp = 999 })
|
|
local _, out = use(st2, ad2, st2.player, 33)
|
|
local dealt = 999 - ad2:hp(st2.enemy)
|
|
check(ad2:hp(st2.player) == 50 + math.max(1, math.floor(dealt / 8)), "Shell Bell heals 1/8 damage dealt")
|
|
check(hasAnim(out.events, "HELD_ITEM_EFFECT", "player"), "Shell Bell anim")
|
|
end
|
|
|
|
print("=== FLEE / SMOKE BALL / RUN AWAY / TRAPPING ===")
|
|
do
|
|
local st, ad = battle({ item = 194, speed = 1 }, { speed = 200 })
|
|
local ok, how = Engine.tryFlee(st, ad)
|
|
check(ok and how == "item", "Smoke Ball always escapes")
|
|
check(hasAnim(ad:events(), "SMOKEBALL_ESCAPE"), "SMOKEBALL_ESCAPE anim")
|
|
local st2, ad2 = battle({ ability = "RUN_AWAY", speed = 1 }, { speed = 200 })
|
|
local ok2, how2 = Engine.tryFlee(st2, ad2)
|
|
check(ok2 and how2 == "ability", "Run Away always escapes")
|
|
local st3, ad3 = battle({ speed = 10 }, { speed = 100 }, { rng = { ["0,255"] = 11 } })
|
|
check(Engine.tryFlee(st3, ad3) == true, "flee: 10*128/100 = 12 > 11")
|
|
local st4, ad4 = battle({ speed = 10 }, { speed = 100 }, { rng = { ["0,255"] = 12 } })
|
|
check(Engine.tryFlee(st4, ad4) == false and st4.fleeAttempts == 1, "flee fails and counts attempts")
|
|
local st5, ad5 = battle({}, { ability = "SHADOW_TAG" })
|
|
local can, msg = Engine.canRun(st5, ad5)
|
|
check(not can and has(msg, "prevents\nescape with SHADOW TAG!"), "Shadow Tag prevents escape")
|
|
local st6, ad6 = battle({ type1 = T.FLYING }, { ability = "ARENA_TRAP" })
|
|
check(Engine.canRun(st6, ad6) == true, "Arena Trap does not trap Flying types")
|
|
end
|
|
|
|
print("=== INTIMIDATE / DRIZZLE / TRACE at battle start ===")
|
|
do
|
|
local st, ad = battle({ ability = "INTIMIDATE" }, {})
|
|
local out = {}
|
|
ad._say = function(t) out[#out + 1] = t end
|
|
Engine.battleStartEffects(st, ad)
|
|
local txt = table.concat(out, " || ")
|
|
check(st.enemy.stages.attack == -1 and has(txt, "ALPHA's INTIMIDATE\ncuts Wild BRAVO's ATTACK!"), "Intimidate lowers foe Attack")
|
|
check(hasAnim(ad:events(), "STATS_CHANGE", "enemy"), "Intimidate plays STATS_CHANGE on the foe")
|
|
|
|
local st2, ad2 = battle({ ability = "INTIMIDATE" }, { ability = "HYPER_CUTTER" })
|
|
local o2 = {}
|
|
ad2._say = function(t) o2[#o2 + 1] = t end
|
|
Engine.battleStartEffects(st2, ad2)
|
|
check(st2.enemy.stages.attack == 0 and has(table.concat(o2), "Wild BRAVO's HYPER CUTTER\nprevented ALPHA's\\lINTIMIDATE from working!"),
|
|
"Hyper Cutter blocks Intimidate")
|
|
|
|
local st3, ad3 = battle({}, { ability = "DRIZZLE" })
|
|
local o3 = {}
|
|
ad3._say = function(t) o3[#o3 + 1] = t end
|
|
Engine.battleStartEffects(st3, ad3)
|
|
check(st3.weather == "RAIN" and (st3.weatherTurns or 0) == 0, "Drizzle sets permanent rain")
|
|
check(has(table.concat(o3), "BRAVO's DRIZZLE\nmade it rain!") and hasAnim(ad3:events(), "RAIN_CONTINUES"), "Drizzle message + anim")
|
|
|
|
local st4, ad4 = battle({ ability = "TRACE" }, { ability = "STATIC" })
|
|
local o4 = {}
|
|
ad4._say = function(t) o4[#o4 + 1] = t end
|
|
Engine.battleStartEffects(st4, ad4)
|
|
check(ad4:abilityOf(st4.player) == "STATIC" and has(table.concat(o4), "ALPHA TRACED\nWild BRAVO's STATIC!"), "Trace copies the foe ability")
|
|
end
|
|
|
|
print("=== CONTACT ABILITIES ===")
|
|
do
|
|
local st, ad = battle({ moves = { 33 } }, { ability = "STATIC", hp = 999, maxHp = 999 }, { rng = { ["0,2"] = 0 } })
|
|
local txt = use(st, ad, st.player, 33)
|
|
check(ad:status(st.player) == "PAR" and has(txt, "BRAVO's STATIC\nparalyzed ALPHA!"), "Static paralyzes on contact")
|
|
local st2, ad2 = battle({ moves = { 55 } }, { ability = "STATIC", hp = 999, maxHp = 999 }, { rng = { ["0,2"] = 0 } })
|
|
use(st2, ad2, st2.player, 55)
|
|
check(ad2:status(st2.player) == nil, "Static ignores non-contact moves")
|
|
local st3, ad3 = battle({ moves = { 33 }, maxHp = 160, hp = 160 }, { ability = "ROUGH_SKIN", hp = 999, maxHp = 999 })
|
|
local txt3 = use(st3, ad3, st3.player, 33)
|
|
check(ad3:hp(st3.player) == 150 and has(txt3, "BRAVO's ROUGH SKIN\nhurt ALPHA!"), "Rough Skin deals 1/16")
|
|
local st4, ad4 = battle({ moves = { 33 } }, { ability = "EFFECT_SPORE", hp = 999, maxHp = 999 },
|
|
{ rng = { ["0,9"] = 0, ["0,3"] = 1 } })
|
|
local txt4 = use(st4, ad4, st4.player, 33)
|
|
check(ad4:status(st4.player) == "SLP" and has(txt4, "EFFECT SPORE\nmade ALPHA sleep!"), "Effect Spore sleep")
|
|
local st5, ad5 = battle({ moves = { 33 }, ability = "LIMBER" }, { ability = "STATIC", hp = 999, maxHp = 999 }, { rng = { ["0,2"] = 0 } })
|
|
local txt5 = use(st5, ad5, st5.player, 33)
|
|
check(ad5:status(st5.player) == nil and not has(txt5, "prevents"), "Limber silently blocks Static")
|
|
local st6, ad6 = battle({ moves = { 33 }, gender = "M" }, { ability = "CUTE_CHARM", gender = "F", hp = 999, maxHp = 999 }, { rng = { ["0,2"] = 0 } })
|
|
local txt6 = use(st6, ad6, st6.player, 33)
|
|
check(st6.player.expInfatuated and has(txt6, "BRAVO's CUTE CHARM\ninfatuated ALPHA!"), "Cute Charm infatuates")
|
|
local st7, ad7 = battle({ moves = { 52 } }, { ability = "COLOR_CHANGE", hp = 999, maxHp = 999 })
|
|
local txt7 = use(st7, ad7, st7.player, 52)
|
|
check(st7.enemy.type1 == T.FIRE and has(txt7, "COLOR CHANGE\nmade it the FIRE type!"), "Color Change")
|
|
end
|
|
|
|
print("=== ABSORB ABILITIES / SOUNDPROOF ===")
|
|
do
|
|
local st, ad = battle({ moves = { 85 } }, { ability = "VOLT_ABSORB", hp = 50, maxHp = 100 })
|
|
local txt = use(st, ad, st.player, 85)
|
|
check(ad:hp(st.enemy) == 75 and has(txt, "BRAVO restored HP\nusing its VOLT ABSORB!"), "Volt Absorb heals 1/4")
|
|
local st2, ad2 = battle({ moves = { 55 } }, { ability = "WATER_ABSORB" })
|
|
local txt2 = use(st2, ad2, st2.player, 55)
|
|
check(ad2:hp(st2.enemy) == 100 and has(txt2, "BRAVO's WATER ABSORB\nmade ") and has(txt2, " useless!"), "Water Absorb at full HP")
|
|
local st3, ad3 = battle({ moves = { 52 } }, { ability = "FLASH_FIRE" })
|
|
local txt3 = use(st3, ad3, st3.player, 52)
|
|
check(st3.enemy.expFlashFire and has(txt3, "FLASH FIRE\nraised its FIRE power!"), "Flash Fire activates")
|
|
local st4, ad4 = battle({ moves = { 45 } }, { ability = "SOUNDPROOF" })
|
|
local txt4 = use(st4, ad4, st4.player, 45)
|
|
check(st4.enemy.stages.attack == 0 and has(txt4, "BRAVO's SOUNDPROOF\nblocks GROWL!"), "Soundproof blocks Growl")
|
|
end
|
|
|
|
print("=== SYNCHRONIZE / IMMUNITY CURE ===")
|
|
do
|
|
local st, ad = battle({ moves = { 86 } }, { ability = "SYNCHRONIZE" })
|
|
local txt = use(st, ad, st.player, 86)
|
|
check(ad:status(st.enemy) == "PAR" and ad:status(st.player) == "PAR", "Synchronize passes paralysis back")
|
|
check(has(txt, "BRAVO's SYNCHRONIZE\nparalyzed ALPHA!"), "Synchronize message")
|
|
local st2, ad2 = battle({ moves = { 92 } }, { ability = "SYNCHRONIZE" }, { rng = { ["1,100"] = 1 } })
|
|
use(st2, ad2, st2.player, 92)
|
|
check(ad2:status(st2.player) == "PSN", "Synchronize turns Toxic into regular poison")
|
|
local st3, ad3 = battle({ status = "PSN", ability = "IMMUNITY" }, { moves = { 150 } })
|
|
local txt3 = use(st3, ad3, st3.enemy, 150)
|
|
check(ad3:status(st3.player) == nil and has(txt3, "ALPHA's IMMUNITY\ncured its poison problem!"), "Immunity cures poison at move end")
|
|
end
|
|
|
|
print("=== END-OF-TURN ABILITIES ===")
|
|
do
|
|
local st, ad = battle({ ability = "SPEED_BOOST" }, {})
|
|
st.player.isFirstTurn = 1
|
|
local txt = eot(st, ad)
|
|
check(st.player.stages.speed == 1 and has(txt, "ALPHA's SPEED BOOST\nraised its SPEED!"), "Speed Boost")
|
|
local st2, ad2 = battle({ ability = "SPEED_BOOST" }, {})
|
|
eot(st2, ad2)
|
|
check(st2.player.stages.speed == 0, "Speed Boost skips the switch-in turn")
|
|
local st3, ad3 = battle({ ability = "RAIN_DISH", hp = 50, maxHp = 160 }, {})
|
|
st3.weather, st3.weatherTurns = "RAIN", 5
|
|
eot(st3, ad3)
|
|
check(ad3:hp(st3.player) == 60, "Rain Dish heals 1/16 in rain")
|
|
local st4, ad4 = battle({ ability = "SHED_SKIN", status = "BRN" }, {}, { rng = { ["0,2"] = 0 } })
|
|
local txt4 = eot(st4, ad4)
|
|
check(ad4:status(st4.player) == nil and has(txt4, "SHED SKIN\ncured its burn problem!"), "Shed Skin")
|
|
end
|
|
|
|
print("=== TRUANT ===")
|
|
do
|
|
local st, ad = battle({ ability = "TRUANT", moves = { 33 } }, { hp = 999, maxHp = 999 })
|
|
use(st, ad, st.player, 33)
|
|
eot(st, ad)
|
|
local txt = use(st, ad, st.player, 33)
|
|
check(has(txt, "ALPHA is\nloafing around!"), "Truant loafs every other turn")
|
|
eot(st, ad)
|
|
local txt2 = use(st, ad, st.player, 33)
|
|
check(not has(txt2, "loafing"), "Truant acts again the next turn")
|
|
end
|
|
|
|
print("=== NATURAL CURE / FORECAST ===")
|
|
do
|
|
local st, ad = battle({ ability = "NATURAL_CURE", status = "PSN" }, {})
|
|
Engine.switchOutEffects(st, ad, st.player)
|
|
check(ad:status(st.player) == nil and st.playerParty[1].status == nil, "Natural Cure cures on switch-out")
|
|
local st2, ad2 = battle({}, { ability = "FORECAST", species = 385, moves = { 240 } })
|
|
local txt2 = use(st2, ad2, st2.enemy, 240)
|
|
check(st2.enemy.type1 == T.WATER and has(txt2, "BRAVO transformed!"), "Forecast turns Castform Water in rain")
|
|
check(hasAnim(ad2:events(), "CASTFORM_CHANGE", "enemy"), "CASTFORM_CHANGE anim")
|
|
end
|
|
|
|
print("=== DISOBEDIENCE ===")
|
|
do
|
|
local st, ad = battle({ moves = { 33 }, level = 60, otId = 999, otName = "ASH" }, { hp = 999, maxHp = 999 },
|
|
{ playerTrainerId = 1, playerName = "RED", rng = { ["0,255"] = { 255, 255, 0, 255 }, ["0,3"] = 1 } })
|
|
local txt = use(st, ad, st.player, 33)
|
|
check(has(txt, "ALPHA began to nap!") and ad:status(st.player) == "SLP", "traded mon over the cap naps")
|
|
local st2, ad2 = battle({ moves = { 33 }, level = 60, otId = 1, otName = "RED" }, { hp = 999, maxHp = 999 },
|
|
{ playerTrainerId = 1, playerName = "RED", rng = { ["0,255"] = 255 } })
|
|
local txt2 = use(st2, ad2, st2.player, 33)
|
|
check(has(txt2, "ALPHA used"), "own mon always obeys")
|
|
local st3, ad3 = battle({ moves = { 33 }, level = 60, otId = 999 }, { hp = 999, maxHp = 999 },
|
|
{ playerTrainerId = 1, playerName = "RED", badges = { true, true, true, true, true, true, true, true } })
|
|
local txt3 = use(st3, ad3, st3.player, 33)
|
|
check(has(txt3, "ALPHA used"), "Earth Badge makes traded mons obey")
|
|
end
|
|
|
|
print(string.format("\n%d passed, %d failed", passed, failed))
|
|
if failed > 0 then
|
|
print("[FAIL] game3 battle items + abilities")
|
|
os.exit(1)
|
|
end
|
|
print("[ok] game3 battle items + abilities")
|