import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:wolf_3d_dart/wolf_3d_data.dart'; 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_synth.dart'; import 'package:wolf_3d_flutter/wolf_3d_input_flutter.dart'; class Wolf3d { Wolf3d(); // --- State --- final List availableGames = []; WolfensteinData? _activeGame; // --- Core Systems --- final EngineAudio audio = WolfAudio(); final Wolf3dFlutterInput input = Wolf3dFlutterInput(); // --- 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) { 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; } // 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) { 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 { 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 = 'packages/wolf_3d_assets/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"); } } return this; } Future _tryLoad(String path) async { try { return await rootBundle.load(path); } catch (e) { debugPrint("Asset not found: $path"); return null; } } }