mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 19:24:01 +02:00
Merge pull request #1378 from syybott/experiment/fixed-extended-world-alignment
This commit is contained in:
@@ -88,6 +88,33 @@ function BattleState:wantsFillScale()
|
||||
return options and options.battleFit == "fill" or false
|
||||
end
|
||||
|
||||
-- EXTENDED HUD configurations are admitted one at a time after their own
|
||||
-- placement and screenshot review. FIXED supports the three authored battle
|
||||
-- backgrounds; FILL uses one adaptive presentation stored as WHITE: stock
|
||||
-- battles retain the paper field required by Gen 1 back sprites, while arena
|
||||
-- providers may replace it with their own scene. Only the HUD moves to window
|
||||
-- space.
|
||||
function BattleState:extendedHUD()
|
||||
local options = self.game and self.game.save and self.game.save.options
|
||||
local bg = options and options.battleBg
|
||||
return self:wideLayout()
|
||||
and options and options.battleHud == "extended"
|
||||
and ((options.battleFit == "fixed"
|
||||
and (bg == "world" or bg == "white" or bg == "black"))
|
||||
or (options.battleFit == "fill" and bg == "white"))
|
||||
end
|
||||
|
||||
function BattleState:extendedWorldHUD()
|
||||
local options = self.game and self.game.save and self.game.save.options
|
||||
return self:extendedHUD() and options
|
||||
and options.battleFit == "fixed" and options.battleBg == "world"
|
||||
end
|
||||
|
||||
function BattleState:extendedBlackHUD()
|
||||
local options = self.game and self.game.save and self.game.save.options
|
||||
return self:extendedHUD() and options and options.battleBg == "black"
|
||||
end
|
||||
|
||||
-- BATTLE BG: what fills the screen AROUND the battle -- the letterbox voids
|
||||
-- that grow as the window gets bigger or the view is zoomed out. The battle
|
||||
-- screen itself is untouched: it keeps its white paper field in every mode.
|
||||
|
||||
@@ -92,6 +92,26 @@ local function levelAt(battle, battler, x, y)
|
||||
end
|
||||
end
|
||||
|
||||
local function battleIsTopState(battle)
|
||||
local stack = battle.game and battle.game.stack
|
||||
return not (stack and stack.top) or stack:top() == battle
|
||||
end
|
||||
|
||||
local function anchorHUD(battle, x, y, w, h, anchor)
|
||||
if not battle:extendedHUD() or not battleIsTopState(battle) then return end
|
||||
local renderer = battle.game and battle.game.renderer
|
||||
if not (renderer and renderer.setBattleUIAnchor) then return end
|
||||
x = x + (battle.extendedHUDOffsetX or 0)
|
||||
y = y + (battle.extendedHUDOffsetY or 0)
|
||||
local x2 = math.min(WideBattle.WIDTH, x + w)
|
||||
local y2 = math.min(WideBattle.HEIGHT, y + h)
|
||||
x, y = math.max(0, x), math.max(0, y)
|
||||
w, h = x2 - x, y2 - y
|
||||
if w > 0 and h > 0 then
|
||||
renderer:setBattleUIAnchor(x, y, w, h, anchor)
|
||||
end
|
||||
end
|
||||
|
||||
-- One side's status box: name and level on the first line, a long HP bar
|
||||
-- under it, and the numeric HP on the player's box only (the foe's exact
|
||||
-- HP is never shown, like the original).
|
||||
@@ -114,6 +134,7 @@ local function drawStatusPanel(battle, battler, x, y, player)
|
||||
Font.draw(("%3d/%3d"):format(shownHP(battler), battler.mon.stats.hp),
|
||||
x + tw * 8 - 64, y + 24)
|
||||
end
|
||||
anchorHUD(battle, x, y, tw * 8, th * 8, player and "bottom" or "top")
|
||||
end
|
||||
|
||||
-- the party ball rows DrawAllPokeballs puts up with the intro text, moved
|
||||
@@ -144,7 +165,6 @@ local function drawHUDs(battle, slide)
|
||||
and not battle.showPlayerBack and slide == 0 then
|
||||
drawStatusPanel(battle, battle.player, 184, 56, true)
|
||||
end
|
||||
drawIntroBalls(battle)
|
||||
end
|
||||
|
||||
local function drawMessageBox(battle)
|
||||
@@ -268,6 +288,8 @@ local function drawTextArea(battle)
|
||||
else
|
||||
Font.drawBox(0, 13, 38, 5)
|
||||
end
|
||||
anchorHUD(battle, 0, WideBattle.FIELD_BOTTOM,
|
||||
WideBattle.WIDTH, WideBattle.HEIGHT - WideBattle.FIELD_BOTTOM, "bottom")
|
||||
end
|
||||
|
||||
-- Battle animations are authored in the original 160px coordinate space.
|
||||
@@ -309,17 +331,23 @@ end
|
||||
-- The whole 304x144 composition for one frame.
|
||||
function WideBattle.draw(battle)
|
||||
local g = love.graphics
|
||||
local renderer = battle.game and battle.game.renderer
|
||||
local extendedHUD = battle:extendedHUD() and renderer
|
||||
and renderer.beginBattleHUDPass
|
||||
and renderer.endBattleHUDPass
|
||||
-- The field is the display mode's paper. Under a forced-mono mode the
|
||||
-- whole surface is remapped downstream (WideBattle.zones), so the field
|
||||
-- goes down as DMG white and comes out of that pass as the mode's paper;
|
||||
-- painting the resolved shade there would run it through the remap twice
|
||||
-- and land a shade off the letterbox the renderer fills around it.
|
||||
if monoMode() then
|
||||
g.setColor(1, 1, 1, 1)
|
||||
else
|
||||
g.setColor(PaletteFX.paperShade(battle.data))
|
||||
if not (extendedHUD and battle:extendedWorldHUD()) then
|
||||
if monoMode() then
|
||||
g.setColor(1, 1, 1, 1)
|
||||
else
|
||||
g.setColor(PaletteFX.paperShade(battle.data))
|
||||
end
|
||||
g.rectangle("fill", 0, 0, WideBattle.WIDTH, WideBattle.HEIGHT)
|
||||
end
|
||||
g.rectangle("fill", 0, 0, WideBattle.WIDTH, WideBattle.HEIGHT)
|
||||
-- AskName clears the field the same way the classic layout does
|
||||
if battle.blankForAskName then return end
|
||||
|
||||
@@ -346,6 +374,7 @@ function WideBattle.draw(battle)
|
||||
inRegion(160 + sx, sy, 144, WideBattle.FIELD_BOTTOM, 136 + sx, sy,
|
||||
function() battle:drawPicsLayer(slide, 0, 0, "enemy", true) end)
|
||||
battle.wideRegion = nil
|
||||
drawIntroBalls(battle)
|
||||
|
||||
-- A battle sets rWY to 0 (engine/battle/core.asm), so the window the
|
||||
-- shakes move IS the whole screen: PredefShakeScreenHorizontally,
|
||||
@@ -359,12 +388,26 @@ function WideBattle.draw(battle)
|
||||
if sx == 0 and sy == 0 then return fn() end
|
||||
g.push()
|
||||
g.translate(sx, sy)
|
||||
battle.extendedHUDOffsetX, battle.extendedHUDOffsetY = sx, sy
|
||||
fn()
|
||||
battle.extendedHUDOffsetX, battle.extendedHUDOffsetY = nil, nil
|
||||
g.pop()
|
||||
end
|
||||
shaken(function() drawHUDs(battle, slide) end)
|
||||
drawAnimationLayer(battle)
|
||||
shaken(function() drawTextArea(battle) end)
|
||||
|
||||
if extendedHUD then
|
||||
local previous = renderer:beginBattleHUDPass()
|
||||
shaken(function() drawHUDs(battle, slide) end)
|
||||
shaken(function() drawTextArea(battle) end)
|
||||
if fx and fx.flash and fx.flash > 0 and battle.frame % 4 < 2 then
|
||||
g.setColor(1, 1, 1, 0.85)
|
||||
g.rectangle("fill", 0, 0, WideBattle.WIDTH, WideBattle.HEIGHT)
|
||||
end
|
||||
renderer:endBattleHUDPass(previous)
|
||||
else
|
||||
shaken(function() drawHUDs(battle, slide) end)
|
||||
shaken(function() drawTextArea(battle) end)
|
||||
end
|
||||
|
||||
if fx and fx.flash and fx.flash > 0 and battle.frame % 4 < 2 then
|
||||
g.setColor(1, 1, 1, 0.85)
|
||||
|
||||
+44
-10
@@ -306,12 +306,32 @@ function Game.worldBgBattleDim(stack)
|
||||
for i = #(stack and stack.states or {}), 1, -1 do
|
||||
local state = stack.states[i]
|
||||
if state and state.bgMode and state:bgMode() == "world" then
|
||||
-- The extended fixed HUD intentionally exposes the live world across
|
||||
-- the whole physical window. Keep this as a world-backed battle (zero
|
||||
-- is non-nil, so scaling and overlay holds remain active), but do not
|
||||
-- paint the standard dim veil around the native battle rectangle.
|
||||
if state.extendedWorldHUD and state:extendedWorldHUD() then
|
||||
return 0
|
||||
end
|
||||
return state.BG_WORLD_DIM or 0.55
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Does the stack contain the opt-in fixed Extended WORLD battle? Renderer
|
||||
-- uses this separately from battleDim: the world remains the surround, while
|
||||
-- the native-width battle field receives a paper backing from top to bottom.
|
||||
function Game.extendedWorldHUDInStack(stack)
|
||||
for i = #(stack and stack.states or {}), 1, -1 do
|
||||
local state = stack.states[i]
|
||||
if state and state.extendedWorldHUD and state:extendedWorldHUD() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Is a BATTLE BG "world" battle composing itself over the live map right now?
|
||||
-- Same whole-stack walk as worldBgBattleDim, asked for a different reason: the
|
||||
-- dark-cave shade shift (wMapPalOffset) must not reach a frame a battle is
|
||||
@@ -404,13 +424,13 @@ function Game.uiAnchorsHeldInStack(stack)
|
||||
end
|
||||
|
||||
-- Where Game:draw starts drawing this frame. Normally the topmost opaque
|
||||
-- state (StateStack:visibleBase) -- but BATTLE BG "world" composes the battle
|
||||
-- over the LIVE map, and an opaque state pushed on top of it (the party menu,
|
||||
-- the bag) becomes that base, cutting the overworld -- and with it the world
|
||||
-- pass -- out of the frame entirely. The backdrop the battle established
|
||||
-- then collapses to endFrame's flat black clear for as long as the menu is
|
||||
-- up. So a world-bg battle keeps the frame starting from underneath itself
|
||||
-- until it leaves the stack, the same hold uiFill and the dim already use.
|
||||
-- state (StateStack:visibleBase) -- but an opaque menu pushed over a WIDE
|
||||
-- battle must not prevent that battle from drawing. Native WIDE battles own
|
||||
-- the 304x144 surround around a centred classic menu, while external arena
|
||||
-- providers establish their window-sized scene from BattleState:draw. If the
|
||||
-- menu becomes the draw base, neither owner runs and the menu's white field
|
||||
-- replaces the whole presentation. BATTLE BG "world" additionally needs the
|
||||
-- overworld below the battle, as before.
|
||||
--
|
||||
-- Only the START of the draw moves. The clear stays keyed to the real
|
||||
-- visibleBase, so the menu still gets its opaque canvas and draws exactly as
|
||||
@@ -421,9 +441,13 @@ function Game.drawBaseInStack(stack, visibleBase)
|
||||
local states = stack and stack.states or {}
|
||||
for i = visibleBase - 1, 1, -1 do
|
||||
local state = states[i]
|
||||
if state and state.bgMode and state:bgMode() == "world" then
|
||||
local worldBattle = state and state.bgMode and state:bgMode() == "world"
|
||||
local wideBattle = state and state.isWideBattleLayout
|
||||
and state:isWideBattleLayout()
|
||||
if worldBattle or wideBattle then
|
||||
-- restart the search from under the battle: the highest opaque state at
|
||||
-- or below it (the overworld), not the menu sitting over it
|
||||
-- or below it (the battle itself for white/black WIDE, the overworld for
|
||||
-- a non-opaque world-backed battle), not the menu sitting over it
|
||||
for j = i, 1, -1 do
|
||||
if states[j].isOpaque then return j end
|
||||
end
|
||||
@@ -433,6 +457,14 @@ function Game.drawBaseInStack(stack, visibleBase)
|
||||
return visibleBase
|
||||
end
|
||||
|
||||
-- A classic overlay above the approved world-backed extended HUD paints only
|
||||
-- its centred area. Keep the wider owner surface transparent so its margins
|
||||
-- continue to reveal the world instead of becoming an opaque white sheet.
|
||||
function Game.uiCanvasTransparent(worldBelow, worldDrawn, wideBattle)
|
||||
return worldBelow or (worldDrawn and wideBattle ~= nil
|
||||
and wideBattle.extendedHUD and wideBattle:extendedHUD())
|
||||
end
|
||||
|
||||
-- Shift classic SGB zones to the centred UI. A full-width base zone extends
|
||||
-- into both margins, keeping the canvas' paper color continuous; narrower
|
||||
-- sprite and status zones move with the classic UI content.
|
||||
@@ -487,6 +519,7 @@ function Game:draw()
|
||||
-- the stack for the same reason as uiFill above -- a prompt opened during
|
||||
-- the battle must not drop the dim for a frame.
|
||||
Renderer.battleDim = Game.worldBgBattleDim(self.stack)
|
||||
Renderer.extendedWorldBand = Game.extendedWorldHUDInStack(self.stack)
|
||||
-- ...and for the same reason the UI's own scale has to know the world is
|
||||
-- still the backdrop while an opaque menu covers it. Renderer:uiScale
|
||||
-- steps the UI down with the survey zoom only while a world is behind it,
|
||||
@@ -504,7 +537,8 @@ function Game:draw()
|
||||
-- menu to its top right, and the whole UI steps down with the zoom.
|
||||
Renderer.uiCentered = not Game.dynamicUI(self.save)
|
||||
Renderer.uiAnchorHold = Game.uiAnchorsHeldInStack(self.stack)
|
||||
Renderer:beginFrame(worldBelow)
|
||||
Renderer:beginFrame(Game.uiCanvasTransparent(
|
||||
worldBelow, worldDrawn, wideBattle))
|
||||
for i = drawFrom, #self.stack.states do
|
||||
local state = self.stack.states[i]
|
||||
local wideState = state and state.isWideBattleLayout
|
||||
|
||||
@@ -239,6 +239,11 @@ function SaveData.defaultOptions()
|
||||
-- scale the battle surface to the window so it fills vertically. See
|
||||
-- BattleState:wantsFillScale.
|
||||
battleFit = "fixed",
|
||||
-- BATTLE HUD: STANDARD keeps every wide-battle element inside the native
|
||||
-- 304x144 surface. EXTENDED is opt-in window-space placement for selected
|
||||
-- wide layouts; unsupported combinations deliberately fall back to the
|
||||
-- standard composition.
|
||||
battleHud = "standard",
|
||||
-- BATTLE BG: what fills the screen behind and around the battle.
|
||||
-- "white" = the display mode's paper shade (the classic look),
|
||||
-- "black" = plain black bars, "world" = the frozen overworld showing
|
||||
|
||||
@@ -138,15 +138,72 @@ local function coreRows(opts, hooks)
|
||||
ladder(opts, "battleStyle",
|
||||
{ { "shift", "SHIFT" }, { "set", "SET" } }, "shift"))
|
||||
add(Strings("BATTLE LAYOUT"),
|
||||
ladder(opts, "battleLayout",
|
||||
{ { "og", "OG" }, { "wide", "WIDE" } }, "og"))
|
||||
function()
|
||||
return opts.battleLayout == "wide" and Strings("WIDE") or Strings("OG")
|
||||
end,
|
||||
function()
|
||||
opts.battleLayout = opts.battleLayout == "wide" and "og" or "wide"
|
||||
if opts.battleLayout ~= "wide" then
|
||||
opts.battleHud = "standard"
|
||||
elseif opts.battleFit == "fill" and opts.battleHud == "extended" then
|
||||
opts.battleBg = "white"
|
||||
end
|
||||
return true
|
||||
end)
|
||||
add(Strings("BATTLE SIZE"),
|
||||
ladder(opts, "battleFit",
|
||||
{ { "fixed", "FIXED" }, { "fill", "FILL" } }, "fixed"))
|
||||
function()
|
||||
return opts.battleFit == "fill" and Strings("FILL") or Strings("FIXED")
|
||||
end,
|
||||
function()
|
||||
opts.battleFit = opts.battleFit == "fill" and "fixed" or "fill"
|
||||
if opts.battleFit == "fill" and opts.battleLayout == "wide"
|
||||
and opts.battleHud == "extended" then
|
||||
opts.battleBg = "white"
|
||||
end
|
||||
return true
|
||||
end)
|
||||
add(Strings("BATTLE HUD"),
|
||||
function()
|
||||
return opts.battleLayout == "wide" and opts.battleHud == "extended"
|
||||
and Strings("EXTENDED")
|
||||
or Strings("STANDARD")
|
||||
end,
|
||||
function()
|
||||
if opts.battleLayout ~= "wide" then
|
||||
opts.battleHud = "standard"
|
||||
return false
|
||||
end
|
||||
opts.battleHud = opts.battleHud == "extended" and "standard" or "extended"
|
||||
if opts.battleHud == "extended" and opts.battleFit == "fill" then
|
||||
opts.battleBg = "white"
|
||||
end
|
||||
return true
|
||||
end)
|
||||
add(Strings("BATTLE BG"),
|
||||
ladder(opts, "battleBg",
|
||||
{ { "white", "WHITE" }, { "black", "BLACK" }, { "world", "WORLD" } },
|
||||
"white"))
|
||||
function()
|
||||
if opts.battleLayout == "wide" and opts.battleFit == "fill"
|
||||
and opts.battleHud == "extended" then
|
||||
opts.battleBg = "white"
|
||||
return Strings("AUTO")
|
||||
end
|
||||
if opts.battleBg == "black" then return Strings("BLACK") end
|
||||
if opts.battleBg == "world" then return Strings("WORLD") end
|
||||
return Strings("WHITE")
|
||||
end,
|
||||
function(dir)
|
||||
if opts.battleLayout == "wide" and opts.battleFit == "fill"
|
||||
and opts.battleHud == "extended" then
|
||||
opts.battleBg = "white"
|
||||
return false
|
||||
end
|
||||
local order = { "white", "black", "world" }
|
||||
local cur = 1
|
||||
for i, mode in ipairs(order) do
|
||||
if opts.battleBg == mode then cur = i break end
|
||||
end
|
||||
opts.battleBg = order[wrapIndex(cur - 1 + (dir or 1), #order) + 1]
|
||||
return true
|
||||
end)
|
||||
add(Strings("UI LAYOUT"),
|
||||
ladder(opts, "uiLayout",
|
||||
{ { "centered", "CENTERED" }, { "dynamic", "DYNAMIC" } }, "centered"))
|
||||
|
||||
+92
-6
@@ -93,6 +93,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
|
||||
@@ -201,10 +202,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
|
||||
@@ -670,6 +699,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
|
||||
@@ -683,9 +723,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
|
||||
@@ -798,6 +845,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
|
||||
@@ -824,7 +873,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)
|
||||
@@ -832,6 +889,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
|
||||
@@ -974,6 +1035,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.
|
||||
@@ -996,14 +1073,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])
|
||||
@@ -1012,7 +1098,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
|
||||
|
||||
+42
-1
@@ -180,6 +180,11 @@ local function buildRows(game)
|
||||
step = function(g)
|
||||
local o = g.save.options
|
||||
o.battleLayout = o.battleLayout == "wide" and "og" or "wide"
|
||||
if o.battleLayout ~= "wide" then
|
||||
o.battleHud = "standard"
|
||||
elseif o.battleFit == "fill" and o.battleHud == "extended" then
|
||||
o.battleBg = "white"
|
||||
end
|
||||
return true
|
||||
end },
|
||||
-- FIXED keeps the classic integer-scaled letterbox -- a GB pixel is a
|
||||
@@ -195,6 +200,31 @@ local function buildRows(game)
|
||||
step = function(g)
|
||||
local o = g.save.options
|
||||
o.battleFit = o.battleFit == "fill" and "fixed" or "fill"
|
||||
if o.battleFit == "fill" and o.battleLayout == "wide"
|
||||
and o.battleHud == "extended" then
|
||||
o.battleBg = "white"
|
||||
end
|
||||
return true
|
||||
end },
|
||||
{ id = "battleHud", label = Strings("BATTLE HUD"),
|
||||
value = function(g)
|
||||
local o = g.save.options
|
||||
return o.battleLayout == "wide" and o.battleHud == "extended"
|
||||
and Strings("EXTENDED")
|
||||
or Strings("STANDARD")
|
||||
end,
|
||||
step = function(g)
|
||||
local o = g.save.options
|
||||
-- The extended HUD is a widescreen-only composition. Keep OG locked
|
||||
-- to the author's standard HUD even if an older save says otherwise.
|
||||
if o.battleLayout ~= "wide" then
|
||||
o.battleHud = "standard"
|
||||
return false
|
||||
end
|
||||
o.battleHud = o.battleHud == "extended" and "standard" or "extended"
|
||||
if o.battleHud == "extended" and o.battleFit == "fill" then
|
||||
o.battleBg = "white"
|
||||
end
|
||||
return true
|
||||
end },
|
||||
-- What sits behind and around the battle. WHITE is the classic paper
|
||||
@@ -203,13 +233,24 @@ local function buildRows(game)
|
||||
-- shows through everywhere the battle does not paint).
|
||||
{ id = "battleBg", label = Strings("BATTLE BG"),
|
||||
value = function(g)
|
||||
local m = g.save.options.battleBg
|
||||
local o = g.save.options
|
||||
if o.battleLayout == "wide" and o.battleFit == "fill"
|
||||
and o.battleHud == "extended" then
|
||||
o.battleBg = "white"
|
||||
return Strings("AUTO")
|
||||
end
|
||||
local m = o.battleBg
|
||||
if m == "black" then return Strings("BLACK") end
|
||||
if m == "world" then return Strings("WORLD") end
|
||||
return Strings("WHITE")
|
||||
end,
|
||||
step = function(g, dir)
|
||||
local o = g.save.options
|
||||
if o.battleLayout == "wide" and o.battleFit == "fill"
|
||||
and o.battleHud == "extended" then
|
||||
o.battleBg = "white"
|
||||
return false
|
||||
end
|
||||
local order = { "white", "black", "world" }
|
||||
local cur = 1
|
||||
for i, m in ipairs(order) do if o.battleBg == m then cur = i break end end
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local OUT = os.getenv("SHOT_DIR") or "battle-hud-layout-lock"
|
||||
local BEFORE = OUT .. "/battle_hud_wide_extended.png"
|
||||
local AFTER = OUT .. "/battle_hud_og_locked_standard.png"
|
||||
|
||||
return function(game)
|
||||
os.remove(BEFORE)
|
||||
os.remove(AFTER)
|
||||
|
||||
local options = game.save.options
|
||||
options.battleLayout = "wide"
|
||||
options.battleHud = "extended"
|
||||
|
||||
local menu = require("src.ui.Screens").push(game, "OptionsMenu")
|
||||
local layoutRow, hudRow
|
||||
for _, row in ipairs(menu.rows) do
|
||||
if row.id == "battleLayout" then layoutRow = row end
|
||||
if row.id == "battleHud" then hudRow = row end
|
||||
end
|
||||
assert(layoutRow and hudRow, "battle layout/HUD rows are present")
|
||||
|
||||
menu.index = 6
|
||||
menu.scroll = 3
|
||||
assert(hudRow.value(game) == "EXTENDED", "WIDE displays EXTENDED")
|
||||
assert(U.shot(game, BEFORE), "WIDE/EXTENDED screenshot was written")
|
||||
|
||||
layoutRow.step(game, 1)
|
||||
assert(options.battleLayout == "og", "layout switched to OG")
|
||||
assert(options.battleHud == "standard", "OG normalized HUD to STANDARD")
|
||||
assert(hudRow.value(game) == "STANDARD", "OG displays STANDARD")
|
||||
assert(U.shot(game, AFTER), "OG/STANDARD screenshot was written")
|
||||
|
||||
print("[driver] BATTLE_HUD_LAYOUT_LOCK_PASS")
|
||||
game.driverDone = true
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
-- Visual and behavioral acceptance for the adaptive BATTLE BG menu rule.
|
||||
return function(game)
|
||||
local U = dofile("tests/drivers/util.lua")
|
||||
local DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
||||
|
||||
love.window.setMode(1920, 1080, { resizable = true })
|
||||
U.wait(3)
|
||||
|
||||
local options = game.save.options
|
||||
options.battleLayout = "wide"
|
||||
options.battleFit = "fixed"
|
||||
options.battleHud = "extended"
|
||||
options.battleBg = "black"
|
||||
|
||||
local menu = require("src.ui.Screens").push(game, "OptionsMenu")
|
||||
local fitRow, bgRow
|
||||
local bgIndex
|
||||
for i, row in ipairs(menu.rows) do
|
||||
if row.id == "battleFit" then fitRow = row end
|
||||
if row.id == "battleBg" then bgRow, bgIndex = row, i end
|
||||
end
|
||||
assert(fitRow and bgRow and bgIndex, "battle size/background rows are present")
|
||||
|
||||
fitRow.step(game, 1)
|
||||
assert(options.battleFit == "fill", "battle size switched to FILL")
|
||||
assert(options.battleBg == "white", "FILL + EXTENDED normalized background to WHITE")
|
||||
assert(bgRow.value(game) == "AUTO", "adaptive background is labeled AUTO")
|
||||
assert(bgRow.step(game, 1) == false, "AUTO background row is locked")
|
||||
assert(options.battleBg == "white", "locked AUTO retains the WHITE value")
|
||||
|
||||
menu.index = bgIndex
|
||||
menu.scroll = math.max(0, bgIndex - 5)
|
||||
U.wait(2)
|
||||
local autoPath = DIR .. "/fill_extended_auto_menu.png"
|
||||
os.remove(autoPath)
|
||||
local ok = U.shot(game, autoPath)
|
||||
|
||||
fitRow.step(game, -1)
|
||||
assert(options.battleFit == "fixed", "battle size switched back to FIXED")
|
||||
assert(bgRow.value(game) == "WHITE", "FIXED exposes the stored WHITE choice")
|
||||
assert(bgRow.step(game, 1) == true and options.battleBg == "black",
|
||||
"FIXED can select BLACK")
|
||||
assert(bgRow.step(game, 1) == true and options.battleBg == "world",
|
||||
"FIXED can select WORLD")
|
||||
U.wait(2)
|
||||
local fixedPath = DIR .. "/fixed_extended_background_choices.png"
|
||||
os.remove(fixedPath)
|
||||
ok = U.shot(game, fixedPath) and ok
|
||||
|
||||
U.log(ok and "FILL_EXTENDED_AUTO_MENU_PASS"
|
||||
or "FILL_EXTENDED_AUTO_MENU_FAIL")
|
||||
love.event.quit(ok and 0 or 1)
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
-- Visual acceptance driver for WIDE + FILL + EXTENDED + WHITE.
|
||||
-- The full physical-window backing remains white while the four battle HUD
|
||||
-- panels move to their approved window anchors.
|
||||
return function(game)
|
||||
local U = dofile("tests/drivers/util.lua")
|
||||
local DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
||||
local BattleState = require("src.battle.BattleState")
|
||||
local Pokemon = require("src.pokemon.Pokemon")
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(3)
|
||||
|
||||
local options = game.save.options
|
||||
options.battleLayout = "wide"
|
||||
options.battleFit = "fill"
|
||||
options.battleHud = "extended"
|
||||
options.battleBg = "white"
|
||||
|
||||
game.save.party = { Pokemon.new(game.data, "PIKACHU", 100) }
|
||||
U.teleport(game, "ROUTE_1", 5, 5, "down")
|
||||
U.wait(60)
|
||||
|
||||
local battle = BattleState.newWild(game, "PIDGEY", 3,
|
||||
{ onFinish = function() end })
|
||||
game.overworld:pushBattle(battle)
|
||||
U.wait(360)
|
||||
|
||||
battle.introSlide = 0
|
||||
battle.introBalls = nil
|
||||
battle.showEnemyTrainer = false
|
||||
battle.showPlayerBack = false
|
||||
battle.enemySendingOut = false
|
||||
battle.sendingOut = false
|
||||
battle.phase = "menu"
|
||||
battle.menuIndex = 1
|
||||
U.wait(2)
|
||||
|
||||
assert(battle:extendedHUD(), "FILL/WHITE activates the approved extended HUD")
|
||||
assert(not battle:extendedWorldHUD(), "FILL/WHITE does not use FIXED's paper band")
|
||||
|
||||
local path = DIR .. "/fill_extended_white_separate_layer.png"
|
||||
os.remove(path)
|
||||
local ok = U.shot(game, path)
|
||||
|
||||
love.window.setMode(960, 540, { resizable = true })
|
||||
U.wait(5)
|
||||
local smallPath = DIR .. "/fill_extended_white_small_16x9.png"
|
||||
os.remove(smallPath)
|
||||
ok = U.shot(game, smallPath) and ok
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(5)
|
||||
local NamingScreen = require("src.ui.NamingScreen")
|
||||
game.stack:push(NamingScreen.new(game, {
|
||||
title = "NICKNAME?", maxLen = 10, onDone = function() end,
|
||||
}))
|
||||
U.wait(5)
|
||||
local overlayPath = DIR .. "/fill_extended_white_naming_overlay.png"
|
||||
os.remove(overlayPath)
|
||||
ok = U.shot(game, overlayPath) and ok
|
||||
|
||||
U.log(ok and "FILL_EXTENDED_WHITE_PASS" or "FILL_EXTENDED_WHITE_FAIL")
|
||||
love.event.quit(ok and 0 or 1)
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local OUT = os.getenv("SHOT_DIR") or "fixed-extended-black"
|
||||
local FULL = OUT .. "/fixed_extended_black_full.png"
|
||||
local SMALL = OUT .. "/fixed_extended_black_small_16x9.png"
|
||||
local OVERLAY = OUT .. "/fixed_extended_black_overlay.png"
|
||||
|
||||
return function(game)
|
||||
local BattleState = require("src.battle.BattleState")
|
||||
local Pokemon = require("src.pokemon.Pokemon")
|
||||
|
||||
os.remove(FULL)
|
||||
os.remove(SMALL)
|
||||
os.remove(OVERLAY)
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(3)
|
||||
|
||||
local options = game.save.options
|
||||
options.battleLayout = "wide"
|
||||
options.battleFit = "fixed"
|
||||
options.battleHud = "extended"
|
||||
options.battleBg = "black"
|
||||
|
||||
game.save.party = { Pokemon.new(game.data, "PIKACHU", 100) }
|
||||
U.teleport(game, "ROUTE_1", 5, 5, "down")
|
||||
U.wait(60)
|
||||
|
||||
local battle = BattleState.newWild(game, "PIDGEY", 3,
|
||||
{ onFinish = function() end })
|
||||
game.overworld:pushBattle(battle)
|
||||
U.wait(360)
|
||||
battle.introSlide = 0
|
||||
battle.introBalls = nil
|
||||
battle.showEnemyTrainer = false
|
||||
battle.showPlayerBack = false
|
||||
battle.enemySendingOut = false
|
||||
battle.sendingOut = false
|
||||
battle.phase = "menu"
|
||||
battle.menuIndex = 1
|
||||
U.wait(2)
|
||||
|
||||
assert(battle:extendedHUD(), "BLACK activates the approved extended HUD")
|
||||
assert(battle:extendedBlackHUD(), "BLACK activates the white vertical battle band")
|
||||
assert(not battle:extendedWorldHUD(), "BLACK remains separate from WORLD")
|
||||
assert(U.shot(game, FULL), "full black-background screenshot was written")
|
||||
|
||||
love.window.setMode(960, 540, { resizable = true })
|
||||
U.wait(10)
|
||||
assert(U.shot(game, SMALL), "small black-background screenshot was written")
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(10)
|
||||
battle.blankForAskName = true
|
||||
local naming = require("src.ui.NamingScreen").new(game, {
|
||||
title = "NICKNAME?", maxLen = 10, onDone = function() end,
|
||||
})
|
||||
game.stack:push(naming)
|
||||
U.wait(10)
|
||||
assert(U.shot(game, OVERLAY), "black-background overlay screenshot was written")
|
||||
|
||||
print("[driver] FIXED_EXTENDED_BLACK_PASS")
|
||||
love.event.quit(0)
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
local U = require("tests.drivers.util")
|
||||
|
||||
local OUT = os.getenv("SHOT_DIR") or "fixed-extended-white"
|
||||
local FULL = OUT .. "/fixed_extended_white_full.png"
|
||||
local SMALL = OUT .. "/fixed_extended_white_small_16x9.png"
|
||||
local OVERLAY = OUT .. "/fixed_extended_white_overlay.png"
|
||||
|
||||
return function(game)
|
||||
local BattleState = require("src.battle.BattleState")
|
||||
local Pokemon = require("src.pokemon.Pokemon")
|
||||
|
||||
os.remove(FULL)
|
||||
os.remove(SMALL)
|
||||
os.remove(OVERLAY)
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(3)
|
||||
|
||||
local options = game.save.options
|
||||
options.battleLayout = "wide"
|
||||
options.battleFit = "fixed"
|
||||
options.battleHud = "extended"
|
||||
options.battleBg = "white"
|
||||
|
||||
game.save.party = { Pokemon.new(game.data, "PIKACHU", 100) }
|
||||
U.teleport(game, "ROUTE_1", 5, 5, "down")
|
||||
U.wait(60)
|
||||
|
||||
local battle = BattleState.newWild(game, "PIDGEY", 3,
|
||||
{ onFinish = function() end })
|
||||
game.overworld:pushBattle(battle)
|
||||
U.wait(360)
|
||||
battle.introSlide = 0
|
||||
battle.introBalls = nil
|
||||
battle.showEnemyTrainer = false
|
||||
battle.showPlayerBack = false
|
||||
battle.enemySendingOut = false
|
||||
battle.sendingOut = false
|
||||
battle.phase = "menu"
|
||||
battle.menuIndex = 1
|
||||
U.wait(2)
|
||||
assert(battle:extendedHUD(), "WHITE activates the approved extended HUD")
|
||||
assert(not battle:extendedWorldHUD(), "WHITE keeps its opaque paper field")
|
||||
assert(U.shot(game, FULL), "full WHITE screenshot was written")
|
||||
|
||||
love.window.setMode(960, 540, { resizable = true })
|
||||
U.wait(10)
|
||||
assert(U.shot(game, SMALL), "small WHITE screenshot was written")
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(10)
|
||||
battle.blankForAskName = true
|
||||
local naming = require("src.ui.NamingScreen").new(game, {
|
||||
title = "NICKNAME?",
|
||||
maxLen = 10,
|
||||
initial = "",
|
||||
onDone = function() end,
|
||||
})
|
||||
game.stack:push(naming)
|
||||
U.wait(10)
|
||||
assert(U.shot(game, OVERLAY), "WHITE overlay screenshot was written")
|
||||
|
||||
print("[driver] FIXED_EXTENDED_WHITE_PASS")
|
||||
love.event.quit(0)
|
||||
end
|
||||
@@ -0,0 +1,44 @@
|
||||
-- Visual regression coverage for Professor Oak's scripted Yellow capture Bag
|
||||
-- over the WIDE + FIXED + EXTENDED + WORLD composition.
|
||||
return function(game)
|
||||
local U = dofile("tests/drivers/util.lua")
|
||||
local DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
||||
local BattleState = require("src.battle.BattleState")
|
||||
local Pokemon = require("src.pokemon.Pokemon")
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(3)
|
||||
|
||||
local options = game.save.options
|
||||
options.battleLayout = "wide"
|
||||
options.battleFit = "fixed"
|
||||
options.battleHud = "extended"
|
||||
options.battleBg = "world"
|
||||
|
||||
game.save.party = { Pokemon.new(game.data, "PIKACHU", 20) }
|
||||
U.teleport(game, "ROUTE_1", 5, 5, "down")
|
||||
U.wait(30)
|
||||
|
||||
local demo = BattleState.newWild(game, "CHARMANDER", 5)
|
||||
demo:makeOldManDemo("PROF.OAK")
|
||||
demo.onFinish = function() end
|
||||
game.overworld:pushBattle(demo)
|
||||
|
||||
for _ = 1, 100 do
|
||||
if demo.phase == "menu" and (demo.demoTimer or 0) > 5 then break end
|
||||
U.tap(game, "a")
|
||||
U.wait(4)
|
||||
end
|
||||
for _ = 1, 180 do
|
||||
if game.stack:top() ~= demo then break end
|
||||
U.wait(1)
|
||||
end
|
||||
U.wait(3)
|
||||
|
||||
local path = DIR .. "/fixed_extended_world_oak_charmander_bag.png"
|
||||
os.remove(path)
|
||||
local ok = game.stack:top() ~= demo and U.shot(game, path)
|
||||
U.log(ok and "FIXED_EXTENDED_WORLD_BAG_PASS"
|
||||
or "FIXED_EXTENDED_WORLD_BAG_FAIL")
|
||||
love.event.quit(ok and 0 or 1)
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
-- Visual acceptance driver for the first EXTENDED HUD configuration only:
|
||||
-- WIDE + FIXED + EXTENDED + WORLD.
|
||||
-- POKEPORT_DRIVER=tests/drivers/fixed_extended_world_hud_test.lua \
|
||||
-- POKEPORT_IDENTITY=fixed-extended-world POKEPORT_TOUCH=0 \
|
||||
-- SHOT_DIR=/tmp/shots love .
|
||||
return function(game)
|
||||
local U = dofile("tests/drivers/util.lua")
|
||||
local DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
||||
local BattleState = require("src.battle.BattleState")
|
||||
local Pokemon = require("src.pokemon.Pokemon")
|
||||
|
||||
-- Match the 16:9 acceptance screenshot so the fixed 304x144 surface has
|
||||
-- measurable space above and below it.
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(3)
|
||||
|
||||
local options = game.save.options
|
||||
options.battleLayout = "wide"
|
||||
options.battleFit = "fixed"
|
||||
options.battleHud = "extended"
|
||||
options.battleBg = "world"
|
||||
|
||||
game.save.party = { Pokemon.new(game.data, "PIKACHU", 100) }
|
||||
U.teleport(game, "ROUTE_1", 5, 5, "down")
|
||||
U.wait(60)
|
||||
|
||||
local battle = BattleState.newWild(game, "PIDGEY", 3,
|
||||
{ onFinish = function() end })
|
||||
game.overworld:pushBattle(battle)
|
||||
U.wait(360)
|
||||
|
||||
battle.introSlide = 0
|
||||
battle.introBalls = nil
|
||||
battle.showEnemyTrainer = false
|
||||
battle.showPlayerBack = false
|
||||
battle.enemySendingOut = false
|
||||
battle.sendingOut = false
|
||||
battle.phase = "menu"
|
||||
battle.menuIndex = 1
|
||||
U.wait(2)
|
||||
|
||||
local path = DIR .. "/fixed_extended_world_separate_layer.png"
|
||||
os.remove(path)
|
||||
local ok = U.shot(game, path)
|
||||
|
||||
love.window.setMode(960, 540, { resizable = true })
|
||||
U.wait(5)
|
||||
local smallPath = DIR .. "/fixed_extended_world_small_16x9.png"
|
||||
os.remove(smallPath)
|
||||
ok = U.shot(game, smallPath) and ok
|
||||
|
||||
love.window.setMode(2048, 1152, { resizable = true })
|
||||
U.wait(5)
|
||||
local NamingScreen = require("src.ui.NamingScreen")
|
||||
game.stack:push(NamingScreen.new(game, {
|
||||
title = "NICKNAME?", maxLen = 10, onDone = function() end,
|
||||
}))
|
||||
U.wait(5)
|
||||
local overlayPath = DIR .. "/fixed_extended_world_naming_overlay.png"
|
||||
os.remove(overlayPath)
|
||||
ok = U.shot(game, overlayPath) and ok
|
||||
|
||||
U.log(ok and "FIXED_EXTENDED_WORLD_PASS" or "FIXED_EXTENDED_WORLD_FAIL")
|
||||
love.event.quit(ok and 0 or 1)
|
||||
end
|
||||
@@ -92,6 +92,11 @@ local menu = { isOpaque = true } -- PartyMenu / ListMenu
|
||||
local whiteBattle = setmetatable(
|
||||
{ game = { save = { options = { battleBg = "white" } } } },
|
||||
{ __index = BattleState })
|
||||
local wideWhiteBattle = setmetatable(
|
||||
{ game = { save = { options = {
|
||||
battleBg = "white", battleLayout = "wide",
|
||||
} } } },
|
||||
{ __index = BattleState })
|
||||
local function stack(...) return { states = { ... },
|
||||
visibleBase = function(self)
|
||||
for i = #self.states, 1, -1 do
|
||||
@@ -111,7 +116,10 @@ T.eq(s2:visibleBase(), 1, "the battle alone already drew from the overworld")
|
||||
T.eq(Game.drawBaseInStack(s2, s2:visibleBase()), 1, "and still does")
|
||||
local s3 = stack(overworld, whiteBattle, menu)
|
||||
T.eq(Game.drawBaseInStack(s3, s3:visibleBase()), 3,
|
||||
"a white-bg battle has no map to hold, so nothing moves")
|
||||
"a classic white-bg battle has no presentation to hold, so nothing moves")
|
||||
local s3wide = stack(overworld, wideWhiteBattle, menu)
|
||||
T.eq(Game.drawBaseInStack(s3wide, s3wide:visibleBase()), 2,
|
||||
"an opaque WIDE battle still draws beneath its classic menu")
|
||||
local s4 = stack(overworld, menu)
|
||||
T.eq(Game.drawBaseInStack(s4, s4:visibleBase()), 2,
|
||||
"and a menu outside a battle is untouched")
|
||||
|
||||
@@ -77,6 +77,7 @@ local function battleWith(fx, sprites)
|
||||
statusHUDVisible = function() return true end,
|
||||
bottomUIVisible = function() return true end,
|
||||
caughtMarkerVisible = function() return false end,
|
||||
extendedHUD = function() return false end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user