From 55cf73f7f5b4bcaae8bb427c394cda60943223fa Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Tue, 17 Mar 2026 20:24:39 +0100 Subject: [PATCH] Moved away from singleton pattern Signed-off-by: Hans Kokx --- apps/wolf_3d_gui/lib/main.dart | 6 ++--- .../lib/screens/difficulty_screen.dart | 18 +++++++------ .../lib/screens/episode_screen.dart | 18 ++++++++----- .../lib/screens/game_select_screen.dart | 12 +++++---- .../lib/screens/sprite_gallery.dart | 15 ++++------- .../lib/audio/audio_adaptor.dart | 22 +++++++++------- packages/wolf_3d_flutter/lib/wolf_3d.dart | 26 +++++++++++++++---- 7 files changed, 70 insertions(+), 47 deletions(-) diff --git a/apps/wolf_3d_gui/lib/main.dart b/apps/wolf_3d_gui/lib/main.dart index f4cc521..bdd8fa5 100644 --- a/apps/wolf_3d_gui/lib/main.dart +++ b/apps/wolf_3d_gui/lib/main.dart @@ -5,11 +5,11 @@ import 'package:wolf_3d_gui/screens/game_select_screen.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - await Wolf3d.I.init(); + final Wolf3d wolf3d = await Wolf3d().init(); runApp( - const MaterialApp( - home: GameSelectScreen(), + MaterialApp( + home: GameSelectScreen(wolf3d: wolf3d), ), ); } diff --git a/apps/wolf_3d_gui/lib/screens/difficulty_screen.dart b/apps/wolf_3d_gui/lib/screens/difficulty_screen.dart index 36d4e06..f079f15 100644 --- a/apps/wolf_3d_gui/lib/screens/difficulty_screen.dart +++ b/apps/wolf_3d_gui/lib/screens/difficulty_screen.dart @@ -4,8 +4,11 @@ import 'package:wolf_3d_flutter/wolf_3d.dart'; import 'package:wolf_3d_gui/screens/game_screen.dart'; class DifficultyScreen extends StatefulWidget { + final Wolf3d wolf3d; + const DifficultyScreen({ super.key, + required this.wolf3d, }); @override @@ -13,24 +16,25 @@ class DifficultyScreen extends StatefulWidget { } class _DifficultyScreenState extends State { - bool get isShareware => Wolf3d.I.activeGame.version == GameVersion.shareware; + bool get isShareware => + widget.wolf3d.activeGame.version == GameVersion.shareware; @override void dispose() { - Wolf3d.I.audio.stopMusic(); + widget.wolf3d.audio.stopMusic(); super.dispose(); } void _startGame(Difficulty difficulty, {bool showGallery = false}) { - Wolf3d.I.audio.stopMusic(); + widget.wolf3d.audio.stopMusic(); Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) => GameScreen( - data: Wolf3d.I.activeGame, + data: widget.wolf3d.activeGame, difficulty: difficulty, - startingEpisode: Wolf3d.I.activeEpisode, - audio: Wolf3d.I.audio, + startingEpisode: widget.wolf3d.activeEpisode, + audio: widget.wolf3d.audio, ), ), ); @@ -59,8 +63,6 @@ class _DifficultyScreenState extends State { ), ), const SizedBox(height: 40), - - // --- Difficulty Buttons --- ListView.builder( shrinkWrap: true, itemCount: Difficulty.values.length, diff --git a/apps/wolf_3d_gui/lib/screens/episode_screen.dart b/apps/wolf_3d_gui/lib/screens/episode_screen.dart index e9c375d..810bbe6 100644 --- a/apps/wolf_3d_gui/lib/screens/episode_screen.dart +++ b/apps/wolf_3d_gui/lib/screens/episode_screen.dart @@ -6,7 +6,9 @@ import 'package:wolf_3d_gui/screens/sprite_gallery.dart'; import 'package:wolf_3d_gui/screens/vga_gallery.dart'; class EpisodeScreen extends StatefulWidget { - const EpisodeScreen({super.key}); + final Wolf3d wolf3d; + + const EpisodeScreen({super.key, required this.wolf3d}); @override State createState() => _EpisodeScreenState(); @@ -16,21 +18,21 @@ class _EpisodeScreenState extends State { @override void initState() { super.initState(); - Wolf3d.I.audio.playMenuMusic(); + widget.wolf3d.audio.playMenuMusic(); } void _selectEpisode(int index) { - Wolf3d.I.setActiveEpisode(index); + widget.wolf3d.setActiveEpisode(index); Navigator.of(context).push( MaterialPageRoute( - builder: (context) => DifficultyScreen(), + builder: (context) => DifficultyScreen(wolf3d: widget.wolf3d), ), ); } @override Widget build(BuildContext context) { - final List episodes = Wolf3d.I.activeGame.episodes; + final List episodes = widget.wolf3d.activeGame.episodes; return Scaffold( backgroundColor: Colors.teal, @@ -43,7 +45,7 @@ class _EpisodeScreenState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (context) { - return VgaGallery(images: Wolf3d.I.vgaImages); + return VgaGallery(images: widget.wolf3d.vgaImages); }, ), ); @@ -55,7 +57,9 @@ class _EpisodeScreenState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (context) { - return SpriteGallery(sprites: Wolf3d.I.sprites); + return SpriteGallery( + wolf3d: widget.wolf3d, + ); }, ), ); diff --git a/apps/wolf_3d_gui/lib/screens/game_select_screen.dart b/apps/wolf_3d_gui/lib/screens/game_select_screen.dart index f95515a..b4cc9d1 100644 --- a/apps/wolf_3d_gui/lib/screens/game_select_screen.dart +++ b/apps/wolf_3d_gui/lib/screens/game_select_screen.dart @@ -4,25 +4,27 @@ import 'package:wolf_3d_flutter/wolf_3d.dart'; import 'package:wolf_3d_gui/screens/episode_screen.dart'; class GameSelectScreen extends StatelessWidget { - const GameSelectScreen({super.key}); + final Wolf3d wolf3d; + + const GameSelectScreen({super.key, required this.wolf3d}); @override Widget build(BuildContext context) { return Scaffold( body: ListView.builder( - itemCount: Wolf3d.I.availableGames.length, + itemCount: wolf3d.availableGames.length, itemBuilder: (context, i) { - final WolfensteinData data = Wolf3d.I.availableGames[i]; + final WolfensteinData data = wolf3d.availableGames[i]; final GameVersion version = data.version; return Card( child: ListTile( title: Text(version.name), onTap: () { - Wolf3d.I.setActiveGame(data); + wolf3d.setActiveGame(data); Navigator.of(context).push( MaterialPageRoute( - builder: (context) => const EpisodeScreen(), + builder: (context) => EpisodeScreen(wolf3d: wolf3d), ), ); }, diff --git a/apps/wolf_3d_gui/lib/screens/sprite_gallery.dart b/apps/wolf_3d_gui/lib/screens/sprite_gallery.dart index 48b5e33..777cb2e 100644 --- a/apps/wolf_3d_gui/lib/screens/sprite_gallery.dart +++ b/apps/wolf_3d_gui/lib/screens/sprite_gallery.dart @@ -5,11 +5,11 @@ import 'package:wolf_3d_flutter/wolf_3d.dart'; import 'package:wolf_3d_renderer/wolf_3d_asset_painter.dart'; class SpriteGallery extends StatelessWidget { - final List sprites; + final Wolf3d wolf3d; - const SpriteGallery({super.key, required this.sprites}); + const SpriteGallery({super.key, required this.wolf3d}); - bool get isShareware => Wolf3d.I.activeGame.version == GameVersion.shareware; + bool get isShareware => wolf3d.activeGame.version == GameVersion.shareware; @override Widget build(BuildContext context) { @@ -25,9 +25,8 @@ class SpriteGallery extends StatelessWidget { crossAxisSpacing: 8, mainAxisSpacing: 8, ), - itemCount: sprites.length, + itemCount: wolf3d.sprites.length, itemBuilder: (context, index) { - // --- Check which enemy owns this sprite --- String label = "Sprite Index: $index"; for (final enemy in EnemyType.values) { if (enemy.claimsSpriteIndex(index, isShareware: isShareware)) { @@ -36,14 +35,10 @@ class SpriteGallery extends StatelessWidget { isShareware: isShareware, ); - // Appends the enum name (e.g., "guard", "dog") label += "\n${enemy.name}"; - - // Appends the animation name if (animation != null) { label += "\n${animation.name}"; } - break; } } @@ -61,7 +56,7 @@ class SpriteGallery extends StatelessWidget { Expanded( child: AspectRatio( aspectRatio: 4 / 3, - child: WolfAssetPainter.sprite(sprites[index]), + child: WolfAssetPainter.sprite(wolf3d.sprites[index]), ), ), ], diff --git a/packages/wolf_3d_flutter/lib/audio/audio_adaptor.dart b/packages/wolf_3d_flutter/lib/audio/audio_adaptor.dart index e38b590..7d0ce34 100644 --- a/packages/wolf_3d_flutter/lib/audio/audio_adaptor.dart +++ b/packages/wolf_3d_flutter/lib/audio/audio_adaptor.dart @@ -3,48 +3,52 @@ import 'package:wolf_3d_dart/wolf_3d_engine.dart'; import 'package:wolf_3d_flutter/wolf_3d.dart'; class FlutterAudioAdapter implements EngineAudio { + final Wolf3d wolf3d; + + FlutterAudioAdapter(this.wolf3d); + @override void playLevelMusic(WolfLevel level) { - Wolf3d.I.audio.playLevelMusic(level); + wolf3d.audio.playLevelMusic(level); } @override void stopMusic() { - Wolf3d.I.audio.stopMusic(); + wolf3d.audio.stopMusic(); } @override void playSoundEffect(int sfxId) { - Wolf3d.I.audio.playSoundEffect(sfxId); + wolf3d.audio.playSoundEffect(sfxId); } @override void playMenuMusic() { - Wolf3d.I.audio.playMenuMusic(); + wolf3d.audio.playMenuMusic(); } @override Future init() async { - await Wolf3d.I.audio.init(); + await wolf3d.audio.init(); } @override void dispose() { - Wolf3d.I.audio.dispose(); + wolf3d.audio.dispose(); } @override Future debugSoundTest() async { - Wolf3d.I.audio.debugSoundTest(); + wolf3d.audio.debugSoundTest(); } @override - WolfensteinData? get activeGame => Wolf3d.I.activeGame; + WolfensteinData? get activeGame => wolf3d.activeGame; @override set activeGame(WolfensteinData? value) { if (value != null) { - Wolf3d.I.setActiveGame(value); + wolf3d.setActiveGame(value); } } } diff --git a/packages/wolf_3d_flutter/lib/wolf_3d.dart b/packages/wolf_3d_flutter/lib/wolf_3d.dart index 6ea714f..b86b97e 100644 --- a/packages/wolf_3d_flutter/lib/wolf_3d.dart +++ b/packages/wolf_3d_flutter/lib/wolf_3d.dart @@ -6,9 +6,7 @@ import 'package:wolf_3d_dart/wolf_3d_engine.dart'; import 'package:wolf_3d_dart/wolf_3d_synth.dart'; class Wolf3d { - Wolf3d._(); - static final Wolf3d _instance = Wolf3d._(); - static Wolf3d get I => _instance; + Wolf3d(); // --- State --- final List availableGames = []; @@ -31,6 +29,13 @@ class Wolf3d { int get activeEpisode => _activeEpisode; 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; } @@ -45,12 +50,22 @@ class Wolf3d { // --- Actions --- 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; // No change needed + } + _activeGame = game; audio.activeGame = game; } /// Initializes the engine by loading available game data. - Future init({String? directory}) async { + Future init({String? directory}) async { await audio.init(); availableGames.clear(); @@ -63,7 +78,6 @@ class Wolf3d { for (final version in versionsToTry) { try { final ext = version.version.fileExtension; - // final folder = 'assets/${version.path}'; final folder = 'packages/wolf_3d_assets/assets/${version.path}'; final data = WolfensteinLoader.loadFromBytes( @@ -100,6 +114,8 @@ class Wolf3d { debugPrint("External discovery failed: $e"); } } + + return this; } Future _tryLoad(String path) async {