DOOM
ProCreations grug 27b GGUF IQ4 · typescript

Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 30,354 bytes · SHA-256 b441e2754389
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Doom</title>
<style>
/* ── css: layout ── */
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
}
#app {
width: 100%;
height: 100%;
}
#game-layout {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
}
#game-canvas {
width: 100%;
height: 100%;
display: block;
}
#hud {
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 20px;
font-family: monospace;
font-size: 14px;
color: #fff;
text-shadow: 1px 1px 2px #000;
pointer-events: none;
}
#overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.7);
color: #fff;
font-family: monospace;
font-size: 48px;
text-align: center;
pointer-events: none;
}
</style>
</head>
<body>
<div id="app"><div id="game-layout" style="position:relative;width:100vw;height:100vh;overflow:hidden;background:#000">
<canvas id="game-canvas"></canvas>
<div id="hud"></div>
<div id="overlay"></div>
</div></div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./player":"src/player.ts","./world":"src/world.ts","./renderer":"src/renderer.ts"},"src/world.ts":{"./state":"src/state.ts"},"src/player.ts":{"./state":"src/state.ts"},"src/renderer.ts":{"./state":"src/state.ts"}};
function __require(id) {
if (__cache[id]) return __cache[id].exports;
var module = __cache[id] = { exports: {} };
var factory = __mods[id];
if (!factory) throw new Error("Module not found: " + id);
factory(module.exports, function (spec) {
var target = (__map[id] && __map[id][spec]) || spec;
return __require(target);
}, module);
return module.exports;
}
// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const player_1 = require("./player");
const world_1 = require("./world");
const renderer_1 = require("./renderer");
const canvas = document.getElementById('game-canvas');
const game = (0, player_1.createGame)();
game.world = (0, world_1.createWorld)();
game.totalEnemies = game.world.enemies.length;
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
function requestPointerLock() {
canvas.requestPointerLock();
}
function handleMouseMove(event) {
if (document.pointerLockElement === canvas) {
game.mouseDX += event.movementX;
game.mouseDY += event.movementY;
}
}
function handleClick(event) {
if (!document.pointerLockElement) {
requestPointerLock();
}
else {
game.mouseDown = true;
}
}
function handleMouseUp() {
game.mouseDown = false;
}
function handleKeyDown(event) {
if (event.key === 'ArrowLeft')
game.keys.add('ArrowLeft');
if (event.key === 'ArrowRight')
game.keys.add('ArrowRight');
if (event.key === 'ArrowUp')
game.keys.add('ArrowUp');
if (event.key === 'ArrowDown')
game.keys.add('ArrowDown');
if (event.key === '1')
(0, player_1.switchWeapon)(game, 'pistol');
if (event.key === '2')
(0, player_1.switchWeapon)(game, 'shotgun');
if (event.key === '3')
(0, player_1.switchWeapon)(game, 'rifle');
if (event.key === 'r')
(0, player_1.reloadWeapon)(game);
if (event.key === 'r' && game.gameOver) {
game.gameOver = false;
game.gameWin = false;
game.player.health = game.player.maxHealth;
game.player.x = 5;
game.player.z = 5;
game.player.angle = 0;
game.world.enemies = game.world.enemies.concat((0, world_1.createWorld)().enemies);
game.world.pickups = game.world.pickups.concat((0, world_1.createWorld)().pickups);
game.enemiesKilled = 0;
game.totalEnemies = game.world.enemies.length;
}
}
function handleKeyUp(event) {
if (event.key === 'ArrowLeft')
game.keys.delete('ArrowLeft');
if (event.key === 'ArrowRight')
game.keys.delete('ArrowRight');
if (event.key === 'ArrowUp')
game.keys.delete('ArrowUp');
if (event.key === 'ArrowDown')
game.keys.delete('ArrowDown');
}
canvas.addEventListener('mousemove', handleMouseMove);
canvas.addEventListener('click', handleClick);
window.addEventListener('mouseup', handleMouseUp);
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
let lastTime = performance.now();
function gameLoop(currentTime) {
const deltaTime = Math.min((currentTime - lastTime) / 1000, 1 / 30);
lastTime = currentTime;
game.time += deltaTime;
game.damageFlash *= 0.9;
game.shotFlash *= 0.8;
(0, player_1.updatePlayer)(game, deltaTime);
(0, player_1.updateEnemies)(game, deltaTime);
(0, player_1.updatePickups)(game);
(0, player_1.updateWeapons)(game);
if (game.mouseDown) {
(0, player_1.fireWeapon)(game);
}
game.mouseDX = 0;
game.mouseDY = 0;
game.mouseDown = false;
(0, renderer_1.render)(game, canvas);
if (game.gameOver) {
setTimeout(() => {
game.gameOver = false;
game.player.health = game.player.maxHealth;
game.player.x = 5;
game.player.z = 5;
game.player.angle = 0;
}, 3000);
}
requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);
};
// ── module: src/state.ts ──
__mods["src/state.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WEAPONS = void 0;
exports.WEAPONS = {
pistol: { name: 'Pistol', damage: 25, rate: 0.4, ammoCost: 1, magSize: 12, maxAmmo: 60, reloadTime: 1.5, spread: 0.02, pellets: 1, range: 2000 },
shotgun: { name: 'Shotgun', damage: 15, rate: 0.8, ammoCost: 1, magSize: 6, maxAmmo: 30, reloadTime: 2.5, spread: 0.15, pellets: 5, range: 1200 },
rifle: { name: 'Assault Rifle', damage: 18, rate: 0.12, ammoCost: 1, magSize: 30, maxAmmo: 120, reloadTime: 2.0, spread: 0.05, pellets: 1, range: 2500 }
};
};
// ── module: src/world.ts ──
__mods["src/world.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWorld = createWorld;
function createMap() {
const map = [];
for (let y = 0; y < 40; y++) {
let row = '';
for (let x = 0; x < 40; x++) {
if (x === 0 || y === 0 || x === 39 || y === 39) {
row += '#';
}
else if (x === 20 && y >= 10 && y <= 30) {
row += '#';
}
else if ((x === 10 || x === 30) && (y >= 5 || y <= 15)) {
row += '#';
}
else if (x === 5 && y === 20) {
row += '#';
}
else if (x === 35 && y === 20) {
row += '#';
}
else {
row += ' ';
}
}
map.push(row);
}
return map;
}
function createEnemies() {
const enemies = [];
const positions = [
{ x: 15, z: 10 },
{ x: 25, z: 15 },
{ x: 35, z: 10 },
{ x: 5, z: 30 },
{ x: 15, z: 35 },
{ x: 25, z: 30 },
{ x: 35, z: 35 },
{ x: 10, z: 5 },
{ x: 30, z: 5 },
{ x: 20, z: 5 },
{ x: 8, z: 25 },
{ x: 32, z: 25 },
{ x: 12, z: 18 },
{ x: 28, z: 22 },
{ x: 22, z: 35 },
{ x: 18, z: 38 },
{ x: 12, z: 32 },
{ x: 28, z: 8 },
{ x: 36, z: 36 },
{ x: 4, z: 36 },
{ x: 20, z: 15 },
{ x: 30, z: 30 },
{ x: 15, z: 8 },
{ x: 25, z: 38 },
{ x: 18, z: 25 },
{ x: 22, z: 20 },
{ x: 8, z: 15 },
{ x: 32, z: 18 },
{ x: 35, z: 30 },
{ x: 5, z: 10 },
{ x: 10, z: 35 },
{ x: 30, z: 35 },
{ x: 15, z: 20 },
{ x: 25, z: 10 },
{ x: 35, z: 15 },
{ x: 5, z: 25 },
{ x: 10, z: 30 },
{ x: 30, z: 10 },
{ x: 20, z: 8 },
{ x: 25, z: 25 },
{ x: 35, z: 25 },
{ x: 10, z: 15 },
{ x: 20, z: 30 },
{ x: 30, z: 20 },
{ x: 8, z: 30 },
{ x: 32, z: 30 },
{ x: 12, z: 25 },
{ x: 28, z: 15 },
{ x: 18, z: 10 },
{ x: 22, z: 35 }
];
for (const pos of positions) {
const enemy = {
x: pos.x,
z: pos.z,
health: 100,
maxHealth: 100,
speed: 0.02,
state: 'walking',
walkTimer: Math.random() * 100,
attackTimer: 0
};
enemies.push(enemy);
}
return enemies;
}
function createPickups() {
const pickups = [];
const positions = [
{ x: 20, z: 5, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 15, z: 10, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 25, z: 15, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 35, z: 10, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 5, z: 30, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 15, z: 35, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 25, z: 30, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 35, z: 35, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 10, z: 5, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 30, z: 5, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 15, z: 20, type: 'ammo', ammoAmount: 25, healthAmount: 0 },
{ x: 25, z: 25, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 5, z: 15, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 35, z: 20, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 20, z: 15, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 20, z: 35, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 8, z: 25, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 32, z: 25, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 12, z: 18, type: 'ammo', ammoAmount: 25, healthAmount: 0 },
{ x: 28, z: 22, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 18, z: 25, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 22, z: 20, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 8, z: 15, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 32, z: 18, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 35, z: 30, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 5, z: 10, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 10, z: 35, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 30, z: 35, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 15, z: 20, type: 'ammo', ammoAmount: 25, healthAmount: 0 },
{ x: 25, z: 10, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 35, z: 15, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 5, z: 25, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 10, z: 30, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 30, z: 10, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 20, z: 8, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 25, z: 25, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 35, z: 25, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 10, z: 15, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 20, z: 30, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 30, z: 20, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 8, z: 30, type: 'ammo', ammoAmount: 15, healthAmount: 0 },
{ x: 32, z: 30, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 12, z: 25, type: 'ammo', ammoAmount: 25, healthAmount: 0 },
{ x: 28, z: 15, type: 'health', ammoAmount: 0, healthAmount: 25 },
{ x: 18, z: 10, type: 'ammo', ammoAmount: 20, healthAmount: 0 },
{ x: 22, z: 35, type: 'health', ammoAmount: 0, healthAmount: 25 }
];
for (const pos of positions) {
const pickup = {
x: pos.x,
z: pos.z,
type: pos.type,
ammoAmount: pos.ammoAmount,
healthAmount: pos.healthAmount
};
pickups.push(pickup);
}
return pickups;
}
function createWorld() {
return {
width: 40,
height: 40,
map: createMap(),
enemies: createEnemies(),
pickups: createPickups()
};
}
};
// ── module: src/player.ts ──
__mods["src/player.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPlayer = createPlayer;
exports.createWeapons = createWeapons;
exports.createGame = createGame;
exports.updatePlayer = updatePlayer;
exports.switchWeapon = switchWeapon;
exports.reloadWeapon = reloadWeapon;
exports.fireWeapon = fireWeapon;
exports.updateWeapons = updateWeapons;
exports.updatePickups = updatePickups;
exports.updateEnemies = updateEnemies;
const state_1 = require("./state");
function createPlayer() {
return {
x: 5,
z: 5,
angle: 0,
pitch: 0,
health: 100,
maxHealth: 100
};
}
function createWeapons() {
const weapons = {};
for (const [type, weapon] of Object.entries(state_1.WEAPONS)) {
weapons[type] = {
type,
ammo: weapon.maxAmmo,
maxAmmo: weapon.maxAmmo,
mag: weapon.magSize,
maxMag: weapon.magSize,
reloading: false,
reloadStart: 0,
lastShot: 0
};
}
return weapons;
}
function createGame() {
return {
player: createPlayer(),
weapons: createWeapons(),
currentWeapon: 'pistol',
world: null,
cameraHeight: 0.5,
moveSpeed: 0.05,
rotSpeed: 0.005,
keys: new Set(),
mouseDown: false,
mouseDX: 0,
mouseDY: 0,
time: 0,
deltaTime: 0,
enemiesKilled: 0,
totalEnemies: 0,
gameOver: false,
gameWin: false,
gameStarted: false,
damageFlash: 0,
shotFlash: 0
};
}
function updatePlayer(game, deltaTime) {
if (game.gameOver || game.gameWin)
return;
const moveX = Math.cos(game.player.angle);
const moveZ = Math.sin(game.player.angle);
const strafeX = Math.cos(game.player.angle + Math.PI / 2);
const strafeZ = Math.sin(game.player.angle + Math.PI / 2);
if (game.keys.has('ArrowLeft')) {
game.player.angle -= game.rotSpeed * 360 * deltaTime;
}
if (game.keys.has('ArrowRight')) {
game.player.angle += game.rotSpeed * 360 * deltaTime;
}
if (game.keys.has('ArrowUp')) {
const newX = game.player.x + moveX * game.moveSpeed * deltaTime;
const newZ = game.player.z + moveZ * game.moveSpeed * deltaTime;
if (!isWall(game, newX, newZ)) {
game.player.x = newX;
game.player.z = newZ;
}
}
if (game.keys.has('ArrowDown')) {
const newX = game.player.x - moveX * game.moveSpeed * deltaTime;
const newZ = game.player.z - moveZ * game.moveSpeed * deltaTime;
if (!isWall(game, newX, newZ)) {
game.player.x = newX;
game.player.z = newZ;
}
}
if (game.keys.has('ArrowLeft') && game.keys.has('ArrowUp')) {
const newX = game.player.x + (moveX + strafeX) * game.moveSpeed * deltaTime * 0.7;
const newZ = game.player.z + (moveZ + strafeZ) * game.moveSpeed * deltaTime * 0.7;
if (!isWall(game, newX, newZ)) {
game.player.x = newX;
game.player.z = newZ;
}
}
if (game.keys.has('ArrowLeft') && game.keys.has('ArrowDown')) {
const newX = game.player.x + (moveX - strafeX) * game.moveSpeed * deltaTime * 0.7;
const newZ = game.player.z + (moveZ - strafeZ) * game.moveSpeed * deltaTime * 0.7;
if (!isWall(game, newX, newZ)) {
game.player.x = newX;
game.player.z = newZ;
}
}
if (game.keys.has('ArrowRight') && game.keys.has('ArrowUp')) {
const newX = game.player.x + (moveX - strafeX) * game.moveSpeed * deltaTime * 0.7;
const newZ = game.player.z + (moveZ - strafeZ) * game.moveSpeed * deltaTime * 0.7;
if (!isWall(game, newX, newZ)) {
game.player.x = newX;
game.player.z = newZ;
}
}
if (game.keys.has('ArrowRight') && game.keys.has('ArrowDown')) {
const newX = game.player.x + (moveX + strafeX) * game.moveSpeed * deltaTime * 0.7;
const newZ = game.player.z + (moveZ + strafeZ) * game.moveSpeed * deltaTime * 0.7;
if (!isWall(game, newX, newZ)) {
game.player.x = newX;
game.player.z = newZ;
}
}
game.player.pitch -= game.mouseDY * 0.01;
game.player.pitch = Math.max(-1, Math.min(1, game.player.pitch));
}
function isWall(game, x, z) {
const mapX = Math.floor(x);
const mapZ = Math.floor(z);
if (mapX < 0 || mapX >= game.world.width || mapZ < 0 || mapZ >= game.world.height) {
return true;
}
return game.world.map[mapZ][mapX] === '#';
}
function switchWeapon(game, weaponType) {
if (game.weapons[weaponType] && !game.gameOver && !game.gameWin) {
game.currentWeapon = weaponType;
}
}
function reloadWeapon(game) {
const weapon = game.weapons[game.currentWeapon];
if (!weapon || weapon.reloading || weapon.mag === weapon.maxMag || weapon.ammo === 0)
return;
weapon.reloading = true;
weapon.reloadStart = game.time;
}
function fireWeapon(game) {
const weapon = game.weapons[game.currentWeapon];
const weaponDef = state_1.WEAPONS[game.currentWeapon];
if (!weapon || weapon.reloading || weapon.mag === 0 || weapon.lastShot + weaponDef.rate > game.time)
return;
weapon.mag--;
weapon.lastShot = game.time;
game.shotFlash = 0.1;
const pellets = weaponDef.pellets;
for (let i = 0; i < pellets; i++) {
const spread = (Math.random() - 0.5) * weaponDef.spread;
const angle = game.player.angle + spread;
const damage = weaponDef.damage * (0.8 + Math.random() * 0.4);
const range = weaponDef.range;
for (const enemy of game.world.enemies) {
const dx = enemy.x - game.player.x;
const dz = enemy.z - game.player.z;
const distance = Math.sqrt(dx * dx + dz * dz);
if (distance > range)
continue;
const enemyAngle = Math.atan2(dz, dx);
const angleDiff = Math.abs(normalizeAngle(angle - enemyAngle));
if (angleDiff < 0.15 / distance) {
enemy.health -= damage;
enemy.state = 'attacking';
enemy.attackTimer = 0.5;
break;
}
}
}
}
function normalizeAngle(angle) {
while (angle > Math.PI)
angle -= 2 * Math.PI;
while (angle < -Math.PI)
angle += 2 * Math.PI;
return angle;
}
function updateWeapons(game) {
for (const [type, weapon] of Object.entries(game.weapons)) {
if (weapon.reloading) {
const weaponDef = state_1.WEAPONS[type];
const reloadTime = weaponDef.reloadTime;
const reloadProgress = (game.time - weapon.reloadStart) / reloadTime;
if (reloadProgress >= 1) {
const ammoNeeded = weapon.maxMag - weapon.mag;
const ammoGiven = Math.min(ammoNeeded, weapon.ammo);
weapon.mag += ammoGiven;
weapon.ammo -= ammoGiven;
weapon.reloading = false;
}
}
}
}
function updatePickups(game) {
for (let i = game.world.pickups.length - 1; i >= 0; i--) {
const pickup = game.world.pickups[i];
const dx = pickup.x - game.player.x;
const dz = pickup.z - game.player.z;
const distance = Math.sqrt(dx * dx + dz * dz);
if (distance < 0.5) {
if (pickup.type === 'health') {
game.player.health = Math.min(game.player.maxHealth, game.player.health + pickup.healthAmount);
}
else if (pickup.type === 'ammo') {
const weapon = game.weapons[game.currentWeapon];
if (weapon) {
weapon.ammo = Math.min(weapon.maxAmmo, weapon.ammo + pickup.ammoAmount);
}
}
game.world.pickups.splice(i, 1);
}
}
}
function updateEnemies(game, deltaTime) {
for (const enemy of game.world.enemies) {
if (enemy.health <= 0)
continue;
const dx = game.player.x - enemy.x;
const dz = game.player.z - enemy.z;
const distance = Math.sqrt(dx * dx + dz * dz);
if (distance < 15) {
enemy.state = 'attacking';
enemy.attackTimer += deltaTime;
if (enemy.attackTimer > 2) {
game.player.health -= 5;
enemy.attackTimer = 0;
}
}
else if (distance < 30) {
enemy.state = 'walking';
const moveX = dx / distance;
const moveZ = dz / distance;
const newX = enemy.x + moveX * enemy.speed * deltaTime;
const newZ = enemy.z + moveZ * enemy.speed * deltaTime;
if (!isWall(game, newX, newZ)) {
enemy.x = newX;
enemy.z = newZ;
}
}
else {
enemy.state = 'idle';
}
}
for (let i = game.world.enemies.length - 1; i >= 0; i--) {
if (game.world.enemies[i].health <= 0) {
game.world.enemies.splice(i, 1);
game.enemiesKilled++;
}
}
if (game.player.health <= 0) {
game.gameOver = true;
respawnPlayer(game);
}
if (game.world.enemies.length === 0 && game.enemiesKilled > 0) {
game.gameWin = true;
}
}
function respawnPlayer(game) {
game.player.health = game.player.maxHealth;
game.player.x = 5;
game.player.z = 5;
game.player.angle = 0;
game.gameOver = false;
}
};
// ── module: src/renderer.ts ──
__mods["src/renderer.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.render = render;
const state_1 = require("./state");
function render(game, canvas) {
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// Clear screen
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, width, height);
// Render walls
renderWalls(game, ctx, width, height);
// Render enemies
renderEnemies(game, ctx, width, height);
// Render pickups
renderPickups(game, ctx, width, height);
// Render HUD
renderHUD(game, ctx, width, height);
// Render shot flash
if (game.shotFlash > 0) {
ctx.fillStyle = `rgba(255, 255, 0, ${game.shotFlash})`;
ctx.fillRect(0, 0, width, height);
}
// Render damage flash
if (game.damageFlash > 0) {
ctx.fillStyle = `rgba(255, 0, 0, ${game.damageFlash})`;
ctx.fillRect(0, 0, width, height);
}
// Render game over screen
if (game.gameOver) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = '#fff';
ctx.font = '48px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', width / 2, height / 2);
ctx.font = '24px sans-serif';
ctx.fillText('Respawning...', width / 2, height / 2 + 50);
}
// Render win screen
if (game.gameWin) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, width, height);
ctx.fillStyle = '#0f0';
ctx.font = '48px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('VICTORY!', width / 2, height / 2);
ctx.font = '24px sans-serif';
ctx.fillText(`Enemies killed: ${game.enemiesKilled}`, width / 2, height / 2 + 50);
ctx.fillText('Press R to restart', width / 2, height / 2 + 80);
}
}
function renderWalls(game, ctx, width, height) {
const numRays = width / 4;
const FOV = Math.PI / 3;
const halfFOV = FOV / 2;
const pitchOffset = game.player.pitch * height / 2;
for (let i = 0; i < numRays; i++) {
const rayAngle = game.player.angle - halfFOV + (FOV / numRays) * i;
const rayX = Math.cos(rayAngle);
const rayZ = Math.sin(rayAngle);
let distance = 0;
let hitWall = false;
let wallX = 0;
let wallZ = 0;
while (!hitWall && distance < 50) {
const testX = game.player.x + rayX * distance;
const testZ = game.player.z + rayZ * distance;
const mapX = Math.floor(testX);
const mapZ = Math.floor(testZ);
if (mapX < 0 || mapX >= game.world.width || mapZ < 0 || mapZ >= game.world.height) {
hitWall = true;
distance = 50;
}
else if (game.world.map[mapZ][mapX] === '#') {
hitWall = true;
wallX = mapX;
wallZ = mapZ;
}
distance += 0.01;
}
if (hitWall) {
const perpDistance = distance * Math.cos(rayAngle - game.player.angle);
const lineHeight = height / perpDistance;
const drawStart = (height - lineHeight) / 2 + pitchOffset;
const shade = Math.max(0, 1 - perpDistance / 20);
ctx.fillStyle = `rgb(${Math.floor(128 * shade)}, ${Math.floor(64 * shade)}, ${Math.floor(32 * shade)})`;
ctx.fillRect(i * 4, drawStart, 4, lineHeight);
}
}
}
function renderEnemies(game, ctx, width, height) {
const pitchOffset = game.player.pitch * height / 2;
for (const enemy of game.world.enemies) {
const dx = enemy.x - game.player.x;
const dz = enemy.z - game.player.z;
const distance = Math.sqrt(dx * dx + dz * dz);
const enemyAngle = Math.atan2(dz, dx);
const screenX = width / 2 + (enemyAngle - game.player.angle) * width / Math.PI;
if (Math.abs(screenX - width / 2) < width / 2 && distance < 50) {
const perpDistance = distance * Math.cos(enemyAngle - game.player.angle);
const lineHeight = height / perpDistance;
const spriteHeight = lineHeight * 0.8;
const drawStart = (height - spriteHeight) / 2 + pitchOffset;
const shade = Math.max(0, 1 - perpDistance / 20);
const color = enemy.health > 0 ? `rgb(${Math.floor(255 * shade)}, ${Math.floor(64 * shade)}, ${Math.floor(32 * shade)})` : '#000';
ctx.fillStyle = color;
ctx.fillRect(screenX - spriteHeight / 4, drawStart, spriteHeight / 2, spriteHeight);
// Draw health bar
if (enemy.health > 0 && enemy.health < enemy.maxHealth) {
const healthBarWidth = spriteHeight / 2;
const healthPercent = enemy.health / enemy.maxHealth;
ctx.fillStyle = '#f00';
ctx.fillRect(screenX - healthBarWidth / 2, drawStart - 10, healthBarWidth, 5);
ctx.fillStyle = '#0f0';
ctx.fillRect(screenX - healthBarWidth / 2, drawStart - 10, healthBarWidth * healthPercent, 5);
}
}
}
}
function renderPickups(game, ctx, width, height) {
const pitchOffset = game.player.pitch * height / 2;
for (const pickup of game.world.pickups) {
const dx = pickup.x - game.player.x;
const dz = pickup.z - game.player.z;
const distance = Math.sqrt(dx * dx + dz * dz);
const pickupAngle = Math.atan2(dz, dx);
const screenX = width / 2 + (pickupAngle - game.player.angle) * width / Math.PI;
if (Math.abs(screenX - width / 2) < width / 2 && distance < 50) {
const perpDistance = distance * Math.cos(pickupAngle - game.player.angle);
const lineHeight = height / perpDistance;
const spriteHeight = lineHeight * 0.4;
const drawStart = (height - spriteHeight) / 2 + pitchOffset;
const shade = Math.max(0, 1 - perpDistance / 20);
ctx.fillStyle = pickup.type === 'health' ? `rgb(${Math.floor(0 * shade)}, ${Math.floor(255 * shade)}, ${Math.floor(0 * shade)})` : `rgb(${Math.floor(255 * shade)}, ${Math.floor(255 * shade)}, ${Math.floor(0 * shade)})`;
ctx.fillRect(screenX - spriteHeight / 4, drawStart, spriteHeight / 2, spriteHeight);
}
}
}
function renderHUD(game, ctx, width, height) {
const weapon = game.weapons[game.currentWeapon];
const weaponDef = state_1.WEAPONS[game.currentWeapon];
ctx.fillStyle = '#fff';
ctx.font = '24px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(`Health: ${Math.ceil(game.player.health)}`, 20, 40);
ctx.fillText(`Weapon: ${weaponDef.name}`, 20, 70);
ctx.fillText(`Ammo: ${weapon.mag}/${weapon.ammo}`, 20, 100);
ctx.fillText(`Enemies: ${game.world.enemies.length}`, 20, 130);
if (weapon.reloading) {
const reloadTime = weaponDef.reloadTime;
const reloadProgress = (game.time - weapon.reloadStart) / reloadTime;
ctx.fillText('Reloading...', 20, 160);
ctx.fillStyle = '#f00';
ctx.fillRect(20, 170, 200 * reloadProgress, 10);
}
}
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"bartowski/ProCreations_grug-27b-GGUF-IQ4","provider":"brigid","persona":"typescript","sessionId":"a1e3e37b-e9d3-41ad-9f39-88333948da81","tokensIn":259662,"tokensOut":13078,"tokensTotal":272740,"cost":0,"turns":17,"toolCalls":16,"failedToolCalls":3,"timestamp":"2026-08-05T17:23:35.281Z"} -->