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
@@ -0,0 +1,41 @@
import 'dart:math' as math;
/// Canonical player movement and rotation constants derived directly from the
/// original Wolfenstein 3D source code (WL_PLAY.C / WL_AGENT.C).
///
/// Source constants (keyboard, walking state):
/// BASEMOVE = 35, RUNMOVE = 70
/// MOVESCALE = 150, BACKMOVESCALE = 100
/// BASETURN = 35, RUNTURN = 70, ANGLESCALE = 20
/// TILEGLOBAL = 65536 (fixed-point units per tile), TICRATE = 70
abstract final class PlayerLocomotionConstants {
static const double basemove = 35.0;
static const double runmove = 70.0;
static const double movescale = 150.0;
static const double backmovescale = 100.0;
static const double baseturn = 35.0;
static const double runturn = 70.0;
static const double anglescale = 20.0;
static const double tileglobal = 65536.0;
static const double ticrate = 70.0;
/// Walking forward speed in tiles per second.
///
/// Derived from: `(BASEMOVE * MOVESCALE * TICRATE) / TILEGLOBAL`
static const double forwardTilesPerSecond =
(basemove * movescale * ticrate) / tileglobal;
/// Walking backward speed in tiles per second (intentionally slower than
/// forward in the original game).
///
/// Derived from: `(BASEMOVE * BACKMOVESCALE * TICRATE) / TILEGLOBAL`
static const double backwardTilesPerSecond =
(basemove * backmovescale * ticrate) / tileglobal;
/// Walking turn rate in radians per second.
///
/// Derived from: `(BASETURN / ANGLESCALE) * TICRATE` in degrees/sec,
/// then converted to radians.
static const double turnRadiansPerSecond =
(baseturn / anglescale) * ticrate * (math.pi / 180.0);
}
@@ -877,9 +877,15 @@ class WolfEngine {
// Standardize movement to 60 FPS (16.66ms per frame) // Standardize movement to 60 FPS (16.66ms per frame)
final double timeScale = delta.inMilliseconds / 16.666; final double timeScale = delta.inMilliseconds / 16.666;
// Apply the timeScale multiplier to ensure consistent speed at any framerate // Apply the timeScale multiplier to ensure consistent speed at any framerate.
final double moveSpeed = 0.10 * timeScale; // Rates are derived from the canonical Wolf3D source constants; see
final double turnSpeed = 0.08 * timeScale; // PlayerLocomotionConstants for the full derivation.
final double forwardMoveSpeed =
PlayerLocomotionConstants.forwardTilesPerSecond / 60.0 * timeScale;
final double backwardMoveSpeed =
PlayerLocomotionConstants.backwardTilesPerSecond / 60.0 * timeScale;
final double turnSpeed =
PlayerLocomotionConstants.turnRadiansPerSecond / 60.0 * timeScale;
Coordinate2D movement = const Coordinate2D(0, 0); Coordinate2D movement = const Coordinate2D(0, 0);
double dAngle = 0.0; double dAngle = 0.0;
@@ -910,8 +916,8 @@ class WolfEngine {
math.cos(player.angle), math.cos(player.angle),
math.sin(player.angle), math.sin(player.angle),
); );
if (input.isMovingForward) movement += forwardVec * moveSpeed; if (input.isMovingForward) movement += forwardVec * forwardMoveSpeed;
if (input.isMovingBackward) movement -= forwardVec * moveSpeed; if (input.isMovingBackward) movement -= forwardVec * backwardMoveSpeed;
if (input.isInteracting) { if (input.isInteracting) {
int targetX = (player.x + math.cos(player.angle)).toInt(); int targetX = (player.x + math.cos(player.angle)).toInt();
@@ -48,6 +48,9 @@ class RetailHudModule extends HudModule {
HudKey.pistolIcon: 89, HudKey.pistolIcon: 89,
HudKey.machineGunIcon: 90, HudKey.machineGunIcon: 90,
HudKey.chainGunIcon: 91, HudKey.chainGunIcon: 91,
HudKey.noKeyIcon: 92,
HudKey.goldKeyIcon: 93,
HudKey.silverKeyIcon: 94,
}; };
@override @override
@@ -47,6 +47,9 @@ class SharewareHudModule extends HudModule {
HudKey.pistolIcon: 89, HudKey.pistolIcon: 89,
HudKey.machineGunIcon: 90, HudKey.machineGunIcon: 90,
HudKey.chainGunIcon: 91, HudKey.chainGunIcon: 91,
HudKey.noKeyIcon: 92,
HudKey.goldKeyIcon: 93,
HudKey.silverKeyIcon: 94,
}; };
int? _offset; int? _offset;
@@ -65,7 +68,8 @@ class SharewareHudModule extends HudModule {
return 0; return 0;
} }
int get _effectiveOffset => useOriginalWl1Map ? _originalWl1Offset : (_offset ?? 0); int get _effectiveOffset =>
useOriginalWl1Map ? _originalWl1Offset : (_offset ?? 0);
@override @override
HudAssetRef? resolve(HudKey key) { HudAssetRef? resolve(HudKey key) {
@@ -40,6 +40,9 @@ class SpearDemoHudModule extends HudModule {
HudKey.pistolIcon: 77, // GUNPIC HudKey.pistolIcon: 77, // GUNPIC
HudKey.machineGunIcon: 78, // MACHINEGUNPIC HudKey.machineGunIcon: 78, // MACHINEGUNPIC
HudKey.chainGunIcon: 79, // GATLINGGUNPIC HudKey.chainGunIcon: 79, // GATLINGGUNPIC
HudKey.noKeyIcon: 80,
HudKey.goldKeyIcon: 81,
HudKey.silverKeyIcon: 82,
}; };
@override @override
@@ -28,7 +28,12 @@ enum HudKey {
// --- Weapon icons --- // --- Weapon icons ---
pistolIcon('pistolIcon'), pistolIcon('pistolIcon'),
machineGunIcon('machineGunIcon'), machineGunIcon('machineGunIcon'),
chainGunIcon('chainGunIcon') chainGunIcon('chainGunIcon'),
// --- Key icons ---
noKeyIcon('noKeyIcon'),
goldKeyIcon('goldKeyIcon'),
silverKeyIcon('silverKeyIcon')
; ;
const HudKey(this.id); const HudKey(this.id);
@@ -416,11 +416,15 @@ class AsciiRenderer extends CliRendererBackend<dynamic> {
final int mapBgColor = ColorPalette.vga32Bit[0]; final int mapBgColor = ColorPalette.vga32Bit[0];
final int floorColor = ColorPalette.vga32Bit[8]; final int floorColor = ColorPalette.vga32Bit[8];
final int wallColor = ColorPalette.vga32Bit[7]; 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 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 enemyColor = ColorPalette.vga32Bit[12];
final int playerColor = ColorPalette.vga32Bit[10]; final int playerColor = ColorPalette.vga32Bit[15];
final int facingColor = ColorPalette.vga32Bit[15]; final int facingColor = ColorPalette.vga32Bit[14];
final int viewportY = 0; final int viewportY = 0;
final int viewportX = _usesTerminalLayout ? projectionOffsetX : 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 y = 0; y < 64; y++) {
for (int x = 0; x < 64; x++) { 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( final bool isPushwall = engine.pushwallManager.pushwalls.containsKey(
'$x,$y', '$x,$y',
); );
final int color = isPushwall final int color = isPushwall
? pushwallColor ? pushwallColor
: (tileId == 0 : objTile == MapObject.normalExitTrigger
? floorColor ? normalExitColor
: (tileId >= 90 ? doorColor : wallColor)); : objTile == MapObject.secretExitTrigger
? secretExitColor
: objTile == MapObject.goldKey
? goldKeyColor
: objTile == MapObject.silverKey
? silverKeyColor
: (wallTile == 0
? floorColor
: (wallTile >= 90 ? doorColor : wallColor));
_fillMapRect( _fillMapRect(
mapStartX + (x * tileSize), mapStartX + (x * tileSize),
mapStartY + (y * tileSize), mapStartY + (y * tileSize),
@@ -184,6 +184,7 @@ abstract class RendererBackend<T>
_drawHudNumber(engine, vgaImages, engine.player.ammo, 232, 176); _drawHudNumber(engine, vgaImages, engine.player.ammo, 232, 176);
_drawHudFace(engine, vgaImages); _drawHudFace(engine, vgaImages);
_drawHudWeaponIcon(engine, vgaImages); _drawHudWeaponIcon(engine, vgaImages);
_drawHudKeys(engine, vgaImages);
} }
void _drawHudNumber( 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). /// Calculates depth-based lighting falloff (0.0 to 1.0).
/// While the original Wolf3D didn't use depth fog, this provides a great /// While the original Wolf3D didn't use depth fog, this provides a great
/// atmospheric effect for custom backends (like ASCII dithering). /// atmospheric effect for custom backends (like ASCII dithering).
@@ -370,11 +370,15 @@ class SixelRenderer extends CliRendererBackend<String> {
const int mapBgColor = 0; const int mapBgColor = 0;
const int floorColor = 8; const int floorColor = 8;
const int wallColor = 7; const int wallColor = 7;
const int doorColor = 14; const int doorColor = 9;
const int pushwallColor = 6; 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 enemyColor = 12;
const int playerColor = 10; const int playerColor = 15;
const int facingColor = 15; const int facingColor = 14;
_fillMapRect(0, 0, width, viewHeight, mapBgColor); _fillMapRect(0, 0, width, viewHeight, mapBgColor);
@@ -395,15 +399,24 @@ class SixelRenderer extends CliRendererBackend<String> {
for (int y = 0; y < 64; y++) { for (int y = 0; y < 64; y++) {
for (int x = 0; x < 64; x++) { 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( final bool isPushwall = engine.pushwallManager.pushwalls.containsKey(
'$x,$y', '$x,$y',
); );
final int color = isPushwall final int color = isPushwall
? pushwallColor ? pushwallColor
: (tileId == 0 : objTile == MapObject.normalExitTrigger
? floorColor ? normalExitColor
: (tileId >= 90 ? doorColor : wallColor)); : objTile == MapObject.secretExitTrigger
? secretExitColor
: objTile == MapObject.goldKey
? goldKeyColor
: objTile == MapObject.silverKey
? silverKeyColor
: (wallTile == 0
? floorColor
: (wallTile >= 90 ? doorColor : wallColor));
_fillMapRect( _fillMapRect(
mapStartX + (x * tileSize), mapStartX + (x * tileSize),
mapStartY + (y * tileSize), mapStartY + (y * tileSize),
@@ -150,11 +150,15 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
final int mapBgColor = ColorPalette.vga32Bit[0]; final int mapBgColor = ColorPalette.vga32Bit[0];
final int floorColor = ColorPalette.vga32Bit[8]; final int floorColor = ColorPalette.vga32Bit[8];
final int wallColor = ColorPalette.vga32Bit[7]; 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 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 enemyColor = ColorPalette.vga32Bit[12];
final int playerColor = ColorPalette.vga32Bit[10]; final int playerColor = ColorPalette.vga32Bit[15];
final int facingColor = ColorPalette.vga32Bit[15]; final int facingColor = ColorPalette.vga32Bit[14];
_fillMenuPanel(0, 0, width, viewHeight, mapBgColor); _fillMenuPanel(0, 0, width, viewHeight, mapBgColor);
@@ -175,15 +179,20 @@ class SoftwareRenderer extends RendererBackend<FrameBuffer> {
for (int y = 0; y < 64; y++) { for (int y = 0; y < 64; y++) {
for (int x = 0; x < 64; x++) { 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( final bool isPushwall = engine.pushwallManager.pushwalls.containsKey(
'$x,$y', '$x,$y',
); );
final int color = isPushwall final int color = isPushwall
? pushwallColor ? pushwallColor
: (tileId == 0 : objTile == MapObject.normalExitTrigger
? normalExitColor
: objTile == MapObject.secretExitTrigger
? secretExitColor
: (wallTile == 0
? floorColor ? floorColor
: (tileId >= 90 ? doorColor : wallColor)); : (wallTile >= 90 ? doorColor : wallColor));
_fillMenuPanel( _fillMenuPanel(
mapStartX + (x * tileSize), mapStartX + (x * tileSize),
mapStartY + (y * tileSize), mapStartY + (y * tileSize),
@@ -11,6 +11,7 @@ export 'src/engine/input/engine_input.dart';
export 'src/engine/managers/door_manager.dart'; export 'src/engine/managers/door_manager.dart';
export 'src/engine/managers/pushwall_manager.dart'; export 'src/engine/managers/pushwall_manager.dart';
export 'src/engine/player/player.dart'; export 'src/engine/player/player.dart';
export 'src/engine/player_locomotion_constants.dart';
export 'src/engine/rendering/renderer_settings.dart'; export 'src/engine/rendering/renderer_settings.dart';
export 'src/engine/rendering/renderer_settings_persistence.dart'; export 'src/engine/rendering/renderer_settings_persistence.dart';
export 'src/engine/wolf_3d_engine_base.dart'; export 'src/engine/wolf_3d_engine_base.dart';
@@ -0,0 +1,129 @@
import 'dart:typed_data';
import 'package:test/test.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
import 'package:wolf_3d_dart/wolf_3d_input.dart';
void main() {
group('Player movement parity', () {
test('matches classic Wolf3D forward walking speed', () {
final input = _HeldMoveInput(forward: true, backward: false);
final engine = _buildEngine(input: input);
engine.init();
final startX = engine.player.x;
engine.tick(const Duration(milliseconds: 1000));
final movedForward = engine.player.x - startX;
expect(
movedForward,
closeTo(PlayerLocomotionConstants.forwardTilesPerSecond, 0.01),
);
});
test('matches classic Wolf3D backward walking speed', () {
final input = _HeldMoveInput(forward: false, backward: true);
final engine = _buildEngine(input: input);
engine.init();
final startX = engine.player.x;
engine.tick(const Duration(milliseconds: 1000));
final movedBackward = startX - engine.player.x;
expect(
movedBackward,
closeTo(PlayerLocomotionConstants.backwardTilesPerSecond, 0.01),
);
});
});
}
class _HeldMoveInput extends Wolf3dInput {
_HeldMoveInput({required this.forward, required this.backward});
final bool forward;
final bool backward;
@override
void update() {
isMovingForward = forward;
isMovingBackward = backward;
isTurningLeft = false;
isTurningRight = false;
isMapToggle = false;
isInteracting = false;
isFiring = false;
isBack = false;
requestedWeapon = null;
menuTapX = null;
menuTapY = null;
}
}
WolfEngine _buildEngine({
required Wolf3dInput input,
}) {
final wallGrid = _buildGrid();
final objectGrid = _buildGrid();
_fillBoundaries(wallGrid, 2);
objectGrid[20][20] = MapObject.playerEast;
return WolfEngine(
data: WolfensteinData(
version: GameVersion.shareware,
dataVersion: DataVersion.unknown,
registry: RetailAssetRegistry(),
walls: [
_solidSprite(1),
_solidSprite(1),
_solidSprite(2),
_solidSprite(2),
],
sprites: List.generate(436, (_) => _solidSprite(255)),
sounds: const [],
adLibSounds: const [],
music: const [],
vgaImages: const [],
episodes: [
Episode(
name: 'Episode 1',
levels: [
WolfLevel(
name: 'Level 1',
wallGrid: wallGrid,
areaGrid: List.generate(64, (_) => List.filled(64, -1)),
objectGrid: objectGrid,
music: Music.level01,
),
],
),
],
),
difficulty: Difficulty.medium,
startingEpisode: 0,
frameBuffer: FrameBuffer(64, 64),
input: input,
onGameWon: () {},
);
}
SpriteMap _buildGrid() => List.generate(64, (_) => List.filled(64, 0));
void _fillBoundaries(SpriteMap grid, int wallId) {
for (int i = 0; i < 64; i++) {
grid[0][i] = wallId;
grid[63][i] = wallId;
grid[i][0] = wallId;
grid[i][63] = wallId;
}
}
Sprite _solidSprite(int colorIndex) {
return Sprite(Uint8List.fromList(List.filled(64 * 64, colorIndex)));
}
@@ -0,0 +1,111 @@
import 'dart:typed_data';
import 'package:test/test.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
import 'package:wolf_3d_dart/wolf_3d_input.dart';
void main() {
group('Player rotation parity', () {
test('matches classic Wolf3D keyboard walking turn rate', () {
final input = _HeldTurnInput(turnRight: true);
final engine = _buildEngine(input: input);
engine.init();
final startingAngle = engine.player.angle;
engine.tick(const Duration(milliseconds: 1000));
final turned = engine.player.angle - startingAngle;
expect(
turned,
closeTo(PlayerLocomotionConstants.turnRadiansPerSecond, 0.01),
);
});
});
}
class _HeldTurnInput extends Wolf3dInput {
_HeldTurnInput({required this.turnRight});
final bool turnRight;
@override
void update() {
isTurningRight = turnRight;
isTurningLeft = !turnRight;
isMovingForward = false;
isMovingBackward = false;
isMapToggle = false;
isInteracting = false;
isFiring = false;
isBack = false;
requestedWeapon = null;
menuTapX = null;
menuTapY = null;
}
}
WolfEngine _buildEngine({
required Wolf3dInput input,
}) {
final wallGrid = _buildGrid();
final objectGrid = _buildGrid();
_fillBoundaries(wallGrid, 2);
objectGrid[2][2] = MapObject.playerEast;
return WolfEngine(
data: WolfensteinData(
version: GameVersion.shareware,
dataVersion: DataVersion.unknown,
registry: RetailAssetRegistry(),
walls: [
_solidSprite(1),
_solidSprite(1),
_solidSprite(2),
_solidSprite(2),
],
sprites: List.generate(436, (_) => _solidSprite(255)),
sounds: const [],
adLibSounds: const [],
music: const [],
vgaImages: const [],
episodes: [
Episode(
name: 'Episode 1',
levels: [
WolfLevel(
name: 'Level 1',
wallGrid: wallGrid,
areaGrid: List.generate(64, (_) => List.filled(64, -1)),
objectGrid: objectGrid,
music: Music.level01,
),
],
),
],
),
difficulty: Difficulty.medium,
startingEpisode: 0,
frameBuffer: FrameBuffer(64, 64),
input: input,
onGameWon: () {},
);
}
SpriteMap _buildGrid() => List.generate(64, (_) => List.filled(64, 0));
void _fillBoundaries(SpriteMap grid, int wallId) {
for (int i = 0; i < 64; i++) {
grid[0][i] = wallId;
grid[63][i] = wallId;
grid[i][0] = wallId;
grid[i][63] = wallId;
}
}
Sprite _solidSprite(int colorIndex) {
return Sprite(Uint8List.fromList(List.filled(64 * 64, colorIndex)));
}
@@ -27,6 +27,36 @@ void main() {
expect(livesDigitCall.imageIndex, expectedDigitIndex); expect(livesDigitCall.imageIndex, expectedDigitIndex);
}); });
test('standard VGA HUD renders gold key icon when collected', () {
final engine = _buildEngine();
engine.init();
engine.player.hasGoldKey = true;
final renderer = _HudProbeRenderer(vgaImages: engine.data.vgaImages);
renderer.drawHudForTest(engine);
final expectedGoldKeyIndex = engine.data.registry.hud
.resolve(HudKey.goldKeyIcon)
?.vgaIndex;
final expectedNoKeyIndex = engine.data.registry.hud
.resolve(HudKey.noKeyIcon)
?.vgaIndex;
expect(expectedGoldKeyIndex, isNotNull);
expect(expectedNoKeyIndex, isNotNull);
final goldKeyCall = renderer.drawCalls.firstWhere(
(call) => call.startY == 164 && call.startX == 30,
orElse: () => throw StateError('Gold key slot was not rendered.'),
);
final silverKeyCall = renderer.drawCalls.firstWhere(
(call) => call.startY == 180 && call.startX == 30,
orElse: () => throw StateError('Silver key slot was not rendered.'),
);
expect(goldKeyCall.imageIndex, expectedGoldKeyIndex);
expect(silverKeyCall.imageIndex, expectedNoKeyIndex);
});
} }
class _HudProbeRenderer extends RendererBackend<int> { class _HudProbeRenderer extends RendererBackend<int> {
@@ -45,7 +45,8 @@ void main() {
expect(changedPixels, greaterThan(mapPixels.length ~/ 5)); expect(changedPixels, greaterThan(mapPixels.length ~/ 5));
expect(mapPixels.contains(ColorPalette.vga32Bit[6]), isTrue); expect(mapPixels.contains(ColorPalette.vga32Bit[6]), isTrue);
expect(mapPixels.contains(ColorPalette.vga32Bit[12]), isTrue); expect(mapPixels.contains(ColorPalette.vga32Bit[12]), isTrue);
expect(mapPixels.contains(ColorPalette.vga32Bit[10]), isTrue); expect(mapPixels.contains(ColorPalette.vga32Bit[15]), isTrue);
expect(mapPixels.contains(ColorPalette.vga32Bit[2]), isTrue);
expect(mapPixels[hudProbeIndex], equals(normalPixels[hudProbeIndex])); expect(mapPixels[hudProbeIndex], equals(normalPixels[hudProbeIndex]));
}); });
}); });
@@ -78,6 +79,7 @@ WolfEngine _buildEngine() {
wallGrid[5][5] = 1; wallGrid[5][5] = 1;
objectGrid[2][2] = MapObject.playerEast; objectGrid[2][2] = MapObject.playerEast;
objectGrid[5][5] = MapObject.pushwallTrigger; objectGrid[5][5] = MapObject.pushwallTrigger;
objectGrid[10][10] = MapObject.normalExitTrigger;
return WolfEngine( return WolfEngine(
data: WolfensteinData( data: WolfensteinData(