336 lines
23 KiB
JavaScript
336 lines
23 KiB
JavaScript
// /public/js/ui.js
|
||
// Этот файл отвечает за обновление DOM на основе состояния игры,
|
||
// полученного от client.js (который, в свою очередь, получает его от сервера).
|
||
|
||
(function() {
|
||
// --- DOM Элементы ---
|
||
const uiElements = {
|
||
player: { // Панель для персонажа, которым управляет ЭТОТ клиент
|
||
panel: document.getElementById('player-panel'),
|
||
name: document.getElementById('player-name'),
|
||
avatar: document.getElementById('player-panel')?.querySelector('.player-avatar'),
|
||
hpFill: document.getElementById('player-hp-fill'), hpText: document.getElementById('player-hp-text'),
|
||
resourceFill: document.getElementById('player-resource-fill'), resourceText: document.getElementById('player-resource-text'),
|
||
status: document.getElementById('player-status'),
|
||
effectsContainer: document.getElementById('player-effects'),
|
||
buffsList: document.getElementById('player-effects')?.querySelector('.player-buffs'),
|
||
debuffsList: document.getElementById('player-effects')?.querySelector('.player-debuffs')
|
||
},
|
||
opponent: { // Панель для персонажа-противника ЭТОГО клиента
|
||
panel: document.getElementById('opponent-panel'),
|
||
name: document.getElementById('opponent-name'),
|
||
avatar: document.getElementById('opponent-panel')?.querySelector('.opponent-avatar'),
|
||
hpFill: document.getElementById('opponent-hp-fill'), hpText: document.getElementById('opponent-hp-text'),
|
||
resourceFill: document.getElementById('opponent-resource-fill'), resourceText: document.getElementById('opponent-resource-text'),
|
||
status: document.getElementById('opponent-status'),
|
||
effectsContainer: document.getElementById('opponent-effects'),
|
||
buffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-buffs'),
|
||
debuffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-debuffs')
|
||
},
|
||
controls: {
|
||
turnIndicator: document.getElementById('turn-indicator'),
|
||
buttonAttack: document.getElementById('button-attack'),
|
||
buttonBlock: document.getElementById('button-block'),
|
||
abilitiesGrid: document.getElementById('abilities-grid'),
|
||
},
|
||
log: {
|
||
list: document.getElementById('log-list'),
|
||
},
|
||
gameOver: {
|
||
screen: document.getElementById('game-over-screen'),
|
||
message: document.getElementById('result-message'),
|
||
restartButton: document.getElementById('restart-game-button'),
|
||
modalContent: document.getElementById('game-over-screen')?.querySelector('.modal-content')
|
||
},
|
||
gameHeaderTitle: document.querySelector('.game-header h1'),
|
||
playerResourceTypeIcon: document.getElementById('player-resource-bar')?.closest('.stat-bar-container')?.querySelector('.bar-icon i'),
|
||
opponentResourceTypeIcon: document.getElementById('opponent-resource-bar')?.closest('.stat-bar-container')?.querySelector('.bar-icon i'),
|
||
playerResourceBarContainer: document.getElementById('player-resource-bar')?.closest('.stat-bar-container'),
|
||
opponentResourceBarContainer: document.getElementById('opponent-resource-bar')?.closest('.stat-bar-container'),
|
||
};
|
||
|
||
function addToLog(message, type = 'info') {
|
||
const logListElement = uiElements.log.list;
|
||
if (!logListElement) return;
|
||
const li = document.createElement('li');
|
||
li.textContent = message;
|
||
const config = window.GAME_CONFIG || {};
|
||
const logTypeClass = config[`LOG_TYPE_${type.toUpperCase()}`] ? `log-${config[`LOG_TYPE_${type.toUpperCase()}`]}` : `log-${type}`;
|
||
li.className = logTypeClass;
|
||
logListElement.appendChild(li);
|
||
requestAnimationFrame(() => { logListElement.scrollTop = logListElement.scrollHeight; });
|
||
}
|
||
|
||
function updateFighterPanelUI(panelRole, fighterState, fighterBaseStats, isControlledByThisClient) {
|
||
const elements = uiElements[panelRole];
|
||
const config = window.GAME_CONFIG || {};
|
||
if (!elements || !elements.hpFill || !elements.hpText || !elements.resourceFill || !elements.resourceText || !elements.status || !fighterState || !fighterBaseStats) {
|
||
console.warn(`updateFighterPanelUI: Отсутствуют элементы/состояние/статы для панели ${panelRole}.`);
|
||
return;
|
||
}
|
||
|
||
if (elements.name) {
|
||
let iconClass = 'fa-question'; let accentColor = 'var(--text-muted)';
|
||
const characterKey = fighterBaseStats.characterKey;
|
||
if (characterKey === 'elena') { iconClass = 'fa-hat-wizard icon-player'; accentColor = 'var(--accent-player)'; }
|
||
else if (characterKey === 'almagest') { iconClass = 'fa-staff-aesculapius icon-almagest'; accentColor = 'var(--accent-almagest)'; } // Используем новый цвет
|
||
else if (characterKey === 'balard') { iconClass = 'fa-khanda icon-opponent'; accentColor = 'var(--accent-opponent)'; }
|
||
let nameHtml = `<i class="fas ${iconClass}"></i> ${fighterBaseStats.name}`;
|
||
if (isControlledByThisClient) nameHtml += " (Вы)";
|
||
elements.name.innerHTML = nameHtml; elements.name.style.color = accentColor;
|
||
}
|
||
|
||
if (elements.avatar && fighterBaseStats.avatarPath) elements.avatar.src = fighterBaseStats.avatarPath;
|
||
else if (elements.avatar) elements.avatar.src = 'images/default_avatar.png';
|
||
|
||
const maxHp = Math.max(1, fighterBaseStats.maxHp);
|
||
const maxRes = Math.max(1, fighterBaseStats.maxResource);
|
||
const currentHp = Math.max(0, fighterState.currentHp);
|
||
const currentRes = Math.max(0, fighterState.currentResource);
|
||
elements.hpFill.style.width = `${(currentHp / maxHp) * 100}%`;
|
||
elements.hpText.textContent = `${Math.round(currentHp)} / ${fighterBaseStats.maxHp}`;
|
||
elements.resourceFill.style.width = `${(currentRes / maxRes) * 100}%`;
|
||
elements.resourceText.textContent = `${Math.round(currentRes)} / ${fighterBaseStats.maxResource}`;
|
||
|
||
const resourceBarContainer = elements[`${panelRole}ResourceBarContainer`];
|
||
const resourceIconElement = elements[`${panelRole}ResourceTypeIcon`];
|
||
if (resourceBarContainer && resourceIconElement) {
|
||
resourceBarContainer.classList.remove('mana', 'stamina', 'dark-energy');
|
||
let resourceClass = 'mana'; let iconClass = 'fa-flask';
|
||
if (fighterBaseStats.resourceName === 'Ярость') { resourceClass = 'stamina'; iconClass = 'fa-fire-alt'; }
|
||
else if (fighterBaseStats.resourceName === 'Темная Энергия') { resourceClass = 'dark-energy'; iconClass = 'fa-skull'; }
|
||
resourceBarContainer.classList.add(resourceClass);
|
||
resourceIconElement.className = `fas ${iconClass}`;
|
||
}
|
||
|
||
const statusText = fighterState.isBlocking ? (config.STATUS_BLOCKING || 'Защищается') : (config.STATUS_READY || 'Готов(а)');
|
||
elements.status.textContent = statusText;
|
||
elements.status.classList.toggle(config.CSS_CLASS_BLOCKING || 'blocking', fighterState.isBlocking);
|
||
|
||
if (elements.panel) {
|
||
let glowColorVar = '--panel-glow-opponent'; let borderColorVar = '--accent-opponent';
|
||
if (fighterBaseStats.characterKey === 'elena') { glowColorVar = '--panel-glow-player'; borderColorVar = '--accent-player'; }
|
||
else if (fighterBaseStats.characterKey === 'almagest') { glowColorVar = '--panel-glow-opponent'; borderColorVar = 'var(--accent-almagest)'; } // Цвет рамки Альмагест
|
||
elements.panel.style.borderColor = borderColorVar; // Прямое присвоение, т.к. var() не сработает для accent-almagest если он не в :root
|
||
elements.panel.style.boxShadow = `0 0 15px var(${glowColorVar}), inset 0 0 10px rgba(0, 0, 0, 0.3)`;
|
||
}
|
||
}
|
||
|
||
function generateEffectsHTML(effectsArray) {
|
||
const config = window.GAME_CONFIG || {};
|
||
if (!effectsArray || effectsArray.length === 0) return 'Нет';
|
||
return effectsArray.map(eff => {
|
||
let effectClasses = config.CSS_CLASS_EFFECT || 'effect';
|
||
const title = `${eff.name}${eff.description ? ` - ${eff.description}` : ''} (Осталось: ${eff.turnsLeft} х.)`;
|
||
const displayText = `${eff.name} (${eff.turnsLeft} х.)`;
|
||
if (eff.type === config.ACTION_TYPE_DISABLE || eff.isFullSilence || eff.id.startsWith('playerSilencedOn_')) effectClasses += ' effect-stun';
|
||
else if (eff.type === config.ACTION_TYPE_DEBUFF || (eff.power && eff.power < 0) || eff.id.startsWith('effect_')) effectClasses += ' effect-debuff';
|
||
else if (eff.grantsBlock) effectClasses += ' effect-block';
|
||
else effectClasses += ' effect-buff';
|
||
return `<span class="${effectClasses}" title="${title}">${displayText}</span>`;
|
||
}).join(' ');
|
||
}
|
||
|
||
function updateEffectsUI(currentGameState) {
|
||
if (!currentGameState || !uiElements.player.buffsList || !uiElements.opponent.buffsList) return;
|
||
const mySlotId = window.myPlayerId; // Наш слот ('player' или 'opponent')
|
||
const opponentSlotId = mySlotId === window.GAME_CONFIG.PLAYER_ID ? window.GAME_CONFIG.OPPONENT_ID : window.GAME_CONFIG.PLAYER_ID;
|
||
|
||
const myState = currentGameState[mySlotId];
|
||
if (uiElements.player && myState && myState.activeEffects) {
|
||
uiElements.player.buffsList.innerHTML = generateEffectsHTML(myState.activeEffects.filter(e => e.type === window.GAME_CONFIG.ACTION_TYPE_BUFF || e.grantsBlock));
|
||
uiElements.player.debuffsList.innerHTML = generateEffectsHTML(myState.activeEffects.filter(e => e.type !== window.GAME_CONFIG.ACTION_TYPE_BUFF && !e.grantsBlock));
|
||
}
|
||
|
||
const opponentState = currentGameState[opponentSlotId];
|
||
if (uiElements.opponent && opponentState && opponentState.activeEffects) {
|
||
uiElements.opponent.buffsList.innerHTML = generateEffectsHTML(opponentState.activeEffects.filter(e => e.type === window.GAME_CONFIG.ACTION_TYPE_BUFF || e.grantsBlock));
|
||
uiElements.opponent.debuffsList.innerHTML = generateEffectsHTML(opponentState.activeEffects.filter(e => e.type !== window.GAME_CONFIG.ACTION_TYPE_BUFF && !e.grantsBlock));
|
||
}
|
||
}
|
||
|
||
function updateUI() {
|
||
const currentGameState = window.gameState;
|
||
const gameDataGlobal = window.gameData;
|
||
const configGlobal = window.GAME_CONFIG;
|
||
const myActualPlayerId = window.myPlayerId; // Слот, который занимает ЭТОТ клиент ('player' или 'opponent')
|
||
|
||
if (!currentGameState || !gameDataGlobal || !configGlobal || !myActualPlayerId) {
|
||
console.warn("updateUI: Отсутствуют глобальные gameState, gameData, GAME_CONFIG или myActualPlayerId.");
|
||
return;
|
||
}
|
||
if (!uiElements.player.panel || !uiElements.opponent.panel || !uiElements.controls.turnIndicator || !uiElements.controls.abilitiesGrid || !uiElements.log.list) {
|
||
console.warn("updateUI: Некоторые базовые uiElements не найдены.");
|
||
return;
|
||
}
|
||
|
||
// Определяем ID слота того, кто сейчас ходит
|
||
const actorSlotWhoseTurnItIs = currentGameState.isPlayerTurn ? configGlobal.PLAYER_ID : configGlobal.OPPONENT_ID;
|
||
|
||
// Обновление панелей бойцов
|
||
const opponentActualSlotId = myActualPlayerId === configGlobal.PLAYER_ID ? configGlobal.OPPONENT_ID : configGlobal.PLAYER_ID;
|
||
updateFighterPanelUI('player', currentGameState[myActualPlayerId], gameDataGlobal.playerBaseStats, true);
|
||
updateFighterPanelUI('opponent', currentGameState[opponentActualSlotId], gameDataGlobal.opponentBaseStats, false);
|
||
|
||
updateEffectsUI(currentGameState);
|
||
|
||
if (uiElements.gameHeaderTitle && gameDataGlobal.playerBaseStats && gameDataGlobal.opponentBaseStats) {
|
||
const myName = gameDataGlobal.playerBaseStats.name;
|
||
const opponentName = gameDataGlobal.opponentBaseStats.name;
|
||
const myKey = gameDataGlobal.playerBaseStats.characterKey;
|
||
const opponentKey = gameDataGlobal.opponentBaseStats.characterKey;
|
||
let myClass = 'title-player';
|
||
if (myKey === 'elena') myClass = 'title-enchantress';
|
||
else if (myKey === 'almagest') myClass = 'title-sorceress';
|
||
let opponentClass = 'title-opponent';
|
||
if (opponentKey === 'elena') opponentClass = 'title-enchantress';
|
||
else if (opponentKey === 'almagest') opponentClass = 'title-sorceress';
|
||
else if (opponentKey === 'balard') opponentClass = 'title-knight';
|
||
uiElements.gameHeaderTitle.innerHTML = `<span class="${myClass}">${myName}</span> <span class="separator"><i class="fas fa-fist-raised"></i></span> <span class="${opponentClass}">${opponentName}</span>`;
|
||
}
|
||
|
||
if (uiElements.controls.turnIndicator) {
|
||
const currentTurnActorState = currentGameState[actorSlotWhoseTurnItIs];
|
||
const currentTurnName = currentTurnActorState?.name || 'Неизвестно';
|
||
uiElements.controls.turnIndicator.textContent = `Ход: ${currentTurnName}`;
|
||
const currentTurnCharacterKey = currentTurnActorState?.characterKey;
|
||
let turnColor = 'var(--turn-color)';
|
||
if (currentTurnCharacterKey === 'elena') turnColor = 'var(--accent-player)';
|
||
else if (currentTurnCharacterKey === 'almagest') turnColor = 'var(--accent-almagest)';
|
||
else if (currentTurnCharacterKey === 'balard') turnColor = 'var(--accent-opponent)';
|
||
uiElements.controls.turnIndicator.style.color = turnColor;
|
||
}
|
||
|
||
const canThisClientAct = actorSlotWhoseTurnItIs === myActualPlayerId;
|
||
const isGameActive = !currentGameState.isGameOver;
|
||
|
||
if (uiElements.controls.buttonAttack) {
|
||
uiElements.controls.buttonAttack.disabled = !(canThisClientAct && isGameActive);
|
||
const myCharKey = gameDataGlobal.playerBaseStats.characterKey;
|
||
const myState = currentGameState[myActualPlayerId];
|
||
let attackBuffId = null;
|
||
if (myCharKey === 'elena') attackBuffId = configGlobal.ABILITY_ID_NATURE_STRENGTH;
|
||
else if (myCharKey === 'almagest') attackBuffId = configGlobal.ABILITY_ID_ALMAGEST_BUFF_ATTACK;
|
||
if (attackBuffId && myState) {
|
||
const isAttackBuffReady = myState.activeEffects.some(eff => eff.id === attackBuffId && !eff.justCast);
|
||
uiElements.controls.buttonAttack.classList.toggle(configGlobal.CSS_CLASS_ATTACK_BUFFED || 'attack-buffed', isAttackBuffReady && canThisClientAct && isGameActive);
|
||
} else {
|
||
uiElements.controls.buttonAttack.classList.remove(configGlobal.CSS_CLASS_ATTACK_BUFFED || 'attack-buffed');
|
||
}
|
||
}
|
||
if (uiElements.controls.buttonBlock) uiElements.controls.buttonBlock.disabled = true;
|
||
|
||
const actingPlayerState = currentGameState[myActualPlayerId];
|
||
const actingPlayerAbilities = gameDataGlobal.playerAbilities;
|
||
const actingPlayerResourceName = gameDataGlobal.playerBaseStats.resourceName;
|
||
|
||
uiElements.controls.abilitiesGrid?.querySelectorAll(`.${configGlobal.CSS_CLASS_ABILITY_BUTTON || 'ability-button'}`).forEach(button => {
|
||
if (!(button instanceof HTMLButtonElement) || !actingPlayerState || !actingPlayerAbilities) {
|
||
if(button instanceof HTMLButtonElement) button.disabled = true;
|
||
return;
|
||
}
|
||
const abilityId = button.dataset.abilityId;
|
||
const ability = actingPlayerAbilities.find(ab => ab.id === abilityId);
|
||
if (!ability) { button.disabled = true; return; }
|
||
|
||
const hasEnoughResource = actingPlayerState.currentResource >= ability.cost;
|
||
const isBuffAlreadyActive = ability.type === configGlobal.ACTION_TYPE_BUFF && actingPlayerState.activeEffects.some(eff => eff.id === ability.id);
|
||
const isOnCooldown = (actingPlayerState.abilityCooldowns?.[ability.id] || 0) > 0;
|
||
const isGenerallySilenced = actingPlayerState.activeEffects.some(eff => eff.isFullSilence && eff.turnsLeft > 0);
|
||
const isSpecificallySilenced = actingPlayerState.disabledAbilities?.some(dis => dis.abilityId === abilityId && dis.turnsLeft > 0);
|
||
const isSilenced = isGenerallySilenced || isSpecificallySilenced;
|
||
const silenceTurnsLeft = isGenerallySilenced ? (actingPlayerState.activeEffects.find(eff => eff.isFullSilence)?.turnsLeft || 0)
|
||
: (isSpecificallySilenced ? (actingPlayerState.disabledAbilities.find(dis => dis.abilityId === abilityId)?.turnsLeft || 0) : 0);
|
||
|
||
let isDisabledByDebuffOnTarget = false;
|
||
const opponentStateForDebuffCheck = currentGameState[opponentActualSlotId];
|
||
if ((ability.id === configGlobal.ABILITY_ID_SEAL_OF_WEAKNESS || ability.id === configGlobal.ABILITY_ID_ALMAGEST_DEBUFF) && opponentStateForDebuffCheck) {
|
||
const effectIdForDebuff = 'effect_' + ability.id;
|
||
isDisabledByDebuffOnTarget = opponentStateForDebuffCheck.activeEffects.some(e => e.id === effectIdForDebuff);
|
||
}
|
||
|
||
button.disabled = !(canThisClientAct && isGameActive) || !hasEnoughResource || isBuffAlreadyActive || isSilenced || isOnCooldown || isDisabledByDebuffOnTarget;
|
||
button.classList.remove(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced', configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown');
|
||
const cooldownDisplay = button.querySelector('.ability-cooldown-display');
|
||
|
||
if (isOnCooldown) {
|
||
button.classList.add(configGlobal.CSS_CLASS_ABILITY_ON_COOLDOWN||'is-on-cooldown');
|
||
if (cooldownDisplay) { cooldownDisplay.textContent = `КД: ${actingPlayerState.abilityCooldowns[ability.id]}`; cooldownDisplay.style.display = 'block'; }
|
||
} else if (isSilenced) {
|
||
button.classList.add(configGlobal.CSS_CLASS_ABILITY_SILENCED||'is-silenced');
|
||
if (cooldownDisplay) { cooldownDisplay.textContent = `Безм: ${silenceTurnsLeft}`; cooldownDisplay.style.display = 'block'; }
|
||
} else {
|
||
if (cooldownDisplay) cooldownDisplay.style.display = 'none';
|
||
button.classList.toggle(configGlobal.CSS_CLASS_NOT_ENOUGH_RESOURCE||'not-enough-resource', !hasEnoughResource && !isBuffAlreadyActive && !isDisabledByDebuffOnTarget);
|
||
button.classList.toggle(configGlobal.CSS_CLASS_BUFF_IS_ACTIVE||'buff-is-active', isBuffAlreadyActive && !isDisabledByDebuffOnTarget);
|
||
}
|
||
|
||
let titleText = `${ability.name} (${ability.cost} ${actingPlayerResourceName})`;
|
||
let descriptionText = ability.description;
|
||
if (typeof ability.descriptionFunction === 'function') {
|
||
descriptionText = ability.descriptionFunction(configGlobal, gameDataGlobal.opponentBaseStats);
|
||
}
|
||
if (descriptionText) titleText += ` - ${descriptionText}`;
|
||
let abilityBaseCooldown = ability.cooldown;
|
||
if (ability.internalCooldownFromConfig && configGlobal[ability.internalCooldownFromConfig]) abilityBaseCooldown = configGlobal[ability.internalCooldownFromConfig];
|
||
else if (ability.internalCooldownValue) abilityBaseCooldown = ability.internalCooldownValue;
|
||
if (abilityBaseCooldown) titleText += ` (КД: ${abilityBaseCooldown} х.)`;
|
||
|
||
if (isOnCooldown) titleText = `${ability.name} - На перезарядке! Осталось: ${actingPlayerState.abilityCooldowns[ability.id]} х.`;
|
||
else if (isSilenced) titleText = `Безмолвие! Осталось: ${silenceTurnsLeft} х.`;
|
||
else if (isBuffAlreadyActive) {
|
||
const activeEffect = actingPlayerState.activeEffects.find(eff => eff.id === abilityId);
|
||
titleText = `Эффект "${ability.name}" уже активен${activeEffect ? ` (${activeEffect.turnsLeft} х.)` : ''}`;
|
||
} else if (isDisabledByDebuffOnTarget && opponentStateForDebuffCheck) {
|
||
const activeDebuff = opponentStateForDebuffCheck.activeEffects.find(e => e.id === 'effect_' + ability.id);
|
||
titleText = `Эффект "${ability.name}" уже наложен на ${opponentStateForDebuffCheck.name}${activeDebuff ? ` (${activeDebuff.turnsLeft} х.)` : ''}`;
|
||
}
|
||
button.setAttribute('title', titleText);
|
||
});
|
||
}
|
||
|
||
function showGameOver(playerWon, reason = "") {
|
||
const config = window.GAME_CONFIG || {};
|
||
const gameDataGlobal = window.gameData || {};
|
||
const currentGameState = window.gameState;
|
||
const gameOverScreenElement = uiElements.gameOver.screen;
|
||
if (!gameOverScreenElement || !currentGameState) return;
|
||
|
||
const resultMsgElement = uiElements.gameOver.message;
|
||
const opponentPanelElement = uiElements.opponent.panel;
|
||
const myName = gameDataGlobal.playerBaseStats?.name || "Игрок";
|
||
const opponentName = gameDataGlobal.opponentBaseStats?.name || "Противник";
|
||
const opponentCharacterKey = gameDataGlobal.opponentBaseStats?.characterKey;
|
||
|
||
if (resultMsgElement) {
|
||
let winText = `Победа! ${myName} празднует!`;
|
||
let loseText = `Поражение! ${opponentName} оказался(лась) сильнее!`;
|
||
if (reason === 'opponent_disconnected') winText = `${opponentName} покинул(а) игру. Победа присуждается вам!`;
|
||
resultMsgElement.textContent = playerWon ? winText : loseText;
|
||
resultMsgElement.style.color = playerWon ? 'var(--heal-color)' : 'var(--damage-color)';
|
||
}
|
||
|
||
if (opponentPanelElement) {
|
||
opponentPanelElement.classList.remove('dissolving');
|
||
if (playerWon && reason !== 'opponent_disconnected' && (opponentCharacterKey === 'balard' || opponentCharacterKey === 'almagest')) {
|
||
opponentPanelElement.classList.add('dissolving');
|
||
}
|
||
}
|
||
|
||
setTimeout(() => {
|
||
gameOverScreenElement.classList.remove(config.CSS_CLASS_HIDDEN || 'hidden');
|
||
requestAnimationFrame(() => {
|
||
gameOverScreenElement.style.opacity = '0';
|
||
setTimeout(() => {
|
||
gameOverScreenElement.style.opacity = '1';
|
||
if (uiElements.gameOver.modalContent) {
|
||
uiElements.gameOver.modalContent.style.transform = 'scale(1) translateY(0)';
|
||
uiElements.gameOver.modalContent.style.opacity = '1';
|
||
}
|
||
}, config.MODAL_TRANSITION_DELAY || 10);
|
||
});
|
||
}, config.DELAY_BEFORE_VICTORY_MODAL || 1500);
|
||
}
|
||
|
||
window.gameUI = { uiElements, addToLog, updateUI, showGameOver };
|
||
})(); |