Current directory: /home/klas4s23/domains/585455.klas4s23.mid-ica.nl/public_html/Gastenboek/uploads
// ========================================
// ============= HUD CLASS ================
// ========================================
import { CONFIG } from '../config.js';
export class HUD {
constructor(car, collectibles) {
this.car = car;
this.collectibles = collectibles;
// DOM elements
this.hudContainer = null;
this.speedometer = null;
this.collectibleCounter = null;
this.minimap = null;
this.minimapCanvas = null;
this.minimapContext = null;
this.notificationContainer = null;
this.achievementContainer = null;
// State
this.collectedCount = 0;
this.totalCollectibles = collectibles.length;
this.notifications = [];
this.init();
}
init() {
this.createHUD();
this.createMinimap();
this.createNotifications();
}
// ========== HUD CREATION ==========
createHUD() {
// Main HUD container
this.hudContainer = document.createElement('div');
this.hudContainer.id = 'hud-container';
this.hudContainer.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 50;
font-family: 'Poppins', sans-serif;
`;
document.body.appendChild(this.hudContainer);
// Speedometer
this.createSpeedometer();
// Collectible counter
this.createCollectibleCounter();
// Controls info
this.createControlsInfo();
}
createSpeedometer() {
this.speedometer = document.createElement('div');
this.speedometer.id = 'speedometer';
this.speedometer.style.cssText = `
position: absolute;
bottom: 30px;
right: 30px;
background: rgba(0, 0, 0, 0.7);
padding: 20px 30px;
border-radius: 15px;
color: white;
backdrop-filter: blur(10px);
`;
this.speedometer.innerHTML = `
<div style="font-size: 14px; opacity: 0.8; margin-bottom: 5px;">Speed</div>
<div style="font-size: 36px; font-weight: bold; color: ${CONFIG.colors.first};">
<span id="speed-value">0</span>
<span style="font-size: 20px; opacity: 0.8;">km/h</span>
</div>
<div style="
width: 200px;
height: 6px;
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
margin-top: 10px;
overflow: hidden;
">
<div id="speed-bar" style="
height: 100%;
width: 0%;
background: ${CONFIG.colors.first};
transition: width 0.1s ease;
border-radius: 3px;
"></div>
</div>
`;
this.hudContainer.appendChild(this.speedometer);
}
createCollectibleCounter() {
this.collectibleCounter = document.createElement('div');
this.collectibleCounter.id = 'collectible-counter';
this.collectibleCounter.style.cssText = `
position: absolute;
top: 30px;
right: 30px;
background: rgba(0, 0, 0, 0.7);
padding: 15px 25px;
border-radius: 15px;
color: white;
backdrop-filter: blur(10px);
display: flex;
align-items: center;
gap: 15px;
`;
this.collectibleCounter.innerHTML = `
<div style="
width: 40px;
height: 40px;
background: ${CONFIG.colors.first};
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
">★</div>
<div>
<div style="font-size: 12px; opacity: 0.8;">Collectibles</div>
<div style="font-size: 20px; font-weight: bold;">
<span id="collected-count">0</span>/<span id="total-count">${this.totalCollectibles}</span>
</div>
</div>
`;
this.hudContainer.appendChild(this.collectibleCounter);
}
createControlsInfo() {
const controls = document.createElement('div');
controls.id = 'controls-info';
controls.style.cssText = `
position: absolute;
bottom: 30px;
left: 30px;
background: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 15px;
color: white;
backdrop-filter: blur(10px);
font-size: 14px;
line-height: 1.8;
`;
controls.innerHTML = `
<div style="font-weight: bold; margin-bottom: 10px; color: ${CONFIG.colors.first};">
Controls
</div>
<div><span style="opacity: 0.8;">WASD / Arrows:</span> Move</div>
<div><span style="opacity: 0.8;">Space:</span> Brake</div>
<div><span style="opacity: 0.8;">L:</span> Headlights</div>
<div><span style="opacity: 0.8;">E:</span> Interact</div>
`;
this.hudContainer.appendChild(controls);
}
// ========== MINIMAP ==========
createMinimap() {
const minimapContainer = document.createElement('div');
minimapContainer.id = 'minimap-container';
minimapContainer.style.cssText = `
position: absolute;
top: 30px;
left: 30px;
background: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 15px;
backdrop-filter: blur(10px);
`;
this.minimapCanvas = document.createElement('canvas');
this.minimapCanvas.width = 200;
this.minimapCanvas.height = 200;
this.minimapCanvas.style.cssText = `
display: block;
border-radius: 10px;
`;
minimapContainer.appendChild(this.minimapCanvas);
this.hudContainer.appendChild(minimapContainer);
this.minimapContext = this.minimapCanvas.getContext('2d');
}
updateMinimap() {
if (!this.minimapContext) return;
const ctx = this.minimapContext;
const canvas = this.minimapCanvas;
const worldSize = CONFIG.world.size;
const scale = canvas.width / worldSize;
// Clear
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Background
ctx.fillStyle = 'rgba(222, 222, 222, 0.3)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Roads
ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)';
ctx.lineWidth = CONFIG.world.roadWidth * scale;
// Horizontal road
ctx.beginPath();
ctx.moveTo(0, canvas.height / 2);
ctx.lineTo(canvas.width, canvas.height / 2);
ctx.stroke();
// Vertical road
ctx.beginPath();
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.stroke();
// Collectibles
this.collectibles.forEach(collectible => {
if (!collectible.isCollected()) {
const pos = collectible.getPosition();
const x = (pos.x + worldSize / 2) * scale;
const y = (pos.z + worldSize / 2) * scale;
ctx.fillStyle = CONFIG.colors.first;
ctx.beginPath();
ctx.arc(x, y, 4, 0, Math.PI * 2);
ctx.fill();
}
});
// Car
const carPos = this.car.getPosition();
const carX = (carPos.x + worldSize / 2) * scale;
const carY = (carPos.z + worldSize / 2) * scale;
const carRot = this.car.getRotation();
ctx.save();
ctx.translate(carX, carY);
ctx.rotate(-carRot);
// Car triangle
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.moveTo(0, -8);
ctx.lineTo(-5, 5);
ctx.lineTo(5, 5);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = CONFIG.colors.first;
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
// ========== NOTIFICATIONS ==========
createNotifications() {
this.notificationContainer = document.createElement('div');
this.notificationContainer.id = 'notification-container';
this.notificationContainer.style.cssText = `
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
z-index: 100;
`;
document.body.appendChild(this.notificationContainer);
this.achievementContainer = document.createElement('div');
this.achievementContainer.id = 'achievement-container';
this.achievementContainer.style.cssText = `
position: absolute;
bottom: 150px;
right: 30px;
width: 300px;
pointer-events: none;
z-index: 100;
`;
document.body.appendChild(this.achievementContainer);
}
showNotification(message, duration = 2000) {
const notification = document.createElement('div');
notification.style.cssText = `
background: ${CONFIG.colors.first};
color: white;
padding: 20px 40px;
border-radius: 15px;
font-size: 24px;
font-weight: bold;
text-align: center;
animation: slideIn 0.5s ease, slideOut 0.5s ease ${duration - 500}ms;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
`;
notification.textContent = message;
// Add animation keyframes if not exist
if (!document.getElementById('notification-animations')) {
const style = document.createElement('style');
style.id = 'notification-animations';
style.textContent = `
@keyframes slideIn {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(20px); }
}
`;
document.head.appendChild(style);
}
this.notificationContainer.appendChild(notification);
setTimeout(() => {
notification.remove();
}, duration);
}
showAchievement(achievement) {
const achievementEl = document.createElement('div');
achievementEl.style.cssText = `
background: rgba(0, 0, 0, 0.9);
color: white;
padding: 15px 20px;
border-radius: 10px;
margin-bottom: 10px;
border-left: 4px solid ${CONFIG.colors.first};
animation: slideInRight 0.5s ease, slideOutRight 0.5s ease 2.5s;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
`;
achievementEl.innerHTML = `
<div style="font-size: 12px; opacity: 0.8; margin-bottom: 5px;">
🏆 Achievement Unlocked!
</div>
<div style="font-weight: bold; margin-bottom: 3px;">
${achievement.name}
</div>
<div style="font-size: 12px; opacity: 0.9;">
${achievement.description}
</div>
`;
// Add animation keyframes if not exist
if (!document.getElementById('achievement-animations')) {
const style = document.createElement('style');
style.id = 'achievement-animations';
style.textContent = `
@keyframes slideInRight {
from { opacity: 0; transform: translateX(100px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes slideOutRight {
from { opacity: 1; transform: translateX(0); }
to { opacity: 0; transform: translateX(100px); }
}
`;
document.head.appendChild(style);
}
this.achievementContainer.appendChild(achievementEl);
setTimeout(() => {
achievementEl.remove();
}, 3000);
}
// ========== UPDATE ==========
update() {
this.updateSpeedometer();
this.updateCollectibleCounter();
this.updateMinimap();
}
updateSpeedometer() {
const speed = this.car.getSpeed();
const maxSpeed = CONFIG.car.maxSpeed;
// Convert to km/h for display (arbitrary conversion)
const speedKmh = Math.round(speed * 200);
document.getElementById('speed-value').textContent = speedKmh;
const speedPercentage = (speed / maxSpeed) * 100;
document.getElementById('speed-bar').style.width = `${speedPercentage}%`;
}
updateCollectibleCounter() {
const collected = this.collectibles.filter(c => c.isCollected()).length;
if (collected !== this.collectedCount) {
this.collectedCount = collected;
document.getElementById('collected-count').textContent = this.collectedCount;
// Show notification
if (this.collectedCount > 0) {
this.showNotification(`Collectible ${this.collectedCount}/${this.totalCollectibles} found!`, 1500);
}
// Check if all collected
if (this.collectedCount === this.totalCollectibles) {
setTimeout(() => {
this.showNotification('All Collectibles Found! 🎉', 3000);
}, 1000);
}
}
}
// ========== UTILITIES ==========
hide() {
if (this.hudContainer) {
this.hudContainer.style.display = 'none';
}
}
show() {
if (this.hudContainer) {
this.hudContainer.style.display = 'block';
}
}
setCollectibles(collectibles) {
this.collectibles = collectibles;
this.totalCollectibles = collectibles.length;
document.getElementById('total-count').textContent = this.totalCollectibles;
}
}
export default HUD;