-- #2328: the save screen's location header printed the engine's internal map -- id ("FR_ROUTE_22") instead of the place name ("ROUTE 22"). -- -- The header drew `session.mapName or session.map`, and nothing in src/ ever -- assigns session.mapName (only tests did), so the `or` chain always fell -- through to the raw map id. pret prints the sMapNames place name, resolved -- from the map header's region map section: -- pokefirered/src/start_menu.c PrintSaveStats -> SAVE_STAT_LOCATION -- pokefirered/src/save_menu_util.c GetMapNameGeneric(dest, gMapHeader.regionMapSectionId) -- pokefirered/src/region_map.c GetMapName(dst, mapsec, 0) -- fill = 0 -- and centres it in the 14-tile stats window: -- x = (112 - GetStringWidth(FONT_NORMAL, gStringVar4, -1)) / 2 package.path = "./?.lua;./?/init.lua;" .. package.path require("tests.fixture_data.game3_map_sections").install({ [135] = "POKéMON MANSION", [196] = "CELADON DEPT." }) local T = require("tests.harness") local check, eq = T.check, T.eq love = love or require("tests.love_stub") package.loaded["src.core.game3.rom_text"] = { plain = function(key) return key end, box = function(key) return key end, ascii = function(key) return key end, has = function() return true end, key = function(n, i, j) return j and (n .. "[" .. i .. "][" .. j .. "]") or (n .. "[" .. i .. "]") end, at = function(n, i, j) return j and (n .. "[" .. i .. "][" .. j .. "]") or (n .. "[" .. i .. "]") end, count = function() return 0 end, list = function() return {} end, lazy = function(map) return setmetatable({}, { __index = function(_, k) return map[k] end }) end, } -- The header is the only thing under test, so stand the frame/menu plumbing -- down: nothing here needs a real window stack or a chrome image. package.loaded["src.ui.game3.stack"] = { push = function() end, pop = function() end, busy = function() return false end, drawOrder = function() return {} end, } package.loaded["src.ui.game3.window"] = { template = function(l, t, w, h) return { left = l, top = t, width = w, height = h } end, stdFrame = function() end, fixedStdFrame = function() end, cursorPx = function() end, } package.loaded["src.ui.game3.chrome"] = { dialogueFrame = function() end } local SaveMenu = require("src.ui.game3.save_menu") local FrlgFont = require("src.ui.game3.frlg_font") eq(type(SaveMenu.locationName), "function", "SaveMenu.locationName exists") -- --- the reported map resolves to its place name ---------------------------- -- regionMapSectionId values come from the game's own map headers. local function gameWith(maps) return { data = { maps = maps } } end SaveMenu._game = gameWith({ FR_ROUTE_22 = { regionMapSectionId = 122 }, FR_PALLET_TOWN = { regionMapSectionId = 88 }, FR_CELADON_CITY_DEPARTMENT_STORE_1F = { regionMapSectionId = 94 }, FR_POKEMON_MANSION = { regionMapSectionId = 135 }, }) eq(SaveMenu.locationName({ map = "FR_ROUTE_22" }), "ROUTE 22", "the reported map shows its place name, not the engine id") check(SaveMenu.locationName({ map = "FR_ROUTE_22" }) ~= "FR_ROUTE_22", "the internal id is not shown verbatim (the reported bug)") eq(SaveMenu.locationName({ map = "FR_PALLET_TOWN" }), "PALLET TOWN", "Pallet Town") eq(SaveMenu.locationName({ map = "FR_POKEMON_MANSION" }), "POKéMON MANSION", "the mapsec name, accents and all, is what pret prints") eq(SaveMenu.locationName({ map = "FR_CELADON_CITY_DEPARTMENT_STORE_1F" }), "CELADON DEPT.", "region_map.c's Celadon Dept. Store override still applies") eq(SaveMenu.locationName({ mapName = "CUSTOM PLACE" }), "CUSTOM PLACE", "an explicit session.mapName still wins") eq(SaveMenu.locationName({}), "PALLET TOWN", "a session with no map keeps the default") eq(SaveMenu.locationName(nil), "PALLET TOWN", "a missing session keeps the default") -- --- with no dataset loaded the map id is still resolved by name ------------ -- getInfo() fuzzy-matches the id against sMapNames; the match must be -- deterministic, or "ROUTE_22" can land on "ROUTE 2" depending on pairs() order. SaveMenu._game = nil eq(SaveMenu.locationName({ map = "FR_ROUTE_22" }), "ROUTE 22", "the map id alone resolves, with no dataset and no arbitrary-match drift") eq(SaveMenu.locationName({ map = "FR_PALLET_TOWN" }), "PALLET TOWN", "the map id alone resolves to Pallet Town") local unknown = SaveMenu.locationName({ map = "FR_MOD_CUSTOM_CAVE" }) check(not unknown:find("FR_", 1, true), "an unknown map id never shows the engine prefix") eq(unknown, "MOD CUSTOM CAVE", "an unknown map id falls back to a readable id") -- --- the header is the resolved name, centred in the 14-tile window --------- SaveMenu._game = gameWith({ FR_ROUTE_22 = { regionMapSectionId = 122 } }) local realDraw = FrlgFont.draw local seen = {} FrlgFont.draw = function(text, x, y, opts) seen[#seen + 1] = { text = text, x = x, y = y, maxWidth = opts and opts.maxWidth } return 0 end SaveMenu.open = true SaveMenu._session = { map = "FR_ROUTE_22", name = "RED" } local ok, err = pcall(SaveMenu.draw) SaveMenu.open = false FrlgFont.draw = realDraw check(ok, "the save screen draws headless: " .. tostring(err)) local header for _, s in ipairs(seen) do if s.text == "ROUTE 22" then header = s end end check(header ~= nil, "the header is drawn as the resolved place name") local BOX_X, BOX_W = 1 * 8, 14 * 8 -- sSaveStatsWindowTemplate (1, 1, 14, 9) eq(header and header.y, 1 * 8 + 2, "header keeps its row") eq(header and header.maxWidth, BOX_W, "header is clamped to the window, not 240px") check(header and header.x >= BOX_X and header.x + FrlgFont.measure("ROUTE 22") <= BOX_X + BOX_W, "the centred header stays inside the window") -- pret: x = (112 - GetStringWidth(FONT_NORMAL, text)) / 2, from the window's left edge local nameW = FrlgFont.measure("ROUTE 22") eq(header and header.x, BOX_X + math.floor((BOX_W - nameW) / 2), "header is centred like start_menu.c PrintSaveStats") -- no place name pret can print may overflow the 112px box for _, n in ipairs({ "POKéMON MANSION", "VIRIDIAN FOREST", "UNDERGROUND PATH", "POKéMON LEAGUE", "DIGLETT'S CAVE", "MT. MOON", "SAFFRON CITY", "CELADON DEPT.", }) do local w = FrlgFont.measure(n) check(w <= BOX_W, ("%q fits the 14-tile window (%dpx of %d)"):format(n, w, BOX_W)) end T.finish("save_menu_location_bug2328")