// /server/game/logic/tauntLogic.js const GAME_CONFIG = require('../../core/config'); // Предполагаем, что gameData.tauntSystem импортируется или доступен. // Если tauntSystem экспортируется напрямую из data/taunts.js: // const { tauntSystem } = require('../../data/taunts'); // Если он часть общего gameData, который собирается в data/index.js: const gameData = require('../../data'); // Тогда используем gameData.tauntSystem /** * Получает случайную насмешку из системы насмешек. * @param {string} speakerCharacterKey - Ключ персонажа, который говорит. * @param {string} trigger - Тип триггера насмешки (например, 'selfCastAbility', 'onBattleState', 'onOpponentAction'). * @param {string|number|object} [subTriggerOrContext={}] - Может быть ID способности, специфичный ключ состояния ('start', 'dominating') или объект контекста. * @param {object} configToUse - Конфигурационный объект игры (GAME_CONFIG). * @param {object} opponentFullData - Полные данные персонажа, к которому обращена насмешка (цель). * @param {object} currentGameState - Текущее полное состояние игры. * @returns {string} Текст насмешки или "(Молчание)". */ function getRandomTaunt(speakerCharacterKey, trigger, subTriggerOrContext = {}, configToUse, opponentFullData, currentGameState) { // console.log(`[TauntLogic DEBUG] Called with: speaker=${speakerCharacterKey}, trigger=${trigger}, subTriggerOrContext=`, subTriggerOrContext, `opponentKey=${opponentFullData?.baseStats?.characterKey}`); const tauntSystemToUse = gameData.tauntSystem || (gameData.default && gameData.default.tauntSystem); // Совместимость, если gameData имеет default экспорт if (!tauntSystemToUse) { console.error("[TauntLogic ERROR] tauntSystem is not available from gameData import!"); return "(Молчание)"; } const speakerTauntBranch = tauntSystemToUse[speakerCharacterKey]; if (!speakerTauntBranch) { // console.log(`[TauntLogic] No taunt branch for speaker: ${speakerCharacterKey}`); return "(Молчание)"; } const opponentKeyForTaunts = opponentFullData?.baseStats?.characterKey; if (!opponentKeyForTaunts) { // console.log(`[TauntLogic] Opponent key for taunts not available for speaker ${speakerCharacterKey}, trigger ${trigger}. OpponentData:`, opponentFullData); // Особый случай для старта AI игры, где оппонент (AI Балард) может быть известен, даже если opponentFullData не полон if (trigger === 'onBattleState' && subTriggerOrContext === 'start' && speakerCharacterKey === 'elena' && currentGameState.gameMode === 'ai') { // Елена против Баларда (AI) в начале боя const elenaVsBalardStartTaunts = speakerTauntBranch.balard?.onBattleState?.start; if (Array.isArray(elenaVsBalardStartTaunts) && elenaVsBalardStartTaunts.length > 0) { return elenaVsBalardStartTaunts[Math.floor(Math.random() * elenaVsBalardStartTaunts.length)] || "(Молчание)"; } } return "(Молчание)"; } const specificTauntBranch = speakerTauntBranch[opponentKeyForTaunts]; if (!specificTauntBranch || !specificTauntBranch[trigger]) { // console.log(`[TauntLogic] No specific taunt branch or trigger branch for ${speakerCharacterKey} vs ${opponentKeyForTaunts}, trigger: ${trigger}`); return "(Молчание)"; } let tauntSet = specificTauntBranch[trigger]; let context = {}; let subTriggerKey = null; // Это будет ключ для прямого доступа к массиву насмешек, например, ID способности или 'start' if (typeof subTriggerOrContext === 'string' || typeof subTriggerOrContext === 'number') { subTriggerKey = subTriggerOrContext; // Если subTriggerOrContext - это ID способности, помещаем его в контекст для onOpponentAction if (trigger === 'onOpponentAction' || trigger === 'selfCastAbility') { context.abilityId = subTriggerOrContext; } } else if (typeof subTriggerOrContext === 'object' && subTriggerOrContext !== null) { context = { ...subTriggerOrContext }; // Если ID способности передан в контексте, используем его как subTriggerKey для прямого доступа if (context.abilityId && (trigger === 'selfCastAbility' || trigger === 'onOpponentAction')) { subTriggerKey = context.abilityId; } else if (trigger === 'onBattleState' && typeof context === 'string') { // на случай если GameInstance передает строку для onBattleState subTriggerKey = context; } } // Для basicAttack subTriggerKey может быть 'merciful', 'dominating' или null (тогда general) if (trigger === 'basicAttack' && typeof subTriggerOrContext === 'string') { subTriggerKey = subTriggerOrContext; } // console.log(`[TauntLogic DEBUG] Parsed: trigger=${trigger}, subTriggerKey=${subTriggerKey}, context=`, context); let potentialTaunts = []; if (subTriggerKey !== null && typeof tauntSet === 'object' && !Array.isArray(tauntSet) && tauntSet[subTriggerKey]) { // Если есть subTriggerKey и tauntSet - это объект (а не массив), то получаем вложенный набор tauntSet = tauntSet[subTriggerKey]; } else if (Array.isArray(tauntSet)) { // Если tauntSet уже массив (например, для onOpponentAttackBlocked), используем его как есть potentialTaunts = tauntSet; // Присваиваем сразу } else if (typeof tauntSet === 'object' && tauntSet.general) { // Фоллбэк на general, если subTriggerKey не найден в объекте tauntSet = tauntSet.general; } // Специальная обработка для onOpponentAction с исходом (success/fail) if (trigger === 'onOpponentAction' && typeof tauntSet === 'object' && !Array.isArray(tauntSet) && context.outcome) { if (tauntSet[context.outcome]) { potentialTaunts = tauntSet[context.outcome]; } else { // console.log(`[TauntLogic] No outcome '${context.outcome}' for onOpponentAction, abilityId ${context.abilityId}`); potentialTaunts = []; // Явно пустой, чтобы не упасть ниже } } else if (Array.isArray(tauntSet)) { potentialTaunts = tauntSet; } // Обработка basicAttack (merciful/dominating/general) if (trigger === 'basicAttack' && specificTauntBranch.basicAttack) { // Убедимся что ветка basicAttack существует const basicAttackBranch = specificTauntBranch.basicAttack; if (speakerCharacterKey === 'elena' && opponentKeyForTaunts === 'balard' && currentGameState && currentGameState[GAME_CONFIG.OPPONENT_ID]) { const opponentState = currentGameState[GAME_CONFIG.OPPONENT_ID]; // Балард всегда оппонент для Елены в этом контексте if (opponentState && opponentState.maxHp > 0) { const opponentHpPerc = (opponentState.currentHp / opponentState.maxHp) * 100; if (opponentHpPerc <= configToUse.PLAYER_MERCY_TAUNT_THRESHOLD_PERCENT && basicAttackBranch.dominating) { potentialTaunts = basicAttackBranch.dominating; } else if (basicAttackBranch.merciful) { potentialTaunts = basicAttackBranch.merciful; } else if (basicAttackBranch.general) { // Фоллбэк на general если нет merciful potentialTaunts = basicAttackBranch.general; } } else if (basicAttackBranch.general) { // Если нет HP данных, используем general potentialTaunts = basicAttackBranch.general; } } else if (basicAttackBranch.general) { // Общий случай для basicAttack potentialTaunts = basicAttackBranch.general; } // Если subTriggerKey был ('merciful'/'dominating') и он найден в basicAttackBranch, то tauntSet уже установлен выше // Этот блок if (trigger === 'basicAttack') должен быть более специфичным или объединен с логикой subTriggerKey выше. // Пока оставим как есть, предполагая, что subTriggerKey для basicAttack обрабатывается отдельно. // Если subTriggerKey был 'merciful' или 'dominating', и такой ключ есть в basicAttackBranch, то tauntSet уже должен быть им. if (subTriggerKey && basicAttackBranch[subTriggerKey]) { potentialTaunts = basicAttackBranch[subTriggerKey]; } else if (potentialTaunts.length === 0 && basicAttackBranch.general) { // Если не нашли по subTriggerKey, берем general potentialTaunts = basicAttackBranch.general; } } if (!Array.isArray(potentialTaunts) || potentialTaunts.length === 0) { // console.log(`[TauntLogic] No potential taunts found or empty array for ${speakerCharacterKey} vs ${opponentKeyForTaunts}, trigger: ${trigger}, subTriggerKey: ${subTriggerKey}`); return "(Молчание)"; } const selectedTaunt = potentialTaunts[Math.floor(Math.random() * potentialTaunts.length)]; // console.log(`[TauntLogic] Selected for ${speakerCharacterKey} vs ${opponentKeyForTaunts} (Trigger: ${trigger}, SubTriggerKey: ${subTriggerKey}): "${selectedTaunt}"`); return selectedTaunt || "(Молчание)"; } module.exports = { getRandomTaunt };