feat: Add bonus flash effect for player pickups and update rendering logic

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-23 12:11:19 +01:00
parent 604923618a
commit 8ed460b03e
7 changed files with 165 additions and 37 deletions
@@ -483,16 +483,16 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
final int color = isPushwall
? pushwallColor
: objTile == MapObject.normalExitTrigger
? normalExitColor
: objTile == MapObject.secretExitTrigger
? secretExitColor
: objTile == MapObject.goldKey
? goldKeyColor
: objTile == MapObject.silverKey
? silverKeyColor
: (wallTile == 0
? floorColor
: (wallTile >= 90 ? doorColor : wallColor));
? normalExitColor
: objTile == MapObject.secretExitTrigger
? secretExitColor
: objTile == MapObject.goldKey
? goldKeyColor
: objTile == MapObject.silverKey
? silverKeyColor
: (wallTile == 0
? floorColor
: (wallTile >= 90 ? doorColor : wallColor));
_fillMapRect(
mapStartX + (x * tileSize),
mapStartY + (y * tileSize),
@@ -2094,11 +2094,19 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
@override
dynamic finalizeFrame() {
if (engine.difficulty != null && engine.player.damageFlash > 0.0) {
if (_usesTerminalLayout) {
_applyDamageFlashToScene();
} else {
_applyDamageFlash();
if (engine.difficulty != null) {
if (engine.player.damageFlash > 0.0) {
if (_usesTerminalLayout) {
_applyDamageFlashToScene();
} else {
_applyDamageFlash();
}
} else if (engine.player.bonusFlash > 0.0) {
if (_usesTerminalLayout) {
_applyBonusFlashToScene();
} else {
_applyBonusFlash();
}
}
}
if (_usesTerminalLayout) {
@@ -2275,6 +2283,44 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
return (0xFF000000) | (b << 16) | (g << 8) | r;
}
void _applyBonusFlash() {
for (int y = 0; y < viewHeight; y++) {
for (int x = 0; x < width; x++) {
final ColoredChar cell = _screen[y][x];
_screen[y][x] = ColoredChar(
cell.char,
_applyBonusFlashToColor(cell.rawColor),
cell.rawBackgroundColor == null
? null
: _applyBonusFlashToColor(cell.rawBackgroundColor!),
);
}
}
}
void _applyBonusFlashToScene() {
for (int y = 0; y < _terminalPixelHeight; y++) {
for (int x = projectionOffsetX; x < _viewportRightX; x++) {
_scenePixels[y][x] = _applyBonusFlashToColor(_scenePixels[y][x]);
}
}
}
int _applyBonusFlashToColor(int color) {
final double intensity = engine.player.bonusFlash;
final double whiteMix = 0.65 * intensity;
int r = color & 0xFF;
int g = (color >> 8) & 0xFF;
int b = (color >> 16) & 0xFF;
r = (r + ((255 - r) * whiteMix)).round().clamp(0, 255);
g = (g + ((255 - g) * whiteMix)).round().clamp(0, 255);
b = (b + ((255 - b) * whiteMix)).round().clamp(0, 255);
return (0xFF000000) | (b << 16) | (g << 8) | r;
}
int _scaleColor(int color, double brightness) {
int r = ((color & 0xFF) * brightness).toInt().clamp(0, 255);
int g = (((color >> 8) & 0xFF) * brightness).toInt().clamp(0, 255);
@@ -248,7 +248,7 @@ abstract class RendererBackend<T>
_drawHudKeySlot(
engine,
vgaImages,
startX: 30,
startX: 240,
startY: 164,
hasKey: engine.player.hasGoldKey,
presentKey: HudKey.goldKeyIcon,
@@ -256,7 +256,7 @@ abstract class RendererBackend<T>
_drawHudKeySlot(
engine,
vgaImages,
startX: 30,
startX: 240,
startY: 180,
hasKey: engine.player.hasSilverKey,
presentKey: HudKey.silverKeyIcon,
@@ -407,16 +407,16 @@ class SixelRenderer extends CliRendererBackend<String> {
final int color = isPushwall
? pushwallColor
: objTile == MapObject.normalExitTrigger
? normalExitColor
: objTile == MapObject.secretExitTrigger
? secretExitColor
: objTile == MapObject.goldKey
? goldKeyColor
: objTile == MapObject.silverKey
? silverKeyColor
: (wallTile == 0
? floorColor
: (wallTile >= 90 ? doorColor : wallColor));
? normalExitColor
: objTile == MapObject.secretExitTrigger
? secretExitColor
: objTile == MapObject.goldKey
? goldKeyColor
: objTile == MapObject.silverKey
? silverKeyColor
: (wallTile == 0
? floorColor
: (wallTile >= 90 ? doorColor : wallColor));
_fillMapRect(
mapStartX + (x * tileSize),
mapStartY + (y * tileSize),
@@ -1336,11 +1336,16 @@ class SixelRenderer extends CliRendererBackend<String> {
sb.write('\x1bPq');
sb.write('"1;1;$_outputWidth;$_outputHeight');
double damageIntensity = engine.difficulty == null
? 0.0
: engine.player.damageFlash;
int redBoost = (150 * damageIntensity).toInt();
double colorDrop = 1.0 - (0.5 * damageIntensity);
final bool gameplayActive = engine.difficulty != null;
final double damageIntensity = gameplayActive
? engine.player.damageFlash
: 0.0;
final double bonusIntensity = gameplayActive && damageIntensity <= 0.0
? engine.player.bonusFlash
: 0.0;
final int redBoost = (150 * damageIntensity).toInt();
final double colorDrop = 1.0 - (0.5 * damageIntensity);
final double whiteMix = 0.65 * bonusIntensity;
for (int i = 0; i < 256; i++) {
int color = ColorPalette.vga32Bit[i];
@@ -1352,6 +1357,10 @@ class SixelRenderer extends CliRendererBackend<String> {
r = (r + redBoost).clamp(0, 255);
g = (g * colorDrop).toInt().clamp(0, 255);
b = (b * colorDrop).toInt().clamp(0, 255);
} else if (bonusIntensity > 0) {
r = (r + ((255 - r) * whiteMix)).round().clamp(0, 255);
g = (g + ((255 - g) * whiteMix)).round().clamp(0, 255);
b = (b + ((255 - b) * whiteMix)).round().clamp(0, 255);
}
int sixelR = (r * 100) ~/ 255;
@@ -1397,9 +1397,12 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
@override
FrameBuffer finalizeFrame() {
// If the player took damage, overlay a red tint across the 3D view
if (engine.difficulty != null && engine.player.damageFlash > 0) {
_applyDamageFlash();
if (engine.difficulty != null) {
if (engine.player.damageFlash > 0) {
_applyDamageFlash();
} else if (engine.player.bonusFlash > 0) {
_applyBonusFlash();
}
}
return _buffer; // Return the fully painted pixel array
}
@@ -1467,4 +1470,26 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
}
}
}
void _applyBonusFlash() {
final double intensity = engine.player.bonusFlash;
final double whiteMix = 0.65 * intensity;
for (int y = 0; y < viewHeight; y++) {
for (int x = 0; x < width; x++) {
final int index = y * width + x;
final int color = _buffer.pixels[index];
int r = color & 0xFF;
int g = (color >> 8) & 0xFF;
int b = (color >> 16) & 0xFF;
r = (r + ((255 - r) * whiteMix)).round().clamp(0, 255);
g = (g + ((255 - g) * whiteMix)).round().clamp(0, 255);
b = (b + ((255 - b) * whiteMix)).round().clamp(0, 255);
_buffer.pixels[index] = (0xFF000000) | (b << 16) | (g << 8) | r;
}
}
}
}