Files
gen1recomp/tests/drivers/em_berry_blender.lua
T
bryanthaboi 690f0cd9a1 TOUCHDOWN
2026-09-27 16:07:31 -04:00

327 lines
13 KiB
Lua

local U = require("tests.drivers.util")
local S = require("tests.drivers.em_story_util")
local F = require("tests.drivers.em_fc_util").new("em_berry_blender")
local d = S.new("em_berry_blender", "/tmp/em_berry_blender")
local function waitFor(pred, frames)
for _ = 1, frames or 300 do
if pred() then return true end
U.wait(1)
end
return pred() and true or false
end
local function settleSafe(game, opts)
return F.try("settle", function() S.settle(game, opts) end)
end
local function lcg(seed)
local Rng = require("src.core.game3.rng")
local st = seed
return function()
st = (Rng.mulU32(st, 1103515245) + 24691) % 4294967296
return math.floor(st / 65536) % 65536
end
end
local function wrap(ui, plan)
local B = require("src.core.game3.rse.berry_blender")
local orig = ui.frame
local press = {}
for _, p in ipairs(plan.press or {}) do press[p] = true end
plan.playN = -1
plan.holds = {}
local function holdOnce(name)
if not plan.holds[name] then
plan.holds[name] = true
ui._hold = name
end
end
ui.frame = function(self, inp)
if self._hold then return end
if self.cb == "load" and self.state == 0 then
plan.playN = -1
plan.holds = {}
end
inp = inp or { new = {}, held = {} }
if self.cb == "play" then
if plan.playN < 0 then
plan.playN = 0
if plan.seed then
self.random = lcg(plan.seed)
self.vblankRandom = true
end
end
local new = {}
for k, v in pairs(inp.new or {}) do if k ~= "a" then new[k] = v end end
if plan.seed then
new.a = press[plan.playN] or nil
else
local g = self.game
local nxt = (g.arrowPos + g.speed) % 65536
if g:arrowProximity(nxt, 0) == B.PROXIMITY.BEST and (plan.cool or 0) == 0 then
new.a = true
plan.cool = 12
end
plan.cool = math.max(0, (plan.cool or 0) - 1)
end
inp = { new = new, held = inp.held or {} }
plan.playN = plan.playN + 1
end
orig(self, inp)
if self.cb == "load" and self.state == 4 and self.printer and self.printer.state == "clear" then holdOnce("load") end
if self.cb == "play" and plan.shotAt and plan.shotAt[plan.playN] then holdOnce("play_" .. plan.playN) end
local g = self.game
if self.cb == "end" and g then
if g.gameEndState == 5 and self.rankState == 5 then holdOnce("ranking") end
if g.gameEndState == 6 and self.resState == 4 then holdOnce("results") end
if g.gameEndState == 6 and self.resState == 6 and self.printer and self.printer.state == "clear" and self.printer.arrowFrame then holdOnce("made") end
if g.gameEndState == 10 then holdOnce("yesno") end
end
end
end
local function runScreen(game, UI, plan)
local ui = UI.active()
wrap(ui, plan)
local seen = {}
local answered = 0
local BagMenu = require("src.ui.game3.bag_menu")
for _ = 1, 60000 do
if ui.done or not UI.isOpen() then break end
if BagMenu.open and BagMenu._location == "blender" then
if not seen.bag then
seen.bag = true
U.wait(30)
d.shot(game, "01b_choose_berry")
end
if BagMenu.mode == "action" then
for i, a in ipairs(BagMenu.ACTIONS or {}) do if a == "CONFIRM" then BagMenu.actionCursor = i end end
end
U.tap(game, "a")
U.wait(6)
elseif ui._hold then
local name = ui._hold
seen[name] = true
if plan.shots and plan.shots[name] then d.shot(game, plan.shots[name]) end
if plan.onHold then plan.onHold(name, ui) end
ui._hold = nil
if name == "yesno" then
answered = answered + 1
local yes = plan.answer and plan.answer(answered)
if not yes then U.tap(game, "down"); U.wait(4) end
U.tap(game, "a")
elseif name ~= "load" and not name:find("^play_") then
U.wait(2)
U.tap(game, "a")
end
elseif ui.cb == "load" or (ui.cb == "end" and ui.game and ui.game.gameEndState >= 5) or ui.cb == "again" then
if ui.printer and ui.printer.state == "clear" then U.tap(game, "a") else U.wait(1) end
else
U.wait(1)
end
end
return ui, seen
end
return function(game)
local UI = require("src.ui.game3.rse.berry_blender")
local B = require("src.core.game3.rse.berry_blender")
local Pokeblock = require("src.core.game3.rse.pokeblock")
local Records = require("src.ui.game3.rse.frontier_records")
local Message = require("src.ui.game3.message")
local Bag = require("src.core.game3.bag")
local golden = dofile("tests/data/emerald_blender/golden.lua").mister2
if not F.boot(game) then return d.finish() end
local s = S.session()
s.name = "NICK"
s.playerName = "NICK"
local Options = require("src.core.game3.options")
local o = Options.block(s.options or {})
if type(s.options) == "table" then
o.textSpeed, o.frameType = 2, 4
end
pcall(function() require("src.ui.game3.chrome").setFrameType(4) end)
s.party = {}
S.giveMon("SPECIES_TORCHIC", 30)
F.flags().setVar("VAR_REPEL_STEP_COUNT", 250)
F.flags().set("FLAG_RECEIVED_POKEBLOCK_CASE", true)
S.giveItem("ITEM_POKEBLOCK_CASE", 1)
S.giveItem("ITEM_CHERI_BERRY", 4)
Pokeblock.clearAll(s)
Pokeblock.add(s, { color = Pokeblock.COLOR.PINK, sweet = 12, feel = 23 })
s.berryBlenderRecords = { 11052, 0, 0 }
s.gameStats = s.gameStats or {}
local cheri = S.item("ITEM_CHERI_BERRY")
-- pokeemerald/data/maps/LilycoveCity_ContestLobby/scripts.inc:528
F.goTo(game, "EM_LILYCOVE_CITY_CONTEST_LOBBY", 30, 2, "up", { keepScripts = true })
settleSafe(game, { limit = 600 })
local opened = false
for _ = 1, 5 do
U.tap(game, "a")
opened = waitFor(function() return Records.isOpen() end, 60)
if opened then break end
end
d.check(opened, "ShowBerryBlenderRecordWindow opens the speed record window")
if opened then
local texts = {}
for _, p in ipairs(Records._window.prints) do texts[#texts + 1] = p.s end
local all = table.concat(texts, " | ")
d.check(all:find("MAXIMUM SPEED RECORD!", 1, true) ~= nil and all:find("110.52 RPM", 1, true) ~= nil
and all:find("{UNK_SPACER}{UNK_SPACER}0.00 RPM", 1, true) ~= nil, "records window: " .. all)
d.shot(game, "00_speed_records")
for _ = 1, 30 do
if not Records.isOpen() then break end
U.wait(8)
U.tap(game, "a")
end
d.check(not Records.isOpen(), "a button press closes the record window")
for _ = 1, 30 do
if not S.vmRunning() then break end
U.wait(8)
U.tap(game, "a")
end
if not d.check(not S.vmRunning(), "waitbuttonpress + RemoveRecordsWindow + releaseall finish the sign script") then
local Hud = require("src.ui.game3.hud")
local top = require("src.ui.game3.stack").top()
d.note("vm=" .. S.vmWhere() .. " wait=" .. tostring(Hud._waitButton ~= nil) .. " top=" .. tostring(top and top.id)
.. " msg=" .. tostring(Message.isOpen()) .. " rec=" .. tostring(Records.isOpen()))
end
end
settleSafe(game, { limit = 600 })
-- pokeemerald/data/scripts/berry_blender.inc:229
F.goTo(game, "EM_LILYCOVE_CITY_CONTEST_LOBBY", 27, 6, "up", { keepScripts = true })
settleSafe(game, { limit = 600 })
d.shot(game, "01_lobby_blender")
local pages = {}
U.tap(game, "a")
settleSafe(game, {
limit = 6000,
until_ = function() return UI.isOpen() end,
watch = function()
local page = Message.isOpen() and Message.currentPage() or ""
if page ~= "" and pages[#pages] ~= page then pages[#pages + 1] = page end
end,
choice = function() return "yes" end,
})
local talk = table.concat(pages, " / "):gsub("\n", " ")
d.check(talk:find("make some", 1, true) ~= nil and talk:find("old-timer", 1, true) ~= nil,
"BerryBlender_Text_WantToMakePokeblocks (" .. talk .. ")")
d.check(talk:find("Let's BERRY BLENDER!", 1, true) ~= nil, "BerryBlender_Text_LetsBerryBlender")
if not d.check(UI.isOpen(), "special DoBerryBlending opens the BERRY BLENDER") then return d.finish() end
local ui = UI.active()
d.check(ui.var8004 == 1, "VAR_0x8004 = 1 NPC opponent")
local okBag = require("src.core.game3.rse.init").system("bag")
if not (okBag and okBag.chooseBerry) then
d.note("NOTE Rse bag system has no chooseBerry (ChooseBerryForMachine, crossfile bag owner); the blender takes the first berry in the BERRIES pocket")
d.dropChooser = true
end
local plan = {
seed = golden.rng0,
press = golden.press,
shotAt = { [1] = true, [100] = true, [300] = true, [600] = true, [1000] = true },
shots = {
load = "02_load_msg", play_1 = "07_play_0001", play_100 = "07_play_0100", play_300 = "07_play_0300",
play_600 = "07_play_0600", play_1000 = "07_play_1000", ranking = "10_ranking", results = "11_results",
made = "12_made", yesno = "13_yesno",
},
answer = function() return false end,
onHold = function(name, u)
if name == "load" then
local page = u.printer and table.concat(u.printer.lines, "\n") or ""
d.check(page:find("Starting up the BERRY BLENDER.", 1, true) ~= nil, "sText_BerryBlenderStart printed")
elseif name == "play_1" then
local g = u.game
d.check(g.playerNames[1] == "MISTER", "the NPC is MISTER (blend master away)")
d.check(g.chosenItemId[0] == cheri and g.chosenItemId[1] == S.item("ITEM_ASPEAR_BERRY"),
"CHERI vs MISTER's ASPEAR in the blender")
d.check(g.playerIdToArrowId[0] == 1 and g.playerIdToArrowId[1] == 2, "player arrow top right, MISTER bottom left")
elseif name == "ranking" then
local g = u.game
local sc = {}
for p = 0, 3 do for k = 0, 2 do sc[#sc + 1] = g.scores[p][k] end end
d.check(table.concat(sc, ",") == table.concat(golden.final.scores, ","),
"scores equal the pygba run (" .. table.concat(sc, ",") .. ")")
d.check(g.maxRPM == golden.final.maxRPM, "max RPM equals the pygba run (" .. g.maxRPM .. ")")
d.check(g.gameFrameTime == golden.final.frameTime, "blend time equals the pygba run (" .. g.gameFrameTime .. ")")
elseif name == "made" then
local page = u.printer and table.concat(u.printer.lines, "\n") or ""
d.check(page:find("RED ", 1, true) ~= nil and page:find("The level is 12, and the feel is 23.", 1, true) ~= nil,
"made message: " .. page:gsub("\n", " "))
end
end,
}
local berriesBefore = Bag.get(s.bag, cheri)
local dailyBefore = F.flags().var("VAR_DAILY_BLENDER")
ui = runScreen(game, UI, plan)
d.check(plan.playN == golden.frames, "play lasted " .. tostring(plan.playN) .. " frames (pygba " .. golden.frames .. ")")
waitFor(function() return not UI.isOpen() end, 300)
d.check(not UI.isOpen(), "NO returns to the field")
settleSafe(game, { limit = 1200 })
d.check(S.mapNow() == "EM_LILYCOVE_CITY_CONTEST_LOBBY", "back in the contest lobby")
local b = s.pokeblocks[2]
local P = golden.pokeblock
d.check(b and b.color == P.color and b.spicy == P.spicy and b.feel == P.feel,
string.format("RED POKeBLOCK lv%d feel %d added to the case (pygba %d/%d)", b and b.spicy or -1, b and b.feel or -1, P.spicy, P.feel))
d.check(Bag.get(s.bag, cheri) == berriesBefore - 1, "one CHERI BERRY used")
d.check(F.flags().var("VAR_DAILY_BLENDER") == dailyBefore + 1, "IncrementDailyBerryBlender")
d.check((s.gameStats[B.GAME_STAT_POKEBLOCKS] or 0) == 1, "GAME_STAT_POKEBLOCKS counted")
d.check(s.berryBlenderRecords[1] == 11052, "2-player record kept (110.52 > " .. golden.final.maxRPM / 100 .. ")")
d.shot(game, "14_back")
-- pokeemerald/data/scripts/berry_blender.inc:329
F.goTo(game, "EM_LILYCOVE_CITY_CONTEST_LOBBY", 23, 10, "up", { keepScripts = true })
settleSafe(game, { limit = 600 })
pages = {}
U.tap(game, "a")
settleSafe(game, {
limit = 6000,
until_ = function() return UI.isOpen() end,
watch = function()
local page = Message.isOpen() and Message.currentPage() or ""
if page ~= "" and pages[#pages] ~= page then pages[#pages + 1] = page end
end,
choice = function() return "yes" end,
})
talk = table.concat(pages, " / "):gsub("\n", " ")
d.check(talk:find("good at blending", 1, true) ~= nil, "BerryBlender_Text_LookGoodAtBlendingJoinUs")
if d.check(UI.isOpen(), "the three-NPC blender opens") then
ui = UI.active()
d.check(ui.var8004 == 3, "three NPC opponents")
local rounds = 0
local plan3 = {
shotAt = { [300] = true },
shots = { play_300 = "20_three_play", ranking = "21_three_ranking" },
answer = function(n) return n == 1 end,
onHold = function(name, u)
if name == "play_300" then
rounds = rounds + 1
local g = u.game
d.check(g.playerNames[1] == "MISS" and g.playerNames[2] == "LADDIE" and g.playerNames[3] == "LASSIE",
"MISS, LADDIE and LASSIE join (round " .. rounds .. ")")
end
end,
}
local before = Pokeblock.count(s)
ui = runScreen(game, UI, plan3)
d.check(rounds == 2, "YES blends another BERRY (" .. rounds .. " rounds)")
waitFor(function() return not UI.isOpen() end, 300)
settleSafe(game, { limit = 1200 })
d.check(Pokeblock.count(s) == before + 2, "two more POKeBLOCKS (" .. Pokeblock.count(s) .. ")")
d.check(s.berryBlenderRecords[3] > 0, "4-player speed record set (" .. s.berryBlenderRecords[3] .. ")")
d.check(Bag.get(s.bag, cheri) == berriesBefore - 3, "three CHERI BERRIES used in all")
end
if d.dropChooser then
local keep = {}
for _, m in ipairs(d.vmLogs) do if not m:find("ChooseBerryForMachine", 1, true) then keep[#keep + 1] = m end end
d.vmLogs = keep
end
d.finish()
end