improvements
This commit is contained in:
@@ -156,6 +156,26 @@ body, html {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.btn-speed-control {
|
||||
width: auto !important;
|
||||
min-width: 60px;
|
||||
padding: 0 10px;
|
||||
border-radius: 20px !important;
|
||||
font-size: 0.8rem !important;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.3px;
|
||||
background: rgba(245, 176, 65, 0.15) !important;
|
||||
border-color: rgba(245, 176, 65, 0.45) !important;
|
||||
color: var(--gold) !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-speed-control:hover {
|
||||
background: rgba(245, 176, 65, 0.35) !important;
|
||||
border-color: var(--gold) !important;
|
||||
box-shadow: 0 0 10px rgba(245, 176, 65, 0.4);
|
||||
}
|
||||
|
||||
/* TABLE FELT & SLOTS */
|
||||
.poker-table-surface {
|
||||
flex: 1;
|
||||
@@ -405,14 +425,229 @@ body, html {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* FROZEN / ICE STATUS STYLES */
|
||||
.avatar-badge.is-frozen,
|
||||
.avatar-badge.is-skipped {
|
||||
border-color: var(--ice-blue);
|
||||
box-shadow: 0 0 15px var(--ice-blue);
|
||||
background: linear-gradient(135deg, rgba(0, 229, 255, 0.45) 0%, rgba(255, 255, 255, 0.25) 45%, rgba(0, 150, 255, 0.4) 100%) !important;
|
||||
border-color: #00e5ff !important;
|
||||
box-shadow: 0 0 26px rgba(0, 229, 255, 0.95), inset 0 0 16px rgba(255, 255, 255, 0.7) !important;
|
||||
backdrop-filter: blur(8px) brightness(1.25) !important;
|
||||
transform: scale(1.08);
|
||||
overflow: visible;
|
||||
animation: freeze-pulse 2s infinite ease-in-out alternate;
|
||||
}
|
||||
|
||||
@keyframes freeze-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 18px rgba(0, 229, 255, 0.75), inset 0 0 12px rgba(255, 255, 255, 0.45);
|
||||
border-color: #00e5ff;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 35px rgba(0, 229, 255, 1), 0 0 55px rgba(0, 229, 255, 0.5), inset 0 0 20px rgba(255, 255, 255, 0.85);
|
||||
border-color: #e0ffff;
|
||||
}
|
||||
}
|
||||
|
||||
/* FROZEN ICE AURA (SLOT) */
|
||||
.player-slot.frozen-slot::before,
|
||||
.slot-bottom.frozen-slot::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 150%;
|
||||
height: 150%;
|
||||
background: radial-gradient(ellipse at center, rgba(0, 229, 255, 0.38) 0%, rgba(0, 229, 255, 0.12) 50%, transparent 75%) !important;
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
border-radius: 50%;
|
||||
animation: ice-mist 2.4s infinite ease-in-out alternate !important;
|
||||
}
|
||||
|
||||
@keyframes ice-mist {
|
||||
0% { transform: translate(-50%, -50%) scale(0.92); opacity: 0.7; }
|
||||
100% { transform: translate(-50%, -50%) scale(1.18); opacity: 1; filter: drop-shadow(0 0 18px rgba(0, 229, 255, 0.6)); }
|
||||
}
|
||||
|
||||
/* FROZEN BADGE (ZMRAZENÝ) */
|
||||
.frozen-indicator-badge {
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
background: linear-gradient(135deg, #00e5ff, #80d8ff, #ffffff);
|
||||
color: #003040;
|
||||
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 18px rgba(0, 229, 255, 0.9);
|
||||
border: 1.5px solid #ffffff;
|
||||
white-space: nowrap;
|
||||
z-index: 25;
|
||||
animation: frozen-badge-float 2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes frozen-badge-float {
|
||||
0% { transform: translateX(-50%) translateY(0); }
|
||||
100% { transform: translateX(-50%) translateY(-3px); }
|
||||
}
|
||||
|
||||
.frozen-indicator-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background-color: #00b0ff;
|
||||
box-shadow: 0 0 6px #00e5ff;
|
||||
animation: dot-blink 0.8s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
/* ICICLES HANGING UNDER FROZEN BADGE */
|
||||
.icicles-row {
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
height: 12px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
pointer-events: none;
|
||||
z-index: 22;
|
||||
}
|
||||
|
||||
.icicle {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 3.5px solid transparent;
|
||||
border-right: 3.5px solid transparent;
|
||||
border-top: 9px solid rgba(224, 247, 250, 0.92);
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 229, 255, 0.85));
|
||||
}
|
||||
|
||||
.icicle:nth-child(even) {
|
||||
border-top-width: 13px;
|
||||
border-left-width: 4.5px;
|
||||
border-right-width: 4.5px;
|
||||
}
|
||||
|
||||
.icicle:nth-child(3n) {
|
||||
border-top-width: 7px;
|
||||
border-left-width: 2.5px;
|
||||
border-right-width: 2.5px;
|
||||
}
|
||||
|
||||
/* DISCARD PILE ACE FREEZE BADGE */
|
||||
.ace-freeze-badge {
|
||||
position: absolute;
|
||||
top: -18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(135deg, #00e5ff, #0091ea, #00b0ff);
|
||||
color: #ffffff;
|
||||
font-weight: 900;
|
||||
font-size: 0.82rem;
|
||||
padding: 3px 12px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 0 22px rgba(0, 229, 255, 0.95), 0 4px 12px rgba(0, 0, 0, 0.7);
|
||||
border: 2px solid #ffffff;
|
||||
white-space: nowrap;
|
||||
z-index: 25;
|
||||
animation: ace-freeze-pulse 1.6s infinite alternate;
|
||||
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
@keyframes ace-freeze-pulse {
|
||||
0% { transform: translateX(-50%) scale(0.95); box-shadow: 0 0 16px rgba(0, 229, 255, 0.75); }
|
||||
100% { transform: translateX(-50%) scale(1.06); box-shadow: 0 0 32px rgba(0, 229, 255, 1); }
|
||||
}
|
||||
|
||||
/* DISCARD PILE GREEN WILDCARD BADGE */
|
||||
.green-wildcard-badge {
|
||||
position: absolute;
|
||||
top: -18px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(135deg, #1b4332, #2d6a4f, #52b788);
|
||||
color: #ffffff;
|
||||
font-weight: 900;
|
||||
font-size: 0.82rem;
|
||||
padding: 3px 12px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 0 20px rgba(82, 183, 136, 0.9), 0 4px 12px rgba(0, 0, 0, 0.7);
|
||||
border: 2px solid #b7e4c7;
|
||||
white-space: nowrap;
|
||||
z-index: 25;
|
||||
animation: green-wildcard-pulse 1.6s infinite alternate;
|
||||
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
@keyframes green-wildcard-pulse {
|
||||
0% { transform: translateX(-50%) scale(0.95); box-shadow: 0 0 14px rgba(82, 183, 136, 0.7); }
|
||||
100% { transform: translateX(-50%) scale(1.06); box-shadow: 0 0 28px rgba(82, 183, 136, 1); }
|
||||
}
|
||||
|
||||
.avatar-badge.is-finished {
|
||||
opacity: 0.6;
|
||||
filter: grayscale(0.5);
|
||||
opacity: 0.65;
|
||||
filter: grayscale(0.4);
|
||||
}
|
||||
|
||||
.avatar-badge.can-be-revived {
|
||||
opacity: 0.95 !important;
|
||||
filter: none !important;
|
||||
border-color: #ff1744 !important;
|
||||
box-shadow: 0 0 20px rgba(255, 23, 68, 0.7), inset 0 0 10px rgba(255, 23, 68, 0.3) !important;
|
||||
animation: revive-target-pulse 1.4s infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes revive-target-pulse {
|
||||
0% { box-shadow: 0 0 12px rgba(255, 23, 68, 0.5); }
|
||||
100% { box-shadow: 0 0 24px rgba(255, 23, 68, 0.95), 0 0 35px rgba(255, 82, 82, 0.4); }
|
||||
}
|
||||
|
||||
/* REVIVE COUNTDOWN BADGE */
|
||||
.revive-countdown-badge {
|
||||
position: absolute;
|
||||
bottom: -13px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(135deg, #b71c1c, #d50000, #ff1744);
|
||||
color: #ffffff;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 2px 9px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 0 14px rgba(255, 23, 68, 0.85), 0 2px 6px rgba(0, 0, 0, 0.6);
|
||||
border: 1.5px solid #ff8a80;
|
||||
white-space: nowrap;
|
||||
z-index: 25;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
animation: heartbeat-glow 1.2s infinite ease-in-out alternate;
|
||||
}
|
||||
|
||||
@keyframes heartbeat-glow {
|
||||
0% { transform: translateX(-50%) scale(0.96); box-shadow: 0 0 8px rgba(255, 23, 68, 0.6); }
|
||||
100% { transform: translateX(-50%) scale(1.05); box-shadow: 0 0 18px rgba(255, 23, 68, 1); }
|
||||
}
|
||||
|
||||
.heartbeat-icon {
|
||||
display: inline-block;
|
||||
animation: heart-beat 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes heart-beat {
|
||||
0% { transform: scale(1); }
|
||||
15% { transform: scale(1.35); }
|
||||
30% { transform: scale(1); }
|
||||
45% { transform: scale(1.25); }
|
||||
60% { transform: scale(1); }
|
||||
}
|
||||
|
||||
.badge-finished-pos {
|
||||
|
||||
@@ -3,11 +3,23 @@
|
||||
window.FaraonFx = (function () {
|
||||
let audioCtx = null;
|
||||
let isMuted = false;
|
||||
let speedMultiplier = 1.0;
|
||||
let particleCanvas = null;
|
||||
let particleCtx = null;
|
||||
let particles = [];
|
||||
let animationFrameId = null;
|
||||
|
||||
function setSpeedLevel(level) {
|
||||
switch (level) {
|
||||
case 1: speedMultiplier = 0.7; break; // 0.5x
|
||||
case 2: speedMultiplier = 0.85; break; // 0.75x
|
||||
case 4: speedMultiplier = 1.5; break; // 1.5x
|
||||
case 5: speedMultiplier = 2.4; break; // 2.5x Turbo
|
||||
case 3:
|
||||
default: speedMultiplier = 1.0; break;
|
||||
}
|
||||
}
|
||||
|
||||
function getAudioContext() {
|
||||
if (!audioCtx) {
|
||||
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
@@ -168,6 +180,79 @@ window.FaraonFx = (function () {
|
||||
triggerScreenShake(3);
|
||||
}
|
||||
|
||||
function playIceCrack() {
|
||||
if (isMuted) return;
|
||||
const ctx = getAudioContext();
|
||||
if (!ctx) return;
|
||||
|
||||
const now = ctx.currentTime;
|
||||
|
||||
// Sharp ice snap noise
|
||||
const bufferSize = ctx.sampleRate * 0.12;
|
||||
const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
|
||||
const data = buffer.getChannelData(0);
|
||||
for (let i = 0; i < bufferSize; i++) {
|
||||
data[i] = (Math.random() * 2 - 1) * Math.exp(-i / (ctx.sampleRate * 0.02));
|
||||
}
|
||||
|
||||
const noise = ctx.createBufferSource();
|
||||
noise.buffer = buffer;
|
||||
|
||||
const filter = ctx.createBiquadFilter();
|
||||
filter.type = 'highpass';
|
||||
filter.frequency.setValueAtTime(3200, now);
|
||||
|
||||
const gain = ctx.createGain();
|
||||
gain.gain.setValueAtTime(0.45, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.12);
|
||||
|
||||
noise.connect(filter);
|
||||
filter.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
noise.start(now);
|
||||
|
||||
// Resonant high crystal ringing
|
||||
[2400, 3600, 4800].forEach((freq, idx) => {
|
||||
const osc = ctx.createOscillator();
|
||||
const oscGain = ctx.createGain();
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(freq + Math.random() * 200, now + idx * 0.02);
|
||||
oscGain.gain.setValueAtTime(0.12, now + idx * 0.02);
|
||||
oscGain.gain.exponentialRampToValueAtTime(0.001, now + idx * 0.02 + 0.22);
|
||||
|
||||
osc.connect(oscGain);
|
||||
oscGain.connect(ctx.destination);
|
||||
osc.start(now + idx * 0.02);
|
||||
osc.stop(now + idx * 0.02 + 0.23);
|
||||
});
|
||||
|
||||
triggerScreenShake(4);
|
||||
}
|
||||
|
||||
function playLeafShield() {
|
||||
if (isMuted) return;
|
||||
const ctx = getAudioContext();
|
||||
if (!ctx) return;
|
||||
|
||||
const now = ctx.currentTime;
|
||||
[523.25, 659.25, 783.99, 1046.50].forEach((freq, idx) => {
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
osc.type = 'triangle';
|
||||
osc.frequency.setValueAtTime(freq, now + idx * 0.05);
|
||||
|
||||
gain.gain.setValueAtTime(0.2, now + idx * 0.05);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, now + idx * 0.05 + 0.35);
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
osc.start(now + idx * 0.05);
|
||||
osc.stop(now + idx * 0.05 + 0.36);
|
||||
});
|
||||
|
||||
triggerScreenShake(2);
|
||||
}
|
||||
|
||||
function playHornikChime() {
|
||||
if (isMuted) return;
|
||||
const ctx = getAudioContext();
|
||||
@@ -386,7 +471,7 @@ window.FaraonFx = (function () {
|
||||
opacity: 1
|
||||
}
|
||||
], {
|
||||
duration: 420,
|
||||
duration: Math.round(420 / speedMultiplier),
|
||||
easing: 'cubic-bezier(0.175, 0.885, 0.32, 1.2)'
|
||||
});
|
||||
|
||||
@@ -429,7 +514,7 @@ window.FaraonFx = (function () {
|
||||
{ transform: `translate(-50%, -50%) translate3d(${dx * 0.5}px, ${dy * 0.5 - 30}px, 0) scale(1.15) rotate(10deg)`, opacity: 1, offset: 0.5 },
|
||||
{ transform: `translate(-50%, -50%) translate3d(${dx}px, ${dy}px, 0) scale(0.6) rotate(0deg)`, opacity: 0.2 }
|
||||
], {
|
||||
duration: 380,
|
||||
duration: Math.round(380 / speedMultiplier),
|
||||
easing: 'cubic-bezier(0.25, 1, 0.5, 1)'
|
||||
});
|
||||
|
||||
@@ -513,7 +598,7 @@ window.FaraonFx = (function () {
|
||||
const midX = (startPos.x + endPos.x) / 2;
|
||||
const midY = Math.min(startPos.y, endPos.y) - 35;
|
||||
|
||||
const duration = 380;
|
||||
const duration = Math.round(380 / speedMultiplier);
|
||||
const startTime = performance.now();
|
||||
|
||||
function step(currentTime) {
|
||||
@@ -693,6 +778,71 @@ window.FaraonFx = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
function triggerIceShatter(position) {
|
||||
if (!particleCanvas) return;
|
||||
let x = window.innerWidth / 2;
|
||||
let y = window.innerHeight / 2;
|
||||
|
||||
if (typeof position === 'string') {
|
||||
const elem = getSlotElement(position);
|
||||
const coords = elem ? getElementCenter(elem) : getPositionCoords(position);
|
||||
x = coords.x;
|
||||
y = coords.y;
|
||||
} else if (position && position.x && position.y) {
|
||||
x = position.x;
|
||||
y = position.y;
|
||||
}
|
||||
|
||||
playIceCrack();
|
||||
|
||||
const colors = ['#00ffff', '#80d8ff', '#e0f7fa', '#ffffff', '#b2ebf2', '#87cefa'];
|
||||
for (let i = 0; i < 40; i++) {
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const speed = 3 + Math.random() * 10;
|
||||
particles.push({
|
||||
x: x + (Math.random() - 0.5) * 50,
|
||||
y: y + (Math.random() - 0.5) * 40,
|
||||
vx: Math.cos(angle) * speed,
|
||||
vy: Math.sin(angle) * speed - 2.5,
|
||||
gravity: 0.22,
|
||||
size: 6 + Math.random() * 9,
|
||||
aspect: 0.35 + Math.random() * 1.5,
|
||||
color: colors[Math.floor(Math.random() * colors.length)],
|
||||
shape: Math.random() > 0.35 ? 'rect' : 'spark',
|
||||
life: 1.1,
|
||||
decay: 0.022 + Math.random() * 0.02,
|
||||
rotation: Math.random() * Math.PI * 2,
|
||||
vRot: (Math.random() - 0.5) * 0.4
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function triggerLeafParticles(x, y, count = 35) {
|
||||
if (!particleCanvas) return;
|
||||
const startX = x || window.innerWidth / 2;
|
||||
const startY = y || window.innerHeight / 2;
|
||||
const colors = ['#2d6a4f', '#52b788', '#74c69d', '#b7e4c7', '#d8f3dc', '#ffd700'];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const angle = Math.random() * Math.PI * 2;
|
||||
const speed = 2.5 + Math.random() * 8;
|
||||
particles.push({
|
||||
x: startX + (Math.random() - 0.5) * 30,
|
||||
y: startY + (Math.random() - 0.5) * 30,
|
||||
vx: Math.cos(angle) * speed,
|
||||
vy: Math.sin(angle) * speed - 1.5,
|
||||
gravity: 0.12,
|
||||
size: 5 + Math.random() * 7,
|
||||
aspect: 0.4 + Math.random() * 1.5,
|
||||
color: colors[Math.floor(Math.random() * colors.length)],
|
||||
shape: 'spark',
|
||||
life: 1.0,
|
||||
decay: 0.025 + Math.random() * 0.02,
|
||||
rotation: Math.random() * Math.PI * 2,
|
||||
vRot: (Math.random() - 0.5) * 0.3
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function triggerBurnExplosion(x, y) {
|
||||
if (!particleCanvas) return;
|
||||
const colors = ['#ff0000', '#ff4500', '#ffaa00', '#333333', '#ffffff'];
|
||||
@@ -1016,6 +1166,8 @@ window.FaraonFx = (function () {
|
||||
playCardDraw,
|
||||
playSevenStack,
|
||||
playAceFreeze,
|
||||
playIceCrack,
|
||||
playLeafShield,
|
||||
playHornikChime,
|
||||
playBurnExplosion,
|
||||
playVictory,
|
||||
@@ -1029,6 +1181,8 @@ window.FaraonFx = (function () {
|
||||
initParticles,
|
||||
triggerFireParticles,
|
||||
triggerIceParticles,
|
||||
triggerIceShatter,
|
||||
triggerLeafParticles,
|
||||
triggerBurnExplosion,
|
||||
triggerConfetti,
|
||||
triggerCoinJackpot,
|
||||
@@ -1037,6 +1191,7 @@ window.FaraonFx = (function () {
|
||||
animateCardPlay,
|
||||
animateCardDraw,
|
||||
animateTurnTransition,
|
||||
setSpeedLevel,
|
||||
toggleMute
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user