fix white screen flash on shake on versions > 1.3.0
DramaticShape · 2026-07-28 ·
parent 2e0ae37bcd
fix white screen flash on shake on versions > 1.3.0
Changed files
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
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",