DOOM
Gemma 4 12b · 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 16,786 bytes · SHA-256 70fee858fd05
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Retro Raycaster FPS</title>
<style>
/* ── css: retro-style ── */
body {
margin: 0;
padding: 0;
background-color: #1a1a1a;
color: #eee;
font-family: 'Courier New', Courier, monospace;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
#game-container {
position: relative;
border: 4px solid #555;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
canvas {
display: block;
image-rendering: pixelated;
}
#hud {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
padding: 10px;
display: flex;
justify-content: space-around;
border-top: 2px solid #555;
}
.stat {
text-align: center;
}
.stat-label {
font-size: 0.8rem;
color: #aaa;
}
.stat-value {
font-size: 1.5rem;
font-weight: bold;
color: #f00;
}
#crosshair {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: rgba(255, 255, 255, 0.8);
font-size: 1.5rem;
}
#overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
#overlay h1 {
font-size: 3rem;
color: #f00;
margin-bottom: 20px;
}
button {
padding: 15px 30px;
font-size: 1.2rem;
background: #444;
color: white;
border: 2px solid #fff;
cursor: pointer;
}
button:hover {
background: #666;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="gameCanvas"></canvas>
<div id="crosshair">+</div>
<div id="hud">
<div class="stat">
<div class="stat-label">HEALTH</div>
<div id="health" class="stat-value">100</div>
</div>
<div class="stat">
<div class="stat-label">AMMO</div>
<div id="ammo" class="stat-value">30</div>
</div>
<div class="stat">
<div class="stat-label">WEAPON</div>
<div id="weapon-name" class="stat-value">Pistol</div>
</div>
</div>
<div id="overlay">
<h1 id="overlay-title">RETRO FPS</h1>
<p id="overlay-desc">Use WASD to move, Mouse to aim/shoot</p>
<button id="start-btn">START GAME</button>
</div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/world.ts":{"./constants":"src/constants.ts"},"src/player.ts":{"./constants":"src/constants.ts"},"src/engine.ts":{"./constants":"src/constants.ts","./world":"src/world.ts","./player":"src/player.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";
// Entry point. Runs after the DOM is parsed and after any classic
// <script> imports, so document queries and CDN globals are available.
// Import your other modules from here.
Object.defineProperty(exports, "__esModule", { value: true });
exports.start = start;
function start() {
// Replace this with the real startup sequence.
}
start();
};
// ── module: src/constants.ts ──
__mods["src/constants.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ENTITY_TYPES = exports.WEAPONS = exports.FOV = exports.TILE_SIZE = exports.MAP = void 0;
exports.MAP = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 5, 0, 0, 0, 0, 6, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 1],
[1, 0, 8, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
exports.TILE_SIZE = 64;
exports.FOV = Math.PI / 3; // 60 degrees
exports.WEAPONS = {
pistol: { name: 'Pistol', damage: 20, ammoCount: 30, maxAmmo: 100, fireRate: 400, cooldown: 0.3, color: '#fff' },
shotgun: { name: 'Shotgun', damage: 60, ammoCount: 15, maxAmmo: 20, fireRate: 800, cooldown: 0.8, color: '#f90' },
rocket: { name: 'Rocket', damage: 150, ammoCount: 5, maxAmmo: 10, fireRate: 1500, cooldown: 2.0, color: '#f30' }
};
exports.ENTITY_TYPES = {
WALL: 1,
PICKUP_HEALTH: 2,
PICKUP_AMMO: 3,
ENEMY: 4,
WIN_CONDITION: 5,
EXPLOSIVE: 6,
PORTAL: 7,
DOOR: 8,
CHEST: 9
};
};
// ── module: src/world.ts ──
__mods["src/world.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.World = void 0;
const constants_1 = require("./constants");
class World {
constructor() {
this.entities = [];
this.map = constants_1.MAP;
this.initEntities();
}
initEntities() {
// Place some enemies (type 4) and items
// Note: The constants.ts says ENTITY_TYPES.ENEMY is 4, PICKUP_HEALTH is 2, etc.
this.entities = [
{ x: 3 * constants_1.TILE_SIZE, y: 2 * constants_1.TILE_SIZE, type: 4, hp: 100, id: 'e1' },
{ x: 8 * constants_1.TILE_SIZE, y: 2 * constants_1.TILE_SIZE, type: 4, hp: 100, id: 'e2' },
{ x: 6 * constants_1.TILE_SIZE, y: 4 * constants_1.TILE_SIZE, type: 4, hp: 80, id: 'e3' },
{ x: 11 * constants_1.TILE_SIZE, y: 5 * constants_1.TILE_SIZE, type: 4, hp: 120, id: 'e4' },
{ x: 2 * constants_1.TILE_SIZE, y: 6 * constants_1.TILE_SIZE, type: 4, hp: 90, id: 'e5' },
// Pickups
{ x: 5 * constants_1.TILE_SIZE, y: 3 * constants_1.TILE_SIZE, type: 2, hp: 50, id: 'h1' }, // health
{ x: 9 * constants_1.TILE_SIZE, y: 4 * constants_1.TILE_SIZE, type: 3, hp: 30, id: 'a1' }, // ammo
{ x: 7 * constants_1.TILE_SIZE, y: 6 * constants_1.TILE_SIZE, type: 3, hp: 50, id: 'a2' }, // ammo
// Win condition
{ x: 13 * constants_1.TILE_SIZE, y: 5 * constants_1.TILE_SIZE, type: 5, hp: 1, id: 'win' }
];
}
getTile(x, y) {
const tx = Math.floor(x / constants_1.TILE_SIZE);
const ty = Math.floor(y / constants_1.TILE_SIZE);
if (tx < 0 || tx >= this.map[0].length || ty < 0 || ty >= this.map.length)
return 1;
return this.map[ty][tx];
}
isWall(x, y) {
return this.getTile(x, y) === 1;
}
getEntityAt(x, y) {
for (const e of this.entities) {
const dist = Math.sqrt((e.x - x) ** 2 + (e.y - y) ** 2);
if (dist < constants_1.TILE_SIZE * 0.8)
return e;
}
return null;
}
removeEntity(id) {
this.entities = this.entities.filter(e => e.id !== id);
}
}
exports.World = World;
};
// ── module: src/player.ts ──
__mods["src/player.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Player = void 0;
class Player {
constructor() {
this.x = 4 * 64;
this.y = 4 * 64;
this.angle = 0;
this.health = 100;
this.ammo = 30;
this.weaponType = 'pistol';
this.lastFireTime = 0;
}
update(keys, world) {
const moveSpeed = 3;
let dx = 0;
let dy = 0;
if (keys['w'])
dy -= moveSpeed;
if (keys['s'])
dy += moveSpeed;
if (keys['a'])
dx -= moveSpeed;
if (keys['d'])
dx += moveSpeed;
const nextX = this.x + dx;
const nextY = this.y + dy;
if (!world.isWall(nextX, nextY)) {
this.x = nextX;
this.y = nextY;
}
this.health = Math.min(100, this.health);
}
shoot(weaponData) {
if (this.ammo > 0 && Date.now() - this.lastFireTime > weaponData.cooldown * 1000) {
this.ammo--;
this.lastFireTime = Date.now();
return true;
}
return false;
}
}
exports.Player = Player;
};
// ── module: src/engine.ts ──
__mods["src/engine.ts"] = function (exports, require, module) {
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.GameEngine = void 0;
const Consts = __importStar(require("./constants"));
const world_1 = require("./world");
const player_1 = require("./player");
class GameEngine {
constructor() {
this.keys = {};
this.gameActive = false;
this.lastTime = 0;
this.canvas = document.getElementById('gameCanvas');
// Set internal resolution for retro feel
this.canvas.width = 320;
this.canvas.height = 200;
this.ctx = this.canvas.getContext('2d');
this.world = new world_1.World();
this.player = new player_1.Player();
window.Player = player_1.Player;
window.addEventListener('keydown', e => this.keys[e.key.toLowerCase()] = true);
window.addEventListener('keyup', e => this.keys[e.key.toLowerCase()] = false);
this.canvas.requestPointerLock = this.canvas.requestPointerLock;
this.canvas.onclick = () => {
if (!this.gameActive) {
this.startGame();
}
else if (this.canvas.requestPointerLock) {
this.canvas.requestPointerLock();
}
};
// Call initMouse after construction to ensure elements exist
setTimeout(() => this.initMouse(), 100);
this.loop = this.loop.bind(this);
requestAnimationFrame(this.loop);
}
startGame() {
this.gameActive = true;
document.getElementById('overlay').style.display = 'none';
this.player.health = 100;
this.player.ammo = Consts.WEAPONS.pistol.ammoCount;
}
loop(time) {
const dt = time - this.lastTime;
this.lastTime = time;
if (this.gameActive) {
this.update();
this.render();
}
requestAnimationFrame(this.loop);
}
update() {
this.player.update(this.keys, this.world);
// Check entity interactions
for (let i = this.world.entities.length - 1; i >= 0; i--) {
const e = this.world.entities[i];
const dist = Math.sqrt((e.x - this.player.x) ** 2 + (e.y - this.player.y) ** 2);
if (dist < Consts.TILE_SIZE * 0.8) {
if (e.type === Consts.ENTITY_TYPES.PICKUP_HEALTH) {
this.player.health = Math.min(100, this.player.health + e.hp);
this.world.removeEntity(e.id);
}
else if (e.type === Consts.ENTITY_TYPES.PICKUP_AMMO) {
this.player.ammo += e.hp;
this.world.removeEntity(e.id);
}
else if (e.type === Consts.ENTITY_TYPES.WIN_CONDITION) {
this.gameActive = false;
alert('YOU WIN!');
location.reload();
}
}
}
// Simple enemy AI / damage
for (const e of this.world.entities) {
if (e.type === Consts.ENTITY_TYPES.ENEMY) {
const dist = Math.sqrt((e.x - this.player.x) ** 2 + (e.y - this.player.y) ** 2);
if (dist < 300) {
// Move towards player
const angleToPlayer = Math.atan2(this.player.y - e.y, this.player.x - e.x);
e.x += Math.cos(angleToPlayer) * 1;
e.y += Math.sin(angleToPlayer) * 1;
if (dist < 40) {
this.player.health -= 0.5;
}
}
}
}
if (this.player.health <= 0) {
alert('GAME OVER');
location.reload();
}
// Update HUD
const healthEl = document.getElementById('health');
const ammoEl = document.getElementById('ammo');
const weaponEl = document.getElementById('weapon-name');
if (healthEl)
healthEl.innerText = Math.floor(this.player.health);
if (ammoEl)
ammoEl.innerText = this.player.ammo;
if (weaponEl)
weaponEl.innerText = Consts.WEAPONS[this.player.weaponType].name;
}
render() {
const { ctx, canvas, player, world } = this;
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Raycasting logic
const numRays = canvas.width;
const stepAngle = (Consts.FOV) / numRays;
for (let i = 0; i < numRays; i++) {
const rayAngle = player.angle - (Consts.FOV / 2) + (i * stepAngle);
let distance = 0;
let hitWall = false;
const cosA = Math.cos(rayAngle);
const sinA = Math.sin(rayAngle);
while (!hitWall && distance < 800) {
distance += 2;
const testX = player.x + cosA * distance;
const testY = player.y + sinA * distance;
if (world.isWall(testX, testY)) {
hitWall = true;
}
}
// Correcting for wall height and perspective
let wallHeight = (Consts.TILE_SIZE * 10) / (distance * Math.cos(rayAngle - player.angle));
if (wallHeight > canvas.height)
wallHeight = canvas.height;
const brightness = Math.max(0, 255 - (distance / 3));
ctx.fillStyle = `rgb(${brightness}, ${brightness / 1.5}, ${brightness / 4})`;
ctx.fillRect(i, (canvas.height - wallHeight) / 2, 1, wallHeight);
}
// Draw a simple gun overlay
ctx.fillStyle = '#333';
ctx.fillRect(canvas.width / 2 - 20, canvas.height - 40, 40, 40);
}
initMouse() {
window.addEventListener('mousemove', (e) => {
if (document.pointerLockElement === this.canvas) {
this.player.angle += e.movementX * 0.003;
}
});
window.addEventListener('mousedown', () => {
if (this.gameActive && document.pointerLockElement === this.canvas) {
const weapon = Consts.WEAPONS[this.player.weaponType];
if (this.player.shoot(weapon)) {
// Simple visual feedback for shooting: flash the screen or gun
}
}
});
window.addEventListener('keydown', (e) => {
if (e.key === '1')
this.player.weaponType = 'pistol';
if (e.key === '2')
this.player.weaponType = 'shotgun';
if (e.key === '3')
this.player.weaponType = 'rocket';
});
}
}
exports.GameEngine = GameEngine;
// Entry Point
const engine = new GameEngine();
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>