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:
@@ -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)));
|
||||
}
|
||||
Reference in New Issue
Block a user