Add built-in asset modules for Wolfenstein 3D v1.4 Shareware release

- Implement RetailSfxModule to map sound effects to numeric slots.
- Create SharewareAssetRegistry to manage assets for the Shareware version.
- Introduce SharewareEntityModule to define available enemies in Shareware.
- Add SharewareMenuPicModule to handle menu pictures with runtime offset computation.
- Implement SharewareMusicModule for music routing in Shareware.
- Define keys for entities, HUD elements, menu pictures, and music tracks.
- Create abstract modules for entity, HUD, menu picture, and music assets.
- Add registry resolver to select appropriate asset registry based on game version and data version.
- Update WolfensteinData to include new asset registry exports.
- Modify tests to utilize the new asset registry structure for Shareware and Retail versions.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-19 13:45:19 +01:00
parent de0d99588e
commit fcda0f9ff4
38 changed files with 1411 additions and 119 deletions

View File

@@ -0,0 +1,37 @@
import 'package:wolf_3d_dart/src/data_types/enemy_animation.dart';
import 'package:wolf_3d_dart/src/registry/keys/entity_key.dart';
/// All version-sensitive asset data for a single entity type.
///
/// - [animations] is `null` for bosses that drive sprite selection with
/// custom logic and do not use the standard [EnemyAnimationMap].
/// - [baseSpriteIndex] is the sprite-list offset used by custom-logic bosses
/// as the base for their manual frame calculations.
/// - [availableInShareware] mirrors the original engine's version-exclusion
/// flag; entities with `false` are skipped during shareware level loading.
class EntityAssetDefinition {
const EntityAssetDefinition({
this.animations,
this.baseSpriteIndex,
this.availableInShareware = true,
});
final EnemyAnimationMap? animations;
final int? baseSpriteIndex;
final bool availableInShareware;
}
/// Owns the mapping from symbolic [EntityKey] values to version-specific
/// [EntityAssetDefinition] objects.
///
/// Implement this class to provide custom sprite data for a modded or
/// alternate game version. Pass the implementation inside a custom
/// [AssetRegistry] when loading data.
abstract class EntityAssetModule {
const EntityAssetModule();
/// Resolves [key] to an [EntityAssetDefinition].
///
/// Returns `null` if the key is not recognised by this module.
EntityAssetDefinition? resolve(EntityKey key);
}

View File

@@ -0,0 +1,33 @@
import 'package:wolf_3d_dart/src/registry/keys/hud_key.dart';
/// The resolved reference for a HUD element: a zero-based index into
/// [WolfensteinData.vgaImages].
class HudAssetRef {
const HudAssetRef(this.vgaIndex);
final int vgaIndex;
@override
String toString() => 'HudAssetRef($vgaIndex)';
}
/// Owns the mapping from symbolic [HudKey] identifiers to VGA image indices.
///
/// Implement this class to provide a custom HUD layout for a modded or
/// alternate game version. Pass the implementation inside a custom
/// [AssetRegistry] when loading data.
abstract class HudModule {
const HudModule();
/// Resolves [key] to a [HudAssetRef] containing the VGA image index.
///
/// Returns `null` if the key is not supported by this module.
HudAssetRef? resolve(HudKey key);
/// Returns the appropriate [HudKey] for BJ's face sprite given the
/// player's current [health].
HudKey faceKeyForHealth(int health);
/// Convenience: resolves BJ's face image directly from [health].
HudAssetRef? faceForHealth(int health) => resolve(faceKeyForHealth(health));
}

View File

@@ -0,0 +1,34 @@
import 'package:wolf_3d_dart/src/data_types/difficulty.dart';
import 'package:wolf_3d_dart/src/registry/keys/menu_pic_key.dart';
/// The resolved reference for a menu picture: a zero-based index into
/// [WolfensteinData.vgaImages].
class MenuPicRef {
const MenuPicRef(this.pictureIndex);
final int pictureIndex;
@override
String toString() => 'MenuPicRef($pictureIndex)';
}
/// Owns the mapping from symbolic [MenuPicKey] values to VGA picture indices.
///
/// Implement this class to provide custom menu art for a modded or alternate
/// game version. Pass the implementation inside a custom [AssetRegistry] when
/// loading data.
abstract class MenuPicModule {
const MenuPicModule();
/// Resolves [key] to a [MenuPicRef] containing the VGA picture index.
///
/// Returns `null` if the key is not supported by this module.
MenuPicRef? resolve(MenuPicKey key);
/// Returns the [MenuPicKey] for the episode selection picture at
/// zero-based [episodeIndex].
MenuPicKey episodeKey(int episodeIndex);
/// Returns the [MenuPicKey] for the given [difficulty] selection screen.
MenuPicKey difficultyKey(Difficulty difficulty);
}

View File

@@ -0,0 +1,36 @@
import 'package:wolf_3d_dart/src/registry/keys/music_key.dart';
/// The resolved reference for a music track: a numeric index into
/// [WolfensteinData.music].
class MusicRoute {
const MusicRoute(this.trackIndex);
final int trackIndex;
@override
String toString() => 'MusicRoute($trackIndex)';
}
/// Owns the mapping from music contexts to IMF track indices.
///
/// Implement this class to provide a custom music layout for a modded or
/// alternate game version. Pass the implementation inside a custom
/// [AssetRegistry] when loading data.
abstract class MusicModule {
const MusicModule();
/// Resolves a named [MusicKey] to a [MusicRoute].
///
/// Returns `null` if the key is not supported by this module.
MusicRoute? resolve(MusicKey key);
/// Resolves the level music track for a given [episodeIndex] and
/// zero-based [levelIndex].
///
/// This is the primary routing path for in-game level music, which the
/// original engine mapped via a lookup table rather than per-level fields.
MusicRoute musicForLevel(int episodeIndex, int levelIndex);
/// The music track to play while the main menu is shown.
MusicRoute get menuMusic;
}

View File

@@ -0,0 +1,26 @@
import 'package:wolf_3d_dart/src/registry/keys/sfx_key.dart';
/// The resolved reference for a sound effect: a numeric slot index into
/// [WolfensteinData.sounds].
class SoundAssetRef {
const SoundAssetRef(this.slotIndex);
final int slotIndex;
@override
String toString() => 'SoundAssetRef($slotIndex)';
}
/// Owns the mapping from symbolic [SfxKey] identifiers to numeric sound slots.
///
/// Implement this class to provide a custom sound layout for a modded or
/// alternate game version. Pass the implementation inside a custom
/// [AssetRegistry] when loading data.
abstract class SfxModule {
const SfxModule();
/// Resolves [key] to a [SoundAssetRef] containing the numeric slot index.
///
/// Returns `null` if the key is not supported by this module.
SoundAssetRef? resolve(SfxKey key);
}