Files
wolf_dart/packages/wolf_3d_dart/README.md
T
hans d393ca98ec Refactor menu rendering and asset registry structure
- Updated SoftwareRenderer to incorporate MenuHeaderBand for handling spear variant menus and improved backdrop drawing.
- Refactored asset registry imports to organize menu-related assets under a dedicated menu structure.
- Enhanced game session snapshot tests to validate menu theme restoration for spear variant games.
- Added tests for classic menu presentation module to ensure palette consistency with canonical constants.
- Implemented tests for spear asset registry to verify correct menu VGA index resolutions.
- Created unit tests for MenuHeaderBand to validate functionality in rendering menu headers and sidebars.
- Adjusted HUD module imports to align with new menu structure.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
2026-03-24 23:35:56 +01:00

297 lines
9.3 KiB
Markdown

# wolf_3d_dart
Core non-Flutter Wolfenstein 3D runtime package used by both CLI and Flutter hosts.
## What This Package Provides
`wolf_3d_dart` contains the platform-neutral simulation/runtime surface:
- engine and session lifecycle,
- game-data loading and data types,
- renderer backends and frame-buffer abstractions,
- menu state/navigation models,
- input/audio host abstractions,
- entity and gameplay logic.
## Public Library Surfaces
Primary entry libraries in `lib/`:
- `wolf_3d_engine.dart` — engine exports and runtime contracts.
- `wolf_3d_data.dart` / `wolf_3d_data_types.dart` — game-data discovery and DTOs.
- `wolf_3d_renderer.dart` — rendering/backends integration points.
- `wolf_3d_audio.dart` — audio interfaces and host backends.
- `wolf_3d_input.dart` — input abstractions.
- `wolf_3d_menu.dart` — menu models/managers and the registry-backed `WolfMenuPresentation` helpers for hosts.
- `wolf_3d_host.dart` — host-level glue contracts.
Implementation details live under `lib/src/`.
## Setup
From this directory:
```bash
dart pub get
```
## Development Commands
From this directory:
```bash
dart analyze
dart test
```
## Architecture Notes
- Hosts own platform concerns (windowing, lifecycle, platform input wiring).
- This package owns deterministic engine/frame progression and shared game logic.
- Frame-buffer sizing is controlled by hosts through engine APIs.
- Rendering code is maintained under `lib/src/rendering/`.
- Menu coordination is split under `lib/src/menu/manager/`; public consumers should prefer `lib/wolf_3d_menu.dart` or the internal barrel at `lib/src/menu/menu_manager.dart` instead of reaching into individual implementation files.
- Menu presentation is selected through `AssetRegistry.menuPresentation`, which keeps retail/shareware/Spear variants and user-defined menu overrides aligned with the rest of the registry system.
## Custom Menus
Custom menu support is split across two registry modules:
- `MenuPicModule` maps symbolic menu keys such as `MenuPicKey.title` or an episode selection entry to concrete VGA picture indices in `WolfensteinData.vgaImages`.
- `MenuPresentationModule` defines the palette indices and higher-level menu art lookups that renderers and hosts consume.
That split is intentional:
- `MenuPicModule` answers "which image index represents this menu asset for this game/mod?"
- `MenuPresentationModule` answers "which colors and optional art should the UI use?"
In practice, most custom variants will either:
- reuse an existing `MenuPicModule` and only change colors/presentation, or
- provide both a custom `MenuPicModule` and a matching `MenuPresentationModule` when the menu art layout itself changes.
### Using Menu Presentation From Loaded Data
Once game data has been loaded, bind menu presentation through the active registry:
```dart
final WolfMenuPresentation menu = WolfMenuPresentation(data);
final int panelColor = menu.panelColor;
final VgaImage? title = menu.title;
final VgaImage? episode1 = menu.episodeOption(0);
```
This is the normal path for renderers and any UI that should track the active game variant automatically.
### Fallback Presentation Before Data Loads
Host-owned screens that appear before game data discovery can still use menu-consistent colors:
```dart
const WolfMenuPresentation classicMenu = WolfMenuPresentation.classic();
const WolfMenuPresentation spearMenu = WolfMenuPresentation.spear();
```
Those fallback constructors expose colors without requiring a loaded `WolfensteinData` instance. Art getters return `null` until real data is attached.
### Implementing A Custom MenuPicModule
Use `MenuPicModule` when your mod changes which VGA pictures back the classic menu keys:
```dart
class ModMenuPics extends MenuPicModule {
const ModMenuPics();
@override
MenuPicRef? resolve(MenuPicKey key) {
switch (key) {
case MenuPicKey.title:
return const MenuPicRef(140);
case MenuPicKey.optionTitle:
return const MenuPicRef(141);
case MenuPicKey.customizeTitle:
return const MenuPicRef(142);
default:
return null;
}
}
@override
MenuPicKey episodeKey(int episodeIndex) {
switch (episodeIndex) {
case 0:
return MenuPicKey.episode1;
case 1:
return MenuPicKey.episode2;
case 2:
return MenuPicKey.episode3;
default:
return MenuPicKey.episode1;
}
}
@override
MenuPicKey difficultyKey(Difficulty difficulty) {
switch (difficulty) {
case Difficulty.easy:
return MenuPicKey.skill1;
case Difficulty.medium:
return MenuPicKey.skill2;
case Difficulty.hard:
return MenuPicKey.skill3;
case Difficulty.expert:
return MenuPicKey.skill4;
}
}
}
```
Returning `null` from `resolve` means that the key is not provided by that module.
### Implementing A Custom MenuPresentationModule
Use `MenuPresentationModule` when you want to change menu colors, point existing menu concepts at different art, or selectively omit optional art:
### Custom Menu Presentation Example
```dart
class ModMenuPresentation extends MenuPresentationModule {
const ModMenuPresentation();
@override
int get backgroundIndex => 111;
@override
int get panelIndex => 97;
@override
int get borderIndex => 87;
@override
int get emphasisIndex => 10;
@override
int get warningIndex => 14;
@override
int get mutedIndex => 8;
@override
int get selectedTextIndex => 19;
@override
int get unselectedTextIndex => 23;
@override
int get disabledTextIndex => 4;
@override
int get headerTextIndex => 15;
@override
VgaImage? controlBackground(WolfensteinData data) => null;
@override
VgaImage? title(WolfensteinData data) => null;
@override
VgaImage? heading(WolfensteinData data) => null;
@override
VgaImage? selectedMarker(WolfensteinData data) => null;
@override
VgaImage? unselectedMarker(WolfensteinData data) => null;
@override
VgaImage? optionsLabel(WolfensteinData data) => null;
@override
VgaImage? customizeLabel(WolfensteinData data) => null;
@override
VgaImage? credits(WolfensteinData data) => null;
@override
VgaImage? episodeOption(WolfensteinData data, int episodeIndex) => null;
@override
VgaImage? difficultyOption(WolfensteinData data, Difficulty difficulty) =>
null;
@override
VgaImage? mappedPic(WolfensteinData data, int index) => null;
}
final registry = AssetRegistry(
sfx: mySfxModule,
music: myMusicModule,
entities: myEntityModule,
hud: myHudModule,
menu: myMenuPicModule,
menuPresentation: const ModMenuPresentation(),
);
```
The presentation module should treat its image-returning methods as optional hooks:
- return a `VgaImage` when that surface has variant-specific art,
- return `null` when the presentation intentionally has no image for that concept,
### Palette Conversion Guardrail
When mapping target RGB menu tones to a VGA palette index (for example, preserving Wolf classic dark-red theme), resolve nearest colors from `ColorPalette.argbFromVgaIndex()` values.
Do not use `ColorPalette.findClosestPaletteIndex()` for this specific workflow, because its channel interpretation is legacy-oriented and can produce hue-swapped matches (for example, red targets resolving to blue-ish indices).
In short:
- For variant-defined menu colors: use explicit palette indices from `MenuPresentationModule`.
- For host-defined fallback RGB tones: find nearest VGA index by comparing RGB distance against `argbFromVgaIndex()` output.
- use `mappedPic(...)` only for legacy numeric menu art lookups that still matter for a renderer path.
### Wiring A Fully Custom Registry
To ship a complete custom menu variant, provide both modules through `AssetRegistry` when loading data:
```dart
final registry = AssetRegistry(
sfx: mySfxModule,
music: myMusicModule,
entities: myEntityModule,
hud: myHudModule,
menu: const ModMenuPics(),
menuPresentation: const ModMenuPresentation(),
);
```
If your menu art still follows the built-in retail/shareware layout, you may not need a custom `MenuPicModule`. In that case, keep the built-in module and only swap `menuPresentation`.
### Choosing The Right Extension Point
- Change colors only: implement `MenuPresentationModule`.
- Change symbolic menu art mapping: implement `MenuPicModule`.
- Change both colors and art layout: implement both modules.
- Build a host setup screen before data loads: use `WolfMenuPresentation.classic()` or `WolfMenuPresentation.spear()`.
For most host code, prefer the public `wolf_3d_menu.dart` surface instead of importing internal files directly.
## Non-Goals
- Flutter widgets/screens are not part of this package.
- Bundled app assets are handled by `wolf_3d_assets`.
## Troubleshooting
- **Parity regressions**: run targeted tests under `test/engine/` and `test/entities/`.
- **Host integration issues**: verify host packages/apps are using exported surfaces from `lib/` rather than private `src/` paths.
## Related Modules
- Flutter integration layer: [`../wolf_3d_flutter/README.md`](../wolf_3d_flutter/README.md)
- Shared packaged assets: [`../wolf_3d_assets/README.md`](../wolf_3d_assets/README.md)
- CLI host app: [`../../apps/wolf_3d_cli/README.md`](../../apps/wolf_3d_cli/README.md)
- GUI host app: [`../../apps/wolf_3d_gui/README.md`](../../apps/wolf_3d_gui/README.md)
- Workspace overview: [`../../README.md`](../../README.md)