← DOOM results

DOOM

GPT 6 Luna · full

Initial view of DOOM

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 25,492 bytes · SHA-256 16806d17c253
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>IRON VEIL — Sector 09</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Share+Tech+Mono&display=swap');
:root{--acid:#d8ff50;--orange:#ff713e;--paper:#d9d4bd;--ink:#090d0d}
*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#080b0b;color:var(--paper);font-family:'Share Tech Mono',monospace}body{display:grid;place-items:center}.shell{position:relative;width:min(100vw,1440px);height:min(100vh,900px);background:#111;border:1px solid #3a4039;overflow:hidden;box-shadow:0 0 70px #000}.game{position:absolute;inset:0;background:#101515}canvas{display:block;width:100%;height:100%;image-rendering:pixelated;cursor:crosshair}.topbar{position:absolute;z-index:3;top:0;left:0;right:0;height:54px;padding:0 24px;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(#080b0de8,#080b0d00);pointer-events:none}.brand{font:11px 'Press Start 2P',monospace;color:var(--acid);letter-spacing:-1px}.brand span{color:#e8e1cb}.sector{font-size:12px;letter-spacing:3px;color:#aaa996}.top-right{display:flex;gap:20px;align-items:center;font-size:11px;letter-spacing:2px;color:#aaa996}.signal{color:var(--acid)}.hud{position:absolute;z-index:2;bottom:0;left:0;right:0;height:124px;background:linear-gradient(0deg,#080b0df5 0%,#101414f2 72%,#14181700 100%);display:flex;align-items:flex-end;padding:0 30px 20px;gap:25px;pointer-events:none}.stat{min-width:115px}.label{font-size:10px;letter-spacing:2.4px;color:#777e73;margin-bottom:7px}.value{font:18px 'Press Start 2P',monospace;color:#eee8d3}.health .value{color:var(--acid)}.armor .value{color:#70d5cc}.bar{height:4px;background:#313630;margin-top:9px;width:100%;position:relative}.bar i{display:block;height:100%;background:var(--acid);width:100%;transition:width .2s}.armor .bar i{background:#70d5cc}.weapon-info{margin-left:auto;text-align:right;min-width:200px}.weapon-name{font:12px 'Press Start 2P',monospace;color:var(--acid);line-height:1.7}.ammo{font:20px 'Press Start 2P',monospace;color:#f1ecd9}.ammo small{font-size:10px;color:#828477}.weapon-slots{display:flex;gap:5px;margin-top:8px;justify-content:flex-end}.slot{font-size:10px;color:#777e73;border:1px solid #454a40;padding:3px 7px}.slot.active{color:var(--acid);border-color:var(--acid)}.face{position:absolute;bottom:14px;left:50%;transform:translateX(-50%);width:48px;height:58px;opacity:.9}.toast{position:absolute;z-index:4;top:76px;left:50%;transform:translateX(-50%);font:10px 'Press Start 2P',monospace;letter-spacing:1px;color:var(--acid);text-align:center;text-shadow:2px 2px #000;opacity:0;transition:opacity .2s;pointer-events:none;white-space:nowrap}.toast.show{opacity:1}.crosshair{position:absolute;z-index:2;left:50%;top:50%;width:17px;height:17px;transform:translate(-50%,-50%);pointer-events:none;opacity:.8}.crosshair:before,.crosshair:after{content:'';position:absolute;background:#e7e5d3;box-shadow:0 0 3px #000}.crosshair:before{width:1px;height:13px;left:8px;top:2px}.crosshair:after{height:1px;width:13px;left:2px;top:8px}.overlay{position:absolute;z-index:5;inset:0;display:grid;place-items:center;background:#050808bf;backdrop-filter:blur(3px)}.overlay.hidden{display:none}.modal{width:min(570px,90vw);padding:38px 42px 32px;background:#111614;border:1px solid #59604b;box-shadow:0 0 0 5px #0b0f0d,0 0 80px #000;text-align:center;position:relative}.modal:before{content:'';position:absolute;top:-1px;left:25px;width:86px;height:3px;background:var(--acid)}.eyebrow{font-size:11px;letter-spacing:4px;color:var(--acid);margin-bottom:20px}.title{font:clamp(24px,5vw,38px)/1.35 'Press Start 2P',monospace;color:#eee9d7;text-shadow:4px 4px #30372c;margin:0 0 10px}.subtitle{font:14px 'Share Tech Mono',monospace;letter-spacing:3px;color:#9a9e8e;margin-bottom:27px}.story{max-width:410px;margin:0 auto 25px;font-size:15px;line-height:1.65;color:#c2c0ae}.controls{display:flex;justify-content:center;gap:22px;margin:20px 0 27px;color:#929587;font-size:12px;line-height:1.9}.controls b{color:#e5e0cf;font-weight:normal}.button{border:1px solid var(--acid);background:var(--acid);color:#10140e;padding:16px 24px;font:11px 'Press Start 2P',monospace;cursor:pointer;box-shadow:4px 4px 0 #65792b;transition:transform .12s,box-shadow .12s}.button:hover{transform:translate(2px,2px);box-shadow:2px 2px 0 #65792b}.footnote{margin-top:20px;color:#64695e;font-size:11px;letter-spacing:1px}.mobile-controls{display:none}.minimap{position:absolute;right:24px;top:65px;z-index:2;opacity:.78;border:1px solid #606556;background:#080c0bc9;width:128px;height:128px}.damage-vignette{position:absolute;inset:0;z-index:1;pointer-events:none;background:radial-gradient(transparent 42%,rgba(194,30,18,.4));opacity:0;transition:opacity .12s}.damage-vignette.hit{opacity:1}
@media(max-width:650px){.topbar{padding:0 12px;height:42px}.brand{font-size:8px}.sector{font-size:9px;letter-spacing:1px}.top-right{font-size:9px}.hud{height:104px;padding:0 12px 12px;gap:10px}.stat{min-width:74px}.label{font-size:8px}.value{font-size:13px}.weapon-info{min-width:130px}.weapon-name{font-size:9px}.ammo{font-size:15px}.face{display:none}.minimap{width:88px;height:88px;right:10px;top:49px}.controls{gap:10px;font-size:10px}.modal{padding:28px 20px}.mobile-controls{display:block;position:absolute;z-index:3;bottom:118px;left:12px;right:12px;pointer-events:none}.mobile-controls button{pointer-events:auto;touch-action:none;background:#111b;color:#d8ff50;border:1px solid #a2b74c;padding:13px;font:10px 'Share Tech Mono';}.pad{display:flex;gap:5px}.pad button{width:43px;height:43px}.mobile-fire{position:absolute;right:4px;bottom:0;width:68px;height:68px;border-radius:50%}.mobile-controls.hidden{display:none}}
</style>
</head>
<body>
<div class="shell"><div class="game"><canvas id="view" width="480" height="270"></canvas><canvas class="minimap" id="map" width="128" height="128"></canvas><div class="crosshair"></div><div class="damage-vignette" id="vignette"></div>
<div class="topbar"><div class="brand">IRON <span>VEIL</span></div><div class="sector">SECTOR 09 &nbsp; / &nbsp; THE BLACK FOUNDRY</div><div class="top-right"><span class="signal">● LIVE</span><span id="killsText">KILLS 0 / 7</span></div></div>
<div class="toast" id="toast"></div>
<div class="mobile-controls hidden" id="mobileControls"><div class="pad"><button data-move="forward">▲</button><button data-move="left">◀</button><button data-move="back">▼</button><button data-move="right">▶</button></div><button class="mobile-fire" id="mobileFire">FIRE</button></div>
<div class="hud"><div class="stat health"><div class="label">VITALS</div><div class="value" id="healthNum">100</div><div class="bar"><i id="healthBar"></i></div></div><div class="stat armor"><div class="label">PLATING</div><div class="value" id="armorNum">25</div><div class="bar"><i id="armorBar"></i></div></div><div class="face" id="face"></div><div class="weapon-info"><div class="label">CURRENT WEAPON</div><div class="weapon-name" id="weaponName">SERVICE PISTOL</div><div class="ammo" id="ammoNum">12 <small>/ 48</small></div><div class="weapon-slots" id="slots"></div></div></div>
<div class="overlay" id="overlay"><div class="modal"><div class="eyebrow" id="eyebrow">INCOMING TRANSMISSION // 09.14</div><h1 class="title" id="title">IRON VEIL</h1><div class="subtitle" id="subtitle">A ONE-PERSON BREACH</div><p class="story" id="story">The foundry went dark six hours ago. Seven hostiles remain between you and the extraction lift. Clear the sector. Find the green beacon. Do not let them close in.</p><div class="controls"><div><b>W A S D</b> &nbsp; MOVE<br><b>MOUSE</b> &nbsp; AIM</div><div><b>CLICK</b> &nbsp; FIRE<br><b>1 / 2 / 3</b> &nbsp; WEAPONS</div></div><button class="button" id="startBtn">BREACH THE DOOR</button><div class="footnote">MOUSE CLICK TO LOCK AIM &nbsp; · &nbsp; ESC TO RELEASE</div></div></div>
</div></div>
<script>
(()=>{
const canvas=document.getElementById('view'),ctx=canvas.getContext('2d'),mapCanvas=document.getElementById('map'),mctx=mapCanvas.getContext('2d');
const W=canvas.width,H=canvas.height,FOV=Math.PI/3, map=[
'1111111111111111','1000000000000001','1020010002000001','1000010000000001','1000010000110001','1000000000100001','1110111000100001','1000000000000001','1002000000010001','1000000000010001','1000011000000001','1000000000000001','1000000000000001','1111111111111111'];
// 0 floor, 1 masonry, 2 rusted bulkhead
const player={x:1.5,y:5.5,a:.08,hp:100,armor:25};let keys={},running=false,last=0,elapsed=0,lock=false,shotFlash=0,damageFlash=0,toastTimer=0,gameState='intro',kills=0;
const weapons=[{name:'SERVICE PISTOL',mag:12,ammo:48,max:12,damage:34,rate:300,spread:.035,reload:900,color:'#dbb657'},{name:'BREACHER 12G',mag:6,ammo:24,max:6,damage:23,rate:650,spread:.19,reload:1150,color:'#ff7044'},{name:'NAIL DRIVER',mag:30,ammo:120,max:30,damage:13,rate:95,spread:.075,reload:1200,color:'#85e0b2'}];let wi=0,lastShot=0,reloading=false;
const enemies=[];function spawn(x,y,hp=100){enemies.push({x,y,hp,max:hp,alive:true,phase:Math.random()*6.2,attack:Math.random()*900,hit:0})}
[[7.5,2.5],[12.6,2.4],[3.2,5.4],[8.1,7.7],[13.1,8.5],[5.1,10.3],[11.3,11.2]].forEach((p,i)=>spawn(...p,i===4?135:100));
const pickups=[{x:4.4,y:3.2,type:'ammo',taken:false},{x:10.5,y:2.7,type:'health',taken:false},{x:2.4,y:8.7,type:'armor',taken:false},{x:7.4,y:9.5,type:'ammo',taken:false},{x:13.3,y:5.3,type:'health',taken:false},{x:9.7,y:11.2,type:'ammo',taken:false}];
const exit={x:13.9,y:12.2};
function cell(x,y){let mx=Math.floor(x),my=Math.floor(y);return map[my]?.[mx]||'1'}function blocked(x,y,r=.18){return cell(x-r,y-r)!=='0'||cell(x+r,y-r)!=='0'||cell(x-r,y+r)!=='0'||cell(x+r,y+r)!=='0'}
function say(s){const t=document.getElementById('toast');t.textContent=s;t.classList.add('show');toastTimer=1700}
function updateHud(){document.getElementById('healthNum').textContent=Math.max(0,Math.ceil(player.hp));document.getElementById('armorNum').textContent=Math.ceil(player.armor);document.getElementById('healthBar').style.width=Math.max(0,player.hp)+'%';document.getElementById('armorBar').style.width=Math.min(100,player.armor*2)+'%';document.getElementById('ammoNum').innerHTML=`${weapons[wi].mag} <small>/ ${weapons[wi].ammo}</small>`;document.getElementById('weaponName').textContent=weapons[wi].name;document.getElementById('killsText').textContent=`KILLS ${kills} / 7`;document.getElementById('slots').innerHTML=weapons.map((w,i)=>`<span class="slot ${i===wi?'active':''}">${i+1} ${['PST','12G','NDR'][i]}</span>`).join('');drawFace()}
function drawFace(){let el=document.getElementById('face');let hp=player.hp;let brow=hp>65?'#d7cdb4':hp>30?'#cf9363':'#bd5947';el.innerHTML=`<svg viewBox="0 0 48 58" width="48" height="58" shape-rendering="crispEdges"><path fill="#242b25" d="M7 58V39L3 29V17L9 7 20 2h10l11 7 4 10-3 15-6 8v16z"/><path fill="#b8a27c" d="M11 20l5-7 8-2 10 3 5 9-2 16-7 9H17l-7-10z"/><path fill="#695b49" d="M10 20l7-10 12-2 10 8-1 6-10-5-9 2-7 8z"/><path fill="${brow}" d="M12 24l9-3 3 3-4 2-8 1zm14 0 4-3 8 3v3l-8-1z"/><path fill="#171c19" d="M15 27h7v3h-7zm13 0h7v3h-7z"/><path fill="#e1d3b0" d="M24 29h3v8h-3zm-5 11h15l-3 4H21z"/><path fill="#563f37" d="M18 41l4 1 5-1 6 1-4 4h-8z"/><path fill="#29312b" d="M8 53l5-8 8 4 7 1 9-4 5 8v5H8z"/><path fill="#65704b" d="M4 57h40v2H4z"/></svg>`}
function switchWeapon(n){if(n<0||n>2||n===wi)return;wi=n;reloading=false;updateHud();say(weapons[n].name)}
function reload(){let w=weapons[wi];if(reloading||w.mag===w.max||w.ammo<=0)return;reloading=true;say('RELOADING');setTimeout(()=>{if(!running||gameState!=='playing'){reloading=false;return}let take=Math.min(w.max-w.mag,w.ammo);w.mag+=take;w.ammo-=take;reloading=false;updateHud()},w.reload)}
function fire(){if(!running||gameState!=='playing')return;const w=weapons[wi],now=performance.now();if(now-lastShot<w.rate||reloading)return;lastShot=now;if(w.mag<=0){say('CLICK — RELOAD');return}w.mag--;shotFlash=100;updateHud();let candidates=[];for(let e of enemies){if(!e.alive)continue;let dx=e.x-player.x,dy=e.y-player.y,dist=Math.hypot(dx,dy),ang=Math.atan2(dy,dx)-player.a;while(ang>Math.PI)ang-=2*Math.PI;while(ang< -Math.PI)ang+=2*Math.PI;if(Math.abs(ang)<w.spread+Math.atan(.28/dist)){let steps=Math.ceil(dist*12),clear=true;for(let j=1;j<steps;j++){let t=j/steps;if(cell(player.x+dx*t,player.y+dy*t)!=='0'){clear=false;break}}if(clear)candidates.push({e,dist,ang})}}candidates.sort((a,b)=>Math.abs(a.ang)-Math.abs(b.ang));if(candidates.length){let target=candidates[0].e;let dmg=w.damage*(wi===1?Math.max(.45,1-candidates[0].dist*.035):1)*(1+Math.random()*.18);target.hp-=dmg;target.hit=180;if(target.hp<=0){target.alive=false;kills++;say(kills===7?'SECTOR CLEAR — FIND THE BEACON':'HOSTILE DOWN');updateHud();if(kills===7)say('ALL HOSTILES DOWN — FIND THE GREEN BEACON')}}}
function damage(n){if(player.armor>0){let soak=Math.min(player.armor,n*.65);player.armor-=soak;n-=soak}player.hp-=n;damageFlash=180;document.getElementById('vignette').classList.add('hit');setTimeout(()=>document.getElementById('vignette').classList.remove('hit'),130);updateHud();if(player.hp<=0)die()}
function die(){gameState='dead';running=false;document.exitPointerLock?.();document.getElementById('mobileControls').classList.add('hidden');document.getElementById('eyebrow').textContent='VITAL SIGNS LOST // SECTOR 09';document.getElementById('title').textContent='FLATLINED';document.getElementById('subtitle').textContent='THE FOUNDRY CLAIMS ANOTHER';document.getElementById('story').textContent='The breach team is gone. Your backup is already inbound. Get back on your feet and finish the job.';document.getElementById('startBtn').textContent='RESPAWN';document.getElementById('overlay').classList.remove('hidden')}
function win(){gameState='won';running=false;document.exitPointerLock?.();document.getElementById('mobileControls').classList.add('hidden');document.getElementById('eyebrow').textContent='EXTRACTION CONFIRMED // 09.15';document.getElementById('title').textContent='SECTOR CLEAR';document.getElementById('subtitle').textContent='YOU MADE IT OUT';document.getElementById('story').textContent=`${kills} hostiles neutralized. The lift doors seal behind you as the foundry lights die for good. Good work, operator.`;document.getElementById('startBtn').textContent='RUN IT AGAIN';document.getElementById('overlay').classList.remove('hidden')}
function start(){if(gameState==='dead'||gameState==='won')reset();gameState='playing';running=true;last=performance.now();document.getElementById('overlay').classList.add('hidden');if(matchMedia('(pointer:fine)').matches)canvas.requestPointerLock?.();else document.getElementById('mobileControls').classList.remove('hidden');requestAnimationFrame(loop)}
function reset(){player.x=1.5;player.y=5.5;player.a=.08;player.hp=100;player.armor=25;kills=0;wi=0;weapons.forEach((w,i)=>{w.mag=[12,6,30][i];w.ammo=[48,24,120][i]});enemies.forEach((e,i)=>{e.alive=true;e.hp=e.max;e.x=[[7.5,2.5],[12.6,2.4],[3.2,5.4],[8.1,7.7],[13.1,8.5],[5.1,10.3],[11.3,11.2]][i][0];e.y=[[7.5,2.5],[12.6,2.4],[3.2,5.4],[8.1,7.7],[13.1,8.5],[5.1,10.3],[11.3,11.2]][i][1]});pickups.forEach(p=>p.taken=false);updateHud()}
function move(dt){let forward=(keys.KeyW||keys.ArrowUp?1:0)-(keys.KeyS||keys.ArrowDown?1:0),side=(keys.KeyD?1:0)-(keys.KeyA?1:0),strafe=(keys.KeyE?1:0)-(keys.KeyQ?1:0);let speed=dt*.0034;if(keys.ShiftLeft||keys.ShiftRight)speed*=1.45;let dx=(Math.cos(player.a)*forward-Math.sin(player.a)*(side+strafe))*speed,dy=(Math.sin(player.a)*forward+Math.cos(player.a)*(side+strafe))*speed;if(!blocked(player.x+dx,player.y,.2))player.x+=dx;if(!blocked(player.x,player.y+dy,.2))player.y+=dy;if(keys.ArrowLeft)player.a-=dt*.0025;if(keys.ArrowRight)player.a+=dt*.0025}
function enemiesUpdate(dt){for(const e of enemies){if(!e.alive)continue;e.phase+=dt*.005;e.attack-=dt;let dx=player.x-e.x,dy=player.y-e.y,d=Math.hypot(dx,dy);if(d<8&&d>1.2){let sp=dt*.00048;let nx=e.x+dx/d*sp,ny=e.y+dy/d*sp;if(!blocked(nx,e.y,.2))e.x=nx;if(!blocked(e.x,ny,.2))e.y=ny}if(d<7&&e.attack<=0){let a=Math.atan2(dy,dx)-player.a;while(a>Math.PI)a-=Math.PI*2;while(a< -Math.PI)a+=Math.PI*2;if(Math.abs(a)<.33){damage(5+Math.random()*5);e.attack=1250+Math.random()*800}}}for(let p of pickups){if(p.taken)continue;if(Math.hypot(player.x-p.x,player.y-p.y)<.55){let got=false;if(p.type==='health'&&player.hp<100){player.hp=Math.min(100,player.hp+35);say('VITALS +35');got=true}else if(p.type==='armor'&&player.armor<100){player.armor=Math.min(100,player.armor+35);say('PLATING +35');got=true}else if(p.type==='ammo'){weapons[0].ammo+=12;weapons[1].ammo+=6;weapons[2].ammo+=30;say('AMMO ACQUIRED');got=true}if(got){p.taken=true;updateHud()}}}if(kills===7&&Math.hypot(player.x-exit.x,player.y-exit.y)<.8)win()}
function cast(angle){let sin=Math.sin(angle),cos=Math.cos(angle);let dist=0,hit='1',side=0,step=.035;while(dist<20){dist+=step;let x=player.x+cos*dist,y=player.y+sin*dist;let c=cell(x,y);if(c!=='0'){hit=c;side=(Math.abs((x-Math.floor(x))-.5)>Math.abs((y-Math.floor(y))-.5))?1:0;break}}return{dist,hit,side}}
function wallColor(hit,side,dist,u){let base=hit==='2'?[112,65,47]:[79,91,73];let shade=(side?.77:1)*Math.max(.22,1-dist/13);let mortar=(Math.floor(u*8)%2===0?1:0);let yline=(Math.floor((u*5)%1*0)===-1);return base.map(v=>Math.floor(v*shade))}
function render(){let sky=ctx.createLinearGradient(0,0,0,H*.52);sky.addColorStop(0,'#171d1c');sky.addColorStop(1,'#39403a');ctx.fillStyle=sky;ctx.fillRect(0,0,W,H/2);let floor=ctx.createLinearGradient(0,H/2,0,H);floor.addColorStop(0,'#33362e');floor.addColorStop(.25,'#20231f');floor.addColorStop(1,'#101312');ctx.fillStyle=floor;ctx.fillRect(0,H/2,W,H/2);let depth=new Float32Array(W);
for(let x=0;x<W;x++){let angle=player.a+(x/W-.5)*FOV;let ray=cast(angle),d=ray.dist*Math.cos(angle-player.a);depth[x]=d;let wh=Math.min(H*2,H/d),top=(H-wh)/2;let u=(player.x+Math.cos(angle)*d)%1;if(ray.side)u=(player.y+Math.sin(angle)*d)%1;let base=ray.hit==='2'?[113,67,49]:[83,91,70],light=(ray.side?.72:1)*Math.max(.24,1-d/12);for(let y=0;y<wh;y+=1){let yy=y/wh, stripe=Math.floor(yy*8), mortar=(stripe%2===0&&yy*8%1<.045)||(u*8%1<.035);let grime=(Math.sin(u*75+stripe*4)*.05);let val=mortar?.48:1+grime;ctx.fillStyle=`rgb(${base.map(v=>Math.max(0,Math.floor(v*light*val))).join(',')})`;ctx.fillRect(x,top+y,1,1)}if(d<4){ctx.fillStyle=`rgba(15,18,15,${.10*(4-d)})`;ctx.fillRect(x,top,1,wh)}}
// Floor perspective seams
for(let y=Math.floor(H*.56);y<H-80;y+=12){let alpha=(y-H*.53)/(H*.53)*.13;ctx.fillStyle=`rgba(178,164,120,${alpha})`;ctx.fillRect(0,y,W,1)}
// Billboard sprites painter's order
let sprites=[];for(let e of enemies)if(e.alive)sprites.push({x:e.x,y:e.y,type:'enemy',obj:e});for(let p of pickups)if(!p.taken)sprites.push({x:p.x,y:p.y,type:p.type,obj:p});if(kills===7)sprites.push({x:exit.x,y:exit.y,type:'exit'});sprites.sort((a,b)=>Math.hypot(b.x-player.x,b.y-player.y)-Math.hypot(a.x-player.x,a.y-player.y));for(let s of sprites)drawSprite(s,depth);
// weapon view model
let bob=(Math.abs(Math.sin(elapsed*.009))*(keys.KeyW||keys.KeyS?4:1));let cx=W/2,baseY=H+14+bob;ctx.save();if(shotFlash>0){ctx.fillStyle='rgba(255,206,104,.9)';ctx.beginPath();ctx.moveTo(cx-8,baseY-95);ctx.lineTo(cx,baseY-140-Math.random()*12);ctx.lineTo(cx+10,baseY-95);ctx.fill();shotFlash-=18}let gunColor=wi===0?'#697063':wi===1?'#8b5944':'#647b6a';ctx.fillStyle='#151918';ctx.fillRect(cx-36,baseY-77,72,90);ctx.fillStyle=gunColor;if(wi===0){ctx.fillRect(cx-25,baseY-82,50,62);ctx.fillRect(cx-9,baseY-111,18,43);ctx.fillStyle='#282e29';ctx.fillRect(cx-5,baseY-116,10,35);ctx.fillStyle='#a2a17b';ctx.fillRect(cx-3,baseY-105,6,3)}else if(wi===1){ctx.fillRect(cx-34,baseY-91,68,55);ctx.fillRect(cx-15,baseY-122,30,43);ctx.fillStyle='#343d36';ctx.fillRect(cx-7,baseY-138,14,46);ctx.fillRect(cx+22,baseY-65,10,38)}else{ctx.fillRect(cx-33,baseY-87,66,53);ctx.fillRect(cx-12,baseY-113,24,39);ctx.fillStyle='#313b35';ctx.fillRect(cx-4,baseY-136,8,38);ctx.fillRect(cx-25,baseY-56,19,28)}ctx.fillStyle='#b0a783';ctx.fillRect(cx-28,baseY-34,56,9);ctx.fillStyle='#252a25';ctx.fillRect(cx-4,baseY-117,8,3);ctx.restore();
// restrained CRT scanlines
ctx.fillStyle='rgba(0,0,0,.10)';for(let y=0;y<H;y+=3)ctx.fillRect(0,y,W,1);
}
function drawSprite(s,depth){let dx=s.x-player.x,dy=s.y-player.y,dist=Math.hypot(dx,dy),ang=Math.atan2(dy,dx)-player.a;while(ang>Math.PI)ang-=2*Math.PI;while(ang< -Math.PI)ang+=2*Math.PI;if(Math.abs(ang)>FOV*.68||dist<.15)return;let sx=(.5+ang/FOV)*W,size=H/dist*(s.type==='enemy'?0.78:s.type==='exit'?.8:.35),bottom=H/2+size*.5,top=bottom-size;if(sx<0||sx>=W)return;let z=depth[Math.floor(sx)];if(dist>z+.3)return;ctx.save();ctx.translate(sx,0);let pulse=Math.sin(elapsed*.006+s.x)*2;
if(s.type==='enemy'){let hurt=s.obj.hit>0;s.obj.hit=Math.max(0,s.obj.hit-17);let w=size*.45,h=size*.78;let y=bottom-h;ctx.fillStyle='#151b18';ctx.fillRect(-w*.55,y+h*.3,w*1.1,h*.7);ctx.fillStyle=hurt?'#f2c6a2':'#73806a';ctx.fillRect(-w*.42,y+h*.32,w*.84,h*.52);ctx.fillStyle=hurt?'#fff':'#9e654d';ctx.fillRect(-w*.36,y+h*.05,w*.72,h*.32);ctx.fillStyle='#292c27';ctx.fillRect(-w*.47,y+h*.02,w*.94,h*.1);ctx.fillStyle='#ff5b38';ctx.fillRect(-w*.25,y+h*.16,w*.13,h*.065);ctx.fillRect(w*.12,y+h*.16,w*.13,h*.065);ctx.fillStyle='#303832';ctx.fillRect(-w*.7,y+h*.42,w*.23,h*.35);ctx.fillRect(w*.47,y+h*.42,w*.23,h*.35);ctx.fillStyle='#202722';ctx.fillRect(-w*.39,y+h*.78,w*.28,h*.25);ctx.fillRect(w*.12,y+h*.78,w*.28,h*.25);if(s.obj.hp<s.obj.max){ctx.fillStyle='#292b25';ctx.fillRect(-w/2,y-7,w,3);ctx.fillStyle='#dd5c42';ctx.fillRect(-w/2,y-7,w*Math.max(0,s.obj.hp/s.obj.max),3)}}else if(s.type==='health'||s.type==='ammo'||s.type==='armor'){let col=s.type==='health'?'#d7614e':s.type==='ammo'?'#d1a74d':'#5bc7ae',sz=size*.7,y=H/2+size*.55;ctx.fillStyle='rgba(0,0,0,.35)';ctx.fillRect(-sz*.6,y,sz*1.2,3);ctx.fillStyle=col;ctx.fillRect(-sz/2,y-sz-pulse,sz,sz);ctx.fillStyle='#17201b';if(s.type==='health'){ctx.fillRect(-sz*.12,y-sz*.82,sz*.24,sz*.64);ctx.fillRect(-sz*.32,y-sz*.62,sz*.64,sz*.24)}else if(s.type==='ammo'){ctx.fillRect(-sz*.28,y-sz*.75,sz*.56,sz*.5);ctx.fillStyle='#e2d6b5';ctx.fillRect(-sz*.08,y-sz*.72,sz*.16,sz*.35)}else{ctx.fillRect(-sz*.28,y-sz*.8,sz*.56,sz*.62);ctx.fillStyle='#c7eee2';ctx.fillRect(-sz*.07,y-sz*.68,sz*.14,sz*.38)}}else{let sz=size*.9,y=H/2+size*.5;ctx.shadowColor='#a8ff3b';ctx.shadowBlur=18;ctx.fillStyle='#344d2d';ctx.fillRect(-sz*.35,y-sz,sz*.7,sz);ctx.fillStyle='#a8ff3b';ctx.fillRect(-sz*.18,y-sz*.78,sz*.36,sz*.58);ctx.fillRect(-sz*.08,y-sz*.95,sz*.16,sz*.12);ctx.shadowBlur=0;ctx.fillStyle='#d8ff50';ctx.font='7px monospace';ctx.textAlign='center';ctx.fillText('EXIT',0,y-sz-3)}ctx.restore()}
function drawMap(){mctx.fillStyle='#080c0b';mctx.fillRect(0,0,128,128);let scale=8;for(let y=0;y<map.length;y++)for(let x=0;x<map[y].length;x++){if(map[y][x]!=='0'){mctx.fillStyle=map[y][x]==='2'?'#78604b':'#59604c';mctx.fillRect(x*scale,y*scale,scale-1,scale-1)}}for(let e of enemies)if(e.alive){mctx.fillStyle='#ed644a';mctx.fillRect(e.x*scale-2,e.y*scale-2,4,4)}for(let p of pickups)if(!p.taken){mctx.fillStyle=p.type==='health'?'#e07565':p.type==='ammo'?'#d8b657':'#62c9b0';mctx.fillRect(p.x*scale-1.5,p.y*scale-1.5,3,3)}if(kills===7){mctx.fillStyle='#d8ff50';mctx.fillRect(exit.x*scale-2,exit.y*scale-2,4,4)}mctx.save();mctx.translate(player.x*scale,player.y*scale);mctx.rotate(player.a);mctx.fillStyle='#e5ff83';mctx.beginPath();mctx.moveTo(5,0);mctx.lineTo(-4,-3);mctx.lineTo(-3,3);mctx.fill();mctx.restore()}
function loop(now){if(!running)return;let dt=Math.min(40,now-last);last=now;elapsed+=dt;move(dt);enemiesUpdate(dt);if(toastTimer>0){toastTimer-=dt;if(toastTimer<=0)document.getElementById('toast').classList.remove('show')}render();drawMap();requestAnimationFrame(loop)}
// Input
window.addEventListener('keydown',e=>{keys[e.code]=true;if(['Space','ArrowUp','ArrowDown','ArrowLeft','ArrowRight'].includes(e.code))e.preventDefault();if(e.code==='Digit1')switchWeapon(0);if(e.code==='Digit2')switchWeapon(1);if(e.code==='Digit3')switchWeapon(2);if(e.code==='KeyR')reload();if(e.code==='Space')fire()});window.addEventListener('keyup',e=>keys[e.code]=false);canvas.addEventListener('click',()=>{if(gameState==='playing'){if(document.pointerLockElement!==canvas)canvas.requestPointerLock?.();fire()}});document.addEventListener('pointerlockchange',()=>lock=document.pointerLockElement===canvas);document.addEventListener('mousemove',e=>{if(lock&&running)player.a+=e.movementX*.0024});window.addEventListener('mousedown',e=>{if(e.button===0&&gameState==='playing'&&lock)fire()});document.getElementById('startBtn').onclick=start;
document.querySelectorAll('[data-move]').forEach(b=>{let code={forward:'KeyW',back:'KeyS',left:'KeyA',right:'KeyD'}[b.dataset.move];b.addEventListener('pointerdown',e=>{e.preventDefault();keys[code]=true});['pointerup','pointerleave','pointercancel'].forEach(ev=>b.addEventListener(ev,()=>keys[code]=false))});document.getElementById('mobileFire').addEventListener('pointerdown',e=>{e.preventDefault();fire()});
updateHud();render();drawMap();
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"openai/gpt-6-luna","provider":"openrouter","persona":"full","sessionId":"bd6dc73f-107f-43b6-bc20-19ce53d7a3fe","tokensIn":105825,"tokensOut":11545,"tokensTotal":117370,"cost":0.00855055,"turns":8,"toolCalls":7,"failedToolCalls":0,"timestamp":"2026-09-22T23:51:48.083Z"} -->