mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-18 03:35:56 +02:00
bug squashing
# Closed issues CLOSES #17: Incorrect Character Visuals (Only with GBC filter) CLOSES #23: Could you allow the player to change the order of the moves CLOSES #24: Evolution music not playing during evolution CLOSES #26: Standing on door glitch CLOSES #27: Battle Intro text automatically continues CLOSES #29: Visual bug when zoomed out CLOSES #32: Team Rocket recruiter doesn't battle with you unless you speak with him first CLOSES #33: Developer/Debug Console CLOSES #35: Professor Oak's introduction Inaccuracies CLOSES #36: Missing Pokemon Dex entries when picking starter + Rival Pathing issues CLOSES #39: Guy who stops player from skipping brock doesn't bring you to brock's gym + doesn't leave once you've beaten brock CLOSES #40: Bill cutscene is broken CLOSES #41: Ticket guy failing to be a Ticket guy CLOSES #42: S.S. anne odd behavior + Missing sailing away animation CLOSES #43: Dig Attack animation appears to be glitched CLOSES #44: Pokeball flashing doesn't appear to be accurate CLOSES #45: Inaccurate Cut Animation CLOSES #46: Dugtrio i caught in diglett cave has two of the same move CLOSES #47: Rival ignores player in Lavender Tower CLOSES #48: Healing pad in lavender tower does not function CLOSES #49: Incorrect dialogue with parched security guard CLOSES #50: (Game Breaking!) Rocket grunt guarding poster refuses to move CLOSES #51: Badges showing up as items I can deposit in PC CLOSES #52: Visual bug on Celadon Department Store roof (Red Filter) CLOSES #54: Visual issue on route 15 + Fuchsia City CLOSES #56: Running animation missing CLOSES #57: Safari Zone does not display steps while you are inside of it CLOSES #58: (Game Breaking!) Softlock at cycling road gate CLOSES #59: Bike visual issues CLOSES #60: Cycling road not forcing you to get on your bike CLOSES #61: No keycard doors in Silph Co. CLOSES #63: Missing teleporter animation CLOSES #64: Inaccurate spinning CLOSES #65: Reimplement unused Silph Co. Chief and Professor Oak trainer battles
This commit is contained in:
@@ -141,6 +141,54 @@ function PaletteFX.usesGbcPack(mode)
|
||||
return mode == "redpp"
|
||||
end
|
||||
|
||||
-- Per-object overworld sprite coloring (ColorOverworldSprite) applies in
|
||||
-- plain GBC mode too, not only under the RED++ pack: without it the
|
||||
-- whole-map zone shader paints characters with whatever two mid shades
|
||||
-- the terrain palette defines. RED++ handles sprites through the baked
|
||||
-- usesGbcPack() path in SpriteRenderer; this names the modes where the
|
||||
-- OBP bake plus the post-zone redraw (below) stand in for real OBJ
|
||||
-- palettes over a shader-colorized background.
|
||||
function PaletteFX.usesSpriteObp(mode)
|
||||
mode = mode or PaletteFX.mode
|
||||
return mode == "gbc"
|
||||
end
|
||||
|
||||
-- ------- post-zone sprite redraw (GBC mode)
|
||||
--
|
||||
-- In GBC mode the world canvas still runs through the per-map zone
|
||||
-- shade-remap shader, which would corrupt an OBP-baked sprite's true-color
|
||||
-- pixels. So SpriteRenderer draws the baked sprite into the canvas (its
|
||||
-- pixels come out zone-tinted there) AND records the draw here;
|
||||
-- Renderer:endFrame replays the list on top of the finished zone pass,
|
||||
-- scaled into screen space -- the GBC's OBJ-over-BG compositing, one draw
|
||||
-- late. Entries carrying `colors` are re-colorized draws (the tall-grass
|
||||
-- feet overdraw, which must keep hiding sprite feet) issued through the
|
||||
-- color-0-keyed shade-remap shader. World pass only; cleared per frame.
|
||||
local spriteRedraws = {}
|
||||
|
||||
function PaletteFX.clearSpriteRedraws()
|
||||
for i = #spriteRedraws, 1, -1 do spriteRedraws[i] = nil end
|
||||
end
|
||||
|
||||
function PaletteFX.markSpriteRedraw(image, quad, x, y, sx, colors, keyed)
|
||||
if currentPass ~= "world" then return end
|
||||
spriteRedraws[#spriteRedraws + 1] =
|
||||
{ image = image, quad = quad, x = x, y = y, sx = sx or 1,
|
||||
colors = colors, keyed = keyed }
|
||||
end
|
||||
|
||||
-- whether a draw issued right now would land in the redraw list -- the
|
||||
-- OBP bake is only correct when the replay can restore it after the zone
|
||||
-- pass (tilt's upright pass colorizes per-billboard instead, so sprites
|
||||
-- there keep the raw sheet)
|
||||
function PaletteFX.spriteRedrawPassActive()
|
||||
return currentPass == "world"
|
||||
end
|
||||
|
||||
function PaletteFX.spriteRedraws()
|
||||
return spriteRedraws
|
||||
end
|
||||
|
||||
-- Active named-palette table for COLORS: RED++ uses data/palettes_gbc.lua,
|
||||
-- everything else uses the ROM-imported data.palettes.
|
||||
function PaletteFX.pack(data)
|
||||
@@ -223,6 +271,29 @@ local TILE_GROUP_EXCEPTIONS = {
|
||||
CELADON_MART_1F = { tiles = { [0x07] = true, [0x08] = true,
|
||||
[0x17] = true, [0x18] = true }, group = 4 },
|
||||
}
|
||||
|
||||
-- keyed by tileset id (applies on every map that uses it), consulted after
|
||||
-- the per-map table above
|
||||
local TILESET_GROUP_EXCEPTIONS = {
|
||||
-- tile $22 (the hollow-square grave marker) -> GRAY: the extracted pack
|
||||
-- files it under the bright blue family, which makes a purely
|
||||
-- decorative floor marker read as an interactive pad
|
||||
CEMETERY = { tiles = { [0x22] = true }, group = 0 },
|
||||
}
|
||||
|
||||
-- pokered-gbc's lobby.bst repoints the Celadon roof table's flat top
|
||||
-- (block 29, cells 5/6/9/10) at a duplicate tile ($5a, BROWN) so the
|
||||
-- tabletop and the checkerboard floor -- both raw tile $37 -- can take
|
||||
-- different palettes; the vanilla-derived blockset shares the one tile
|
||||
-- id, so the RED++ atlas path re-creates the duplicate: the alias slot
|
||||
-- is baked as a copy of `tile` in `group`'s colors, and the listed
|
||||
-- 0-based block cells draw the alias instead of the shared tile.
|
||||
PaletteFX.TILE_ALIASES = {
|
||||
CELADON_MART_ROOF = {
|
||||
{ block = 29, cells = { [5] = true, [6] = true, [9] = true, [10] = true },
|
||||
tile = 0x37, alias = 0x5a, group = 5 },
|
||||
},
|
||||
}
|
||||
local ROOF_GROUP = 6
|
||||
local ROUTE_6_SAFFRON = { mapId = "ROUTE_6", useMapId = "SAFFRON_CITY", cellYBelow = 2 }
|
||||
|
||||
@@ -244,6 +315,8 @@ function PaletteFX.worldGroupAt(tileset, mapId, tileId)
|
||||
if not groups then return nil end
|
||||
local exc = TILE_GROUP_EXCEPTIONS[mapId]
|
||||
if exc and exc.tiles[tileId] then return exc.group end
|
||||
exc = TILESET_GROUP_EXCEPTIONS[tileset]
|
||||
if exc and exc.tiles[tileId] then return exc.group end
|
||||
return groups[tileId] or 7 -- TEXT: tile ids past the tileset's 96 (menus)
|
||||
end
|
||||
|
||||
@@ -293,6 +366,10 @@ function PaletteFX.spriteObp(spriteDef, seed)
|
||||
local src = spriteDef and spriteDef.source
|
||||
if not (w and src) then return nil end
|
||||
local idx = tonumber(src:match("%[(%d+)%]"))
|
||||
-- RedBikeSprite loads outside SpriteSheetPointerTable
|
||||
-- (LoadBikePlayerSpriteGraphics), so its source carries no bracketed
|
||||
-- index; it wears the player's own palette, same as SPRITE_RED
|
||||
if not idx and src:find("RedBikeSprite", 1, true) then idx = 0 end
|
||||
local group = idx and w.spriteAssignment[idx]
|
||||
if group == nil then return nil end
|
||||
if group == "random" then
|
||||
|
||||
+32
-1
@@ -62,8 +62,10 @@ end
|
||||
function Renderer:beginFrame(transparent)
|
||||
self.worldActive = false
|
||||
self.uprightActive = false
|
||||
-- last frame's trueColor rects go before anything draws this one
|
||||
-- last frame's trueColor rects and sprite redraws go before anything
|
||||
-- draws this one
|
||||
PaletteFX.clearTrueColor()
|
||||
PaletteFX.clearSpriteRedraws()
|
||||
PaletteFX.setPass("ui")
|
||||
love.graphics.setCanvas(self.canvas)
|
||||
if transparent then
|
||||
@@ -363,6 +365,35 @@ function Renderer:endFrame(zones, worldZones)
|
||||
else
|
||||
blit(self.worldCanvas, s, zones, S, wox, woy, 0, 0, ww, wh)
|
||||
end
|
||||
-- OBP-baked overworld sprites replay on top of the zone pass (GBC
|
||||
-- mode per-object coloring; see PaletteFX.markSpriteRedraw). Grass
|
||||
-- feet-overdraw entries carry `colors` and re-colorize through the
|
||||
-- color-0-keyed shade-remap shader so they keep hiding sprite feet.
|
||||
local redraws = PaletteFX.spriteRedraws()
|
||||
if redraws[1] then
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.setScissor(0, 0, ww, wh)
|
||||
local activeShader = nil
|
||||
for _, r in ipairs(redraws) do
|
||||
local wanted = r.colors
|
||||
and (r.keyed and PaletteFX.keyedShader() or PaletteFX.shader())
|
||||
or nil
|
||||
if wanted ~= activeShader then
|
||||
activeShader = wanted
|
||||
love.graphics.setShader(wanted)
|
||||
end
|
||||
if wanted then PaletteFX.sendColors(wanted, r.colors) end
|
||||
if r.quad then
|
||||
love.graphics.draw(r.image, r.quad, wox + r.x * s, woy + r.y * s,
|
||||
0, s * r.sx, s)
|
||||
else
|
||||
love.graphics.draw(r.image, wox + r.x * s, woy + r.y * s,
|
||||
0, s * r.sx, s)
|
||||
end
|
||||
end
|
||||
if activeShader then love.graphics.setShader() end
|
||||
love.graphics.setScissor()
|
||||
end
|
||||
end
|
||||
-- Composite the tilt upright pass over the ground (projected or, in the
|
||||
-- rare no-shader fallback, flat). It already carries its billboards'
|
||||
|
||||
@@ -85,10 +85,21 @@ end
|
||||
|
||||
-- facing: down/up/left/right; walkPhase: 0 stand, 1 walk; flip: alternate
|
||||
-- steps mirror the walk frame for up/down (GB uses OAM flip for this).
|
||||
local function blitFrame(image, quad, x, y, flip, redraw)
|
||||
if flip then
|
||||
love.graphics.draw(image, quad, x + 16, y, 0, -1, 1)
|
||||
if redraw then PaletteFX.markSpriteRedraw(image, quad, x + 16, y, -1) end
|
||||
else
|
||||
love.graphics.draw(image, quad, x, y)
|
||||
if redraw then PaletteFX.markSpriteRedraw(image, quad, x, y, 1) end
|
||||
end
|
||||
end
|
||||
|
||||
function SpriteRenderer:draw(px, py, camX, camY, facing, walkPhase, stepFlip)
|
||||
local x = math.floor(px - camX)
|
||||
local y = math.floor(py - camY) - 4
|
||||
local image = self.image
|
||||
local redraw = false
|
||||
-- full-color art claims its 16x16 cell out of the shade-remap pass
|
||||
if self.def.trueColor then
|
||||
PaletteFX.markTrueColor(x, y, 16, 16)
|
||||
@@ -102,12 +113,21 @@ function SpriteRenderer:draw(px, py, camX, camY, facing, walkPhase, stepFlip)
|
||||
if colors then
|
||||
image = getObpImage(self.def.image, colors, group)
|
||||
end
|
||||
elseif PaletteFX.usesSpriteObp() and PaletteFX.spriteRedrawPassActive() then
|
||||
-- plain GBC: the terrain zone shader still runs over the world canvas,
|
||||
-- so the baked sprite is also queued for a post-zone redraw
|
||||
-- (PaletteFX.markSpriteRedraw) that restores its own OBP colors on top
|
||||
local colors, group = PaletteFX.spriteObp(self.def, self.seed)
|
||||
if colors then
|
||||
image = getObpImage(self.def.image, colors, group)
|
||||
redraw = true
|
||||
end
|
||||
end
|
||||
-- single-frame sprites (item balls, fossils...) have one fixed pose;
|
||||
-- still 3-frame sprites turn to face (the nurse at her machine,
|
||||
-- facePlayer on STAY NPCs) but never show walk frames
|
||||
if self.def.frames <= 1 then
|
||||
love.graphics.draw(image, self.frames[0], x, y)
|
||||
blitFrame(image, self.frames[0], x, y, false, redraw)
|
||||
return
|
||||
end
|
||||
local frame = (self.def.walker and walkPhase == 1)
|
||||
@@ -119,11 +139,7 @@ function SpriteRenderer:draw(px, py, camX, camY, facing, walkPhase, stepFlip)
|
||||
flip = true
|
||||
end
|
||||
local quad = self.frames[frame] or self.frames[0]
|
||||
if flip then
|
||||
love.graphics.draw(image, quad, x + 16, y, 0, -1, 1)
|
||||
else
|
||||
love.graphics.draw(image, quad, x, y)
|
||||
end
|
||||
blitFrame(image, quad, x, y, flip, redraw)
|
||||
end
|
||||
|
||||
return SpriteRenderer
|
||||
|
||||
@@ -347,9 +347,10 @@ local function getGbcAtlas(imagePath, tilesetId, mapId, perRow, data)
|
||||
if groupColors then
|
||||
local src = Assets.imageData(imagePath)
|
||||
local iw, ih = src:getDimensions()
|
||||
local total = (iw / 8) * (ih / 8)
|
||||
local out = love.image.newImageData(iw, ih)
|
||||
local tileColors = {}
|
||||
for t = 0, (iw / 8) * (ih / 8) - 1 do
|
||||
for t = 0, total - 1 do
|
||||
local colors = tileColors[t]
|
||||
if colors == nil then
|
||||
local group = PaletteFX.worldGroupAt(tilesetId, mapId, t)
|
||||
@@ -366,6 +367,25 @@ local function getGbcAtlas(imagePath, tilesetId, mapId, perRow, data)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- duplicate-tile aliases: bake a copy of a shared tile graphic into
|
||||
-- a spare slot under a different palette group, so block cells that
|
||||
-- draw the alias can color apart from cells sharing the raw tile
|
||||
for _, al in ipairs(PaletteFX.TILE_ALIASES and PaletteFX.TILE_ALIASES[mapId] or {}) do
|
||||
if al.alias < total then
|
||||
local colors = groupColors[al.group + 1]
|
||||
local sxo = (al.tile % perRow) * 8
|
||||
local syo = math.floor(al.tile / perRow) * 8
|
||||
local dxo = (al.alias % perRow) * 8
|
||||
local dyo = math.floor(al.alias / perRow) * 8
|
||||
for py = 0, 7 do
|
||||
for px = 0, 7 do
|
||||
local r, g, b, a = src:getPixel(sxo + px, syo + py)
|
||||
r, g, b, a = recolorSample(r, g, b, a, colors)
|
||||
out:setPixel(dxo + px, dyo + py, r, g, b, a)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
img = love.graphics.newImage(out)
|
||||
end
|
||||
end
|
||||
@@ -429,14 +449,38 @@ function TileRenderer.new(map, data)
|
||||
end
|
||||
end
|
||||
|
||||
-- duplicate-tile alias remap (RED++ atlas only): [blockId][0-based cell]
|
||||
-- -> alias tile id (see PaletteFX.TILE_ALIASES / getGbcAtlas's bake)
|
||||
local aliasMap
|
||||
if gbcCtx then
|
||||
for _, al in ipairs(PaletteFX.TILE_ALIASES and PaletteFX.TILE_ALIASES[map.id] or {}) do
|
||||
aliasMap = aliasMap or {}
|
||||
local cells = aliasMap[al.block] or {}
|
||||
for ci in pairs(al.cells) do cells[ci] = al.alias end
|
||||
aliasMap[al.block] = cells
|
||||
end
|
||||
end
|
||||
|
||||
for by = -BORDER_BLOCKS, hB + BORDER_BLOCKS - 1 do
|
||||
for bx = -BORDER_BLOCKS, wB + BORDER_BLOCKS - 1 do
|
||||
local inside = bx >= 0 and by >= 0 and bx < wB and by < hB
|
||||
local batch = inside and self.mapBatch or self.ringBatch
|
||||
local block = map.tileset.blocks[map:blockAt(bx, by) + 1]
|
||||
-- beyond-edge ring cells use the same override drawBorderFill does,
|
||||
-- so the ring and the far background fill agree (OVERWORLD maps
|
||||
-- whose raw border_block is water still ring with the tree wall)
|
||||
local blockId = inside and map:blockAt(bx, by) or borderBlockFor(map)
|
||||
local block = map.tileset.blocks[blockId + 1]
|
||||
if not block then
|
||||
-- a tileset without the tree-wall block keeps its own border
|
||||
blockId = map:blockAt(bx, by)
|
||||
block = map.tileset.blocks[blockId + 1]
|
||||
end
|
||||
local remap = aliasMap and aliasMap[blockId]
|
||||
for ty = 0, 3 do
|
||||
for tx = 0, 3 do
|
||||
local tile = block[ty * 4 + tx + 1]
|
||||
local ci = ty * 4 + tx
|
||||
local tile = block[ci + 1]
|
||||
if remap and remap[ci] then tile = remap[ci] end
|
||||
local quad = self.quads[tile]
|
||||
if quad then
|
||||
batch:add(quad, bx * 32 + tx * 8, by * 32 + ty * 8)
|
||||
@@ -556,6 +600,22 @@ function TileRenderer:drawCellBottom(cx, cy, camX, camY)
|
||||
if shader then love.graphics.setShader() end
|
||||
end
|
||||
|
||||
-- queue the same bottom tile row for the post-zone sprite-redraw pass
|
||||
-- (GBC mode: OBP-baked sprites replay after the zone shader, so the
|
||||
-- grass patch that hides their feet must replay over them, colorized
|
||||
-- with the map's palette and color-0 keyed)
|
||||
function TileRenderer:markCellBottomRedraw(cx, cy, camX, camY, colors)
|
||||
local ty = cy * 2 + 1
|
||||
for i = 0, 1 do
|
||||
local tx = cx * 2 + i
|
||||
local quad = self.quads[self.map:tileAt(tx, ty)]
|
||||
if quad then
|
||||
PaletteFX.markSpriteRedraw(self.image, quad, tx * 8 - math.floor(camX),
|
||||
ty * 8 - math.floor(camY), 1, colors, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- animated overdraw at the current step; bodyOnly skips the ring
|
||||
-- positions (connected maps draw body-only)
|
||||
function TileRenderer:drawAnimated(camX, camY, bodyOnly)
|
||||
|
||||
Reference in New Issue
Block a user