151 lines
10 KiB
JavaScript
151 lines
10 KiB
JavaScript
// /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
|
||
}; |