Compare commits
2 Commits
65df43ed23
...
f1880f08a8
| Author | SHA1 | Date |
|---|---|---|
|
|
f1880f08a8 | |
|
|
c3beda405c |
|
|
@ -10,7 +10,8 @@
|
|||
"Bash(mkdir:*)",
|
||||
"WebSearch",
|
||||
"Bash(git merge:*)",
|
||||
"Bash(findstr:*)"
|
||||
"Bash(findstr:*)",
|
||||
"Bash(find:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ public record GameStateSerializationDto
|
|||
/// </summary>
|
||||
public JsonElement? GameModel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Game setup configuration. Persisted for game recovery.
|
||||
/// </summary>
|
||||
public JsonElement? Setup { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Stack of game snapshots for undo functionality.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Koogle.Domain.Enums;
|
||||
|
||||
namespace Koogle.Application.Games;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for game setup configuration.
|
||||
/// Supports JSON polymorphism for persistence.
|
||||
/// </summary>
|
||||
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
|
||||
[JsonDerivedType(typeof(Shit.ShitGameSetup), "Shit")]
|
||||
[JsonDerivedType(typeof(Training.TrainingGameSetup), "Training")]
|
||||
public interface IGameSetupModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Throw mode for the game (Reposition or Decrease).
|
||||
/// </summary>
|
||||
ThrowMode ThrowMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Throws allowed per round.
|
||||
/// </summary>
|
||||
int ThrowsPerRound { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Mode for participant rotation.
|
||||
/// </summary>
|
||||
ParticipantsMode ParticipantsMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Game type identifier.
|
||||
/// </summary>
|
||||
string GameType { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract base record implementing common setup properties.
|
||||
/// </summary>
|
||||
public abstract record GameSetupModelBase : IGameSetupModel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ThrowMode ThrowMode { get; init; } = ThrowMode.Reposition;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ThrowsPerRound { get; init; } = 3;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ParticipantsMode ParticipantsMode { get; init; } = ParticipantsMode.GameLogic;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string GameType { get; }
|
||||
}
|
||||
|
|
@ -230,33 +230,33 @@ public class ShitGameLogicService : IGameLogicService
|
|||
: GameSetupValidationResult.Valid();
|
||||
}
|
||||
|
||||
private static ShitSetupOptions ParseSetupOptions(object? setupOptions)
|
||||
private static ShitGameSetup ParseSetupOptions(object? setupOptions)
|
||||
{
|
||||
if (setupOptions is null)
|
||||
{
|
||||
return new ShitSetupOptions();
|
||||
return new ShitGameSetup();
|
||||
}
|
||||
|
||||
if (setupOptions is ShitSetupOptions typedOptions)
|
||||
if (setupOptions is ShitGameSetup typedSetup)
|
||||
{
|
||||
return typedOptions;
|
||||
return typedSetup;
|
||||
}
|
||||
|
||||
if (setupOptions is JsonElement jsonElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<ShitSetupOptions>(
|
||||
return JsonSerializer.Deserialize<ShitGameSetup>(
|
||||
jsonElement.GetRawText(),
|
||||
GameModelFactory.JsonSerializerOptions) ?? new ShitSetupOptions();
|
||||
GameModelFactory.JsonSerializerOptions) ?? new ShitGameSetup();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new ShitSetupOptions();
|
||||
return new ShitGameSetup();
|
||||
}
|
||||
}
|
||||
|
||||
return new ShitSetupOptions();
|
||||
return new ShitGameSetup();
|
||||
}
|
||||
|
||||
private static ShitGameModel CastModel(object gameModel)
|
||||
|
|
|
|||
|
|
@ -55,25 +55,3 @@ public record ShitGameModel
|
|||
/// </summary>
|
||||
public bool LastThrowWasGutter { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup options for Scheiss-Spiel (Shit Game).
|
||||
/// </summary>
|
||||
public record ShitSetupOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The "Scheiss-Zahl" (1-9, default 5). When a player knocks down exactly this many pins,
|
||||
/// they must take the collected points.
|
||||
/// </summary>
|
||||
public int ShitNumber { get; init; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Starting points for each player (10-1000, default 50).
|
||||
/// </summary>
|
||||
public int StartNumber { get; init; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Mode for player rotation.
|
||||
/// </summary>
|
||||
public ParticipantsMode ParticipantsMode { get; init; } = ParticipantsMode.GameLogic;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
using Koogle.Domain.Enums;
|
||||
|
||||
namespace Koogle.Application.Games.Shit;
|
||||
|
||||
/// <summary>
|
||||
/// Setup model for Scheiss-Spiel (Shit Game).
|
||||
/// </summary>
|
||||
public record ShitGameSetup : GameSetupModelBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string GameType => "Shit";
|
||||
|
||||
/// <summary>
|
||||
/// The "Scheiss-Zahl" (1-9, default 5).
|
||||
/// When a player knocks down exactly this many pins, they must take the collected points.
|
||||
/// </summary>
|
||||
public int ShitNumber { get; init; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Starting points for each player (10-1000, default 50).
|
||||
/// </summary>
|
||||
public int StartNumber { get; init; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a default ShitGameSetup with specified base parameters.
|
||||
/// </summary>
|
||||
public static ShitGameSetup Create(
|
||||
ThrowMode throwMode,
|
||||
int throwsPerRound,
|
||||
ParticipantsMode participantsMode,
|
||||
int shitNumber = 5,
|
||||
int startNumber = 50) => new()
|
||||
{
|
||||
ThrowMode = throwMode,
|
||||
ThrowsPerRound = throwsPerRound,
|
||||
ParticipantsMode = participantsMode,
|
||||
ShitNumber = shitNumber,
|
||||
StartNumber = startNumber
|
||||
};
|
||||
}
|
||||
|
|
@ -110,18 +110,18 @@ public class TrainingGameLogicService : IGameLogicService
|
|||
return GameSetupValidationResult.Valid();
|
||||
}
|
||||
|
||||
TrainingSetupOptions options;
|
||||
if (setupOptions is TrainingSetupOptions typedOptions)
|
||||
TrainingGameSetup? setup;
|
||||
if (setupOptions is TrainingGameSetup typedSetup)
|
||||
{
|
||||
options = typedOptions;
|
||||
setup = typedSetup;
|
||||
}
|
||||
else if (setupOptions is JsonElement jsonElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
options = JsonSerializer.Deserialize<TrainingSetupOptions>(
|
||||
setup = JsonSerializer.Deserialize<TrainingGameSetup>(
|
||||
jsonElement.GetRawText(),
|
||||
GameModelFactory.JsonSerializerOptions)!;
|
||||
GameModelFactory.JsonSerializerOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -133,9 +133,14 @@ public class TrainingGameLogicService : IGameLogicService
|
|||
return GameSetupValidationResult.Invalid("Ungültige Setup-Optionen.");
|
||||
}
|
||||
|
||||
if (setup is null)
|
||||
{
|
||||
return GameSetupValidationResult.Valid();
|
||||
}
|
||||
|
||||
var errors = new List<string>();
|
||||
|
||||
if (options.ThrowsPerRound < 1 || options.ThrowsPerRound > 5)
|
||||
if (setup.ThrowsPerRound < 1 || setup.ThrowsPerRound > 5)
|
||||
{
|
||||
errors.Add("Würfe pro Runde muss zwischen 1 und 5 liegen.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
using Koogle.Domain.Enums;
|
||||
|
||||
namespace Koogle.Application.Games.Training;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -48,24 +46,3 @@ public record TrainingPlayerStats
|
|||
/// </summary>
|
||||
public double Average => ThrowCount > 0 ? (double)PinCount / ThrowCount : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup options for Training game.
|
||||
/// </summary>
|
||||
public record TrainingSetupOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Throw mode: Reposition (in die Vollen) or Decrease (Abräumen).
|
||||
/// </summary>
|
||||
public ThrowMode ThrowMode { get; init; } = ThrowMode.Reposition;
|
||||
|
||||
/// <summary>
|
||||
/// Number of throws per round (1-5, default 3).
|
||||
/// </summary>
|
||||
public int ThrowsPerRound { get; init; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Mode for player rotation.
|
||||
/// </summary>
|
||||
public ParticipantsMode ParticipantsMode { get; init; } = ParticipantsMode.GameLogic;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
using Koogle.Domain.Enums;
|
||||
|
||||
namespace Koogle.Application.Games.Training;
|
||||
|
||||
/// <summary>
|
||||
/// Setup model for Training game.
|
||||
/// </summary>
|
||||
public record TrainingGameSetup : GameSetupModelBase
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string GameType => "Training";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a default TrainingGameSetup with specified base parameters.
|
||||
/// </summary>
|
||||
public static TrainingGameSetup Create(
|
||||
ThrowMode throwMode,
|
||||
int throwsPerRound,
|
||||
ParticipantsMode participantsMode) => new()
|
||||
{
|
||||
ThrowMode = throwMode,
|
||||
ThrowsPerRound = throwsPerRound,
|
||||
ParticipantsMode = participantsMode
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
@using Fluxor
|
||||
@using Koogle.Application.DTOs
|
||||
@using Koogle.Application.Games
|
||||
@using Koogle.Application.Games.Shit
|
||||
@using Koogle.Application.Games.Training
|
||||
@using Koogle.Domain.Enums
|
||||
@using Koogle.Domain.Interfaces
|
||||
@using Koogle.Web.Store.DayState
|
||||
|
|
@ -201,9 +203,13 @@
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Create initial game model
|
||||
// Create initial game model and setup
|
||||
var playerIds = _selectedParticipantIds.ToArray();
|
||||
var initialModel = logicService.CreateInitialModel(playerIds, _gameSpecificSetupOptions);
|
||||
|
||||
// Create typed setup model based on game type
|
||||
IGameSetupModel? setup = CreateSetupModel(_selectedDefinition.Name);
|
||||
|
||||
var initialModel = logicService.CreateInitialModel(playerIds, setup ?? _gameSpecificSetupOptions);
|
||||
|
||||
// Dispatch start game action
|
||||
var action = new StartGameAction(
|
||||
|
|
@ -213,7 +219,8 @@
|
|||
ThrowMode: _throwMode,
|
||||
ThrowsPerRound: _throwsPerRound,
|
||||
ParticipantsMode: _participantsMode,
|
||||
InitialGameModel: initialModel);
|
||||
InitialGameModel: initialModel,
|
||||
Setup: setup);
|
||||
|
||||
Dispatcher.Dispatch(action);
|
||||
|
||||
|
|
@ -243,4 +250,42 @@
|
|||
ThrowMode.Decrease => "Abräumen",
|
||||
_ => mode.ToString()
|
||||
};
|
||||
|
||||
private IGameSetupModel? CreateSetupModel(string gameTypeName)
|
||||
{
|
||||
return gameTypeName switch
|
||||
{
|
||||
"Shit" => CreateShitSetup(),
|
||||
"Training" => CreateTrainingSetup(),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private ShitGameSetup CreateShitSetup()
|
||||
{
|
||||
// Extract game-specific options if available
|
||||
var shitNumber = 5;
|
||||
var startNumber = 50;
|
||||
|
||||
if (_gameSpecificSetupOptions is ShitGameSetup existingSetup)
|
||||
{
|
||||
shitNumber = existingSetup.ShitNumber;
|
||||
startNumber = existingSetup.StartNumber;
|
||||
}
|
||||
|
||||
return ShitGameSetup.Create(
|
||||
throwMode: _throwMode,
|
||||
throwsPerRound: _throwsPerRound,
|
||||
participantsMode: _participantsMode,
|
||||
shitNumber: shitNumber,
|
||||
startNumber: startNumber);
|
||||
}
|
||||
|
||||
private TrainingGameSetup CreateTrainingSetup()
|
||||
{
|
||||
return TrainingGameSetup.Create(
|
||||
throwMode: _throwMode,
|
||||
throwsPerRound: _throwsPerRound,
|
||||
participantsMode: _participantsMode);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,13 +80,13 @@
|
|||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (InitialOptions is ShitSetupOptions options)
|
||||
if (InitialOptions is ShitGameSetup setup)
|
||||
{
|
||||
_options = new ShitSetupOptionsInternal
|
||||
{
|
||||
ShitNumber = options.ShitNumber,
|
||||
StartNumber = options.StartNumber,
|
||||
ParticipantsMode = options.ParticipantsMode
|
||||
ShitNumber = setup.ShitNumber,
|
||||
StartNumber = setup.StartNumber,
|
||||
ParticipantsMode = setup.ParticipantsMode
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -104,19 +104,19 @@
|
|||
|
||||
private async Task NotifyOptionsChanged()
|
||||
{
|
||||
var options = new ShitSetupOptions
|
||||
var setup = new ShitGameSetup
|
||||
{
|
||||
ShitNumber = _options.ShitNumber,
|
||||
StartNumber = _options.StartNumber,
|
||||
ParticipantsMode = _options.ParticipantsMode
|
||||
};
|
||||
await OnOptionsChanged.InvokeAsync(options);
|
||||
await OnOptionsChanged.InvokeAsync(setup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current setup options.
|
||||
/// Gets the current setup.
|
||||
/// </summary>
|
||||
public ShitSetupOptions GetOptions() => new()
|
||||
public ShitGameSetup GetSetup() => new()
|
||||
{
|
||||
ShitNumber = _options.ShitNumber,
|
||||
StartNumber = _options.StartNumber,
|
||||
|
|
|
|||
|
|
@ -58,13 +58,13 @@
|
|||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (InitialOptions is TrainingSetupOptions options)
|
||||
if (InitialOptions is TrainingGameSetup setup)
|
||||
{
|
||||
_options = new TrainingSetupOptionsInternal
|
||||
{
|
||||
ThrowMode = options.ThrowMode,
|
||||
ThrowsPerRound = options.ThrowsPerRound,
|
||||
ParticipantsMode = options.ParticipantsMode
|
||||
ThrowMode = setup.ThrowMode,
|
||||
ThrowsPerRound = setup.ThrowsPerRound,
|
||||
ParticipantsMode = setup.ParticipantsMode
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -79,19 +79,19 @@
|
|||
|
||||
private async Task NotifyOptionsChanged()
|
||||
{
|
||||
var options = new TrainingSetupOptions
|
||||
var setup = new TrainingGameSetup
|
||||
{
|
||||
ThrowMode = _options.ThrowMode,
|
||||
ThrowsPerRound = _options.ThrowsPerRound,
|
||||
ParticipantsMode = _options.ParticipantsMode
|
||||
};
|
||||
await OnOptionsChanged.InvokeAsync(options);
|
||||
await OnOptionsChanged.InvokeAsync(setup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current setup options.
|
||||
/// Gets the current setup.
|
||||
/// </summary>
|
||||
public TrainingSetupOptions GetOptions() => new()
|
||||
public TrainingGameSetup GetSetup() => new()
|
||||
{
|
||||
ThrowMode = _options.ThrowMode,
|
||||
ThrowsPerRound = _options.ThrowsPerRound,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Koogle.Application.Games;
|
||||
using Koogle.Domain.Enums;
|
||||
|
||||
namespace Koogle.Web.Store.GameState;
|
||||
|
|
@ -14,7 +15,8 @@ public record StartGameAction(
|
|||
ThrowMode ThrowMode,
|
||||
int ThrowsPerRound,
|
||||
ParticipantsMode ParticipantsMode,
|
||||
object? InitialGameModel);
|
||||
object? InitialGameModel,
|
||||
IGameSetupModel? Setup);
|
||||
|
||||
/// <summary>
|
||||
/// Action dispatched when game is started successfully.
|
||||
|
|
@ -24,7 +26,8 @@ public record StartGameSuccessAction(
|
|||
string GameTypeName,
|
||||
ThrowPanelState ThrowPanel,
|
||||
ParticipantsState Participants,
|
||||
object? GameModel);
|
||||
object? GameModel,
|
||||
IGameSetupModel? Setup);
|
||||
|
||||
/// <summary>
|
||||
/// Action dispatched when starting game fails.
|
||||
|
|
@ -60,7 +63,8 @@ public record LoadActiveGameSuccessAction(
|
|||
ThrowPanelState ThrowPanel,
|
||||
ParticipantsState Participants,
|
||||
object? GameModel,
|
||||
IReadOnlyList<GameSnapshot> UndoStack);
|
||||
IReadOnlyList<GameSnapshot> UndoStack,
|
||||
IGameSetupModel? Setup);
|
||||
|
||||
/// <summary>
|
||||
/// Action dispatched when no active game exists.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Text.Json;
|
||||
using Fluxor;
|
||||
using Koogle.Application.DTOs;
|
||||
using Koogle.Application.Games;
|
||||
using Koogle.Application.Interfaces;
|
||||
using Koogle.Domain.Enums;
|
||||
|
||||
|
|
@ -61,6 +62,9 @@ public class GameEffects
|
|||
GameModel = action.InitialGameModel != null
|
||||
? JsonSerializer.SerializeToElement(action.InitialGameModel, GameStateSerializationDto.JsonOptions)
|
||||
: null,
|
||||
Setup = action.Setup != null
|
||||
? JsonSerializer.SerializeToElement(action.Setup, GameStateSerializationDto.JsonOptions)
|
||||
: null,
|
||||
UndoStack = []
|
||||
};
|
||||
|
||||
|
|
@ -94,7 +98,8 @@ public class GameEffects
|
|||
action.GameTypeName,
|
||||
throwPanel,
|
||||
participants,
|
||||
action.InitialGameModel));
|
||||
action.InitialGameModel,
|
||||
action.Setup));
|
||||
|
||||
_logger.LogInformation("Game started: {GameId}, Type: {GameType}", gameId, action.GameTypeName);
|
||||
}
|
||||
|
|
@ -183,13 +188,23 @@ public class GameEffects
|
|||
})
|
||||
.ToList();
|
||||
|
||||
// Deserialize setup if present (may be null for legacy games)
|
||||
IGameSetupModel? setup = null;
|
||||
if (stateDto.Setup.HasValue)
|
||||
{
|
||||
setup = JsonSerializer.Deserialize<IGameSetupModel>(
|
||||
stateDto.Setup.Value.GetRawText(),
|
||||
GameStateSerializationDto.JsonOptions);
|
||||
}
|
||||
|
||||
dispatcher.Dispatch(new LoadActiveGameSuccessAction(
|
||||
activeGame.Id,
|
||||
activeGame.GameType,
|
||||
throwPanel,
|
||||
participants,
|
||||
stateDto.GameModel.HasValue ? (object?)stateDto.GameModel.Value : null,
|
||||
undoStack));
|
||||
undoStack,
|
||||
setup));
|
||||
|
||||
_logger.LogInformation("Active game loaded: {GameId}, Type: {GameType}", activeGame.Id, activeGame.GameType);
|
||||
}
|
||||
|
|
@ -353,6 +368,9 @@ public class GameEffects
|
|||
GameModel = state.GameModel != null
|
||||
? JsonSerializer.SerializeToElement(state.GameModel, GameStateSerializationDto.JsonOptions)
|
||||
: null,
|
||||
Setup = state.Setup != null
|
||||
? JsonSerializer.SerializeToElement(state.Setup, GameStateSerializationDto.JsonOptions)
|
||||
: null,
|
||||
UndoStack = state.UndoStack.Select(s => new GameSnapshotDto
|
||||
{
|
||||
ThrowPanel = MapThrowPanelToDto(s.ThrowPanel),
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public static class GameReducers
|
|||
ThrowPanel = action.ThrowPanel,
|
||||
Participants = action.Participants,
|
||||
GameModel = action.GameModel,
|
||||
Setup = action.Setup,
|
||||
UndoStack = [],
|
||||
IsLoading = false,
|
||||
Error = null
|
||||
|
|
@ -75,6 +76,7 @@ public static class GameReducers
|
|||
ThrowPanel = ThrowPanelState.Initial,
|
||||
Participants = ParticipantsState.Initial,
|
||||
GameModel = null,
|
||||
Setup = null,
|
||||
UndoStack = [],
|
||||
CompletedGames = [.. state.CompletedGames, action.Summary],
|
||||
IsLoading = false
|
||||
|
|
@ -116,6 +118,7 @@ public static class GameReducers
|
|||
ThrowPanel = action.ThrowPanel,
|
||||
Participants = action.Participants,
|
||||
GameModel = action.GameModel,
|
||||
Setup = action.Setup,
|
||||
UndoStack = action.UndoStack.ToImmutableList(),
|
||||
IsLoading = false
|
||||
};
|
||||
|
|
@ -387,6 +390,7 @@ public static class GameReducers
|
|||
ThrowPanel = ThrowPanelState.Initial,
|
||||
Participants = ParticipantsState.Initial,
|
||||
GameModel = null,
|
||||
Setup = null,
|
||||
UndoStack = [],
|
||||
CompletedGames = [.. state.CompletedGames, action.Summary]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Immutable;
|
||||
using Fluxor;
|
||||
using Koogle.Application.Games;
|
||||
using Koogle.Domain.Enums;
|
||||
|
||||
namespace Koogle.Web.Store.GameState;
|
||||
|
|
@ -45,6 +46,11 @@ public record GameState
|
|||
/// </summary>
|
||||
public object? GameModel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Game setup configuration (persisted for recovery).
|
||||
/// </summary>
|
||||
public IGameSetupModel? Setup { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Stack of game snapshots for undo functionality (unlimited).
|
||||
/// </summary>
|
||||
|
|
@ -92,6 +98,7 @@ public record GameState
|
|||
ThrowPanel = ThrowPanelState.Initial,
|
||||
Participants = ParticipantsState.Initial,
|
||||
GameModel = null,
|
||||
Setup = null,
|
||||
UndoStack = [],
|
||||
CompletedGames = [],
|
||||
IsLoading = false,
|
||||
|
|
|
|||
Loading…
Reference in New Issue