Moved all widgets and logic from gui app to Flutter package
- Implemented DebugToolsScreen for navigation to asset galleries. - Created GameScreen to manage gameplay and renderer integrations. - Added NoGameDataScreen to handle scenarios with missing game data. - Developed SpriteGallery for visual browsing of sprite assets. - Introduced VgaGallery for displaying VGA images from game data. - Added GalleryGameSelector widget for selecting game variants in galleries. - Created Wolf3dApp as the main application shell for managing game states. - Implemented WolfMenuShell for consistent menu layouts across screens. - Enhanced Wolf3d class to support debug mode and related functionalities. - Updated pubspec.yaml to include window_manager dependency. - Added tests for game screen lifecycle and debug mode functionalities. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.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/wolf_3d_flutter.dart';
|
||||
|
||||
class _CountingAudio implements EngineAudio {
|
||||
@override
|
||||
WolfensteinData? activeGame;
|
||||
|
||||
int stopAllAudioCallCount = 0;
|
||||
int disposeCallCount = 0;
|
||||
|
||||
@override
|
||||
Future<void> debugSoundTest() async {}
|
||||
|
||||
@override
|
||||
Future<void> init() async {}
|
||||
|
||||
@override
|
||||
void playLevelMusic(Music music) {}
|
||||
|
||||
@override
|
||||
void playMenuMusic() {}
|
||||
|
||||
@override
|
||||
void playSoundEffect(SoundEffect effect) {}
|
||||
|
||||
@override
|
||||
void playSoundEffectId(int sfxId) {}
|
||||
|
||||
@override
|
||||
Future<void> stopAllAudio() async {
|
||||
stopAllAudioCallCount++;
|
||||
await Future<void>.delayed(const Duration(milliseconds: 1));
|
||||
}
|
||||
|
||||
@override
|
||||
void stopMusic() {}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
disposeCallCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('dispose path shuts down audio', (tester) async {
|
||||
final audio = _CountingAudio();
|
||||
final wolf3d = Wolf3d(audioBackend: audio);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: GameScreen(
|
||||
wolf3d: wolf3d,
|
||||
skipEngineBootstrapForTest: true,
|
||||
appExitHandler: () async {},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(const MaterialApp(home: SizedBox.shrink()));
|
||||
await tester.pump(const Duration(milliseconds: 10));
|
||||
|
||||
expect(audio.stopAllAudioCallCount, 1);
|
||||
expect(audio.disposeCallCount, 1);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.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/wolf_3d_flutter.dart';
|
||||
|
||||
class _NoopAudio implements EngineAudio {
|
||||
@override
|
||||
WolfensteinData? activeGame;
|
||||
|
||||
@override
|
||||
Future<void> debugSoundTest() async {}
|
||||
|
||||
@override
|
||||
Future<void> init() async {}
|
||||
|
||||
@override
|
||||
void playLevelMusic(Music music) {}
|
||||
|
||||
@override
|
||||
void playMenuMusic() {}
|
||||
|
||||
@override
|
||||
void playSoundEffect(SoundEffect effect) {}
|
||||
|
||||
@override
|
||||
void playSoundEffectId(int sfxId) {}
|
||||
|
||||
@override
|
||||
Future<void> stopAllAudio() async {}
|
||||
|
||||
@override
|
||||
void stopMusic() {}
|
||||
|
||||
@override
|
||||
void dispose() {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('Wolf3d debug mode', () {
|
||||
test('is disabled by default', () {
|
||||
final wolf3d = Wolf3d(audioBackend: _NoopAudio());
|
||||
|
||||
expect(wolf3d.isDebugEnabled, isFalse);
|
||||
});
|
||||
|
||||
test('enableDebug toggles debug mode', () {
|
||||
final wolf3d = Wolf3d(audioBackend: _NoopAudio());
|
||||
|
||||
final returned = wolf3d.enableDebug();
|
||||
|
||||
expect(returned, same(wolf3d));
|
||||
expect(wolf3d.isDebugEnabled, isTrue);
|
||||
});
|
||||
|
||||
test('init(debug: true) enables debug mode', () async {
|
||||
final wolf3d = Wolf3d(audioBackend: _NoopAudio());
|
||||
|
||||
await wolf3d.init(debug: true);
|
||||
|
||||
expect(wolf3d.isDebugEnabled, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('GameScreen hides debug FAB when debug mode is disabled', (
|
||||
tester,
|
||||
) async {
|
||||
final wolf3d = _TestWolf3d(audioBackend: _NoopAudio());
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: GameScreen(
|
||||
wolf3d: wolf3d,
|
||||
appExitHandler: () async {},
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byType(FloatingActionButton), findsNothing);
|
||||
expect(find.byIcon(Icons.bug_report), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('GameScreen shows debug FAB when debug mode is enabled', (
|
||||
tester,
|
||||
) async {
|
||||
final wolf3d = _TestWolf3d(audioBackend: _NoopAudio())..enableDebug();
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: GameScreen(
|
||||
wolf3d: wolf3d,
|
||||
appExitHandler: () async {},
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byType(FloatingActionButton), findsOneWidget);
|
||||
expect(find.byIcon(Icons.bug_report), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class _TestWolf3d extends Wolf3d {
|
||||
_TestWolf3d({required super.audioBackend});
|
||||
|
||||
@override
|
||||
WolfEngine launchEngine({
|
||||
required void Function() onGameWon,
|
||||
void Function()? onQuit,
|
||||
SaveGamePersistence? saveGamePersistence,
|
||||
WolfRendererCapabilities? rendererCapabilities,
|
||||
WolfRendererSettings? rendererSettings,
|
||||
void Function(WolfRendererSettings settings)? onRendererSettingsChanged,
|
||||
}) {
|
||||
final engine = WolfEngine(
|
||||
data: _buildTestData(),
|
||||
difficulty: Difficulty.easy,
|
||||
startingEpisode: 0,
|
||||
frameBuffer: FrameBuffer(64, 64),
|
||||
engineAudio: audio,
|
||||
input: input,
|
||||
onGameWon: onGameWon,
|
||||
onMenuExit: () {},
|
||||
onQuit: onQuit,
|
||||
saveGamePersistence: saveGamePersistence,
|
||||
rendererCapabilities: rendererCapabilities,
|
||||
rendererSettings: const WolfRendererSettings(
|
||||
mode: WolfRendererMode.software,
|
||||
),
|
||||
onRendererSettingsChanged: onRendererSettingsChanged,
|
||||
);
|
||||
engine.init();
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
|
||||
WolfensteinData _buildTestData() {
|
||||
final wallGrid = List.generate(64, (_) => List.filled(64, 0));
|
||||
final objectGrid = List.generate(64, (_) => List.filled(64, 0));
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
wallGrid[0][i] = 2;
|
||||
wallGrid[63][i] = 2;
|
||||
wallGrid[i][0] = 2;
|
||||
wallGrid[i][63] = 2;
|
||||
}
|
||||
objectGrid[2][2] = MapObject.playerEast;
|
||||
|
||||
return WolfensteinData(
|
||||
version: GameVersion.retail,
|
||||
dataVersion: DataVersion.unknown,
|
||||
registry: RetailAssetRegistry(),
|
||||
walls: [_sprite(1), _sprite(1), _sprite(2), _sprite(2)],
|
||||
sprites: List.generate(436, (_) => _sprite(255)),
|
||||
sounds: List.generate(200, (_) => PcmSound(Uint8List(1))),
|
||||
adLibSounds: const [],
|
||||
music: const [],
|
||||
vgaImages: const [],
|
||||
episodes: [
|
||||
Episode(
|
||||
name: 'Test Episode',
|
||||
levels: [
|
||||
WolfLevel(
|
||||
name: 'Test Level',
|
||||
wallGrid: wallGrid,
|
||||
areaGrid: List.generate(64, (_) => List.filled(64, -1)),
|
||||
objectGrid: objectGrid,
|
||||
music: Music.level01,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Sprite _sprite(int color) =>
|
||||
Sprite(Uint8List.fromList(List.filled(64 * 64, color)));
|
||||
Reference in New Issue
Block a user