feat: Refactor to use Wolf3dFlutterEngine across the application
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
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_input.dart';
|
||||
|
||||
/// Platform-agnostic gameplay session facade for Wolf3D hosts.
|
||||
///
|
||||
/// This class owns shared engine state and host-facing session configuration
|
||||
/// while remaining independent of Flutter and other UI frameworks.
|
||||
class Wolf3dEngine {
|
||||
/// Creates a session facade backed by [audio] and [input].
|
||||
Wolf3dEngine({required this.audio, required this.input});
|
||||
|
||||
/// All successfully discovered or supplied game data sets.
|
||||
final List<WolfensteinData> availableGames = [];
|
||||
|
||||
WolfensteinData? _activeGame;
|
||||
|
||||
/// Shared engine audio backend used by menus and gameplay sessions.
|
||||
final EngineAudio audio;
|
||||
|
||||
/// Shared input adapter reused by hosts and gameplay screens.
|
||||
final Wolf3dInput input;
|
||||
|
||||
Future<void>? _audioShutdownFuture;
|
||||
|
||||
/// Engine menu background color as 24-bit RGB.
|
||||
int menuBackgroundRgb = 0x890000;
|
||||
|
||||
/// Engine menu panel color as 24-bit RGB.
|
||||
int menuPanelRgb = 0x590002;
|
||||
|
||||
bool _debugEnabled = false;
|
||||
|
||||
/// Whether host-level debug affordances should be visible.
|
||||
bool get isDebugEnabled => _debugEnabled;
|
||||
|
||||
/// Enables host-level debug affordances such as debug navigation UI.
|
||||
Wolf3dEngine enableDebug() {
|
||||
_debugEnabled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// The currently selected game data set.
|
||||
///
|
||||
/// Throws a [StateError] until [setActiveGame] has been called.
|
||||
WolfensteinData get activeGame {
|
||||
if (_activeGame == null) {
|
||||
throw StateError('No active game selected. Call setActiveGame() first.');
|
||||
}
|
||||
return _activeGame!;
|
||||
}
|
||||
|
||||
/// Nullable access to the selected game, useful during menu bootstrap.
|
||||
WolfensteinData? get maybeActiveGame => _activeGame;
|
||||
|
||||
// Episode selection lives on the facade so menus can configure gameplay
|
||||
// before constructing a new engine instance.
|
||||
int? _activeEpisode;
|
||||
|
||||
/// Index of the episode currently selected in the UI flow.
|
||||
int? get activeEpisode => _activeEpisode;
|
||||
|
||||
Difficulty? _activeDifficulty;
|
||||
|
||||
/// The difficulty applied when [launchEngine] creates a new session.
|
||||
Difficulty? get activeDifficulty => _activeDifficulty;
|
||||
|
||||
/// Stores [difficulty] so the next [launchEngine] call uses it.
|
||||
void setActiveDifficulty(Difficulty difficulty) {
|
||||
_activeDifficulty = difficulty;
|
||||
}
|
||||
|
||||
/// Clears any previously selected difficulty so the engine can prompt for one.
|
||||
void clearActiveDifficulty() {
|
||||
_activeDifficulty = null;
|
||||
}
|
||||
|
||||
WolfEngine? _engine;
|
||||
|
||||
/// The most recently launched engine.
|
||||
///
|
||||
/// Throws a [StateError] until [launchEngine] has been called.
|
||||
WolfEngine get engine {
|
||||
if (_engine == null) {
|
||||
throw StateError('No engine launched. Call launchEngine() first.');
|
||||
}
|
||||
return _engine!;
|
||||
}
|
||||
|
||||
/// Creates and initializes a [WolfEngine] for the current session config.
|
||||
///
|
||||
/// Uses [activeGame], [activeEpisode], and [activeDifficulty]. Stores the
|
||||
/// engine so it can be retrieved via [engine]. [onGameWon] is invoked when
|
||||
/// the player completes the final level of the episode.
|
||||
WolfEngine launchEngine({
|
||||
required void Function() onGameWon,
|
||||
void Function()? onQuit,
|
||||
SaveGamePersistence? saveGamePersistence,
|
||||
WolfRendererCapabilities? rendererCapabilities,
|
||||
WolfRendererSettings? rendererSettings,
|
||||
void Function(WolfRendererSettings settings)? onRendererSettingsChanged,
|
||||
}) {
|
||||
if (availableGames.isEmpty) {
|
||||
throw StateError(
|
||||
'No game data was discovered. Add game files before launching the engine.',
|
||||
);
|
||||
}
|
||||
|
||||
_engine = WolfEngine(
|
||||
availableGames: availableGames,
|
||||
difficulty: _activeDifficulty,
|
||||
startingEpisode: _activeEpisode,
|
||||
frameBuffer: FrameBuffer(320, 200),
|
||||
menuBackgroundRgb: menuBackgroundRgb,
|
||||
menuPanelRgb: menuPanelRgb,
|
||||
engineAudio: audio,
|
||||
input: input,
|
||||
onGameWon: onGameWon,
|
||||
// In Flutter we keep the renderer screen active while browsing menus,
|
||||
// so backing out of the top-level menu should not pop the route.
|
||||
onMenuExit: () {},
|
||||
onQuit: onQuit,
|
||||
saveGamePersistence: saveGamePersistence,
|
||||
rendererCapabilities: rendererCapabilities,
|
||||
rendererSettings: rendererSettings,
|
||||
onRendererSettingsChanged: onRendererSettingsChanged,
|
||||
onGameSelected: (game) {
|
||||
_activeGame = game;
|
||||
audio.activeGame = game;
|
||||
},
|
||||
onEpisodeSelected: (episodeIndex) {
|
||||
_activeEpisode = episodeIndex;
|
||||
},
|
||||
);
|
||||
_engine!.init();
|
||||
return _engine!;
|
||||
}
|
||||
|
||||
/// Sets the active episode for the current [activeGame].
|
||||
void setActiveEpisode(int episodeIndex) {
|
||||
if (_activeGame == null) {
|
||||
throw StateError('No active game selected. Call setActiveGame() first.');
|
||||
}
|
||||
if (episodeIndex < 0 || episodeIndex >= _activeGame!.episodes.length) {
|
||||
throw RangeError('Episode index out of range for the active game.');
|
||||
}
|
||||
|
||||
_activeEpisode = episodeIndex;
|
||||
}
|
||||
|
||||
/// Clears any selected episode so menu flow starts fresh.
|
||||
void clearActiveEpisode() {
|
||||
_activeEpisode = null;
|
||||
}
|
||||
|
||||
/// Convenience access to the active episode's level list.
|
||||
List<WolfLevel> get levels {
|
||||
if (_activeEpisode == null) {
|
||||
throw StateError('No active episode selected.');
|
||||
}
|
||||
return activeGame.episodes[_activeEpisode!].levels;
|
||||
}
|
||||
|
||||
/// Convenience access to the active game's wall textures.
|
||||
List<Sprite> get walls => activeGame.walls;
|
||||
|
||||
/// Convenience access to the active game's sprite set.
|
||||
List<Sprite> get sprites => activeGame.sprites;
|
||||
|
||||
/// Convenience access to digitized PCM effects.
|
||||
List<PcmSound> get sounds => activeGame.sounds;
|
||||
|
||||
/// Convenience access to AdLib/OPL effect assets.
|
||||
List<PcmSound> get adLibSounds => activeGame.adLibSounds;
|
||||
|
||||
/// Convenience access to level music tracks.
|
||||
List<ImfMusic> get music => activeGame.music;
|
||||
|
||||
/// Convenience access to VGA UI and splash images.
|
||||
List<VgaImage> get vgaImages => activeGame.vgaImages;
|
||||
|
||||
/// Makes [game] the active data set and points shared services at it.
|
||||
void setActiveGame(WolfensteinData game) {
|
||||
if (!availableGames.contains(game)) {
|
||||
throw ArgumentError(
|
||||
'The provided game data is not in the list of available games.',
|
||||
);
|
||||
}
|
||||
|
||||
if (_activeGame == game) {
|
||||
return;
|
||||
}
|
||||
|
||||
_activeGame = game;
|
||||
_activeEpisode = null;
|
||||
audio.activeGame = game;
|
||||
}
|
||||
|
||||
/// Stops and disposes shared audio exactly once for app shutdown.
|
||||
///
|
||||
/// Repeated calls return the same in-flight/completed future so hosts can
|
||||
/// safely invoke shutdown from multiple lifecycle paths.
|
||||
Future<void> shutdownAudio() {
|
||||
final existing = _audioShutdownFuture;
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
final shutdown = () async {
|
||||
await audio.stopAllAudio();
|
||||
audio.dispose();
|
||||
}();
|
||||
_audioShutdownFuture = shutdown;
|
||||
return shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
/// Backward-compatible alias for the previous class name.
|
||||
typedef Wolf3dSession = Wolf3dEngine;
|
||||
@@ -19,4 +19,5 @@ export 'src/engine/save/default_save_game_persistence.dart';
|
||||
export 'src/engine/save/game_session_snapshot.dart';
|
||||
export 'src/engine/save/save_game_codec.dart';
|
||||
export 'src/engine/save/save_game_persistence.dart';
|
||||
export 'src/engine/wolf_3d_engine.dart';
|
||||
export 'src/engine/wolf_3d_engine_base.dart';
|
||||
|
||||
Reference in New Issue
Block a user