-- mobile/mobile_46.asm:5271-5340, load_trainer.asm:101-108, maps/BattleTower1F.asm:122-133 -- POKEPORT_IDENTITY=crystal-sep04 POKEPORT_GAME=crystal POKEPORT_VERSION=crystal \ -- POKEPORT_SHOT_DIR=/tmp/tower-2202 \ -- POKEPORT_DRIVER=tests/drivers/crystal_battle_tower_bug2202.lua love . local U = require("tests.drivers.util") local BattleTower = require("src.core.gen2.BattleTower") local BattleTowerMenu = require("src.ui.gen2.BattleTowerMenu") local Mon = require("src.battle.gen2.Mon") local PackMenu = require("src.ui.gen2.PackMenu") local Specials = require("src.script.gen2.Specials") local VITAMINS = { "HP_UP", "PROTEIN", "IRON", "CARBOS", "CALCIUM" } return function(game) local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/tower-2202" local fails = 0 local function say(line) print("[driver] " .. line) end local function ok(cond, line) if not cond then fails = fails + 1 end say((cond and "PASS " or "FAIL ") .. line) end local function shot(name) U.shot(game, ("%s/%s.png"):format(out, name)) end local function tap(button, frames) game.input.pressQueue[#game.input.pressQueue + 1] = button game.input.state[button] = true U.wait(2) game.input.state[button] = false U.wait(frames or 4) end local function top() return game.stack:top() end U.wait(60) local world = game.world assert(world and world.map, "crystal world did not boot") local function party(spec) local list = {} for _, row in ipairs(spec) do local opts = nil if row[3] then local def = game.data.moves[row[3]] opts = { moves = { { id = row[3], pp = def.pp, maxPp = def.pp } } } end local m = Mon.new(game.data, row[1], row[2], opts) m.item = row[4] list[#list + 1] = m end return list end say("--- A: the room menu's message rows and the uber refusal's pages") while top() do game.stack:pop() end local save = game.save save.party = party({ { "LUGIA", 30 }, { "FERALIGATR", 30, nil, "BERRY" }, { "MEGANIUM", 30, nil, "GOLD_BERRY" } }) local picked = nil ok(world:pushScreen("Gen2BattleTowerMenu", { save = save, party = save.party, rows = BattleTower.levelGroupRows(save), monName = world.vm.specials.monName, onDone = function(group) picked = group end, }), "Gen2BattleTowerMenu pushed") U.wait(4) local screen = top() ok(getmetatable(screen) == BattleTowerMenu, "and it is on top") tap("up", 4); tap("up", 4) ok(screen.cursor == 3, "the spinner sits on L:30") tap("a", 6) ok(screen.phase == "message", "an uber under L70 is refused") ok(screen.pages and #screen.pages == 2, "Text_UberRestriction's para is two pages") ok(screen.page == 1 and (screen.message or ""):find("may go", 1, true) ~= nil, "page one: may go / only to BATTLE") shot("2202_01_uber_page1_rows_14_16") U.wait(0x90) ok(screen.phase == "message" and screen.page == 1, "page one waits on a button, not the $80 countdown") tap("a", 6) ok(screen.page == 2 and (screen.message or ""):find("Lv.70", 1, true) ~= nil, "A turns to ROOMS that are / Lv.70 or higher.") shot("2202_02_uber_page2_rows_14_16") for _ = 1, 0x90 do if screen.phase == "pick" then break end U.wait(1) end ok(screen.phase == "pick", "and the menu restarts after the hold") tap("b", 6) ok(screen.phase == "quit", "B opens the cancel prompt") shot("2202_03_cancel_prompt_rows_14_16") tap("down", 4); tap("a", 6) ok(screen.phase == "pick", "NO puts the spinner back") save.party = party({ { "TYPHLOSION", 30, "FLAMETHROWER" }, { "FERALIGATR", 30, "SURF", "BERRY" }, { "MEGANIUM", 30, "BODY_SLAM", "GOLD_BERRY" } }) screen.party = save.party tap("up", 4); tap("up", 4) ok(screen.cursor == 3, "back on L:30") tap("a", 6) ok(picked == 3, "L:30 is accepted, level group 3") ok(top() == nil, "and the screen is gone") -- mobile/mobile_46.asm:1283-1286 world.vm.btLevelGroup = picked say("--- B: the battle room draws out of the chosen group") save.party = party({ { "TYPHLOSION", 60, "FLAMETHROWER" }, { "FERALIGATR", 60, "SURF", "BERRY" }, { "MEGANIUM", 60, "BODY_SLAM", "GOLD_BERRY" } }) local tower = BattleTower.state(save) tower.levelGroup = 1 tower.streak = 6 tower.trainers = {} tower.prevTeams = nil tower.challenge = BattleTower.NO_CHALLENGE world.vm.scriptVar = BattleTower.ACTIONS.CHOOSEREWARD Specials.ALL.BattleTowerAction(world.vm) say("reward banked at the desk: " .. tostring(tower.reward)) ok(tower.reward ~= nil and tower.reward ~= "POTION", "BATTLETOWERACTION_CHOOSEREWARD rolled a vitamin") assert(world:setMap("BATTLE_TOWER_BATTLE_ROOM", 3, 7, "up"), "no BATTLE_TOWER_BATTLE_ROOM in the cache") U.wait(30) local opponent, sawBattle = nil, false for _ = 1, 900 do local vm = world.vm if vm and vm.btOpponent and not opponent then opponent = vm.btOpponent say(("opponent: %s %s group %d"):format(tostring(opponent.classId), tostring(opponent.name), opponent.group)) for slot, mon in ipairs(opponent.rows) do say((" mon %d: %s L%d"):format(slot, mon.species, mon.level)) end U.wait(20) shot("2202_04_opponent_from_l30_group") end if world.battleActive then sawBattle = true break end if world.textbox or world.choicebox then tap("a", 4) else U.wait(2) end end ok(opponent ~= nil, "LoadOpponentTrainerAndPokemonWithOTSprite drew one") ok(opponent and opponent.group == 3, "out of level group 3, not the SRAM byte's 1") local allThirty = opponent ~= nil and #opponent.rows == 3 for _, mon in ipairs((opponent and opponent.rows) or {}) do if mon.level ~= 30 then allThirty = false end end ok(allThirty, "every drawn mon is L30") ok(sawBattle, "BattleTowerBattle pushed the battle screen") local bscreen = sawBattle and top() or nil local battle = bscreen and bscreen.battle if battle then -- engine/battle/core.asm:7808-7817, the enemy HUD comes up on the send-out local hudUp = false for _ = 1, 600 do if bscreen.showEnemyHud and not bscreen:hudCleared("enemy") then hudUp = true break end tap("a", 3) end ok(hudUp, "the enemy HUD is drawn") ok(battle.enemy and battle.enemy.level == 30, "the enemy lead is L30 in battle") U.wait(8) shot("2202_05_enemy_hud_l30") end local function hardestMove(mon) local best, bestPower = 1, -1 for index, move in ipairs((mon and mon.moves) or {}) do local def = game.data.moves and game.data.moves[move.id] local power = (def and def.power) or 0 if (move.pp or 0) > 0 and power > bestPower then best, bestPower = index, power end end return best end local function refill(mon) for _, move in ipairs((mon and mon.moves) or {}) do move.pp = move.maxPp or move.pp end end for _ = 1, 2400 do if not world.battleActive or (battle and battle.over) then break end local phase = bscreen and bscreen.phase if phase == "menu" then refill(battle and battle.player) bscreen.menuIndex = 1 tap("a", 4) elseif phase == "moves" then bscreen.menuIndex = hardestMove(battle and battle.player) tap("a", 4) elseif phase == "submenu" then tap("down", 3) tap("a", 4) else tap("a", 3) end end say("battle over=" .. tostring(battle and battle.over) .. " outcome=" .. tostring(battle and battle.outcome) .. " phase=" .. tostring(bscreen and bscreen.phase)) ok(battle and battle.over and battle.outcome == "win", "the seventh battle is won") for _ = 1, 200 do if not world.battleActive then break end tap("a", 3) end ok(not world.battleActive, "the battle screen came down") say("--- C: Script_GivePlayerHisPrize") local pages = {} local shotCongrats, shotPrize = false, false for _ = 1, 600 do local body = world.lastText if type(body) == "string" and body ~= "" and pages[#pages] ~= body then pages[#pages + 1] = body say("page: " .. body:gsub("\n", " / ")) if not shotCongrats and body:find("Congratulations", 1, true) then shotCongrats = true U.wait(90) shot("2202_06_congratulations") elseif not shotPrize and body:find("got five", 1, true) then shotPrize = true U.wait(90) shot("2202_07_got_five_vitamin") end end if shotPrize and not world:busy() and top() == nil then break end if world.choicebox then tap("a", 4) else tap("a", 3) end end local joined = table.concat(pages, " | ") ok(joined:find("got five", 1, true) ~= nil, "Text_PlayerGotFive printed") local named = nil for _, id in ipairs(VITAMINS) do local def = game.data.items[id] if def and joined:find(def.name, 1, true) then named = def.name end end ok(named ~= nil, "and it names a vitamin: " .. tostring(named)) ok(joined:find("Item0", 1, true) == nil, "not Item0") local banked = nil for _, id in ipairs(VITAMINS) do if (save.inventory[id] or 0) >= 5 then banked = id end end ok(banked ~= nil, "the pack holds five of " .. tostring(banked)) local junk = {} for key in pairs(save.inventory) do if tostring(key):match("^ITEM_?%d+$") then junk[#junk + 1] = key end end ok(#junk == 0, "and no ITEM_255 row: " .. table.concat(junk, ",")) say("map after the prize: " .. tostring(world.map and world.map.id)) while top() do game.stack:pop() end ok(world:pushScreen("Gen2PackMenu", { onClose = function() end }), "the pack opens") U.wait(10) ok(getmetatable(top()) == PackMenu, "on top") shot("2202_08_pack_with_vitamins") while top() do game.stack:pop() end say(fails == 0 and "PASS" or (fails .. " FAILURES")) love.event.quit(fails == 0 and 0 or 1) end