mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-01 07:40:29 -04:00
26506b0677
- Fix Emerald party menu TM learnability descriptions (sDescriptionStringTable indexing) - Fix summary menu 5th move slot display and move learning in RSE - Fix pause/start menu viewport scrolling and scroll arrow indicators - Fix berry planting bag pocket routing and screen veil fading in Emerald - Fix Route 101 Birch rescue positioning and truck sequence unfreeze - Add comprehensive regression tests for all fixes
221 lines
7.7 KiB
Lua
221 lines
7.7 KiB
Lua
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
|
|
local function romTextPlain(key) return key end
|
|
package.loaded["src.core.game3.rom_text"] = {
|
|
plain = romTextPlain, box = romTextPlain, ascii = romTextPlain, has = function() return true end,
|
|
key = function(n, i, j) return n end, at = function(n, i, j) return n end,
|
|
count = function() return 0 end, list = function() return {} end,
|
|
lazy = function(map) return setmetatable({}, { __index = function(_, k) return map[k] end }) end,
|
|
}
|
|
|
|
local StartMenu = require("src.ui.game3.start_menu")
|
|
local Window = require("src.ui.game3.window")
|
|
local RseData = require("src.ui.game3.rse.start_menu_data")
|
|
|
|
-- Mock love for draw test
|
|
local loveStub = require("tests.love_stub")
|
|
_G.love = _G.love or loveStub
|
|
|
|
-- 1. Test standard 8-or-fewer entries (Vanilla Emerald without MODS)
|
|
do
|
|
StartMenu.resetCursor()
|
|
eq(StartMenu.cursor, 1, "Cursor is 1 after reset")
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset is 0 after reset")
|
|
|
|
local fakeSession = { name = "MAY", version = "emerald", bag = {}, party = {}, dex = {} }
|
|
local fakeGame = { modStatus = { available = {} } } -- No mods -> 8 vanilla items (Dex, Mon, Bag, Nav, Name, Save, Option, Exit)
|
|
|
|
StartMenu.show({ session = fakeSession, game = fakeGame })
|
|
check(StartMenu.isOpen(), "StartMenu is open")
|
|
eq(#StartMenu.ENTRIES, 8, "Vanilla Emerald has 8 entries with pokedex/pokemon/pokenav flags")
|
|
|
|
local tpl = StartMenu.contentTemplate()
|
|
eq(tpl.h, 18, "8 items window height is (8*2)+2 = 18 tiles")
|
|
eq(tpl.top + tpl.h, 19, "Top(1) + Height(18) = 19 <= 19 (fits in 20-tile screen)")
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset stays 0")
|
|
|
|
-- Move down through all 8 items
|
|
for i = 2, 8 do
|
|
StartMenu.move(1)
|
|
eq(StartMenu.cursor, i, "Cursor moved to " .. i)
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset stays 0 for <= 8 items")
|
|
end
|
|
|
|
-- Wrap down from 8 to 1
|
|
StartMenu.move(1)
|
|
eq(StartMenu.cursor, 1, "Cursor wrapped to 1")
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset stays 0")
|
|
|
|
-- Wrap up from 1 to 8
|
|
StartMenu.move(-1)
|
|
eq(StartMenu.cursor, 8, "Cursor wrapped to 8")
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset stays 0")
|
|
|
|
StartMenu.close(true)
|
|
end
|
|
|
|
-- 2. Test 9 entries (Emerald with MODS menu entry)
|
|
do
|
|
StartMenu.resetCursor()
|
|
local fakeSession = { name = "MAY", version = "emerald", bag = {}, party = {}, dex = {} }
|
|
local fakeGame = {
|
|
modStatus = {
|
|
available = {
|
|
{ id = "test_mod", name = "Test Mod", enabled = true },
|
|
},
|
|
},
|
|
}
|
|
|
|
StartMenu.show({ session = fakeSession, game = fakeGame })
|
|
check(StartMenu.isOpen(), "StartMenu is open with mods")
|
|
eq(#StartMenu.ENTRIES, 9, "Emerald with MODS has 9 entries")
|
|
|
|
-- Window template must be capped at 8 visible entries
|
|
local tpl = StartMenu.contentTemplate()
|
|
eq(tpl.h, 18, "Window height is capped at (8*2)+2 = 18 tiles despite 9 entries")
|
|
eq(tpl.top + tpl.h, 19, "Window bottom frame is within row 19 (no viewport clipping)")
|
|
eq(StartMenu._scrollOffset, 0, "Initial scroll offset is 0")
|
|
|
|
-- Navigate down to item 8
|
|
for i = 2, 8 do
|
|
StartMenu.move(1)
|
|
eq(StartMenu.cursor, i, "Cursor is " .. i)
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset is 0 while on items 1..8")
|
|
end
|
|
|
|
-- Navigate down to item 9 (EXIT) -> should scroll
|
|
StartMenu.move(1)
|
|
eq(StartMenu.cursor, 9, "Cursor is on item 9 (EXIT)")
|
|
eq(StartMenu._scrollOffset, 1, "Scroll offset shifted to 1 to show items 2..9")
|
|
|
|
-- Navigate down from item 9 -> wraps to item 1 (POKéDEX) and resets scroll
|
|
StartMenu.move(1)
|
|
eq(StartMenu.cursor, 1, "Cursor wrapped to item 1 (POKéDEX)")
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset reset to 0 on wrap to top")
|
|
|
|
-- Navigate up from item 1 -> wraps to item 9 (EXIT) and shifts scroll to bottom
|
|
StartMenu.move(-1)
|
|
eq(StartMenu.cursor, 9, "Cursor wrapped to item 9 (EXIT)")
|
|
eq(StartMenu._scrollOffset, 1, "Scroll offset shifted to 1 on wrap to bottom")
|
|
|
|
-- Navigate up to item 8 (MODS) -> scroll stays 1, cursor row is 7
|
|
StartMenu.move(-1)
|
|
eq(StartMenu.cursor, 8, "Cursor moved to item 8 (MODS)")
|
|
eq(StartMenu._scrollOffset, 1, "Scroll offset stays 1")
|
|
|
|
-- Navigate up until item 1
|
|
for i = 7, 2, -1 do
|
|
StartMenu.move(-1)
|
|
eq(StartMenu.cursor, i, "Cursor is " .. i)
|
|
end
|
|
eq(StartMenu._scrollOffset, 1, "Scroll offset stays 1 until item 1 is reached")
|
|
|
|
StartMenu.move(-1)
|
|
eq(StartMenu.cursor, 1, "Cursor is 1")
|
|
eq(StartMenu._scrollOffset, 0, "Scroll offset adjusted to 0 when cursor hits row 1")
|
|
|
|
-- Verify draw executes cleanly when scrolled
|
|
StartMenu.move(-1) -- Move to 9 (scrolled)
|
|
local ok, err = pcall(StartMenu.draw)
|
|
check(ok, "StartMenu.draw() executes cleanly while scrolled: " .. tostring(err))
|
|
|
|
StartMenu.close(true)
|
|
end
|
|
|
|
-- 3. Test arbitrary large list (e.g. 14 modded entries)
|
|
do
|
|
StartMenu.resetCursor()
|
|
StartMenu.ENTRIES = {}
|
|
for i = 1, 14 do
|
|
table.insert(StartMenu.ENTRIES, { id = "item" .. i, label = "ITEM " .. i })
|
|
end
|
|
StartMenu._data = RseData
|
|
StartMenu.open = true
|
|
StartMenu.cursor = 1
|
|
StartMenu.clampScroll()
|
|
|
|
eq(StartMenu._scrollOffset, 0, "Scroll is 0")
|
|
local tpl = StartMenu.contentTemplate()
|
|
eq(tpl.h, 18, "Height is still 18 tiles max")
|
|
|
|
-- Step down 10 times (to item 11)
|
|
for i = 2, 11 do
|
|
StartMenu.move(1)
|
|
end
|
|
eq(StartMenu.cursor, 11, "Cursor is 11")
|
|
eq(StartMenu._scrollOffset, 3, "Scroll offset is 11 - 8 = 3 (showing items 4..11)")
|
|
|
|
-- Step up 5 times (to item 6)
|
|
for _ = 1, 5 do
|
|
StartMenu.move(-1)
|
|
end
|
|
eq(StartMenu.cursor, 6, "Cursor is 6")
|
|
eq(StartMenu._scrollOffset, 3, "Scroll offset remains 3 (item 6 is visible in 4..11)")
|
|
|
|
-- Direct cursor assignment & clampScroll
|
|
StartMenu.cursor = 14
|
|
StartMenu.clampScroll()
|
|
eq(StartMenu._scrollOffset, 6, "clampScroll sets scroll to 14 - 8 = 6 for item 14")
|
|
|
|
StartMenu.cursor = 2
|
|
StartMenu.clampScroll()
|
|
eq(StartMenu._scrollOffset, 1, "clampScroll sets scroll to 1 for item 2")
|
|
|
|
StartMenu.close(true)
|
|
end
|
|
|
|
-- 4. Test scroll arrow indicators during draw
|
|
do
|
|
local ListMenu = require("src.ui.game3.list_menu")
|
|
local capturedArrows = {}
|
|
local origDrawScrollArrows = ListMenu.drawScrollArrows
|
|
ListMenu.drawScrollArrows = function(tpl, showUp, showDown, t)
|
|
capturedArrows[#capturedArrows + 1] = { up = showUp, down = showDown }
|
|
end
|
|
|
|
StartMenu.resetCursor()
|
|
StartMenu.ENTRIES = {}
|
|
for i = 1, 9 do
|
|
table.insert(StartMenu.ENTRIES, { id = "item" .. i, label = "ITEM " .. i })
|
|
end
|
|
StartMenu._data = RseData
|
|
StartMenu.open = true
|
|
|
|
-- At top (scroll = 0): down arrow should be visible, up arrow hidden
|
|
StartMenu.cursor = 1
|
|
StartMenu.clampScroll()
|
|
capturedArrows = {}
|
|
StartMenu.draw()
|
|
eq(#capturedArrows, 1, "drawScrollArrows called once")
|
|
eq(capturedArrows[1].up, false, "Up arrow hidden at top")
|
|
eq(capturedArrows[1].down, true, "Down arrow shown at top to indicate more items below")
|
|
|
|
-- At bottom (scroll = 1): up arrow should be visible, down arrow hidden
|
|
StartMenu.cursor = 9
|
|
StartMenu.clampScroll()
|
|
capturedArrows = {}
|
|
StartMenu.draw()
|
|
eq(#capturedArrows, 1, "drawScrollArrows called once")
|
|
eq(capturedArrows[1].up, true, "Up arrow shown at bottom to indicate more items above")
|
|
eq(capturedArrows[1].down, false, "Down arrow hidden at bottom")
|
|
|
|
-- In middle of 14 items: both up and down arrows visible
|
|
StartMenu.ENTRIES = {}
|
|
for i = 1, 14 do
|
|
table.insert(StartMenu.ENTRIES, { id = "item" .. i, label = "ITEM " .. i })
|
|
end
|
|
StartMenu.cursor = 6
|
|
StartMenu._scrollOffset = 3
|
|
capturedArrows = {}
|
|
StartMenu.draw()
|
|
eq(#capturedArrows, 1, "drawScrollArrows called once in middle")
|
|
eq(capturedArrows[1].up, true, "Up arrow shown in middle")
|
|
eq(capturedArrows[1].down, true, "Down arrow shown in middle")
|
|
|
|
ListMenu.drawScrollArrows = origDrawScrollArrows
|
|
StartMenu.close(true)
|
|
end
|
|
|
|
T.finish("game3_start_menu_scroll_test")
|