diff --git a/src/battle/Gen3Battle.lua b/src/battle/Gen3Battle.lua index b4631c24..8503479b 100644 --- a/src/battle/Gen3Battle.lua +++ b/src/battle/Gen3Battle.lua @@ -1595,7 +1595,20 @@ end local warnedNoPanel = false local function panelImage(battle, which) local record = Gen3Battle.textbox(battle) - local path = record and record.images and record.images[which] + local path = nil + if record then + local options = battle.game and battle.game.save and battle.game.save.options + or battle.save and battle.save.options + or {} + local frame = math.floor(tonumber(options.gen3Frame) or 1) + local count = math.floor(tonumber(record.frames and record.frames.count) or 0) + if count > 0 then frame = math.max(1, math.min(count, frame)) end + local perFrame = record.frameImages and record.frameImages[frame] + path = perFrame and perFrame[which] + if type(path) ~= "string" then + path = record.images and record.images[which] + end + end if type(path) ~= "string" then if not warnedNoPanel then warnedNoPanel = true diff --git a/src/import/RomExtractorGen3.lua b/src/import/RomExtractorGen3.lua index bd1247d2..42d62791 100644 --- a/src/import/RomExtractorGen3.lua +++ b/src/import/RomExtractorGen3.lua @@ -53226,7 +53226,10 @@ function RomExtractorGen3:extractBattleTextbox() local base = colours(palRaw) -- banks 0 and 1 local menuPal = colours(menuRaw) -- BG palette 5 - -- the twenty OPTIONS frames, each nine tiles and sixteen colours + -- The OPTIONS frames, each nine tiles and sixteen colours. Follow the + -- pointer table itself: FireRed has small empty blobs between some of the + -- frame graphics, so the tile sheets are deliberately not one contiguous + -- `FRAME_TILES + i * size` array. local frames = {} for i = 0, T.FRAME_COUNT - 1 do local at = T.FRAMES + i * 8 @@ -53234,76 +53237,81 @@ function RomExtractorGen3:extractBattleTextbox() tiles = rom:pointer(at), palette = rom:pointer(at + 4), } end - -- ...and the table has to be the contiguous run the two blob arrays are, - -- which is what says 0x51021C is the struct array and not a palette - local contiguous = true - for i = 0, T.FRAME_COUNT - 1 do - if frames[i + 1].tiles ~= T.FRAME_TILES + i * T.FRAME_TILES_BYTES then - contiguous = false - end - if frames[i + 1].palette ~= T.FRAME_PALETTES + i * T.FRAME_PALETTE_BYTES then - contiguous = false + local validFrames = true + for i = 1, T.FRAME_COUNT do + if not (frames[i] and frames[i].tiles and frames[i].palette) then + validFrames = false + break end end - if not contiguous then - Logger.warn("gen3 battle textbox: the frame table at %07X does not point " - .. "at the two contiguous blob arrays -- one frame is used " - .. "for every setting", T.FRAMES) + if not validFrames then + Logger.warn("gen3 battle textbox: the frame table at %07X has a missing " + .. "tiles/palette pointer -- the battle keeps frame 1", T.FRAMES) end -- ---- compose ------------------------------------------------------------ local images = {} + local frameImages = {} local composed = pcall(function() - -- the frame the strips are drawn with. Composing all twenty would be - -- twenty times the pictures for a setting almost nobody moves, so this - -- takes the first -- which is the frame a new save starts on -- and the - -- table above is recorded so a later pass can follow the option. - local ft = {} - for k = 1, T.FRAME_TILES_BYTES do - ft[k] = rom:u8(T.FRAME_TILES + k - 1) - end - local framePal = {} - for k = 1, T.FRAME_PALETTE_BYTES do - framePal[k] = rom:u8(T.FRAME_PALETTES + k - 1) - end - local sheet = {} - for k = 1, #tiles do sheet[k] = tiles[k] end - for _, tileBase in ipairs(T.FRAME_TILE_BASES or { T.FRAME_TILE_BASE }) do - for k = 1, T.FRAME_TILES_BYTES do - sheet[tileBase * 32 + k] = ft[k] + -- FireRed loads the selected user frame into BOTH battle tile ranges every + -- time battle BGs are initialized. Bake every available option so the + -- battle panel follows the same Frame Type setting as normal windows. + for frameIndex = 0, (validFrames and T.FRAME_COUNT or 1) - 1 do + local ft = {} + local frame = frames[frameIndex + 1] + local ftAt = (frame and frame.tiles) or T.FRAME_TILES + for k = 1, T.FRAME_TILES_BYTES do ft[k] = rom:u8(ftAt + k - 1) end + + local framePal = {} + local fpAt = (frame and frame.palette) or T.FRAME_PALETTES + for k = 1, T.FRAME_PALETTE_BYTES do framePal[k] = rom:u8(fpAt + k - 1) end + + local sheet = {} + for k = 1, #tiles do sheet[k] = tiles[k] end + for _, tileBase in ipairs(T.FRAME_TILE_BASES or { T.FRAME_TILE_BASE }) do + for k = 1, T.FRAME_TILES_BYTES do sheet[tileBase * 32 + k] = ft[k] end end - end - local pal = {} - for i = 0, 31 do pal[i] = base[i] end - for i = 0, 15 do - local r, g, b = RomGba.bgr555(framePal[i * 2 + 1] + framePal[i * 2 + 2] * 256) - pal[T.FRAME_BANK * 16 + i] = { r, g, b } - end - -- partyTile wants a one-based colour list indexed bank*16 + index + 1 - local flat = {} - for i = 0, 31 do flat[i + 1] = pal[i] end - - for _, panel in ipairs(T.PANELS) do - local img = ImageWriter.blank(T.WIDTH * 8, T.PANEL_ROWS * 8) - for r = 0, T.PANEL_ROWS - 1 do - for c = 0, T.WIDTH - 1 do - local cell = (panel.row + r) * T.COLS + c - local e = map[cell * 2 + 1] + map[cell * 2 + 2] * 256 - local id = e % 1024 - local bank = math.floor(e / 4096) % 16 - if id > 0 and id < tileCount and bank < 2 then - RomExtractorGen3.partyTile(img, sheet, flat, id, bank, c * 8, r * 8) + + local pal = {} + for i = 0, 31 do pal[i] = base[i] end + for i = 0, 15 do + local r, g, b = RomGba.bgr555(framePal[i * 2 + 1] + framePal[i * 2 + 2] * 256) + pal[T.FRAME_BANK * 16 + i] = { r, g, b } + end + local flat = {} + for i = 0, 31 do flat[i + 1] = pal[i] end + + local perFrame = {} + for _, panel in ipairs(T.PANELS) do + local img = ImageWriter.blank(T.WIDTH * 8, T.PANEL_ROWS * 8) + for r = 0, T.PANEL_ROWS - 1 do + for c = 0, T.WIDTH - 1 do + local cell = (panel.row + r) * T.COLS + c + local e = map[cell * 2 + 1] + map[cell * 2 + 2] * 256 + local id = e % 1024 + local bank = math.floor(e / 4096) % 16 + if id > 0 and id < tileCount and bank < 2 then + RomExtractorGen3.partyTile(img, sheet, flat, id, bank, c * 8, r * 8) + end end end + local suffix = frameIndex == 0 and panel.key + or (panel.key .. "_frame" .. tostring(frameIndex + 1)) + local rel = "battle/textbox_" .. suffix .. ".png" + self:saveImage(img, rel) + perFrame[panel.key] = "assets/generated/" .. rel + if frameIndex == 0 then images[panel.key] = perFrame[panel.key] end end - self:saveImage(img, "battle/textbox_" .. panel.key .. ".png") - images[panel.key] = "assets/generated/battle/textbox_" .. panel.key .. ".png" + frameImages[frameIndex + 1] = perFrame end - -- ...and the arrow, which is two tiles of the same sheet in bank 0 + -- ...and the arrow, which is two tiles of the original battle sheet in + -- bank 0; it is independent of the user's decorative frame selection. + local cursorPal = {} + for i = 0, 31 do cursorPal[i + 1] = base[i] end local cur = ImageWriter.blank(8, 8 * #T.CURSOR_TILES) for i, id in ipairs(T.CURSOR_TILES) do - RomExtractorGen3.partyTile(cur, sheet, flat, id, T.CURSOR_BANK, + RomExtractorGen3.partyTile(cur, tiles, cursorPal, id, T.CURSOR_BANK, 0, (i - 1) * 8) end self:saveImage(cur, "battle/textbox_cursor.png") @@ -53326,6 +53334,7 @@ function RomExtractorGen3:extractBattleTextbox() local constants = self._constants or {} constants.gen3BattleTextbox = { images = images, + frameImages = frameImages, y = T.SCREEN_Y, width = T.WIDTH * 8, height = T.PANEL_ROWS * 8, diff --git a/src/script/Gen3Commands.lua b/src/script/Gen3Commands.lua index f908d36d..3c94e5d9 100644 --- a/src/script/Gen3Commands.lua +++ b/src/script/Gen3Commands.lua @@ -3237,10 +3237,11 @@ end -- handles that by ticking the script runner from its own update, because the -- state stack only updates the state on top. -- --- The GEOMETRY is the script's own. `10 3` is the window's tile position and --- a Gen 3 front pic is 64x64 -- eight tiles, not the Game Boy's seven -- so --- the frame is drawn one tile outside that and PicBox is told both rather --- than keeping its Game Boy constants. +-- The GEOMETRY is the script's own. FireRed passes `10 3` to +-- CreateWindowFromRect, which puts the 8x8 content at (x+1,y+1); the standard +-- border therefore begins at the script's (x,y). CreateMonSprite_PicBox uses +-- the same content origin for the 64x64 front pic. PicBox is told the outer +-- frame directly rather than keeping its Game Boy constants. function Commands.g3_show_mon_pic(ctx, species, x, y) species = valueOf(ctx, species) -- ScrCmd_showmonpic: VarGet ctx.g3MonPic = tonumber(species) @@ -3259,8 +3260,8 @@ function Commands.g3_show_mon_pic(ctx, species, x, y) local box = require("src.ui.PicBox").new(game, { path = path, trueColor = trueColor and true or false, passive = true, overworld = ow, - -- the frame sits one tile outside the window the script names - box = { x = tx - 1, y = ty - 1, w = PIC_TILES + 2, h = PIC_TILES + 2 }, + -- FireRed's script coordinates name the outer frame origin. + box = { x = tx, y = ty, w = PIC_TILES + 2, h = PIC_TILES + 2 }, picTiles = PIC_TILES, }) ow.pokepicBox = box