Some checks failed
Deploy Project BC / deploy (push) Failing after 33s
153 lines
9.6 KiB
JavaScript
153 lines
9.6 KiB
JavaScript
// /server/game/logic/effectsLogic.js
|
||
|
||
// GAME_CONFIG и dataUtils будут передаваться в функции как параметры.
|
||
// const GAME_CONFIG_STATIC = require('../../core/config'); // Если нужен для внутренних констант
|
||
// const DATA_UTILS_STATIC = require('../../data/dataUtils'); // Если нужен для внутренних констант
|
||
|
||
/**
|
||
* Обрабатывает активные эффекты (баффы/дебаффы) для бойца в конце его хода.
|
||
* Длительность эффекта уменьшается на 1.
|
||
* Периодические эффекты (DoT, сжигание ресурса и т.п.) срабатывают, если эффект не "justCast" в этом ходу.
|
||
* @param {Array<object>} activeEffectsArray - Массив активных эффектов бойца (из gameState.player.activeEffects или gameState.opponent.activeEffects).
|
||
* @param {object} ownerState - Состояние бойца, на котором эффекты (currentHp, currentResource и т.д.).
|
||
* @param {object} ownerBaseStats - Базовые статы бойца (включая characterKey, name, maxHp, maxResource).
|
||
* @param {string} ownerRoleInGame - Роль бойца в игре ('player' или 'opponent'), для контекста.
|
||
* @param {object} currentGameState - Полное текущее состояние игры.
|
||
* @param {function} addToLogCallback - Функция для добавления сообщений в лог игры.
|
||
* @param {object} configToUse - Конфигурационный объект игры (GAME_CONFIG).
|
||
* @param {object} dataUtils - Утилиты для доступа к данным игры (getCharacterData, getCharacterAbilities и т.д.).
|
||
*/
|
||
function processEffects(
|
||
activeEffectsArray,
|
||
ownerState,
|
||
ownerBaseStats,
|
||
ownerRoleInGame, // 'player' или 'opponent'
|
||
currentGameState,
|
||
addToLogCallback,
|
||
configToUse,
|
||
dataUtils
|
||
) {
|
||
if (!activeEffectsArray || activeEffectsArray.length === 0) {
|
||
return;
|
||
}
|
||
|
||
const ownerName = ownerBaseStats.name;
|
||
const effectsToRemoveIndexes = [];
|
||
|
||
for (let i = 0; i < activeEffectsArray.length; i++) {
|
||
const effect = activeEffectsArray[i];
|
||
|
||
// --- Применяем периодический эффект (DoT, сжигание ресурса и т.п.), если он не только что наложен ---
|
||
if (!effect.justCast) {
|
||
// 1. Урон от эффектов полного безмолвия (Гипнотический Взгляд, Раскол Разума)
|
||
// Эти эффекты наносят урон цели В КОНЦЕ ее хода.
|
||
if (effect.isFullSilence && typeof effect.power === 'number' && effect.power > 0) {
|
||
const damage = effect.power; // Урон, заложенный в эффекте
|
||
ownerState.currentHp = Math.max(0, Math.round(ownerState.currentHp - damage));
|
||
if (addToLogCallback) {
|
||
addToLogCallback(
|
||
`😵 Эффект "${effect.name}" наносит ${damage} урона персонажу ${ownerName}! (HP: ${ownerState.currentHp}/${ownerBaseStats.maxHp})`,
|
||
configToUse.LOG_TYPE_DAMAGE
|
||
);
|
||
}
|
||
}
|
||
|
||
// 2. Сжигание ресурса (Печать Слабости, Проклятие Увядания)
|
||
// Эти эффекты сжигают ресурс цели В КОНЦЕ ее хода.
|
||
// ID эффекта на цели имеет префикс 'effect_' + ID способности, которая его наложила.
|
||
const isResourceBurnDebuff = effect.id === 'effect_' + configToUse.ABILITY_ID_SEAL_OF_WEAKNESS ||
|
||
effect.id === 'effect_' + configToUse.ABILITY_ID_ALMAGEST_DEBUFF;
|
||
if (isResourceBurnDebuff && typeof effect.power === 'number' && effect.power > 0) {
|
||
const resourceToBurn = effect.power; // Количество ресурса, сжигаемое за ход
|
||
if (ownerState.currentResource > 0) {
|
||
const actualBurn = Math.min(ownerState.currentResource, resourceToBurn);
|
||
ownerState.currentResource = Math.max(0, Math.round(ownerState.currentResource - actualBurn));
|
||
if (addToLogCallback) {
|
||
addToLogCallback(
|
||
`🔥 Эффект "${effect.name}" сжигает ${actualBurn} ${ownerBaseStats.resourceName} у ${ownerName}! (Ресурс: ${ownerState.currentResource}/${ownerBaseStats.maxResource})`,
|
||
configToUse.LOG_TYPE_EFFECT
|
||
);
|
||
}
|
||
}
|
||
}
|
||
// Примечание: Отложенные эффекты (isDelayed: true, например, Сила Природы)
|
||
// применяют свою основную силу в GameInstance.processPlayerAction (после атаки), а не здесь.
|
||
// Здесь они просто тикают по длительности.
|
||
}
|
||
|
||
// --- Уменьшаем длительность ---
|
||
effect.turnsLeft--;
|
||
effect.justCast = false; // Эффект больше не считается "just cast" после обработки этого хода
|
||
|
||
// --- Отмечаем для удаления, если длительность закончилась ---
|
||
if (effect.turnsLeft <= 0) {
|
||
effectsToRemoveIndexes.push(i);
|
||
if (addToLogCallback) {
|
||
addToLogCallback(
|
||
`Эффект "${effect.name}" на персонаже ${ownerName} закончился.`,
|
||
configToUse.LOG_TYPE_EFFECT
|
||
);
|
||
}
|
||
// Если это был эффект, дающий блок, нужно обновить статус блокировки
|
||
if (effect.grantsBlock) {
|
||
updateBlockingStatus(ownerState); // Вызываем сразу, т.к. эффект удаляется
|
||
}
|
||
// Если это был эффект заглушения конкретной способности (playerSilencedOn_X),
|
||
// то соответствующая запись в ownerState.disabledAbilities должна быть удалена в cooldownLogic.processDisabledAbilities.
|
||
// Здесь мы просто удаляем сам эффект из activeEffects.
|
||
}
|
||
}
|
||
|
||
// Удаляем эффекты с конца массива, чтобы не нарушить индексы при удалении
|
||
for (let i = effectsToRemoveIndexes.length - 1; i >= 0; i--) {
|
||
activeEffectsArray.splice(effectsToRemoveIndexes[i], 1);
|
||
}
|
||
|
||
// После удаления всех истекших эффектов, еще раз обновляем статус блока,
|
||
// так как какой-то из удаленных эффектов мог быть последним дающим блок.
|
||
// (хотя updateBlockingStatus вызывается и при удалении конкретного блокирующего эффекта)
|
||
updateBlockingStatus(ownerState);
|
||
}
|
||
|
||
/**
|
||
* Обновляет статус 'isBlocking' для бойца на основе его активных эффектов.
|
||
* Боец считается блокирующим, если у него есть хотя бы один активный эффект с флагом grantsBlock: true.
|
||
* @param {object} fighterState - Состояние бойца (объект из gameState.player или gameState.opponent).
|
||
*/
|
||
function updateBlockingStatus(fighterState) {
|
||
if (!fighterState || !fighterState.activeEffects) {
|
||
// console.warn("[EffectsLogic] updateBlockingStatus: fighterState or activeEffects missing.");
|
||
if (fighterState) fighterState.isBlocking = false; // Если нет эффектов, то точно не блокирует
|
||
return;
|
||
}
|
||
// Боец блокирует, если есть ХОТЯ БЫ ОДИН активный эффект, дающий блок
|
||
const wasBlocking = fighterState.isBlocking;
|
||
fighterState.isBlocking = fighterState.activeEffects.some(eff => eff.grantsBlock && eff.turnsLeft > 0);
|
||
|
||
// Можно добавить лог, если статус блока изменился, для отладки
|
||
// if (wasBlocking !== fighterState.isBlocking && addToLogCallback) {
|
||
// addToLogCallback(`${fighterState.name} ${fighterState.isBlocking ? 'встает в защиту' : 'перестает защищаться'} из-за эффектов.`, 'info');
|
||
// }
|
||
}
|
||
|
||
/**
|
||
* Проверяет, находится ли персонаж под действием полного безмолвия.
|
||
* @param {object} characterState - Состояние персонажа из gameState.
|
||
* @param {object} configToUse - Конфигурационный объект игры.
|
||
* @returns {boolean} true, если персонаж под полным безмолвием, иначе false.
|
||
*/
|
||
function isCharacterFullySilenced(characterState, configToUse) {
|
||
if (!characterState || !characterState.activeEffects) {
|
||
return false;
|
||
}
|
||
return characterState.activeEffects.some(
|
||
eff => eff.isFullSilence && eff.turnsLeft > 0
|
||
);
|
||
}
|
||
|
||
|
||
module.exports = {
|
||
processEffects,
|
||
updateBlockingStatus,
|
||
isCharacterFullySilenced
|
||
}; |