/// CLI entry point for the terminal Wolf3D host. /// /// This executable locates bundled retail assets, constructs a [WolfEngine] /// configured for terminal rendering, and then hands control to [CliGameLoop]. library; import 'dart:io'; import 'package:args/args.dart'; import 'package:wolf_3d_dart/wolf_3d_audio.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_host.dart'; import 'package:wolf_3d_dart/wolf_3d_input.dart'; /// Restores terminal state before exiting the process with [code]. void exitCleanly(int code) { stdout.write('\x1b[0m'); // Reset color stdout.write('\x1b[2J\x1b[H'); // Clear screen stdout.write('\x1b[?25h'); // SHOW the cursor again exit(code); } /// Launches the CLI renderer using discoverable or user-provided game data. void main(List arguments) async { final argParser = ArgParser() ..addOption( 'data-directory', abbr: 'd', valueHelp: 'path', help: 'Directory containing Wolf3D data files.', ) ..addFlag( 'help', abbr: 'h', negatable: false, help: 'Show usage information.', ); late final ArgResults parsedArgs; try { parsedArgs = argParser.parse(arguments); } on FormatException catch (e) { stderr.writeln(e.message); stderr.writeln('Usage: wolf_3d_cli [options]'); stderr.writeln(argParser.usage); exitCleanly(64); } if (parsedArgs.flag('help')) { stdout.writeln('Usage: wolf_3d_cli [options]'); stdout.writeln(argParser.usage); exitCleanly(0); } final String? rawPath = parsedArgs.option('data-directory'); final String? dataDirectory = rawPath != null && rawPath.trim().isNotEmpty ? rawPath.trim() : null; if (dataDirectory != null) { stdout.write('Discovering game data in "$dataDirectory"...'); } else { stdout.write('Discovering game data...'); } final availableGames = await WolfensteinLoader.discover( directoryPath: dataDirectory, recursive: true, ); if (availableGames.isEmpty) { if (dataDirectory == null) { stderr.writeln('\nNo Wolf3D game data was discovered.'); stderr.writeln('Provide a game-data directory with one of these flags:'); stderr.writeln(' --data-directory '); stderr.writeln(' -d '); } else { stderr.writeln('\nNo Wolf3D game files were found at: $dataDirectory'); stderr.writeln( 'Please provide valid game data files before starting the CLI host.', ); } exitCleanly(1); } CliGameLoop? gameLoop; late final WolfEngine engine; void stopAndExit(int code) { gameLoop?.stop(); engine.audio.dispose(); exitCleanly(code); } engine = WolfEngine( availableGames: availableGames.values.toList(growable: false), startingEpisode: 0, frameBuffer: FrameBuffer( stdout.terminalColumns, stdout.terminalLines, ), input: CliInput(), engineAudio: NativeSubprocessAudio(), onGameWon: () => stopAndExit(0), onQuit: () => stopAndExit(0), saveGamePersistence: DefaultSaveGamePersistence(), ); await engine.audio.init(); engine.init(); final persistence = DefaultRendererSettingsPersistence( hostKey: rendererSettingsHostCli, ); final WolfRendererSettings? saved = await persistence.load(); gameLoop = CliGameLoop( engine: engine, onExit: stopAndExit, persistence: persistence, initialSettings: saved, ); await gameLoop.start(); }