From d8d1d7e336d155004e93da68493c15ec7429e7b6 Mon Sep 17 00:00:00 2001 From: DramaticShape Date: Tue, 28 Jul 2026 16:10:58 -0400 Subject: [PATCH] fix white screen flash on shake on versions > 1.3.0 --- CHANGELOG.md | 12 ++++++++++++ lib/BattleScene.lua | 16 ++++++++++++---- lib/OverworldBattle.lua | 41 +++++++++++++++++++++++++++++++++++++++++ lib/Voxel3D.lua | 7 +++++-- main.lua | 2 +- manifest.json | 2 +- 6 files changed, 72 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f76a85..5be5443 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 1.1.1 + +### Fixed + +- A move that shakes the screen no longer whites out the frame. The zone pass + fills each zone with its blank colour before drawing the shifted copy -- the + hardware showing empty BG in the strip the shake vacated -- and a shake + program alternates offset and no-offset frames, so over the map that read as + the whole battle screen, menu box included, flashing white a few times a + second. The fill is dropped while a battle is staged on the map; the shake + itself still moves the HUD. + ## 1.2.0 ### Added diff --git a/lib/BattleScene.lua b/lib/BattleScene.lua index 42b452f..43c6003 100644 --- a/lib/BattleScene.lua +++ b/lib/BattleScene.lua @@ -278,10 +278,16 @@ end -- yet (the terrain mesh is still building, the driver has no depth support). -- nil is not a failure: the caller simply leaves the battle screen as the -- engine drew it for that frame. --- White, for the hit flash. The shader replaces the card's colour outright --- rather than multiplying it, so this is the sprite's own silhouette turned --- solid white -- not a lightened picture of itself. +-- White, for the hit flash, and how far toward it the card goes. +-- +-- The shader replaces the card's colour rather than multiplying it, so at +-- full strength this is the sprite turned into a solid white silhouette -- +-- which is what the effect is on a flat GB screen and far too much on a +-- sprite standing in a lit world. Held well short of 1, the mon's own +-- shading still reads through the flash: it looks struck rather than +-- deleted. BattleScene.FLASH_COLOR = { 1, 1, 1 } +BattleScene.FLASH_STRENGTH = 0.5 function BattleScene.render(state, arena, textures, token) if not (state and state.map and arena) then return nil end @@ -369,7 +375,9 @@ function BattleScene.render(state, arena, textures, token) -- (see OverworldBattle) and put back HERE, on the two things it was ever -- about: the mons themselves go solid white for those frames. local flashing = textures and textures.flash - if flashing then Voxel3D.flatten(BattleScene.FLASH_COLOR) end + if flashing then + Voxel3D.flatten(BattleScene.FLASH_COLOR, BattleScene.FLASH_STRENGTH) + end for _, card in ipairs(monCards(arena, groundY, textures)) do Voxel3D.draw(BattleBillboard.mesh(), card.tex, card.model, BattleBillboard.PULL) diff --git a/lib/OverworldBattle.lua b/lib/OverworldBattle.lua index 0cf2ab7..f3998f0 100644 --- a/lib/OverworldBattle.lua +++ b/lib/OverworldBattle.lua @@ -645,6 +645,47 @@ function OverworldBattle.install() if not ok then error(err, 0) end end + -- The engine's flash has a SECOND half, and it is the one that reaches the + -- menu. Beside the white rectangle (dropped above) the flash moves are + -- driven by a BGP palette fade -- BGP_LIGHT and friends -- which the + -- colorized pipeline applies in drawZonePass to the WHOLE background + -- canvas. That canvas carries the HUD glyphs and the text box, so a fade + -- meant for the two mons washed the menu out with them. + -- + -- The fade is left switched on for the pics, which read it through + -- picImage, and switched off for the zone pass alone. So the mons flash + -- and the furniture around them does not. + -- + -- The zone pass has a SECOND thing it paints, and this is the one that + -- reads as the menu box flashing. A screen shake makes it fill every zone + -- with the zone's own color 0 before it draws the offset copy -- the + -- hardware showing empty BG in the strip the shake vacated. On a white + -- battle field that fill is invisible; over a world it is an opaque white + -- sheet across the whole frame, and since a shake program alternates + -- offset and no-offset frames (SE_SHAKE_SCREEN steps dx 1, 0, 1, 0...) it + -- switches on and off a few times a second. It is dropped: the background + -- here is the map, so what the shake vacates should show the map. + local innerZone = BattleState.drawZonePass + function BattleState:drawZonePass(src, sx, sy) + if not self.dramaticShapeShot then return innerZone(self, src, sx, sy) end + -- shadow the method on the instance for this call only; putting the + -- field back to whatever it was (normally nil) lets the class method be + -- found again + local had = rawget(self, "activeBgp") + self.activeBgp = function() return nil end + local g = love.graphics + local rectangle = g.rectangle + g.rectangle = function(mode, ...) + -- the pass draws no other rectangle; the shake still shifts the copy + if mode == "fill" then return end + return rectangle(mode, ...) + end + local ok, err = pcall(innerZone, self, src, sx, sy) + g.rectangle = rectangle + self.activeBgp = had + if not ok then error(err, 0) end + end + -- Black glyphs on grass are not readable; over a frosted panel measured -- dark they are not readable either, so they go white. Mapped rather than -- rewritten: the HUD sets pure black for its text and nothing else, and in diff --git a/lib/Voxel3D.lua b/lib/Voxel3D.lua index cb4344f..78679b7 100644 --- a/lib/Voxel3D.lua +++ b/lib/Voxel3D.lua @@ -544,12 +544,15 @@ end -- simply wants to come out one colour, which is what a hit flash on a sprite -- is. beginScene resets the uniform every frame, so a pass that forgets to -- clear it cannot leak into the next one. -function Voxel3D.flatten(color) +-- `amount` is how far toward that colour, 0..1; omitted is all the way. +-- Anything short of 1 leaves the sprite's own shading showing through, which +-- is the difference between a hit flash and a white cut-out. +function Voxel3D.flatten(color, amount) if not (active and activeShader) then return end local sh = activeShader if color then pcall(sh.send, sh, "ghostColor", color) - pcall(sh.send, sh, "ghost", 1) + pcall(sh.send, sh, "ghost", math.max(0, math.min(1, amount or 1))) else pcall(sh.send, sh, "ghost", 0) end diff --git a/main.lua b/main.lua index d07292b..faf2595 100644 --- a/main.lua +++ b/main.lua @@ -620,7 +620,7 @@ mod.events:on("battle.ended", function() OverworldBattle.finish() end) -mod.exports.version = "1.2.0" +mod.exports.version = "1.1.1" -- exposed so a companion mod can pin its own tiles' shapes or read the -- camera without reaching into this mod's file layout mod.exports.lib = V diff --git a/manifest.json b/manifest.json index e53591d..f5a7633 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "DRAMATIC_SHAPE", "name": "Dramatic Shape Voxel Mod", - "version": "1.2.0", + "version": "1.1.1", "api": 2, "entry": "main.lua", "profile": "content",