-- engine/items/item_effects.asm:1954 -- engine/battle/core.asm:2520 -- Not under POKEPORT_SPEED: SFX_HEAL_AILMENT rides the real-time audio clock. -- POKEPORT_DRIVER=tests/drivers/pp_restore_menu_2155_2158.lua \ -- POKEPORT_IDENTITY=pp2155 POKEPORT_TOUCH=0 POKEPORT_VERSION=red \ -- SHOT_DIR=/tmp/shots2155 love . return function(game) local U = dofile("tests/drivers/util.lua") local DIR = os.getenv("SHOT_DIR") or "/tmp/shots" local Pokemon = require("src.pokemon.Pokemon") local Bag = require("src.inventory.Bag") local ItemEffects = require("src.inventory.ItemEffects") local PartyMenu = require("src.ui.PartyMenu") local MoveSelectMenu = require("src.ui.MoveSelectMenu") local TextBox = require("src.render.TextBox") local pass, fail = 0, 0 local function check(label, ok) if ok then pass = pass + 1 else fail = fail + 1 end U.log(ok and "PASS" or "FAIL", label) return ok end local function top() return game.stack:top() end local function isPicker(s) return s ~= nil and (s.screenId == "PartyMenu" or getmetatable(s) == PartyMenu) end local function isMoveMenu(s) return getmetatable(s) == MoveSelectMenu end local function isBox(s) return getmetatable(s) == TextBox end local function inStack(pred) for _, s in ipairs(game.stack.states or {}) do if pred(s) then return true end end return false end U.log("======== #2155/#2158 PP restore: machine checks ========") for _, id in ipairs({ "ELIXER", "MAX_ELIXER", "ETHER", "MAX_ETHER", "PP_UP" }) do check(id .. " keeps the party menu drawn", ItemEffects.keepsPartyMenuOpen(id) == true) check(id .. " resolves in the item table", game.data.items[id] ~= nil) end check("MoveSelectMenu draws", type(MoveSelectMenu.draw) == "function") local lead = Pokemon.new(game.data, "CHARIZARD", 50) local topped = Pokemon.new(game.data, "SNORLAX", 40) for _, mv in ipairs(lead.moves) do mv.pp = 1 end game.save.party = { lead, topped } game.save.player.name = "RED" for _, id in ipairs({ "ELIXER", "MAX_ELIXER", "ETHER", "MAX_ETHER" }) do Bag.add(game.save, id, 5) end U.teleport(game, "PALLET_TOWN", 10, 8, "down") U.wait(10) local function cursorTo(menu, want) for _ = 1, 40 do if not menu or menu.index == want then return menu and menu.index == want end U.tap(game, menu.index < want and "down" or "up") U.wait(3) end return menu.index == want end local function openPickerFor(id) U.tap(game, "start") U.wait(10) local menu = top() if not (menu and menu.screenId == "StartMenu") then return nil, "start menu never opened" end local itemRow for i, it in ipairs(menu.items or {}) do if it.label == "ITEM" then itemRow = i break end end if not itemRow or not cursorTo(menu, itemRow) then return nil, "no ITEM row" end U.tap(game, "a") U.wait(10) local bag = top() if not (bag and bag.screenId == "BagMenu") then return nil, "bag never opened" end local bagRow for i, r in ipairs(bag.items or {}) do if r.value == id then bagRow = i break end end if not bagRow or not cursorTo(bag, bagRow) then return nil, id .. " not in bag" end U.tap(game, "a") U.wait(10) local ut = top() if ut and ut.items and ut.items[1] and ut.items[1].label == "USE" then if not cursorTo(ut, 1) then return nil, "USE row unreachable" end U.tap(game, "a") U.wait(10) end local picker = top() if not isPicker(picker) then return nil, "party picker never opened" end return picker end local function backToOverworld() for _ = 1, 30 do if top() == game.overworld then return true end U.tap(game, "b") U.wait(6) end return top() == game.overworld end local function dismiss() for _ = 1, 30 do if not inStack(isPicker) then break end U.tap(game, "a") U.wait(8) end end local function waitMoveMenu() for _ = 1, 240 do local t = top() if isMoveMenu(t) and t.ready then return true end U.wait(1) end return false end local function waitForBag() for _ = 1, 60 do local t = top() if t ~= nil and t.screenId == "BagMenu" then break end U.wait(1) end end U.log("======== #2155 ELIXER: SFX_HEAL_AILMENT over a live party menu ========") local picker, why = openPickerFor("ELIXER") check("party picker opened for ELIXER" .. (why and (" (" .. why .. ")") or ""), picker ~= nil) if picker then check("keepOpen is set for the ELIXERs", picker.keepOpen == true) cursorTo(picker, 1) U.tap(game, "a") U.wait(6) check("the party menu is still on the stack after the pick", inStack(isPicker)) -- engine/items/item_effects.asm:2022 check("the cursor is still drawn", picker.cursorsErased ~= true) for _ = 1, 60 do if isBox(top()) then break end U.wait(1) end check("the PP restored box opened", isBox(top())) check("...over the STILL-drawn party menu", inStack(isPicker)) check("every slot came back up", lead.moves[1].pp > 1) U.log("LISTEN", "SFX_HEAL_AILMENT should have played as the box opened") U.wait(60) U.shot(game, DIR .. "/pp2155_elixir_message_over_party.png") dismiss() check("the picker is gone once the message is dismissed", not inStack(isPicker)) -- pokered engine/items/item_effects.asm:1244 waitForBag() check("and we are back on the ITEM list", top() ~= nil and top().screenId == "BagMenu") end backToOverworld() U.log("======== #2155 MAX ELIXER refusal prints over the party menu ========") local refuse = openPickerFor("MAX_ELIXER") if check("party picker opened for MAX ELIXER", refuse ~= nil) then cursorTo(refuse, 2) -- the untouched SNORLAX U.tap(game, "a") U.wait(6) for _ = 1, 60 do if isBox(top()) then break end U.wait(1) end check("the no-effect box opened", isBox(top())) check("...over the STILL-drawn party menu", inStack(isPicker)) U.wait(60) U.shot(game, DIR .. "/pp2155_max_elixir_no_effect.png") dismiss() end backToOverworld() U.log("======== #2158 ETHER: wMoveMenuType 2 window over the party menu ========") for _, mv in ipairs(lead.moves) do mv.pp = 1 end local ether = openPickerFor("ETHER") if check("party picker opened for ETHER", ether ~= nil) then check("keepOpen is set for the ETHERs", ether.keepOpen == true) cursorTo(ether, 1) U.tap(game, "a") check("the move window is the top state", waitMoveMenu()) check("...with the party menu still drawn underneath", inStack(isPicker)) U.wait(20) U.shot(game, DIR .. "/pp2158_ether_move_window.png") -- engine/items/item_effects.asm:1985 U.tap(game, "b") U.wait(8) check("B goes back to the party menu", isPicker(top())) U.tap(game, "a") check("and the move window opens again", waitMoveMenu()) local before = lead.moves[1].pp U.tap(game, "a") U.wait(6) for _ = 1, 60 do if isBox(top()) then break end U.wait(1) end check("the PP restored box opened", isBox(top())) check("...over the STILL-drawn party menu", inStack(isPicker)) check("the picked move gained PP", lead.moves[1].pp > before) U.log("LISTEN", "SFX_HEAL_AILMENT should have played for the ETHER too") U.wait(60) U.shot(game, DIR .. "/pp2158_ether_message_over_party.png") dismiss() -- pokered engine/items/item_effects.asm:1244 waitForBag() check("back on the ITEM list", top() ~= nil and top().screenId == "BagMenu") end backToOverworld() U.log("======== #2158 MAX ETHER refusal keeps both windows up ========") local maxEther = openPickerFor("MAX_ETHER") if check("party picker opened for MAX ETHER", maxEther ~= nil) then cursorTo(maxEther, 2) -- the untouched SNORLAX U.tap(game, "a") check("the move window opened", waitMoveMenu()) U.tap(game, "a") U.wait(6) for _ = 1, 60 do if isBox(top()) then break end U.wait(1) end check("the no-effect box opened", isBox(top())) check("...over the STILL-drawn party menu", inStack(isPicker)) U.wait(60) U.shot(game, DIR .. "/pp2158_max_ether_no_effect.png") dismiss() end backToOverworld() U.log(("======== PP restore driver: %d passed, %d failed ========") :format(pass, fail)) U.log(fail == 0 and "DRIVER PASS" or "DRIVER FAIL") for _, mv in ipairs(lead.moves) do mv.pp = 1 end local handoff = openPickerFor("ETHER") if handoff then cursorTo(handoff, 1) U.tap(game, "a") U.wait(8) end U.log("The ETHER move window is open over the party menu. Compare it with") U.log("shots/issue_2158_1.png: a 16x6 box at tile 4,7, four names single") U.log("spaced with '-' for empty slots, no PP column, cursor at column 5,") U.log("and 'Restore PP of / which technique?' in the bottom box. B returns") U.log("to the party menu; A restores PP with the Heal_Ailment jingle.") love.event.quit(fail == 0 and 0 or 1) end