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 gameTypeName = state.GameTypeName;
|
||||||
var currentPlayerId = state.Participants.CurrentPlayerId;
|
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)
|
if (string.IsNullOrEmpty(gameTypeName) || !currentPlayerId.HasValue)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Cannot process throw: missing game type or player");
|
_logger.LogWarning("Cannot process throw: missing game type or player");
|
||||||
|
|
@ -407,6 +414,12 @@ public class GameEffects
|
||||||
isGameOver,
|
isGameOver,
|
||||||
winnerId));
|
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
|
// Debounce save operations to avoid excessive DB writes
|
||||||
_saveDebounceTimer?.Dispose();
|
_saveDebounceTimer?.Dispose();
|
||||||
_saveDebounceTimer = new Timer(
|
_saveDebounceTimer = new Timer(
|
||||||
|
|
@ -428,6 +441,13 @@ public class GameEffects
|
||||||
var gameTypeName = state.GameTypeName;
|
var gameTypeName = state.GameTypeName;
|
||||||
var currentPlayerId = state.Participants.CurrentPlayerId;
|
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)
|
if (string.IsNullOrEmpty(gameTypeName) || !currentPlayerId.HasValue || state.GameModel == null)
|
||||||
{
|
{
|
||||||
dispatcher.Dispatch(new ExecuteGameActionFailureAction("Cannot execute action: missing game state"));
|
dispatcher.Dispatch(new ExecuteGameActionFailureAction("Cannot execute action: missing game state"));
|
||||||
|
|
@ -451,6 +471,16 @@ public class GameEffects
|
||||||
result.IsGameOver,
|
result.IsGameOver,
|
||||||
result.WinnerId));
|
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
|
// Debounce save operations
|
||||||
_saveDebounceTimer?.Dispose();
|
_saveDebounceTimer?.Dispose();
|
||||||
_saveDebounceTimer = new Timer(
|
_saveDebounceTimer = new Timer(
|
||||||
|
|
@ -458,10 +488,6 @@ public class GameEffects
|
||||||
null,
|
null,
|
||||||
SaveDebounceMs,
|
SaveDebounceMs,
|
||||||
Timeout.Infinite);
|
Timeout.Infinite);
|
||||||
|
|
||||||
_logger.LogDebug(
|
|
||||||
"Game action executed: {ActionId}, rotate={Rotate}, gameOver={GameOver}",
|
|
||||||
action.ActionId, result.ShouldRotatePlayer, result.IsGameOver);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ public static class GameReducers
|
||||||
UndoStack = [],
|
UndoStack = [],
|
||||||
RedoStack = [],
|
RedoStack = [],
|
||||||
IsLoading = false,
|
IsLoading = false,
|
||||||
Error = null
|
Error = null,
|
||||||
|
IsGameOver = false,
|
||||||
|
WinnerId = null
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -84,7 +86,9 @@ public static class GameReducers
|
||||||
UndoStack = [],
|
UndoStack = [],
|
||||||
RedoStack = [],
|
RedoStack = [],
|
||||||
CompletedGames = [.. state.CompletedGames, action.Summary],
|
CompletedGames = [.. state.CompletedGames, action.Summary],
|
||||||
IsLoading = false
|
IsLoading = false,
|
||||||
|
IsGameOver = false,
|
||||||
|
WinnerId = null
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -235,25 +239,30 @@ public static class GameReducers
|
||||||
ThrowPanelAfter = action.NewThrowPanelState,
|
ThrowPanelAfter = action.NewThrowPanelState,
|
||||||
GameModel = action.UpdatedGameModel,
|
GameModel = action.UpdatedGameModel,
|
||||||
IsLoading = false,
|
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
|
// Apply player rotation or override (only if game not over)
|
||||||
if (action.NextPlayerId.HasValue)
|
if (!action.IsGameOver)
|
||||||
{
|
{
|
||||||
// Game logic specifies exact next player
|
if (action.NextPlayerId.HasValue)
|
||||||
newState = newState with
|
|
||||||
{
|
{
|
||||||
Participants = newState.Participants.SetCurrentPlayer(action.NextPlayerId.Value)
|
// Game logic specifies exact next player
|
||||||
};
|
newState = newState with
|
||||||
}
|
{
|
||||||
else if (action.ShouldRotatePlayer)
|
Participants = newState.Participants.SetCurrentPlayer(action.NextPlayerId.Value)
|
||||||
{
|
};
|
||||||
// Standard rotation to next player
|
}
|
||||||
newState = newState with
|
else if (action.ShouldRotatePlayer)
|
||||||
{
|
{
|
||||||
Participants = newState.Participants.NextPlayer()
|
// Standard rotation to next player
|
||||||
};
|
newState = newState with
|
||||||
|
{
|
||||||
|
Participants = newState.Participants.NextPlayer()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
|
|
@ -569,11 +578,13 @@ public static class GameReducers
|
||||||
{
|
{
|
||||||
GameModel = action.UpdatedGameModel,
|
GameModel = action.UpdatedGameModel,
|
||||||
IsLoading = false,
|
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
|
// Apply player rotation if requested (only if game not over)
|
||||||
if (action.ShouldRotatePlayer)
|
if (!action.IsGameOver && action.ShouldRotatePlayer)
|
||||||
{
|
{
|
||||||
newState = newState with
|
newState = newState with
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,16 @@ public record GameState
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsConcurrencyConflict { get; init; }
|
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>
|
/// <summary>
|
||||||
/// Private constructor for Fluxor initialization.
|
/// Private constructor for Fluxor initialization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -115,7 +125,9 @@ public record GameState
|
||||||
IsLoading = false,
|
IsLoading = false,
|
||||||
IsSaving = false,
|
IsSaving = false,
|
||||||
Error = null,
|
Error = null,
|
||||||
IsConcurrencyConflict = false
|
IsConcurrencyConflict = false,
|
||||||
|
IsGameOver = false,
|
||||||
|
WinnerId = null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue