123
This commit is contained in:
119
public/game.js
119
public/game.js
@ -411,32 +411,139 @@
|
|||||||
const attackerWrap = document.querySelector(`[data-minion-id="${lastLog.attackerMinionId}"]`);
|
const attackerWrap = document.querySelector(`[data-minion-id="${lastLog.attackerMinionId}"]`);
|
||||||
const targetWrap = document.querySelector(`[data-minion-id="${lastLog.targetMinionId}"]`);
|
const targetWrap = document.querySelector(`[data-minion-id="${lastLog.targetMinionId}"]`);
|
||||||
if (!attackerWrap || !targetWrap) return false;
|
if (!attackerWrap || !targetWrap) return false;
|
||||||
attackerWrap.classList.add('combat-lunge');
|
|
||||||
|
// Вычисляем позиции для точного перемещения карты
|
||||||
|
const attackerRect = attackerWrap.getBoundingClientRect();
|
||||||
|
const targetRect = targetWrap.getBoundingClientRect();
|
||||||
|
|
||||||
|
// Вычисляем смещение
|
||||||
|
const dx = targetRect.left + targetRect.width / 2 - (attackerRect.left + attackerRect.width / 2);
|
||||||
|
const dy = targetRect.top + targetRect.height / 2 - (attackerRect.top + attackerRect.height / 2);
|
||||||
|
|
||||||
|
// Сохраняем начальную позицию
|
||||||
|
const startX = attackerRect.left;
|
||||||
|
const startY = attackerRect.top;
|
||||||
|
const startWidth = attackerRect.width;
|
||||||
|
const startHeight = attackerRect.height;
|
||||||
|
|
||||||
|
// Устанавливаем fixed positioning для наложения
|
||||||
|
attackerWrap.style.position = 'fixed';
|
||||||
|
attackerWrap.style.left = startX + 'px';
|
||||||
|
attackerWrap.style.top = startY + 'px';
|
||||||
|
attackerWrap.style.width = startWidth + 'px';
|
||||||
|
attackerWrap.style.height = startHeight + 'px';
|
||||||
|
attackerWrap.style.zIndex = '1000';
|
||||||
|
attackerWrap.style.transition = 'none';
|
||||||
|
|
||||||
|
// Добавляем класс для анимации
|
||||||
|
attackerWrap.classList.add('combat-lunge-overlay');
|
||||||
targetWrap.classList.add('combat-hit');
|
targetWrap.classList.add('combat-hit');
|
||||||
|
|
||||||
|
// Устанавливаем CSS переменные для перемещения
|
||||||
|
attackerWrap.style.setProperty('--attack-dx', dx + 'px');
|
||||||
|
attackerWrap.style.setProperty('--attack-dy', dy + 'px');
|
||||||
|
|
||||||
if (lastLog.damage) showDamageEffect(targetWrap, lastLog.damage, false);
|
if (lastLog.damage) showDamageEffect(targetWrap, lastLog.damage, false);
|
||||||
if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
|
if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
|
||||||
if (lastLog.attackerDied) {
|
|
||||||
|
// Анимация удара
|
||||||
|
setTimeout(() => {
|
||||||
|
targetWrap.classList.add('combat-hit-impact');
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
// Возврат карты и очистка
|
||||||
|
setTimeout(() => {
|
||||||
|
// Если атакующий не умер, возвращаем карту на место
|
||||||
|
if (!lastLog.attackerDied) {
|
||||||
|
attackerWrap.classList.remove('combat-lunge-overlay');
|
||||||
|
attackerWrap.style.position = '';
|
||||||
|
attackerWrap.style.left = '';
|
||||||
|
attackerWrap.style.top = '';
|
||||||
|
attackerWrap.style.width = '';
|
||||||
|
attackerWrap.style.height = '';
|
||||||
|
attackerWrap.style.zIndex = '';
|
||||||
|
attackerWrap.style.setProperty('--attack-dx', '');
|
||||||
|
attackerWrap.style.setProperty('--attack-dy', '');
|
||||||
|
attackerWrap.style.transition = '';
|
||||||
|
} else {
|
||||||
|
// Если атакующий умер, сразу применяем анимацию смерти
|
||||||
|
attackerWrap.classList.remove('combat-lunge-overlay');
|
||||||
|
attackerWrap.style.position = '';
|
||||||
|
attackerWrap.style.left = '';
|
||||||
|
attackerWrap.style.top = '';
|
||||||
|
attackerWrap.style.width = '';
|
||||||
|
attackerWrap.style.height = '';
|
||||||
|
attackerWrap.style.zIndex = '';
|
||||||
|
attackerWrap.style.setProperty('--attack-dx', '');
|
||||||
|
attackerWrap.style.setProperty('--attack-dy', '');
|
||||||
|
attackerWrap.style.transition = '';
|
||||||
attackerWrap.classList.add('combat-death');
|
attackerWrap.classList.add('combat-death');
|
||||||
showDamageEffect(attackerWrap, '💀', false);
|
showDamageEffect(attackerWrap, '💀', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastLog.targetDied) {
|
if (lastLog.targetDied) {
|
||||||
targetWrap.classList.add('combat-death');
|
targetWrap.classList.add('combat-death');
|
||||||
showDamageEffect(targetWrap, '💀', false);
|
showDamageEffect(targetWrap, '💀', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetWrap.classList.remove('combat-hit-impact');
|
||||||
|
}, 500);
|
||||||
|
|
||||||
combatTimeout = setTimeout(function () {
|
combatTimeout = setTimeout(function () {
|
||||||
combatTimeout = null;
|
combatTimeout = null;
|
||||||
renderBoards(state);
|
renderBoards(state);
|
||||||
bindGameEvents(state);
|
bindGameEvents(state);
|
||||||
}, 600);
|
}, 600);
|
||||||
return true;
|
return true;
|
||||||
} else if (lastLog.type === 'attackHero' && lastLog.toPlayer !== undefined) {
|
} else if (lastLog.type === 'attackHero' && lastLog.toPlayer !== undefined && lastLog.attackerMinionId) {
|
||||||
|
const attackerWrap = document.querySelector(`[data-minion-id="${lastLog.attackerMinionId}"]`);
|
||||||
const targetPlayer = state.players[lastLog.toPlayer];
|
const targetPlayer = state.players[lastLog.toPlayer];
|
||||||
if (targetPlayer) {
|
if (attackerWrap && targetPlayer) {
|
||||||
const heroEl = document.querySelector(`[data-player-index="${lastLog.toPlayer}"].hero-target, .opponent-block[data-opponent-index="${lastLog.toPlayer}"] .drop-target-hero`);
|
const heroEl = document.querySelector(`[data-player-index="${lastLog.toPlayer}"].hero-target, .opponent-block[data-opponent-index="${lastLog.toPlayer}"] .drop-target-hero`);
|
||||||
if (heroEl) {
|
if (heroEl) {
|
||||||
heroEl.classList.add('hero-damage-flash');
|
// Вычисляем позиции для атаки по герою
|
||||||
|
const attackerRect = attackerWrap.getBoundingClientRect();
|
||||||
|
const heroRect = heroEl.getBoundingClientRect();
|
||||||
|
|
||||||
|
const dx = heroRect.left + heroRect.width / 2 - (attackerRect.left + attackerRect.width / 2);
|
||||||
|
const dy = heroRect.top + heroRect.height / 2 - (attackerRect.top + attackerRect.height / 2);
|
||||||
|
|
||||||
|
const startX = attackerRect.left;
|
||||||
|
const startY = attackerRect.top;
|
||||||
|
const startWidth = attackerRect.width;
|
||||||
|
const startHeight = attackerRect.height;
|
||||||
|
|
||||||
|
// Устанавливаем fixed positioning
|
||||||
|
attackerWrap.style.position = 'fixed';
|
||||||
|
attackerWrap.style.left = startX + 'px';
|
||||||
|
attackerWrap.style.top = startY + 'px';
|
||||||
|
attackerWrap.style.width = startWidth + 'px';
|
||||||
|
attackerWrap.style.height = startHeight + 'px';
|
||||||
|
attackerWrap.style.zIndex = '1000';
|
||||||
|
attackerWrap.style.transition = 'none';
|
||||||
|
|
||||||
|
attackerWrap.classList.add('combat-lunge-overlay');
|
||||||
|
attackerWrap.style.setProperty('--attack-dx', dx + 'px');
|
||||||
|
attackerWrap.style.setProperty('--attack-dy', dy + 'px');
|
||||||
|
|
||||||
|
heroEl.classList.add('hero-damage-flash', 'combat-hit-impact');
|
||||||
if (lastLog.damage) showDamageEffect(heroEl, lastLog.damage, true);
|
if (lastLog.damage) showDamageEffect(heroEl, lastLog.damage, true);
|
||||||
setTimeout(() => heroEl.classList.remove('hero-damage-flash'), 500);
|
if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
attackerWrap.classList.remove('combat-lunge-overlay');
|
||||||
|
attackerWrap.style.position = '';
|
||||||
|
attackerWrap.style.left = '';
|
||||||
|
attackerWrap.style.top = '';
|
||||||
|
attackerWrap.style.width = '';
|
||||||
|
attackerWrap.style.height = '';
|
||||||
|
attackerWrap.style.zIndex = '';
|
||||||
|
attackerWrap.style.setProperty('--attack-dx', '');
|
||||||
|
attackerWrap.style.setProperty('--attack-dy', '');
|
||||||
|
attackerWrap.style.transition = '';
|
||||||
|
|
||||||
|
heroEl.classList.remove('hero-damage-flash', 'combat-hit-impact');
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -842,6 +842,48 @@ html, body {
|
|||||||
35% { transform: translate(var(--lunge-dx, 30px), var(--lunge-dy, -20px)) scale(1.15); filter: brightness(1.4); }
|
35% { transform: translate(var(--lunge-dx, 30px), var(--lunge-dy, -20px)) scale(1.15); filter: brightness(1.4); }
|
||||||
100% { transform: translate(0, 0) scale(1); filter: brightness(1); }
|
100% { transform: translate(0, 0) scale(1); filter: brightness(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Новая анимация: карта накладывается на цель */
|
||||||
|
.card-wrap.combat-lunge-overlay {
|
||||||
|
animation: combatLungeOverlay 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-wrap.combat-lunge-overlay .card {
|
||||||
|
box-shadow: 0 0 40px rgba(255, 200, 0, 0.6), 0 0 80px rgba(255, 100, 0, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes combatLungeOverlay {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0) scale(1) rotate(0deg);
|
||||||
|
filter: brightness(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: translate(calc(var(--attack-dx, 0) * 0.6), calc(var(--attack-dy, 0) * 0.6)) scale(1.15) rotate(-3deg);
|
||||||
|
filter: brightness(1.4) drop-shadow(0 0 25px rgba(255, 200, 0, 0.7));
|
||||||
|
}
|
||||||
|
45% {
|
||||||
|
transform: translate(var(--attack-dx, 0), var(--attack-dy, 0)) scale(1.3) rotate(0deg);
|
||||||
|
filter: brightness(2) drop-shadow(0 0 40px rgba(255, 100, 0, 1));
|
||||||
|
z-index: 1001;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translate(var(--attack-dx, 0), var(--attack-dy, 0)) scale(1.28) rotate(1deg);
|
||||||
|
filter: brightness(1.9) drop-shadow(0 0 35px rgba(255, 150, 0, 0.95));
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: translate(var(--attack-dx, 0), var(--attack-dy, 0)) scale(1.25) rotate(-1deg);
|
||||||
|
filter: brightness(1.7) drop-shadow(0 0 30px rgba(255, 120, 0, 0.9));
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0, 0) scale(1) rotate(0deg);
|
||||||
|
filter: brightness(1);
|
||||||
|
z-index: auto;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-wrap.combat-hit .card {
|
.card-wrap.combat-hit .card {
|
||||||
animation: combatHit 0.45s ease-out forwards;
|
animation: combatHit 0.45s ease-out forwards;
|
||||||
}
|
}
|
||||||
@ -851,6 +893,66 @@ html, body {
|
|||||||
40% { transform: scale(0.95); filter: brightness(1.2); }
|
40% { transform: scale(0.95); filter: brightness(1.2); }
|
||||||
100% { transform: scale(1); filter: brightness(1); }
|
100% { transform: scale(1); filter: brightness(1); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Эффект удара - карта трясётся и подсвечивается */
|
||||||
|
.card-wrap.combat-hit-impact {
|
||||||
|
animation: combatHitImpact 0.35s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-wrap.combat-hit-impact .card {
|
||||||
|
box-shadow: 0 0 30px rgba(255, 50, 50, 0.8), 0 0 60px rgba(255, 100, 100, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes combatHitImpact {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0) scale(1) rotate(0deg);
|
||||||
|
filter: brightness(1);
|
||||||
|
}
|
||||||
|
8% {
|
||||||
|
transform: translate(-4px, -3px) scale(1.08) rotate(-3deg);
|
||||||
|
filter: brightness(2.8) saturate(1.3);
|
||||||
|
}
|
||||||
|
16% {
|
||||||
|
transform: translate(4px, 3px) scale(1.08) rotate(3deg);
|
||||||
|
filter: brightness(3) saturate(1.4);
|
||||||
|
}
|
||||||
|
24% {
|
||||||
|
transform: translate(-3px, 2px) scale(1.05) rotate(-2deg);
|
||||||
|
filter: brightness(2.5) saturate(1.2);
|
||||||
|
}
|
||||||
|
32% {
|
||||||
|
transform: translate(3px, -2px) scale(1.05) rotate(2deg);
|
||||||
|
filter: brightness(2.7) saturate(1.3);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: translate(-2px, 1px) scale(1.03) rotate(-1deg);
|
||||||
|
filter: brightness(2.2) saturate(1.1);
|
||||||
|
}
|
||||||
|
48% {
|
||||||
|
transform: translate(2px, -1px) scale(1.03) rotate(1deg);
|
||||||
|
filter: brightness(2.4) saturate(1.2);
|
||||||
|
}
|
||||||
|
56% {
|
||||||
|
transform: translate(-1px, 0) scale(1.02) rotate(-0.5deg);
|
||||||
|
filter: brightness(2) saturate(1.05);
|
||||||
|
}
|
||||||
|
64% {
|
||||||
|
transform: translate(1px, 0) scale(1.02) rotate(0.5deg);
|
||||||
|
filter: brightness(1.8) saturate(1.1);
|
||||||
|
}
|
||||||
|
72% {
|
||||||
|
transform: translate(0, 0) scale(1.01) rotate(0deg);
|
||||||
|
filter: brightness(1.5) saturate(1.05);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: translate(0, 0) scale(1.005) rotate(0deg);
|
||||||
|
filter: brightness(1.2) saturate(1.02);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(0, 0) scale(1) rotate(0deg);
|
||||||
|
filter: brightness(1) saturate(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
.card-wrap.combat-death .card {
|
.card-wrap.combat-death .card {
|
||||||
animation: combatDeath 0.4s ease-in forwards;
|
animation: combatDeath 0.4s ease-in forwards;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user