mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-29 07:00:55 -04:00
230 lines
9.2 KiB
Lua
230 lines
9.2 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
|
|
local ROM_PATH = os.getenv("POKEPORT_EMERALD_ROM") or "../pokeemerald/pokeemerald.gba"
|
|
local f = io.open(ROM_PATH, "rb")
|
|
if not f then
|
|
print("emerald_contest_engine_test: skipped (no ROM at " .. ROM_PATH .. ")")
|
|
os.exit(0)
|
|
end
|
|
local data = f:read("*a")
|
|
f:close()
|
|
if data:sub(0xAD, 0xB0) ~= "BPEE" then
|
|
print("emerald_contest_engine_test: skipped (" .. ROM_PATH .. " is not Emerald)")
|
|
os.exit(0)
|
|
end
|
|
|
|
local GV = require("src.core.GameVersion")
|
|
local Versions = require("src.import.gba.versions")
|
|
local Rom = require("src.import.gba.rom")
|
|
GV.set("emerald")
|
|
Versions.select("emerald")
|
|
local sha = GV.VERSIONS.emerald.sha1
|
|
local rom = assert(Rom.open({
|
|
info = function() return { size = #data, md5 = sha } end,
|
|
read = function(_, _, off, len) return data:sub(off + 1, off + len) end,
|
|
}, GV.forSha1(sha)))
|
|
local files = {}
|
|
local cache = {
|
|
write = function(_, rel, bytes) files[rel] = bytes; return true end,
|
|
read = function(_, rel) return files[rel] end,
|
|
}
|
|
local CX = require("src.import.gba.rse.contest_extract")
|
|
CX.run(rom, cache, { cacheRoot = "gba", game = "emerald" })
|
|
local man = assert(load(files["gba/" .. CX.SUB .. "/manifest.lua"], "@m", "t", {}))()
|
|
local moves = require("src.import.gba.contest_moves_extract").extract(rom)
|
|
local Contest = require("src.core.game3.rse.contest")
|
|
local Util = require("src.core.game3.rse.contest_util")
|
|
local D = Contest.buildData(moves, man)
|
|
local E = Contest.EFFECT
|
|
|
|
local function lcg(seed)
|
|
local Rng = require("src.core.game3.rng")
|
|
local v = seed
|
|
return function()
|
|
v = (Rng.mulU32(v, 1103515245) + 24691) % 4294967296
|
|
return math.floor(v / 65536)
|
|
end
|
|
end
|
|
|
|
local function moveWith(effect, opts)
|
|
opts = opts or {}
|
|
for id = 1, moves.count - 1 do
|
|
local m = moves.moves[id]
|
|
if m.effect == effect and (opts.allowCombo or m.comboStarterId == 0) and (opts.category == nil or m.category == opts.category)
|
|
and (opts.notCategory == nil or m.category ~= opts.notCategory) then
|
|
local combos = m.comboMoves
|
|
if opts.allowCombo or (combos[1] == 0 and combos[2] == 0 and combos[3] == 0 and combos[4] == 0) then return id end
|
|
end
|
|
end
|
|
error("no plain move with contest effect " .. effect)
|
|
end
|
|
|
|
local function newContest(category, list)
|
|
local c = Contest.new({ data = D, category = category, rank = 0, rng = lcg(77) })
|
|
for i = 0, 3 do
|
|
c.mons[i] = { species = 1, aiFlags = 0, moves = { [0] = list[i], 0, 0, 0 }, cool = 0, beauty = 0, cute = 0,
|
|
smart = 0, tough = 0, sheen = 0 }
|
|
end
|
|
c:calculateRound1Points()
|
|
c:init()
|
|
return c
|
|
end
|
|
|
|
local function play(c, list)
|
|
for i = 0, 3 do c.status[i].currMove = c:isTurnDisabled(i) and 0 or list[i] end
|
|
local events = {}
|
|
for t = 0, 3 do
|
|
c.contest.turnNumber = t
|
|
local who = c:startTurn()
|
|
events[who] = c:completeTurn(who)
|
|
end
|
|
c:finishRound()
|
|
local appeals = {}
|
|
for i = 0, 3 do appeals[i] = c.status[i].appeal end
|
|
c:resetForNextRound()
|
|
c:nextRound()
|
|
return appeals, events
|
|
end
|
|
|
|
local function byOrder(c, turn)
|
|
for i = 0, 3 do if c.results.turnOrder[i] == turn then return i end end
|
|
end
|
|
|
|
-- pokeemerald/src/contest.c:4425
|
|
do
|
|
local plain = moveWith(E.HIGHLY_APPEALING)
|
|
local cat = moves.moves[plain].category
|
|
local c = newContest(cat, { [0] = plain, plain, plain, plain })
|
|
local list = { [0] = plain, plain, plain, plain }
|
|
local a = play(c, list)
|
|
for i = 0, 3 do eq(a[i], 40 + 10, "highly appealing move in its own category appeals 40 + 10 excitement") end
|
|
eq(c.contest.applauseLevel, 4, "four same-category appeals fill the meter to 4")
|
|
local a2 = play(c, list)
|
|
for i = 0, 3 do eq(a2[i], 40 - 20, "repeated move loses 20 and earns no excitement") end
|
|
eq(c.contest.applauseLevel, 4, "repeats leave the meter alone")
|
|
end
|
|
|
|
-- pokeemerald/src/contest_effect.c:522
|
|
do
|
|
local m = moveWith(E.BETTER_IF_FIRST)
|
|
local cat = (moves.moves[m].category + 2) % 5
|
|
local plain = moveWith(E.HIGHLY_APPEALING, { notCategory = cat, allowCombo = true })
|
|
local c = newContest(cat, { [0] = m, m, m, m })
|
|
local first = byOrder(c, 0)
|
|
local list = { [0] = plain, plain, plain, plain }
|
|
list[first] = m
|
|
local excite = c:moveExcitement(m)
|
|
local a = play(c, list)
|
|
local base = D.effects[E.BETTER_IF_FIRST].appeal
|
|
eq(a[first], base * 3 + (excite > 0 and 10 or 0), "better-if-first move appealing first triples its base")
|
|
end
|
|
|
|
-- pokeemerald/src/contest_effect.c:93
|
|
do
|
|
local boom = moveWith(E.GREAT_APPEAL_BUT_NO_MORE_MOVES)
|
|
local cat = moves.moves[boom].category
|
|
local plain = moveWith(E.HIGHLY_APPEALING, { notCategory = cat, allowCombo = true })
|
|
local c = newContest(cat, { [0] = boom, plain, plain, plain })
|
|
play(c, { [0] = boom, plain, plain, plain })
|
|
eq(c.status[0].noMoreTurns, 1, "great-appeal move sets noMoreTurns at round end")
|
|
check(c:isTurnDisabled(0), "exploded contestant cannot appeal again")
|
|
local a, events = play(c, { [0] = boom, plain, plain, plain })
|
|
eq(a[0], 0, "exploded contestant appeals 0 next round")
|
|
eq(events[0][2].text, "gText_MonWasWatchingOthers", "exploded contestant watches the others")
|
|
end
|
|
|
|
-- pokeemerald/src/contest_effect.c:307
|
|
do
|
|
local jam = moveWith(E.JAMS_OTHERS_BUT_MISS_ONE_TURN)
|
|
local cat = moves.moves[jam].category
|
|
local plain = moveWith(E.HIGHLY_APPEALING, { notCategory = cat, allowCombo = true })
|
|
local c = newContest(cat, { [0] = jam, plain, plain, plain })
|
|
local last = byOrder(c, 3)
|
|
local list = { [0] = plain, plain, plain, plain }
|
|
list[last] = jam
|
|
play(c, list)
|
|
eq(c.status[last].numTurnsSkipped, 1, "jam-and-rest move skips the next turn")
|
|
play(c, list)
|
|
eq(c.status[last].numTurnsSkipped, 0, "skip counter runs out after one round")
|
|
end
|
|
|
|
-- pokeemerald/src/contest_effect.c:754
|
|
do
|
|
local early = moveWith(E.NEXT_APPEAL_EARLIER)
|
|
local cat = moves.moves[early].category
|
|
local plain = moveWith(E.HIGHLY_APPEALING, { notCategory = cat, allowCombo = true })
|
|
local c = newContest(cat, { [0] = plain, plain, plain, plain })
|
|
local last = byOrder(c, 3)
|
|
local list = { [0] = plain, plain, plain, plain }
|
|
list[last] = early
|
|
play(c, list)
|
|
eq(c.results.turnOrder[last], 0, "move-up-in-line appeals first next round")
|
|
end
|
|
|
|
-- pokeemerald/src/contest_effect.c:964
|
|
do
|
|
local freeze = moveWith(E.DONT_EXCITE_AUDIENCE)
|
|
local cat = moves.moves[freeze].category
|
|
local same, sameBase
|
|
for _, e in ipairs({ E.HIGHLY_APPEALING, E.USER_LESS_EASILY_STARTLED, E.AVOID_STARTLE, E.AVOID_STARTLE_ONCE,
|
|
E.AVOID_STARTLE_SLIGHTLY, E.USER_MORE_EASILY_STARTLED }) do
|
|
local ok, id = pcall(moveWith, e, { category = cat, allowCombo = true })
|
|
if ok then same, sameBase = id, D.effects[e].appeal; break end
|
|
end
|
|
local c = newContest(cat, { [0] = same, same, same, same })
|
|
local first = byOrder(c, 0)
|
|
local list = { [0] = same, same, same, same }
|
|
list[first] = freeze
|
|
local a, events = play(c, list)
|
|
eq(c.contest.applauseLevel, 1, "only the freezer's appeal moved the meter")
|
|
for i = 0, 3 do
|
|
if i ~= first then
|
|
eq(a[i], sameBase, "appeals after a crowd freeze get no excitement bonus")
|
|
local ignored = false
|
|
for _, e in ipairs(events[i]) do if e.text == "gText_MonsMoveIsIgnored" then ignored = true end end
|
|
check(ignored, "crowd ignores appeals after a freeze")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- pokeemerald/src/contest_util.c:1470
|
|
do
|
|
local c = { round1 = { [0] = 63, 0, 1, 700 }, round2 = { [0] = 80, -80, 1, -1 } }
|
|
eq(Util.preliminaryStars(c, 0, true), 1, "63 condition points = 1 star")
|
|
eq(Util.preliminaryStars(c, 1, true), 0, "0 points = 0 stars")
|
|
eq(Util.preliminaryStars(c, 2, true), 1, "any condition shows at least one star")
|
|
eq(Util.preliminaryStars(c, 3, true), 10, "stars cap at 10")
|
|
eq(Util.round2Hearts(c, 0, true), 1, "80 round-2 points = 1 heart")
|
|
eq(Util.round2Hearts(c, 1, true), -1, "negative round-2 points = negative hearts")
|
|
eq(Util.round2Hearts(c, 3, true), -1, "any negative round-2 points shows one lost heart")
|
|
end
|
|
|
|
-- pokeemerald/src/contest.c:5594
|
|
do
|
|
local session = {}
|
|
Util.clearAllWinners(session, D)
|
|
eq(#session.contestWinners, 13, "13 contest winner slots after new game")
|
|
eq(session.contestWinners[1].species, man.defaultWinners[0].species, "hall winner 1 is the ROM default")
|
|
eq(session.contestWinners[9].species, 0, "museum slots start empty")
|
|
eq(Util.countPlayerMuseumPaintings(session), 0, "no museum paintings at new game")
|
|
local plain = moveWith(E.HIGHLY_APPEALING)
|
|
local c = newContest(0, { [0] = plain, plain, plain, plain })
|
|
c.standings = { [0] = 1, 2, 3, 0 }
|
|
c.mons[3].species = 25
|
|
c.mons[3].personality = 1234
|
|
eq(Util.saveContestWinner(session, 0, c), true, "hall winner saved")
|
|
eq(session.contestWinners[1].species, 25, "newest hall winner goes to slot 1")
|
|
eq(session.contestWinners[2].species, man.defaultWinners[0].species, "older hall winners shift down")
|
|
eq(Util.saveContestWinner(session, Util.SAVE_FOR_MUSEUM, c), true, "player winner saved for the museum")
|
|
eq(session.contestWinners[9].species, 25, "cool museum painting slot 9")
|
|
check(session.contestWinners[9].contestCategory < 3, "museum caption id stays in the cool band")
|
|
eq(Util.countPlayerMuseumPaintings(session), 1, "one museum painting")
|
|
c.standings = { [0] = 0, 2, 3, 1 }
|
|
eq(Util.saveContestWinner(session, Util.SAVE_FOR_MUSEUM, c), false, "an NPC winner is never saved to the museum")
|
|
end
|
|
|
|
T.finish()
|