268 lines
10 KiB
Plaintext
268 lines
10 KiB
Plaintext
@namespace Faraon.Client.Components
|
|
@inject FxService Fx
|
|
|
|
<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.2)) : 0;
|
|
double yOffset = Math.Abs(offset) * 3.5;
|
|
double stepFactor = total > 8 ? 0.75 : (total > 5 ? 0.88 : 1.0);
|
|
|
|
// 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(calc({offset.ToString(System.Globalization.CultureInfo.InvariantCulture)} * var(--fan-card-step) * {stepFactor.ToString(System.Globalization.CultureInfo.InvariantCulture)})) translateY({yOffset.ToString(System.Globalization.CultureInfo.InvariantCulture)}px) rotate({angle.ToString(System.Globalization.CultureInfo.InvariantCulture)}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"
|
|
CardIndex="@i"
|
|
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);
|
|
await Fx.PlaySelectStepAsync(1);
|
|
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);
|
|
await Fx.PlaySelectStepAsync(1);
|
|
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);
|
|
await Fx.PlaySelectStepAsync(Math.Max(1, SelectedCards.Count));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Pridáme do poradia ako 2., 3., 4. kartu
|
|
SelectedCards.Add(card);
|
|
await Fx.PlaySelectStepAsync(SelectedCards.Count);
|
|
}
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task ReverseSelectionOrder()
|
|
{
|
|
if (SelectedCards.Count <= 1) return;
|
|
SelectedCards.Reverse();
|
|
await Fx.PlaySelectStepAsync(SelectedCards.Count);
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task 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);
|
|
}
|
|
}
|
|
await Fx.PlaySelectStepAsync(SelectedCards.Count);
|
|
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);
|
|
}
|
|
}
|
|
}
|