fox hunt part1
This commit is contained in:
parent
9356dab062
commit
29fa9c5e84
|
|
@ -0,0 +1,32 @@
|
|||
using Koogle.Application.Games.Shit;
|
||||
using Koogle.Domain.Interfaces;
|
||||
|
||||
namespace Koogle.Application.Games.FoxHunt
|
||||
{
|
||||
/// <summary>
|
||||
/// Game definition for Fuchsjagd-Spiel (Fox Hunt) game type.
|
||||
/// One Player starts as fox, and all others try to hunt him.
|
||||
/// </summary>
|
||||
public class FoxHuntGameDefinition : IGameDefinition
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name => "FoxHunt";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string DisplayName => "Fuchsjagd";
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type SetupComponentType => Type.GetType(
|
||||
"Koogle.Web.Components.Game.FoxHunt.FoxSetup, Koogle.Web")!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type BoardComponentType => Type.GetType(
|
||||
"Koogle.Web.Components.Game.FoxHunt.FoxBoard, Koogle.Web")!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type GameLogicServiceType => typeof(FoxHuntGameLogicService);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type GameModelType => typeof(FoxHuntGameModel);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
using Koogle.Application.Games.DeathBox;
|
||||
using Koogle.Application.Interfaces;
|
||||
using Koogle.Domain.Enums;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Org.BouncyCastle.Cms;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Koogle.Application.Games.FoxHunt
|
||||
{
|
||||
/// <summary>
|
||||
/// Game logic service for Fuchsjagd-Spiel (Fox Hunt) game type.
|
||||
/// One Player starts as fox, and all others try to hunt him.
|
||||
/// </summary>
|
||||
public class FoxHuntGameLogicService : IGameLogicService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
public FoxHuntGameLogicService(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public object CreateInitialModel(Guid[] playerIds, object? setupOptions)
|
||||
{
|
||||
var options = ParseSetupOptions(setupOptions);
|
||||
|
||||
var playerOrder = playerIds.ToArray();
|
||||
|
||||
var playerStates = playerIds.ToDictionary(
|
||||
id => id,
|
||||
_ => new FoxHuntPlayerState());
|
||||
|
||||
return new FoxHuntGameModel
|
||||
{
|
||||
FoxIndex = 0, // aktueller Fuchs (Index in PlayerOrder)
|
||||
NonFoxIndex = 0, // Index für Nicht-Fuchs-Spieler
|
||||
FoxTurnsRemaining = 2, // erste 2 Fuchs-Züge
|
||||
FoxTurn = false,
|
||||
|
||||
LeadingThrows = options.LeadingThrows,
|
||||
PlayerStates = playerStates,
|
||||
PlayerOrder = playerOrder,
|
||||
//CurrentPlayerIndex = 0,
|
||||
//FoxLeadingThrowCount = 0,
|
||||
//CurrentFoxIndex = 0,
|
||||
WinnerId = null,
|
||||
IsGameOver = false,
|
||||
};
|
||||
}
|
||||
|
||||
public (object UpdatedModel, ThrowResult Result) ProcessThrow(object gameModel, AfterThrowState afterThrow)
|
||||
{
|
||||
var model = CastModel(gameModel);
|
||||
var playerId = afterThrow.CurrentPlayerId;
|
||||
var pinsKnocked = afterThrow.PinsKnocked;
|
||||
|
||||
if (model.IsGameOver)
|
||||
{
|
||||
return (model, new ThrowResult
|
||||
{
|
||||
PointsScored = 0,
|
||||
ShouldRotatePlayer = false,
|
||||
IsGameOver = true,
|
||||
WinnerId = model.WinnerId
|
||||
});
|
||||
}
|
||||
|
||||
//var actualName = GetPlayerName(playerId).Result;
|
||||
var playerStates = new Dictionary<Guid, FoxHuntPlayerState>(model.PlayerStates);
|
||||
//var eliminatedPlayers = new List<Guid>(model.EliminatedPlayers);
|
||||
var triggers = new List<TriggerEvent>();
|
||||
|
||||
var foxId = model.PlayerOrder[model.FoxIndex];
|
||||
var isFoxThrow = playerId == foxId;
|
||||
|
||||
|
||||
// 5. Check GAME END
|
||||
bool isGameOver = false;
|
||||
Guid? winnerId = null;
|
||||
|
||||
|
||||
var nextPlayerId = GetNextId(model);
|
||||
//var nextName = GetPlayerName(nextPlayerId).Result;
|
||||
|
||||
// Update model
|
||||
model = model with
|
||||
{
|
||||
FoxIndex = model.FoxIndex,
|
||||
FoxTurn = model.FoxTurn,
|
||||
FoxTurnsRemaining = model.FoxTurnsRemaining,
|
||||
NonFoxIndex = model.NonFoxIndex
|
||||
};
|
||||
|
||||
var result = new ThrowResult
|
||||
{
|
||||
PointsScored = pinsKnocked,
|
||||
ShouldRotatePlayer = true,
|
||||
IsGameOver = isGameOver,
|
||||
WinnerId = winnerId,
|
||||
Triggers = triggers,
|
||||
Overrides = isGameOver ? null : new GameLogicOverrides
|
||||
{
|
||||
NextPlayerId = nextPlayerId
|
||||
}
|
||||
};
|
||||
|
||||
return (model, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// algorithm:
|
||||
/// A, A, B, A, C, A, D,
|
||||
/// B, B, A, B, C, B, D,
|
||||
/// C, C, A, C, B, C, D,
|
||||
/// D, D, A, D, B, D, C
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
private Guid GetNextId(FoxHuntGameModel model)
|
||||
{
|
||||
// Abbruch: alle waren einmal Fuchs
|
||||
if (model.FoxIndex >= model.PlayerOrder.Length)
|
||||
throw new InvalidOperationException("Alle Spieler waren bereits Fuchs.");
|
||||
|
||||
Guid foxId = model.PlayerOrder[model.FoxIndex];
|
||||
|
||||
// 1️⃣ Fuchs ist 2x hintereinander dran
|
||||
if (model.FoxTurnsRemaining > 0)
|
||||
{
|
||||
model.FoxTurnsRemaining--;
|
||||
model.FoxTurn = false; // danach abwechselnd
|
||||
return foxId;
|
||||
}
|
||||
|
||||
// 2️⃣ Abwechselnd Nicht-Fuchs → Fuchs
|
||||
if (!model.FoxTurn)
|
||||
{
|
||||
// nächsten Nicht-Fuchs suchen
|
||||
while (model.NonFoxIndex == model.FoxIndex)
|
||||
model.NonFoxIndex++;
|
||||
|
||||
if (model.NonFoxIndex < model.PlayerOrder.Length)
|
||||
{
|
||||
Guid next = model.PlayerOrder[model.NonFoxIndex];
|
||||
model.NonFoxIndex++;
|
||||
model.FoxTurn = true;
|
||||
return next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Alle Nicht-Füchse durch → neuer Fuchs
|
||||
model.FoxIndex++;
|
||||
model.NonFoxIndex = 0;
|
||||
model.FoxTurnsRemaining = 2;
|
||||
model.FoxTurn = true;
|
||||
return GetNextId(model);
|
||||
}
|
||||
}
|
||||
|
||||
// 3️⃣ Fuchs-Zug im Wechsel
|
||||
model.FoxTurn = false;
|
||||
return foxId;
|
||||
}
|
||||
|
||||
private static int GetNextActivePlayerIndex(
|
||||
Guid[] playerOrder,
|
||||
int currentIndex,
|
||||
Dictionary<Guid, FoxHuntPlayerState> playerStates)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
private async Task<string> GetPlayerName(Guid playerId)
|
||||
{
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
var _personService = scope.ServiceProvider.GetRequiredService<IPersonService>();
|
||||
var persons = await _personService.GetAllAsync();
|
||||
var person = persons.FirstOrDefault(p => p.Id == playerId);
|
||||
return person?.Name ?? "Unbekannt";
|
||||
}
|
||||
}
|
||||
public (bool IsGameOver, Guid? WinnerId) CheckGameEnd(object gameModel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Guid, PlayerStatsSummary> GetPlayerStats(object gameModel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public GameSetupValidationResult ValidateSetup(object? setupOptions)
|
||||
{
|
||||
if (setupOptions is null)
|
||||
{
|
||||
return GameSetupValidationResult.Valid();
|
||||
}
|
||||
|
||||
var options = ParseSetupOptions(setupOptions);
|
||||
var errors = new List<string>();
|
||||
|
||||
if (options.LeadingThrows < 2 || options.LeadingThrows > 4)
|
||||
{
|
||||
errors.Add("Vorsprung muss zwischen 6 und 12 liegen.");
|
||||
}
|
||||
|
||||
//TODO: validate player-count, at lease 2 players needed
|
||||
|
||||
return errors.Count > 0
|
||||
? GameSetupValidationResult.Invalid(errors.ToArray())
|
||||
: GameSetupValidationResult.Valid();
|
||||
}
|
||||
|
||||
public IReadOnlyList<GameActionDescriptor> GetAvailableActions(object gameModel, Guid currentPlayerId)
|
||||
{
|
||||
// FoxHunt has no custom actions
|
||||
return [];
|
||||
}
|
||||
|
||||
public GameActionResult ExecuteAction(object gameModel, string actionId, Guid currentPlayerId,
|
||||
IReadOnlyDictionary<string, object>? parameters = null)
|
||||
{
|
||||
return GameActionResult.Failure($"Unknown action: {actionId}");
|
||||
}
|
||||
|
||||
private static FoxHuntGameSetup ParseSetupOptions(object? setupOptions)
|
||||
{
|
||||
if (setupOptions is null)
|
||||
{
|
||||
return new FoxHuntGameSetup();
|
||||
}
|
||||
|
||||
if (setupOptions is FoxHuntGameSetup typedSetup)
|
||||
{
|
||||
return typedSetup;
|
||||
}
|
||||
|
||||
if (setupOptions is JsonElement jsonElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<FoxHuntGameSetup>(
|
||||
jsonElement.GetRawText(),
|
||||
GameModelFactory.JsonSerializerOptions) ?? new FoxHuntGameSetup();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new FoxHuntGameSetup();
|
||||
}
|
||||
}
|
||||
|
||||
return new FoxHuntGameSetup();
|
||||
}
|
||||
|
||||
private static FoxHuntGameModel CastModel(object gameModel)
|
||||
{
|
||||
if (gameModel is FoxHuntGameModel model)
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
if (gameModel is JsonElement jsonElement)
|
||||
{
|
||||
return JsonSerializer.Deserialize<FoxHuntGameModel>(
|
||||
jsonElement.GetRawText(),
|
||||
GameModelFactory.JsonSerializerOptions)!;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Expected FoxHuntGameModel but got {gameModel.GetType().Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
namespace Koogle.Application.Games.FoxHunt
|
||||
{
|
||||
/// <summary>
|
||||
/// Game model for Fuchsjagd-Spiel (Fox Hunt) game type.
|
||||
/// One Player starts as fox, and all others try to hunt him.
|
||||
/// </summary>
|
||||
public record FoxHuntGameModel
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the winner (first player to reach 0 points).
|
||||
/// </summary>
|
||||
public Guid? WinnerId { get; set; }
|
||||
|
||||
public bool IsGameOver { get; set; }
|
||||
//public int LeadingThrows { get; set; }
|
||||
//public int FoxLeadingThrowCount { get; set; }
|
||||
public Dictionary<Guid, FoxHuntPlayerState> PlayerStates { get; set; }
|
||||
public Guid[] PlayerOrder { get; set; }
|
||||
|
||||
public int LeadingThrows { get; set; } // copy from setup model
|
||||
//public int CurrentPlayerIndex { get; set; }
|
||||
//public int CurrentFoxIndex { get; set; }
|
||||
|
||||
|
||||
|
||||
public int FoxIndex = 0; // aktueller Fuchs (Index in PlayerOrder)
|
||||
public int NonFoxIndex = 0; // Index für Nicht-Fuchs-Spieler
|
||||
public int FoxTurnsRemaining = 2; // erste 2 Fuchs-Züge
|
||||
public bool FoxTurn = true; // wer ist dran
|
||||
|
||||
}
|
||||
|
||||
public record FoxHuntPlayerState
|
||||
{
|
||||
|
||||
public int PinCount { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using Koogle.Domain.Enums;
|
||||
|
||||
namespace Koogle.Application.Games.FoxHunt
|
||||
{
|
||||
/// <summary>
|
||||
/// Setup model for Fuchsjagd-Spiel (Fox Hunt) game type.
|
||||
/// One Player starts as fox, and all others try to hunt him.
|
||||
/// </summary>
|
||||
public record FoxHuntGameSetup : GameSetupModelBase
|
||||
{
|
||||
public override string GameType => "FoxHunt";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Leading throw the fox do, before the hunt begins
|
||||
/// </summary>
|
||||
public int LeadingThrows { get; init; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a default ShitGameSetup with specified base parameters.
|
||||
/// </summary>
|
||||
public static FoxHuntGameSetup Create(
|
||||
ThrowMode throwMode,
|
||||
int throwsPerRound,
|
||||
ParticipantsMode participantsMode,
|
||||
int leadingThrows = 2) => new()
|
||||
{
|
||||
ThrowMode = throwMode,
|
||||
ThrowsPerRound = throwsPerRound,
|
||||
ParticipantsMode = participantsMode,
|
||||
LeadingThrows = leadingThrows
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ namespace Koogle.Application.Games;
|
|||
[JsonDerivedType(typeof(Shit.ShitGameSetup), "Shit")]
|
||||
[JsonDerivedType(typeof(Training.TrainingGameSetup), "Training")]
|
||||
[JsonDerivedType(typeof(DeathBox.DeathBoxGameSetup), "DeathBox")]
|
||||
[JsonDerivedType(typeof(FoxHunt.FoxHuntGameSetup), "FoxHunt")]
|
||||
public interface IGameSetupModel
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,213 @@
|
|||
@inherits Fluxor.Blazor.Web.Components.FluxorComponent
|
||||
|
||||
@using System.Text.Json
|
||||
@using Koogle.Application.Games.FoxHunt
|
||||
@using Koogle.Web.Store.DayState
|
||||
@using Koogle.Web.Store.GameState
|
||||
@using Koogle.Application.Games;
|
||||
|
||||
@implements IDisposable
|
||||
@inject IState<GameState> GameState
|
||||
@inject IState<DayState> DayState
|
||||
|
||||
<pre>
|
||||
@_debug
|
||||
</pre>
|
||||
|
||||
<MudPaper Class="pa-4">
|
||||
@if (_model == null)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info">
|
||||
Spiel noch nicht gestartet.
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
@* Game info header *@
|
||||
<MudPaper Class="pa-3 mb-4" Elevation="0" Style="background-color: var(--mud-palette-background-grey);">
|
||||
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.body1">
|
||||
<strong>Vorsprung für den Fuchs:</strong>
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Default" Variant="Variant.Outlined">
|
||||
@_model.LeadingThrows Vorsprung
|
||||
</MudChip>
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body1">
|
||||
<strong>Füchse übrig:</strong>
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Primary" Variant="Variant.Filled">
|
||||
0
|
||||
</MudChip>
|
||||
</MudText>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
|
||||
@* Last throw info *@
|
||||
@* @if (_model.LastThrow != null)
|
||||
{
|
||||
<MudAlert Severity="@GetLastThrowSeverity()" Class="mb-4" Dense="true">
|
||||
@GetLastThrowMessage()
|
||||
</MudAlert>
|
||||
} *@
|
||||
|
||||
@* Winner announcement *@
|
||||
@if (_model.IsGameOver && _model.WinnerId.HasValue)
|
||||
{
|
||||
var winnerName = GetPlayerName(_model.WinnerId.Value);
|
||||
<MudAlert Severity="Severity.Success" Class="mb-4">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudIcon Icon="@Icons.Material.Filled.EmojiEvents" />
|
||||
<MudText Typo="Typo.h6">@winnerName hat überlebt!</MudText>
|
||||
</MudStack>
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@* Players table *@
|
||||
<MudTable Items="@_playerStats"
|
||||
Dense="true"
|
||||
Hover="true"
|
||||
Striped="true"
|
||||
Bordered="true"
|
||||
Class="mb-4">
|
||||
<HeaderContent>
|
||||
<MudTh>Spieler</MudTh>
|
||||
<MudTh>Status</MudTh>
|
||||
<MudTh Style="text-align: center">Xs</MudTh>
|
||||
<MudTh Style="text-align: center">Eier</MudTh>
|
||||
<MudTh Style="text-align: center">Status</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>
|
||||
@if (context.IsCurrentPlayer && !_model.IsGameOver)
|
||||
{
|
||||
<MudBadge Color="Color.Primary" Dot="true" Overlap="false"
|
||||
Icon=@Icons.Material.Filled.ArrowCircleDown
|
||||
Origin="Origin.TopLeft">
|
||||
<MudText Typo="Typo.body1" Style="font-weight: 600">
|
||||
@context.PlayerName
|
||||
</MudText>
|
||||
</MudBadge>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Typo="Typo.body1" Style="@(context.IsEliminated ? "text-decoration: line-through; color: var(--mud-palette-text-disabled);" : "")">
|
||||
@context.PlayerName
|
||||
</MudText>
|
||||
}
|
||||
</MudTd>
|
||||
|
||||
|
||||
|
||||
<MudTd Style="text-align: center">
|
||||
@if (context.IsWinner)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Success" Variant="Variant.Filled"
|
||||
Icon="@Icons.Material.Filled.EmojiEvents">
|
||||
SIEGER
|
||||
</MudChip>
|
||||
}
|
||||
else if (context.IsEliminated)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Error" Variant="Variant.Outlined">
|
||||
☠️ Platz @context.FoxSurvivalOrder
|
||||
</MudChip>
|
||||
}
|
||||
else if (context.IsCurrentPlayer)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Primary" Variant="Variant.Outlined">
|
||||
Am Zug
|
||||
</MudChip>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
|
||||
@* Info footer *@
|
||||
@if (!_model.IsGameOver)
|
||||
{
|
||||
<MudPaper Class="pa-3" Elevation="0" Style="background-color: var(--mud-palette-background-grey);">
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
Überlebe als als Fuchs, mit dem größten Vorsprung!
|
||||
</MudText>
|
||||
</MudPaper>
|
||||
}
|
||||
}
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
private FoxHuntGameModel? _model;
|
||||
private List<PlayerStatsRow> _playerStats = [];
|
||||
private string _debug;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
GameState.StateChanged += OnGameStateChanged;
|
||||
UpdateStats();
|
||||
}
|
||||
|
||||
private void OnGameStateChanged(object? sender, EventArgs e)
|
||||
{
|
||||
UpdateStats();
|
||||
InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private void UpdateStats()
|
||||
{
|
||||
_playerStats.Clear();
|
||||
|
||||
_model = null;
|
||||
// _activePlayerCount = 0;
|
||||
|
||||
var gameState = GameState.Value;
|
||||
if (gameState.GameModel is FoxHuntGameModel model)
|
||||
{
|
||||
_model = model;
|
||||
}
|
||||
else if (gameState.GameModel is JsonElement jsonElement)
|
||||
{
|
||||
try
|
||||
{
|
||||
_model = JsonSerializer.Deserialize<FoxHuntGameModel>(
|
||||
jsonElement.GetRawText(),
|
||||
GameModelFactory.JsonSerializerOptions);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_debug = JsonSerializer.Serialize(_model, new JsonSerializerOptions { WriteIndented = true });
|
||||
InvokeAsync(StateHasChanged);
|
||||
|
||||
|
||||
if (_model?.PlayerStates == null || _model.PlayerOrder == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GameState.StateChanged -= OnGameStateChanged;
|
||||
}
|
||||
|
||||
private string GetPlayerName(Guid playerId)
|
||||
{
|
||||
var person = DayState.Value.AvailablePersons.FirstOrDefault(p => p.Id == playerId);
|
||||
return person?.Name ?? "Unbekannt";
|
||||
}
|
||||
|
||||
private record PlayerStatsRow
|
||||
{
|
||||
public Guid PlayerId { get; init; }
|
||||
public string PlayerName { get; init; } = "";
|
||||
public string Status { get; init; } = "";
|
||||
public bool IsWinner { get; init; }
|
||||
public bool IsCurrentPlayer { get; init; }
|
||||
public bool IsEliminated { get; init; }
|
||||
public int FoxSurvivalOrder { get; init; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
@inject ITriggerService TriggerService
|
||||
@using Koogle.Application.Games
|
||||
@using Koogle.Application.Games.FoxHunt
|
||||
@using Koogle.Application.Interfaces
|
||||
@using Koogle.Domain.Enums
|
||||
@implements Koogle.Application.Interfaces.IGameSetupControl
|
||||
|
||||
<MudPaper Class="pa-4">
|
||||
<MudText Typo="Typo.h6" Class="mb-4">Fuchsjagd Einstellungen</MudText>
|
||||
|
||||
<MudStack Spacing="4">
|
||||
<MudNumericField T="int"
|
||||
@bind-Value="_options.LeadingThrows"
|
||||
Label="Würfe-Vorsprung"
|
||||
Variant="Variant.Outlined"
|
||||
Min="2"
|
||||
Max="4"
|
||||
HelperText="Anzahl Würfe, die der Fuchs an Vorspring bekommt (2-4)" />
|
||||
</MudStack>
|
||||
|
||||
|
||||
@if (!_hasExpensePointTrigger)
|
||||
{
|
||||
<MudAlert Severity="Severity.Warning" Class="mt-4">
|
||||
<MudText Typo="Typo.body2">
|
||||
<strong>Hinweis:</strong> Es ist kein "Strafpunkt"-Trigger konfiguriert.
|
||||
Die Verlierer-Strafen werden nicht automatisch berechnet.
|
||||
Gehe zu Stammdaten → Trigger, um einen ExpensePoint-Trigger einzurichten.
|
||||
</MudText>
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
<MudDivider Class="my-4" />
|
||||
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
<strong>Spielregeln:</strong>
|
||||
<ul style="margin: 8px 0 0 16px; padding: 0;">
|
||||
<li>Jeder Spieler reihum ist einmal der Fuchs</li>
|
||||
<li>Der Fuchs startet mit 'Anzahl-Würfe-Vorsprung' in die Vollen</li>
|
||||
<li>Er sammelt möglichst viele Kegel und erarbeitet sich sich einen Vorsprung</li>
|
||||
<li>Anschließend hat jeder weitere Spieler einen Wurf, abwechselnd mit dem Fuchs</li>
|
||||
<li>Ziel der anderen Spieler ist es, den Fuchs zu fangen!</li>
|
||||
<li>Ziel des Fuchses ist es, am Ende der Runde mindestens einen Punkt Vorsprung zu haben</li>
|
||||
<li>Es gewinnt der Fuchs mit dem größten Vorsprung</li>
|
||||
</ul>
|
||||
</MudText>
|
||||
|
||||
</MudPaper>
|
||||
@code {
|
||||
/// <summary>
|
||||
/// Callback when setup options change.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public EventCallback<object> OnOptionsChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initial setup options.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public object? InitialOptions { get; set; }
|
||||
|
||||
private bool _hasExpensePointTrigger = true;
|
||||
|
||||
private FoxSetupOptionsInternal _options = new();
|
||||
|
||||
// Internal mutable class for two-way binding
|
||||
private class FoxSetupOptionsInternal
|
||||
{
|
||||
public int LeadingThrows { get; set; } = 2;
|
||||
|
||||
public ParticipantsMode ParticipantsMode { get; set; } = ParticipantsMode.GameLogic;
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (InitialOptions is FoxHuntGameSetup setup)
|
||||
{
|
||||
_options = new FoxSetupOptionsInternal
|
||||
{
|
||||
LeadingThrows = setup.LeadingThrows,
|
||||
ParticipantsMode = setup.ParticipantsMode
|
||||
};
|
||||
}
|
||||
|
||||
// Check if ExpensePoint trigger is configured
|
||||
_hasExpensePointTrigger = await TriggerService.HasExpensesForTriggerAsync(ExpenseTriggerType.ExpensePoint);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await NotifyOptionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NotifyOptionsChanged()
|
||||
{
|
||||
var setup = new FoxHuntGameSetup
|
||||
{
|
||||
LeadingThrows = _options.LeadingThrows,
|
||||
ParticipantsMode = _options.ParticipantsMode
|
||||
};
|
||||
await OnOptionsChanged.InvokeAsync(setup);
|
||||
}
|
||||
|
||||
public IGameSetupModel GameSetupModel => CreateFoxSetup();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a default FoxHuntSetup with specified base parameters.
|
||||
/// </summary>
|
||||
private FoxHuntGameSetup CreateFoxSetup()
|
||||
{
|
||||
return FoxHuntGameSetup.Create(
|
||||
throwMode: ThrowMode.Reposition,
|
||||
throwsPerRound: 1,
|
||||
participantsMode: ParticipantsMode.GameLogic,
|
||||
leadingThrows: _options.LeadingThrows);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -92,6 +92,8 @@
|
|||
{
|
||||
"Training" => Icons.Material.Filled.FitnessCenter,
|
||||
"Shit" => Icons.Material.Filled.Casino,
|
||||
"DeathBox" => Icons.Material.Filled.Church,
|
||||
"FoxHunt" => Icons.Material.Filled.DirectionsRun,
|
||||
_ => Icons.Material.Filled.SportsScore
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Components.Authorization;
|
|||
using Microsoft.AspNetCore.Components.Server;
|
||||
using MudBlazor.Services;
|
||||
using System.Reflection;
|
||||
using Koogle.Application.Games.FoxHunt;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ builder.Services.AddApplication();
|
|||
builder.Services.AddGameType<TrainingGameDefinition, TrainingGameLogicService>();
|
||||
builder.Services.AddGameType<ShitGameDefinition, ShitGameLogicService>();
|
||||
builder.Services.AddGameType<DeathBoxGameDefinition, DeathBoxGameLogicService>();
|
||||
builder.Services.AddGameType<FoxHuntGameDefinition, FoxHuntGameLogicService>();
|
||||
|
||||
// SignalR for real-time game updates
|
||||
builder.Services.AddSignalR();
|
||||
|
|
|
|||
Loading…
Reference in New Issue