fixes
This commit is contained in:
@@ -1,7 +1,15 @@
|
|||||||
@namespace Faraon.Client.Components
|
@namespace Faraon.Client.Components
|
||||||
|
|
||||||
<div class="player-slot @PositionClass">
|
<div class="player-slot @PositionClass @(IsCurrentTurn ? "active-turn-slot" : "")">
|
||||||
<div class="avatar-badge @(IsCurrentTurn ? "active-turn" : "") @(Player.IsSkipped ? "is-skipped" : "") @(Player.HasFinished ? "is-finished" : "")">
|
<div class="avatar-badge @(IsCurrentTurn ? "active-turn" : "") @(Player.IsSkipped ? "is-skipped" : "") @(Player.HasFinished ? "is-finished" : "")">
|
||||||
|
@if (IsCurrentTurn && !Player.HasFinished && !Player.IsEliminated)
|
||||||
|
{
|
||||||
|
<div class="turn-indicator-badge">
|
||||||
|
<span class="turn-indicator-dot"></span>
|
||||||
|
<span class="turn-indicator-text">NA ŤAHU</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@if (Player.HasFinished)
|
@if (Player.HasFinished)
|
||||||
{
|
{
|
||||||
<div class="badge-finished-pos">
|
<div class="badge-finished-pos">
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ else
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 0.95rem; font-weight: 700; color: #a3b899;">
|
<div class="turn-header-info">
|
||||||
Ťah #@Engine.State.TurnCount • Na rade:
|
Ťah #@Engine.State.TurnCount • Na rade:
|
||||||
<span style="color: var(--gold); font-weight: 900;">
|
<span class="active-turn-header-player">
|
||||||
@Engine.State.CurrentPlayer.Avatar @Engine.State.CurrentPlayer.Name
|
@Engine.State.CurrentPlayer.Avatar @Engine.State.CurrentPlayer.Name
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,13 +98,20 @@ else
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Spodný slot: Aktívny hráč v zobrazení -->
|
<!-- Spodný slot: Aktívny hráč v zobrazení -->
|
||||||
<div class="slot-bottom">
|
<div class="slot-bottom @(IsViewingPlayerTurn ? "active-turn-slot" : "")">
|
||||||
<div class="avatar-badge @(IsViewingPlayerTurn ? "active-turn" : "") @(ViewingPlayer.IsSkipped ? "is-skipped" : "")"
|
<div class="avatar-badge @(IsViewingPlayerTurn ? "active-turn" : "") @(ViewingPlayer.IsSkipped ? "is-skipped" : "")"
|
||||||
style="margin-bottom: 8px;">
|
style="margin-bottom: 8px;">
|
||||||
|
@if (IsViewingPlayerTurn && !ViewingPlayer.HasFinished && !ViewingPlayer.IsEliminated)
|
||||||
|
{
|
||||||
|
<div class="turn-indicator-badge">
|
||||||
|
<span class="turn-indicator-dot"></span>
|
||||||
|
<span class="turn-indicator-text">SI NA ŤAHU</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div style="display: flex; align-items: center; gap: 8px;">
|
<div style="display: flex; align-items: center; gap: 8px;">
|
||||||
<span class="avatar-icon">@ViewingPlayer.Avatar</span>
|
<span class="avatar-icon">@ViewingPlayer.Avatar</span>
|
||||||
<span class="avatar-name">@ViewingPlayer.Name</span>
|
<span class="avatar-name">@ViewingPlayer.Name</span>
|
||||||
<span class="avatar-card-count">(@ViewingPlayer.Hand.Count kariet)</span>
|
<span class="avatar-card-count">(@ViewingPlayer.Hand.Count @(ViewingPlayer.Hand.Count == 1 ? "karta" : (ViewingPlayer.Hand.Count < 5 ? "karty" : "kariet")))</span>
|
||||||
@if (Engine.State.Rules.OlympicMode)
|
@if (Engine.State.Rules.OlympicMode)
|
||||||
{
|
{
|
||||||
<span style="font-size: 0.75rem; color: var(--gold); background: rgba(0,0,0,0.4); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(245, 176, 65, 0.4);">
|
<span style="font-size: 0.75rem; color: var(--gold); background: rgba(0,0,0,0.4); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(245, 176, 65, 0.4);">
|
||||||
@@ -211,6 +218,7 @@ else
|
|||||||
|
|
||||||
private List<Player> ConfiguredPlayers = new();
|
private List<Player> ConfiguredPlayers = new();
|
||||||
private RuleOptions ConfiguredRules = new();
|
private RuleOptions ConfiguredRules = new();
|
||||||
|
private Player? _previousTurnPlayer;
|
||||||
|
|
||||||
private Player ViewingPlayer
|
private Player ViewingPlayer
|
||||||
{
|
{
|
||||||
@@ -260,6 +268,7 @@ else
|
|||||||
Engine.OnGameEvent += HandleGameEvent;
|
Engine.OnGameEvent += HandleGameEvent;
|
||||||
Engine.StartGame(gamePlayers, ConfiguredRules);
|
Engine.StartGame(gamePlayers, ConfiguredRules);
|
||||||
|
|
||||||
|
_previousTurnPlayer = Engine.State.CurrentPlayer;
|
||||||
IsInLobby = false;
|
IsInLobby = false;
|
||||||
LastEventMessage = "Hra začala! Veľa šťastia.";
|
LastEventMessage = "Hra začala! Veľa šťastia.";
|
||||||
|
|
||||||
@@ -282,7 +291,7 @@ else
|
|||||||
await Fx.PlayCardDrawAsync();
|
await Fx.PlayCardDrawAsync();
|
||||||
break;
|
break;
|
||||||
case GameEventType.SevenStacked:
|
case GameEventType.SevenStacked:
|
||||||
await Fx.PlaySevenStackAsync(Math.Max(1, e.ExtraData / 2));
|
await Fx.PlaySevenStackAsync(Math.Max(1, e.ExtraData / 3));
|
||||||
await Fx.TriggerDopamineBannerAsync($"🔥 +{e.ExtraData} KARIET!", "Trestná reťaz sediem", "triple");
|
await Fx.TriggerDopamineBannerAsync($"🔥 +{e.ExtraData} KARIET!", "Trestná reťaz sediem", "triple");
|
||||||
break;
|
break;
|
||||||
case GameEventType.AceSkipped:
|
case GameEventType.AceSkipped:
|
||||||
@@ -297,7 +306,6 @@ else
|
|||||||
break;
|
break;
|
||||||
case GameEventType.CardsBurned:
|
case GameEventType.CardsBurned:
|
||||||
await Fx.PlayBurnExplosionAsync();
|
await Fx.PlayBurnExplosionAsync();
|
||||||
await Fx.TriggerDopamineBannerAsync("💥 SPÁLENÉ!", "Zhoda 4 kariet na stole", "quad");
|
|
||||||
break;
|
break;
|
||||||
case GameEventType.PlayerFinished:
|
case GameEventType.PlayerFinished:
|
||||||
case GameEventType.MatchWon:
|
case GameEventType.MatchWon:
|
||||||
@@ -311,10 +319,27 @@ else
|
|||||||
await Fx.PlayHeartbeatAsync();
|
await Fx.PlayHeartbeatAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await CheckTurnTransitionAsync();
|
||||||
StateHasChanged();
|
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)
|
private string GetPlayerPosition(Player player)
|
||||||
{
|
{
|
||||||
if (player == ViewingPlayer) return "bottom";
|
if (player == ViewingPlayer) return "bottom";
|
||||||
|
|||||||
@@ -114,6 +114,15 @@ public class FxService
|
|||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async ValueTask AnimateTurnTransitionAsync(string fromPosition, string toPosition)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _js.InvokeVoidAsync("FaraonFx.animateTurnTransition", fromPosition, toPosition);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
public async ValueTask PlaySelectStepAsync(int step)
|
public async ValueTask PlaySelectStepAsync(int step)
|
||||||
{
|
{
|
||||||
try { await _js.InvokeVoidAsync("FaraonFx.playSelectStep", step); } catch { }
|
try { await _js.InvokeVoidAsync("FaraonFx.playSelectStep", step); } catch { }
|
||||||
|
|||||||
@@ -190,6 +190,26 @@ body, html {
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.player-slot.active-turn-slot::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 140%;
|
||||||
|
height: 140%;
|
||||||
|
background: radial-gradient(ellipse at center, rgba(245, 176, 65, 0.28) 0%, rgba(245, 176, 65, 0.08) 55%, transparent 75%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: aura-breathe 2.2s infinite ease-in-out alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes aura-breathe {
|
||||||
|
0% { transform: translate(-50%, -50%) scale(0.92); opacity: 0.6; }
|
||||||
|
100% { transform: translate(-50%, -50%) scale(1.15); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
.slot-top {
|
.slot-top {
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
@@ -208,6 +228,22 @@ body, html {
|
|||||||
.slot-bottom {
|
.slot-bottom {
|
||||||
grid-row: 3;
|
grid-row: 3;
|
||||||
grid-column: 1 / span 3;
|
grid-column: 1 / span 3;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot-bottom.active-turn-slot::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 320px;
|
||||||
|
height: 120px;
|
||||||
|
background: radial-gradient(ellipse at center, rgba(245, 176, 65, 0.22) 0%, rgba(245, 176, 65, 0.05) 50%, transparent 75%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: aura-breathe 2.2s infinite ease-in-out alternate;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slot-center {
|
.slot-center {
|
||||||
@@ -219,6 +255,35 @@ body, html {
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TURN HEADER INFO */
|
||||||
|
.turn-header-info {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #a3b899;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-turn-header-player {
|
||||||
|
color: var(--gold);
|
||||||
|
font-weight: 900;
|
||||||
|
text-shadow: 0 0 10px rgba(245, 176, 65, 0.8);
|
||||||
|
background: rgba(245, 176, 65, 0.15);
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid rgba(245, 176, 65, 0.45);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
animation: header-pulse 1.8s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes header-pulse {
|
||||||
|
0% { box-shadow: 0 0 4px rgba(245, 176, 65, 0.3); }
|
||||||
|
100% { box-shadow: 0 0 16px rgba(245, 176, 65, 0.8); }
|
||||||
|
}
|
||||||
|
|
||||||
/* AVATAR BADGE */
|
/* AVATAR BADGE */
|
||||||
.avatar-badge {
|
.avatar-badge {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -230,19 +295,92 @@ body, html {
|
|||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-badge.active-turn {
|
.avatar-badge.active-turn {
|
||||||
border-color: var(--gold);
|
border-color: var(--gold);
|
||||||
box-shadow: 0 0 20px rgba(245, 176, 65, 0.8), inset 0 0 10px rgba(245, 176, 65, 0.3);
|
box-shadow: 0 0 25px rgba(245, 176, 65, 0.9), inset 0 0 12px rgba(245, 176, 65, 0.35);
|
||||||
transform: scale(1.08);
|
transform: scale(1.09);
|
||||||
animation: turn-pulse 1.8s infinite;
|
animation: turn-pulse 1.8s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes turn-pulse {
|
@keyframes turn-pulse {
|
||||||
0%, 100% { box-shadow: 0 0 15px rgba(245, 176, 65, 0.7); }
|
0%, 100% { box-shadow: 0 0 16px rgba(245, 176, 65, 0.7); }
|
||||||
50% { box-shadow: 0 0 25px rgba(245, 176, 65, 1), 0 0 40px rgba(245, 176, 65, 0.5); }
|
50% { box-shadow: 0 0 30px rgba(245, 176, 65, 1), 0 0 45px rgba(245, 176, 65, 0.55); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TURN INDICATOR BADGE (NA ŤAHU) */
|
||||||
|
.turn-indicator-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -14px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
background: linear-gradient(135deg, #ffd700, #f5b041);
|
||||||
|
color: #11281d;
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: 0.8px;
|
||||||
|
padding: 2px 9px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6), 0 0 14px rgba(255, 215, 0, 0.8);
|
||||||
|
border: 1.5px solid #ffffff;
|
||||||
|
white-space: nowrap;
|
||||||
|
z-index: 20;
|
||||||
|
animation: turn-badge-float 1.8s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes turn-badge-float {
|
||||||
|
0% { transform: translateX(-50%) translateY(0); }
|
||||||
|
100% { transform: translateX(-50%) translateY(-3px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.turn-indicator-dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #0d3824;
|
||||||
|
box-shadow: 0 0 4px #0d3824;
|
||||||
|
animation: dot-blink 1s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dot-blink {
|
||||||
|
0% { opacity: 0.4; transform: scale(0.75); }
|
||||||
|
100% { opacity: 1; transform: scale(1.25); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TURN ARRIVAL EFFECT */
|
||||||
|
.turn-arrived-pulse {
|
||||||
|
animation: turn-arrival 0.55s cubic-bezier(0.175, 0.885, 0.32, 1.275) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes turn-arrival {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
40% { transform: scale(1.18); box-shadow: 0 0 35px rgba(245, 176, 65, 1), 0 0 55px rgba(255, 215, 0, 0.8); }
|
||||||
|
100% { transform: scale(1.09); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TURN TRANSFER ORB */
|
||||||
|
.turn-transfer-orb {
|
||||||
|
position: fixed;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle at 35% 35%, #ffffff, #ffd700 45%, #ff8c00 85%, transparent);
|
||||||
|
box-shadow: 0 0 18px rgba(255, 215, 0, 0.95), 0 0 36px rgba(245, 176, 65, 0.7);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 998;
|
||||||
|
filter: drop-shadow(0 0 10px rgba(245, 176, 65, 0.85));
|
||||||
|
animation: orb-glow 0.38s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes orb-glow {
|
||||||
|
0% { transform: translate(-50%, -50%) scale(0.9); }
|
||||||
|
100% { transform: translate(-50%, -50%) scale(1.25); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-icon {
|
.avatar-icon {
|
||||||
|
|||||||
@@ -461,6 +461,130 @@ window.FaraonFx = (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- TURN TRANSITION ANIMATION ---
|
||||||
|
function getSlotElement(pos) {
|
||||||
|
if (!pos) return null;
|
||||||
|
const normalized = pos.replace('slot-', '');
|
||||||
|
if (normalized === 'bottom') {
|
||||||
|
return document.querySelector('.slot-bottom .avatar-badge') || document.querySelector('.slot-bottom');
|
||||||
|
}
|
||||||
|
return document.querySelector('.slot-' + normalized + ' .avatar-badge') || document.querySelector('.slot-' + normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playTurnPassSound() {
|
||||||
|
if (isMuted) return;
|
||||||
|
const ctx = getAudioContext();
|
||||||
|
if (!ctx) return;
|
||||||
|
const now = ctx.currentTime;
|
||||||
|
|
||||||
|
const osc = ctx.createOscillator();
|
||||||
|
const gain = ctx.createGain();
|
||||||
|
osc.type = 'sine';
|
||||||
|
osc.frequency.setValueAtTime(540, now);
|
||||||
|
osc.frequency.exponentialRampToValueAtTime(820, now + 0.11);
|
||||||
|
|
||||||
|
gain.gain.setValueAtTime(0.07, now);
|
||||||
|
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.13);
|
||||||
|
|
||||||
|
osc.connect(gain);
|
||||||
|
gain.connect(ctx.destination);
|
||||||
|
osc.start(now);
|
||||||
|
osc.stop(now + 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
function animateTurnTransition(fromPosition, toPosition) {
|
||||||
|
if (!fromPosition || !toPosition || fromPosition === toPosition) return Promise.resolve();
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const fromElem = getSlotElement(fromPosition);
|
||||||
|
const toElem = getSlotElement(toPosition);
|
||||||
|
|
||||||
|
const startPos = fromElem ? getElementCenter(fromElem) : getPositionCoords(fromPosition);
|
||||||
|
const endPos = toElem ? getElementCenter(toElem) : getPositionCoords(toPosition);
|
||||||
|
|
||||||
|
const container = document.getElementById('game-table-container') || document.body;
|
||||||
|
|
||||||
|
const orb = document.createElement('div');
|
||||||
|
orb.className = 'turn-transfer-orb';
|
||||||
|
orb.style.left = `${startPos.x}px`;
|
||||||
|
orb.style.top = `${startPos.y}px`;
|
||||||
|
container.appendChild(orb);
|
||||||
|
|
||||||
|
const midX = (startPos.x + endPos.x) / 2;
|
||||||
|
const midY = Math.min(startPos.y, endPos.y) - 35;
|
||||||
|
|
||||||
|
const duration = 380;
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
|
function step(currentTime) {
|
||||||
|
const elapsed = currentTime - startTime;
|
||||||
|
const progress = Math.min(elapsed / duration, 1);
|
||||||
|
const t = 1 - Math.pow(1 - progress, 3); // Ease out cubic
|
||||||
|
|
||||||
|
// Quadratic Bezier curve
|
||||||
|
const currentX = (1 - t) * (1 - t) * startPos.x + 2 * (1 - t) * t * midX + t * t * endPos.x;
|
||||||
|
const currentY = (1 - t) * (1 - t) * startPos.y + 2 * (1 - t) * t * midY + t * t * endPos.y;
|
||||||
|
|
||||||
|
orb.style.left = `${currentX}px`;
|
||||||
|
orb.style.top = `${currentY}px`;
|
||||||
|
|
||||||
|
if (particleCanvas && Math.random() < 0.4) {
|
||||||
|
particles.push({
|
||||||
|
x: currentX,
|
||||||
|
y: currentY,
|
||||||
|
vx: (Math.random() - 0.5) * 1.5,
|
||||||
|
vy: (Math.random() - 0.5) * 1.5,
|
||||||
|
gravity: 0,
|
||||||
|
size: 3 + Math.random() * 3,
|
||||||
|
color: Math.random() > 0.4 ? '#ffd700' : '#f5b041',
|
||||||
|
shape: 'spark',
|
||||||
|
life: 0.6,
|
||||||
|
decay: 0.04,
|
||||||
|
rotation: Math.random() * Math.PI
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
requestAnimationFrame(step);
|
||||||
|
} else {
|
||||||
|
orb.remove();
|
||||||
|
|
||||||
|
if (toElem) {
|
||||||
|
toElem.classList.remove('turn-arrived-pulse');
|
||||||
|
void toElem.offsetWidth;
|
||||||
|
toElem.classList.add('turn-arrived-pulse');
|
||||||
|
setTimeout(() => toElem.classList.remove('turn-arrived-pulse'), 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (particleCanvas) {
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
const angle = Math.random() * Math.PI * 2;
|
||||||
|
const spd = 1.5 + Math.random() * 2.5;
|
||||||
|
particles.push({
|
||||||
|
x: endPos.x,
|
||||||
|
y: endPos.y,
|
||||||
|
vx: Math.cos(angle) * spd,
|
||||||
|
vy: Math.sin(angle) * spd,
|
||||||
|
gravity: 0.02,
|
||||||
|
size: 3 + Math.random() * 3,
|
||||||
|
color: '#ffd700',
|
||||||
|
shape: 'spark',
|
||||||
|
life: 0.7,
|
||||||
|
decay: 0.035,
|
||||||
|
rotation: Math.random() * Math.PI
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playTurnPassSound();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(step);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- PARTICLE ENGINE ---
|
// --- PARTICLE ENGINE ---
|
||||||
function initParticles(canvasId) {
|
function initParticles(canvasId) {
|
||||||
particleCanvas = document.getElementById(canvasId);
|
particleCanvas = document.getElementById(canvasId);
|
||||||
@@ -912,6 +1036,7 @@ window.FaraonFx = (function () {
|
|||||||
triggerGoldStarExplosion,
|
triggerGoldStarExplosion,
|
||||||
animateCardPlay,
|
animateCardPlay,
|
||||||
animateCardDraw,
|
animateCardDraw,
|
||||||
|
animateTurnTransition,
|
||||||
toggleMute
|
toggleMute
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ public class GameEngine
|
|||||||
// Efekt Sediem
|
// Efekt Sediem
|
||||||
else if (firstRank == Rank.Seven)
|
else if (firstRank == Rank.Seven)
|
||||||
{
|
{
|
||||||
int addedPenalty = cards.Count * 2;
|
int addedPenalty = cards.Count * 3;
|
||||||
State.PendingDrawCount += addedPenalty;
|
State.PendingDrawCount += addedPenalty;
|
||||||
|
|
||||||
var redSeven = cards.FirstOrDefault(c => c.IsRedSeven);
|
var redSeven = cards.FirstOrDefault(c => c.IsRedSeven);
|
||||||
@@ -325,7 +325,7 @@ public class GameEngine
|
|||||||
// Efekt Sedmy
|
// Efekt Sedmy
|
||||||
if (card.IsSeven)
|
if (card.IsSeven)
|
||||||
{
|
{
|
||||||
State.PendingDrawCount += 2;
|
State.PendingDrawCount += 3;
|
||||||
|
|
||||||
// Červená sedma vracia do hry
|
// Červená sedma vracia do hry
|
||||||
if (card.IsRedSeven && State.Rules.RedSevenRevives && State.LastFinishedPlayer != null && State.LastFinishedPlayer != player)
|
if (card.IsRedSeven && State.Rules.RedSevenRevives && State.LastFinishedPlayer != null && State.LastFinishedPlayer != player)
|
||||||
@@ -353,7 +353,7 @@ public class GameEngine
|
|||||||
player,
|
player,
|
||||||
Card: card,
|
Card: card,
|
||||||
ExtraData: State.PendingDrawCount,
|
ExtraData: State.PendingDrawCount,
|
||||||
Message: $"⚡ Sedma! Ďalší hráč berie {State.PendingDrawCount} karty."
|
Message: $"⚡ Sedma! Ďalší hráč berie {State.PendingDrawCount} {(State.PendingDrawCount < 5 ? "karty" : "kariet")}."
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public class RuleOptions
|
|||||||
public int InitialCardCount { get; set; } = 5;
|
public int InitialCardCount { get; set; } = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ak je zapnuté, sedma na sedmu pridáva ďalšie +2 karty (kumulácia trestu).
|
/// Ak je zapnuté, sedma na sedmu pridáva ďalšie +3 karty (kumulácia trestu).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool StackSevens { get; set; } = true;
|
public bool StackSevens { get; set; } = true;
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class GameEngineTests
|
|||||||
|
|
||||||
var played = engine.PlayCard(p1, sevenCard);
|
var played = engine.PlayCard(p1, sevenCard);
|
||||||
Assert.True(played);
|
Assert.True(played);
|
||||||
Assert.Equal(2, engine.State.PendingDrawCount);
|
Assert.Equal(3, engine.State.PendingDrawCount);
|
||||||
Assert.Equal(p2, engine.State.CurrentPlayer);
|
Assert.Equal(p2, engine.State.CurrentPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ public class GameEngineTests
|
|||||||
|
|
||||||
var played = engine.PlayCards(p1, sevens);
|
var played = engine.PlayCards(p1, sevens);
|
||||||
Assert.True(played);
|
Assert.True(played);
|
||||||
Assert.Equal(4, engine.State.PendingDrawCount); // 2x sedma = +4 karty
|
Assert.Equal(6, engine.State.PendingDrawCount); // 2x sedma = +6 kariet
|
||||||
Assert.Equal(p2, engine.State.CurrentPlayer);
|
Assert.Equal(p2, engine.State.CurrentPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user