This commit is contained in:
2026-01-26 15:21:40 +03:00
parent 934cb46128
commit a3b270fc62
2 changed files with 222 additions and 13 deletions

View File

@ -411,32 +411,139 @@
const attackerWrap = document.querySelector(`[data-minion-id="${lastLog.attackerMinionId}"]`);
const targetWrap = document.querySelector(`[data-minion-id="${lastLog.targetMinionId}"]`);
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');
// Устанавливаем CSS переменные для перемещения
attackerWrap.style.setProperty('--attack-dx', dx + 'px');
attackerWrap.style.setProperty('--attack-dy', dy + 'px');
if (lastLog.damage) showDamageEffect(targetWrap, lastLog.damage, false);
if (typeof window.Sounds !== 'undefined') window.Sounds.attack();
if (lastLog.attackerDied) {
attackerWrap.classList.add('combat-death');
showDamageEffect(attackerWrap, '💀', false);
}
if (lastLog.targetDied) {
targetWrap.classList.add('combat-death');
showDamageEffect(targetWrap, '💀', false);
}
// Анимация удара
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');
showDamageEffect(attackerWrap, '💀', false);
}
if (lastLog.targetDied) {
targetWrap.classList.add('combat-death');
showDamageEffect(targetWrap, '💀', false);
}
targetWrap.classList.remove('combat-hit-impact');
}, 500);
combatTimeout = setTimeout(function () {
combatTimeout = null;
renderBoards(state);
bindGameEvents(state);
}, 600);
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];
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`);
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);
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;

View File

@ -842,6 +842,48 @@ html, body {
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); }
}
/* Новая анимация: карта накладывается на цель */
.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 {
animation: combatHit 0.45s ease-out forwards;
}
@ -851,6 +893,66 @@ html, body {
40% { transform: scale(0.95); filter: brightness(1.2); }
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 {
animation: combatDeath 0.4s ease-in forwards;
}