feat: Implement player locomotion constants and update movement logic in engine

feat: Add key icons to HUD modules and implement key rendering in HUD
test: Add player movement and rotation parity tests to ensure consistency with classic Wolf3D
test: Enhance HUD rendering tests for gold and silver key icons

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-23 12:04:25 +01:00
parent 7941c2902c
commit 604923618a
15 changed files with 434 additions and 28 deletions
@@ -416,11 +416,15 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
final int mapBgColor = ColorPalette.vga32Bit[0];
final int floorColor = ColorPalette.vga32Bit[8];
final int wallColor = ColorPalette.vga32Bit[7];
final int doorColor = ColorPalette.vga32Bit[14];
final int doorColor = ColorPalette.vga32Bit[9];
final int pushwallColor = ColorPalette.vga32Bit[6];
final int normalExitColor = ColorPalette.vga32Bit[2];
final int secretExitColor = ColorPalette.vga32Bit[10];
final int goldKeyColor = ColorPalette.vga32Bit[11];
final int silverKeyColor = ColorPalette.vga32Bit[16];
final int enemyColor = ColorPalette.vga32Bit[12];
final int playerColor = ColorPalette.vga32Bit[10];
final int facingColor = ColorPalette.vga32Bit[15];
final int playerColor = ColorPalette.vga32Bit[15];
final int facingColor = ColorPalette.vga32Bit[14];
final int viewportY = 0;
final int viewportX = _usesTerminalLayout ? projectionOffsetX : 0;
@@ -471,15 +475,24 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
for (int y = 0; y < 64; y++) {
for (int x = 0; x < 64; x++) {
final int tileId = engine.currentLevel[y][x];
final int wallTile = engine.currentLevel[y][x];
final int objTile = engine.activeLevel.objectGrid[y][x];
final bool isPushwall = engine.pushwallManager.pushwalls.containsKey(
'$x,$y',
);
final int color = isPushwall
? pushwallColor
: (tileId == 0
? floorColor
: (tileId >= 90 ? doorColor : wallColor));
: objTile == MapObject.normalExitTrigger
? 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),
@@ -184,6 +184,7 @@ abstract class RendererBackend<T>
_drawHudNumber(engine, vgaImages, engine.player.ammo, 232, 176);
_drawHudFace(engine, vgaImages);
_drawHudWeaponIcon(engine, vgaImages);
_drawHudKeys(engine, vgaImages);
}
void _drawHudNumber(
@@ -243,6 +244,41 @@ abstract class RendererBackend<T>
}
}
void _drawHudKeys(WolfEngine engine, List<VgaImage> vgaImages) {
_drawHudKeySlot(
engine,
vgaImages,
startX: 30,
startY: 164,
hasKey: engine.player.hasGoldKey,
presentKey: HudKey.goldKeyIcon,
);
_drawHudKeySlot(
engine,
vgaImages,
startX: 30,
startY: 180,
hasKey: engine.player.hasSilverKey,
presentKey: HudKey.silverKeyIcon,
);
}
void _drawHudKeySlot(
WolfEngine engine,
List<VgaImage> vgaImages, {
required int startX,
required int startY,
required bool hasKey,
required HudKey presentKey,
}) {
final HudKey keyIcon = hasKey ? presentKey : HudKey.noKeyIcon;
final keyRef = engine.data.registry.hud.resolve(keyIcon);
final int keyIndex = keyRef?.vgaIndex ?? -1;
if (keyIndex >= 0 && keyIndex < vgaImages.length) {
blitHudVgaImage(vgaImages[keyIndex], startX, startY);
}
}
/// Calculates depth-based lighting falloff (0.0 to 1.0).
/// While the original Wolf3D didn't use depth fog, this provides a great
/// atmospheric effect for custom backends (like ASCII dithering).
@@ -370,11 +370,15 @@ class SixelRenderer extends CliRendererBackend<String> {
const int mapBgColor = 0;
const int floorColor = 8;
const int wallColor = 7;
const int doorColor = 14;
const int doorColor = 9;
const int pushwallColor = 6;
const int normalExitColor = 2;
const int secretExitColor = 10;
const int goldKeyColor = 11;
const int silverKeyColor = 16;
const int enemyColor = 12;
const int playerColor = 10;
const int facingColor = 15;
const int playerColor = 15;
const int facingColor = 14;
_fillMapRect(0, 0, width, viewHeight, mapBgColor);
@@ -395,15 +399,24 @@ class SixelRenderer extends CliRendererBackend<String> {
for (int y = 0; y < 64; y++) {
for (int x = 0; x < 64; x++) {
final int tileId = engine.currentLevel[y][x];
final int wallTile = engine.currentLevel[y][x];
final int objTile = engine.activeLevel.objectGrid[y][x];
final bool isPushwall = engine.pushwallManager.pushwalls.containsKey(
'$x,$y',
);
final int color = isPushwall
? pushwallColor
: (tileId == 0
? floorColor
: (tileId >= 90 ? doorColor : wallColor));
: objTile == MapObject.normalExitTrigger
? 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),
@@ -150,11 +150,15 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
final int mapBgColor = ColorPalette.vga32Bit[0];
final int floorColor = ColorPalette.vga32Bit[8];
final int wallColor = ColorPalette.vga32Bit[7];
final int doorColor = ColorPalette.vga32Bit[14];
final int doorColor = ColorPalette.vga32Bit[9];
final int pushwallColor = ColorPalette.vga32Bit[6];
final int normalExitColor = ColorPalette.vga32Bit[2];
final int secretExitColor = ColorPalette.vga32Bit[10];
final int goldKeyColor = ColorPalette.vga32Bit[11];
final int silverKeyColor = ColorPalette.vga32Bit[16];
final int enemyColor = ColorPalette.vga32Bit[12];
final int playerColor = ColorPalette.vga32Bit[10];
final int facingColor = ColorPalette.vga32Bit[15];
final int playerColor = ColorPalette.vga32Bit[15];
final int facingColor = ColorPalette.vga32Bit[14];
_fillMenuPanel(0, 0, width, viewHeight, mapBgColor);
@@ -175,15 +179,20 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
for (int y = 0; y < 64; y++) {
for (int x = 0; x < 64; x++) {
final int tileId = engine.currentLevel[y][x];
final int wallTile = engine.currentLevel[y][x];
final int objTile = engine.activeLevel.objectGrid[y][x];
final bool isPushwall = engine.pushwallManager.pushwalls.containsKey(
'$x,$y',
);
final int color = isPushwall
? pushwallColor
: (tileId == 0
: objTile == MapObject.normalExitTrigger
? normalExitColor
: objTile == MapObject.secretExitTrigger
? secretExitColor
: (wallTile == 0
? floorColor
: (tileId >= 90 ? doorColor : wallColor));
: (wallTile >= 90 ? doorColor : wallColor));
_fillMenuPanel(
mapStartX + (x * tileSize),
mapStartY + (y * tileSize),