@page "/" @inject FxService Fx @inject IFaraonAI AI @inject IJSRuntime JS @if (IsInLobby || Engine == null) { } else {

FARAÓN

Ťah #@Engine.State.TurnCount • Na rade: @Engine.State.CurrentPlayer.Avatar @Engine.State.CurrentPlayer.Name
@foreach (var opp in GetOpponents()) { }
@Engine.State.DrawDeck.Count
@if (Engine.State.TopCard != null) { } @if (Engine.State.PendingDrawCount > 0) {
🔥 +@Engine.State.PendingDrawCount Kariet!
} @if (Engine.State.ActiveSuit.HasValue) { var dummy = new Card(Engine.State.ActiveSuit.Value, Rank.OverKnave);
Farba:
@dummy.SuitName
}
@ViewingPlayer.Avatar @ViewingPlayer.Name (@ViewingPlayer.Hand.Count kariet)
@if (IsViewingPlayerTurn && Engine.State.TurnState == TurnState.Playing) {
@if (CheckFourOfAKindRank(ViewingPlayer) is Rank burnRank) { }
} @if (!string.IsNullOrEmpty(LastEventMessage)) {
@LastEventMessage
} @if (Engine.State.TurnState == TurnState.SelectingSuit && Engine.State.PendingHornikPlayer == ViewingPlayer) { } @if (Engine.State.TurnState == TurnState.PassScreen) { } @if (Engine.State.IsGameOver) { }
} @code { private bool IsInLobby = true; private bool IsMuted = false; private GameEngine? Engine; private string LastEventMessage = ""; private System.Threading.CancellationTokenSource? _aiCts; private List ConfiguredPlayers = new(); private RuleOptions ConfiguredRules = new(); private Player ViewingPlayer { get { if (Engine == null || Engine.State.Players.Count == 0) return new Player(); // Ak je Pass & Play a aktuálny hráč je človek, zobrazujeme jeho ruku if (Engine.State.CurrentPlayer.Type == PlayerType.Human) { return Engine.State.CurrentPlayer; } // Inak primárne prvého živého hráča v hre var firstHuman = Engine.State.Players.FirstOrDefault(p => p.Type == PlayerType.Human); return firstHuman ?? Engine.State.CurrentPlayer; } } private bool IsViewingPlayerTurn => Engine != null && Engine.State.TurnState == TurnState.Playing && Engine.State.CurrentPlayer == ViewingPlayer; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await Fx.InitParticlesAsync(); } } private void HandleStartGame((List players, RuleOptions rules) setup) { ConfiguredPlayers = setup.players; ConfiguredRules = setup.rules; StartNewGame(); } private void StartNewGame() { // Klony hráčov pre čistú hru var gamePlayers = ConfiguredPlayers.Select(p => new Player(p.Name, p.Type, p.Avatar, p.Difficulty)).ToList(); Engine = new GameEngine(); Engine.OnGameEvent += HandleGameEvent; Engine.StartGame(gamePlayers, ConfiguredRules); IsInLobby = false; LastEventMessage = "Hra začala! Veľa šťastia."; StateHasChanged(); CheckTriggerAI(); } private void HandleGameEvent(GameEvent e) { LastEventMessage = e.Message; _ = InvokeAsync(async () => { switch (e.Type) { case GameEventType.CardPlayed: await Fx.PlayCardSnapAsync(); break; case GameEventType.CardDrawn: await Fx.PlayCardDrawAsync(); break; case GameEventType.SevenStacked: await Fx.PlaySevenStackAsync(Math.Max(1, e.ExtraData / 2)); break; case GameEventType.AceSkipped: case GameEventType.AceCountered: await Fx.PlayAceFreezeAsync(); break; case GameEventType.SuitChanged: await Fx.PlayHornikChimeAsync(); break; case GameEventType.CardsBurned: await Fx.PlayBurnExplosionAsync(); break; case GameEventType.PlayerFinished: case GameEventType.GameOver: await Fx.PlayVictoryAsync(); break; } if (Engine != null && Engine.State.CurrentPlayer.Hand.Count == 1 && !Engine.State.CurrentPlayer.HasFinished) { await Fx.PlayHeartbeatAsync(); } StateHasChanged(); }); } private string GetPlayerPosition(Player player) { if (player == ViewingPlayer) return "bottom"; var opp = GetOpponents().FirstOrDefault(o => o.Player == player); if (!string.IsNullOrEmpty(opp.PositionClass)) { return opp.PositionClass.Replace("slot-", ""); } return "top"; } private void ToggleDeck() { if (Engine == null) return; Engine.State.Rules.SelectedDeck = Engine.State.Rules.SelectedDeck == "spongebob" ? "original" : "spongebob"; StateHasChanged(); } private async Task HandlePlayCards(List cards) { if (Engine == null || cards.Count == 0) return; string pos = GetPlayerPosition(ViewingPlayer); string deck = Engine.State.Rules.SelectedDeck; foreach (var card in cards) { _ = Fx.AnimateCardPlayAsync(pos, card, deck); if (cards.Count > 1) await Task.Delay(90); } Engine.PlayCards(ViewingPlayer, cards); StateHasChanged(); CheckTriggerAI(); } private async Task HandleDeckClick() { if (Engine == null || !IsViewingPlayerTurn) return; string pos = GetPlayerPosition(ViewingPlayer); string deck = Engine.State.Rules.SelectedDeck; if (Engine.State.PendingDrawCount > 0) { for (int i = 0; i < Math.Min(Engine.State.PendingDrawCount, 4); i++) { _ = Fx.AnimateCardDrawAsync(pos, deck); await Task.Delay(90); } } else if (!Engine.State.PendingSkip) { await Fx.AnimateCardDrawAsync(pos, deck); } Engine.DrawOrAcknowledge(ViewingPlayer); StateHasChanged(); CheckTriggerAI(); } private void HandleSuitSelected(Suit suit) { if (Engine == null) return; Engine.ChooseSuit(ViewingPlayer, suit); StateHasChanged(); CheckTriggerAI(); } private void HandlePassContinue() { if (Engine == null) return; Engine.ContinueFromPassScreen(); StateHasChanged(); CheckTriggerAI(); } private async Task HandleBurn(Rank rank) { if (Engine == null) return; var cards = ViewingPlayer.Hand.Where(c => c.Rank == rank).ToList(); if (cards.Count == 4) { string pos = GetPlayerPosition(ViewingPlayer); string deck = Engine.State.Rules.SelectedDeck; foreach (var c in cards) { _ = Fx.AnimateCardPlayAsync(pos, c, deck); await Task.Delay(75); } Engine.BurnCards(ViewingPlayer, cards); StateHasChanged(); CheckTriggerAI(); } } private Rank? CheckFourOfAKindRank(Player player) { if (Engine == null || !Engine.State.Rules.FourOfAKindBurns) return null; var group = player.Hand.GroupBy(c => c.Rank).FirstOrDefault(g => g.Count() == 4); return group?.Key; } private void CheckTriggerAI() { if (Engine == null || Engine.State.IsGameOver) return; if (Engine.State.TurnState == TurnState.Playing && Engine.State.CurrentPlayer.Type == PlayerType.AI) { _aiCts?.Cancel(); _aiCts = new System.Threading.CancellationTokenSource(); var token = _aiCts.Token; _ = Task.Run(async () => { try { await Task.Delay(Engine.State.Rules.AIDelayMs, token); if (token.IsCancellationRequested || Engine == null) return; var aiPlayer = Engine.State.CurrentPlayer; if (aiPlayer.Type != PlayerType.AI) return; var decision = AI.MakeDecision(Engine.State, aiPlayer); await InvokeAsync(async () => { string pos = GetPlayerPosition(aiPlayer); string deck = Engine.State.Rules.SelectedDeck; if (decision.Type == AIDecisionType.PlayCard) { var cardsToPlay = decision.Cards ?? (decision.Card != null ? new List { decision.Card } : new List()); if (cardsToPlay.Count > 0) { foreach (var c in cardsToPlay) { _ = Fx.AnimateCardPlayAsync(pos, c, deck); if (cardsToPlay.Count > 1) await Task.Delay(85); } Engine.PlayCards(aiPlayer, cardsToPlay, decision.ChosenSuit); } } else if (decision.Type == AIDecisionType.BurnCards && decision.Cards != null) { foreach (var c in decision.Cards) { _ = Fx.AnimateCardPlayAsync(pos, c, deck); await Task.Delay(75); } Engine.BurnCards(aiPlayer, decision.Cards); } else { if (Engine.State.PendingDrawCount > 0) { for (int i = 0; i < Math.Min(Engine.State.PendingDrawCount, 4); i++) { _ = Fx.AnimateCardDrawAsync(pos, deck); await Task.Delay(90); } } else if (!Engine.State.PendingSkip) { await Fx.AnimateCardDrawAsync(pos, deck); } Engine.DrawOrAcknowledge(aiPlayer); } StateHasChanged(); CheckTriggerAI(); }); } catch (TaskCanceledException) { } }, token); } } private IEnumerable<(Player Player, string PositionClass)> GetOpponents() { if (Engine == null) return Enumerable.Empty<(Player, string)>(); var opponents = Engine.State.Players.Where(p => p != ViewingPlayer).ToList(); var result = new List<(Player, string)>(); if (opponents.Count == 1) { result.Add((opponents[0], "slot-top")); } else if (opponents.Count == 2) { result.Add((opponents[0], "slot-left")); result.Add((opponents[1], "slot-right")); } else if (opponents.Count >= 3) { result.Add((opponents[0], "slot-left")); result.Add((opponents[1], "slot-top")); result.Add((opponents[2], "slot-right")); } return result; } private async Task ToggleMute() { IsMuted = await Fx.ToggleMuteAsync(); } private void HandlePlayAgain() { StartNewGame(); } private void ReturnToLobby() { _aiCts?.Cancel(); IsInLobby = true; Engine = null; } }