Files
FaraonCardGame/Faraon.Client/Components/PlayerHandView.razor
T
2026-08-28 14:37:21 +02:00

260 lines
9.6 KiB
Plaintext

@namespace Faraon.Client.Components
<div class="player-hand-container">
@if (SelectedCards.Count > 0 && IsMyTurn)
{
var firstSelected = SelectedCards[0];
var matchingHandCards = Player.Hand.Where(c => c.Rank == firstSelected.Rank).ToList();
var canPlayCombo = CanPlayCards(SelectedCards) && CanPlayCard(firstSelected);
<div class="hand-combo-action-bar animate-pop">
<!-- Poradie vykladania kariet -->
<div class="combo-sequence-container">
<span class="combo-seq-label">Poradie:</span>
<div class="combo-sequence-items">
@for (int s = 0; s < SelectedCards.Count; s++)
{
var sc = SelectedCards[s];
var isFinal = (s == SelectedCards.Count - 1 && SelectedCards.Count > 1);
<span class="combo-seq-item @(isFinal ? "combo-seq-final" : "")" style="color: @sc.SuitColor;">
<span class="seq-num">@(s + 1).</span>
<span class="seq-symbol">@sc.SuitSymbol</span>
<span class="seq-suit">@sc.SuitName</span>
@if (isFinal)
{
<span class="seq-crown" title="Karta ostane na vrchu odhadzovacieho balíčka">👑</span>
}
</span>
@if (s < SelectedCards.Count - 1)
{
<span class="combo-seq-arrow">➔</span>
}
}
</div>
</div>
<!-- Tlačidlo na zahranie vybratých kariet -->
@if (canPlayCombo)
{
<button class="btn btn-gold btn-combo-play" @onclick="PlaySelected">
<span>⚡ Zahrať (@SelectedCards.Count @(SelectedCards.Count == 1 ? "kartu" : (SelectedCards.Count < 5 ? "karty" : "kariet")))</span>
</button>
}
else
{
<div class="combo-invalid-badge">
<span>⚠️ Prvá karta (@firstSelected.SuitName) nesedí na stôl</span>
</div>
}
<!-- Možnosť otočiť poradie kariet (ak je otočenie tiež legálne) -->
@if (SelectedCards.Count > 1)
{
<button class="btn btn-dark btn-combo-reverse" @onclick="ReverseSelectionOrder" title="Otočiť poradie kariet (zmení vrchnú kartu na stole)">
<span>⇄ Otočiť</span>
</button>
}
<!-- Vybrať všetky karty danej hodnoty -->
@if (matchingHandCards.Count > SelectedCards.Count)
{
<button class="btn btn-dark btn-combo-all" @onclick="SelectAllOfRank" title="Pridať všetky zostávajúce karty tejto hodnoty">
<span>+ Všetky (@matchingHandCards.Count)</span>
</button>
}
<!-- Zrušenie výberu -->
<button class="btn btn-dark btn-combo-clear" @onclick="ClearSelection" title="Zrušiť výber">
<span>✕</span>
</button>
</div>
}
<div class="player-hand-fanning">
@for (int i = 0; i < Player.Hand.Count; i++)
{
var card = Player.Hand[i];
var selectedIdx = SelectedCards.IndexOf(card);
var isSelected = selectedIdx >= 0;
var isSameRankAsSelected = SelectedCards.Count > 0 && SelectedCards[0].Rank == card.Rank;
// Karta je hrateľná ako 1. na stôl, ALEBO patrí do aktívneho comba rovnakej hodnoty
var isPlayable = IsMyTurn && (
(SelectedCards.Count == 0 && CanPlayCard(card)) ||
(SelectedCards.Count > 0 && isSameRankAsSelected)
);
// Výpočet vejárovitého natočenia kariet v ruke
int total = Player.Hand.Count;
double mid = (total - 1) / 2.0;
double offset = i - mid;
double angle = total > 1 ? offset * (Math.Min(35.0 / total, 5.5)) : 0;
double yOffset = Math.Abs(offset) * 4.0;
double xOffset = offset * (total > 8 ? 26 : 38);
// Ak patrí do aktívnej skupiny rovnakej hodnoty, vysunieme ju
string slotClasses = "hand-card-slot";
if (isSameRankAsSelected && !isSelected)
{
slotClasses += " rank-group-lifted";
}
string fanSlotStyle = $"transform: translateX({xOffset}px) translateY({yOffset}px) rotate({angle}deg); z-index: {(isSelected ? 140 + i : (isSameRankAsSelected ? 110 + i : i + 1))};";
string selectedClass = isSelected ? "card-selected-combo" : (isSameRankAsSelected ? "card-group-active" : "");
<div class="@slotClasses" style="@fanSlotStyle">
<CardView Card="card"
Deck="@Deck"
IsInteractive="@IsMyTurn"
IsPlayable="@isPlayable"
CustomClass="@selectedClass"
OnCardClicked="HandleCardClick" />
@if (isSelected)
{
var orderNum = selectedIdx + 1;
var isTopCard = (selectedIdx == SelectedCards.Count - 1 && SelectedCards.Count > 1);
<div class="card-order-badge @(isTopCard ? "is-top-badge" : "")">
<span class="order-number">@orderNum.</span>
@if (isTopCard)
{
<span class="order-tag">👑 Vrch</span>
}
</div>
}
</div>
}
</div>
</div>
@code {
[Parameter, EditorRequired] public Player Player { get; set; } = default!;
[Parameter] public string Deck { get; set; } = "original";
[Parameter] public bool IsMyTurn { get; set; } = false;
[Parameter] public Func<Card, bool> CanPlayCardFunc { get; set; } = _ => false;
[Parameter] public Func<List<Card>, bool> CanPlayCardsFunc { get; set; } = _ => false;
[Parameter] public EventCallback<List<Card>> OnPlayCards { get; set; }
private List<Card> SelectedCards = new();
private bool CanPlayCard(Card card) => CanPlayCardFunc(card);
private bool CanPlayCards(List<Card> cards) => CanPlayCardsFunc(cards);
protected override void OnParametersSet()
{
// Vyčistíme karty, ktoré hráč už nemá v ruke
SelectedCards.RemoveAll(c => !Player.Hand.Contains(c));
}
private async Task HandleCardClick(Card card)
{
if (!IsMyTurn) return;
var rankCount = Player.Hand.Count(c => c.Rank == card.Rank);
// 1. Ak ešte nie je nič vybraté:
if (SelectedCards.Count == 0)
{
// Prvá karta MUSÍ legálne sedieť na stôl!
if (!CanPlayCard(card))
{
// Karta nemôže ísť ako prvá (napr. Žaluď na Červeň) -> nedovolíme začať s ňou
return;
}
// Ak má hráč v ruke len 1 kartu danej hodnoty -> okamžite zahrať
if (rankCount == 1)
{
await OnPlayCards.InvokeAsync(new List<Card> { card });
return;
}
// Ak má viac kariet rovnakej hodnoty -> označíme túto ako 1. a vysunú sa všetky karty danej hodnoty!
SelectedCards.Add(card);
StateHasChanged();
return;
}
// 2. Ak už máme niečo vybraté z INEJ hodnoty:
if (SelectedCards[0].Rank != card.Rank)
{
// Môže táto nová karta ísť legálne na stôl ako prvá?
if (!CanPlayCard(card))
{
return;
}
SelectedCards.Clear();
if (rankCount == 1)
{
await OnPlayCards.InvokeAsync(new List<Card> { card });
return;
}
SelectedCards.Add(card);
StateHasChanged();
return;
}
// 3. Ak klikáme na kartu ROVNAKEJ hodnoty:
if (SelectedCards.Contains(card))
{
// Ak je to prvá a jediná vybratá karta, kliknutie ju odznačí a zruší combo
if (SelectedCards.Count == 1 && SelectedCards[0] == card)
{
SelectedCards.Clear();
}
else
{
// Odznačíme kartu zo zoznamu
SelectedCards.Remove(card);
}
}
else
{
// Pridáme do poradia ako 2., 3., 4. kartu
SelectedCards.Add(card);
}
StateHasChanged();
}
private void ReverseSelectionOrder()
{
if (SelectedCards.Count <= 1) return;
SelectedCards.Reverse();
StateHasChanged();
}
private void SelectAllOfRank()
{
if (SelectedCards.Count == 0) return;
var rank = SelectedCards[0].Rank;
foreach (var c in Player.Hand.Where(c => c.Rank == rank))
{
if (!SelectedCards.Contains(c))
{
SelectedCards.Add(c);
}
}
StateHasChanged();
}
private void ClearSelection()
{
SelectedCards.Clear();
StateHasChanged();
}
private async Task PlaySelected()
{
if (SelectedCards.Count > 0 && CanPlayCards(SelectedCards) && CanPlayCard(SelectedCards[0]))
{
var cardsToPlay = new List<Card>(SelectedCards);
SelectedCards.Clear();
await OnPlayCards.InvokeAsync(cardsToPlay);
}
}
}