feat: Add mutant death and god mode face animations, update HUD rendering and player damage handling

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-23 12:29:56 +01:00
parent 400ce4f680
commit 827b8c779e
8 changed files with 124 additions and 5 deletions
@@ -30,6 +30,10 @@ class Player {
int _chaingunPickupFaceMsRemaining = 0;
static const int _chaingunPickupFaceDurationMs = 900;
// Additional classic face states.
bool _mutantDeathFaceActive = false;
bool _godModeFaceEnabled = false;
// Classic face animation (UpdateFace/FACETICS random glance frames)
math.Random _faceRng = math.Random(0);
int _faceFrame = 0;
@@ -69,6 +73,8 @@ class Player {
Coordinate2D get position => Coordinate2D(x, y);
bool get isChaingunPickupFaceActive => _chaingunPickupFaceMsRemaining > 0;
bool get isMutantDeathFaceActive => _mutantDeathFaceActive;
bool get isGodModeFaceEnabled => _godModeFaceEnabled;
int get hudFaceFrame => _faceFrame;
void setHudFaceAnimationSeed(int seed) {
@@ -76,6 +82,12 @@ class Player {
_faceFrame = 0;
_faceCountTics = 0.0;
_nextFaceChangeThreshold = _faceRng.nextInt(256);
_mutantDeathFaceActive = false;
_godModeFaceEnabled = false;
}
void setGodModeFaceEnabled(bool enabled) {
_godModeFaceEnabled = enabled;
}
// --- General Update ---
@@ -156,13 +168,15 @@ class Player {
// --- Health & Damage ---
void takeDamage(int damage) {
void takeDamage(int damage, {EnemyType? attackerType}) {
health = math.max(0, health - damage);
// Spike the damage flash based on how much damage was taken
// A 10 damage hit gives a 0.5 flash, a 20 damage hit maxes it out at 1.0
damageFlash = math.min(1.0, damageFlash + (damage * 0.05));
_chaingunPickupFaceMsRemaining = 0;
_mutantDeathFaceActive =
health <= 0 && attackerType == EnemyType.mutant;
if (health <= 0) {
log("[PLAYER] Died! Final Score: $score");
@@ -178,6 +192,7 @@ class Player {
}
health = newHealth;
_chaingunPickupFaceMsRemaining = 0;
_mutantDeathFaceActive = false;
}
void triggerBonusFlash() {
@@ -1032,7 +1032,10 @@ class WolfEngine {
tryOpenDoor: doorManager.tryOpenDoor,
onDamagePlayer: (int damage) {
final difficultyMode = difficulty ?? Difficulty.medium;
player.takeDamage(difficultyMode.scaleIncomingEnemyDamage(damage));
player.takeDamage(
difficultyMode.scaleIncomingEnemyDamage(damage),
attackerType: entity.type,
);
},
onPlaySound: audio.playSoundEffect,
);
@@ -45,6 +45,7 @@ class RetailHudModule extends HudModule {
HudKey.faceNearDeath: 124,
HudKey.faceDead: 127,
HudKey.faceGotGatling: 128,
HudKey.faceMutantDeath: 129,
// Weapon icons.
HudKey.pistolIcon: 89,
HudKey.machineGunIcon: 90,
@@ -45,6 +45,7 @@ class SharewareHudModule extends HudModule {
HudKey.faceNearDeath: 124,
HudKey.faceDead: 127,
HudKey.faceGotGatling: 128,
HudKey.faceMutantDeath: 129,
HudKey.pistolIcon: 89,
HudKey.machineGunIcon: 90,
HudKey.chainGunIcon: 91,
@@ -38,6 +38,7 @@ class SpearDemoHudModule extends HudModule {
HudKey.faceNearDeath: 112,
HudKey.faceDead: 122, // BJOUCHPIC
HudKey.faceGotGatling: 116, // GOTGATLINGPIC
HudKey.faceGodMode: 117, // GODMODEFACE1PIC
HudKey.pistolIcon: 77, // GUNPIC
HudKey.machineGunIcon: 78, // MACHINEGUNPIC
HudKey.chainGunIcon: 79, // GATLINGGUNPIC
@@ -25,6 +25,8 @@ enum HudKey {
faceNearDeath('faceNearDeath'), // health 1-4
faceDead('faceDead'), // health <= 0
faceGotGatling('faceGotGatling'),
faceMutantDeath('faceMutantDeath'),
faceGodMode('faceGodMode'),
// --- Weapon icons ---
pistolIcon('pistolIcon'),
@@ -228,6 +228,20 @@ abstract class RendererBackend<T>
faceIndex =
engine.data.registry.hud.resolve(HudKey.faceGotGatling)?.vgaIndex ??
-1;
} else if (engine.player.isMutantDeathFaceActive) {
faceIndex =
engine.data.registry.hud.resolve(HudKey.faceMutantDeath)?.vgaIndex ??
-1;
if (faceIndex < 0) {
faceIndex =
engine.data.registry.hud.resolve(HudKey.faceDead)?.vgaIndex ?? -1;
}
} else if (engine.player.isGodModeFaceEnabled) {
final int baseGodFace =
engine.data.registry.hud.resolve(HudKey.faceGodMode)?.vgaIndex ?? -1;
if (baseGodFace >= 0) {
faceIndex = baseGodFace + engine.player.hudFaceFrame;
}
} else {
final HudKey faceKey = engine.data.registry.hud.faceKeyForHealth(
engine.player.health,
@@ -241,6 +255,19 @@ abstract class RendererBackend<T>
}
}
if (faceIndex < 0) {
final HudKey fallbackKey = engine.data.registry.hud.faceKeyForHealth(
engine.player.health,
);
final int fallbackBase =
engine.data.registry.hud.resolve(fallbackKey)?.vgaIndex ?? -1;
if (fallbackBase >= 0) {
faceIndex = fallbackKey == HudKey.faceDead
? fallbackBase
: fallbackBase + engine.player.hudFaceFrame;
}
}
if (faceIndex >= 0 && faceIndex < vgaImages.length) {
blitHudVgaImage(vgaImages[faceIndex], 136, 164);
}