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

FARAÓN

@if (Engine.State.Rules.OlympicMode) { 🏅 KOLO #@Engine.State.RoundNumber }
Ť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
}
@if (IsViewingPlayerTurn && !ViewingPlayer.HasFinished && !ViewingPlayer.IsEliminated) {
SI NA ŤAHU
}
@ViewingPlayer.Avatar @ViewingPlayer.Name (@ViewingPlayer.Hand.Count @(ViewingPlayer.Hand.Count == 1 ? "karta" : (ViewingPlayer.Hand.Count < 5 ? "karty" : "kariet"))) @if (Engine.State.Rules.OlympicMode) { 🏅 @ViewingPlayer.OlympicCardsCount @(ViewingPlayer.OlympicCardsCount == 1 ? "karta" : (ViewingPlayer.OlympicCardsCount < 5 ? "karty" : "kariet")) }
@if (!ViewingPlayer.IsEliminated) { } else {
☠️ Bol si vyradený z Olympijského turnaja. Sleduj zápas až do konca!
}
@if (IsViewingPlayerTurn && Engine.State.TurnState == TurnState.Playing && !ViewingPlayer.IsEliminated) {
@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.TurnState == TurnState.RoundOver) { } @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? _previousTurnPlayer; 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); _previousTurnPlayer = Engine.State.CurrentPlayer; 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 / 3)); await Fx.TriggerDopamineBannerAsync($"🔥 +{e.ExtraData} KARIET!", "Trestná reťaz sediem", "triple"); break; case GameEventType.AceSkipped: case GameEventType.AceCountered: await Fx.TriggerFreezeEffectAsync(); break; case GameEventType.PlayerRevived: await Fx.TriggerReviveEffectAsync(e.TargetPlayer?.Name ?? e.SourcePlayer?.Name ?? "Hráč"); break; case GameEventType.SuitChanged: await Fx.PlayHornikChimeAsync(); break; case GameEventType.CardsBurned: await Fx.PlayBurnExplosionAsync(); break; case GameEventType.PlayerFinished: case GameEventType.MatchWon: case GameEventType.GameOver: await Fx.TriggerJackpotWinAsync(e.SourcePlayer?.Name ?? "Víťaz"); break; } if (Engine != null && Engine.State.CurrentPlayer.Hand.Count == 1 && !Engine.State.CurrentPlayer.HasFinished) { await Fx.PlayHeartbeatAsync(); } await CheckTurnTransitionAsync(); StateHasChanged(); }); } private async Task CheckTurnTransitionAsync() { if (Engine == null || Engine.State.Players.Count == 0 || Engine.State.IsGameOver) return; var current = Engine.State.CurrentPlayer; if (_previousTurnPlayer != null && _previousTurnPlayer != current) { var prev = _previousTurnPlayer; _previousTurnPlayer = current; await Fx.AnimateTurnTransitionAsync(GetPlayerPosition(prev), GetPlayerPosition(current)); } else { _previousTurnPlayer = current; } } 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 static readonly string[] AvailableDecks = new[] { "original", "janosik", "spongebob", "cyberpunk", "fantasy", "egypt", "pets", "pixel" }; private void ToggleDeck() { if (Engine == null) return; int currentIdx = Array.IndexOf(AvailableDecks, Engine.State.Rules.SelectedDeck); int nextIdx = (currentIdx + 1) % AvailableDecks.Length; Engine.State.Rules.SelectedDeck = AvailableDecks[nextIdx]; StateHasChanged(); } private string GetDeckIcon(string deck) => deck switch { "janosik" => "🪓", "spongebob" => "🧽", "cyberpunk" => "⚡", "fantasy" => "🐉", "egypt" => "⚔️", "pets" => "🐱", "pixel" => "🕹️", _ => "🎴" }; private async Task HandlePlayCards(List cards) { if (Engine == null || cards.Count == 0) return; string pos = GetPlayerPosition(ViewingPlayer); string deck = Engine.State.Rules.SelectedDeck; if (cards.Count == 2) { _ = Fx.TriggerComboSlamAsync(2, "⚡ DVOJIČKA!"); } else if (cards.Count == 3) { _ = Fx.TriggerComboSlamAsync(3, "🔥 TROJITÝ ÚDER!"); } else if (cards.Count >= 4) { _ = Fx.TriggerComboSlamAsync(4, "👑 KVADRA / MEGA COMBO!"); } 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 void HandleStartNextRound() { if (Engine == null) return; Engine.StartNextRound(); 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) { if (cardsToPlay.Count == 2) { _ = Fx.TriggerComboSlamAsync(2, $"⚡ {aiPlayer.Name}: DVOJIČKA!"); } else if (cardsToPlay.Count == 3) { _ = Fx.TriggerComboSlamAsync(3, $"🔥 {aiPlayer.Name}: TROJITÝ ÚDER!"); } else if (cardsToPlay.Count >= 4) { _ = Fx.TriggerComboSlamAsync(4, $"👑 {aiPlayer.Name}: KVADRA!"); } 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; } }