fix sound on game navigation
Effectively rewriting the entire audio stack - propperly await futures - only play sound once on take play back Further testing is needed but as we await the autio play we should be able to remove the stopPlay now. fix late init problem
This commit is contained in:
parent
c029bf39d0
commit
755e75231d
|
@ -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<bool> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<int> 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<bool> 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<bool> 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<bool> 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<int> 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<String> _gotoHistory(int moveIndex) async {
|
||||
String errString = "";
|
||||
Future<String> 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<String> 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<String> takeBack() async {
|
||||
return _gotoHistory(recorder!.cur - 1);
|
||||
}
|
||||
Future<void> _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<String> stepForward() async {
|
||||
return _gotoHistory(recorder!.cur + 1);
|
||||
}
|
||||
|
||||
Future<String> takeBackAll() async {
|
||||
return _gotoHistory(-1);
|
||||
}
|
||||
|
||||
Future<String> 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 }
|
||||
|
|
|
@ -172,7 +172,7 @@ class _GamePageState extends State<GamePage>
|
|||
}
|
||||
}
|
||||
|
||||
dynamic onBoardTap(int index) {
|
||||
Future<dynamic> onBoardTap(int index) async {
|
||||
if (!isReady) {
|
||||
debugPrint("[tap] Not ready, ignore tapping.");
|
||||
return false;
|
||||
|
@ -208,18 +208,18 @@ class _GamePageState extends State<GamePage>
|
|||
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<GamePage>
|
|||
// 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<GamePage>
|
|||
}
|
||||
}
|
||||
} 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<GamePage>
|
|||
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<GamePage>
|
|||
|
||||
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<GamePage>
|
|||
}
|
||||
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<GamePage>
|
|||
}
|
||||
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<GamePage>
|
|||
}
|
||||
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<GamePage>
|
|||
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<GamePage>
|
|||
}
|
||||
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<GamePage>
|
|||
}
|
||||
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<GamePage>
|
|||
}
|
||||
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<GamePage>
|
|||
);
|
||||
}
|
||||
|
||||
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<GamePage>
|
|||
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<GamePage>
|
|||
return;
|
||||
}
|
||||
|
||||
await onStepForwardAllButtonPressed(pop: false);
|
||||
await onStepForwardAllButtonPressed(false);
|
||||
|
||||
showTip(S.of(context).gameImported);
|
||||
if (Config.screenReaderSupport) {
|
||||
|
@ -774,8 +774,8 @@ class _GamePageState extends State<GamePage>
|
|||
Future<void> 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<GamePage>
|
|||
}
|
||||
|
||||
Future<void> onGotoHistoryButtonsPressed(
|
||||
Future<String> func, {
|
||||
HistoryMove move, {
|
||||
bool pop = true,
|
||||
int? number,
|
||||
}) async {
|
||||
if (pop == true) {
|
||||
Navigator.pop(context);
|
||||
|
@ -989,9 +990,7 @@ class _GamePageState extends State<GamePage>
|
|||
|
||||
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<GamePage>
|
|||
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<GamePage>
|
|||
}
|
||||
*/
|
||||
|
||||
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<GamePage>
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> onTakeBackButtonPressed({bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.takeBack(), pop: pop);
|
||||
}
|
||||
Future<void> onTakeBackButtonPressed([bool pop = true]) async =>
|
||||
onGotoHistoryButtonsPressed(
|
||||
HistoryMove.backOne,
|
||||
pop: pop,
|
||||
);
|
||||
|
||||
Future<void> onStepForwardButtonPressed({bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.stepForward(), pop: pop);
|
||||
}
|
||||
Future<void> onStepForwardButtonPressed([bool pop = true]) async =>
|
||||
onGotoHistoryButtonsPressed(
|
||||
HistoryMove.farward,
|
||||
pop: pop,
|
||||
);
|
||||
|
||||
Future<void> onTakeBackAllButtonPressed({bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.takeBackAll(), pop: pop);
|
||||
}
|
||||
Future<void> onTakeBackAllButtonPressed([bool pop = true]) async =>
|
||||
onGotoHistoryButtonsPressed(
|
||||
HistoryMove.backAll,
|
||||
pop: pop,
|
||||
);
|
||||
|
||||
Future<void> onStepForwardAllButtonPressed({bool pop = true}) async {
|
||||
Future<void> onStepForwardAllButtonPressed([bool pop = true]) async {
|
||||
onGotoHistoryButtonsPressed(
|
||||
gameInstance.position.stepForwardAll(),
|
||||
HistoryMove.forwardAll,
|
||||
pop: pop,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onTakeBackNButtonPressed(int n, {bool pop = true}) async {
|
||||
onGotoHistoryButtonsPressed(gameInstance.position.takeBackN(n), pop: pop);
|
||||
}
|
||||
Future<void> 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<GamePage>
|
|||
|
||||
debugPrint("[config] isPrivacyPolicyAccepted: $value");
|
||||
|
||||
Config.save();
|
||||
await Config.save();
|
||||
}
|
||||
|
||||
Future<void> onShowPrivacyDialog() async {
|
||||
|
@ -1259,10 +1264,10 @@ class _GamePageState extends State<GamePage>
|
|||
|
||||
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<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onTakeBackAllButtonPressed(pop: false),
|
||||
onPressed: () => onTakeBackAllButtonPressed(false),
|
||||
);
|
||||
|
||||
final takeBackButton = TextButton(
|
||||
|
@ -1707,7 +1712,7 @@ class _GamePageState extends State<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onTakeBackButtonPressed(pop: false),
|
||||
onPressed: () async => onTakeBackButtonPressed(false),
|
||||
);
|
||||
|
||||
final stepForwardButton = TextButton(
|
||||
|
@ -1720,7 +1725,7 @@ class _GamePageState extends State<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onStepForwardButtonPressed(pop: false),
|
||||
onPressed: () async => onStepForwardButtonPressed(false),
|
||||
);
|
||||
|
||||
final stepForwardAllButton = TextButton(
|
||||
|
@ -1733,7 +1738,7 @@ class _GamePageState extends State<GamePage>
|
|||
color: Color(Config.navigationToolbarIconColor),
|
||||
),
|
||||
),
|
||||
onPressed: () => onStepForwardAllButtonPressed(pop: false),
|
||||
onPressed: () async => onStepForwardAllButtonPressed(false),
|
||||
);
|
||||
|
||||
return GamePageToolBar(
|
||||
|
|
|
@ -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<void> 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<void> _playSound(int? soundId) async {
|
||||
if (Platform.isWindows) {
|
||||
return;
|
||||
static Future<void> _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<void> _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<void> disposePool() async {
|
||||
if (Platform.isWindows) {
|
||||
return;
|
||||
}
|
||||
static void disposePool() {
|
||||
assert(!Platform.isWindows);
|
||||
|
||||
_soundpool!.dispose();
|
||||
_soundpool.dispose();
|
||||
}
|
||||
|
||||
static Future<void> playTone(int? soundId) async {
|
||||
Chain.capture(() async {
|
||||
static Future<void> 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());
|
||||
|
|
|
@ -111,7 +111,7 @@ class _DrawerControllerState extends State<DrawerController>
|
|||
super.initState();
|
||||
}
|
||||
|
||||
Future<bool> getInitState() async {
|
||||
bool getInitState() {
|
||||
scrollController.jumpTo(
|
||||
widget.drawerWidth,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue