-- U2 verification: ENGINE_PLAYER_IS_FEMALE end to end, plus the four gendered -- pictures and the Crystal surf refusal. Not a suite; a shot harness. -- -- POKEPORT_GAME=crystal POKEPORT_BOOT_CINEMA=1 POKEPORT_GENDER=girl \ -- POKEPORT_SHOT_DIR= \ -- POKEPORT_DRIVER=tests/drivers/crystal_gender_flag_shots.lua love . local U = require("tests.drivers.util") local BattleState = require("src.ui.gen2.BattleState") local FieldMoves = require("src.world.gen2.FieldMoves") local GameVersion = require("src.core.GameVersion") local GenderSelect = require("src.ui.gen2.GenderSelect") local HallOfFame = require("src.ui.gen2.HallOfFame") local MainMenu = require("src.ui.gen2.MainMenu") local Mon = require("src.battle.gen2.Mon") local NamePick = require("src.ui.gen2.NamePick") local NamingScreen = require("src.ui.gen2.NamingScreen") local OakSpeech = require("src.ui.gen2.OakSpeech") local Screens = require("src.ui.Screens") return function(game) local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/u2-gender" local want = (os.getenv("POKEPORT_GENDER") or "girl"):lower() local log = io.open(out .. "/driver.log", "w") local function say(line) print("[u2] " .. line) if log then log:write(line .. "\n"); log:flush() end end local function done(code) if log then log:close() end love.event.quit(code) if code ~= 0 then error("driver failed", 0) end coroutine.yield() end local function bail(reason) say("FAIL " .. reason) done(1) end local function top() return game.stack:top() end local function isA(class) local s = top() return s ~= nil and getmetatable(s) == class 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 waitFor(label, predicate, frames) for _ = 1, frames or 900 do if predicate() then return true end U.wait(1) end bail(("stalled waiting for %s (top is %s)"):format(label, tostring(top()))) end say("version=" .. GameVersion.get() .. " engine=" .. GameVersion.engine()) say("fixes.surfOntoNpc=" .. tostring(GameVersion.fixes().surfOntoNpc)) ------------------------------------------------------------------ new game U.wait(10) for _ = 1, 1200 do if isA(MainMenu) then break end tap("start", 2) end waitFor("the main menu", function() return isA(MainMenu) end, 600) local menu = top() for i, item in ipairs(menu.list.items) do if item.value == "new" then menu.list.index = i end end tap("a") local gendered = false for _ = 1, 400 do if isA(GenderSelect) then gendered = true break end if isA(OakSpeech) or isA(NamePick) then break end U.wait(1) end if GameVersion.engine() == "crystal" then if not gendered then bail("Crystal never offered InitGender") end local pick = top() pick.cursor = (want == "girl") and 2 or 1 U.shot(game, out .. "/00-gender.png") say("OK gender screen, choosing " .. want) tap("a") else if gendered then bail("Gold offered a gender prompt; it has none") end say("OK no gender prompt on " .. GameVersion.get()) end for _ = 1, 900 do if game.phase == "play" and game.world and game.world.map then break end if isA(NamingScreen) then local naming = top() naming.row = naming:bottomRow() naming.col = 6 end tap("a", 2) end if not (game.phase == "play" and game.world and game.world.map) then bail("never reached the overworld (top is " .. tostring(top()) .. ")") end local world = game.world local save = game.save say("OK overworld, name=" .. tostring(save.player.name) .. " gender=" .. tostring(save.player.gender) .. " sprite=" .. tostring(world:playerSpriteName())) ------------------------------------------------------ the engine flag itself local id = FieldMoves.FEMALE_FLAG say("FieldMoves.FEMALE_FLAG=" .. tostring(id)) if GameVersion.engine() == "crystal" then if id ~= 99 then bail("female flag id is " .. tostring(id) .. ", want 99") end local reads = world:engineFlag(id) say("world:engineFlag(" .. id .. ")=" .. tostring(reads)) if reads ~= (want == "girl") then bail("checkflag ENGINE_PLAYER_IS_FEMALE read " .. tostring(reads)) end elseif id ~= nil then bail("Gold bound a female flag id: " .. tostring(id)) end ------------------------------------------------------------- the three sites local sites = { { "COPYCATS_HOUSE_2F", 3, 4, "up", "01-copycat" }, { "ROUTE_37", 5, 9, "up", "02-route37" }, { "POKECENTER_2F", 5, 5, "up", "03-pokecenter2f" }, } for _, site in ipairs(sites) do local mapId, x, y, facing, name = site[1], site[2], site[3], site[4], site[5] if world.maps and world.maps[mapId] then world:setMap(mapId, x, y, facing) U.wait(40) U.shot(game, out .. "/" .. name .. ".png") local names = {} for _, npc in ipairs(world.npcs or {}) do names[#names + 1] = tostring(npc.def and npc.def.sprite) end say(name .. " map=" .. tostring(world.map.id) .. " objects=" .. table.concat(names, ",")) else say("SKIP " .. mapId .. ": not in this cache") end end ------------------------------------------------------------------- the pack world:setMap("NEW_BARK_TOWN", 13, 6, "down") U.wait(20) save.inventory = save.inventory or {} save.inventory.POTION = 3 Screens.push(game, "Gen2PackMenu", { save = save, world = {}, onClose = function() game.stack:pop() end }) U.wait(20) U.shot(game, out .. "/04-pack.png") local packGfx = top() and top().gfx local pals = packGfx and packGfx.gfx and packGfx.gfx.palettes local menuGfx = game.data.gen2MenuGfx or {} local femalePals = menuGfx.pack and menuGfx.pack.palettesFemale say("pack palettes are the female set: " .. tostring(pals ~= nil and pals == femalePals)) tap("b") U.wait(10) while top() do game.stack:pop() end ------------------------------------------------------------ battle back pic local hud = menuGfx.battleHud or {} say("battleHud.playerBack=" .. tostring(hud.playerBack) .. " playerBackFemale=" .. tostring(hud.playerBackFemale)) save.party = { Mon.new(game.data, "CYNDAQUIL", 12) } world:startBattle({ wild = Mon.new(game.data, "WOOPER", 5) }) local battle for _ = 1, 600 do if getmetatable(top()) == BattleState then battle = top() break end U.wait(1) end if not battle then bail("startBattle never reached the battle screen") end -- showPlayerTrainer is only true until SendOutPlayerMon; hold it so the -- back pic is what the shot catches. for _ = 1, 90 do battle.showPlayerTrainer = true U.wait(1) end battle.showPlayerTrainer = true U.shot(game, out .. "/05-battle-backpic.png") say("battle backpic path=" .. tostring(battle.playerBackPath)) while top() do game.stack:pop() end U.wait(10) ------------------------------------------------------------- trainer card Screens.push(game, "Gen2TrainerCard", { onClose = function() game.stack:pop() end }) U.wait(30) U.shot(game, out .. "/06-trainercard.png") say("trainer card female arm=" .. tostring(top() and top().female)) tap("b") U.wait(10) while top() do game.stack:pop() end ------------------------------------------------------------- hall of fame save.hallOfFame = nil Screens.push(game, "Gen2HallOfFame", { save = save, mode = "induct", text = world.text, entry = { winCount = 1, mons = { { species = "CYNDAQUIL", level = 12, nickname = "CYNDAQUIL", otId = 1234, gender = "male" } } }, onDone = function() game.stack:pop() end }) U.wait(20) local hof = top() if getmetatable(hof) ~= HallOfFame then bail("Gen2HallOfFame did not push (top is " .. tostring(hof) .. ")") end -- The ceremony walks its own phases; freeze it so the two player cards can -- be posed and shot. hof.update = function() end hof.scx, hof.scy = 0, 0 hof.phase = "player" U.shot(game, out .. "/07-hof-trainerpic.png") hof.phase = "playerBack" U.shot(game, out .. "/08-hof-backpic.png") say("hof backpic=" .. tostring(hof.playerBackPath) .. " trainerPic=" .. tostring(hof.trainerPicPath)) while top() do game.stack:pop() end U.wait(10) ------------------------------------------------------------- surf onto NPC -- -- SurfFunction.TrySurf with a facing object: Crystal refuses, Gold does not -- (../pokecrystal/engine/events/overworld.asm:364). local ctx = { save = { player = { badges = { FOG = true } } }, mon = { species = "LAPRAS" }, facing = "down", facingColl = 0x29, playerColl = 0x00, playerState = FieldMoves.PLAYER_NORMAL, facingObject = { id = "an NPC standing on the water" }, } local refused = FieldMoves.surfFromMenu(ctx) say("surf onto an NPC: ok=" .. tostring(refused.ok) .. " text=" .. tostring(refused.text)) local shouldRefuse = GameVersion.fixes().surfOntoNpc == true if (refused.ok ~= true) ~= shouldRefuse then bail("surf-onto-NPC answered ok=" .. tostring(refused.ok) .. " but fixes().surfOntoNpc=" .. tostring(shouldRefuse)) end ctx.facingObject = nil if not FieldMoves.surfFromMenu(ctx).ok then bail("open water refused SURF") end say("PASS " .. GameVersion.get() .. " / " .. want .. " in " .. out) done(0) end