feat(integrations): add retro doom web engine and agy proxy nginx config
This commit is contained in:
Executable
+270
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* DOOM 3D - Authentic Retro Status Bar & HUD
|
||||
* LED segmented numbers, animated Doomguy face canvas reacting to HP/pain/godmode,
|
||||
* weapon slot matrix, keycard inventory, ticker notifications, and damage screen flashes.
|
||||
*/
|
||||
|
||||
class DoomHUD {
|
||||
constructor(player) {
|
||||
this.player = player;
|
||||
|
||||
// Container elements
|
||||
this.hudContainer = document.getElementById('doom-hud');
|
||||
this.damageOverlay = document.getElementById('damage-flash');
|
||||
this.ticker = document.getElementById('hud-ticker');
|
||||
this.faceCanvas = document.getElementById('doomguy-face');
|
||||
this.faceCtx = this.faceCanvas ? this.faceCanvas.getContext('2d') : null;
|
||||
|
||||
// Doomguy Face animation states
|
||||
this.faceLookDir = 0; // -1 = left, 0 = forward, 1 = right
|
||||
this.faceTimer = 0;
|
||||
this.painTimer = 0;
|
||||
this.grinTimer = 0;
|
||||
|
||||
// Notification ticker
|
||||
this.tickerTimer = 0;
|
||||
|
||||
// Flash states
|
||||
this.flashAlpha = 0;
|
||||
this.flashColor = 'rgba(255, 0, 0, ';
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.faceCanvas) {
|
||||
this.faceCanvas.width = 64;
|
||||
this.faceCanvas.height = 72;
|
||||
}
|
||||
}
|
||||
|
||||
showNotification(text) {
|
||||
if (!this.ticker) return;
|
||||
this.ticker.innerText = text.toUpperCase();
|
||||
this.ticker.style.opacity = '1';
|
||||
this.tickerTimer = 3.5;
|
||||
}
|
||||
|
||||
triggerDamageFlash(color = 'red') {
|
||||
this.flashAlpha = 0.55;
|
||||
this.painTimer = 0.5;
|
||||
if (color === 'green') {
|
||||
this.flashColor = 'rgba(50, 255, 0, ';
|
||||
} else if (color === 'yellow') {
|
||||
this.flashColor = 'rgba(255, 220, 0, ';
|
||||
} else {
|
||||
this.flashColor = 'rgba(220, 0, 0, ';
|
||||
}
|
||||
}
|
||||
|
||||
triggerGrin() {
|
||||
this.grinTimer = 1.2;
|
||||
}
|
||||
|
||||
showGameOver() {
|
||||
const overlay = document.getElementById('game-over-modal');
|
||||
if (overlay) overlay.classList.remove('hidden');
|
||||
}
|
||||
|
||||
hideGameOver() {
|
||||
const overlay = document.getElementById('game-over-modal');
|
||||
if (overlay) overlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// DOOMGUY FACE RENDERING (Dynamic Procedural Pixel Art)
|
||||
// ----------------------------------------------------
|
||||
|
||||
renderDoomguyFace(delta) {
|
||||
if (!this.faceCtx) return;
|
||||
const ctx = this.faceCtx;
|
||||
ctx.clearRect(0, 0, 64, 72);
|
||||
|
||||
const hp = this.player.health;
|
||||
const isDead = this.player.isDead;
|
||||
const godMode = this.player.godMode;
|
||||
|
||||
// Update look direction timer
|
||||
this.faceTimer += delta;
|
||||
if (this.faceTimer > 1.8) {
|
||||
this.faceTimer = 0;
|
||||
const r = Math.random();
|
||||
this.faceLookDir = r < 0.3 ? -1 : (r > 0.7 ? 1 : 0);
|
||||
}
|
||||
|
||||
if (this.painTimer > 0) this.painTimer -= delta;
|
||||
if (this.grinTimer > 0) this.grinTimer -= delta;
|
||||
|
||||
// 1. Background / Skin Base
|
||||
let skinTone = '#d49b78';
|
||||
if (hp <= 40) skinTone = '#c28766';
|
||||
if (hp <= 20) skinTone = '#b07255';
|
||||
if (isDead) skinTone = '#808a70'; // Pale zombie/corpse skin
|
||||
|
||||
// Jaw / Head Contour
|
||||
ctx.fillStyle = skinTone;
|
||||
ctx.fillRect(12, 10, 40, 52);
|
||||
ctx.fillRect(16, 62, 32, 6);
|
||||
|
||||
// Brown Hair & Crewcut
|
||||
ctx.fillStyle = '#4a2c18';
|
||||
ctx.fillRect(10, 4, 44, 14);
|
||||
ctx.fillRect(8, 12, 6, 20);
|
||||
ctx.fillRect(50, 12, 6, 20);
|
||||
|
||||
if (isDead) {
|
||||
// Dead Doomguy: X-eyes or blank white eyes + blood
|
||||
ctx.fillStyle = '#111';
|
||||
ctx.fillRect(18, 30, 10, 4);
|
||||
ctx.fillRect(36, 30, 10, 4);
|
||||
|
||||
ctx.fillStyle = '#aa0000';
|
||||
ctx.fillRect(26, 44, 12, 18);
|
||||
ctx.fillRect(16, 52, 32, 6);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Eyes
|
||||
const eyeOffset = this.faceLookDir * 2;
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillRect(18, 28, 10, 8);
|
||||
ctx.fillRect(36, 28, 10, 8);
|
||||
|
||||
// Pupils
|
||||
if (godMode) {
|
||||
// Glowing Yellow God Mode Eyes
|
||||
ctx.fillStyle = '#ffff00';
|
||||
ctx.fillRect(18, 28, 10, 8);
|
||||
ctx.fillRect(36, 28, 10, 8);
|
||||
} else {
|
||||
ctx.fillStyle = '#3a6688'; // Blue eyes
|
||||
ctx.fillRect(21 + eyeOffset, 30, 4, 4);
|
||||
ctx.fillRect(39 + eyeOffset, 30, 4, 4);
|
||||
}
|
||||
|
||||
// Eyebrows
|
||||
ctx.fillStyle = '#3a2010';
|
||||
if (this.painTimer > 0) {
|
||||
// Pain / Anguished eyebrows
|
||||
ctx.fillRect(16, 24, 12, 3);
|
||||
ctx.fillRect(36, 24, 12, 3);
|
||||
} else if (this.grinTimer > 0) {
|
||||
// Evil smirk eyebrows
|
||||
ctx.fillRect(16, 26, 12, 3);
|
||||
ctx.fillRect(36, 23, 12, 3);
|
||||
} else {
|
||||
// Normal stern eyebrows
|
||||
ctx.fillRect(16, 25, 12, 3);
|
||||
ctx.fillRect(36, 25, 12, 3);
|
||||
}
|
||||
|
||||
// 3. Nose
|
||||
ctx.fillStyle = '#a66e4e';
|
||||
ctx.fillRect(29, 36, 6, 12);
|
||||
if (hp <= 60) {
|
||||
// Bloody nose
|
||||
ctx.fillStyle = '#aa0000';
|
||||
ctx.fillRect(29, 44, 6, 6);
|
||||
ctx.fillRect(31, 50, 3, 6);
|
||||
}
|
||||
|
||||
// 4. Mouth / Expressions
|
||||
if (this.painTimer > 0) {
|
||||
// Ouch open screaming mouth
|
||||
ctx.fillStyle = '#111';
|
||||
ctx.fillRect(24, 52, 16, 10);
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(26, 52, 12, 3);
|
||||
} else if (this.grinTimer > 0) {
|
||||
// Evil grin
|
||||
ctx.fillStyle = '#111';
|
||||
ctx.fillRect(22, 53, 20, 6);
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(24, 54, 16, 3);
|
||||
} else if (hp <= 30) {
|
||||
// Bruised grimace
|
||||
ctx.fillStyle = '#441111';
|
||||
ctx.fillRect(24, 54, 16, 4);
|
||||
} else {
|
||||
// Classic Doomguy smirk
|
||||
ctx.fillStyle = '#6b3e28';
|
||||
ctx.fillRect(24, 54, 16, 4);
|
||||
}
|
||||
|
||||
// 5. Blood wounds for low health
|
||||
if (hp <= 80) {
|
||||
ctx.fillStyle = '#aa0000';
|
||||
ctx.fillRect(44, 18, 4, 8); // Forehead cut
|
||||
}
|
||||
if (hp <= 40) {
|
||||
ctx.fillStyle = '#aa0000';
|
||||
ctx.fillRect(14, 38, 8, 10); // Cheek wound
|
||||
ctx.fillRect(40, 56, 6, 8);
|
||||
}
|
||||
if (hp <= 20) {
|
||||
ctx.fillStyle = '#880000';
|
||||
ctx.fillRect(16, 28, 14, 10); // Black eye
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// UPDATE STATUS BAR METRICS
|
||||
// ----------------------------------------------------
|
||||
|
||||
update(delta) {
|
||||
// 1. Ticker fadeout
|
||||
if (this.tickerTimer > 0) {
|
||||
this.tickerTimer -= delta;
|
||||
if (this.tickerTimer <= 0 && this.ticker) {
|
||||
this.ticker.style.opacity = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Damage Screen Flash fadeout
|
||||
if (this.flashAlpha > 0) {
|
||||
this.flashAlpha = Math.max(0, this.flashAlpha - delta * 2.0);
|
||||
if (this.damageOverlay) {
|
||||
this.damageOverlay.style.backgroundColor = `${this.flashColor}${this.flashAlpha})`;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Render Animated Doomguy Face
|
||||
this.renderDoomguyFace(delta);
|
||||
|
||||
// 4. Update HUD Numerical Values
|
||||
const currentWeapon = (window.game && window.game.weapons) ? window.game.weapons.current : null;
|
||||
const ammoCount = (currentWeapon && currentWeapon.ammoType) ? this.player.ammo[currentWeapon.ammoType] : '---';
|
||||
|
||||
const ammoElem = document.getElementById('stat-ammo');
|
||||
const hpElem = document.getElementById('stat-health');
|
||||
const armorElem = document.getElementById('stat-armor');
|
||||
|
||||
if (ammoElem) ammoElem.innerText = ammoCount;
|
||||
if (hpElem) hpElem.innerText = `${Math.max(0, this.player.health)}%`;
|
||||
if (armorElem) armorElem.innerText = `${this.player.armor}%`;
|
||||
|
||||
// 5. Update ARMS Numbers (1-7)
|
||||
if (window.game && window.game.weapons) {
|
||||
const weapons = window.game.weapons;
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const armSlot = document.getElementById(`arm-slot-${i + 1}`);
|
||||
if (armSlot) {
|
||||
const isUnlocked = weapons.unlocked[i];
|
||||
const isCurrent = (weapons.currentIndex === i);
|
||||
armSlot.className = `arm-num ${isUnlocked ? 'active' : 'inactive'} ${isCurrent ? 'selected' : ''}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Update KEYS Indicators (Blue, Red, Yellow)
|
||||
const blueKeyElem = document.getElementById('key-blue');
|
||||
const redKeyElem = document.getElementById('key-red');
|
||||
const yellowKeyElem = document.getElementById('key-yellow');
|
||||
|
||||
if (blueKeyElem) blueKeyElem.className = `key-slot ${this.player.keysInventory.blue ? 'has-key' : ''}`;
|
||||
if (redKeyElem) redKeyElem.className = `key-slot ${this.player.keysInventory.red ? 'has-key' : ''}`;
|
||||
if (yellowKeyElem) yellowKeyElem.className = `key-slot ${this.player.keysInventory.yellow ? 'has-key' : ''}`;
|
||||
}
|
||||
}
|
||||
|
||||
window.DoomHUD = DoomHUD;
|
||||
Reference in New Issue
Block a user