mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 00:10:56 +02:00
177 lines
7.5 KiB
Lua
177 lines
7.5 KiB
Lua
-- In-battle HUD tiles, shared by the battle screen and the status
|
|
-- screen: pokered overlays the $62-$7F font area with the HP bar /
|
|
-- status sheet (font_battle_extra -> $62) and the HUD line tiles
|
|
-- (battle_hud_1 -> $6D, battle_hud_2+3 -> $73).
|
|
--
|
|
-- The two screens do NOT use the same overlay: the status screen scatters
|
|
-- hud_2 and hud_3 instead of copying them contiguously, which is what keeps
|
|
-- its № and <ID> glyphs alive. HudTiles.tile draws the battle layout,
|
|
-- HudTiles.statusTile the status one -- see STATUS_PAGES below. #280
|
|
|
|
local Assets = require("src.render.Assets")
|
|
|
|
local HudTiles = {}
|
|
|
|
-- The four HUD sheets are glyph pages like any other, so they resolve
|
|
-- through the font registry: mod.content.font:register("battle_hud_1",
|
|
-- { image = ..., base = 0x6D }) reskins the HP bar. These are the
|
|
-- vanilla pages the importer's cache carries, in the order the asm
|
|
-- overlays them ($6D lands on top of font_battle_extra's tail).
|
|
local PAGES = {
|
|
{ id = "font_battle_extra",
|
|
image = "assets/generated/battle/font_battle_extra.png", base = 0x62 },
|
|
{ id = "battle_hud_1",
|
|
image = "assets/generated/battle/battle_hud_1.png", base = 0x6D },
|
|
{ id = "battle_hud_2",
|
|
image = "assets/generated/battle/battle_hud_2.png", base = 0x73 },
|
|
{ id = "battle_hud_3",
|
|
image = "assets/generated/battle/battle_hud_3.png", base = 0x76 },
|
|
}
|
|
|
|
-- The STATUS SCREEN overlays the SAME sheets differently, and the layout
|
|
-- above would break it: engine/pokemon/status_screen.asm:86-97 copies 3
|
|
-- tiles of hud_1 to $6D, ONE tile of hud_2 to $78 and 2 tiles of hud_3 to
|
|
-- $76, which leaves $70/$73/$74 as font_battle_extra's <to>, <ID> and № --
|
|
-- the glyphs the screen prints "№." and "<ID>№/" from
|
|
-- (constants/charmap.asm:69-73). The battle overlay instead copies
|
|
-- hud_2+hud_3 contiguously over $73-$78 (engine/battle/core.asm:6520/6532),
|
|
-- burying № under a line tile, so the status screen needs its own table.
|
|
-- The line glyphs land identically either way -- $76 ─, $77 ┘, $6F the
|
|
-- halfarrow -- only the vertical bar moves ($73 in battle, $78 here). #280
|
|
local STATUS_PAGES = {
|
|
{ id = "font_battle_extra",
|
|
image = "assets/generated/battle/font_battle_extra.png", base = 0x62 },
|
|
{ id = "battle_hud_1",
|
|
image = "assets/generated/battle/battle_hud_1.png", base = 0x6D },
|
|
{ id = "battle_hud_3",
|
|
image = "assets/generated/battle/battle_hud_3.png", base = 0x76, count = 2 },
|
|
{ id = "battle_hud_2",
|
|
image = "assets/generated/battle/battle_hud_2.png", base = 0x78, count = 1 },
|
|
}
|
|
|
|
local tiles, statusTiles
|
|
|
|
-- Build one code -> {img, quad} map from a page list. `count` caps a page
|
|
-- at the number of tiles the asm actually copies (the extracted sheets all
|
|
-- carry 3 tiles; the status overlay uses fewer). A mod's registered page
|
|
-- swaps the image in either table, but only the battle table honors its
|
|
-- `base`: the status layout is the asm's own placement, and sliding hud_2
|
|
-- there would bury № again.
|
|
local function build(pages, fixedBase)
|
|
local out = {}
|
|
local registered = require("src.core.Data").font
|
|
registered = registered and registered.pages or nil
|
|
for _, page in ipairs(pages) do
|
|
local override = registered and registered[page.id]
|
|
local path, base = page.image, page.base
|
|
if override and override.image then path = override.image end
|
|
if not fixedBase and override and override.base then base = override.base end
|
|
local ok, img = pcall(Assets.image, path)
|
|
if ok then
|
|
local iw, ih = img:getDimensions()
|
|
local per = iw / 8
|
|
local count = page.count or per * (ih / 8)
|
|
for i = 0, count - 1 do
|
|
out[base + i] = {
|
|
img = img,
|
|
quad = love.graphics.newQuad((i % per) * 8,
|
|
math.floor(i / per) * 8, 8, 8, iw, ih),
|
|
}
|
|
end
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function put(t, x, y, tint)
|
|
if not t then return end
|
|
local r, g, b, a = love.graphics.getColor()
|
|
love.graphics.setColor(tint or { 1, 1, 1, 1 })
|
|
love.graphics.draw(t.img, t.quad, x, y)
|
|
love.graphics.setColor(r, g, b, a)
|
|
end
|
|
|
|
function HudTiles.tile(code, x, y, tint)
|
|
if not tiles then tiles = build(PAGES) end
|
|
put(tiles[code], x, y, tint)
|
|
end
|
|
|
|
-- The same sheets under the status screen's overlay (STATUS_PAGES). The HP
|
|
-- bar codes $62-$6D are identical in both layouts, so drawHPBar below keeps
|
|
-- using the battle table. #280
|
|
function HudTiles.statusTile(code, x, y, tint)
|
|
if not statusTiles then statusTiles = build(STATUS_PAGES, true) end
|
|
put(statusTiles[code], x, y, tint)
|
|
end
|
|
|
|
-- lazy: the next tile() rebuilds every page from the search path
|
|
function HudTiles.invalidate()
|
|
tiles = nil
|
|
statusTiles = nil
|
|
end
|
|
|
|
Assets.register(HudTiles.invalidate)
|
|
|
|
-- The bar's right-end tile follows wHPBarType (DrawHPBar's "Right"
|
|
-- branch): only type 1 -- the player's in-battle bar and the status
|
|
-- screen -- gets the double-bar $6D; the enemy bar (0) and the party
|
|
-- menu (2) close with the near-blank $6C nub.
|
|
function HudTiles.capTile(barType)
|
|
return barType == 1 and 0x6D or 0x6C
|
|
end
|
|
|
|
-- Tile HP bar (home/pokemon.asm DrawHPBar): "HP" ($71) + ":[" ($62),
|
|
-- six 8px segments ($63 empty, +n partial, $6B full), then the
|
|
-- wHPBarType right cap. A nonzero HP always shows at least a
|
|
-- one-pixel sliver. The fill is tinted with the SGB bar palettes at
|
|
-- GetHealthBarColor's thresholds (>= 27 px green, >= 10 yellow, else
|
|
-- red).
|
|
--
|
|
-- segments: how many 8px cells the bar spans (6, the hardware width,
|
|
-- unless a caller asks for more -- the widescreen battle layout has room
|
|
-- for a longer bar in the same tiles). The color thresholds scale with
|
|
-- it so a wider bar turns yellow and red at the same fractions of full.
|
|
--
|
|
-- grayFill (#229): when the caller will colorize this bar with an SGB
|
|
-- region palette (BattleState's zone pass, BATTLE_ZONES pal 0/1 =
|
|
-- GetHealthBarColor), leave the fill as its raw DMG shade-2 gray and skip
|
|
-- the per-pixel tint -- the DMG hardware bar is ONE gray shade recolored by
|
|
-- the region palette (engine/gfx/palettes.asm SetPal_Battle,
|
|
-- data/sgb/sgb_packets.asm BlkPacket_Battle), never a per-pixel repaint.
|
|
-- Tinting first would double-apply the color: GREENBAR's fill {0,189,0} has
|
|
-- red channel 0, so the tint zeroes the whole bar's red and the zone's
|
|
-- red-channel-keyed shade shader then maps every pixel to color 3 = black.
|
|
function HudTiles.drawHPBar(data, tx, ty, mon, barType, grayFill, segments)
|
|
local x, y = tx * 8, ty * 8
|
|
segments = math.max(1, math.floor(segments or 6))
|
|
HudTiles.tile(0x71, x, y)
|
|
HudTiles.tile(0x62, x + 8, y)
|
|
local px = 0
|
|
if mon.stats.hp > 0 and mon.hp > 0 then
|
|
px = math.max(1, math.floor(mon.hp * segments * 8 / mon.stats.hp))
|
|
end
|
|
local tint
|
|
if not grayFill then
|
|
local PaletteFX = require("src.render.PaletteFX")
|
|
local green = math.ceil(27 * segments / 6)
|
|
local yellow = math.ceil(10 * segments / 6)
|
|
local name = px >= green and "GREENBAR"
|
|
or px >= yellow and "YELLOWBAR" or "REDBAR"
|
|
local colors = PaletteFX.pal(data, name)
|
|
if colors then
|
|
local c = colors[3] -- GB color 2 is the fill shade
|
|
-- the fill pixels are the 2/3-gray shade; divide so they land on
|
|
-- the palette color exactly (the black outline stays black)
|
|
tint = { math.min(1, c[1] / 170), math.min(1, c[2] / 170),
|
|
math.min(1, c[3] / 170), 1 }
|
|
end
|
|
end
|
|
for i = 0, segments - 1 do
|
|
local seg = math.min(8, math.max(0, px - i * 8))
|
|
HudTiles.tile(seg >= 8 and 0x6B or 0x63 + seg, x + 16 + i * 8, y, tint)
|
|
end
|
|
HudTiles.tile(HudTiles.capTile(barType), x + 16 + segments * 8, y)
|
|
end
|
|
|
|
return HudTiles
|