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