import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:wolf_3d_data/wolf_3d_data.dart'; import 'package:wolf_3d_data_types/wolf_3d_data_types.dart'; import 'package:wolf_3d_synth/wolf_3d_synth.dart'; class Wolf3d { Wolf3d._(); static final Wolf3d _instance = Wolf3d._(); static Wolf3d get I => _instance; // --- State --- final List availableGames = []; WolfensteinData? _activeGame; // --- Core Systems --- final WolfAudio audio = WolfAudio(); // --- Getters --- WolfensteinData get activeGame { if (_activeGame == null) { throw StateError("No active game selected. Call setActiveGame() first."); } return _activeGame!; } // --- Episode --- int _activeEpisode = 0; int get activeEpisode => _activeEpisode; void setActiveEpisode(int episodeIndex) { _activeEpisode = episodeIndex; } // Convenience getters for the active game's assets List get levels => activeGame.episodes[activeEpisode].levels; List get walls => activeGame.walls; List get sprites => activeGame.sprites; List get sounds => activeGame.sounds; List get adLibSounds => activeGame.adLibSounds; List get music => activeGame.music; List get vgaImages => activeGame.vgaImages; // --- Actions --- void setActiveGame(WolfensteinData game) { _activeGame = game; audio.activeGame = game; } /// Initializes the engine by loading available game data. Future init({String? directory}) async { await audio.init(); availableGames.clear(); // 1. Bundle asset loading (migrated from GameSelectScreen) final versionsToTry = [ (version: GameVersion.retail, path: 'retail'), (version: GameVersion.shareware, path: 'shareware'), ]; for (final version in versionsToTry) { try { final ext = version.version.fileExtension; final folder = 'assets/${version.path}'; final data = WolfensteinLoader.loadFromBytes( version: version.version, vswap: await _tryLoad('$folder/VSWAP.$ext'), mapHead: await _tryLoad('$folder/MAPHEAD.$ext'), gameMaps: await _tryLoad('$folder/GAMEMAPS.$ext'), vgaDict: await _tryLoad('$folder/VGADICT.$ext'), vgaHead: await _tryLoad('$folder/VGAHEAD.$ext'), vgaGraph: await _tryLoad('$folder/VGAGRAPH.$ext'), audioHed: await _tryLoad('$folder/AUDIOHED.$ext'), audioT: await _tryLoad('$folder/AUDIOT.$ext'), ); availableGames.add(data); } catch (e) { debugPrint(e.toString()); } } // 2. External side-loading (non-web) if (!kIsWeb) { try { final externalGames = await WolfensteinLoader.discover( directoryPath: directory, recursive: true, ); for (var entry in externalGames.entries) { if (!availableGames.any((g) => g.version == entry.key)) { availableGames.add(entry.value); } } } catch (e) { debugPrint("External discovery failed: $e"); } } } Future _tryLoad(String path) async { try { return await rootBundle.load(path); } catch (e) { debugPrint("Asset not found: $path"); return null; } } }