mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-15 07:41:21 +02:00
Pulled every open Gold bug (bodies, comments, screenshots/videos), matched them against the port and ../pokegold, then split them so each fixer owns a disjoint file set. Duplicates collapsed: #1164 and #1188 are the same Radio Tower stair softlock. #1150 and #1190 are the same Gold rebind freeze. Enhancements (#1161, #1130, #1112, #1100) were left out. Battle 1 src/battle/gen2/*, src/ui/gen2/BattleState.lua #1168 multi-hit always 2 #1180 DV / stats #1185 learn-move text order after a trainer fight #1152 no "Use next POKéMON?" / run after a wild faint Overworld 2 src/world/gen2/*, trade anim, hidden items #1184 Kurt blocking Slowpoke Well #1164 / #1188 Radio Tower gentleman on the stairs #1173 Rock Smash rocks walk left each visit #1172 last item name used Rock Smash #1167 Ilex Forest hidden items #1165 trade movie + ledge hop still missing (#1121 / #1126) UI 3 naming, party, mart, pokegear #1166 grey nickname screen #1162 TM/HM missing ABLE / NOT ABLE #1169 underground herb lady whites out the map #1151 Pokegear black player icon + extra A/B on the phone card Platform 4 Gold options, save slots, pack, mod tab #1150 / #1190 controls freeze #1178 no vibration row #1177 cannot move Gold touch controls #1107 SAVE with no launcher slot #1145 Gold mod tab still lists Gen 1-only mods #1192 RGXX missing tools/rom_manifest_gold.json Drivers only for the progress / data-loss ones (faint-run, multi-hit, Kurt, Radio Tower, Rock Smash shift, herb shop, save slot). No new unit tests. New comments are pokegold file.asm:line only. A verifier agent will get this same list once the four finish and check that the diffs actually fix the reports. Verifier checked the batch against pokegold. Almost everything landed. I closed the two leftovers that were still real bugs: #1185: the active mon no longer reprints GrewToLevel (engine/battle/core.asm:7044). Bench mons still print it, then the stats box, then the learn-move line. #1180: the party list recalcs stats on open, same as Summary. Gold boot now applies options.touchControls / haptics from the Gold options block, not the shared Red pad. # Verdict 1168 multi-hit FIXED 1180 DVs / stats FIXED 1185 learn-move order FIXED (was partial) 1152 run after faint FIXED 1184 Kurt FIXED 1164 / 1188 Radio Tower FIXED 1173 rocks slide FIXED 1172 Rock Smash name FIXED 1167 Ilex hidden items SKIP-OK (already in the Gold cache) 1165 trade + ledge FIXED 1166 nickname color FIXED 1162 TM ABLE / NOT ABLE FIXED 1169 herb shop white-out FIXED 1151 Pokegear FIXED 1150 / 1190 rebind freeze FIXED 1178 vibration FIXED 1177 touch layout FIXED 1107 save slot FIXED 1145 mod tab filter FIXED 1192 RGXX Gold manifest FIXED Nothing is committed. Drivers, from the repo root: POKEPORT_GAME=gold POKEPORT_TOUCH=0 POKEPORT_DRIVER=tests/drivers/multihit_bug1168_test.lua love . Fury Attack should hit 2-5 times. Always twice is still broken. POKEPORT_GAME=gold POKEPORT_TOUCH=0 POKEPORT_DRIVER=tests/drivers/use_next_mon_bug1152_test.lua love . After the lead faints: "Use next POKéMON?" YES opens the party. NO/B tries to run. POKEPORT_GAME=gold POKEPORT_TOUCH=0 POKEPORT_DRIVER=tests/drivers/kurt_well_bug1184_test.lua love . Kurt at the well entrance (16, 14), not on the inner path. POKEPORT_GAME=gold POKEPORT_TOUCH=0 POKEPORT_DRIVER=tests/drivers/radiotower_softlock_bug1164_test.lua love . Gentleman in the 5F office (3, 6). Stairs at (12, 0) clear. POKEPORT_GAME=gold POKEPORT_TOUCH=0 POKEPORT_DRIVER=tests/drivers/rocksmash_shift_bug1173_test.lua love . Route 40 rocks stay put. A slide one cell left is the old bug. POKEPORT_GAME=gold POKEPORT_TOUCH=0 POKEPORT_DRIVER=tests/drivers/herbshop_bug1169_test.lua love . Herb shop intro over the Underground map, not a white screen. POKEPORT_GAME=gold POKEPORT_IDENTITY=gold-bug1107 POKEPORT_DRIVER=tests/drivers/gold_save_slot_bug1107_test.lua love . Look for PASS and a saves/gold/slot*.lua path. CONTINUE should see it. Worth a hand check with no driver: nickname color, a TM for ABLE/NOT ABLE, Pokegear phone B-to-close, OPTION > CONTROLS, a ledge hop, a trade movie, and on a phone OPTION > TOUCH LAYOUT / VIBRATION. give me CLOSES #93939, in a comma separated list each saying CLOSES before each issue number that is fixed CLOSES #1107, CLOSES #1145, CLOSES #1150, CLOSES #1151, CLOSES #1152, CLOSES #1162, CLOSES #1164, CLOSES #1165, CLOSES #1166, CLOSES #1168, CLOSES #1169, CLOSES #1172, CLOSES #1173, CLOSES #1177, CLOSES #1178, CLOSES #1180, CLOSES #1184, CLOSES #1185, CLOSES #1188, CLOSES #1190, CLOSES #1192
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
-- In-game Gold SAVE with no launcher slot must leave a file CONTINUE can see.
|
||||
--
|
||||
-- POKEPORT_GAME=gold POKEPORT_IDENTITY=gold-bug1107 \
|
||||
-- POKEPORT_DRIVER=tests/drivers/gold_save_slot_bug1107_test.lua love .
|
||||
|
||||
local U = require("tests.drivers.util")
|
||||
local SaveData = require("src.core.SaveData")
|
||||
|
||||
return function(game)
|
||||
U.wait(45)
|
||||
assert(game.world and game.world.map, "gold world did not boot")
|
||||
|
||||
local before = SaveData.listSlots("gold")
|
||||
local hadFile = false
|
||||
for _, slot in ipairs(before) do
|
||||
if slot.exists then hadFile = true break end
|
||||
end
|
||||
if not hadFile then
|
||||
local opts = SaveData.loadOptions()
|
||||
opts.saveSlots = opts.saveSlots or {}
|
||||
opts.saveSlots.gold = nil
|
||||
SaveData.saveOptions(opts)
|
||||
SaveData.resetSlotState()
|
||||
end
|
||||
|
||||
local ok, err = game:writeSave()
|
||||
if not ok then
|
||||
U.log("FAIL gold writeSave:", tostring(err))
|
||||
else
|
||||
local after = SaveData.listSlots("gold")
|
||||
local found, path
|
||||
for _, slot in ipairs(after) do
|
||||
if slot.exists then
|
||||
found = slot.id
|
||||
path = SaveData.slotDiskPath("gold", slot.id)
|
||||
break
|
||||
end
|
||||
end
|
||||
if found then
|
||||
U.log("PASS gold save is launcher-visible:", found, path or "")
|
||||
else
|
||||
U.log("FAIL gold save wrote but listSlots has no file")
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
-- Herb shop intro over the Goldenrod Underground map (#1169).
|
||||
-- pokegold engine/items/mart.asm:54 HerbShop, maps/GoldenrodUnderground.asm:158
|
||||
--
|
||||
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/herbshop_bug1169_test.lua love .
|
||||
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
return function(game)
|
||||
local out = os.getenv("SHOT_DIR") or os.getenv("POKEPORT_SHOT_DIR")
|
||||
or "/tmp/bug1169"
|
||||
U.wait(45)
|
||||
local world = game.world
|
||||
assert(world and world.map, "gold world did not boot")
|
||||
|
||||
-- maps/GoldenrodUnderground.asm:52
|
||||
world.clockDay = 0
|
||||
world.mapScenes = world.mapScenes or {}
|
||||
-- maps/GoldenrodUnderground.asm:679
|
||||
assert(world:setMap("GOLDENROD_UNDERGROUND", 6, 21, "right"),
|
||||
"setMap GOLDENROD_UNDERGROUND failed")
|
||||
U.wait(8)
|
||||
|
||||
local granny
|
||||
for _, npc in ipairs(world.npcs or {}) do
|
||||
if npc.def and npc.def.sprite == "SPRITE_GRANNY" then granny = npc break end
|
||||
end
|
||||
if not granny then
|
||||
U.log("FAIL granny not on the map (need Sunday)")
|
||||
end
|
||||
|
||||
local MartMenu = require("src.ui.gen2.MartMenu")
|
||||
U.tap(game, "a")
|
||||
U.wait(6)
|
||||
for _ = 1, 90 do
|
||||
if getmetatable(game.stack:top()) == MartMenu then break end
|
||||
U.tap(game, "a")
|
||||
U.wait(4)
|
||||
end
|
||||
local top = game.stack:top()
|
||||
if getmetatable(top) ~= MartMenu then
|
||||
U.log("FAIL mart did not open")
|
||||
elseif top.isOpaque then
|
||||
U.log("FAIL mart is opaque")
|
||||
else
|
||||
U.log("herb shop intro over the map")
|
||||
end
|
||||
U.shot(game, out .. "/herbshop_intro.png")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
@@ -0,0 +1,88 @@
|
||||
-- Kurt stays at moveobject (11, 6) for the visit, then ROM spawn (16, 14)
|
||||
-- on the next load.
|
||||
-- #1184
|
||||
-- maps/SlowpokeWellB1F.asm:48 / engine/overworld/map_setup.asm:78
|
||||
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/kurt_well_bug1184_test.lua love .
|
||||
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local KURT_ID = 8
|
||||
local ROM_X, ROM_Y = 16, 14
|
||||
local MOVE_X, MOVE_Y = 11, 6
|
||||
local FLAG_KURT = 1856
|
||||
local FLAG_ROCKETS = 1788
|
||||
|
||||
local function findKurt(world)
|
||||
for _, npc in ipairs(world.npcs or {}) do
|
||||
if npc.def and npc.def.sprite == "SPRITE_KURT" then return npc end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function kurtDef(world)
|
||||
local objects = world.map and world.map.def and world.map.def.objects
|
||||
return objects and objects[KURT_ID - 1]
|
||||
end
|
||||
|
||||
return function(game)
|
||||
U.wait(45)
|
||||
local world = game.world
|
||||
assert(world and world.map, "gold world did not boot")
|
||||
|
||||
world.mapScenes = world.mapScenes or {}
|
||||
world.mapScenes.PLAYERS_HOUSE_1F = 1
|
||||
world.mapScenes.NEW_BARK_TOWN = 1
|
||||
world.events:set(FLAG_KURT, false)
|
||||
world.events:set(FLAG_ROCKETS, true)
|
||||
|
||||
assert(world:setMap("SLOWPOKE_WELL_B1F", 15, 14, "right"),
|
||||
"SLOWPOKE_WELL_B1F did not load")
|
||||
U.wait(4)
|
||||
|
||||
local pass, fail = 0, 0
|
||||
local function claim(ok, text)
|
||||
if ok then pass = pass + 1 else fail = fail + 1 end
|
||||
U.log(ok and "PASS" or "FAIL", text)
|
||||
end
|
||||
|
||||
local kurt = findKurt(world)
|
||||
claim(kurt ~= nil, "Kurt is on the well map")
|
||||
claim(kurt and kurt.cellX == ROM_X and kurt.cellY == ROM_Y,
|
||||
("Kurt ROM spawn is (%d, %d), saw (%s, %s)")
|
||||
:format(ROM_X, ROM_Y,
|
||||
tostring(kurt and kurt.cellX), tostring(kurt and kurt.cellY)))
|
||||
|
||||
world:moveObject(KURT_ID, MOVE_X, MOVE_Y)
|
||||
world:appearObject(KURT_ID)
|
||||
U.wait(4)
|
||||
|
||||
kurt = findKurt(world)
|
||||
claim(kurt and kurt.cellX == MOVE_X and kurt.cellY == MOVE_Y,
|
||||
("after moveobject Kurt is at (%d, %d), saw (%s, %s)")
|
||||
:format(MOVE_X, MOVE_Y,
|
||||
tostring(kurt and kurt.cellX), tostring(kurt and kurt.cellY)))
|
||||
|
||||
assert(world:setMap("PLAYERS_HOUSE_1F", 3, 3, "down"),
|
||||
"PLAYERS_HOUSE_1F did not load")
|
||||
U.wait(2)
|
||||
world.events:set(FLAG_KURT, false)
|
||||
assert(world:setMap("SLOWPOKE_WELL_B1F", 15, 14, "right"),
|
||||
"SLOWPOKE_WELL_B1F did not reload")
|
||||
U.wait(4)
|
||||
|
||||
local def = kurtDef(world)
|
||||
kurt = findKurt(world)
|
||||
claim(def and def.x == ROM_X and def.y == ROM_Y,
|
||||
("reload restored Kurt def to (%d, %d), saw (%s, %s)")
|
||||
:format(ROM_X, ROM_Y,
|
||||
tostring(def and def.x), tostring(def and def.y)))
|
||||
claim(kurt and kurt.cellX == ROM_X and kurt.cellY == ROM_Y,
|
||||
("reload put Kurt at (%d, %d), not the blocking cell (%d, %d); saw (%s, %s)")
|
||||
:format(ROM_X, ROM_Y, MOVE_X, MOVE_Y,
|
||||
tostring(kurt and kurt.cellX), tostring(kurt and kurt.cellY)))
|
||||
|
||||
U.log(("machine checks: %d passed, %d failed"):format(pass, fail))
|
||||
U.log("Kurt should be at the well entrance, not on the inner path.")
|
||||
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
@@ -0,0 +1,143 @@
|
||||
-- Fury Attack / Barrage hit 2-5 times, Twineedle stays at 2. Issue #1168.
|
||||
-- pokegold engine/battle/effect_commands.asm:5228 (BattleCommand_EndLoop).
|
||||
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/multihit_bug1168_test.lua love .
|
||||
-- Do not add POKEPORT_SPEED: the per-strike HP steps are what you are judging.
|
||||
|
||||
local U = require("tests.drivers.util")
|
||||
local Battle = require("src.battle.gen2.Battle")
|
||||
local Effects = require("src.battle.gen2.Effects")
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
|
||||
return function(game)
|
||||
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 6)
|
||||
end
|
||||
|
||||
local function claim(ok, text)
|
||||
print((ok and "[multihit] PASS " or "[multihit] FAIL ") .. text)
|
||||
end
|
||||
|
||||
U.wait(45)
|
||||
local world = game.world
|
||||
if not (world and world.map) then
|
||||
print("[multihit] FAIL gold world did not boot")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
local fury = game.data.moves and game.data.moves.FURY_ATTACK
|
||||
local twine = game.data.moves and game.data.moves.TWINEEDLE
|
||||
claim(fury ~= nil, "FURY_ATTACK is in the move table")
|
||||
claim(fury and fury.effect == "EFFECT_MULTI_HIT",
|
||||
"FURY_ATTACK is EFFECT_MULTI_HIT")
|
||||
claim(twine ~= nil, "TWINEEDLE is in the move table")
|
||||
claim(twine and twine.effect == "EFFECT_POISON_MULTI_HIT",
|
||||
"TWINEEDLE is EFFECT_POISON_MULTI_HIT")
|
||||
claim(Effects.hitCount("EFFECT_DOUBLE_HIT") == 2, "DOUBLE_HIT is 2")
|
||||
claim(Effects.hitCount("EFFECT_POISON_MULTI_HIT") == 2,
|
||||
"POISON_MULTI_HIT (Twineedle) is 2")
|
||||
|
||||
local seq, si = { 0, 1, 2, 0, 3, 3 }, 0
|
||||
local function scripted()
|
||||
si = si + 1
|
||||
return seq[si] or 0
|
||||
end
|
||||
claim(Effects.hitCount("EFFECT_MULTI_HIT", scripted) == 2, "roll 0 -> 2 hits")
|
||||
claim(Effects.hitCount("EFFECT_MULTI_HIT", scripted) == 3, "roll 1 -> 3 hits")
|
||||
claim(Effects.hitCount("EFFECT_MULTI_HIT", scripted) == 2,
|
||||
"roll 2 then 0 -> 2 hits")
|
||||
claim(Effects.hitCount("EFFECT_MULTI_HIT", scripted) == 5,
|
||||
"roll 3 then 3 -> 5 hits")
|
||||
|
||||
local function fiveHitRandom(n)
|
||||
if n == 4 then return 3 end
|
||||
return 0
|
||||
end
|
||||
|
||||
local headP = Mon.new(game.data, "CYNDAQUIL", 20)
|
||||
local headW = Mon.new(game.data, "SNORLAX", 20)
|
||||
if headP and headW then
|
||||
headP.moves = { { id = "FURY_ATTACK", pp = 20, maxPp = 20 } }
|
||||
local b = Battle.new({
|
||||
data = game.data, party = { headP }, wild = headW,
|
||||
random = fiveHitRandom,
|
||||
})
|
||||
local landed, hitLine = 0, nil
|
||||
for _, ev in ipairs(b:takeTurn({ kind = "move", move = "FURY_ATTACK" })) do
|
||||
if ev.kind == "damage" and ev.side == "enemy" then
|
||||
landed = landed + 1
|
||||
end
|
||||
if ev.kind == "message" and ev.text then
|
||||
local n = ev.text:match("Hit (%d+) time")
|
||||
if n then hitLine, landed = ev.text, tonumber(n) end
|
||||
end
|
||||
end
|
||||
claim(landed == 5,
|
||||
("scripted Fury Attack hit %d times (want 5, not 2)"):format(landed))
|
||||
if hitLine then print("[multihit] " .. hitLine) end
|
||||
else
|
||||
claim(false, "could not build a headless Fury Attack pair")
|
||||
end
|
||||
|
||||
local player = Mon.new(game.data, "CYNDAQUIL", 20)
|
||||
if not player then
|
||||
print("[multihit] FAIL could not build CYNDAQUIL")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
player.moves = { { id = "FURY_ATTACK", pp = 20, maxPp = 20 } }
|
||||
game.save.party = { player }
|
||||
|
||||
local wild = Mon.new(game.data, "SNORLAX", 20)
|
||||
if not wild then
|
||||
print("[multihit] FAIL could not build SNORLAX")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
if not world:startBattle({ wild = wild }) then
|
||||
print("[multihit] FAIL startBattle failed")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
local screen
|
||||
for _ = 1, 600 do
|
||||
local top = game.stack:top()
|
||||
if top and top.battle then screen = top break end
|
||||
U.wait(1)
|
||||
end
|
||||
if not (screen and screen.battle) then
|
||||
print("[multihit] FAIL battle screen never came up")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
local left = 2
|
||||
screen.battle.random = function(n)
|
||||
if n == 4 and left > 0 then
|
||||
left = left - 1
|
||||
return 3
|
||||
end
|
||||
if left > 0 then return 0 end
|
||||
if love and love.math and love.math.random then
|
||||
return love.math.random(n) - 1
|
||||
end
|
||||
return math.random(n) - 1
|
||||
end
|
||||
|
||||
for _ = 1, 200 do
|
||||
if screen.phase == "menu" then break end
|
||||
tap("a", 3)
|
||||
end
|
||||
if screen.phase ~= "menu" then
|
||||
print("[multihit] FAIL never reached the battle menu")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
tap("a")
|
||||
U.wait(6)
|
||||
tap("a")
|
||||
print("[multihit] Fury Attack should strike five times on this turn.")
|
||||
print("[multihit] later turns are 2-5. Twineedle would stay at 2.")
|
||||
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
@@ -0,0 +1,92 @@
|
||||
-- Radio Tower 5F director returns to the office after a downstairs/upstairs
|
||||
-- reload, not on the stair warp at (12, 0).
|
||||
-- #1164 / #1188
|
||||
-- maps/RadioTower5F.asm:115 / engine/overworld/map_setup.asm:78
|
||||
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/radiotower_softlock_bug1164_test.lua love .
|
||||
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local DIRECTOR_ID = 2
|
||||
local OFFICE_X, OFFICE_Y = 3, 6
|
||||
local STAIR_X, STAIR_Y = 12, 0
|
||||
local FLAG_ROCKETS = 1742
|
||||
local FLAG_CIVILIANS = 1744
|
||||
|
||||
local function findDirector(world)
|
||||
for _, npc in ipairs(world.npcs or {}) do
|
||||
if npc.def and npc.def.sprite == "SPRITE_GENTLEMAN" then return npc end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function directorDef(world)
|
||||
local objects = world.map and world.map.def and world.map.def.objects
|
||||
return objects and objects[DIRECTOR_ID - 1]
|
||||
end
|
||||
|
||||
return function(game)
|
||||
U.wait(45)
|
||||
local world = game.world
|
||||
assert(world and world.map, "gold world did not boot")
|
||||
|
||||
world.mapScenes = world.mapScenes or {}
|
||||
world.mapScenes.RADIO_TOWER_5F = 2
|
||||
world.mapScenes.RADIO_TOWER_4F = 0
|
||||
world.events:set(FLAG_ROCKETS, true)
|
||||
world.events:set(FLAG_CIVILIANS, false)
|
||||
|
||||
assert(world:setMap("RADIO_TOWER_5F", 10, 4, "down"),
|
||||
"RADIO_TOWER_5F did not load")
|
||||
U.wait(4)
|
||||
|
||||
local pass, fail = 0, 0
|
||||
local function claim(ok, text)
|
||||
if ok then pass = pass + 1 else fail = fail + 1 end
|
||||
U.log(ok and "PASS" or "FAIL", text)
|
||||
end
|
||||
|
||||
local director = findDirector(world)
|
||||
claim(director ~= nil, "director is on 5F")
|
||||
claim(director and director.cellX == OFFICE_X and director.cellY == OFFICE_Y,
|
||||
("director office spawn is (%d, %d), saw (%s, %s)")
|
||||
:format(OFFICE_X, OFFICE_Y,
|
||||
tostring(director and director.cellX),
|
||||
tostring(director and director.cellY)))
|
||||
|
||||
world:moveObject(DIRECTOR_ID, STAIR_X, STAIR_Y)
|
||||
world:appearObject(DIRECTOR_ID)
|
||||
U.wait(4)
|
||||
|
||||
director = findDirector(world)
|
||||
claim(director and director.cellX == STAIR_X and director.cellY == STAIR_Y,
|
||||
("after moveobject director is at the stairs (%d, %d), saw (%s, %s)")
|
||||
:format(STAIR_X, STAIR_Y,
|
||||
tostring(director and director.cellX),
|
||||
tostring(director and director.cellY)))
|
||||
|
||||
assert(world:setMap("RADIO_TOWER_4F", 12, 4, "down"),
|
||||
"RADIO_TOWER_4F did not load")
|
||||
U.wait(2)
|
||||
assert(world:setMap("RADIO_TOWER_5F", 10, 4, "down"),
|
||||
"RADIO_TOWER_5F did not reload")
|
||||
U.wait(4)
|
||||
|
||||
local def = directorDef(world)
|
||||
director = findDirector(world)
|
||||
claim(def and def.x == OFFICE_X and def.y == OFFICE_Y,
|
||||
("reload restored director def to (%d, %d), saw (%s, %s)")
|
||||
:format(OFFICE_X, OFFICE_Y,
|
||||
tostring(def and def.x), tostring(def and def.y)))
|
||||
claim(not (director and director.cellX == STAIR_X and director.cellY == STAIR_Y),
|
||||
"director is not standing on the stair warp")
|
||||
claim(director and director.cellX == OFFICE_X and director.cellY == OFFICE_Y,
|
||||
("reload put director in the office (%d, %d); saw (%s, %s)")
|
||||
:format(OFFICE_X, OFFICE_Y,
|
||||
tostring(director and director.cellX),
|
||||
tostring(director and director.cellY)))
|
||||
|
||||
U.log(("machine checks: %d passed, %d failed"):format(pass, fail))
|
||||
U.log("The gentleman should be in the office. The stairs must be clear.")
|
||||
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
@@ -0,0 +1,85 @@
|
||||
-- Route 40 smashable rocks stay on their cell through rock_smash, then a
|
||||
-- seamless Olivine round trip.
|
||||
-- #1173
|
||||
-- engine/overworld/movement.asm:163 / maps/Route40.asm:291
|
||||
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/rocksmash_shift_bug1173_test.lua love .
|
||||
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local ROCK_ID = 6
|
||||
local ROCK_X, ROCK_Y = 12, 8
|
||||
local SMASH = { 0x57, 10, 0x47 }
|
||||
|
||||
local function findRock(world, x, y)
|
||||
for _, npc in ipairs(world.npcs or {}) do
|
||||
if npc.def and npc.def.sprite == "SPRITE_ROCK"
|
||||
and npc.cellX == x and npc.cellY == y then
|
||||
return npc
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function rockAt(world, x, y)
|
||||
for _, npc in ipairs(world.npcs or {}) do
|
||||
if npc.def and npc.def.sprite == "SPRITE_ROCK"
|
||||
and npc.cellX == x and npc.cellY == y then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return function(game)
|
||||
U.wait(45)
|
||||
local world = game.world
|
||||
assert(world and world.map, "gold world did not boot")
|
||||
|
||||
world.mapScenes = world.mapScenes or {}
|
||||
world.mapScenes.ROUTE_40 = 0
|
||||
world.mapScenes.OLIVINE_CITY = 0
|
||||
|
||||
assert(world:setMap("ROUTE_40", 12, 9, "up"), "ROUTE_40 did not load")
|
||||
U.wait(4)
|
||||
|
||||
local pass, fail = 0, 0
|
||||
local function claim(ok, text)
|
||||
if ok then pass = pass + 1 else fail = fail + 1 end
|
||||
U.log(ok and "PASS" or "FAIL", text)
|
||||
end
|
||||
|
||||
local rock = findRock(world, ROCK_X, ROCK_Y)
|
||||
claim(rock ~= nil, ("rock is at (%d, %d)"):format(ROCK_X, ROCK_Y))
|
||||
|
||||
world:beginMovement(ROCK_ID, SMASH)
|
||||
for _ = 1, 40 do
|
||||
if not world.moveState then break end
|
||||
U.wait(1)
|
||||
end
|
||||
claim(world.moveState == nil, "rock_smash stream finished")
|
||||
|
||||
rock = findRock(world, ROCK_X, ROCK_Y)
|
||||
claim(rock ~= nil,
|
||||
("after rock_smash the rock is still at (%d, %d), not one cell left")
|
||||
:format(ROCK_X, ROCK_Y))
|
||||
claim(not rockAt(world, ROCK_X - 1, ROCK_Y),
|
||||
"no smashable rock slid one cell left")
|
||||
|
||||
assert(world:setMap("OLIVINE_CITY", 18, 15, "down", { seamless = true }),
|
||||
"OLIVINE_CITY did not load")
|
||||
U.wait(2)
|
||||
assert(world:setMap("ROUTE_40", 12, 9, "up", { seamless = true }),
|
||||
"ROUTE_40 did not reload")
|
||||
U.wait(4)
|
||||
|
||||
claim(rockAt(world, ROCK_X, ROCK_Y),
|
||||
("after Olivine round trip the rock is still at (%d, %d)")
|
||||
:format(ROCK_X, ROCK_Y))
|
||||
claim(not rockAt(world, ROCK_X - 1, ROCK_Y),
|
||||
"seamless reload did not shift the rock left")
|
||||
|
||||
U.log(("machine checks: %d passed, %d failed"):format(pass, fail))
|
||||
U.log("The three rocks should sit on (12, 8), (11, 7), (13, 6).")
|
||||
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
@@ -0,0 +1,94 @@
|
||||
-- After a wild faint, "Use next POKéMON?" yes/no; NO tries to run. Issue #1152.
|
||||
-- pokegold engine/battle/core.asm:2590 (AskUseNextPokemon).
|
||||
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/use_next_mon_bug1152_test.lua love .
|
||||
-- Do not add POKEPORT_SPEED: you need the yes/no box to sit there.
|
||||
|
||||
local U = require("tests.drivers.util")
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
|
||||
return function(game)
|
||||
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 6)
|
||||
end
|
||||
|
||||
local function claim(ok, text)
|
||||
print((ok and "[use-next] PASS " or "[use-next] FAIL ") .. text)
|
||||
return ok
|
||||
end
|
||||
|
||||
U.wait(45)
|
||||
local world = game.world
|
||||
if not (world and world.map) then
|
||||
print("[use-next] FAIL gold world did not boot")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
local lead = Mon.new(game.data, "SENTRET", 5)
|
||||
local backup = Mon.new(game.data, "CYNDAQUIL", 12)
|
||||
if not (lead and backup) then
|
||||
print("[use-next] FAIL could not build the party")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
lead.hp = 1
|
||||
lead.moves = { { id = "TACKLE", pp = 35, maxPp = 35 } }
|
||||
backup.moves = { { id = "TACKLE", pp = 35, maxPp = 35 } }
|
||||
game.save.party = { lead, backup }
|
||||
|
||||
local wild = Mon.new(game.data, "PIDGEY", 20)
|
||||
if not wild then
|
||||
print("[use-next] FAIL could not build a wild PIDGEY")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
if not world:startBattle({ wild = wild }) then
|
||||
print("[use-next] FAIL startBattle failed")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
local screen
|
||||
for _ = 1, 600 do
|
||||
local top = game.stack:top()
|
||||
if top and top.battle then screen = top break end
|
||||
U.wait(1)
|
||||
end
|
||||
if not (screen and screen.battle) then
|
||||
print("[use-next] FAIL battle screen never came up")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
for _ = 1, 200 do
|
||||
if screen.phase == "menu" then break end
|
||||
tap("a", 3)
|
||||
end
|
||||
if screen.phase ~= "menu" then
|
||||
print("[use-next] FAIL never reached the battle menu")
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
|
||||
tap("a")
|
||||
U.wait(6)
|
||||
tap("a")
|
||||
|
||||
local sawAsk, sawForced = false, false
|
||||
for _ = 1, 400 do
|
||||
if screen.phase == "ask-next-mon" then sawAsk = true break end
|
||||
if screen.phase == "forced-switch" or screen.phase == "submenu" then
|
||||
sawForced = true
|
||||
break
|
||||
end
|
||||
if screen.battle.over then break end
|
||||
tap("a", 3)
|
||||
end
|
||||
|
||||
claim(sawAsk, "wild faint opened Use next POKéMON?")
|
||||
claim(not sawForced, "wild faint did not skip to the party list")
|
||||
claim(screen.phase == "ask-next-mon",
|
||||
"phase is ask-next-mon (YES switches, NO/B tries to run)")
|
||||
print("[use-next] YES sends you to the party. NO or B tries to run.")
|
||||
print("[use-next] a failed run still opens the party. trainers skip this.")
|
||||
|
||||
while true do U.wait(60) end
|
||||
end
|
||||
Reference in New Issue
Block a user