diff --git a/src/ui/flutter_app/lib/mill/game.dart b/src/ui/flutter_app/lib/mill/game.dart index d8b497fb..5d65ec26 100644 --- a/src/ui/flutter_app/lib/mill/game.dart +++ b/src/ui/flutter_app/lib/mill/game.dart @@ -32,6 +32,7 @@ class Game { final String tag = "[game]"; void init() { + // TODO: _position is allready initialized with Position(). seems like duplicate code _position = Position(); focusIndex = blurIndex = invalidIndex; } @@ -71,7 +72,7 @@ class Game { PieceColor.black: false }; - bool aiIsSearching() { + bool get aiIsSearching { debugPrint( "$tag White is searching? ${isSearching[PieceColor.white]}\n" "$tag Black is searching? ${isSearching[PieceColor.black]}\n", @@ -115,14 +116,14 @@ class Game { blurIndex = invalidIndex; } - bool doMove(String move) { + Future doMove(String move) async { if (position.phase == Phase.ready) { start(); } debugPrint("$tag AI do move: $move"); - if (!position.doMove(move)) { + if (await position.doMove(move) == false) { return false; } diff --git a/src/ui/flutter_app/lib/mill/position.dart b/src/ui/flutter_app/lib/mill/position.dart index a219e540..5e6415c6 100644 --- a/src/ui/flutter_app/lib/mill/position.dart +++ b/src/ui/flutter_app/lib/mill/position.dart @@ -25,6 +25,7 @@ import 'package:sanmill/mill/types.dart'; import 'package:sanmill/mill/zobrist.dart'; import 'package:sanmill/services/audios.dart'; import 'package:sanmill/services/engine/engine.dart'; +import 'package:sanmill/shared/common/config.dart'; List posKeyHistory = []; @@ -62,7 +63,7 @@ class Position { String us = PieceColor.white; String them = PieceColor.black; - String winner = PieceColor.nobody; + String _winner = PieceColor.nobody; GameOverReason gameOverReason = GameOverReason.noReason; @@ -114,7 +115,7 @@ class Position { st.pliesFromNull = other.st.pliesFromNull; them = other.them; - winner = other.winner; + _winner = other._winner; gameOverReason = other.gameOverReason; phase = other.phase; @@ -135,7 +136,7 @@ class Position { String movedPiece(int move) => pieceOn(fromSq(move)); - bool movePiece(int from, int to) { + Future movePiece(int from, int to) async { if (selectPiece(from) == 0) { return putPiece(to); } @@ -261,7 +262,7 @@ class Position { return true; } - bool doMove(String move) { + Future doMove(String move) async { if (move.length > "Player".length && move.substring(0, "Player".length - 1) == "Player") { if (move["Player".length] == '1') { @@ -278,7 +279,7 @@ class Position { if (move == "draw") { phase = Phase.gameOver; - winner = PieceColor.draw; + _winner = PieceColor.draw; if (score[PieceColor.draw] != null) { score[PieceColor.draw] = score[PieceColor.draw]! + 1; } @@ -287,7 +288,7 @@ class Position { if (rule.nMoveRule > 0 && posKeyHistory.length >= rule.nMoveRule - 1) { gameOverReason = GameOverReason.drawReasonRule50; } else if (rule.endgameNMoveRule < rule.nMoveRule && - isThreeEndgame() && + isThreeEndgame && posKeyHistory.length >= rule.endgameNMoveRule - 1) { gameOverReason = GameOverReason.drawReasonEndgameRule50; } else if (rule.threefoldRepetitionRule) { @@ -308,20 +309,20 @@ class Position { switch (m.type) { case MoveType.remove: - ret = removePiece(m.to) == 0; + ret = await removePiece(m.to) == 0; if (ret) { // Reset rule 50 counter st.rule50 = 0; } break; case MoveType.move: - ret = movePiece(m.from, m.to); + ret = await movePiece(m.from, m.to); if (ret) { ++st.rule50; } break; case MoveType.place: - ret = putPiece(m.to); + ret = await putPiece(m.to); if (ret) { // Reset rule 50 counter st.rule50 = 0; @@ -414,7 +415,7 @@ class Position { setSideToMove(PieceColor.white); action = Act.place; - winner = PieceColor.nobody; + _winner = PieceColor.nobody; gameOverReason = GameOverReason.noReason; clearBoard(); @@ -457,7 +458,7 @@ class Position { } } - bool putPiece(int s) { + Future putPiece(int s) async { var piece = Piece.noPiece; final us = _sideToMove; @@ -472,6 +473,7 @@ class Position { start(); } + // TODO: use switch case if (phase == Phase.placing) { piece = sideToMove; if (pieceInHandCount[us] != null) { @@ -501,7 +503,7 @@ class Position { if (pieceInHandCount[PieceColor.white] == 0 && pieceInHandCount[PieceColor.black] == 0) { - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } @@ -516,14 +518,14 @@ class Position { changeSideToMove(); } - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } } else { changeSideToMove(); } gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.placeSoundId); + await Audios.playTone(Sound.place); } else { pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1; updateKeyMisc(); @@ -539,7 +541,7 @@ class Position { if (pieceInHandCount[PieceColor.white] == 0 && pieceInHandCount[PieceColor.black] == 0) { - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } @@ -550,7 +552,7 @@ class Position { changeSideToMove(); } - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } } @@ -559,10 +561,10 @@ class Position { } gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.millSoundId); + await Audios.playTone(Sound.mill); } } else if (phase == Phase.moving) { - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } @@ -603,18 +605,18 @@ class Position { action = Act.select; changeSideToMove(); - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } else { gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.placeSoundId); + await Audios.playTone(Sound.place); } } else { pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1; updateKeyMisc(); action = Act.remove; gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.millSoundId); + await Audios.playTone(Sound.mill); } } else { assert(false); @@ -623,7 +625,7 @@ class Position { return true; } - int removePiece(int s) { + Future removePiece(int s) async { if (phase == Phase.ready || phase == Phase.gameOver) return -1; if (action != Act.remove) return -1; @@ -641,7 +643,7 @@ class Position { revertKey(s); - Audios.playTone(Audios.removeSoundId); + await Audios.playTone(Sound.remove); if (rule.hasBannedLocations && phase == Phase.placing) { // Remove and put ban @@ -685,7 +687,7 @@ class Position { } if (rule.isDefenderMoveFirst) { - checkIfGameIsOver(); + isGameOver(); return 0; } } else { @@ -696,7 +698,7 @@ class Position { } changeSideToMove(); - checkIfGameIsOver(); + isGameOver(); return 0; } @@ -733,14 +735,12 @@ class Position { return true; } - String getWinner() { - return winner; - } + String get winner => _winner; void setGameOver(String w, GameOverReason reason) { phase = Phase.gameOver; gameOverReason = reason; - winner = w; + _winner = w; debugPrint("[position] Game over, $w win, because of $reason"); updateScore(); @@ -748,7 +748,7 @@ class Position { void updateScore() { if (phase == Phase.gameOver) { - if (winner == PieceColor.draw) { + if (_winner == PieceColor.draw) { if (score[PieceColor.draw] != null) { score[PieceColor.draw] = score[PieceColor.draw]! + 1; } @@ -756,13 +756,13 @@ class Position { return; } - if (score[winner] != null) { - score[winner] = score[winner]! + 1; + if (score[_winner] != null) { + score[_winner] = score[_winner]! + 1; } } } - bool isThreeEndgame() { + bool get isThreeEndgame { if (phase == Phase.placing) { return false; } @@ -771,7 +771,7 @@ class Position { pieceOnBoardCount[PieceColor.black] == 3; } - bool checkIfGameIsOver() { + bool isGameOver() { if (phase == Phase.ready || phase == Phase.gameOver) { return true; } @@ -782,7 +782,7 @@ class Position { } if (rule.endgameNMoveRule < rule.nMoveRule && - isThreeEndgame() && + isThreeEndgame && posKeyHistory.length >= rule.endgameNMoveRule) { setGameOver(PieceColor.draw, GameOverReason.drawReasonEndgameRule50); return true; @@ -1047,7 +1047,7 @@ class Position { nPiecesInHand; pieceToRemoveCount = 0; - winner = PieceColor.nobody; + _winner = PieceColor.nobody; Mills.adjacentSquaresInit(); Mills.millTableInit(); currentSquare = 0; @@ -1086,25 +1086,29 @@ class Position { } /////////////////////////////////////////////////////////////////////////////// - - Future _gotoHistory(int moveIndex) async { - String errString = ""; + Future gotoHistory(HistoryMove move, [int? index]) async { + final int moveIndex = _gotoHistoryIndex(move, index); if (recorder == null) { debugPrint("[goto] recorder is null."); return "null"; } + if (recorder!.cur == moveIndex) { + debugPrint("[goto] cur is equal to moveIndex."); + return "equal"; + } + final history = recorder!.history; + if (moveIndex < -1 || history.length <= moveIndex) { debugPrint("[goto] moveIndex is out of range."); return "out-of-range"; } - if (recorder!.cur == moveIndex) { - debugPrint("[goto] cur is equal to moveIndex."); - return "equal"; - } + String errString = ""; + + Audios.isTemporaryMute = true; // Backup context final engineTypeBackup = gameInstance.engineType; @@ -1121,13 +1125,10 @@ class Position { } for (var i = 0; i <= moveIndex; i++) { - if (gameInstance.doMove(history[i].move!) == false) { + if (await gameInstance.doMove(history[i].move!) == false) { errString = history[i].move!; break; } - - //await Future.delayed(Duration(seconds: 1)); - //setState(() {}); } // Restore context @@ -1136,31 +1137,46 @@ class Position { recorder!.history = historyBack; recorder!.cur = moveIndex; + Audios.isTemporaryMute = false; + await _gotoHistoryPlaySound(move); + return errString; } - Future takeBackN(int n) async { - int index = recorder!.cur - n; - if (index < -1) { - index = -1; + int _gotoHistoryIndex(HistoryMove move, [int? index]) { + switch (move) { + case HistoryMove.forwardAll: + return recorder!.history.length - 1; + case HistoryMove.backAll: + return -1; + case HistoryMove.farward: + return recorder!.cur + 1; + case HistoryMove.backN: + assert(index != null); + int _index = recorder!.cur - index!; + if (_index < -1) { + _index = -1; + } + return _index; + case HistoryMove.backOne: + return recorder!.cur - 1; } - return _gotoHistory(index); } - Future takeBack() async { - return _gotoHistory(recorder!.cur - 1); - } + Future _gotoHistoryPlaySound(HistoryMove move) async { + if (!Config.keepMuteWhenTakingBack) { + switch (move) { + case HistoryMove.forwardAll: + case HistoryMove.farward: + await Audios.playTone(Sound.place); + break; + case HistoryMove.backAll: + case HistoryMove.backN: - Future stepForward() async { - return _gotoHistory(recorder!.cur + 1); - } - - Future takeBackAll() async { - return _gotoHistory(-1); - } - - Future stepForwardAll() async { - return _gotoHistory(recorder!.history.length - 1); + case HistoryMove.backOne: + await Audios.playTone(Sound.remove); + } + } } String movesSinceLastRemove() { @@ -1205,3 +1221,5 @@ class Position { String? get lastPositionWithRemove => recorder!.lastPositionWithRemove; } + +enum HistoryMove { forwardAll, backAll, farward, backN, backOne } diff --git a/src/ui/flutter_app/lib/screens/game_page/game_page.dart b/src/ui/flutter_app/lib/screens/game_page/game_page.dart index 8375a3d1..9b6797cd 100644 --- a/src/ui/flutter_app/lib/screens/game_page/game_page.dart +++ b/src/ui/flutter_app/lib/screens/game_page/game_page.dart @@ -172,7 +172,7 @@ class _GamePageState extends State } } - dynamic onBoardTap(int index) { + Future onBoardTap(int index) async { if (!isReady) { debugPrint("[tap] Not ready, ignore tapping."); return false; @@ -208,18 +208,18 @@ class _GamePageState extends State gameInstance.newGame(); if (gameInstance.isAiToMove) { - if (gameInstance.aiIsSearching()) { + if (gameInstance.aiIsSearching) { debugPrint("$tag AI is thinking, skip tapping."); return false; } else { debugPrint("[tap] AI is not thinking. AI is to move."); - engineToGo(false); + await engineToGo(false); return false; } } } - if (gameInstance.isAiToMove || gameInstance.aiIsSearching()) { + if (gameInstance.isAiToMove || gameInstance.aiIsSearching) { debugPrint("[tap] AI's turn, skip tapping."); return false; } @@ -231,12 +231,12 @@ class _GamePageState extends State // Human to go bool ret = false; - Chain.capture(() { + await Chain.capture(() async { switch (position.action) { case Act.place: - if (position.putPiece(sq)) { + if (await position.putPiece(sq)) { if (position.action == Act.remove) { - //Audios.playTone(Audios.millSoundId); + //Audios.playTone(Audios.mill); if (mounted) { showTip(S.of(context).tipMill); if (Config.screenReaderSupport) { @@ -244,7 +244,7 @@ class _GamePageState extends State } } } else { - //Audios.playTone(Audios.placeSoundId); + //Audios.playTone(Audios.place); if (gameInstance.engineType == EngineType.humanVsAi && mounted) { if (rule.mayOnlyRemoveUnplacedPieceInPlacingPhase) { showTip(S.of(context).continueToMakeMove); @@ -296,7 +296,7 @@ class _GamePageState extends State final int selectRet = position.selectPiece(sq); switch (selectRet) { case 0: - Audios.playTone(Audios.selectSoundId); + await Audios.playTone(Sound.select); gameInstance.select(index); ret = true; debugPrint("[tap] selectPiece: [$sq]"); @@ -325,7 +325,7 @@ class _GamePageState extends State break; case -2: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted && position.phase != Phase.gameOver) { showTip(S.of(context).tipCannotMove); @@ -336,7 +336,7 @@ class _GamePageState extends State } break; case -3: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted) { showTip(S.of(context).tipCanMoveOnePoint); @@ -347,7 +347,7 @@ class _GamePageState extends State } break; case -4: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted) { showTip(S.of(context).tipSelectPieceToMove); @@ -357,7 +357,7 @@ class _GamePageState extends State } break; default: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted) { showTip(S.of(context).tipSelectWrong); @@ -372,11 +372,11 @@ class _GamePageState extends State break; case Act.remove: - final int removeRet = position.removePiece(sq); + final int removeRet = await position.removePiece(sq); switch (removeRet) { case 0: - //Audios.playTone(Audios.removeSoundId); + //Audios.playTone(Audios.remove); ret = true; debugPrint("[tap] removePiece: [$sq]"); if (gameInstance.position.pieceToRemoveCount >= 1) { @@ -404,7 +404,7 @@ class _GamePageState extends State } break; case -2: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint( "[tap] removePiece: Cannot Remove our pieces, skip [$sq]", ); @@ -416,7 +416,7 @@ class _GamePageState extends State } break; case -3: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint( "[tap] removePiece: Cannot remove piece from Mill, skip [$sq]", ); @@ -432,7 +432,7 @@ class _GamePageState extends State } break; default: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] removePiece: skip [$sq]"); if (mounted && position.phase != Phase.gameOver) { showTip(S.of(context).tipBanRemove); @@ -587,7 +587,7 @@ class _GamePageState extends State ); } - gameInstance.doMove(move.move!); + await gameInstance.doMove(move.move!); showTips(); if (Config.screenReaderSupport && move.notation != null) { showSnackBar(context, "${S.of(context).ai}: ${move.notation!}"); @@ -657,7 +657,7 @@ class _GamePageState extends State debugPrint("Clipboard text:"); debugPrint(text); - await onTakeBackAllButtonPressed(pop: false); + await onTakeBackAllButtonPressed(false); gameInstance.position.recorder!.clear(); final importFailedStr = gameInstance.position.recorder!.import(text); @@ -670,7 +670,7 @@ class _GamePageState extends State return; } - await onStepForwardAllButtonPressed(pop: false); + await onStepForwardAllButtonPressed(false); showTip(S.of(context).gameImported); if (Config.screenReaderSupport) { @@ -774,8 +774,8 @@ class _GamePageState extends State Future onAutoReplayButtonPressed() async { Navigator.pop(context); - await onTakeBackAllButtonPressed(pop: false); - await onStepForwardAllButtonPressed(pop: false); + await onTakeBackAllButtonPressed(false); + await onStepForwardAllButtonPressed(false); } void onGameButtonPressed() { @@ -969,8 +969,9 @@ class _GamePageState extends State } Future onGotoHistoryButtonsPressed( - Future func, { + HistoryMove move, { bool pop = true, + int? number, }) async { if (pop == true) { Navigator.pop(context); @@ -989,9 +990,7 @@ class _GamePageState extends State isGoingToHistory = true; - Audios.isTemporaryMute = Config.keepMuteWhenTakingBack; - - final errMove = await func; + final errMove = await gameInstance.position.gotoHistory(move, number); switch (errMove) { case "": @@ -1008,14 +1007,10 @@ class _GamePageState extends State break; } - Audios.isTemporaryMute = false; - isGoingToHistory = false; if (mounted) { - String text = ""; final pos = gameInstance.position; - /* String us = ""; String them = ""; @@ -1028,6 +1023,7 @@ class _GamePageState extends State } */ + late final String text; final lastEffectiveMove = pos.recorder!.lastEffectiveMove; if (lastEffectiveMove != null && lastEffectiveMove.notation != null) { text = "${S.of(context).lastMove}: ${lastEffectiveMove.notation}"; @@ -1044,28 +1040,37 @@ class _GamePageState extends State } } - Future onTakeBackButtonPressed({bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.takeBack(), pop: pop); - } + Future onTakeBackButtonPressed([bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.backOne, + pop: pop, + ); - Future onStepForwardButtonPressed({bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.stepForward(), pop: pop); - } + Future onStepForwardButtonPressed([bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.farward, + pop: pop, + ); - Future onTakeBackAllButtonPressed({bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.takeBackAll(), pop: pop); - } + Future onTakeBackAllButtonPressed([bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.backAll, + pop: pop, + ); - Future onStepForwardAllButtonPressed({bool pop = true}) async { + Future onStepForwardAllButtonPressed([bool pop = true]) async { onGotoHistoryButtonsPressed( - gameInstance.position.stepForwardAll(), + HistoryMove.forwardAll, pop: pop, ); } - Future onTakeBackNButtonPressed(int n, {bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.takeBackN(n), pop: pop); - } + Future onTakeBackNButtonPressed(int n, [bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.backN, + number: n, + pop: pop, + ); void onMoveListButtonPressed() { final moveHistoryText = gameInstance.position.moveHistoryText; @@ -1177,7 +1182,7 @@ class _GamePageState extends State debugPrint("[config] isPrivacyPolicyAccepted: $value"); - Config.save(); + await Config.save(); } Future onShowPrivacyDialog() async { @@ -1259,10 +1264,10 @@ class _GamePageState extends State switch (result) { case GameResult.win: - //Audios.playTone(Audios.winSoundId); + //Audios.playTone(Audios.win); break; case GameResult.lose: - //Audios.playTone(Audios.loseSoundId); + //Audios.playTone(Audios.lose); break; case GameResult.draw: break; @@ -1694,7 +1699,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onTakeBackAllButtonPressed(pop: false), + onPressed: () => onTakeBackAllButtonPressed(false), ); final takeBackButton = TextButton( @@ -1707,7 +1712,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onTakeBackButtonPressed(pop: false), + onPressed: () async => onTakeBackButtonPressed(false), ); final stepForwardButton = TextButton( @@ -1720,7 +1725,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onStepForwardButtonPressed(pop: false), + onPressed: () async => onStepForwardButtonPressed(false), ); final stepForwardAllButton = TextButton( @@ -1733,7 +1738,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onStepForwardAllButtonPressed(pop: false), + onPressed: () async => onStepForwardAllButtonPressed(false), ); return GamePageToolBar( diff --git a/src/ui/flutter_app/lib/services/audios.dart b/src/ui/flutter_app/lib/services/audios.dart index 1c7dabbc..07f8d907 100644 --- a/src/ui/flutter_app/lib/services/audios.dart +++ b/src/ui/flutter_app/lib/services/audios.dart @@ -24,22 +24,35 @@ import 'package:sanmill/shared/common/config.dart'; import 'package:soundpool/soundpool.dart'; import 'package:stack_trace/stack_trace.dart'; +enum Sound { + draw, + fly, + go, + illegal, + lose, + mill, + place, + remove, + select, + win, +} + class Audios { const Audios._(); //static AudioPlayer? _player; - static Soundpool? _soundpool; - // TODO: use enum for the sounds - static int? _alarmSoundStreamId; - static int? drawSoundId; - static int? flySoundId; - static int? goSoundId; - static int? illegalSoundId; - static int? loseSoundId; - static int? millSoundId; - static int? placeSoundId; - static int? removeSoundId; - static int? selectSoundId; - static int? winSoundId; + static final Soundpool _soundpool = Soundpool.fromOptions(); + static bool _initialized = false; + static int _alarmSoundStreamId = 0; + static late final int _drawSoundId; + static late final int _flySoundId; + static late final int _goSoundId; + static late final int _illegalSoundId; + static late final int _loseSoundId; + static late final int _millSoundId; + static late final int _placeSoundId; + static late final int _removeSoundId; + static late final int _selectSoundId; + static late final int _winSoundId; static bool isTemporaryMute = false; static Future loadSounds() async { @@ -48,156 +61,110 @@ class Audios { return; } - _soundpool ??= Soundpool.fromOptions(); + _drawSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/draw.mp3"), + ); - if (_soundpool == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: _soundpool is null."); - return; - } + _flySoundId = await _soundpool.load( + await rootBundle.load("assets/audios/fly.mp3"), + ); - drawSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/draw.mp3")); - if (drawSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: drawSoundId is null."); - return; - } + _goSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/go.mp3"), + ); - flySoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/fly.mp3")); - if (flySoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: flySoundId is null."); - return; - } + _illegalSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/illegal.mp3"), + ); - goSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/go.mp3")); - if (goSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: goSoundId is null."); - return; - } + _loseSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/lose.mp3"), + ); - illegalSoundId = await _soundpool! - .load(await rootBundle.load("assets/audios/illegal.mp3")); - if (illegalSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: illegalSoundId is null."); - return; - } + _millSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/mill.mp3"), + ); - loseSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/lose.mp3")); - if (loseSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: loseSoundId is null."); - return; - } + _placeSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/place.mp3"), + ); - millSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/mill.mp3")); - if (millSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: millSoundId is null."); - return; - } + _removeSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/remove.mp3"), + ); - placeSoundId ??= await _soundpool! - .load(await rootBundle.load("assets/audios/place.mp3")); - if (placeSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: placeSoundId is null."); - return; - } + _selectSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/select.mp3"), + ); - removeSoundId ??= await _soundpool! - .load(await rootBundle.load("assets/audios/remove.mp3")); - if (removeSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: removeSoundId is null."); - return; - } + _winSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/win.mp3"), + ); - selectSoundId ??= await _soundpool! - .load(await rootBundle.load("assets/audios/select.mp3")); - if (selectSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: selectSoundId is null."); - return; - } - - winSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/win.mp3")); - if (winSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: winSoundId is null."); - return; - } + _initialized = true; } - static Future _playSound(int? soundId) async { - if (Platform.isWindows) { - return; + static Future _playSound(Sound sound) async { + assert(!Platform.isWindows); + + late final int soundId; + + switch (sound) { + case Sound.draw: + soundId = _drawSoundId; + break; + case Sound.fly: + soundId = _flySoundId; + break; + case Sound.go: + soundId = _goSoundId; + break; + case Sound.illegal: + soundId = _illegalSoundId; + break; + case Sound.lose: + soundId = _loseSoundId; + break; + case Sound.mill: + soundId = _millSoundId; + break; + case Sound.place: + soundId = _placeSoundId; + break; + case Sound.remove: + soundId = _removeSoundId; + break; + case Sound.select: + soundId = _selectSoundId; + break; + case Sound.win: + soundId = _winSoundId; + break; } - if (soundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: soundId is null."); - return; - } - - _alarmSoundStreamId = await _soundpool!.play(soundId); + _alarmSoundStreamId = await _soundpool.play(soundId); } static Future _stopSound() async { - if (Platform.isWindows) { - return; - } + assert(!Platform.isWindows); - if (_alarmSoundStreamId != null && _alarmSoundStreamId! > 0) { - await _soundpool!.stop(_alarmSoundStreamId!); + if (_alarmSoundStreamId > 0) { + await _soundpool.stop(_alarmSoundStreamId); } } - static Future disposePool() async { - if (Platform.isWindows) { - return; - } + static void disposePool() { + assert(!Platform.isWindows); - _soundpool!.dispose(); + _soundpool.dispose(); } - static Future playTone(int? soundId) async { - Chain.capture(() async { + static Future playTone(Sound sound) async { + await Chain.capture(() async { if (!Config.toneEnabled || isTemporaryMute || - Config.screenReaderSupport) { + Config.screenReaderSupport || + !_initialized) { return; } @@ -206,14 +173,11 @@ class Audios { return; } + // TODO: isn't debug chain meant to catch errors? so why catching them in here and not in onError?? try { - if (_soundpool == null) { - await loadSounds(); - } - await _stopSound(); - _playSound(soundId); + await _playSound(sound); } catch (e) { // Fallback for all errors debugPrint(e.toString()); diff --git a/src/ui/flutter_app/lib/shared/drawer_controller.dart b/src/ui/flutter_app/lib/shared/drawer_controller.dart index 94a14dfe..2ebde3e7 100644 --- a/src/ui/flutter_app/lib/shared/drawer_controller.dart +++ b/src/ui/flutter_app/lib/shared/drawer_controller.dart @@ -111,7 +111,7 @@ class _DrawerControllerState extends State super.initState(); } - Future getInitState() async { + bool getInitState() { scrollController.jumpTo( widget.drawerWidth, );