New update

This commit is contained in:
2026-01-27 22:45:36 +03:00
parent 7d5769ac8b
commit dedc2635f2
3 changed files with 125 additions and 66 deletions

View File

@ -23,11 +23,12 @@ module.exports = {
health: 8,
type: 'minion',
faction: 'empire',
text: 'Sith Lord. Fear is his weapon. Даёт +1/+1 штурмовикам и клонам.',
text: 'Sith Lord. Fear is his weapon. Даёт +1/+1 штурмовикам и клонам. Пока на поле, все джедаи противника получают -1/-1.',
art: 'vader',
legendary: true,
deathrattle: 'Нанеси 2 урона всем врагам.',
deathrattleId: 'deal_2_all_enemies',
aura: 'jedi_debuff_enemy',
bio: 'Дарт Вейдер — бывший Энакин Скайуокер, Избранный, самый сильный в Силе. Рыцарь-джедай, павший на тёмную сторону из-за страха потерять Падме. Ученик Палпатина, ставший тёмным лордом ситхов. Правой рукой Императора, командовал армией Империи. В битве на Мустафаре потерял конечности и был сожжён лавой, выжил благодаря кибернетическим имплантам и костюму. В конце жизни вернулся к свету, убив Палпатина и спася сына, но погиб от полученных ран.',
},
yoda: {
@ -1034,11 +1035,11 @@ module.exports = {
cost: 3,
type: 'spell',
faction: 'pirates',
text: 'Укради 2 карты из колоды противника.',
text: 'Укради 2 карты с доски противника (они перейдут в твою руку).',
art: 'plunder',
spellEffect: 'steal_cards',
spellTarget: 'enemy_player',
bio: 'Пираты грабят и крадут карты у противников.',
spellTarget: 'enemy_board',
bio: 'Пираты грабят и крадут карты у противников с поля боя.',
},
mandalorian_rage: {
name: 'Ярость мандалорца',
@ -1308,12 +1309,13 @@ module.exports = {
health: 6,
type: 'minion',
faction: 'rebellion',
text: 'Избранный. Может пасть на тёмную сторону. Даёт +1/+1 Оби-Вану и Падме.',
text: 'Избранный. Может пасть на тёмную сторону. Даёт +1/+1 Оби-Вану, Падме и Асоке. Пока на поле, все джедаи получают +1/+1.',
art: 'anakin',
legendary: true,
battlecry: 'Уничтожь вражеского миньона с атакой 4 или меньше.',
battlecryId: 'destroy_medium_minion',
transformOnDeath: 'vader',
aura: 'jedi_buff_all',
bio: 'Избранный, самый сильный в Силе. Ученик Оби-Вана, муж Падме. Пад на тёмную сторону и стал Дартом Вейдером.',
},
kanan: {

View File

@ -325,10 +325,10 @@
stealCardsMode.targetDeck = [];
stealCardsMode.selectedIndices = [];
// Получаем колоду противника из gameState
// Получаем доску противника из gameState
const targetPlayer = gameState.players[data.targetPlayerIndex];
if (targetPlayer && targetPlayer.deck) {
stealCardsMode.targetDeck = [...targetPlayer.deck];
if (targetPlayer && targetPlayer.board) {
stealCardsMode.targetDeck = targetPlayer.board.map(m => m.cardId);
}
showStealCardsModal(gameState, data);
@ -1571,22 +1571,22 @@
// Специальная обработка для Грабежа - открываем модальное окно
const spellCard = cardDb[spellMode.cardId];
if (spellCard && spellCard.spellEffect === 'steal_cards' && spellCard.spellTarget === 'enemy_player') {
if (spellCard && spellCard.spellEffect === 'steal_cards' && (spellCard.spellTarget === 'enemy_player' || spellCard.spellTarget === 'enemy_board')) {
// Выбираем противника (tp должен быть индексом противника, tb игнорируем для enemy_player)
if (tp !== state.yourIndex && tp >= 0) {
const targetPlayer = state.players[tp];
if (targetPlayer && targetPlayer.deck && targetPlayer.deck.length > 0) {
if (targetPlayer && targetPlayer.board && targetPlayer.board.length > 0) {
stealCardsMode.active = true;
stealCardsMode.handIndex = spellMode.handIndex;
stealCardsMode.targetPlayerIndex = tp;
// Получаем актуальную колоду из gameState
stealCardsMode.targetDeck = targetPlayer.deck ? [...targetPlayer.deck] : [];
// Получаем актуальную доску из gameState
stealCardsMode.targetDeck = targetPlayer.board ? targetPlayer.board.map(m => m.cardId) : [];
stealCardsMode.selectedIndices = [];
showStealCardsModal(state, {
targetPlayerIndex: tp,
targetPlayerName: targetPlayer.name || `Игрок ${tp + 1}`,
targetDeckSize: targetPlayer.deck.length,
maxCards: Math.min(2, targetPlayer.deck.length)
targetBoardSize: targetPlayer.board.length,
maxCards: Math.min(2, targetPlayer.board.length)
});
spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
$('spell-mode')?.classList.add('hidden');
@ -1614,21 +1614,21 @@
// Специальная обработка для Грабежа
const spellCard = cardDb[spellMode.cardId];
if (spellCard && spellCard.spellEffect === 'steal_cards' && spellCard.spellTarget === 'enemy_player') {
if (spellCard && spellCard.spellEffect === 'steal_cards' && (spellCard.spellTarget === 'enemy_player' || spellCard.spellTarget === 'enemy_board')) {
if (tp !== state.yourIndex && tp >= 0) {
const targetPlayer = state.players[tp];
if (targetPlayer && targetPlayer.deck && targetPlayer.deck.length > 0) {
if (targetPlayer && targetPlayer.board && targetPlayer.board.length > 0) {
stealCardsMode.active = true;
stealCardsMode.handIndex = spellMode.handIndex;
stealCardsMode.targetPlayerIndex = tp;
// Получаем актуальную колоду из gameState
stealCardsMode.targetDeck = targetPlayer.deck ? [...targetPlayer.deck] : [];
// Получаем актуальную доску из gameState
stealCardsMode.targetDeck = targetPlayer.board ? targetPlayer.board.map(m => m.cardId) : [];
stealCardsMode.selectedIndices = [];
showStealCardsModal(state, {
targetPlayerIndex: tp,
targetPlayerName: targetPlayer.name || `Игрок ${tp + 1}`,
targetDeckSize: targetPlayer.deck.length,
maxCards: Math.min(2, targetPlayer.deck.length)
targetBoardSize: targetPlayer.board.length,
maxCards: Math.min(2, targetPlayer.board.length)
});
spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
$('spell-mode')?.classList.add('hidden');
@ -1682,23 +1682,23 @@
el.onclick = function (e) {
if (!spellMode.active) return;
const spellCard = cardDb[spellMode.cardId];
if (!spellCard || spellCard.spellEffect !== 'steal_cards' || spellCard.spellTarget !== 'enemy_player') return;
if (!spellCard || spellCard.spellEffect !== 'steal_cards' || (spellCard.spellTarget !== 'enemy_player' && spellCard.spellTarget !== 'enemy_board')) return;
e.stopPropagation();
var tp = parseInt(el.dataset.playerIndex ?? el.dataset.opponentIndex, 10);
if (tp === state.yourIndex || tp < 0) return;
const targetPlayer = state.players[tp];
if (targetPlayer && targetPlayer.deck && targetPlayer.deck.length > 0) {
if (targetPlayer && targetPlayer.board && targetPlayer.board.length > 0) {
stealCardsMode.active = true;
stealCardsMode.handIndex = spellMode.handIndex;
stealCardsMode.targetPlayerIndex = tp;
// Получаем актуальную колоду из gameState
stealCardsMode.targetDeck = targetPlayer.deck ? [...targetPlayer.deck] : [];
// Получаем актуальную доску из gameState
stealCardsMode.targetDeck = targetPlayer.board ? targetPlayer.board.map(m => m.cardId) : [];
stealCardsMode.selectedIndices = [];
showStealCardsModal(state, {
targetPlayerIndex: tp,
targetPlayerName: targetPlayer.name || `Игрок ${tp + 1}`,
targetDeckSize: targetPlayer.deck.length,
maxCards: Math.min(2, targetPlayer.deck.length)
targetBoardSize: targetPlayer.board.length,
maxCards: Math.min(2, targetPlayer.board.length)
});
spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
$('spell-mode')?.classList.add('hidden');
@ -1710,24 +1710,24 @@
el.ontouchstart = function (e) {
if (!spellMode.active) return;
const spellCard = cardDb[spellMode.cardId];
if (!spellCard || spellCard.spellEffect !== 'steal_cards' || spellCard.spellTarget !== 'enemy_player') return;
if (!spellCard || spellCard.spellEffect !== 'steal_cards' || (spellCard.spellTarget !== 'enemy_player' && spellCard.spellTarget !== 'enemy_board')) return;
e.preventDefault();
e.stopPropagation();
var tp = parseInt(el.dataset.playerIndex ?? el.dataset.opponentIndex, 10);
if (tp === state.yourIndex || tp < 0) return;
const targetPlayer = state.players[tp];
if (targetPlayer && targetPlayer.deck && targetPlayer.deck.length > 0) {
if (targetPlayer && targetPlayer.board && targetPlayer.board.length > 0) {
stealCardsMode.active = true;
stealCardsMode.handIndex = spellMode.handIndex;
stealCardsMode.targetPlayerIndex = tp;
// Получаем актуальную колоду из gameState
stealCardsMode.targetDeck = targetPlayer.deck ? [...targetPlayer.deck] : [];
// Получаем актуальную доску из gameState
stealCardsMode.targetDeck = targetPlayer.board ? targetPlayer.board.map(m => m.cardId) : [];
stealCardsMode.selectedIndices = [];
showStealCardsModal(state, {
targetPlayerIndex: tp,
targetPlayerName: targetPlayer.name || `Игрок ${tp + 1}`,
targetDeckSize: targetPlayer.deck.length,
maxCards: Math.min(2, targetPlayer.deck.length)
targetBoardSize: targetPlayer.board.length,
maxCards: Math.min(2, targetPlayer.board.length)
});
spellMode = { active: false, handIndex: -1, cardId: '', spellTarget: '' };
$('spell-mode')?.classList.add('hidden');
@ -2410,18 +2410,18 @@
return;
}
// Если противник уже выбран, показываем его колоду
// Если противник уже выбран, показываем его доску (карты на столе)
if (stealCardsMode.targetPlayerIndex !== null) {
// Обновляем колоду из актуального gameState
// Обновляем доску из актуального gameState
const targetPlayer = currentState.players[stealCardsMode.targetPlayerIndex];
if (targetPlayer && targetPlayer.deck) {
stealCardsMode.targetDeck = [...targetPlayer.deck];
if (targetPlayer && targetPlayer.board) {
stealCardsMode.targetDeck = targetPlayer.board.map(m => m.cardId);
} else {
console.warn('Target player not found or deck is empty:', stealCardsMode.targetPlayerIndex);
console.warn('Target player not found or board is empty:', stealCardsMode.targetPlayerIndex);
}
if (stealCardsMode.targetDeck.length === 0) {
// Колода пуста, закрываем модальное окно
// Доска пуста, закрываем модальное окно
$('steal-cards-overlay')?.classList.add('hidden');
stealCardsMode.active = false;
return;
@ -2431,10 +2431,11 @@
deckList.classList.remove('hidden');
if (hintEl) {
hintEl.textContent = `Выберите до 2 карт из колоды ${targetPlayer?.name || `Игрока ${stealCardsMode.targetPlayerIndex + 1}`} (${stealCardsMode.targetDeck.length} карт)`;
hintEl.textContent = `Выберите до 2 карт с доски ${targetPlayer?.name || `Игрока ${stealCardsMode.targetPlayerIndex + 1}`} (${stealCardsMode.targetDeck.length} карт на столе)`;
}
deckList.innerHTML = stealCardsMode.targetDeck.map((cardId, idx) => {
deckList.innerHTML = targetPlayer.board.map((minion, idx) => {
const cardId = minion.cardId;
const cardDbToUse = currentState?.cardDb || cardDb;
const meta = cardDbToUse[cardId];
if (!meta) {
@ -2451,7 +2452,7 @@
const costDisplay = `<div class="card-cost-wrap"><span class="card-cost">${cost}</span></div>`;
const art = typeof getCardArt === 'function' ? getCardArt(meta) : '✦';
return `<div class="card-wrap steal-deck-card ${isSelected ? 'selected' : ''}" data-card-id="${cardId}" data-deck-index="${idx}" style="width: 100px; height: 140px; cursor: pointer; position: relative; z-index: 10;">
return `<div class="card-wrap steal-deck-card ${isSelected ? 'selected' : ''}" data-card-id="${cardId}" data-board-index="${idx}" style="width: 100px; height: 140px; cursor: pointer; position: relative; z-index: 10;">
<div class="card faction-${meta.faction || 'neutral'}">
<div class="card-art">${art}</div>
<div class="card-info">
@ -2467,9 +2468,9 @@
const handleCardClick = function(e) {
e.preventDefault();
e.stopPropagation();
const idx = parseInt(card.dataset.deckIndex, 10);
const idx = parseInt(card.dataset.boardIndex, 10);
if (isNaN(idx)) {
console.warn('Invalid deck index:', card.dataset.deckIndex);
console.warn('Invalid board index:', card.dataset.boardIndex);
return;
}
const selectedIdx = stealCardsMode.selectedIndices.indexOf(idx);
@ -2482,8 +2483,8 @@
showStealCardsModal(currentState, data || {
targetPlayerIndex: stealCardsMode.targetPlayerIndex,
targetPlayerName: currentState?.players?.[stealCardsMode.targetPlayerIndex]?.name || `Игрок ${stealCardsMode.targetPlayerIndex + 1}`,
targetDeckSize: stealCardsMode.targetDeck.length,
maxCards: Math.min(2, stealCardsMode.targetDeck.length)
targetBoardSize: currentState?.players?.[stealCardsMode.targetPlayerIndex]?.board?.length || 0,
maxCards: Math.min(2, currentState?.players?.[stealCardsMode.targetPlayerIndex]?.board?.length || 0)
});
};
card.onclick = handleCardClick;
@ -2554,18 +2555,18 @@
return;
}
const targetPlayer = currentState.players[playerIdx];
if (targetPlayer && targetPlayer.deck && targetPlayer.deck.length > 0) {
if (targetPlayer && targetPlayer.board && targetPlayer.board.length > 0) {
stealCardsMode.targetPlayerIndex = playerIdx;
stealCardsMode.targetDeck = targetPlayer.deck ? [...targetPlayer.deck] : [];
console.log('Selected target player:', playerIdx, 'Deck size:', stealCardsMode.targetDeck.length);
stealCardsMode.targetDeck = targetPlayer.board ? targetPlayer.board.map(m => m.cardId) : [];
console.log('Selected target player:', playerIdx, 'Board size:', stealCardsMode.targetDeck.length);
showStealCardsModal(currentState, {
targetPlayerIndex: playerIdx,
targetPlayerName: targetPlayer.name || `Игрок ${playerIdx + 1}`,
targetDeckSize: targetPlayer.deck.length,
maxCards: Math.min(2, targetPlayer.deck.length)
targetBoardSize: targetPlayer.board.length,
maxCards: Math.min(2, targetPlayer.board.length)
});
} else {
console.warn('Target player has no deck or is invalid:', playerIdx, targetPlayer);
console.warn('Target player has no board or is invalid:', playerIdx, targetPlayer);
}
};
option.onclick = handleOptionClick;

View File

@ -122,9 +122,18 @@ function initGame(room) {
// Если режим ИИ, добавляем ИИ игрока
if (room.aiMode) {
const starWarsHeroes = [
'Darth Maul', 'Count Dooku', 'General Grievous', 'Grand Moff Tarkin',
'Admiral Thrawn', 'Kylo Ren', 'Snoke', 'General Hux',
'Captain Phasma', 'Moff Gideon', 'Cad Bane', 'Asajj Ventress',
'Savage Opress', 'Darth Sidious', 'Darth Tyranus', 'Darth Plagueis',
'Jango Fett', 'Boba Fett', 'IG-88', 'Bossk',
'Dengar', 'Zuckuss', '4-LOM', 'Aurra Sing'
];
const randomHero = starWarsHeroes[Math.floor(Math.random() * starWarsHeroes.length)];
players.push({
id: 'AI_' + Date.now(),
name: 'ИИ Противник',
name: randomHero,
deck: createDeck(factionChoice),
hand: [],
board: [],
@ -1076,6 +1085,28 @@ function applySynergies(room) {
}
}
});
// Аура: Пока Дарт Вейдер на поле, все джедаи противника получают -1/-1
if (card.aura === 'jedi_debuff_enemy') {
gameState.players.forEach((enemyPlayer, enemyPlayerIndex) => {
if (enemyPlayerIndex !== playerIndex && enemyPlayer.board) {
enemyPlayer.board.forEach((enemyMinion) => {
const enemyCard = cardDb[enemyMinion.cardId];
if (enemyCard && (enemyCard.name?.includes('Jedi') ||
enemyCard.id === 'luke' || enemyCard.id === 'obiwan' ||
enemyCard.id === 'ahsoka' || enemyCard.id === 'mace' ||
enemyCard.id === 'quigon' || enemyCard.id === 'plo_koon' ||
enemyCard.id === 'ki_adi' || enemyCard.id === 'aayla' ||
enemyCard.id === 'shaak_ti' || enemyCard.id === 'kanan' ||
enemyCard.id === 'ezra' || enemyCard.id === 'cal_kestis' ||
enemyCard.id === 'yoda' || enemyCard.id === 'anakin')) {
enemyMinion.synergyAttackBonus = (enemyMinion.synergyAttackBonus || 0) - 1;
enemyMinion.synergyHealthBonus = (enemyMinion.synergyHealthBonus || 0) - 1;
}
});
}
});
}
}
// Люк Скайуокер даёт +1/+1 всем повстанцам
@ -1195,17 +1226,39 @@ function applySynergies(room) {
});
}
// Энакин даёт +1/+1 Оби-Вану
// Энакин даёт +1/+1 Оби-Вану, Падме и Асоке
if (card.id === 'anakin' || card.name === 'Anakin Skywalker') {
player.board.forEach((other, otherIdx) => {
if (idx !== otherIdx) {
const otherCard = cardDb[other.cardId];
if (otherCard && (otherCard.id === 'obiwan' || otherCard.name === 'Obi-Wan Kenobi')) {
if (otherCard && (otherCard.id === 'obiwan' || otherCard.name === 'Obi-Wan Kenobi' ||
otherCard.id === 'padme' || otherCard.name === 'Padmé Amidala' ||
otherCard.id === 'ahsoka' || otherCard.name === 'Ahsoka Tano')) {
other.synergyAttackBonus = (other.synergyAttackBonus || 0) + 1;
other.synergyHealthBonus = (other.synergyHealthBonus || 0) + 1;
}
}
});
// Аура: Пока Анакин на поле, все джедаи получают +1/+1
if (card.aura === 'jedi_buff_all') {
player.board.forEach((other, otherIdx) => {
if (idx !== otherIdx) {
const otherCard = cardDb[other.cardId];
if (otherCard && (otherCard.name?.includes('Jedi') ||
otherCard.id === 'luke' || otherCard.id === 'obiwan' ||
otherCard.id === 'ahsoka' || otherCard.id === 'mace' ||
otherCard.id === 'quigon' || otherCard.id === 'plo_koon' ||
otherCard.id === 'ki_adi' || otherCard.id === 'aayla' ||
otherCard.id === 'shaak_ti' || otherCard.id === 'kanan' ||
otherCard.id === 'ezra' || otherCard.id === 'cal_kestis' ||
otherCard.id === 'yoda')) {
other.synergyAttackBonus = (other.synergyAttackBonus || 0) + 1;
other.synergyHealthBonus = (other.synergyHealthBonus || 0) + 1;
}
}
});
}
}
// Йода даёт +1/+1 всем джедаям
@ -1390,11 +1443,11 @@ function playSpell(room, socketId, handIndex, targetPlayerIndex, targetBoardInde
const eff = card.spellEffect;
const needTarget = card.spellTarget && card.spellTarget !== 'none';
// Специальная обработка для steal_cards - требует только выбор противника (не требует targetBoardIndex)
// Специальная обработка для steal_cards - требует выбор противника и карт с его доски
if (eff === 'steal_cards') {
if (targetPlayerIndex == null || targetPlayerIndex === pi) return;
const targetPlayer = gameState.players[targetPlayerIndex];
if (!targetPlayer || targetPlayer.health <= 0 || !targetPlayer.deck || targetPlayer.deck.length === 0) return;
if (!targetPlayer || targetPlayer.health <= 0 || !targetPlayer.board || targetPlayer.board.length === 0) return;
// Отправляем запрос на выбор карт для кражи
const socket = io.sockets.sockets.get(p.id);
@ -1402,8 +1455,8 @@ function playSpell(room, socketId, handIndex, targetPlayerIndex, targetBoardInde
socket.emit('stealCardsRequest', {
targetPlayerIndex: targetPlayerIndex,
targetPlayerName: targetPlayer.name || `Игрок ${targetPlayerIndex + 1}`,
targetDeckSize: targetPlayer.deck.length,
maxCards: Math.min(2, targetPlayer.deck.length)
targetBoardSize: targetPlayer.board.length,
maxCards: Math.min(2, targetPlayer.board.length)
});
}
return; // Не тратим ману и не удаляем карту пока - это сделаем после выбора
@ -1579,22 +1632,25 @@ function stealCardsFromDeck(room, socketId, handIndex, targetPlayerIndex, cardIn
const targetPlayer = gameState.players[targetPlayerIndex];
if (!targetPlayer || targetPlayerIndex === pi || targetPlayer.health <= 0) return;
if (!targetPlayer.deck || targetPlayer.deck.length === 0) return;
if (!targetPlayer.board || targetPlayer.board.length === 0) return;
// Проверяем индексы карт
// Проверяем индексы карт на доске
if (!Array.isArray(cardIndices) || cardIndices.length === 0 || cardIndices.length > 2) return;
const validIndices = cardIndices.filter(idx => idx >= 0 && idx < targetPlayer.deck.length);
const validIndices = cardIndices.filter(idx => idx >= 0 && idx < targetPlayer.board.length);
if (validIndices.length === 0) return;
// Убираем дубликаты и сортируем по убыванию (чтобы удалять с конца)
const uniqueIndices = [...new Set(validIndices)].sort((a, b) => b - a);
// Крадём карты
// Крадём карты с доски противника
const stolenCards = [];
uniqueIndices.forEach(idx => {
if (idx >= 0 && idx < targetPlayer.deck.length) {
stolenCards.push(targetPlayer.deck[idx]);
targetPlayer.deck.splice(idx, 1);
uniqueIndices.forEach(boardIdx => {
if (boardIdx >= 0 && boardIdx < targetPlayer.board.length) {
const minion = targetPlayer.board[boardIdx];
if (minion && minion.cardId) {
stolenCards.push(minion.cardId);
targetPlayer.board.splice(boardIdx, 1);
}
}
});