expense handling from gamelogic:

Problem: HandleRecordThrow ignorierte throwResult.Triggers aus ProcessThrow - speziell die ExpensePoint-Trigger vom Scheißspiel für Verlierer.

  Lösung:

  1. HandleRecordThrow (Zeile 358, 370, 427-436):
    - Neue Variable gameLogicTriggers
    - Zuweisung aus throwResult.Triggers
    - Aufruf von FireGameLogicTriggersAsync
  2. HandleExecuteGameAction (Zeile 457, 497-506):
    - Auf async geändert
    - Verarbeitet result.Triggers für Game-Actions wie "Passen"
  3. Neue Methode FireGameLogicTriggersAsync (Zeile 985-1058):
    - Parst TriggerType als ExpenseTriggerType
    - Ruft IGameEventService.RegisterExpensePointsAsync für ExpensePoint-Trigger (mit Multiplier)
    - Ruft IGameEventService.RegisterEliminatedAsync für Eliminated-Trigger
    - Dispatcht TriggerExpensesCreatedAction für UI-Update
This commit is contained in:
beo3000 2025-12-28 17:41:50 +01:00
parent b8bd1b0939
commit 83b0e6a91b
1 changed files with 103 additions and 6 deletions

View File

@ -355,6 +355,7 @@ public class GameEffects
bool isGameOver = false;
Guid? winnerId = null;
object? updatedGameModel = state.GameModel;
IReadOnlyList<TriggerEvent> gameLogicTriggers = [];
// Call ProcessThrow if game logic service is available
if (gameLogicService != null && state.GameModel != null)
@ -366,6 +367,7 @@ public class GameEffects
shouldRotatePlayer = throwResult.ShouldRotatePlayer;
isGameOver = throwResult.IsGameOver;
winnerId = throwResult.WinnerId;
gameLogicTriggers = throwResult.Triggers;
// Check for overrides from game logic
if (throwResult.Overrides != null)
@ -422,6 +424,17 @@ public class GameEffects
// Fire expense triggers for special throw events
await FireThrowTriggersAsync(state, currentPlayerId.Value, action, afterThrowState, dispatcher);
// Fire game-specific triggers (e.g., ExpensePoint from Scheiss-Spiel)
if (gameLogicTriggers.Count > 0 && state.DayId.HasValue && state.ActiveGameId.HasValue)
{
await FireGameLogicTriggersAsync(
state.DayId.Value,
state.ActiveGameId.Value,
state.Participants.PlayerIds.ToList(),
gameLogicTriggers,
dispatcher);
}
// If game is over, skip save (user must confirm end via UI)
if (isGameOver)
{
@ -441,7 +454,7 @@ public class GameEffects
/// Handles ExecuteGameActionAction - executes game-specific action via IGameLogicService.
/// </summary>
[EffectMethod]
public Task HandleExecuteGameAction(ExecuteGameActionAction action, IDispatcher dispatcher)
public async Task HandleExecuteGameAction(ExecuteGameActionAction action, IDispatcher dispatcher)
{
var state = _gameState.Value;
var gameTypeName = state.GameTypeName;
@ -451,13 +464,13 @@ public class GameEffects
if (state.IsGameOver)
{
dispatcher.Dispatch(new ExecuteGameActionFailureAction("Cannot execute action: game is already over"));
return Task.CompletedTask;
return;
}
if (string.IsNullOrEmpty(gameTypeName) || !currentPlayerId.HasValue || state.GameModel == null)
{
dispatcher.Dispatch(new ExecuteGameActionFailureAction("Cannot execute action: missing game state"));
return Task.CompletedTask;
return;
}
try
@ -481,10 +494,21 @@ public class GameEffects
"Game action executed: {ActionId}, rotate={Rotate}, gameOver={GameOver}",
action.ActionId, result.ShouldRotatePlayer, result.IsGameOver);
// Fire game-specific triggers from action result
if (result.Triggers.Count > 0 && state.DayId.HasValue && state.ActiveGameId.HasValue)
{
await FireGameLogicTriggersAsync(
state.DayId.Value,
state.ActiveGameId.Value,
state.Participants.PlayerIds.ToList(),
result.Triggers,
dispatcher);
}
// If game is over, skip save (user must confirm end via UI)
if (result.IsGameOver)
{
return Task.CompletedTask;
return;
}
// Debounce save operations
@ -505,8 +529,6 @@ public class GameEffects
_logger.LogError(ex, "Error executing game action: {ActionId}", action.ActionId);
dispatcher.Dispatch(new ExecuteGameActionFailureAction($"Error: {ex.Message}"));
}
return Task.CompletedTask;
}
private static ThrowPanelSnapshot CreateThrowPanelSnapshot(ThrowPanelState state)
@ -968,4 +990,79 @@ public class GameEffects
// Don't fail the throw recording if trigger fails
}
}
/// <summary>
/// Fires game-specific triggers returned by IGameLogicService.ProcessThrow.
/// These are separate from standard throw triggers (Gutter, Strike, etc.).
/// </summary>
private async Task FireGameLogicTriggersAsync(
Guid dayId,
Guid gameId,
List<Guid> allParticipantIds,
IReadOnlyList<TriggerEvent> triggers,
IDispatcher dispatcher)
{
var allCreatedExpenses = new List<PersonExpenseDto>();
try
{
foreach (var trigger in triggers)
{
// Parse trigger type
if (!Enum.TryParse<ExpenseTriggerType>(trigger.TriggerType, out var triggerType))
{
_logger.LogWarning("Unknown trigger type: {TriggerType}", trigger.TriggerType);
continue;
}
List<PersonExpenseDto> expenses = [];
switch (triggerType)
{
case ExpenseTriggerType.ExpensePoint:
expenses = await _gameEventService.RegisterExpensePointsAsync(
trigger.PersonId,
dayId,
gameId,
allParticipantIds,
trigger.Multiplier);
break;
case ExpenseTriggerType.Eliminated:
expenses = await _gameEventService.RegisterEliminatedAsync(
trigger.PersonId,
dayId,
gameId,
allParticipantIds);
break;
default:
_logger.LogDebug(
"Trigger type {TriggerType} not handled in game logic triggers",
triggerType);
break;
}
allCreatedExpenses.AddRange(expenses);
if (expenses.Count > 0)
{
_logger.LogInformation(
"Game logic trigger fired: Type={TriggerType}, Person={PersonId}, Multiplier={Multiplier}, Expenses={Count}",
triggerType, trigger.PersonId, trigger.Multiplier, expenses.Count);
}
}
// Dispatch action to update UI with all created expenses
if (allCreatedExpenses.Count > 0)
{
dispatcher.Dispatch(new DayState.TriggerExpensesCreatedAction(allCreatedExpenses));
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error firing game logic triggers");
// Don't fail the throw recording if trigger fails
}
}
}