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

303 lines
12 KiB
JavaScript

export class FeedbackUI {
constructor(parentContainer, game) {
this.parentContainer = parentContainer;
this.game = game;
this.logContainer = null;
this.initLogContainer();
}
initLogContainer() {
this.logContainer = document.createElement('div');
Object.assign(this.logContainer.style, {
position: 'absolute',
top: '20%', // Leave space for top HUD
right: '20px',
width: '350px',
height: '60vh', // Fixed height or max height? User said "muy pequeño". Let's give it good vertical space.
maxHeight: 'none',
overflowY: 'auto',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start', // Align text to left
pointerEvents: 'none', // Allow clicking through if needed, but 'auto' for scroll?
// We need pointerEvents auto for scrolling.
pointerEvents: 'auto',
zIndex: '400',
fontFamily: '"Cinzel", serif',
scrollbarWidth: 'thin',
scrollbarColor: '#444 #222'
});
// Add a subtle background
this.logContainer.style.background = 'linear-gradient(to left, rgba(0,0,0,0.8), rgba(0,0,0,0))';
this.logContainer.style.padding = '10px';
this.logContainer.style.borderRadius = '8px';
this.logContainer.style.borderRight = '2px solid #555';
this.parentContainer.appendChild(this.logContainer);
}
addLogMessage(message, type = 'info') {
const entry = document.createElement('div');
Object.assign(entry.style, {
width: '100%',
marginBottom: '6px',
fontSize: '14px',
color: '#ccc',
textShadow: '1px 1px 0 #000',
opacity: '0',
transition: 'opacity 0.3s',
lineHeight: '1.4'
});
// Color coding based on type
if (type === 'combat-hit') entry.style.color = '#ff6666';
if (type === 'combat-miss') entry.style.color = '#aaaaaa';
if (type === 'combat-kill') entry.style.color = '#ff3333';
if (type === 'success') entry.style.color = '#66ff66';
if (type === 'warning') entry.style.color = '#ffcc00';
if (type === 'system') entry.style.color = '#88ccff';
if (type === 'event-log') entry.style.color = '#44ff44'; // Bright Green
entry.innerHTML = message;
this.logContainer.appendChild(entry);
// Auto scroll to bottom
this.logContainer.scrollTop = this.logContainer.scrollHeight;
// Fade In
requestAnimationFrame(() => { entry.style.opacity = '1'; });
// Optional: Fade out very old messages? Or keep history?
// Let's keep history for now, maybe limit children coune
if (this.logContainer.children.length > 50) {
this.logContainer.removeChild(this.logContainer.firstChild);
}
}
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) 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);
}
showEventCard(cardData, callback) {
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.85)', display: 'flex', justifyContent: 'center', alignItems: 'center',
pointerEvents: 'auto', zIndex: '2000'
});
// Card Container
const card = document.createElement('div');
Object.assign(card.style, {
backgroundColor: '#1a1a1a',
backgroundImage: 'repeating-linear-gradient(45deg, #222 25%, transparent 25%, transparent 75%, #222 75%, #222), repeating-linear-gradient(45deg, #222 25%, #1a1a1a 25%, #1a1a1a 75%, #222 75%, #222)',
backgroundPosition: '0 0, 10px 10px',
backgroundSize: '20px 20px',
border: '4px solid #8b0000',
borderRadius: '12px',
padding: '30px',
width: '320px',
textAlign: 'center',
color: '#fff',
fontFamily: '"Cinzel", serif',
boxShadow: '0 0 30px rgba(139, 0, 0, 0.6), inset 0 0 50px rgba(0,0,0,0.8)',
position: 'relative'
});
// Title
const titleEl = document.createElement('h2');
titleEl.textContent = cardData.titulo || "Evento";
Object.assign(titleEl.style, {
marginTop: '0',
marginBottom: '10px',
color: '#ff4444',
textTransform: 'uppercase',
letterSpacing: '2px',
fontSize: '24px',
textShadow: '2px 2px 0 #000'
});
card.appendChild(titleEl);
// Subtitle/Type
if (cardData.tipo) {
const typeEl = document.createElement('div');
typeEl.textContent = cardData.tipo;
Object.assign(typeEl.style, {
fontSize: '12px',
color: '#aaa',
marginBottom: '20px',
textTransform: 'uppercase',
borderBottom: '1px solid #444',
paddingBottom: '5px'
});
card.appendChild(typeEl);
}
// Image Placeholder (Optional)
// const img = document.createElement('div'); ...
// Message
const msgEl = document.createElement('p');
// If it's pure text or HTML
msgEl.innerHTML = cardData.descripcion || cardData.texto || "";
Object.assign(msgEl.style, {
fontSize: '16px',
lineHeight: '1.6',
color: '#ddd',
textAlign: 'justify',
fontStyle: 'italic',
marginBottom: '25px'
});
card.appendChild(msgEl);
// Action Button
const btn = document.createElement('button');
btn.textContent = 'CONTINUAR';
Object.assign(btn.style, {
padding: '12px 30px',
fontSize: '16px',
cursor: 'pointer',
backgroundColor: '#8b0000',
color: '#fff',
border: '2px solid #ff4444',
borderRadius: '4px',
fontFamily: 'inherit',
fontWeight: 'bold',
textTransform: 'uppercase',
boxShadow: '0 4px 0 #440000',
transition: 'transform 0.1s'
});
btn.onmousedown = () => btn.style.transform = 'translateY(2px)';
btn.onmouseup = () => btn.style.transform = 'translateY(0)';
btn.onclick = () => {
if (overlay.parentNode) this.parentContainer.removeChild(overlay);
if (callback) callback();
};
card.appendChild(btn);
overlay.appendChild(card);
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);
}
}