feat(integrations): add retro doom web engine and agy proxy nginx config
This commit is contained in:
1 parent
b8db5a123d
commit
50eadb4b5e
15 files changed
+6397
No files matched your search
Executable
+335
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* DOOM 3D - Pickups, Keycards & Explosive Barrels System
|
||||
* 3D rotating items, bobbing animations, and chain reaction explosive toxic barrels.
|
||||
*/
|
||||
|
||||
class DoomItems {
|
||||
constructor(engine, player, sound, map) {
|
||||
this.engine = engine;
|
||||
this.player = player;
|
||||
this.sound = sound;
|
||||
this.map = map;
|
||||
|
||||
this.items = [];
|
||||
this.barrels = [];
|
||||
this.itemTextures = {};
|
||||
|
||||
this.generateItemTextures();
|
||||
this.spawnMapItems();
|
||||
this.spawnMapBarrels();
|
||||
}
|
||||
|
||||
generateItemTextures() {
|
||||
this.itemTextures.medikit = this.createMedikitTexture();
|
||||
this.itemTextures.armorGreen = this.createArmorTexture('#22aa22');
|
||||
this.itemTextures.armorBlue = this.createArmorTexture('#0088ff');
|
||||
this.itemTextures.ammoClip = this.createAmmoTexture('CLIP');
|
||||
this.itemTextures.ammoShells = this.createAmmoTexture('SHELLS');
|
||||
this.itemTextures.ammoRocket = this.createAmmoTexture('ROCKETS');
|
||||
this.itemTextures.ammoCell = this.createAmmoTexture('CELLS');
|
||||
}
|
||||
|
||||
createMedikitTexture() {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 64;
|
||||
c.height = 64;
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.fillStyle = '#eeeeee';
|
||||
ctx.fillRect(0, 0, 64, 64);
|
||||
ctx.fillStyle = '#cc0000';
|
||||
// Red cross
|
||||
ctx.fillRect(24, 10, 16, 44);
|
||||
ctx.fillRect(10, 24, 44, 16);
|
||||
const tex = new THREE.CanvasTexture(c);
|
||||
return tex;
|
||||
}
|
||||
|
||||
createArmorTexture(color) {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 64;
|
||||
c.height = 64;
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(8, 8, 48, 48);
|
||||
ctx.fillStyle = '#ffffff';
|
||||
ctx.fillRect(18, 18, 28, 28);
|
||||
const tex = new THREE.CanvasTexture(c);
|
||||
return tex;
|
||||
}
|
||||
|
||||
createAmmoTexture(label) {
|
||||
const c = document.createElement('canvas');
|
||||
c.width = 64;
|
||||
c.height = 64;
|
||||
const ctx = c.getContext('2d');
|
||||
ctx.fillStyle = '#3a3a30';
|
||||
ctx.fillRect(0, 0, 64, 64);
|
||||
ctx.fillStyle = '#ddaa00';
|
||||
ctx.font = 'bold 12px monospace';
|
||||
ctx.fillText(label, 6, 36);
|
||||
const tex = new THREE.CanvasTexture(c);
|
||||
return tex;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// SPAWNING ITEMS & KEYCARDS
|
||||
// ----------------------------------------------------
|
||||
|
||||
spawnMapItems() {
|
||||
const spawns = (this.map && this.map.spawns) ? this.map.spawns.items : (window.game && window.game.map ? window.game.map.spawns.items : []);
|
||||
if (!spawns || spawns.length === 0) return;
|
||||
|
||||
for (let i = 0; i < spawns.length; i++) {
|
||||
const s = spawns[i];
|
||||
this.spawnItem(s.type, s.x, s.y, s.z);
|
||||
}
|
||||
}
|
||||
|
||||
spawnItem(type, x, y, z) {
|
||||
let mesh = null;
|
||||
let light = null;
|
||||
let scale = 1.0;
|
||||
|
||||
if (type.startsWith('key_')) {
|
||||
// 3D Keycard with colored glowing point light
|
||||
const colorName = type.replace('key_', '');
|
||||
let colorHex = 0x0088ff;
|
||||
if (colorName === 'red') colorHex = 0xff1122;
|
||||
if (colorName === 'yellow') colorHex = 0xffbb00;
|
||||
|
||||
const geo = new THREE.BoxGeometry(0.8, 1.2, 0.15);
|
||||
const mat = new THREE.MeshStandardMaterial({
|
||||
color: colorHex,
|
||||
emissive: colorHex,
|
||||
emissiveIntensity: 0.8
|
||||
});
|
||||
mesh = new THREE.Mesh(geo, mat);
|
||||
light = new THREE.PointLight(colorHex, 2.5, 6, 2);
|
||||
light.position.set(x, y + 0.6, z);
|
||||
this.engine.scene.add(light);
|
||||
} else if (type === 'soulsphere') {
|
||||
// Blue glowing soulsphere
|
||||
const geo = new THREE.SphereGeometry(0.6, 16, 16);
|
||||
const mat = new THREE.MeshStandardMaterial({
|
||||
color: 0x00d4ff,
|
||||
emissive: 0x00d4ff,
|
||||
emissiveIntensity: 0.9,
|
||||
transparent: true,
|
||||
opacity: 0.85
|
||||
});
|
||||
mesh = new THREE.Mesh(geo, mat);
|
||||
light = new THREE.PointLight(0x00d4ff, 3.5, 10, 2);
|
||||
light.position.set(x, y + 0.6, z);
|
||||
this.engine.scene.add(light);
|
||||
} else if (type === 'medikit') {
|
||||
const geo = new THREE.BoxGeometry(0.8, 0.6, 0.8);
|
||||
const mat = new THREE.MeshStandardMaterial({ map: this.itemTextures.medikit });
|
||||
mesh = new THREE.Mesh(geo, mat);
|
||||
} else if (type.startsWith('armor_')) {
|
||||
const isBlue = type.includes('blue');
|
||||
const geo = new THREE.BoxGeometry(0.9, 0.9, 0.9);
|
||||
const mat = new THREE.MeshStandardMaterial({
|
||||
map: isBlue ? this.itemTextures.armorBlue : this.itemTextures.armorGreen,
|
||||
emissive: isBlue ? 0x0044ff : 0x008800,
|
||||
emissiveIntensity: 0.3
|
||||
});
|
||||
mesh = new THREE.Mesh(geo, mat);
|
||||
} else {
|
||||
// Ammo / Weapon box
|
||||
const geo = new THREE.BoxGeometry(0.7, 0.5, 0.7);
|
||||
const mat = new THREE.MeshStandardMaterial({ map: this.itemTextures.ammoClip });
|
||||
mesh = new THREE.Mesh(geo, mat);
|
||||
}
|
||||
|
||||
mesh.position.set(x, y + 0.4, z);
|
||||
mesh.castShadow = true;
|
||||
this.engine.scene.add(mesh);
|
||||
|
||||
const itemObj = {
|
||||
type,
|
||||
mesh,
|
||||
light,
|
||||
baseY: y + 0.4,
|
||||
rotationSpeed: 1.5,
|
||||
bobTimer: Math.random() * 5,
|
||||
isCollected: false
|
||||
};
|
||||
|
||||
this.items.push(itemObj);
|
||||
return itemObj;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// EXPLOSIVE TOXIC BARRELS
|
||||
// ----------------------------------------------------
|
||||
|
||||
spawnMapBarrels() {
|
||||
const spawns = (this.map && this.map.spawns) ? this.map.spawns.barrels : (window.game && window.game.map ? window.game.map.spawns.barrels : []);
|
||||
if (!spawns || spawns.length === 0) return;
|
||||
|
||||
for (let i = 0; i < spawns.length; i++) {
|
||||
const s = spawns[i];
|
||||
this.spawnBarrel(s.x, s.y, s.z);
|
||||
}
|
||||
}
|
||||
|
||||
spawnBarrel(x, y, z) {
|
||||
const geo = new THREE.CylinderGeometry(0.55, 0.55, 1.4, 16);
|
||||
const mat = this.engine.materials.barrel;
|
||||
const mesh = new THREE.Mesh(geo, mat);
|
||||
mesh.position.set(x, y + 0.7, z);
|
||||
mesh.castShadow = true;
|
||||
mesh.receiveShadow = true;
|
||||
this.engine.scene.add(mesh);
|
||||
|
||||
// Add to map colliders
|
||||
const min = new THREE.Vector3(x - 0.55, y, z - 0.55);
|
||||
const max = new THREE.Vector3(x + 0.55, y + 1.4, z + 0.55);
|
||||
const box = new THREE.Box3(min, max);
|
||||
this.engine.scene.colliders = this.engine.scene.colliders || [];
|
||||
|
||||
const barrelObj = {
|
||||
mesh,
|
||||
box,
|
||||
hp: 20,
|
||||
isExploded: false,
|
||||
takeDamage: (dmg) => this.damageBarrel(barrelObj, dmg)
|
||||
};
|
||||
|
||||
this.barrels.push(barrelObj);
|
||||
return barrelObj;
|
||||
}
|
||||
|
||||
damageBarrel(b, amount) {
|
||||
if (b.isExploded) return;
|
||||
b.hp -= amount;
|
||||
if (b.hp <= 0) {
|
||||
this.explodeBarrel(b);
|
||||
}
|
||||
}
|
||||
|
||||
explodeBarrel(b) {
|
||||
b.isExploded = true;
|
||||
const pos = b.mesh.position;
|
||||
|
||||
this.sound.playExplosion(1.5);
|
||||
this.engine.spawnExplosion(pos, 5.5, 0x88ff00); // Toxic radioactive green blast!
|
||||
|
||||
// Remove mesh
|
||||
this.engine.scene.remove(b.mesh);
|
||||
|
||||
// Area Damage & Chain Reactions
|
||||
if (window.game && window.game.weapons) {
|
||||
window.game.weapons.applySplashDamage(pos, 180, 5.5);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// ITEM COLLECTION & UPDATE
|
||||
// ----------------------------------------------------
|
||||
|
||||
update(delta) {
|
||||
const playerPos = this.player.position;
|
||||
|
||||
for (let i = this.items.length - 1; i >= 0; i--) {
|
||||
const item = this.items[i];
|
||||
if (item.isCollected) continue;
|
||||
|
||||
// Rotation & Bobbing Animation
|
||||
item.bobTimer += delta * 3.0;
|
||||
item.mesh.rotation.y += item.rotationSpeed * delta;
|
||||
item.mesh.position.y = item.baseY + Math.sin(item.bobTimer) * 0.15;
|
||||
if (item.light) {
|
||||
item.light.position.copy(item.mesh.position);
|
||||
}
|
||||
|
||||
// Player proximity pickup check
|
||||
const dist = item.mesh.position.distanceTo(playerPos);
|
||||
if (dist < 1.6) {
|
||||
this.collectItem(item, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collectItem(item, index) {
|
||||
let collected = false;
|
||||
const type = item.type;
|
||||
|
||||
if (type === 'medikit') {
|
||||
collected = this.player.heal(25);
|
||||
if (collected) this.sound.playItemPickup('health');
|
||||
} else if (type === 'soulsphere') {
|
||||
collected = this.player.heal(100, true);
|
||||
if (collected) {
|
||||
this.sound.playItemPickup('weapon');
|
||||
if (window.game && window.game.hud) window.game.hud.showNotification('SOULSPHERE! +100 HP');
|
||||
}
|
||||
} else if (type === 'armor_green') {
|
||||
collected = this.player.addArmor(100);
|
||||
if (collected) this.sound.playItemPickup('armor');
|
||||
} else if (type === 'armor_blue') {
|
||||
collected = this.player.addArmor(200, true);
|
||||
if (collected) this.sound.playItemPickup('armor');
|
||||
} else if (type === 'ammo_clip') {
|
||||
collected = this.player.addAmmo('bullets', 10);
|
||||
if (collected) this.sound.playItemPickup('ammo');
|
||||
} else if (type === 'ammo_box') {
|
||||
collected = this.player.addAmmo('bullets', 50);
|
||||
if (collected) this.sound.playItemPickup('ammo');
|
||||
} else if (type === 'ammo_shells') {
|
||||
collected = this.player.addAmmo('shells', 8);
|
||||
if (collected) this.sound.playItemPickup('ammo');
|
||||
} else if (type === 'ammo_rocket') {
|
||||
collected = this.player.addAmmo('rockets', 5);
|
||||
if (collected) this.sound.playItemPickup('ammo');
|
||||
} else if (type === 'ammo_cell') {
|
||||
collected = this.player.addAmmo('cells', 40);
|
||||
if (collected) this.sound.playItemPickup('ammo');
|
||||
} else if (type === 'shotgun') {
|
||||
this.player.addAmmo('shells', 8);
|
||||
if (window.game && window.game.weapons) window.game.weapons.unlockWeapon(2);
|
||||
collected = true;
|
||||
this.sound.playItemPickup('weapon');
|
||||
if (window.game && window.game.hud) window.game.hud.showNotification('YOU GOT THE SHOTGUN!');
|
||||
} else if (type === 'chaingun') {
|
||||
this.player.addAmmo('bullets', 40);
|
||||
if (window.game && window.game.weapons) window.game.weapons.unlockWeapon(3);
|
||||
collected = true;
|
||||
this.sound.playItemPickup('weapon');
|
||||
if (window.game && window.game.hud) window.game.hud.showNotification('YOU GOT THE CHAINGUN!');
|
||||
} else if (type === 'rocket_launcher') {
|
||||
this.player.addAmmo('rockets', 5);
|
||||
if (window.game && window.game.weapons) window.game.weapons.unlockWeapon(4);
|
||||
collected = true;
|
||||
this.sound.playItemPickup('weapon');
|
||||
if (window.game && window.game.hud) window.game.hud.showNotification('YOU GOT THE ROCKET LAUNCHER!');
|
||||
} else if (type === 'plasma') {
|
||||
this.player.addAmmo('cells', 50);
|
||||
if (window.game && window.game.weapons) window.game.weapons.unlockWeapon(5);
|
||||
collected = true;
|
||||
this.sound.playItemPickup('weapon');
|
||||
if (window.game && window.game.hud) window.game.hud.showNotification('YOU GOT THE PLASMA RIFLE!');
|
||||
} else if (type === 'bfg') {
|
||||
this.player.addAmmo('cells', 100);
|
||||
if (window.game && window.game.weapons) window.game.weapons.unlockWeapon(6);
|
||||
collected = true;
|
||||
this.sound.playItemPickup('weapon');
|
||||
if (window.game && window.game.hud) window.game.hud.showNotification('YOU GOT THE BFG 9000! OH YES.');
|
||||
} else if (type.startsWith('key_')) {
|
||||
const keyColor = type.replace('key_', '');
|
||||
this.player.keysInventory[keyColor] = true;
|
||||
collected = true;
|
||||
this.sound.playItemPickup('key');
|
||||
if (window.game && window.game.hud) window.game.hud.showNotification(`PICKED UP THE ${keyColor.toUpperCase()} KEYCARD`);
|
||||
}
|
||||
|
||||
if (collected) {
|
||||
item.isCollected = true;
|
||||
this.engine.scene.remove(item.mesh);
|
||||
if (item.light) this.engine.scene.remove(item.light);
|
||||
this.items.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.DoomItems = DoomItems;
|
||||
Reference in new issue
Block a user