CLOSES #644, CLOSES #703, CLOSES #726, CLOSES #737, CLOSES #750, CLOSES #752, CLOSES #764, CLOSES #765, CLOSES #768, CLOSES #773, CLOSES #774, CLOSES #775, CLOSES #777, CLOSES #780, CLOSES #782

This commit is contained in:
bryanthaboi
2026-08-04 09:25:28 -04:00
parent 6cfc85ca83
commit f2d9b74490
52 changed files with 3099 additions and 252 deletions
+95 -1
View File
@@ -1639,9 +1639,100 @@ function RomExtractor:raw1bpp(label, width, height, relative, transparent)
return image
end
-- Trading animation art: gfx/trade.asm TradingAnimationGraphics is one
-- 49-tile atlas (game_boy.2bpp, built with --remove-duplicates, then
-- link_cable.2bpp), and the Game Boy and open-cable plates are painted out
-- of it through the tilemaps in data/tilemaps.asm (GameBoyTiles 6x8,
-- LinkCableTiles 12x3), whose ids are absolute vChars2 ids starting at $31
-- because trade.asm reaches them through
-- CopyTileIDsFromList_ZeroBaseTileID. Only the developer-only Python path
-- ever wrote these files, so an imported cache had none of them and
-- TradeAnim drew the whole cinematic as plain rectangles (#750).
function RomExtractor:extractTradeArt()
local BASE, COUNT = 0x31, 49
local gfx = self:symbol("TradingAnimationGraphics")
local atlas = ImageWriter.decode2bpp(
self.rom:bytes(gfx.bank, gfx.address, COUNT * 16), COUNT * 8, 8)
local function tileX(id)
local index = id - BASE
assert(index >= 0 and index < COUNT,
("trade tile $%02X is outside the animation atlas"):format(id))
return index * 8
end
local function plate(label, tilesWide, tilesHigh, relative, matte)
local map = self:symbol(label)
local ids = self.rom:bytes(map.bank, map.address, tilesWide * tilesHigh)
local image = ImageWriter.blank(tilesWide * 8, tilesHigh * 8, 1, 1, 1, 1)
for index, id in ipairs(ids) do
ImageWriter.blit(image, atlas,
(index - 1) % tilesWide * 8,
math.floor((index - 1) / tilesWide) * 8, tileX(id), 0, 8, 8)
end
if matte then image = ImageWriter.matteColor0(image) end
self:save(image, relative)
end
plate("GameBoyTiles", 6, 8, "trade/game_boy.png", true)
plate("LinkCableTiles", 12, 3, "trade/open_cable.png", false)
for _, spec in ipairs({
{ 0x5D, "cable_conn" }, { 0x5E, "cable_seg" }, { 0x5F, "cable_corner" },
{ 0x60, "cable_end" }, { 0x61, "cable_vert" },
}) do
local tile = ImageWriter.blank(8, 8, 1, 1, 1, 1)
ImageWriter.blit(tile, atlas, 0, 0, tileX(spec[1]), 0, 8, 8)
self:save(tile, "trade/" .. spec[2] .. ".png")
end
-- Trade_DrawCableAcrossScreen fills a whole 20-tile row with tile $5e.
local horizontal = ImageWriter.blank(160, 8, 1, 1, 1, 1)
for column = 0, 19 do
ImageWriter.blit(horizontal, atlas, column * 8, 0, tileX(0x5E), 0, 8, 8)
end
self:save(horizontal, "trade/cable_horiz.png")
-- Trade_BallInsideLinkCableOAMBlock draws one tile four times with the
-- X/Y flips, so each of the two frames -- $7e travelling, $7f bulging,
-- the bottom row of TradingAnimationGraphics2 -- makes a 16x16 ball.
local ball = self:symbol("TradingAnimationGraphics2")
local frames = ImageWriter.decode2bpp(
self.rom:bytes(ball.bank, ball.address, 64), 16, 16, true)
for index, name in ipairs({ "cable_ball", "cable_ball_alt" }) do
local image = ImageWriter.blank(16, 16, 1, 1, 1, 0)
for y = 0, 7 do
for x = 0, 7 do
local r, g, b, a = frames:getPixel((index - 1) * 8 + x, 8 + y)
image:setPixel(x, y, r, g, b, a)
image:setPixel(15 - x, y, r, g, b, a)
image:setPixel(x, 15 - y, r, g, b, a)
image:setPixel(15 - x, 15 - y, r, g, b, a)
end
end
self:save(image, "trade/" .. name .. ".png")
end
-- The ring around the travelling mon: one 16x16 quadrant per animation
-- frame (engine/gfx/mon_icons.asm TradeBubbleIconGFX), mirrored into a
-- 32x32 circle by the OAM attributes in Trade_CircleOAMBlocks.
local bubble = self:symbol("TradeBubbleIconGFX")
self:write2bpp(self.rom:bytes(bubble.bank, bubble.address, 128),
16, 32, "trade/bubble.png", true)
return {
gameBoy = "assets/generated/trade/game_boy.png",
openCable = "assets/generated/trade/open_cable.png",
cableHoriz = "assets/generated/trade/cable_horiz.png",
cableConn = "assets/generated/trade/cable_conn.png",
cableVert = "assets/generated/trade/cable_vert.png",
cableCorner = "assets/generated/trade/cable_corner.png",
cableEnd = "assets/generated/trade/cable_end.png",
cableBall = "assets/generated/trade/cable_ball.png",
cableBallAlt = "assets/generated/trade/cable_ball_alt.png",
bubble = "assets/generated/trade/bubble.png",
source = "ROM:TradingAnimationGraphics + ROM:TradeBubbleIconGFX"
.. " (engine/movie/trade.asm InternalClockTradeAnim)",
}
end
function RomExtractor:extractField()
self:beginStage("Interface artwork")
local done, total = 0, 49
local done, total = 0, 50
local function tick()
done = done + 1
self:tick("Interface artwork", math.min(done, total), total)
@@ -1832,6 +1923,8 @@ function RomExtractor:extractField()
end
self:save(emotes, "emotes.png"); tick()
local tradeArt = self:extractTradeArt(); tick()
-- Yellow-only: the Surfing Pikachu minigame sheets
-- (gfx/surfing_pikachu.asm) at pret's canvas widths, so
-- src/ui/SurfingMinigame.lua's quads can be read off the source pngs.
@@ -1962,6 +2055,7 @@ function RomExtractor:extractField()
local converted = {}
for index, values in pairs(adjacency) do converted[tonumber(index)] = values end
data.hiddenExtras.trashCans.adjacent = converted
data.tradeArt = tradeArt
data.source = "canonical Pokemon Red ROM + bundled port metadata"
self:write("field", data)
self:tick("Interface artwork", total, total)