123
This commit is contained in:
217
server.js
217
server.js
@ -52,6 +52,32 @@ function cleanupEmptyRooms() {
|
||||
// Периодическая очистка пустых комнат
|
||||
setInterval(cleanupEmptyRooms, 30000); // каждые 30 секунд
|
||||
|
||||
// Функция для применения урона с учетом брони
|
||||
function applyDamageToPlayer(player, damage) {
|
||||
if (damage <= 0) return;
|
||||
|
||||
// Проверка на Droid Armor - удваиваем броню для мех-юнитов
|
||||
// (это обрабатывается при создании карты, но можно добавить проверку здесь)
|
||||
|
||||
// Сначала урон идет на броню
|
||||
if (player.armor > 0) {
|
||||
const armorDamage = Math.min(player.armor, damage);
|
||||
player.armor -= armorDamage;
|
||||
damage -= armorDamage;
|
||||
}
|
||||
|
||||
// Оставшийся урон идет на HP
|
||||
if (damage > 0) {
|
||||
player.health = Math.max(0, player.health - damage);
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для лечения игрока
|
||||
function healPlayer(player, amount) {
|
||||
if (amount <= 0) return;
|
||||
player.health = Math.min(30, player.health + amount);
|
||||
}
|
||||
|
||||
function shuffle(a) {
|
||||
for (let i = a.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
@ -113,6 +139,7 @@ function initGame(room) {
|
||||
mana: 0,
|
||||
maxMana: 0,
|
||||
health: 30,
|
||||
armor: 0, // Система брони
|
||||
hero: (p.hero || ['luke', 'vader', 'yoda', 'leia', 'rey', 'kylo', 'mace'][i % 7]),
|
||||
manualDrawUsed: false,
|
||||
fatigueCounter: 0,
|
||||
@ -140,6 +167,7 @@ function initGame(room) {
|
||||
mana: 0,
|
||||
maxMana: 0,
|
||||
health: 30,
|
||||
armor: 0, // Система брони
|
||||
hero: 'vader',
|
||||
manualDrawUsed: false,
|
||||
fatigueCounter: 0,
|
||||
@ -473,7 +501,7 @@ function endTurn(room) {
|
||||
const enemies = gameState.players.filter((pl, i) => i !== prev && pl.health > 0);
|
||||
if (enemies.length) {
|
||||
const target = enemies[Math.floor(Math.random() * enemies.length)];
|
||||
target.health = Math.max(0, target.health - 3);
|
||||
applyDamageToPlayer(target, 3);
|
||||
gameState.log.push({ type: 'structure', effect: 'death_star_damage', fromPlayer: prev, toPlayer: gameState.players.indexOf(target), damage: 3 });
|
||||
}
|
||||
} else if (card.name === 'Кантина Мос-Эйсли') {
|
||||
@ -506,7 +534,7 @@ function endTurn(room) {
|
||||
const enemies = gameState.players.filter((pl, i) => i !== prev && pl.health > 0);
|
||||
if (enemies.length) {
|
||||
const target = enemies[Math.floor(Math.random() * enemies.length)];
|
||||
target.health = Math.max(0, target.health - 2);
|
||||
applyDamageToPlayer(target, 2);
|
||||
gameState.log.push({ type: 'planet', effect: 'korriban_damage', fromPlayer: prev, toPlayer: gameState.players.indexOf(target), damage: 2 });
|
||||
}
|
||||
} else if (card.name === 'Tatooine') {
|
||||
@ -525,7 +553,7 @@ function endTurn(room) {
|
||||
} else if (card.name === 'Zakuul') {
|
||||
const enemies = gameState.players.filter((pl, i) => i !== prev && pl.health > 0);
|
||||
enemies.forEach((enemy) => {
|
||||
enemy.health = Math.max(0, enemy.health - 3);
|
||||
applyDamageToPlayer(enemy, 3);
|
||||
});
|
||||
gameState.log.push({ type: 'planet', effect: 'zakuul_damage', fromPlayer: prev });
|
||||
} else if (card.name === 'Kamino') {
|
||||
@ -536,7 +564,7 @@ function endTurn(room) {
|
||||
} else if (card.name === 'Mustafar') {
|
||||
const enemies = gameState.players.filter((pl, i) => i !== prev && pl.health > 0);
|
||||
enemies.forEach((enemy) => {
|
||||
enemy.health = Math.max(0, enemy.health - 1);
|
||||
applyDamageToPlayer(enemy, 1);
|
||||
if (enemy.board) {
|
||||
enemy.board.forEach((min) => {
|
||||
min.health = Math.max(0, min.health - 1);
|
||||
@ -546,8 +574,33 @@ function endTurn(room) {
|
||||
});
|
||||
gameState.log.push({ type: 'planet', effect: 'mustafar_damage', fromPlayer: prev });
|
||||
} else if (card.name === 'Naboo') {
|
||||
prevPlayer.health = Math.min(30, prevPlayer.health + 2);
|
||||
healPlayer(prevPlayer, 2);
|
||||
gameState.log.push({ type: 'planet', effect: 'naboo_heal', playerIndex: prev });
|
||||
}
|
||||
// Обработка эффектов карт с хилом и броней
|
||||
else if (card.fieldEffect === 'heal_1_per_turn') {
|
||||
healPlayer(prevPlayer, 1);
|
||||
gameState.log.push({ type: 'fieldEffect', effect: 'heal_1_per_turn', playerIndex: prev });
|
||||
} else if (card.fieldEffect === 'armor_regen_2_if_jedi') {
|
||||
// Проверяем, есть ли джедай на поле
|
||||
const hasJedi = prevPlayer.board.some(min => {
|
||||
const minCard = getCard(room, min.cardId);
|
||||
return minCard && (minCard.name?.includes('Jedi') ||
|
||||
minCard.id === 'luke' || minCard.id === 'obiwan' ||
|
||||
minCard.id === 'ahsoka' || minCard.id === 'mace' ||
|
||||
minCard.id === 'yoda' || minCard.id === 'anakin');
|
||||
});
|
||||
if (hasJedi) {
|
||||
prevPlayer.armor = (prevPlayer.armor || 0) + 2;
|
||||
gameState.log.push({ type: 'fieldEffect', effect: 'armor_regen_2_if_jedi', playerIndex: prev });
|
||||
}
|
||||
} else if (card.fieldEffect === 'armor_regen_5_every_2_turns') {
|
||||
// Каждые 2 хода восстанавливаем +5 брони
|
||||
const turnCount = gameState.turn || 0;
|
||||
if (turnCount % 2 === 0) {
|
||||
prevPlayer.armor = (prevPlayer.armor || 0) + 5;
|
||||
gameState.log.push({ type: 'fieldEffect', effect: 'armor_regen_5_every_2_turns', playerIndex: prev });
|
||||
}
|
||||
} else if (card.name === 'Endor') {
|
||||
if (prevPlayer.hand.length < 10) {
|
||||
prevPlayer.hand.push('ewok');
|
||||
@ -582,12 +635,16 @@ function endTurn(room) {
|
||||
m.attacksUsed = 0; // Сбрасываем счётчик атак для двойной атаки
|
||||
}
|
||||
}
|
||||
// Сбрасываем флаг использования Energy Shield
|
||||
if (card && card.shieldEffect === 'absorb_first_damage') {
|
||||
m.shieldUsedThisTurn = false;
|
||||
}
|
||||
});
|
||||
np.manualDrawUsed = false;
|
||||
np.heroAbilityUsed = false;
|
||||
if (np.deck.length === 0) {
|
||||
np.fatigueCounter = (np.fatigueCounter || 0) + 1;
|
||||
np.health = Math.max(0, np.health - np.fatigueCounter);
|
||||
applyDamageToPlayer(np, np.fatigueCounter);
|
||||
gameState.log.push({ type: 'fatigue', playerIndex: next, damage: np.fatigueCounter });
|
||||
} else {
|
||||
const drawn = drawCards(np.deck, 1);
|
||||
@ -623,7 +680,7 @@ function manualDraw(room, socketId) {
|
||||
p.manualDrawUsed = true;
|
||||
p.fatigueCounter = (p.fatigueCounter || 0) + 1;
|
||||
const dmg = p.fatigueCounter;
|
||||
p.health = Math.max(0, p.health - dmg);
|
||||
applyDamageToPlayer(p, dmg);
|
||||
gameState.log.push({ type: 'fatigue', playerIndex: pi, damage: dmg });
|
||||
checkGameOver(room);
|
||||
broadcastGameState(room);
|
||||
@ -644,12 +701,12 @@ function runBattlecry(room, card, playerIndex) {
|
||||
if (id === 'deal_1_hero' && enemies.length) {
|
||||
const t = enemies[Math.floor(Math.random() * enemies.length)];
|
||||
const idx = gameState.players.indexOf(t);
|
||||
t.health = Math.max(0, t.health - 1);
|
||||
applyDamageToPlayer(t, 1);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'deal_1_hero', fromPlayer: playerIndex, toPlayer: idx, damage: 1 });
|
||||
} else if (id === 'deal_2_hero' && enemies.length) {
|
||||
const t = enemies[Math.floor(Math.random() * enemies.length)];
|
||||
const idx = gameState.players.indexOf(t);
|
||||
t.health = Math.max(0, t.health - 2);
|
||||
applyDamageToPlayer(t, 2);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'deal_2_hero', fromPlayer: playerIndex, toPlayer: idx, damage: 2 });
|
||||
} else if (id === 'draw_1') {
|
||||
const drawn = drawCards(p.deck, 1);
|
||||
@ -689,10 +746,10 @@ function runBattlecry(room, card, playerIndex) {
|
||||
}
|
||||
}
|
||||
} else if (id === 'heal_hero_2') {
|
||||
p.health = Math.min(30, p.health + 2);
|
||||
healPlayer(p, 2);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'heal_hero_2', playerIndex });
|
||||
} else if (id === 'heal_hero_3') {
|
||||
p.health = Math.min(30, p.health + 3);
|
||||
healPlayer(p, 3);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'heal_hero_3', playerIndex });
|
||||
} else if (id === 'buff_all_friendly_attack') {
|
||||
p.board.forEach((m) => {
|
||||
@ -701,7 +758,7 @@ function runBattlecry(room, card, playerIndex) {
|
||||
gameState.log.push({ type: 'battlecry', effect: 'buff_all_friendly_attack', playerIndex });
|
||||
} else if (id === 'deal_3_all_enemies') {
|
||||
enemies.forEach((e) => {
|
||||
e.health = Math.max(0, e.health - 3);
|
||||
applyDamageToPlayer(e, 3);
|
||||
});
|
||||
gameState.log.push({ type: 'battlecry', effect: 'deal_3_all_enemies', fromPlayer: playerIndex });
|
||||
} else if (id === 'deal_3_any') {
|
||||
@ -718,7 +775,7 @@ function runBattlecry(room, card, playerIndex) {
|
||||
runDeathrattle(room, getCard(room, target.cardId), idx);
|
||||
}
|
||||
} else {
|
||||
t.health = Math.max(0, t.health - 3);
|
||||
applyDamageToPlayer(t, 3);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'deal_3_any', fromPlayer: playerIndex, toPlayer: idx, damage: 3 });
|
||||
}
|
||||
}
|
||||
@ -763,7 +820,7 @@ function runBattlecry(room, card, playerIndex) {
|
||||
gameState.log.push({ type: 'battlecry', effect: 'buff_2_2', playerIndex, toIdx: boardIdx });
|
||||
}
|
||||
} else if (id === 'heal_hero_5') {
|
||||
p.health = Math.min(30, p.health + 5);
|
||||
healPlayer(p, 5);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'heal_hero_5', playerIndex });
|
||||
} else if (id === 'return_hand_enemy') {
|
||||
// Для Ezra Bridger требуется выбор конкретного игрока
|
||||
@ -808,8 +865,8 @@ function runBattlecry(room, card, playerIndex) {
|
||||
} else if (id === 'random_effect') {
|
||||
const effects = [
|
||||
() => { const drawn = drawCards(p.deck, 1); if (drawn.length) p.hand.push(...drawn); gameState.log.push({ type: 'battlecry', effect: 'random_draw', playerIndex }); },
|
||||
() => { if (enemies.length) { const t = enemies[Math.floor(Math.random() * enemies.length)]; t.health = Math.max(0, t.health - 1); gameState.log.push({ type: 'battlecry', effect: 'random_damage', fromPlayer: playerIndex, toPlayer: gameState.players.indexOf(t), damage: 1 }); } },
|
||||
() => { p.health = Math.min(30, p.health + 1); gameState.log.push({ type: 'battlecry', effect: 'random_heal', playerIndex }); }
|
||||
() => { if (enemies.length) { const t = enemies[Math.floor(Math.random() * enemies.length)]; applyDamageToPlayer(t, 1); gameState.log.push({ type: 'battlecry', effect: 'random_damage', fromPlayer: playerIndex, toPlayer: gameState.players.indexOf(t), damage: 1 }); } },
|
||||
() => { healPlayer(p, 1); gameState.log.push({ type: 'battlecry', effect: 'random_heal', playerIndex }); }
|
||||
];
|
||||
const randomEffect = effects[Math.floor(Math.random() * effects.length)];
|
||||
randomEffect();
|
||||
@ -820,6 +877,7 @@ function runBattlecry(room, card, playerIndex) {
|
||||
const target = enemy.board[Math.floor(Math.random() * enemy.board.length)];
|
||||
target.health = Math.max(0, target.health - 2);
|
||||
const boardIdx = enemy.board.indexOf(target);
|
||||
applyDamageToPlayer(enemy, 2);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'deal_2_or_4_hero', fromPlayer: playerIndex, toPlayer: enemyIdx, toIdx: boardIdx, damage: 2 });
|
||||
if (target.health <= 0) {
|
||||
enemy.board.splice(boardIdx, 1);
|
||||
@ -827,6 +885,7 @@ function runBattlecry(room, card, playerIndex) {
|
||||
}
|
||||
} else {
|
||||
enemy.health = Math.max(0, enemy.health - 4);
|
||||
applyDamageToPlayer(enemy, 4);
|
||||
gameState.log.push({ type: 'battlecry', effect: 'deal_2_or_4_hero', fromPlayer: playerIndex, toPlayer: enemyIdx, damage: 4 });
|
||||
}
|
||||
} else if (id === 'deal_2_all_enemy_minions') {
|
||||
@ -899,15 +958,15 @@ function runDeathrattle(room, card, ownerIndex) {
|
||||
gameState.log.push({ type: 'deathrattle', effect: 'draw_1', playerIndex: ownerIndex });
|
||||
} else if (id === 'deal_2_all_enemies') {
|
||||
enemies.forEach((e) => {
|
||||
e.health = Math.max(0, e.health - 2);
|
||||
applyDamageToPlayer(e, 2);
|
||||
});
|
||||
gameState.log.push({ type: 'deathrattle', effect: 'deal_2_all_enemies', fromPlayer: ownerIndex });
|
||||
} else if (id === 'deal_1_random_enemy' && enemies.length) {
|
||||
const t = enemies[Math.floor(Math.random() * enemies.length)];
|
||||
t.health = Math.max(0, t.health - 1);
|
||||
applyDamageToPlayer(t, 1);
|
||||
gameState.log.push({ type: 'deathrattle', effect: 'deal_1_random_enemy', fromPlayer: ownerIndex });
|
||||
} else if (id === 'heal_hero_2') {
|
||||
owner.health = Math.min(30, owner.health + 2);
|
||||
healPlayer(owner, 2);
|
||||
gameState.log.push({ type: 'deathrattle', effect: 'heal_hero_2', playerIndex: ownerIndex });
|
||||
} else if (id === 'draw_3') {
|
||||
const drawn = drawCards(owner.deck, 3);
|
||||
@ -915,11 +974,11 @@ function runDeathrattle(room, card, ownerIndex) {
|
||||
gameState.log.push({ type: 'deathrattle', effect: 'draw_3', playerIndex: ownerIndex });
|
||||
} else if (id === 'deal_4_random' && enemies.length) {
|
||||
const t = enemies[Math.floor(Math.random() * enemies.length)];
|
||||
t.health = Math.max(0, t.health - 4);
|
||||
applyDamageToPlayer(t, 4);
|
||||
gameState.log.push({ type: 'deathrattle', effect: 'deal_4_random', fromPlayer: ownerIndex, toPlayer: gameState.players.indexOf(t), damage: 4 });
|
||||
} else if (id === 'deal_3_random' && enemies.length) {
|
||||
const t = enemies[Math.floor(Math.random() * enemies.length)];
|
||||
t.health = Math.max(0, t.health - 3);
|
||||
applyDamageToPlayer(t, 3);
|
||||
gameState.log.push({ type: 'deathrattle', effect: 'deal_3_random', fromPlayer: ownerIndex, toPlayer: gameState.players.indexOf(t), damage: 3 });
|
||||
}
|
||||
}
|
||||
@ -1011,10 +1070,17 @@ function playCard(room, socketId, handIndex, boardPos) {
|
||||
health: card.health,
|
||||
maxHealth: card.health,
|
||||
canAttack: card.attack > 0,
|
||||
attacksUsed: 0, // Для двойной атаки
|
||||
};
|
||||
p.board.splice(typeof boardPos === 'number' ? boardPos : p.board.length, 0, minion);
|
||||
gameState.log.push({ type: 'play', playerIndex: pi, cardId: cid, minionId: minion.id });
|
||||
|
||||
// Обработка battlecry для карт с броней
|
||||
if (card.battlecry === 'add_armor_15') {
|
||||
p.armor = (p.armor || 0) + 15;
|
||||
gameState.log.push({ type: 'battlecry', effect: 'add_armor_15', playerIndex: pi });
|
||||
}
|
||||
|
||||
// Для battlecry, требующих выбора цели (Ezra Bridger), отправляем запрос клиенту
|
||||
if (card.battlecryId === 'return_hand_enemy') {
|
||||
const enemies = gameState.players.filter((pl, i) => i !== pi && pl.health > 0);
|
||||
@ -1261,6 +1327,21 @@ function applySynergies(room) {
|
||||
}
|
||||
}
|
||||
|
||||
// Аура: Droid Armor - мех-юниты имеют удвоенную броню
|
||||
if (card.aura === 'double_armor_mechs') {
|
||||
// Удваиваем броню игрока, если есть мех-юниты на поле
|
||||
const hasMechs = player.board.some(other => {
|
||||
const otherCard = cardDb[other.cardId];
|
||||
return otherCard && (otherCard.id === 'r2d2' || otherCard.id === 'c3po' ||
|
||||
otherCard.id === 'bb8' || otherCard.id === 'droid' ||
|
||||
otherCard.name?.includes('Droid') || otherCard.name?.includes('дроид'));
|
||||
});
|
||||
if (hasMechs) {
|
||||
// Броня уже применена, но можно добавить бонус
|
||||
// Это будет обрабатываться при применении урона
|
||||
}
|
||||
}
|
||||
|
||||
// Йода даёт +1/+1 всем джедаям
|
||||
if (card.id === 'yoda' || card.name === 'Yoda') {
|
||||
player.board.forEach((other, otherIdx) => {
|
||||
@ -1471,7 +1552,7 @@ function playSpell(room, socketId, handIndex, targetPlayerIndex, targetBoardInde
|
||||
if (targetBoardIndex === -1) {
|
||||
const t = gameState.players[targetPlayerIndex];
|
||||
if (!t || t.health <= 0) return;
|
||||
t.health = Math.max(0, t.health - 2);
|
||||
applyDamageToPlayer(t, 2);
|
||||
gameState.log.push({ type: 'spell', spell: cid, fromPlayer: pi, toPlayer: targetPlayerIndex, effect: 'deal_2_hero', damage: 2 });
|
||||
} else {
|
||||
const t = gameState.players[targetPlayerIndex];
|
||||
@ -1498,7 +1579,7 @@ function playSpell(room, socketId, handIndex, targetPlayerIndex, targetBoardInde
|
||||
} else if (eff === 'deal_1_all_enemies') {
|
||||
gameState.players.forEach((pl, i) => {
|
||||
if (i === pi) return;
|
||||
pl.health = Math.max(0, pl.health - 1);
|
||||
applyDamageToPlayer(pl, 1);
|
||||
pl.board.forEach((m) => {
|
||||
m.health -= 1;
|
||||
if (m.health <= 0) {
|
||||
@ -1526,7 +1607,7 @@ function playSpell(room, socketId, handIndex, targetPlayerIndex, targetBoardInde
|
||||
if (targetBoardIndex === -1) {
|
||||
const t = gameState.players[targetPlayerIndex];
|
||||
if (!t || t.health <= 0) return;
|
||||
t.health = Math.max(0, t.health - 4);
|
||||
applyDamageToPlayer(t, 4);
|
||||
gameState.log.push({ type: 'spell', spell: cid, fromPlayer: pi, toPlayer: targetPlayerIndex, effect: 'deal_4_hero', damage: 4 });
|
||||
} else {
|
||||
const t = gameState.players[targetPlayerIndex];
|
||||
@ -1544,7 +1625,7 @@ function playSpell(room, socketId, handIndex, targetPlayerIndex, targetBoardInde
|
||||
if (targetBoardIndex === -1) {
|
||||
const t = gameState.players[targetPlayerIndex];
|
||||
if (!t) return;
|
||||
t.health = Math.min(30, t.health + 4);
|
||||
healPlayer(t, 4);
|
||||
gameState.log.push({ type: 'spell', spell: cid, fromPlayer: pi, toPlayer: targetPlayerIndex, effect: 'heal_4_hero' });
|
||||
} else {
|
||||
const t = gameState.players[targetPlayerIndex];
|
||||
@ -1583,6 +1664,16 @@ function playSpell(room, socketId, handIndex, targetPlayerIndex, targetBoardInde
|
||||
m.maxHealth = (m.maxHealth || m.health) + 1;
|
||||
});
|
||||
gameState.log.push({ type: 'spell', spell: cid, fromPlayer: pi, effect: 'buff_all_friendly' });
|
||||
} else if (eff === 'heal_hero_5') {
|
||||
healPlayer(p, 5);
|
||||
gameState.log.push({ type: 'spell', spell: cid, fromPlayer: pi, effect: 'heal_hero_5' });
|
||||
} else if (eff === 'heal_hero_30pct') {
|
||||
const healAmount = Math.floor(p.health * 0.3);
|
||||
healPlayer(p, healAmount);
|
||||
gameState.log.push({ type: 'spell', spell: cid, fromPlayer: pi, effect: 'heal_hero_30pct', amount: healAmount });
|
||||
} else if (eff === 'add_armor_10') {
|
||||
p.armor = (p.armor || 0) + 10;
|
||||
gameState.log.push({ type: 'spell', spell: cid, fromPlayer: pi, effect: 'add_armor_10' });
|
||||
} else if (eff === 'destroy_weak_2') {
|
||||
enemies.forEach((enemy) => {
|
||||
if (enemy.board && enemy.board.length > 0) {
|
||||
@ -1763,7 +1854,7 @@ function heroAbility(room, socketId, targetPlayerIndex, targetBoardIndex) {
|
||||
const targetPlayer = gameState.players[targetPlayerIndex];
|
||||
if (!targetPlayer) return;
|
||||
if (targetBoardIndex === -1) {
|
||||
targetPlayer.health = Math.max(0, targetPlayer.health - 1);
|
||||
applyDamageToPlayer(targetPlayer, 1);
|
||||
gameState.log.push({ type: 'heroAbility', fromPlayer: pi, toPlayer: targetPlayerIndex, effect: 'deal_1_hero' });
|
||||
} else {
|
||||
const m = targetPlayer.board[targetBoardIndex];
|
||||
@ -1809,14 +1900,50 @@ function attack(room, socketId, attackerBoardIndex, targetPlayerIndex, targetBoa
|
||||
applySynergies(room);
|
||||
|
||||
if (targetBoardIndex === -1) {
|
||||
const attackerAttack = attacker.attack + (attacker.synergyAttackBonus || 0);
|
||||
targetPlayer.health = Math.max(0, targetPlayer.health - attackerAttack);
|
||||
let attackerAttack = attacker.attack + (attacker.synergyAttackBonus || 0);
|
||||
const attackerCard = getCard(room, attacker.cardId);
|
||||
|
||||
// Если карта делит урон при двойной атаке
|
||||
if (attackerCard && attackerCard.divideDamage && attackerCard.canAttackTwice) {
|
||||
const attacksUsed = attacker.attacksUsed || 0;
|
||||
if (attacksUsed > 0) {
|
||||
// Второй удар - урон делится
|
||||
attackerAttack = Math.floor(attackerAttack / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка на уменьшение урона (Имперский Щитогенератор)
|
||||
const shieldGen = targetPlayer.board.find(m => {
|
||||
const card = getCard(room, m.cardId);
|
||||
return card && card.damageReduction;
|
||||
});
|
||||
if (shieldGen) {
|
||||
const card = getCard(room, shieldGen.cardId);
|
||||
if (card && card.damageReduction) {
|
||||
attackerAttack = Math.floor(attackerAttack * (1 - card.damageReduction));
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка на Energy Shield - поглощает первый урон каждого хода
|
||||
const energyShield = targetPlayer.board.find(m => {
|
||||
const card = getCard(room, m.cardId);
|
||||
return card && card.shieldEffect === 'absorb_first_damage';
|
||||
});
|
||||
if (energyShield && !energyShield.shieldUsedThisTurn) {
|
||||
energyShield.shieldUsedThisTurn = true;
|
||||
attackerAttack = 0; // Полностью поглощается
|
||||
gameState.log.push({ type: 'shieldAbsorb', playerIndex: targetPlayerIndex, minionId: energyShield.id });
|
||||
}
|
||||
|
||||
const oldArmor = targetPlayer.armor || 0;
|
||||
applyDamageToPlayer(targetPlayer, attackerAttack);
|
||||
gameState.log.push({
|
||||
type: 'attackHero',
|
||||
fromPlayer: pi,
|
||||
toPlayer: targetPlayerIndex,
|
||||
attackerMinionId: attacker.id,
|
||||
damage: attacker.attack,
|
||||
damage: attackerAttack,
|
||||
armorBlocked: oldArmor - (targetPlayer.armor || 0),
|
||||
});
|
||||
// Применяем синергии перед расчётом урона
|
||||
applySynergies(room);
|
||||
@ -1852,8 +1979,21 @@ function attack(room, socketId, attackerBoardIndex, targetPlayerIndex, targetBoa
|
||||
});
|
||||
const targetCard = getCard(room, target.cardId);
|
||||
const attackerCard = getCard(room, attacker.cardId);
|
||||
if (targetDied && targetCard && targetCard.deathrattleId) {
|
||||
runDeathrattle(room, targetCard, targetPlayerIndex);
|
||||
|
||||
// Обработка Soul Heal - хил за каждую убитую карту противника
|
||||
if (targetDied) {
|
||||
// Проверяем, есть ли у атакующего игрока карта Soul Heal
|
||||
p.board.forEach((minion) => {
|
||||
const minionCard = getCard(room, minion.cardId);
|
||||
if (minionCard && minionCard.onEnemyDeath === 'heal_1_per_kill') {
|
||||
healPlayer(p, 1);
|
||||
gameState.log.push({ type: 'soulHeal', playerIndex: pi, healed: 1 });
|
||||
}
|
||||
});
|
||||
|
||||
if (targetCard && targetCard.deathrattleId) {
|
||||
runDeathrattle(room, targetCard, targetPlayerIndex);
|
||||
}
|
||||
}
|
||||
if (attackerDied && attackerCard && attackerCard.deathrattleId) {
|
||||
runDeathrattle(room, attackerCard, pi);
|
||||
@ -1862,8 +2002,19 @@ function attack(room, socketId, attackerBoardIndex, targetPlayerIndex, targetBoa
|
||||
|
||||
// Механика двойной атаки
|
||||
const attackerCard = getCard(room, attacker.cardId);
|
||||
if (attackerCard && attackerCard.canAttackTwice && !attacker.attacksUsed) {
|
||||
attacker.attacksUsed = 1; // Использована одна атака
|
||||
if (attackerCard && attackerCard.canAttackTwice) {
|
||||
attacker.attacksUsed = (attacker.attacksUsed || 0) + 1;
|
||||
// Если карта умирает после определенного количества атак
|
||||
if (attackerCard.diesAfterAttacks && attacker.attacksUsed >= attackerCard.diesAfterAttacks) {
|
||||
attacker.health = 0; // Убиваем карту
|
||||
gameState.log.push({ type: 'minionDeath', minionId: attacker.id, reason: 'diesAfterAttacks' });
|
||||
} else if (attacker.attacksUsed >= 2) {
|
||||
attacker.canAttack = false;
|
||||
attacker.attacksUsed = 0;
|
||||
} else {
|
||||
// Может атаковать еще раз
|
||||
attacker.canAttack = true;
|
||||
}
|
||||
} else {
|
||||
attacker.canAttack = false;
|
||||
attacker.attacksUsed = 0;
|
||||
|
||||
Reference in New Issue
Block a user