Refactor menu structure and add Flutter-specific input and persistence layers
- Moved menu-related classes to a new structure under `src/menu/`. - Introduced `WolfMenuPresentation` to handle menu art and mappings. - Added `MenuManager` tests to ensure menu state reflects game status. - Implemented `FlutterRendererSettingsPersistence` and `FlutterSaveGamePersistence` for managing settings and save files on desktop platforms. - Created `Wolf3dFlutterInput` to handle keyboard and mouse input in a Flutter environment. - Updated README to reflect new package structure and usage instructions. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -1,19 +1,11 @@
|
||||
/// High-level Flutter facade for discovering game data and sharing runtime services.
|
||||
library;
|
||||
|
||||
import 'package:flutter/foundation.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_flutter/audio/wolf3d_platform_audio.dart';
|
||||
import 'package:wolf_3d_flutter/managers/desktop_windowing_support.dart'
|
||||
as desktop_windowing_support;
|
||||
import 'package:wolf_3d_flutter/managers/game_data_directory_persistence.dart';
|
||||
import 'package:wolf_3d_flutter/wolf_3d_input_flutter.dart';
|
||||
|
||||
export 'package:wolf_3d_dart/wolf_3d_audio.dart' show DebugMusicPlayer;
|
||||
|
||||
export 'audio/wolf3d_platform_audio.dart' show Wolf3dPlatformAudio;
|
||||
export 'engine/wolf3d_flutter_engine.dart' show Wolf3dFlutterEngine;
|
||||
export 'input/wolf_3d_input_flutter.dart' show Wolf3dFlutterInput;
|
||||
export 'managers/game_app_lifecycle_manager.dart' show GameAppLifecycleManager;
|
||||
export 'managers/game_data_directory_persistence.dart'
|
||||
show DefaultGameDataDirectoryPersistence;
|
||||
@@ -29,6 +21,10 @@ export 'managers/game_screen_input_manager.dart'
|
||||
HostShortcutRegistry,
|
||||
GameScreenInputManager,
|
||||
isAltEnterShortcut;
|
||||
export 'persistence/renderer_settings_persistence_flutter.dart'
|
||||
show FlutterRendererSettingsPersistence;
|
||||
export 'persistence/save_game_persistence_flutter.dart'
|
||||
show FlutterSaveGamePersistence;
|
||||
export 'screens/audio_gallery.dart' show AudioGallery;
|
||||
export 'screens/debug_tools_screen.dart' show DebugToolsScreen;
|
||||
export 'screens/game_screen.dart' show GameScreen;
|
||||
@@ -37,109 +33,3 @@ export 'screens/vga_gallery.dart' show VgaGallery;
|
||||
export 'widgets/gallery_game_selector.dart'
|
||||
show GalleryGameSelector, formatGalleryGameTitle;
|
||||
export 'widgets/wolf_menu_shell.dart' show WolfMenuShell;
|
||||
|
||||
/// Flutter-specific host facade built on top of [Wolf3dEngine].
|
||||
///
|
||||
/// This type keeps platform-neutral session/engine state in the Dart package
|
||||
/// while owning Flutter-only concerns such as platform discovery helpers.
|
||||
class Wolf3dFlutterEngine extends Wolf3dEngine {
|
||||
/// Creates an empty facade that must be initialized with [init].
|
||||
Wolf3dFlutterEngine({
|
||||
bool debug = false,
|
||||
EngineAudio? audioBackend,
|
||||
Wolf3dFlutterInput? inputBackend,
|
||||
DefaultGameDataDirectoryPersistence? dataDirectoryPersistence,
|
||||
}) : dataDirectoryPersistence =
|
||||
dataDirectoryPersistence ?? DefaultGameDataDirectoryPersistence(),
|
||||
super(
|
||||
audio: audioBackend ?? Wolf3dPlatformAudio(),
|
||||
input: inputBackend ?? Wolf3dFlutterInput(),
|
||||
) {
|
||||
if (debug) {
|
||||
enableDebug();
|
||||
}
|
||||
}
|
||||
|
||||
/// Persists and restores the preferred external game-data directory.
|
||||
final DefaultGameDataDirectoryPersistence dataDirectoryPersistence;
|
||||
|
||||
/// Last configured/loaded external game-data directory path.
|
||||
String? configuredDataDirectory;
|
||||
|
||||
/// Shared Flutter input adapter reused by gameplay screens.
|
||||
@override
|
||||
Wolf3dFlutterInput get input => super.input as Wolf3dFlutterInput;
|
||||
|
||||
/// Enables host-level debug affordances such as debug navigation UI.
|
||||
@override
|
||||
Wolf3dFlutterEngine enableDebug() {
|
||||
super.enableDebug();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Initializes the engine by loading available game data.
|
||||
///
|
||||
/// If [directory] is provided, it is persisted and treated as the primary
|
||||
/// external search root. If omitted, a previously persisted directory is
|
||||
/// used when available. [additionalDirectories] are scanned after the
|
||||
/// primary directory and are not persisted.
|
||||
///
|
||||
/// This method scans only configured external directories, deduplicating
|
||||
/// discovered versions by [GameVersion]. Shared package code does not bundle
|
||||
/// or import game data on behalf of host applications.
|
||||
Future<Wolf3dFlutterEngine> init({
|
||||
String? directory,
|
||||
Iterable<String>? additionalDirectories,
|
||||
}) async {
|
||||
await desktop_windowing_support.ensureDesktopWindowingInitialized();
|
||||
await audio.init();
|
||||
availableGames.clear();
|
||||
|
||||
final String? requestedDirectory = directory?.trim();
|
||||
final String? resolvedDirectory =
|
||||
requestedDirectory != null && requestedDirectory.isNotEmpty
|
||||
? requestedDirectory
|
||||
: await dataDirectoryPersistence.loadDataDirectory();
|
||||
configuredDataDirectory = resolvedDirectory;
|
||||
|
||||
if (requestedDirectory != null && requestedDirectory.isNotEmpty) {
|
||||
await dataDirectoryPersistence.saveDataDirectory(requestedDirectory);
|
||||
}
|
||||
|
||||
// On non-web platforms, also scan local filesystem locations for
|
||||
// user-supplied data folders so the host can pick up extra versions.
|
||||
final Set<String> directoriesToScan = <String>{};
|
||||
if (resolvedDirectory != null && resolvedDirectory.isNotEmpty) {
|
||||
directoriesToScan.add(resolvedDirectory);
|
||||
}
|
||||
|
||||
if (additionalDirectories != null) {
|
||||
for (final String directoryPath in additionalDirectories) {
|
||||
final String trimmedPath = directoryPath.trim();
|
||||
if (trimmedPath.isNotEmpty) {
|
||||
directoriesToScan.add(trimmedPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!kIsWeb && directoriesToScan.isNotEmpty) {
|
||||
for (final String directoryPath in directoriesToScan) {
|
||||
try {
|
||||
final externalGames = await WolfensteinLoader.discover(
|
||||
directoryPath: directoryPath,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user