fix endOfGame:
Zusammenfassung der Änderungen:
1. GameState.cs - Neue Properties hinzugefügt:
- IsGameOver - zeigt an, dass das Spiel beendet ist
- WinnerId - ID des Gewinners
2. GameReducers.cs:
- OnProcessThrowResult setzt jetzt IsGameOver und WinnerId
- OnExecuteGameActionSuccess setzt jetzt IsGameOver und WinnerId
- OnStartGameSuccess setzt IsGameOver=false
- OnEndGameSuccess setzt IsGameOver=false
3. GameEffects.cs:
- HandleRecordThrow blockiert Eingaben wenn IsGameOver=true
- HandleExecuteGameAction blockiert Eingaben wenn IsGameOver=true
- EndGameAction wird nicht automatisch dispatcht - der Benutzer muss das Spiel explizit über die UI beenden
Verhalten jetzt:
- Wenn ProcessThrow oder ExecuteAction IsGameOver=true zurückgibt, wird der State auf IsGameOver=true gesetzt
- Die Tafel bleibt sichtbar mit dem Endergebnis
- Weitere Würfe/Actions werden blockiert
- Der Benutzer muss EndGameAction explizit über einen Button in der UI auslösen
Die UI muss jetzt GameState.IsGameOver und GameState.WinnerId nutzen, um:
1. Eingaben zu deaktivieren
2. Eine "Spiel beenden"-Schaltfläche anzuzeigen
This commit is contained in:
parent
5c088345b3
commit
dbb59ed54f
|
|
@ -303,6 +303,13 @@ public class GameEffects
|
|||
var gameTypeName = state.GameTypeName;
|
||||
var currentPlayerId = state.Participants.CurrentPlayerId;
|
||||
|
||||
// Block input if game is already over
|
||||
if (state.IsGameOver)
|
||||
{
|
||||
_logger.LogWarning("Cannot process throw: game is already over");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(gameTypeName) || !currentPlayerId.HasValue)
|
||||
{
|
||||
_logger.LogWarning("Cannot process throw: missing game type or player");
|
||||
|
|
@ -407,6 +414,12 @@ public class GameEffects
|
|||
isGameOver,
|
||||
winnerId));
|
||||
|
||||
// If game is over, skip save (user must confirm end via UI)
|
||||
if (isGameOver)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Debounce save operations to avoid excessive DB writes
|
||||
_saveDebounceTimer?.Dispose();
|
||||
_saveDebounceTimer = new Timer(
|
||||
|
|
@ -428,6 +441,13 @@ public class GameEffects
|
|||
var gameTypeName = state.GameTypeName;
|
||||
var currentPlayerId = state.Participants.CurrentPlayerId;
|
||||
|
||||
// Block input if game is already over
|
||||
if (state.IsGameOver)
|
||||
{
|
||||
dispatcher.Dispatch(new ExecuteGameActionFailureAction("Cannot execute action: game is already over"));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(gameTypeName) || !currentPlayerId.HasValue || state.GameModel == null)
|
||||
{
|
||||
dispatcher.Dispatch(new ExecuteGameActionFailureAction("Cannot execute action: missing game state"));
|
||||
|
|
@ -451,6 +471,16 @@ public class GameEffects
|
|||
result.IsGameOver,
|
||||
result.WinnerId));
|
||||
|
||||
_logger.LogDebug(
|
||||
"Game action executed: {ActionId}, rotate={Rotate}, gameOver={GameOver}",
|
||||
action.ActionId, result.ShouldRotatePlayer, result.IsGameOver);
|
||||
|
||||
// If game is over, skip save (user must confirm end via UI)
|
||||
if (result.IsGameOver)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Debounce save operations
|
||||
_saveDebounceTimer?.Dispose();
|
||||
_saveDebounceTimer = new Timer(
|
||||
|
|
@ -458,10 +488,6 @@ public class GameEffects
|
|||
null,
|
||||
SaveDebounceMs,
|
||||
Timeout.Infinite);
|
||||
|
||||
_logger.LogDebug(
|
||||
"Game action executed: {ActionId}, rotate={Rotate}, gameOver={GameOver}",
|
||||
action.ActionId, result.ShouldRotatePlayer, result.IsGameOver);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ public static class GameReducers
|
|||
UndoStack = [],
|
||||
RedoStack = [],
|
||||
IsLoading = false,
|
||||
Error = null
|
||||
Error = null,
|
||||
IsGameOver = false,
|
||||
WinnerId = null
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -84,7 +86,9 @@ public static class GameReducers
|
|||
UndoStack = [],
|
||||
RedoStack = [],
|
||||
CompletedGames = [.. state.CompletedGames, action.Summary],
|
||||
IsLoading = false
|
||||
IsLoading = false,
|
||||
IsGameOver = false,
|
||||
WinnerId = null
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -235,25 +239,30 @@ public static class GameReducers
|
|||
ThrowPanelAfter = action.NewThrowPanelState,
|
||||
GameModel = action.UpdatedGameModel,
|
||||
IsLoading = false,
|
||||
IsSaving = true
|
||||
IsSaving = !action.IsGameOver, // Don't save if game is over (EndGame handles it)
|
||||
IsGameOver = action.IsGameOver,
|
||||
WinnerId = action.WinnerId
|
||||
};
|
||||
|
||||
// Apply player rotation or override
|
||||
if (action.NextPlayerId.HasValue)
|
||||
// Apply player rotation or override (only if game not over)
|
||||
if (!action.IsGameOver)
|
||||
{
|
||||
// Game logic specifies exact next player
|
||||
newState = newState with
|
||||
if (action.NextPlayerId.HasValue)
|
||||
{
|
||||
Participants = newState.Participants.SetCurrentPlayer(action.NextPlayerId.Value)
|
||||
};
|
||||
}
|
||||
else if (action.ShouldRotatePlayer)
|
||||
{
|
||||
// Standard rotation to next player
|
||||
newState = newState with
|
||||
// Game logic specifies exact next player
|
||||
newState = newState with
|
||||
{
|
||||
Participants = newState.Participants.SetCurrentPlayer(action.NextPlayerId.Value)
|
||||
};
|
||||
}
|
||||
else if (action.ShouldRotatePlayer)
|
||||
{
|
||||
Participants = newState.Participants.NextPlayer()
|
||||
};
|
||||
// Standard rotation to next player
|
||||
newState = newState with
|
||||
{
|
||||
Participants = newState.Participants.NextPlayer()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return newState;
|
||||
|
|
@ -569,11 +578,13 @@ public static class GameReducers
|
|||
{
|
||||
GameModel = action.UpdatedGameModel,
|
||||
IsLoading = false,
|
||||
IsSaving = true
|
||||
IsSaving = !action.IsGameOver, // Don't save if game is over (EndGame handles it)
|
||||
IsGameOver = action.IsGameOver,
|
||||
WinnerId = action.WinnerId
|
||||
};
|
||||
|
||||
// Apply player rotation if requested
|
||||
if (action.ShouldRotatePlayer)
|
||||
// Apply player rotation if requested (only if game not over)
|
||||
if (!action.IsGameOver && action.ShouldRotatePlayer)
|
||||
{
|
||||
newState = newState with
|
||||
{
|
||||
|
|
|
|||
|
|
@ -91,6 +91,16 @@ public record GameState
|
|||
/// </summary>
|
||||
public bool IsConcurrencyConflict { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the game has ended (show results, block input).
|
||||
/// </summary>
|
||||
public bool IsGameOver { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Winner ID if game is over.
|
||||
/// </summary>
|
||||
public Guid? WinnerId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor for Fluxor initialization.
|
||||
/// </summary>
|
||||
|
|
@ -115,7 +125,9 @@ public record GameState
|
|||
IsLoading = false,
|
||||
IsSaving = false,
|
||||
Error = null,
|
||||
IsConcurrencyConflict = false
|
||||
IsConcurrencyConflict = false,
|
||||
IsGameOver = false,
|
||||
WinnerId = null
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue