Refactor collectible handling and scoring system

- Introduced `CollectiblePickupContext` and `CollectiblePickupEffect` to streamline the collectible pickup logic in the Player class.
- Updated the `tryPickup` method in the Player class to utilize the new effect system for health, ammo, score, keys, and weapons.
- Refactored collectible classes to implement the new `tryCollect` method, returning a `CollectiblePickupEffect`.
- Enhanced enemy classes to expose score values based on their type.
- Added unit tests for scoring ownership and shareware menu module functionality.
- Updated rendering logic in various renderer classes to use the new mapped picture retrieval system.
- Improved the `WolfClassicMenuArt` class to utilize a more structured approach for image retrieval based on registry keys.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-19 14:25:14 +01:00
parent 6e53da7095
commit 57dde0f31c
15 changed files with 584 additions and 223 deletions

View File

@@ -15,13 +15,15 @@ import 'package:wolf_3d_dart/src/registry/built_in/shareware_music_module.dart';
/// [SharewareMenuPicModule.initWithImageSizes] after the VGA images are
/// available so the shift is computed correctly.
class SharewareAssetRegistry extends AssetRegistry {
SharewareAssetRegistry()
SharewareAssetRegistry({bool strictOriginalShareware = false})
: super(
sfx: const RetailSfxModule(),
music: const SharewareMusicModule(),
entities: const SharewareEntityModule(),
hud: const RetailHudModule(),
menu: SharewareMenuPicModule(),
menu: SharewareMenuPicModule(
useOriginalWl1Map: strictOriginalShareware,
),
);
/// Convenience accessor to the menu module for post-load initialisation.

View File

@@ -15,7 +15,12 @@ import 'package:wolf_3d_dart/src/registry/modules/menu_pic_module.dart';
/// module falls back to the retail indices (compatible with unrecognised
/// or future shareware variants).
class SharewareMenuPicModule extends MenuPicModule {
SharewareMenuPicModule();
SharewareMenuPicModule({this.useOriginalWl1Map = false});
/// When true, use the canonical original WL1 index table directly.
///
/// Unknown/custom shareware-like data should use the heuristic path instead.
final bool useOriginalWl1Map;
// Retail-baseline indices (same layout as RetailMenuPicModule).
static final Map<MenuPicKey, int> _retailBaseline = {
@@ -42,6 +47,27 @@ class SharewareMenuPicModule extends MenuPicModule {
MenuPicKey.difficultyHard: 19,
};
// Canonical menu picture indices for the original v1.4 shareware data
// identity (DataVersion.version14Shareware).
static final Map<MenuPicKey, int> _originalWl1Map = {
MenuPicKey.title: 96,
MenuPicKey.credits: 98,
MenuPicKey.pg13: 97,
MenuPicKey.controlBackground: 35,
MenuPicKey.footer: 27,
MenuPicKey.heading: 14,
MenuPicKey.optionsLabel: 19,
MenuPicKey.cursorActive: 20,
MenuPicKey.cursorInactive: 21,
MenuPicKey.markerSelected: 23,
MenuPicKey.markerUnselected: 22,
MenuPicKey.episode1: 39,
MenuPicKey.difficultyBaby: 28,
MenuPicKey.difficultyEasy: 29,
MenuPicKey.difficultyNormal: 30,
MenuPicKey.difficultyHard: 31,
};
// Landmark constant: STATUSBARPIC in retail picture-table coords.
static const int _retailStatusBarIndex = 83;
@@ -79,9 +105,19 @@ class SharewareMenuPicModule extends MenuPicModule {
@override
MenuPicRef? resolve(MenuPicKey key) {
if (useOriginalWl1Map) {
final wl1 = _originalWl1Map[key];
return wl1 == null ? null : MenuPicRef(wl1);
}
final base = _retailBaseline[key];
if (base == null) return null;
final shifted = base + (_offset ?? 0);
// The shareware/release drift is observed around STATUSBAR and later
// full-screen art. Earlier control-panel assets keep retail indices.
final shifted = base >= _retailStatusBarIndex
? base + (_offset ?? 0)
: base;
return MenuPicRef(shifted);
}
@@ -95,8 +131,9 @@ class SharewareMenuPicModule extends MenuPicModule {
MenuPicKey.episode5,
MenuPicKey.episode6,
];
if (episodeIndex >= 0 && episodeIndex < keys.length)
if (episodeIndex >= 0 && episodeIndex < keys.length) {
return keys[episodeIndex];
}
return MenuPicKey.episode1;
}

View File

@@ -47,7 +47,7 @@ class BuiltInAssetRegistryResolver implements AssetRegistryResolver {
case DataVersion.version14Retail:
return RetailAssetRegistry();
case DataVersion.version14Shareware:
return SharewareAssetRegistry();
return SharewareAssetRegistry(strictOriginalShareware: true);
case DataVersion.unknown:
break; // fall through to GameVersion family check
}