CLOSES #303, CLOSES #307, CLOSES #314, CLOSES #318

This commit is contained in:
bryanthaboi
2026-07-28 06:16:21 -04:00
parent 72f83760b7
commit 3b1032cc63
38 changed files with 1116 additions and 99 deletions
+11 -2
View File
@@ -1211,6 +1211,15 @@ function BattleState:update(dt)
end
if self.phase == "messages" then
-- Nothing queued starts until the silhouettes have finished sliding in:
-- SlidePlayerAndEnemySilhouettesOnScreen ends with `jpfar
-- PrintBeginningBattleText` (engine/battle/core.asm:100), so the enemy
-- cry and the "Wild X appeared!" box belong at the end of the slide, not
-- on its first frame (#303). The hold sits here rather than in
-- updateQueue because only this loop has a frame clock: updateFx above
-- counts introSlide down, and a headless caller driving updateQueue on
-- its own has no slide to wait for.
if (self.introSlide or 0) > 0 then return end
if not self:updateQueue() then
if self.afterQueue == "menu" then
self.phase = "menu"
@@ -2548,7 +2557,7 @@ function BattleState:performMove(user, target, moveInst, isCalled)
end
-- PP: not for continuations, struggle, called moves, or (under
-- gen1_faithful) wild/trainer enemies pokered DecrementPP only ever
-- gen1_faithful) wild/trainer enemies -- pokered DecrementPP only ever
-- mutates wBattleMonPP / party PP (engine/battle/decrement_pp.asm).
-- That rule doesn't apply in a link battle: "the enemy" there is a real
-- human peer independently tracking their own PP the normal way, not an
@@ -4449,7 +4458,7 @@ function BattleState:drawTextArea()
end
function BattleState:draw()
-- AskName: ClearSprites + wild ClearScreenArea white field under the
-- AskName: ClearSprites + wild ClearScreenArea -- white field under the
-- nickname TextBox / YES/NO (naming_screen.asm); overlays draw on top.
if self.blankForAskName then
love.graphics.setColor(1, 1, 1, 1)
+14
View File
@@ -246,6 +246,20 @@ function Data:resolveText(mapLabel, textConst)
local s = self.text[entry.text]
if s then return s, entry.asm end
end
-- A text_asm entry names its wrapper label but carries no string, because
-- the extractor cannot follow asm. A handful of those wrappers are plain
-- `text_far <label> / text_end` pairs with no logic at all -- BoulderText
-- (home/overworld_text.asm:16), MartSignText, PokeCenterSignText -- and
-- for those the extracted _Label string IS the whole behavior, so the
-- boulders and signs printed nothing at all (#318). Wrappers that really
-- do run logic have no _Label string to find, so they still fall through
-- to their hand-ported script in data/scripts/.
-- needsAsm comes back false here on purpose: showMapText's warning tells
-- the reader to go port a script, and for these there is nothing to port.
if entry.label then
local s = self.text["_" .. entry.label]
if s then return s, false end
end
return nil, entry.asm
end
+5 -1
View File
@@ -162,7 +162,11 @@ function Loader:_loadState()
if next(options.mods or {}) == nil and self.fs.getInfo
and self.fs.getInfo(MOD_STATE_FILE) then
local chunk = self.fs.load(MOD_STATE_FILE)
local ok, state = chunk and pcall(chunk)
-- `chunk and pcall(chunk)` truncates to one value, so state came back nil
-- however well the chunk ran and the migration below never fired once:
-- the guard has to be a statement for pcall's second return to survive.
local ok, state = false, nil
if chunk then ok, state = pcall(chunk) end
if ok and type(state) == "table" then
for id, disabled in pairs(state) do
if disabled then
+31 -9
View File
@@ -5,10 +5,11 @@
-- then drawn once per zone through a shader that remaps the four DMG
-- shades to that zone's palette.
--
-- Port display option: COLORS (GBC / RED++ / OG / OG INV / GBC INV / CLASSIC)
-- transforms every zone's palette at send time via effectiveColors.
-- RED++ swaps the named-palette pack for pokered-gbc SuperPalettes
-- (data/palettes_gbc.lua), including per-species mon colors.
-- Port display option: COLORS (OG RED / SGB / ADVANCED / OG / OG INV /
-- SGB INV / CLASSIC) transforms every zone's palette at send time via
-- effectiveColors. ADVANCED (the `redpp` id below) swaps the named-palette
-- pack for pokered-gbc SuperPalettes (data/palettes_gbc.lua), including
-- per-species mon colors.
local GameVersion = require("src.core.GameVersion")
@@ -18,14 +19,18 @@ local shader -- false = unavailable (headless / no shader support)
local gbcPack -- false = missing; nil = not loaded yet
-- Cycle order matches OptionsMenu / hotkey 2. The three real colorizations
-- come first (OG RED = GBC hardware, SGB = per-map Super Game Boy, RED++ =
-- come first (OG RED = GBC hardware, SGB = per-map Super Game Boy, ADVANCED =
-- pokered-gbc per-tile), then the DMG-shade novelty modes.
PaletteFX.MODES = { "ogred", "gbc", "redpp", "og", "og_inv", "gbc_inv", "classic" }
-- `gbc`/`gbc_inv` keep their save-value ids for back-compat; their LABELS are
-- "SGB"/"SGB INV" because that is what the mode actually is (the old "GBC"
-- label was a misnomer -- it never was the real Game Boy Color palette).
-- `gbc`/`gbc_inv`/`redpp` keep their save-value ids for back-compat while
-- their LABELS say what the mode actually is: "SGB"/"SGB INV" because the old
-- "GBC" label was a misnomer (it never was the real Game Boy Color palette),
-- and "ADVANCED" because `redpp` is the richest colorization of the three
-- rather than anything Red-specific -- it reads as a misnomer outright on a
-- Blue playthrough. Comments elsewhere still call it RED++, the name it has
-- carried in this file since it landed.
PaletteFX.MODE_LABELS = {
ogred = "OG RED", gbc = "SGB", redpp = "RED++", og = "OG",
ogred = "OG RED", gbc = "SGB", redpp = "ADVANCED", og = "OG",
og_inv = "OG INV", gbc_inv = "SGB INV", classic = "CLASSIC",
}
PaletteFX.mode = "gbc"
@@ -598,6 +603,23 @@ end
-- Transform a 4-color palette for the active COLORS display mode.
-- GBC and RED++ pass the zone colors through (RED++ already swapped the
-- pack in pal/monPal); OG* / CLASSIC replace; GBC INV permutes shades.
-- The "paper" shade of the active display mode: what a DMG-white background
-- pixel ends up as once colorization has run. Every palette in the SGB pack
-- carries the same off-white (255,239,255) as color 0, so this is well
-- defined for the whole screen rather than per zone. Goes through
-- PaletteFX.pal so OG RED short-circuits to the one global boot-ROM BG
-- palette and RED++ falls back to the ROM pack, then through effectiveColors
-- so the mono and inverted modes get their own paper (CLASSIC's pea green,
-- and the dark paper the inverted modes should have). Returns r, g, b in
-- 0..1, white if nothing resolves.
function PaletteFX.paperShade(data)
local base = PaletteFX.pal(data, "GREENBAR") or PaletteFX.GRAYS
local colors = PaletteFX.effectiveColors(base) or base
local c = colors and colors[1]
if not c then return 1, 1, 1 end
return c[1] / 255, c[2] / 255, c[3] / 255
end
function PaletteFX.effectiveColors(c)
if not c then return nil end
local mode = PaletteFX.mode or "gbc"
+6 -3
View File
@@ -480,8 +480,11 @@ function Renderer:endFrame(zones, worldZones)
love.graphics.setCanvas(present)
end
-- Default letterbox is black. Battle (and any state that opts in via
-- letterboxWhite) fills the voids white so the window matches the
-- white battle canvas instead of showing black bars.
-- letterboxWhite) fills the voids with the display mode's paper shade, so
-- the bars match the canvas they frame instead of showing black. Not a
-- literal white: the battle canvas is colorized, and in SGB mode its paper
-- 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
if not self.worldActive then
local ok, Game = pcall(require, "src.core.Game")
@@ -489,7 +492,7 @@ function Renderer:endFrame(zones, worldZones)
local base = stack and stack.visibleBase and stack:visibleBase()
local state = base and stack.states and stack.states[base]
if state and state.letterboxWhite then
clearR, clearG, clearB = 1, 1, 1
clearR, clearG, clearB = PaletteFX.paperShade(Game and Game.data)
end
end
love.graphics.setColor(clearR, clearG, clearB, 1)
+8 -1
View File
@@ -294,10 +294,17 @@ function TextBox:draw()
self.scrollPx = self.scrollPx - 2
if self.scrollPx <= 0 then self.scrollPx = nil end
end
-- Only the retained line carries the offset: it slides up from where it
-- already sat (line2Y) to line1Y. The incoming line is drawn at its home
-- row instead, because offsetting it too put fresh glyphs 8px low -- on the
-- box's bottom border -- whenever the typewriter beat the 4-frame slide
-- (#314). The sub-tile slide is ours to begin with: ScrollTextUpOneLine
-- (home/text.asm:283) copies the rows up whole and waits 5 frames, so
-- nothing in the original is ever drawn between two rows.
local off = self.scrollPx or 0
local ys = { self.line1Y, self.line2Y }
for i, line in ipairs(self.shown) do
local y = (ys[i] or self.line2Y) + off
local y = (ys[i] or self.line2Y) + (i == 1 and off or 0)
for j, code in ipairs(line) do
Font.drawCode(code, self.textX + (j - 1) * 8, y)
end
+5 -1
View File
@@ -37,7 +37,11 @@ function DexEntryMenu.new(game, speciesOrOpts)
self.def = game.data.pokemon[species]
local path = require("src.pokemon.Sprites").path(game.data, species, "front",
{ kind = "dex" })
local ok, img = path and pcall(love.graphics.newImage, path)
-- `path and pcall(...)` truncates to one value, so img was always nil and
-- every dex page drew without its pic (#307); the guard has to be a
-- statement for pcall's second return to survive.
local ok, img = false, nil
if path then ok, img = pcall(love.graphics.newImage, path) end
self.sprite = ok and img or nil
require("src.core.Sound").playCry(game.data, species)
return self
+1 -1
View File
@@ -18,7 +18,7 @@
local Check = {}
Check.REPO = "bryanthaboi/pokemon-gen1-recomp-project"
Check.REPO = "bryanthaboi/gen1recomp"
local CMD = "update_check_cmd"
local STATE = "update_check_state"
+1 -1
View File
@@ -50,7 +50,7 @@ local osName = (love.system and love.system.getOS and love.system.getOS()) or
local isWindows = osName == "Windows"
local saveDir = love.filesystem.getSaveDirectory()
local API_URL = "https://api.github.com/repos/bryanthaboi/pokemon-gen1-recomp-project/releases/latest"
local API_URL = "https://api.github.com/repos/bryanthaboi/gen1recomp/releases/latest"
-- the release picked by the last "check"; kept between commands so "download"
-- knows the payload url/size/name without re-fetching