mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 05:32:07 -04:00
189 lines
7.6 KiB
Lua
189 lines
7.6 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 doubles engine: " .. tostring(GameCache.reason))
|
|
os.exit(0)
|
|
end
|
|
|
|
local Moves = require("src.core.game3.battle.moves")
|
|
|
|
-- pokefirered/src/data/battle_moves.h:1
|
|
local ROM = {
|
|
[33] = { effect = 0, power = 35, type = 0, accuracy = 95, pp = 35, secondaryChance = 0, target = 0, priority = 0, flags = 51 },
|
|
[57] = { effect = 0, power = 95, type = 11, accuracy = 100, pp = 15, secondaryChance = 0, target = 8, priority = 0, flags = 50 },
|
|
[89] = { effect = 147, power = 100, type = 4, accuracy = 100, pp = 10, secondaryChance = 0, target = 32, priority = 0, flags = 50 },
|
|
[98] = { effect = 103, power = 40, type = 0, accuracy = 100, pp = 30, secondaryChance = 0, target = 0, priority = 1, flags = 51 },
|
|
[266] = { effect = 172, power = 0, type = 0, accuracy = 0, pp = 20, secondaryChance = 0, target = 16, priority = 3, flags = 0 },
|
|
[270] = { effect = 176, power = 0, type = 0, accuracy = 0, pp = 20, secondaryChance = 0, target = 16, priority = 5, flags = 0 },
|
|
}
|
|
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 Types = require("src.core.game3.battle.types")
|
|
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 200, maxHp = o.maxHp or 200,
|
|
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 = { 20, 20, 20, 20 },
|
|
}
|
|
end
|
|
|
|
local function rng(lo, hi)
|
|
if lo == 1 and hi == 100 then return 1 end
|
|
return hi
|
|
end
|
|
|
|
local function battle(p, e)
|
|
local party, foes = {}, {}
|
|
for i, o in ipairs(p) do
|
|
o.nickname = o.nickname or ("P" .. i)
|
|
party[i] = mon(o)
|
|
end
|
|
for i, o in ipairs(e) do
|
|
o.nickname = o.nickname or ("E" .. i)
|
|
foes[i] = mon(o)
|
|
end
|
|
local st = State.new({ wild = false, double = true, playerParty = party, foeParty = foes })
|
|
for id = 0, 3 do
|
|
local b = st.battlers[id]
|
|
if b then b.type1, b.type2 = T.NORMAL, nil end
|
|
end
|
|
st.rng = rng
|
|
return st, Adapter.new(st)
|
|
end
|
|
|
|
local function hp(st, id) return tonumber(st.battlers[id].mon.hp) end
|
|
|
|
local function run_turn(st, ad, chosen)
|
|
local actions = Engine.planTurnActions(st, ad, chosen)
|
|
for _, act in ipairs(actions) do
|
|
if Engine.actionRunnable(st, act) and act.kind == "move" then
|
|
act.done = true
|
|
Engine.resolveMove(act.user, act.target, act.move, act.slot, ad, st, {})
|
|
end
|
|
end
|
|
return actions
|
|
end
|
|
|
|
print("=== state: four slots, aliases, helpers ===")
|
|
do
|
|
local st = battle({ {}, {}, {} }, { {}, {} })
|
|
check(st.double and st.battlersCount == 4, "double state has four battlers")
|
|
check(st.battlers[0] == st.player and st.battlers[1] == st.enemy, "slots 0/1 alias st.player/st.enemy")
|
|
check(st.battlers[2].partyIndex == 2 and st.battlers[3].partyIndex == 2, "slots 2/3 take the next usable mon")
|
|
check(State.PARTNER(1) == 3 and State.OPPOSITE(2) == 3 and State.sideOf(3) == "enemy", "PARTNER/OPPOSITE/sideOf")
|
|
check(st.battlers[1].participants[1] and st.battlers[3].participants[2], "both flanks start with both player mons sent")
|
|
end
|
|
|
|
print("=== turn order: speed and priority ===")
|
|
do
|
|
local st, ad = battle({ { speed = 10, moves = { 33, 98 } }, { speed = 30 } }, { { speed = 40 }, { speed = 20 } })
|
|
local order = run_turn(st, ad, {
|
|
[0] = { kind = "move", move = 33, slot = 1, target = 1 },
|
|
[1] = { kind = "move", move = 33, slot = 1, target = 0 },
|
|
[2] = { kind = "move", move = 33, slot = 1, target = 1 },
|
|
[3] = { kind = "move", move = 33, slot = 1, target = 0 },
|
|
})
|
|
check(table.concat(st.turnOrder, ",") == "1,2,3,0", "speed order 1,2,3,0")
|
|
check(#order == 4, "four actions planned")
|
|
Engine.planTurnActions(st, ad, {
|
|
[0] = { kind = "move", move = 98, slot = 2, target = 1 },
|
|
[1] = { kind = "move", move = 33, slot = 1, target = 0 },
|
|
[2] = { kind = "switch", partySlot = 3 },
|
|
[3] = { kind = "move", move = 33, slot = 1, target = 0 },
|
|
})
|
|
check(st.turnOrder[1] == 2 and st.turnOrder[2] == 0, "switch first, then priority move")
|
|
end
|
|
|
|
print("=== spread damage halves with two defenders ===")
|
|
do
|
|
local st, ad = battle({ { moves = { 57 } }, {} }, { {}, {} })
|
|
Engine.resolveMove(st.battlers[0], nil, 57, 1, ad, st, {})
|
|
local both1, both3 = 200 - hp(st, 1), 200 - hp(st, 3)
|
|
check(both1 > 0 and both3 > 0 and hp(st, 2) == 200, "Surf hits both foes, not the ally")
|
|
local st2, ad2 = battle({ { moves = { 57 } }, {} }, { {}, {} })
|
|
Engine.markAbsent(st2, 3)
|
|
Engine.resolveMove(st2.battlers[0], nil, 57, 1, ad2, st2, {})
|
|
local solo = 200 - hp(st2, 1)
|
|
check(solo > both1 and math.abs(solo - 2 * both1) <= 2, "one present defender takes full damage")
|
|
end
|
|
|
|
print("=== Earthquake hits ally and foes ===")
|
|
do
|
|
local st, ad = battle({ { moves = { 89 } }, {} }, { {}, {} })
|
|
Engine.resolveMove(st.battlers[0], nil, 89, 1, ad, st, {})
|
|
check(hp(st, 1) < 200 and hp(st, 2) < 200 and hp(st, 3) < 200, "Earthquake damages battlers 1, 2 and 3")
|
|
check((200 - hp(st, 1)) == (200 - hp(st, 3)), "FOES_AND_ALLY is not halved")
|
|
check(st.battlers[0].mon.pp[1] == 19, "PP deducted once")
|
|
end
|
|
|
|
print("=== Follow Me redirects ===")
|
|
do
|
|
local st, ad = battle({ {}, {} }, { {}, { moves = { 266 } } })
|
|
run_turn(st, ad, {
|
|
[0] = { kind = "move", move = 33, slot = 1, target = 1 },
|
|
[1] = { kind = "move", move = 33, slot = 1, target = 0 },
|
|
[2] = { kind = "move", move = 33, slot = 1, target = 1 },
|
|
[3] = { kind = "move", move = 266, slot = 1 },
|
|
})
|
|
check(hp(st, 1) == 200 and hp(st, 3) < 200, "both player attacks go into the Follow Me user")
|
|
end
|
|
|
|
print("=== Helping Hand boosts the partner ===")
|
|
do
|
|
local st, ad = battle({ {}, { moves = { 270 } } }, { {}, {} })
|
|
run_turn(st, ad, {
|
|
[0] = { kind = "move", move = 33, slot = 1, target = 1 },
|
|
[2] = { kind = "move", move = 270, slot = 1 },
|
|
})
|
|
local helped = 200 - hp(st, 1)
|
|
local st2, ad2 = battle({ {}, {} }, { {}, {} })
|
|
run_turn(st2, ad2, { [0] = { kind = "move", move = 33, slot = 1, target = 1 } })
|
|
local plain = 200 - hp(st2, 1)
|
|
check(st.battlers[0].expHelpingHand == true and helped > plain, "Helping Hand boosts the partner's damage")
|
|
end
|
|
|
|
print("=== Intimidate hits both foes ===")
|
|
do
|
|
local st, ad = battle({ { ability = 22 }, {} }, { {}, {} })
|
|
Engine.battleStartEffects(st, ad)
|
|
check(st.battlers[1].stages.attack == -1 and st.battlers[3].stages.attack == -1, "both foes lose one Attack stage")
|
|
check(st.battlers[2].stages.attack == 0, "ally untouched")
|
|
end
|
|
|
|
print("=== faint bookkeeping and absent slots ===")
|
|
do
|
|
local st, ad = battle({ {}, {} }, { {}, {} })
|
|
ad:setHp(st.battlers[3], 0)
|
|
local fainted = Engine.faintedBattlers(st)
|
|
check(#fainted == 1 and fainted[1] == 3, "battler 3 reported fainted")
|
|
local pend = Engine.pendingReplacements(st)
|
|
check(pend[1] and pend[1].noMons, "no replacement left for battler 3")
|
|
Engine.markAbsent(st, 3)
|
|
check(not State.isPresent(st, 3) and ad:foeOf(st.battlers[2]) == st.battlers[1], "absent flank flips foeOf")
|
|
Engine.resolveMove(st.battlers[0], 3, 33, 1, ad, st, {})
|
|
check(hp(st, 1) < 200, "a move aimed at an absent foe hits its partner")
|
|
end
|
|
|
|
print(string.format("\n%d passed, %d failed", passed, failed))
|
|
if failed > 0 then os.exit(1) end
|