Port timing/parity fixes, seamless battle transitions, faithful-res lock, and zoom-aware UI anchoring

Ports from a downstream fork, hand-surgered hunk-by-hunk to exclude the
fork's randomizer/pokescript work and to skip a FixedStep jitter-tolerance
attempt that never fixed the stutter it targeted.

- src/core/Timing.lua: hardware-accurate frame-delay catalog ported from
  pret/pokered, feeding BattleState:waitNext, EffectRegistry's miss/crit
  beats, TextBox/ChoiceBox scroll and prompt holds, and the battle
  silhouette slide/shake/blink/faint timings.
- Seamless battle transitions: Renderer:drawBattleWipe replaces the old
  160x144-only cascade with one wipe drawn over the whole surface at any
  zoom or window size; BattleTransition's per-style frame lengths are
  corrected against pokered-c's derivation; Transition.battleReturn adds
  the post-battle GBFadeInFromWhite the port never had.
- BATTLE SIZE / BATTLE BG options (BattleState:wantsFillScale/bgMode,
  Game.fillScaleInStack/worldBgBattleDim): battle surface can fill the
  window instead of the fixed integer letterbox, and the area around it
  can show white/black/the dimmed overworld instead of only white.
- src/core/FaithfulRes.lua: locks the window to an exact 160x144 multiple.
- Zoom-aware UI anchoring: Renderer:uiScale steps the UI down with survey
  zoom (gated to worldActive so the title/intro never shrink);
  Renderer:setUIAnchor lets TextBox, ChoiceBox, and an opted-in Menu
  (the START menu) pin themselves to a screen edge instead of the
  zoomed-out letterbox.
This commit is contained in:
spiritsnails
2026-08-01 15:52:34 -06:00
parent 5e89e35e02
commit aedc63c40d
23 changed files with 2515 additions and 114 deletions
+32
View File
@@ -8,6 +8,7 @@
local Font = require("src.render.Font")
local Theme = require("src.ui.Theme")
local Timing = require("src.core.Timing")
local TextBox = {}
TextBox.__index = TextBox
@@ -177,6 +178,13 @@ end
function TextBox:update(dt)
local input = self.game.input
self.blink = (self.blink + 1) % 60
-- A page or CONT advance blocks the whole box while the original's scroll
-- and clear run (src/core/Timing.lua TEXT_SCROLL_PAIR / TEXT_PAGE_CLEAR).
-- Nothing types and no input is read until it drains.
if (self.holdFrames or 0) > 0 then
self.holdFrames = self.holdFrames - 1
return
end
if self.done then
if self.auto then
if not self.autoStarted then
@@ -244,6 +252,13 @@ function TextBox:update(dt)
return
end
if self.waiting then
-- _ContText and Paragraph both print the â–¼ and run ProtectedDelay3
-- before ManualTextScroll starts watching the joypad (home/text.asm:265,
-- :234), so the arrow is up for three frames that swallow the button.
if (self.preWait or 0) > 0 then
self.preWait = self.preWait - 1
return
end
if input:wasPressed("a") or input:wasPressed("b") then
require("src.core.Sound").play(self.game.data, "Press_AB")
self.waiting = false
@@ -252,11 +267,17 @@ function TextBox:update(dt)
self.contAdvance = false
self.lineIndex = self.lineIndex + 1
self:beginLine()
-- ScrollTextUpOneLine is 5 blocking frames and, as its own comment
-- says, is "always called twice in a row" (home/text.asm:280-305)
self.holdFrames = Timing.TEXT_SCROLL_PAIR
else
self.shown = {}
self.pageIndex = self.pageIndex + 1
self.lineIndex = 1
self:beginLine()
-- ClearScreenArea then DelayFrames 20: the box sits empty before the
-- next page starts typing (home/text.asm:236-240)
self.holdFrames = Timing.TEXT_PAGE_CLEAR
end
end
return
@@ -283,6 +304,7 @@ function TextBox:update(dt)
if conts and conts[nextIdx] then
-- pokered <CONT>: ▼ + WaitForTextScrollButtonPress before scroll
self.waiting = true
self.preWait = Timing.TEXT_PRE_ADVANCE
self.contAdvance = true
else
self.lineIndex = nextIdx
@@ -290,6 +312,7 @@ function TextBox:update(dt)
end
elseif self.pageIndex < #self.pages then
self.waiting = true
self.preWait = Timing.TEXT_PRE_ADVANCE
self.contAdvance = false
else
self.done = true
@@ -300,6 +323,15 @@ function TextBox:update(dt)
end
function TextBox:draw()
-- The dialogue box belongs against the bottom of the screen, not floating
-- in the middle of a zoomed-out letterbox. Declared per frame; the
-- renderer blits this region to the screen edge and the rest of the UI
-- where it always was (Renderer:setUIAnchor).
local r = self.game and self.game.renderer
if r and r.setUIAnchor then
r:setUIAnchor(self.boxTx * 8, self.boxTy * 8,
self.boxTw * 8, self.boxTh * 8, "bottom")
end
Font.drawBox(self.boxTx, self.boxTy, self.boxTw, self.boxTh)
love.graphics.setColor(0, 0, 0, 1)
if self.scrollPx and self.scrollPx > 0 then