local U = require("tests.drivers.util") local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/game3_ui_fly_map" local PALLET = "FR_PALLET_TOWN" local VIRIDIAN = "FR_VIRIDIAN_CITY" -- pokefirered/include/constants/flags.h:1090 local FLAG_BADGE03_GET = 0x822 local failures = 0 local function say(line) print(line) os.execute('mkdir -p "' .. DIR .. '" 2>/dev/null') local fh = io.open(DIR .. "/ui_fly_map.log", "a") if fh then fh:write(line, "\n") fh:close() end end local function result(ok, label) say((ok and "PASS " or "FAIL ") .. label) if not ok then failures = failures + 1 end return ok end local function finish() if failures == 0 then say("PASS ui_fly_map") love.event.quit(0) else say("FAIL ui_fly_map failures=" .. failures) love.event.quit(1) end end local function run(game) for _ = 1, 900 do if game.phase == "boot" and game.boot then break end U.wait(1) end game:_handleBootAction({ action = "new_game", name = "RED" }) U.wait(240) local Runtime = require("src.core.game3.runtime") local Map = require("src.core.game3.map") local Space = require("src.core.game3.scripting.space") local Flags = require("src.core.game3.scripting.flags") local Player = require("src.core.game3.player") local Party = require("src.core.game3.party") local PartyMenu = require("src.ui.game3.party_menu") local StartMenu = require("src.ui.game3.start_menu") local RegionMap = require("src.ui.game3.region_map") local session = Runtime.getSession() if not result(session ~= nil, "new game reached the game3 field") then return finish() end local function ctx() return Space.vm and Space.vm.ctx end Flags.setFlag(Space.store, ctx(), FLAG_BADGE03_GET, true) session.party = {} require("src.core.game3.scripting.flags").setFlag(require("src.core.game3.scripting.space").store, nil, 0x828, true) -- data/maps/PalletTown_ProfessorOaksLab/scripts.inc:1120 Party.giveMon(session, 6, 40) session.party[1].moves = { "FLY", "EMBER", "SCRATCH", "GROWL" } session.party[1].pp = { 15, 25, 35, 40 } local function goTo(mapId, x, y, facing) Map.load(nil, game, mapId, { x = x, y = y, facing = facing or "down" }) if game.session then game.session.x, game.session.y, game.session.facing = x, y, facing or "down" end Player.cellX, Player.cellY = x, y Player.px, Player.py = x * 16, y * 16 Player.targetX, Player.targetY = x, y Player.facing = facing or "down" U.wait(120) end -- pokefirered/data/maps/ViridianCity/scripts.inc:6 goTo(VIRIDIAN, 21, 27, "down") local viridianVisited = RegionMap.isFlagSet("FLAG_WORLD_MAP_VIRIDIAN_CITY") goTo(PALLET, 10, 6, "down") local palletVisited = RegionMap.isFlagSet("FLAG_WORLD_MAP_PALLET_TOWN") if not (viridianVisited and palletVisited) then say("NOTE the map scripts did not run setworldmapflag on arrival; setting the two" .. " flags the scripts set (viridian=" .. tostring(viridianVisited) .. " pallet=" .. tostring(palletVisited) .. ")") Flags.setFlag(Space.store, ctx(), "FLAG_WORLD_MAP_VIRIDIAN_CITY", true) Flags.setFlag(Space.store, ctx(), "FLAG_WORLD_MAP_PALLET_TOWN", true) end result(RegionMap.isFlagSet("FLAG_WORLD_MAP_PALLET_TOWN") and RegionMap.isFlagSet("FLAG_WORLD_MAP_VIRIDIAN_CITY"), "Pallet Town and Viridian City are marked visited") result(RegionMap.isFlagSet("FLAG_WORLD_MAP_PEWTER_CITY") == false, "Pewter City is not marked visited") local function openParty() for _ = 1, 40 do if PartyMenu.isOpen and PartyMenu.isOpen() then return true end if StartMenu.isOpen and StartMenu.isOpen() then break end U.tap(game, "start") U.wait(8) end for _ = 1, 20 do local e = StartMenu.ENTRIES and StartMenu.ENTRIES[StartMenu.cursor] if e and e.id == "pokemon" then break end U.tap(game, "down") U.wait(6) end U.tap(game, "a") for _ = 1, 60 do if PartyMenu.isOpen and PartyMenu.isOpen() then return true end U.wait(4) end return false end local function chooseAction(label) U.tap(game, "a") U.wait(12) local target = nil for i, name in ipairs(PartyMenu.ACTIONS or {}) do if name == label then target = i end end if not target then return false end for _ = 1, 12 do if PartyMenu.actionCursor == target then break end U.tap(game, "down") U.wait(6) end return PartyMenu.actionCursor == target end local picked, pickedId, pickCalls = nil, nil, 0 -- pokefirered/src/party_menu.c:4099 SetUpFieldMove_Fly if not result(openParty(), "party menu opens in Pallet Town") then return finish() end result(chooseAction("FLY"), "FLY is on the action list") U.tap(game, "a") U.wait(60) result(PartyMenu._messageText == nil, "FLY outdoors is accepted (no refusal text)") -- pokefirered/src/party_menu.c:3953 if not result(RegionMap.isOpen() and RegionMap.isFlyMode(), "the field fly arm opened the fly map") then return finish() end local fieldPick = RegionMap._onPick RegionMap._onPick = function(sec, id, ...) picked, pickedId, pickCalls = sec, id, pickCalls + 1 if fieldPick then return fieldPick(sec, id, ...) end end -- pokefirered/src/region_map.c:3558 CreateFlyIcons local targets = RegionMap.flyTargets() local names = {} for _, t in ipairs(targets) do names[t.sec] = true end result(#targets == 2, "exactly two cells carry a fly icon (" .. #targets .. ")") result(names["MAPSEC_PALLET_TOWN"] == true, "Pallet Town carries a fly icon") result(names["MAPSEC_VIRIDIAN_CITY"] == true, "Viridian City carries a fly icon") result(names["MAPSEC_PEWTER_CITY"] == nil, "Pewter City carries none") result(RegionMap.cursorX == 4 and RegionMap.cursorY == 11, "the cursor opens on the player in Pallet Town") result(RegionMap.canFlyToCursor() == true, "Pallet Town is selectable") U.wait(30) U.still(game, DIR .. "/fly_map_01_pallet_selectable.png") for _ = 1, 4 do U.tap(game, "up") U.wait(10) end result(RegionMap.cursorY == 7, "the cursor reached Route 2 (y=" .. RegionMap.cursorY .. ")") result(RegionMap.canFlyToCursor() == false, "a route is not selectable") U.wait(20) U.still(game, DIR .. "/fly_map_02_route_not_selectable.png") for _ = 1, 3 do U.tap(game, "up") U.wait(10) end result(RegionMap.cursorY == 4, "the cursor reached Pewter City (y=" .. RegionMap.cursorY .. ")") result(RegionMap.canFlyToCursor() == false, "unvisited Pewter City is not selectable") U.wait(20) U.still(game, DIR .. "/fly_map_03_pewter_not_selectable.png") U.tap(game, "a") U.wait(20) result(pickCalls == 0, "A on Pewter City picks nothing") result(RegionMap.isOpen(), "the fly map stays open") for _ = 1, 4 do U.tap(game, "down") U.wait(10) end result(RegionMap.cursorY == 8, "the cursor reached Viridian City (y=" .. RegionMap.cursorY .. ")") result(RegionMap.canFlyToCursor() == true, "visited Viridian City is selectable") U.wait(20) U.still(game, DIR .. "/fly_map_04_viridian_selectable.png") U.tap(game, "a") for _ = 1, 120 do if not RegionMap.isOpen() then break end U.wait(1) end -- pokefirered/src/region_map.c:3991 result(pickCalls == 1, "A on Viridian City picked once") result(picked == "MAPSEC_VIRIDIAN_CITY", "onPick received the symbolic mapsec, got " .. tostring(picked)) result(pickedId == 89, "and the numeric mapsec second, got " .. tostring(pickedId)) result(RegionMap.isOpen() == false, "the fly map closed on the pick") -- pokefirered/src/region_map.c:4022 U.wait(420) local Field = require("src.core.game3.field") result(Field.locked ~= true, "the fly warp finished and unlocked the field") result(session.map == VIRIDIAN, "the player landed in Viridian City (map=" .. tostring(session.map) .. ")") U.still(game, DIR .. "/fly_map_05_landed_viridian.png") -- pokefirered/src/region_map.c:4019 if not result(openParty(), "party menu opens in Viridian City") then return finish() end result(chooseAction("FLY"), "FLY is on the action list again") U.tap(game, "a") U.wait(60) if result(RegionMap.isOpen() and RegionMap.isFlyMode(), "the fly map reopened") then U.tap(game, "b") U.wait(40) result(RegionMap.isOpen() == false, "B closed the fly map") result(PartyMenu.isOpen and PartyMenu.isOpen(), "B came back to the party menu") U.still(game, DIR .. "/fly_map_06_cancel_party_menu.png") for _ = 1, 30 do if not (PartyMenu.isOpen and PartyMenu.isOpen()) then break end U.tap(game, "b") U.wait(6) end result(Field.locked ~= true, "and B out of the party menu leaves the field unlocked") end -- src/region_map.c:1028-1049, :3903 goTo("SEVII_ONE_ISLAND", 12, 12, "down") Flags.setFlag(Space.store, ctx(), "FLAG_WORLD_MAP_ONE_ISLAND", true) if not result(openParty(), "party menu opens on ONE ISLAND") then return finish() end result(chooseAction("FLY"), "FLY is on the action list on ONE ISLAND") U.tap(game, "a") for _ = 1, 200 do if RegionMap.inputReady() then break end U.wait(1) end if result(RegionMap.isOpen() and RegionMap.isFlyMode(), "the fly map opened on ONE ISLAND") then result(RegionMap.state().selectedRegion == 1, "the fly map shows SEVII 1-2-3") result(RegionMap.currentLocationName() == "ONE ISLAND", "the cursor starts on ONE ISLAND, got " .. tostring(RegionMap.currentLocationName())) local seviiTargets = RegionMap.flyTargets() local seen = {} for _, t in ipairs(seviiTargets) do seen[t.sec] = true end result(#seviiTargets == 1 and seen["MAPSEC_ONE_ISLAND"] == true, "only ONE ISLAND carries a fly icon (" .. #seviiTargets .. ")") result(seen["MAPSEC_PALLET_TOWN"] == nil and seen["MAPSEC_VIRIDIAN_CITY"] == nil, "no Kanto town is offered from the Sevii Islands") result(RegionMap.canFlyToCursor() == true, "ONE ISLAND is selectable") U.still(game, DIR .. "/fly_map_07_one_island_sevii123.png") U.tap(game, "b") for _ = 1, 120 do if not RegionMap.isOpen() then break end U.wait(1) end result(RegionMap.isOpen() == false, "B closed the Sevii fly map") for _ = 1, 30 do if not (PartyMenu.isOpen and PartyMenu.isOpen()) then break end U.tap(game, "b") U.wait(6) end end finish() end return run