feat: Add quit callback support to engine and UI components
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -61,6 +61,7 @@ void main() async {
|
||||
),
|
||||
input: CliInput(),
|
||||
onGameWon: () => stopAndExit(0),
|
||||
onQuit: () => stopAndExit(0),
|
||||
);
|
||||
|
||||
engine.init();
|
||||
|
||||
@@ -45,6 +45,9 @@ class _GameScreenState extends State<GameScreen> {
|
||||
widget.wolf3d.clearActiveDifficulty();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onQuit: () {
|
||||
SystemNavigator.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class WolfEngine {
|
||||
this.menuBackgroundRgb = 0x890000,
|
||||
this.menuPanelRgb = 0x590002,
|
||||
this.onMenuExit,
|
||||
this.onQuit,
|
||||
this.onGameSelected,
|
||||
this.onEpisodeSelected,
|
||||
EngineAudio? engineAudio,
|
||||
@@ -95,6 +96,9 @@ class WolfEngine {
|
||||
/// Callback triggered when backing out of the top-level menu.
|
||||
final void Function()? onMenuExit;
|
||||
|
||||
/// Callback triggered when the player explicitly selects QUIT.
|
||||
final void Function()? onQuit;
|
||||
|
||||
/// Callback triggered whenever the active game changes from menu flow.
|
||||
final void Function(WolfensteinData game)? onGameSelected;
|
||||
|
||||
@@ -330,8 +334,10 @@ class WolfEngine {
|
||||
case WolfMenuMainAction.backToGame:
|
||||
_resumeGame();
|
||||
break;
|
||||
case WolfMenuMainAction.backToDemo:
|
||||
case WolfMenuMainAction.quit:
|
||||
_quitProgram();
|
||||
break;
|
||||
case WolfMenuMainAction.backToDemo:
|
||||
_exitTopLevelMenu();
|
||||
break;
|
||||
case WolfMenuMainAction.sound:
|
||||
@@ -445,6 +451,14 @@ class WolfEngine {
|
||||
onGameWon();
|
||||
}
|
||||
|
||||
void _quitProgram() {
|
||||
if (onQuit != null) {
|
||||
onQuit!.call();
|
||||
return;
|
||||
}
|
||||
_exitTopLevelMenu();
|
||||
}
|
||||
|
||||
/// Wipes the current world state and builds a new floor from map data.
|
||||
void _loadLevel({required bool preservePlayerState}) {
|
||||
entities.clear();
|
||||
|
||||
@@ -216,12 +216,16 @@ void main() {
|
||||
expect(manager.selectedMainIndex, 0);
|
||||
});
|
||||
|
||||
test('quit selection triggers top-level menu exit callback', () {
|
||||
test('quit selection triggers dedicated quit callback', () {
|
||||
final input = _TestInput();
|
||||
int quitCalls = 0;
|
||||
int exitCalls = 0;
|
||||
final engine = _buildEngine(
|
||||
input: input,
|
||||
difficulty: null,
|
||||
onQuit: () {
|
||||
quitCalls++;
|
||||
},
|
||||
onMenuExit: () {
|
||||
exitCalls++;
|
||||
},
|
||||
@@ -245,6 +249,32 @@ void main() {
|
||||
engine.tick(const Duration(milliseconds: 16));
|
||||
input.isInteracting = false;
|
||||
|
||||
expect(quitCalls, 1);
|
||||
expect(exitCalls, 0);
|
||||
});
|
||||
|
||||
test('backing out of the top-level menu uses menu-exit callback', () {
|
||||
final input = _TestInput();
|
||||
int quitCalls = 0;
|
||||
int exitCalls = 0;
|
||||
final engine = _buildEngine(
|
||||
input: input,
|
||||
difficulty: null,
|
||||
onQuit: () {
|
||||
quitCalls++;
|
||||
},
|
||||
onMenuExit: () {
|
||||
exitCalls++;
|
||||
},
|
||||
);
|
||||
|
||||
engine.init();
|
||||
|
||||
input.isBack = true;
|
||||
engine.tick(const Duration(milliseconds: 16));
|
||||
input.isBack = false;
|
||||
|
||||
expect(quitCalls, 0);
|
||||
expect(exitCalls, 1);
|
||||
});
|
||||
});
|
||||
@@ -254,6 +284,7 @@ WolfEngine _buildMultiGameEngine({
|
||||
required _TestInput input,
|
||||
required Difficulty? difficulty,
|
||||
void Function()? onMenuExit,
|
||||
void Function()? onQuit,
|
||||
}) {
|
||||
final WolfensteinData retail = _buildTestData(
|
||||
gameVersion: GameVersion.retail,
|
||||
@@ -271,6 +302,7 @@ WolfEngine _buildMultiGameEngine({
|
||||
engineAudio: _SilentAudio(),
|
||||
onGameWon: () {},
|
||||
onMenuExit: onMenuExit,
|
||||
onQuit: onQuit,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -278,6 +310,7 @@ WolfEngine _buildEngine({
|
||||
required _TestInput input,
|
||||
required Difficulty? difficulty,
|
||||
void Function()? onMenuExit,
|
||||
void Function()? onQuit,
|
||||
}) {
|
||||
return WolfEngine(
|
||||
data: _buildTestData(gameVersion: GameVersion.retail),
|
||||
@@ -288,6 +321,7 @@ WolfEngine _buildEngine({
|
||||
engineAudio: _SilentAudio(),
|
||||
onGameWon: () {},
|
||||
onMenuExit: onMenuExit,
|
||||
onQuit: onQuit,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,10 @@ class Wolf3d {
|
||||
/// Uses [activeGame], [activeEpisode], and [activeDifficulty]. Stores the
|
||||
/// engine so it can be retrieved via [engine]. [onGameWon] is invoked when
|
||||
/// the player completes the final level of the episode.
|
||||
WolfEngine launchEngine({required void Function() onGameWon}) {
|
||||
WolfEngine launchEngine({
|
||||
required void Function() onGameWon,
|
||||
void Function()? onQuit,
|
||||
}) {
|
||||
if (availableGames.isEmpty) {
|
||||
throw StateError(
|
||||
'No game data was discovered. Add game files before launching the engine.',
|
||||
@@ -102,6 +105,7 @@ class Wolf3d {
|
||||
// In Flutter we keep the renderer screen active while browsing menus,
|
||||
// so backing out of the top-level menu should not pop the route.
|
||||
onMenuExit: () {},
|
||||
onQuit: onQuit,
|
||||
onGameSelected: (game) {
|
||||
_activeGame = game;
|
||||
audio.activeGame = game;
|
||||
|
||||
Reference in New Issue
Block a user