feat: Add Spear of Destiny demo support with dedicated asset registry and entity definitions
- Introduced SpearDemoAssetRegistry for managing assets specific to the Spear of Destiny demo. - Created SpearDemoEntityModule to define enemy animations with adjusted sprite ranges. - Implemented SpearDemoHudModule and SpearDemoMenuPicModule for HUD and menu assets. - Added SpearDemoSfxModule for sound effect mappings specific to the demo version. - Updated enemy classes (Guard, Mutant, Officer, SS) to support custom animation sets. - Modified entity registry to accept a custom AssetRegistry for spawning entities. - Enhanced rendering with CRT phosphor bloom effect in GLSL shaders. - Adjusted ASCII and software renderer layouts for improved UI spacing. - Added tests for SpearDemoAssetRegistry to ensure correct asset resolution and enemy spawning. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import 'package:wolf_3d_dart/src/data_types/game_version.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/asset_registry.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/built_in_music_module.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/spear_demo_entity_module.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/spear_demo_hud_module.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/spear_demo_menu_module.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/spear_demo_sfx_module.dart';
|
||||
|
||||
/// Built-in [AssetRegistry] for Spear of Destiny demo/shareware (`.SDM`).
|
||||
class SpearDemoAssetRegistry extends AssetRegistry {
|
||||
SpearDemoAssetRegistry()
|
||||
: super(
|
||||
sfx: const SpearDemoSfxModule(),
|
||||
music: const BuiltInMusicModule(GameVersion.spearOfDestinyDemo),
|
||||
entities: const SpearDemoEntityModule(),
|
||||
hud: const SpearDemoHudModule(),
|
||||
menu: const SpearDemoMenuPicModule(),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import 'package:wolf_3d_dart/src/data_types/enemy_animation.dart';
|
||||
import 'package:wolf_3d_dart/src/data_types/sprite_frame_range.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/keys/entity_key.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/modules/entity_asset_module.dart';
|
||||
|
||||
/// Built-in entity module for Spear of Destiny demo/shareware (`.SDM`).
|
||||
///
|
||||
/// The sprite enum in the original source inserts four additional static
|
||||
/// sprites before enemy animations for SPEAR builds, so all Wolf3D enemy
|
||||
/// animation ranges are shifted by +4.
|
||||
class SpearDemoEntityModule extends EntityAssetModule {
|
||||
const SpearDemoEntityModule();
|
||||
|
||||
static final Map<EntityKey, EntityAssetDefinition> _defs = {
|
||||
EntityKey.guard: const EntityAssetDefinition(
|
||||
availableInShareware: true,
|
||||
animations: EnemyAnimationMap(
|
||||
idle: SpriteFrameRange(54, 61),
|
||||
walking: SpriteFrameRange(62, 93),
|
||||
dying: SpriteFrameRange(94, 97),
|
||||
pain: SpriteFrameRange(98, 98),
|
||||
dead: SpriteFrameRange(99, 99),
|
||||
attacking: SpriteFrameRange(100, 102),
|
||||
),
|
||||
),
|
||||
EntityKey.dog: const EntityAssetDefinition(
|
||||
availableInShareware: true,
|
||||
animations: EnemyAnimationMap(
|
||||
idle: SpriteFrameRange(103, 110),
|
||||
walking: SpriteFrameRange(103, 134),
|
||||
attacking: SpriteFrameRange(139, 141),
|
||||
pain: SpriteFrameRange(0, 0),
|
||||
dying: SpriteFrameRange(135, 137),
|
||||
dead: SpriteFrameRange(138, 138),
|
||||
),
|
||||
),
|
||||
EntityKey.ss: const EntityAssetDefinition(
|
||||
availableInShareware: true,
|
||||
animations: EnemyAnimationMap(
|
||||
idle: SpriteFrameRange(142, 149),
|
||||
walking: SpriteFrameRange(150, 181),
|
||||
attacking: SpriteFrameRange(188, 190),
|
||||
pain: SpriteFrameRange(186, 186),
|
||||
dying: SpriteFrameRange(182, 185),
|
||||
dead: SpriteFrameRange(187, 187),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
@override
|
||||
EntityAssetDefinition? resolve(EntityKey key) => _defs[key];
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'package:wolf_3d_dart/src/registry/keys/hud_key.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/modules/hud_module.dart';
|
||||
|
||||
/// Built-in HUD module for Spear of Destiny demo/shareware (`.SDM`).
|
||||
///
|
||||
/// VGA picture indices are derived from `GFXV_SDM.H` (`chunkId - STARTPICS`).
|
||||
class SpearDemoHudModule extends HudModule {
|
||||
const SpearDemoHudModule();
|
||||
|
||||
static const List<HudKey> _faceBands = [
|
||||
HudKey.faceHealthy,
|
||||
HudKey.faceScratched,
|
||||
HudKey.faceHurt,
|
||||
HudKey.faceWounded,
|
||||
HudKey.faceBadlyWounded,
|
||||
HudKey.faceDying,
|
||||
HudKey.faceNearDeath,
|
||||
];
|
||||
|
||||
static final Map<HudKey, int> _indices = {
|
||||
HudKey.statusBar: 73, // STATUSBARPIC (76 - 3)
|
||||
HudKey.digit0: 84, // N_0PIC
|
||||
HudKey.digit1: 85,
|
||||
HudKey.digit2: 86,
|
||||
HudKey.digit3: 87,
|
||||
HudKey.digit4: 88,
|
||||
HudKey.digit5: 89,
|
||||
HudKey.digit6: 90,
|
||||
HudKey.digit7: 91,
|
||||
HudKey.digit8: 92,
|
||||
HudKey.digit9: 93,
|
||||
HudKey.faceHealthy: 94, // FACE1APIC
|
||||
HudKey.faceScratched: 97,
|
||||
HudKey.faceHurt: 100,
|
||||
HudKey.faceWounded: 103,
|
||||
HudKey.faceBadlyWounded: 106,
|
||||
HudKey.faceDying: 109,
|
||||
HudKey.faceNearDeath: 112,
|
||||
HudKey.faceDead: 122, // BJOUCHPIC
|
||||
HudKey.pistolIcon: 77, // GUNPIC
|
||||
HudKey.machineGunIcon: 78, // MACHINEGUNPIC
|
||||
HudKey.chainGunIcon: 79, // GATLINGGUNPIC
|
||||
};
|
||||
|
||||
@override
|
||||
HudAssetRef? resolve(HudKey key) {
|
||||
final index = _indices[key];
|
||||
return index != null ? HudAssetRef(index) : null;
|
||||
}
|
||||
|
||||
@override
|
||||
HudKey faceKeyForHealth(int health) {
|
||||
if (health <= 0) return HudKey.faceDead;
|
||||
final band = ((100 - health) ~/ 16).clamp(0, _faceBands.length - 1);
|
||||
return _faceBands[band];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'package:wolf_3d_dart/src/data_types/difficulty.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/keys/menu_pic_key.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/modules/menu_pic_module.dart';
|
||||
|
||||
/// Built-in menu-picture module for Spear of Destiny demo/shareware (`.SDM`).
|
||||
///
|
||||
/// Picture indices are derived from `GFXV_SDM.H` (`chunkId - STARTPICS`).
|
||||
class SpearDemoMenuPicModule extends MenuPicModule {
|
||||
const SpearDemoMenuPicModule();
|
||||
|
||||
static final Map<MenuPicKey, int> _indices = {
|
||||
MenuPicKey.title: 71, // TITLE1PIC
|
||||
MenuPicKey.credits: 75, // CREDITSPIC
|
||||
MenuPicKey.pg13: 74, // PG13PIC
|
||||
|
||||
MenuPicKey.controlBackground: 12, // C_CONTROLPIC
|
||||
MenuPicKey.footer: 1, // C_MOUSELBACKPIC
|
||||
MenuPicKey.heading: 0, // C_BACKDROPPIC
|
||||
MenuPicKey.optionsLabel: 13, // C_OPTIONSPIC
|
||||
MenuPicKey.customizeLabel: 6, // C_CUSTOMIZEPIC
|
||||
|
||||
MenuPicKey.cursorActive: 2, // C_CURSOR1PIC
|
||||
MenuPicKey.cursorInactive: 3, // C_CURSOR2PIC
|
||||
MenuPicKey.markerSelected: 5, // C_SELECTEDPIC
|
||||
MenuPicKey.markerUnselected: 4, // C_NOTSELECTEDPIC
|
||||
|
||||
MenuPicKey.difficultyBaby: 18, // C_BABYMODEPIC
|
||||
MenuPicKey.difficultyEasy: 19, // C_EASYPIC
|
||||
MenuPicKey.difficultyNormal: 20, // C_NORMALPIC
|
||||
MenuPicKey.difficultyHard: 21, // C_HARDPIC
|
||||
};
|
||||
|
||||
@override
|
||||
MenuPicRef? resolve(MenuPicKey key) {
|
||||
final index = _indices[key];
|
||||
return index != null ? MenuPicRef(index) : null;
|
||||
}
|
||||
|
||||
@override
|
||||
MenuPicKey episodeKey(int episodeIndex) {
|
||||
return MenuPicKey.episode1;
|
||||
}
|
||||
|
||||
@override
|
||||
MenuPicKey difficultyKey(Difficulty difficulty) {
|
||||
return switch (difficulty) {
|
||||
Difficulty.baby => MenuPicKey.difficultyBaby,
|
||||
Difficulty.easy => MenuPicKey.difficultyEasy,
|
||||
Difficulty.medium => MenuPicKey.difficultyNormal,
|
||||
Difficulty.hard => MenuPicKey.difficultyHard,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:wolf_3d_dart/src/registry/keys/sfx_key.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/modules/sfx_module.dart';
|
||||
|
||||
/// Built-in digitized SFX slot mapping for Spear of Destiny demo/shareware.
|
||||
///
|
||||
/// Slots follow the original SPEAR/SPEARDEMO DigiMap table in `WL_MAIN.C`.
|
||||
/// Only sounds present in the SDM digitized table are mapped.
|
||||
class SpearDemoSfxModule extends SfxModule {
|
||||
const SpearDemoSfxModule();
|
||||
|
||||
static final Map<SoundEffect, int> _slots = {
|
||||
SoundEffect.openDoor: 3,
|
||||
SoundEffect.closeDoor: 2,
|
||||
SoundEffect.pushWall: 13,
|
||||
|
||||
SoundEffect.pistolFire: 5,
|
||||
SoundEffect.machineGunFire: 4,
|
||||
SoundEffect.chainGunFire: 6,
|
||||
SoundEffect.enemyFire: 17,
|
||||
|
||||
SoundEffect.getChainGun: 38,
|
||||
|
||||
SoundEffect.guardHalt: 0,
|
||||
SoundEffect.ssAlert: 7,
|
||||
SoundEffect.deathScream1: 10,
|
||||
SoundEffect.deathScream2: 11,
|
||||
SoundEffect.deathScream3: 23,
|
||||
SoundEffect.ssDeath: 28,
|
||||
|
||||
SoundEffect.bossActive: 8,
|
||||
SoundEffect.hansGrosseDeath: 24,
|
||||
|
||||
SoundEffect.levelComplete: 22,
|
||||
};
|
||||
|
||||
@override
|
||||
SoundAssetRef? resolve(SoundEffect key) {
|
||||
final slot = _slots[key];
|
||||
return slot == null ? null : SoundAssetRef(slot);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'package:wolf_3d_dart/src/data_types/game_version.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/asset_registry.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/retail_asset_registry.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/shareware_asset_registry.dart';
|
||||
import 'package:wolf_3d_dart/src/registry/built_in/spear_demo_asset_registry.dart';
|
||||
|
||||
/// The input used by [AssetRegistryResolver] to select or build a registry.
|
||||
class RegistrySelectionContext {
|
||||
@@ -47,8 +48,9 @@ class BuiltInAssetRegistryResolver implements AssetRegistryResolver {
|
||||
case DataVersion.version14Retail:
|
||||
return RetailAssetRegistry();
|
||||
case DataVersion.version14Shareware:
|
||||
case DataVersion.spearOfDestinyShareware:
|
||||
return SharewareAssetRegistry(strictOriginalShareware: true);
|
||||
case DataVersion.spearOfDestinyShareware:
|
||||
return SpearDemoAssetRegistry();
|
||||
case _:
|
||||
break; // fall through to GameVersion family check
|
||||
}
|
||||
@@ -61,9 +63,7 @@ class BuiltInAssetRegistryResolver implements AssetRegistryResolver {
|
||||
return SharewareAssetRegistry();
|
||||
case GameVersion.spearOfDestiny:
|
||||
case GameVersion.spearOfDestinyDemo:
|
||||
// Spear-specific modules are not yet implemented; retail layout is
|
||||
// the closest structural match and will be replaced in a later phase.
|
||||
return RetailAssetRegistry();
|
||||
return SpearDemoAssetRegistry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user