mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-25 15:01:11 +02:00
1905261c5b
CLOSES #1483, CLOSES #1610, CLOSES #1615, CLOSES #1646, CLOSES #1649, CLOSES #1651, CLOSES #1653, CLOSES #1656, CLOSES #1683, CLOSES #1685, CLOSES #1686, CLOSES #1687, CLOSES #1688, CLOSES #1689, CLOSES #1690, CLOSES #1693, CLOSES #1694, CLOSES #1695, CLOSES #1696, CLOSES #1702, CLOSES #1704, CLOSES #1705, CLOSES #1706, CLOSES #1707, CLOSES #1708, CLOSES #1710, CLOSES #1711, CLOSES #1712, CLOSES #1713, CLOSES #1716, CLOSES #1717, CLOSES #1718, CLOSES #1719, CLOSES #1720, CLOSES #1721, CLOSES #1725, CLOSES #1732, CLOSES #1745, CLOSES #1748, CLOSES #1749, CLOSES #1751, CLOSES #1754
235 lines
8.6 KiB
Lua
235 lines
8.6 KiB
Lua
-- #1712: the FLY map's cursor is the flying mon's party icon, not the MAP
|
|
-- card's arrow. FlyMap's .MapHud calls TownMapMon, which reads wCurPartyMon
|
|
-- and spawns SPRITE_ANIM_OBJ_PARTY_MON on that species' icon
|
|
-- (../pokecrystal/engine/pokegear/pokegear.asm:2326, :2708-2721). Reached the
|
|
-- way a player reaches it: START > POKeMON > FLY, standing in New Bark Town.
|
|
--
|
|
-- POKEPORT_IDENTITY=gold-dev POKEPORT_GAME=gold POKEPORT_TOUCH=0 \
|
|
-- POKEPORT_DRIVER=tests/drivers/gold_flymap_cursor_bug1712_test.lua \
|
|
-- POKEPORT_SHOT_DIR=/tmp/gold-flymap \
|
|
-- perl -e 'alarm 240; exec @ARGV' \
|
|
-- python3 -c "import pty; pty.spawn(['love','.'])"
|
|
--
|
|
-- Do not add POKEPORT_SPEED: the icon's two-frame beat is half of what is
|
|
-- being judged here, and the logic clock is the only thing speed scales.
|
|
local U = require("tests.drivers.util")
|
|
|
|
local FieldMoves = require("src.world.gen2.FieldMoves")
|
|
local Mon = require("src.battle.gen2.Mon")
|
|
|
|
local FLY_SPECIES = "PIDGEOTTO"
|
|
|
|
return function(game)
|
|
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-flymap"
|
|
local fails = 0
|
|
|
|
local function ok(cond, msg)
|
|
if cond then
|
|
print("[flymap] ok " .. msg)
|
|
else
|
|
fails = fails + 1
|
|
print("[flymap] FAIL " .. msg)
|
|
end
|
|
return cond
|
|
end
|
|
|
|
local function tap(btn) U.tap(game, btn) U.wait(3) end
|
|
local function top() return game.stack:top() end
|
|
|
|
U.wait(45)
|
|
local world = game.world
|
|
local save, data = game.save, game.data
|
|
if not ok(world and world.map, "gold booted into the overworld") then
|
|
error("gold flymap: no world")
|
|
end
|
|
|
|
-- FlyFunction .TryFly is CheckBadge STORMBADGE then CheckOutdoorMap
|
|
-- (../pokegold/engine/events/overworld.asm:544-551).
|
|
save.player = save.player or {}
|
|
if type(save.player.badges) ~= "table" then save.player.badges = {} end
|
|
save.player.badges.STORM = true
|
|
save.engineFlags = save.engineFlags or {}
|
|
for _, row in ipairs(FieldMoves.FLYPOINTS) do
|
|
save.engineFlags[row.flag] = true
|
|
end
|
|
|
|
-- Mon.learnMove refuses a fifth move (src/battle/gen2/Mon.lua:650), and a
|
|
-- level-24 PIDGEOTTO already knows four.
|
|
local flyer = Mon.new(data, FLY_SPECIES, 24)
|
|
table.remove(flyer.moves, 1)
|
|
Mon.learnMove(flyer, "FLY", data)
|
|
save.party = { flyer }
|
|
local knowsFly = false
|
|
for _, entry in ipairs(flyer.moves or {}) do
|
|
if entry.id == "FLY" then knowsFly = true end
|
|
end
|
|
ok(knowsFly, FLY_SPECIES .. " knows FLY")
|
|
|
|
-- The asset side, which fails exactly as silently as the bug: no icon row
|
|
-- for the species and drawFlyMonCursor gives the arrow back.
|
|
local icons = data and data.gen2Icons
|
|
local iconId = icons and icons.species and icons.species[FLY_SPECIES]
|
|
local entry = iconId and icons.icons and icons.icons[iconId]
|
|
ok(iconId ~= nil, "the cache maps " .. FLY_SPECIES .. " to an icon ("
|
|
.. tostring(iconId) .. ")")
|
|
ok(entry and entry.image ~= nil, "and that icon has a sheet ("
|
|
.. tostring(entry and entry.image) .. ")")
|
|
if entry and entry.image and love.filesystem.getInfo then
|
|
ok(love.filesystem.getInfo(entry.image) ~= nil,
|
|
"and the sheet is on disk")
|
|
end
|
|
|
|
-- New Bark Town is a TOWN, so TryFly's CheckOutdoorMap passes, and it has
|
|
-- two wandering objects to watch -- the teacher at (6,8) and the fisher at
|
|
-- (12,9), ../pokecrystal/maps/NewBarkTown.asm:301-304. Scene 1 is
|
|
-- SCENE_NEWBARKTOWN_NOOP, so the teacher's coord event stays out of the way.
|
|
world.mapScenes = world.mapScenes or {}
|
|
world.mapScenes.NEW_BARK_TOWN = 1
|
|
world:setMap("NEW_BARK_TOWN", 9, 8, "down")
|
|
U.wait(15)
|
|
local p = world.player
|
|
local function free(cx, cy)
|
|
return world.map:isWalkable(cx, cy) and not world:npcAt(cx, cy)
|
|
end
|
|
local DIRS = { down = { 0, 1 }, up = { 0, -1 }, left = { -1, 0 },
|
|
right = { 1, 0 } }
|
|
if not free(p.cellX, p.cellY) then
|
|
for _, d in pairs(DIRS) do
|
|
if free(9 + d[1], 8 + d[2]) then
|
|
world:setMap("NEW_BARK_TOWN", 9 + d[1], 8 + d[2], "down")
|
|
U.wait(10)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
ok(world.map.id == "NEW_BARK_TOWN" and free(p.cellX, p.cellY),
|
|
("standing free on New Bark Town (%d,%d)"):format(p.cellX, p.cellY))
|
|
ok((world.map.def and world.map.def.environment) == "TOWN",
|
|
"which is a TOWN, so FLY is allowed here")
|
|
|
|
-- One real step through the pad, on a free neighbour.
|
|
local fromX, fromY = p.cellX, p.cellY
|
|
for name, d in pairs(DIRS) do
|
|
if free(fromX + d[1], fromY + d[2]) then
|
|
U.hold(game, name, 24)
|
|
break
|
|
end
|
|
end
|
|
U.wait(8)
|
|
ok(p.cellX ~= fromX or p.cellY ~= fromY,
|
|
("the pad walked the player from (%d,%d) to (%d,%d)")
|
|
:format(fromX, fromY, p.cellX, p.cellY))
|
|
|
|
-- START > POKeMON. CheckMenuOW only runs while nothing else owns the frame
|
|
-- (../pokegold/engine/overworld/events.asm), so wait the map's own settling
|
|
-- out rather than pressing into it.
|
|
for _ = 1, 240 do
|
|
if not world:busy() then break end
|
|
U.wait(2)
|
|
end
|
|
local menu
|
|
for _ = 1, 10 do
|
|
tap("start")
|
|
menu = top()
|
|
if menu and menu.screenId == "Gen2StartMenu" then break end
|
|
U.wait(6)
|
|
end
|
|
if not ok(menu and menu.screenId == "Gen2StartMenu",
|
|
"START opened the menu (top is "
|
|
.. tostring(menu and (menu.screenId or "?")) .. ")") then
|
|
error("gold flymap: no start menu")
|
|
end
|
|
for _ = 1, 10 do
|
|
if menu.list:current().value == "pokemon" then break end
|
|
tap("down")
|
|
end
|
|
ok(menu.list:current().value == "pokemon", "the cursor found POKeMON")
|
|
tap("a")
|
|
|
|
local party = top()
|
|
if not ok(party and party.screenId == "Gen2PartyMenu",
|
|
"POKeMON opened the party list") then
|
|
error("gold flymap: no party list")
|
|
end
|
|
|
|
-- A on the flyer, then down the submenu to its FLY row.
|
|
tap("a")
|
|
local sub = party.submenu
|
|
if not ok(sub ~= nil, "and A opened the action submenu") then
|
|
error("gold flymap: no submenu")
|
|
end
|
|
local flyRow
|
|
for index, item in ipairs(sub.items) do
|
|
if item.id == "FLY" then flyRow = index end
|
|
end
|
|
if not ok(flyRow ~= nil, "which lists FLY") then
|
|
error("gold flymap: the submenu has no FLY row")
|
|
end
|
|
for _ = 1, #sub.items do
|
|
if sub.index == flyRow then break end
|
|
tap("down")
|
|
end
|
|
ok(sub.index == flyRow, "the cursor found FLY")
|
|
tap("a")
|
|
U.wait(10)
|
|
|
|
local picker = top()
|
|
if not ok(picker and picker.screenId == "Gen2Pokegear",
|
|
"FLY opened the town map") then
|
|
error("gold flymap: FLY did not open the picker")
|
|
end
|
|
ok(picker.fly ~= nil and #picker.fly > 0,
|
|
"in _FlyMap dress, with " .. tostring(picker.fly and #picker.fly)
|
|
.. " flypoints to walk")
|
|
-- wCurPartyMon, as TownMapMon reads it (World:openFlyMap's flyMon).
|
|
ok(picker.flyMon == flyer, "and it was handed the mon that used the move")
|
|
ok(picker.icons ~= nil, "and it can see the icon table")
|
|
|
|
U.wait(12)
|
|
ok(picker.flyMonIcon ~= nil and picker.flyMonIcon ~= false,
|
|
"the mon icon loaded for the cursor")
|
|
-- No objColors, no OBP bake, and the bake is what keys colour 0 transparent
|
|
-- (src/render/SpriteRenderer.lua:243): the icon would sit in a white box.
|
|
ok(picker.flyMonIcon and picker.flyMonIcon.objColors ~= nil,
|
|
"with an OBJ palette baked onto it")
|
|
|
|
-- Landmark names carry TownMap_ConvertLineBreakCharacters' own <LF>, which
|
|
-- would break a log line in half.
|
|
local function flat(entry)
|
|
return (tostring(entry and entry.name):gsub("%s+", " "))
|
|
end
|
|
|
|
local before = picker:mapLandmark()
|
|
local player = picker:playerLandmark()
|
|
ok(before and before.x and before.y, "the cursor is on " .. flat(before))
|
|
U.shot(game, out .. "/01-flymap-newbark.png")
|
|
|
|
tap("up")
|
|
U.wait(8)
|
|
local after = picker:mapLandmark()
|
|
ok(after and (after.x ~= before.x or after.y ~= before.y),
|
|
("up walked the cursor to %s at (%s,%s)"):format(flat(after),
|
|
tostring(after and after.x), tostring(after and after.y)))
|
|
ok(picker:playerLandmark() == player,
|
|
"and the player's own icon stayed where it was")
|
|
U.shot(game, out .. "/02-flymap-cherrygrove.png")
|
|
|
|
U.log("flew the party list into the FLY map for you (#1712). the cursor over")
|
|
U.log("the highlighted town should be " .. FLY_SPECIES .. "'s 16x16 party")
|
|
U.log("icon, red like the little Chris icon parked on New Bark, swapping")
|
|
U.log("between its two frames about twice a second, and it moves with")
|
|
U.log("up/down while Chris stays put. shots are in " .. out .. ".")
|
|
U.log("the arrow cursor instead of a mon is the bug. a mon in a solid white")
|
|
U.log("box is the near miss -- the icon is right, the palette bake is not.")
|
|
if fails > 0 then
|
|
U.log(("%d assertion(s) failed above; the picture below is not worth")
|
|
:format(fails))
|
|
U.log("reading until those are green.")
|
|
end
|
|
U.log("the picker is open and the controls are yours.")
|
|
|
|
while true do
|
|
coroutine.yield()
|
|
end
|
|
end
|