Added some additional sound effects

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-18 02:40:53 +01:00
parent 806c9b6966
commit 7fe9a8bc40
8 changed files with 284 additions and 13 deletions

View File

@@ -0,0 +1,206 @@
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_entities.dart';
import 'package:wolf_3d_dart/wolf_3d_input.dart';
void main() {
group('Engine audio events', () {
test('plays pickup sound effect when player collects ammo', () {
final input = _TestInput();
final audio = _CapturingAudio();
final engine = _buildEngine(
input: input,
audio: audio,
objectGridSetup: (grid) {
grid[2][2] = MapObject.playerEast;
},
);
engine.init();
engine.entities.add(
Collectible(
x: engine.player.x,
y: engine.player.y,
spriteIndex: 0,
mapId: MapObject.ammoClip,
type: CollectibleType.ammo,
),
);
engine.tick(const Duration(milliseconds: 16));
expect(audio.sfxIds, contains(WolfSound.getAmmo));
});
test('plays guard alert when guard notices player', () {
final input = _TestInput();
final audio = _CapturingAudio();
final engine = _buildEngine(
input: input,
audio: audio,
objectGridSetup: (grid) {
grid[2][2] = MapObject.playerEast;
grid[2][5] = MapObject.guardStart + 2;
},
);
engine.init();
// Wake-up delay is randomized, so tick long enough to always exceed it.
for (int i = 0; i < 80; i++) {
engine.tick(const Duration(milliseconds: 16));
}
expect(audio.sfxIds, contains(WolfSound.guardHalt));
});
test('plays dog death sound when dog dies', () {
final input = _TestInput();
final audio = _CapturingAudio();
final engine = _buildEngine(
input: input,
audio: audio,
objectGridSetup: (grid) {
grid[2][2] = MapObject.playerEast;
grid[2][4] = MapObject.dogStart;
},
);
engine.init();
final dog = engine.entities.whereType<Dog>().single;
dog.takeDamage(999, 0);
engine.tick(const Duration(milliseconds: 16));
expect(audio.sfxIds, contains(WolfSound.dogDeath));
});
test('plays pushwall sound when triggering a secret wall', () {
final input = _TestInput()..isInteracting = true;
final audio = _CapturingAudio();
final engine = _buildEngine(
input: input,
audio: audio,
wallGridSetup: (grid) {
grid[2][3] = 1;
},
objectGridSetup: (grid) {
grid[2][2] = MapObject.playerEast;
grid[2][3] = MapObject.pushwallTrigger;
},
);
engine.init();
engine.tick(const Duration(milliseconds: 16));
expect(audio.sfxIds, contains(WolfSound.pushWall));
});
});
}
/// Builds a minimal deterministic engine world for audio event tests.
WolfEngine _buildEngine({
required _TestInput input,
required _CapturingAudio audio,
void Function(SpriteMap grid)? wallGridSetup,
void Function(SpriteMap grid)? objectGridSetup,
}) {
final wallGrid = _buildGrid();
final objectGrid = _buildGrid();
_fillBoundaries(wallGrid, 2);
wallGridSetup?.call(wallGrid);
objectGridSetup?.call(objectGrid);
return WolfEngine(
data: WolfensteinData(
version: GameVersion.retail,
walls: [
_solidSprite(1),
_solidSprite(1),
_solidSprite(2),
_solidSprite(2),
],
sprites: List.generate(436, (_) => _solidSprite(255)),
sounds: List.generate(200, (_) => PcmSound(Uint8List(1))),
adLibSounds: const [],
music: const [],
vgaImages: const [],
episodes: [
Episode(
name: 'Episode 1',
levels: [
WolfLevel(
name: 'Test Level',
wallGrid: wallGrid,
objectGrid: objectGrid,
musicIndex: 0,
),
],
),
],
),
difficulty: Difficulty.hard,
startingEpisode: 0,
frameBuffer: FrameBuffer(64, 64),
input: input,
audio: audio,
onGameWon: () {},
);
}
class _TestInput extends Wolf3dInput {
@override
void update() {}
}
/// Captures engine SFX IDs so tests can assert dispatch behavior.
class _CapturingAudio implements EngineAudio {
@override
WolfensteinData? activeGame;
final List<int> sfxIds = [];
@override
Future<void> debugSoundTest() async {}
@override
Future<void> init() async {}
@override
void playLevelMusic(WolfLevel level) {}
@override
void playMenuMusic() {}
@override
void playSoundEffect(int sfxId) {
sfxIds.add(sfxId);
}
@override
void stopMusic() {}
@override
void dispose() {}
}
SpriteMap _buildGrid() => List.generate(64, (_) => List.filled(64, 0));
/// Fills the map borders so entities never walk off the test map.
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;
}
}
/// Produces a simple solid-color sprite frame for synthetic test assets.
Sprite _solidSprite(int colorIndex) {
return Sprite(Uint8List.fromList(List.filled(64 * 64, colorIndex)));
}

View File

@@ -11,7 +11,10 @@ void main() {
});
test('uses the original mutant 18-ID difficulty offset', () {
expect(EnemyType.mutant.mapData.getNormalizedId(216, Difficulty.easy), 216);
expect(
EnemyType.mutant.mapData.getNormalizedId(216, Difficulty.easy),
216,
);
expect(
EnemyType.mutant.mapData.getNormalizedId(234, Difficulty.medium),
216,
@@ -44,4 +47,4 @@ Enemy _spawnEnemy(int mapId) {
final enemy = Enemy.spawn(mapId, 8.5, 8.5, Difficulty.hard);
expect(enemy, isNotNull);
return enemy!;
}
}