Files
WarhammerQuest/src/view/ui/FeedbackUI.js

197 lines
8.1 KiB
JavaScript

export class FeedbackUI {
constructor(parentContainer, game) {
this.parentContainer = parentContainer;
this.game = game; // Needed for resolving hero names/ids in logs?
this.combatLogContainer = null;
this.initCombatLogContainer();
}
initCombatLogContainer() {
this.combatLogContainer = document.createElement('div');
Object.assign(this.combatLogContainer.style, {
position: 'absolute',
top: '140px', // Below the top status panel
left: '50%',
transform: 'translateX(-50%)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
pointerEvents: 'none',
width: '100%',
maxWidth: '600px',
zIndex: '500' // Below modals
});
this.parentContainer.appendChild(this.combatLogContainer);
}
showModal(title, message, onClose) {
const overlay = document.createElement('div');
Object.assign(overlay.style, {
position: 'absolute', top: '0', left: '0', width: '100%', height: '100%',
backgroundColor: 'rgba(0, 0, 0, 0.7)', display: 'flex', justifyContent: 'center', alignItems: 'center',
pointerEvents: 'auto', zIndex: '1000'
});
const content = document.createElement('div');
Object.assign(content.style, {
backgroundColor: '#222', border: '2px solid #888', borderRadius: '8px', padding: '20px',
width: '300px', textAlign: 'center', color: '#fff', fontFamily: 'sans-serif'
});
const titleEl = document.createElement('h2');
titleEl.textContent = title;
Object.assign(titleEl.style, { marginTop: '0', color: '#f44' });
content.appendChild(titleEl);
const msgEl = document.createElement('p');
msgEl.innerHTML = message;
Object.assign(msgEl.style, { fontSize: '16px', lineHeight: '1.5' });
content.appendChild(msgEl);
const btn = document.createElement('button');
btn.textContent = 'Entendido';
Object.assign(btn.style, {
marginTop: '20px', padding: '10px 20px', fontSize: '16px', cursor: 'pointer',
backgroundColor: '#444', color: '#fff', border: '1px solid #888'
});
btn.onclick = () => {
if (overlay.parentNode /** Checks if attached */) this.parentContainer.removeChild(overlay);
if (onClose) onClose();
};
content.appendChild(btn);
overlay.appendChild(content);
this.parentContainer.appendChild(overlay);
}
showConfirm(title, message, onConfirm) {
const overlay = document.createElement('div');
Object.assign(overlay.style, {
position: 'absolute', top: '0', left: '0', width: '100%', height: '100%',
backgroundColor: 'rgba(0, 0, 0, 0.7)', display: 'flex', justifyContent: 'center', alignItems: 'center',
pointerEvents: 'auto', zIndex: '1000'
});
const content = document.createElement('div');
Object.assign(content.style, {
backgroundColor: '#222', border: '2px solid #888', borderRadius: '8px', padding: '20px',
width: '300px', textAlign: 'center', color: '#fff', fontFamily: 'sans-serif'
});
const titleEl = document.createElement('h2');
titleEl.textContent = title;
Object.assign(titleEl.style, { marginTop: '0', color: '#f44' });
content.appendChild(titleEl);
const msgEl = document.createElement('p');
msgEl.innerHTML = message;
Object.assign(msgEl.style, { fontSize: '16px', lineHeight: '1.5' });
content.appendChild(msgEl);
const buttons = document.createElement('div');
Object.assign(buttons.style, { display: 'flex', justifyContent: 'space-around', marginTop: '20px' });
const cancelBtn = document.createElement('button');
cancelBtn.textContent = 'Cancelar';
Object.assign(cancelBtn.style, {
padding: '10px 20px', fontSize: '16px', cursor: 'pointer',
backgroundColor: '#555', color: '#fff', border: '1px solid #888'
});
cancelBtn.onclick = () => { this.parentContainer.removeChild(overlay); };
buttons.appendChild(cancelBtn);
const confirmBtn = document.createElement('button');
confirmBtn.textContent = 'Aceptar';
Object.assign(confirmBtn.style, {
padding: '10px 20px', fontSize: '16px', cursor: 'pointer',
backgroundColor: '#2a5', color: '#fff', border: '1px solid #888'
});
confirmBtn.onclick = () => {
if (onConfirm) onConfirm();
this.parentContainer.removeChild(overlay);
};
buttons.appendChild(confirmBtn);
content.appendChild(buttons);
overlay.appendChild(content);
this.parentContainer.appendChild(overlay);
}
showTemporaryMessage(title, message, duration = 2000) {
const modal = document.createElement('div');
Object.assign(modal.style, {
position: 'absolute', top: '25%', left: '50%', transform: 'translate(-50%, -50%)',
backgroundColor: 'rgba(139, 0, 0, 0.9)', color: '#fff', padding: '15px 30px',
borderRadius: '8px', border: '2px solid #ff4444', fontFamily: '"Cinzel", serif',
fontSize: '20px', textShadow: '2px 2px 4px black', zIndex: '2000', pointerEvents: 'none',
opacity: '0', transition: 'opacity 0.5s ease-in-out'
});
modal.innerHTML = `
<h3 style="margin:0; text-align:center; color: #FFD700; text-transform: uppercase;">⚠️ ${title}</h3>
<div style="margin-top:5px; font-size: 16px;">${message}</div>
`;
document.body.appendChild(modal);
requestAnimationFrame(() => { modal.style.opacity = '1'; });
setTimeout(() => {
modal.style.opacity = '0';
setTimeout(() => {
if (modal.parentNode) document.body.removeChild(modal);
}, 500);
}, duration);
}
showCombatLog(log) {
const isHit = log.hitSuccess;
const color = isHit ? '#ff4444' : '#aaaaaa';
let detailHtml = '';
if (isHit) {
if (log.woundsCaused > 0) {
detailHtml = `<div style="font-size: 24px; color: #ff0000; font-weight:bold;">-${log.woundsCaused} HP</div>`;
} else {
detailHtml = `<div style="font-size: 20px; color: #aaa;">Sin Heridas (Armadura)</div>`;
}
} else {
detailHtml = `<div style="font-size: 18px; color: #888;">Esquivado / Fallado</div>`;
}
// We create a new log element or update a singleton?
// The original logic updated a SINGLE notification area.
// Let's create a transient toast style log here, appending to container.
const logItem = document.createElement('div');
Object.assign(logItem.style, {
backgroundColor: 'rgba(0,0,0,0.9)', padding: '15px', border: `2px solid ${color}`,
borderRadius: '5px', textAlign: 'center', minWidth: '250px', marginBottom: '10px',
fontFamily: '"Cinzel", serif', opacity: '0', transition: 'opacity 0.3s'
});
logItem.innerHTML = `
<div style="font-size: 18px; color: ${color}; margin-bottom: 5px; text-transform:uppercase;">${log.attackerId.split('_')[0]} ATACA</div>
${detailHtml}
<div style="font-size: 14px; color: #ccc; margin-top:5px;">${log.message}</div>
`;
// Clear previous logs to act like the single notification area of before, OR stack them?
// Original behavior was overwrite `innerHTML`. I should stick to that to avoid spam.
// So I will clear `combatLogContainer` before adding.
this.combatLogContainer.innerHTML = '';
this.combatLogContainer.appendChild(logItem);
// Fade in
requestAnimationFrame(() => { logItem.style.opacity = '1'; });
// Fade out
setTimeout(() => {
logItem.style.opacity = '0';
// We don't remove immediately to avoid layout jumps if another comes in,
// but we cleared logic above.
}, 3500);
}
}