mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-19 12:15:31 +02:00
Add optional extended widescreen battle HUD
This commit is contained in:
+92
-6
@@ -94,6 +94,7 @@ function Renderer:init()
|
||||
-- reason -- worldViewSize() already works in drawable pixels.
|
||||
self.uiWidth, self.uiHeight = self.WIDTH, self.HEIGHT
|
||||
self.canvas = PixelCanvas.new(self.uiWidth, self.uiHeight, "nearest")
|
||||
self.battleHUDCanvas = nil
|
||||
self.worldCanvas = nil
|
||||
self.worldActive = false
|
||||
-- tilt mode only: a transparent overlay canvas the size of the world
|
||||
@@ -202,10 +203,38 @@ function Renderer:setUISize(w, h)
|
||||
w, h = math.floor(w), math.floor(h)
|
||||
if w == self.uiWidth and h == self.uiHeight and self.canvas then return end
|
||||
if self.canvas and self.canvas.release then self.canvas:release() end
|
||||
if self.battleHUDCanvas and self.battleHUDCanvas.release then
|
||||
self.battleHUDCanvas:release()
|
||||
end
|
||||
self.battleHUDCanvas = nil
|
||||
self.uiWidth, self.uiHeight = w, h
|
||||
self.canvas = PixelCanvas.new(w, h, "nearest")
|
||||
end
|
||||
|
||||
-- Transparent native-pixel surface for an extended WIDE battle HUD. The
|
||||
-- battle scene remains in `canvas`; endFrame places registered HUD regions
|
||||
-- afterward in physical-window space.
|
||||
function Renderer:beginBattleHUDPass()
|
||||
local w, h = self:uiSize()
|
||||
if not self.battleHUDCanvas
|
||||
or self.battleHUDCanvas:getWidth() ~= w
|
||||
or self.battleHUDCanvas:getHeight() ~= h then
|
||||
if self.battleHUDCanvas and self.battleHUDCanvas.release then
|
||||
self.battleHUDCanvas:release()
|
||||
end
|
||||
self.battleHUDCanvas = PixelCanvas.new(w, h, "nearest")
|
||||
end
|
||||
local previous = love.graphics.getCanvas and love.graphics.getCanvas()
|
||||
or self.canvas
|
||||
love.graphics.setCanvas(self.battleHUDCanvas)
|
||||
love.graphics.clear(0, 0, 0, 0)
|
||||
return previous
|
||||
end
|
||||
|
||||
function Renderer:endBattleHUDPass(previous)
|
||||
love.graphics.setCanvas(previous or self.canvas)
|
||||
end
|
||||
|
||||
-- LOVE-unit draw scales endFrame uses for the UI blit: integer framebuffer
|
||||
-- scale (fitScale) divided by each axis's unit→pixel factor, so a GB pixel
|
||||
-- lands on fitScale() whole PHYSICAL pixels on both axes once LOVE applies
|
||||
@@ -671,6 +700,17 @@ end
|
||||
-- centred letterbox. Declared during the element's own draw, in UI-canvas
|
||||
-- pixels, and consumed by endFrame this frame only.
|
||||
-- anchor: "bottom" | "topright" | "topleft" | "bottomright"
|
||||
local function addUIAnchor(renderer, x, y, w, h, anchor, windowClamped,
|
||||
canvas, extract)
|
||||
renderer.uiAnchors = renderer.uiAnchors or {}
|
||||
renderer.uiAnchors[#renderer.uiAnchors + 1] = {
|
||||
x = x, y = y, w = w, h = h, anchor = anchor,
|
||||
windowClamped = windowClamped and true or false,
|
||||
canvas = canvas,
|
||||
extract = extract ~= false,
|
||||
}
|
||||
end
|
||||
|
||||
function Renderer:setUIAnchor(x, y, w, h, anchor)
|
||||
-- UI LAYOUT = CENTERED (uiCentered, set per frame by Game:draw from
|
||||
-- save.options.uiLayout): every element stays where it was drawn in the
|
||||
@@ -684,9 +724,16 @@ function Renderer:setUIAnchor(x, y, w, h, anchor)
|
||||
-- battle -- keeps every element inside it, so the box blits where it was
|
||||
-- drawn in the canvas instead of being pulled to the window edge.
|
||||
if self.uiAnchorHold then return end
|
||||
self.uiAnchors = self.uiAnchors or {}
|
||||
self.uiAnchors[#self.uiAnchors + 1] =
|
||||
{ x = x, y = y, w = w, h = h, anchor = anchor }
|
||||
addUIAnchor(self, x, y, w, h, anchor, false, self.canvas, true)
|
||||
end
|
||||
|
||||
-- Battle-owned window-space placement. Unlike ordinary UI anchors this is
|
||||
-- intentionally allowed while BattleState holds general dialogue/menu
|
||||
-- anchors inside the battle surface. Callers must gate it to an explicit
|
||||
-- battle HUD mode.
|
||||
function Renderer:setBattleUIAnchor(x, y, w, h, anchor)
|
||||
addUIAnchor(self, x, y, w, h, anchor, true,
|
||||
self.battleHUDCanvas or self.canvas, false)
|
||||
end
|
||||
|
||||
-- zones: optional list of SGB palette regions (see PaletteFX) in
|
||||
@@ -799,6 +846,8 @@ function Renderer:endFrame(zones, worldZones)
|
||||
-- is the pack's off-white (255,239,255), which a hardcoded 1,1,1 framed in
|
||||
-- a visibly brighter border.
|
||||
local clearR, clearG, clearB = 0, 0, 0
|
||||
local extendedBlackBand = false
|
||||
local bandR, bandG, bandB = 1, 1, 1
|
||||
if not self.worldActive then
|
||||
local ok, Game = pcall(require, "src.core.Game")
|
||||
local stack = ok and Game and Game.stack
|
||||
@@ -825,7 +874,15 @@ function Renderer:endFrame(zones, worldZones)
|
||||
-- screen stays black (src/core/FaithfulRes.lua); the paper surround
|
||||
-- painted the whole phone white on New Game and in battle (#864), so
|
||||
-- the lock keeps the default black bars.
|
||||
if state and state.letterboxWhite
|
||||
if state and state.extendedBlackHUD and state:extendedBlackHUD()
|
||||
and not FaithfulRes.scaleCap() then
|
||||
-- Extended/Black keeps the author's black surround, but extends the
|
||||
-- fixed battle's paper field vertically through the physical window.
|
||||
-- The band uses the exact centred fixed-width composition bounds, so
|
||||
-- only vertical black bars remain at the sides.
|
||||
extendedBlackBand = true
|
||||
bandR, bandG, bandB = PaletteFX.paperShade(Game and Game.data)
|
||||
elseif state and state.letterboxWhite
|
||||
and not (state.bgMode and state:bgMode() == "black")
|
||||
and not FaithfulRes.scaleCap() then
|
||||
clearR, clearG, clearB = PaletteFX.paperShade(Game and Game.data)
|
||||
@@ -833,6 +890,10 @@ function Renderer:endFrame(zones, worldZones)
|
||||
end
|
||||
love.graphics.setColor(clearR, clearG, clearB, 1)
|
||||
love.graphics.rectangle("fill", 0, 0, ww, wh)
|
||||
if extendedBlackBand then
|
||||
love.graphics.setColor(bandR, bandG, bandB, 1)
|
||||
love.graphics.rectangle("fill", uox, 0, uvpw, wh)
|
||||
end
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
-- render.letterbox: SGB borders / custom void art in the bars around the
|
||||
-- 160x144 (or world) blit. Drawn after the clear and before the game
|
||||
@@ -975,6 +1036,22 @@ function Renderer:endFrame(zones, worldZones)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
-- Extended/WORLD keeps the frozen world as the physical surround, but stock
|
||||
-- Gen 1 back sprites rely on the battle's paper shade for visible highlights.
|
||||
-- Back the exact fixed-width composition from physical top to bottom so only
|
||||
-- the left and right sides expose the world. The battle canvas and detached
|
||||
-- HUD remain transparent layers composited afterward.
|
||||
-- A worldOverride is an arena provider's completed scene (for example,
|
||||
-- StadiumBattleFX/Dramaless). It replaces the stock paper-backed battle
|
||||
-- field, so never cover it with the native back-sprite fallback.
|
||||
if self.extendedWorldBand and not self.worldOverride
|
||||
and not FaithfulRes.scaleCap() then
|
||||
local ok, Game = pcall(require, "src.core.Game")
|
||||
love.graphics.setColor(PaletteFX.paperShade(ok and Game and Game.data))
|
||||
love.graphics.rectangle("fill", uox, 0, uvpw, wh)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
-- UI: anchored regions against their screen edges, the rest in the classic
|
||||
-- centred letterbox. With nothing anchored this is the single blit it has
|
||||
-- always been.
|
||||
@@ -997,14 +1074,23 @@ function Renderer:endFrame(zones, worldZones)
|
||||
if a.anchor == "bottom" then
|
||||
dx = uox + a.x * Ux -- horizontally it stays with the letterbox
|
||||
dy = wh - gapB - dh
|
||||
elseif a.anchor == "top" then
|
||||
dx = uox + a.x * Ux -- horizontally it stays with the letterbox
|
||||
dy = a.y * Uy
|
||||
elseif a.anchor == "topright" then
|
||||
dx = ww - gapR - dw
|
||||
dy = a.y * Uy
|
||||
else -- unknown anchor: leave it where it is
|
||||
dx, dy = uox + a.x * Ux, uoy + a.y * Uy
|
||||
end
|
||||
if a.windowClamped then
|
||||
dx = math.max(0, math.min(math.max(0, ww - dw), dx))
|
||||
dy = math.max(0, math.min(math.max(0, wh - dh), dy))
|
||||
end
|
||||
placed[#placed + 1] = { a = a, dx = dx, dy = dy, dw = dw, dh = dh }
|
||||
rest = subtractRect(rest, uox + a.x * Ux, uoy + a.y * Uy, dw, dh)
|
||||
if a.extract then
|
||||
rest = subtractRect(rest, uox + a.x * Ux, uoy + a.y * Uy, dw, dh)
|
||||
end
|
||||
end
|
||||
for _, r in ipairs(rest) do
|
||||
blit(self.canvas, Ux, Uy, zones, Ux, Uy, uox, uoy, r[1], r[2], r[3], r[4])
|
||||
@@ -1013,7 +1099,7 @@ function Renderer:endFrame(zones, worldZones)
|
||||
-- shift the draw origin so canvas pixel (a.x, a.y) lands on (dx, dy).
|
||||
-- The zone scissors are computed from the same origin, so an SGB
|
||||
-- region travels with the element instead of staying in the letterbox.
|
||||
blit(self.canvas, Ux, Uy, zones, Ux, Uy,
|
||||
blit(p.a.canvas or self.canvas, Ux, Uy, zones, Ux, Uy,
|
||||
p.dx - p.a.x * Ux, p.dy - p.a.y * Uy, p.dx, p.dy, p.dw, p.dh)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user