bc/server/game/logic/effectsLogic.js

153 lines
9.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /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
};