<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AGENT_007: INTERIOR ESCAPE</title>
<style>
body { margin: 0; background: #050505; display: flex; justify-content: center; align-items: center; height: 100vh; color: white; font-family: 'Courier New', monospace; overflow: hidden; }
canvas { background: #0d0d14; box-shadow: 0 0 30px rgba(42, 157, 143, 0.4); border: 2px solid #2a9d8f; cursor: crosshair; }
#ui { position: absolute; top: 10px; text-align: center; width: 100%; pointer-events: none; }
#scoreBoard { position: absolute; top: 20px; right: 30px; font-size: 24px; color: #e9c46a; text-shadow: 0 0 10px #e9c46a; }
.controls { font-size: 14px; opacity: 0.8; }
</style>
</head>
<body>
<div id="ui">
<h2 id="status">AGENT_007: INFILTRATION</h2>
<p class="controls">A/D: Move (Hold 1.5s to Sprint) | W: Moon-Jump | Jump on walls to Wall-Jump<br>
<b>Q: Equip Blaster | Left Click: Shoot</b></p>
</div>
<div id="scoreBoard">DISTANCE: 0m</div>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreBoard = document.getElementById('scoreBoard');
let gameState = 'MENU';
const camera = { x: 0, y: 0 };
let player;
let platforms = [];
let backgroundProps = [];
let projectiles = [];
let enemies = [];
let particles = [];
let items = [];
let nextChunkX = 0;
const chunkWidth = 800;
let distanceScore = 0;
let deathTimer = 0;
const gravity = 0.15;
const friction = 0.85;
const keys = [];
let mouseX = 0, mouseY = 0;
let lastQPress = 0;
function initGame() {
player = {
x: 100, y: 400, width: 28, height: 44,
velX: 0, velY: 0, hp: 100, maxHp: 100,
speed: 5, sprintSpeed: 9, jumpPower: 9,
moveFrames: 0,
grounded: false, touchingWall: null, facingRight: true, state: 'idle',
hasBlasterEquipped: false
};
platforms = []; backgroundProps = []; projectiles = []; enemies = []; particles = []; items = [];
nextChunkX = 0; distanceScore = 0; deathTimer = 0;
platforms.push({ x: -20, y: 0, w: 20, h: 600 });
platforms.push({ x: 0, y: 0, w: chunkWidth, h: 40 });
platforms.push({ x: 0, y: 560, w: chunkWidth, h: 40 });
nextChunkX += chunkWidth;
generateRoom(); generateRoom();
}
// --- INPUT HANDLING ---
window.addEventListener("keydown", e => {
keys[e.code] = true;
if((gameState === 'MENU' || gameState === 'GAMEOVER') && e.code === 'Enter') {
initGame(); gameState = 'PLAYING'; document.getElementById('ui').style.display = 'none';
}
if (e.code === 'KeyQ' && gameState === 'PLAYING') {
const now = Date.now();
if (now - lastQPress > 200) {
player.hasBlasterEquipped = !player.hasBlasterEquipped;
lastQPress = now;
}
}
});
window.addEventListener("keyup", e => keys[e.code] = false);
window.addEventListener('mousemove', e => {
const rect = canvas.getBoundingClientRect();
mouseX = e.clientX - rect.left;
mouseY = e.clientY - rect.top;
});
window.addEventListener('mousedown', e => {
if (e.button === 0 && player.hasBlasterEquipped && gameState === 'PLAYING') {
shootBlaster();
}
});
function shootBlaster() {
const pCenterX = player.x + player.width / 2;
const pCenterY = player.y + player.height / 2;
const targetX = mouseX + camera.x;
const angle = Math.atan2(mouseY - pCenterY, targetX - pCenterX);
player.velX -= Math.cos(angle) * 1.5;
projectiles.push({
x: pCenterX + Math.cos(angle) * 30,
y: pCenterY + Math.sin(angle) * 30,
velX: Math.cos(angle) * 20,
velY: Math.sin(angle) * 20,
life: 60,
owner: 'player'
});
}
// --- ENTITY FACTORIES ---
function createGuard(x, y) {
return {
type: 'guard', x: x, y: y, width: 28, height: 44,
velX: 0, velY: 0, hp: 50, maxHp: 50,
facingRight: false, state: 'idle', cooldown: 0, aimAngle: 0
};
}
function createDrone(x, y) {
return {
type: 'drone', x: x, y: y, startY: y, width: 24, height: 20,
velX: 0, velY: 0, hp: 30, maxHp: 30,
facingRight: false, state: 'patrol', cooldown: 0, aimAngle: 0,
hoverTimer: Math.random() * 100
};
}
function createMedKit(x, y) {
return { x: x, y: y, width: 16, height: 16, velX: 0, velY: -3, grounded: false, type: 'medkit' };
}
// --- PROCEDURAL GENERATOR ---
function generateRoom() {
const ox = nextChunkX;
const roomType = Math.floor(Math.random() * 5);
platforms.push({ x: ox, y: 0, w: chunkWidth, h: 40 });
// General Background Details
backgroundProps.push({ type: 'cautionStripe', x: ox, y: 540, w: chunkWidth, h: 20 });
if (Math.random() > 0.4) backgroundProps.push({ type: 'vent', x: ox + 100 + Math.random() * 500, y: 150 + Math.random() * 150, w: 80, h: 80 });
if (Math.random() > 0.3) backgroundProps.push({ type: 'wires', x: ox + Math.random() * 600, y: 0, w: 200, h: 120 });
if (roomType === 0) {
platforms.push({ x: ox, y: 560, w: 300, h: 40 });
platforms.push({ x: ox + 500, y: 560, w: 300, h: 40 });
platforms.push({ x: ox + 250, y: 400, w: 100, h: 20 });
backgroundProps.push({ type: 'window', x: ox + 150, y: 100, w: 500, h: 200 });
backgroundProps.push({ type: 'terminal', x: ox + 100, y: 510, w: 80, h: 50 });
backgroundProps.push({ type: 'serverRack', x: ox + 520, y: 360, w: 80, h: 200 });
if (Math.random() < 0.6) enemies.push(createGuard(ox + 600, 560 - 44));
if (Math.random() < 0.6) enemies.push(createGuard(ox + 270, 400 - 44));
if (Math.random() < 0.4) enemies.push(createDrone(ox + 400, 150));
}
else if (roomType === 1) {
platforms.push({ x: ox, y: 560, w: 100, h: 40 });
platforms.push({ x: ox + 200, y: 450, w: 100, h: 20 });
platforms.push({ x: ox + 450, y: 300, w: 100, h: 20 });
platforms.push({ x: ox + 700, y: 450, w: 100, h: 20 });
backgroundProps.push({ type: 'pipes', x: ox, y: 50, w: chunkWidth, h: 100 });
backgroundProps.push({ type: 'danger', x: ox, y: 560, w: 100, h: 10 });
backgroundProps.push({ type: 'crates', x: ox + 710, y: 390, w: 60, h: 60 });
if (Math.random() < 0.35) items.push(createMedKit(ox + 732, 360));
if (Math.random() < 0.6) enemies.push(createGuard(ox + 470, 300 - 44));
if (Math.random() < 0.3) enemies.push(createDrone(ox + 250, 100));
}
else if (roomType === 2) {
platforms.push({ x: ox, y: 560, w: chunkWidth, h: 40 });
platforms.push({ x: ox, y: 300, w: 250, h: 20 });
platforms.push({ x: ox + 550, y: 300, w: 250, h: 20 });
platforms.push({ x: ox + 350, y: 400, w: 100, h: 160 });
backgroundProps.push({ type: 'table', x: ox + 100, y: 520, w: 120, h: 40 });
backgroundProps.push({ type: 'table', x: ox + 600, y: 520, w: 120, h: 40 });
backgroundProps.push({ type: 'serverRack', x: ox + 20, y: 100, w: 70, h: 200 });
backgroundProps.push({ type: 'serverRack', x: ox + 700, y: 100, w: 70, h: 200 });
if (Math.random() < 0.25) items.push(createMedKit(ox + 150, 480));
if (Math.random() < 0.5) enemies.push(createGuard(ox + 650, 560 - 44));
if (Math.random() < 0.5) enemies.push(createGuard(ox + 100, 300 - 44));
if (Math.random() < 0.4) enemies.push(createDrone(ox + 400, 200));
}
else if (roomType === 3) {
platforms.push({ x: ox, y: 560, w: 200, h: 40 });
platforms.push({ x: ox + 200, y: 480, w: 150, h: 20 });
platforms.push({ x: ox + 350, y: 380, w: 150, h: 20 });
platforms.push({ x: ox + 500, y: 280, w: 150, h: 20 });
platforms.push({ x: ox + 650, y: 180, w: 150, h: 20 });
backgroundProps.push({ type: 'pipes', x: ox, y: 0, w: chunkWidth, h: 600 });
backgroundProps.push({ type: 'vent', x: ox + 400, y: 100, w: 80, h: 80 });
backgroundProps.push({ type: 'crates', x: ox + 50, y: 500, w: 60, h: 60 });
if (Math.random() < 0.35) items.push(createMedKit(ox + 70, 470));
if (Math.random() < 0.6) enemies.push(createGuard(ox + 680, 180 - 44));
if (Math.random() < 0.3) enemies.push(createDrone(ox + 100, 250));
}
else if (roomType === 4) {
platforms.push({ x: ox, y: 560, w: chunkWidth, h: 40 });
platforms.push({ x: ox + 200, y: 0, w: 50, h: 400 });
platforms.push({ x: ox + 500, y: 200, w: 50, h: 400 });
backgroundProps.push({ type: 'danger', x: ox, y: 560, w: chunkWidth, h: 10 });
backgroundProps.push({ type: 'terminal', x: ox + 100, y: 510, w: 60, h: 50 });
backgroundProps.push({ type: 'serverRack', x: ox + 600, y: 360, w: 100, h: 200 });
backgroundProps.push({ type: 'crates', x: ox + 720, y: 500, w: 60, h: 60 });
backgroundProps.push({ type: 'crates', x: ox + 730, y: 440, w: 50, h: 60 });
if (Math.random() < 0.25) items.push(createMedKit(ox + 745, 410));
if (Math.random() < 0.7) enemies.push(createGuard(ox + 700, 560 - 44));
if (Math.random() < 0.4) enemies.push(createDrone(ox + 350, 300));
}
nextChunkX += chunkWidth;
}
function cleanRooms() {
platforms = platforms.filter(p => p.x + p.w > camera.x - 800);
backgroundProps = backgroundProps.filter(p => p.x + p.w > camera.x - 800);
enemies = enemies.filter(e => e.x + e.width > camera.x - 800);
items = items.filter(i => i.x + i.width > camera.x - 800);
}
function checkCollision(shapeA, shapeB) {
const vX = (shapeA.x + (shapeA.width / 2)) - (shapeB.x + (shapeB.w / 2));
const vY = (shapeA.y + (shapeA.height / 2)) - (shapeB.y + (shapeB.h / 2));
const hWidths = (shapeA.width / 2) + (shapeB.w / 2);
const hHeights = (shapeA.height / 2) + (shapeB.h / 2);
let colDir = null;
if (Math.abs(vX) < hWidths && Math.abs(vY) < hHeights) {
const oX = hWidths - Math.abs(vX);
const oY = hHeights - Math.abs(vY);
if (oX >= oY) {
if (vY > 0) { colDir = "t"; shapeA.y += oY; } else { colDir = "b"; shapeA.y -= oY; }
} else {
if (vX > 0) { colDir = "l"; shapeA.x += oX; } else { colDir = "r"; shapeA.x -= oX; }
}
} return colDir;
}
// --- PARTICLE SYSTEM ---
function createParticles(x, y, type) {
let colors = [];
if (type === 'player') colors = ['#00f2ff', '#0099cc', '#e63946', '#222', '#444'];
else if (type === 'enemy' || type === 'drone') colors = ['#ff3333', '#aa0000', '#111', '#222', '#4a4e69'];
else if (type === 'heal') colors = ['#00ff41', '#b5e48c', '#ffffff'];
let amt = type === 'heal' ? 15 : 40;
for (let i = 0; i < amt; i++) {
particles.push({
x: x, y: y,
velX: (Math.random() - 0.5) * 16,
velY: (Math.random() - 0.5) * 16 - 3,
life: 1.0,
color: colors[Math.floor(Math.random() * colors.length)],
size: Math.random() * 6 + 4,
rot: Math.random() * Math.PI * 2,
rotVel: (Math.random() - 0.5) * 0.5
});
}
}
function updateParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
let p = particles[i];
p.velY += gravity;
p.x += p.velX;
p.y += p.velY;
p.rot += p.rotVel;
p.life -= 0.015;
for (let j = 0; j < platforms.length; j++) {
let plat = platforms[j];
if (p.x > plat.x && p.x < plat.x + plat.w && p.y + p.size/2 > plat.y && p.y - p.size/2 < plat.y + plat.h) {
if (p.velY > 0 && p.y < plat.y + 15) {
p.y = plat.y - p.size/2;
p.velY *= -0.4;
p.velX *= 0.8;
}
}
}
if (p.life <= 0) particles.splice(i, 1);
}
}
function update() {
if (gameState === 'MENU' || gameState === 'GAMEOVER') return;
updateParticles();
if (gameState === 'DYING') {
deathTimer--;
if (deathTimer <= 0) gameState = 'GAMEOVER';
return;
}
if (player.hp <= 0 && gameState === 'PLAYING') {
gameState = 'DYING';
deathTimer = 120;
createParticles(player.x + player.width/2, player.y + player.height/2, 'player');
return;
}
if (player.y > 800) {
gameState = 'GAMEOVER';
return;
}
if (player.hasBlasterEquipped) {
player.facingRight = (mouseX + camera.x > player.x + player.width / 2);
}
// --- ITEMS ---
for (let i = items.length - 1; i >= 0; i--) {
let item = items[i];
item.velY += gravity;
item.y += item.velY;
item.grounded = false;
for (let j = 0; j < platforms.length; j++) {
let dir = checkCollision(item, platforms[j]);
if (dir === "b") { item.grounded = true; item.velY = 0; }
}
if (player.x < item.x + item.width && player.x + player.width > item.x &&
player.y < item.y + item.height && player.y + player.height > item.y) {
if (item.type === 'medkit') {
player.hp = Math.min(player.maxHp, player.hp + 30);
createParticles(item.x + item.width/2, item.y + item.height/2, 'heal');
}
items.splice(i, 1);
continue;
}
if (item.y > 800) items.splice(i, 1);
}
// --- PLAYER PHYSICS & INPUT ---
if (keys['ArrowUp'] || keys['KeyW']) {
if (player.grounded) {
player.velY = -player.jumpPower;
keys['ArrowUp'] = false; keys['KeyW'] = false;
} else if (player.touchingWall) {
player.velY = -player.jumpPower * 0.9;
if (player.touchingWall === 'left') {
player.velX = player.sprintSpeed;
if (!player.hasBlasterEquipped) player.facingRight = true;
} else if (player.touchingWall === 'right') {
player.velX = -player.sprintSpeed;
if (!player.hasBlasterEquipped) player.facingRight = false;
}
keys['ArrowUp'] = false; keys['KeyW'] = false;
}
}
let isMoving = false;
if (keys['ArrowRight'] || keys['KeyD']) { isMoving = true; if(!player.hasBlasterEquipped) player.facingRight = true; }
if (keys['ArrowLeft'] || keys['KeyA']) { isMoving = true; if(!player.hasBlasterEquipped) player.facingRight = false; }
if (isMoving && player.grounded) player.moveFrames++;
else if (!isMoving) player.moveFrames = 0;
let currentMaxSpeed = player.speed;
let currentAccel = 0.5;
if (player.moveFrames >= 90) {
let movingBackward = (keys['ArrowLeft'] || keys['KeyA']) && player.facingRight ||
(keys['ArrowRight'] || keys['KeyD']) && !player.facingRight;
if (!movingBackward || !player.hasBlasterEquipped) { currentMaxSpeed = player.sprintSpeed; currentAccel = 0.8; }
}
if (keys['ArrowRight'] || keys['KeyD']) { if (player.velX < currentMaxSpeed) player.velX += currentAccel; }
if (keys['ArrowLeft'] || keys['KeyA']) { if (player.velX > -currentMaxSpeed) player.velX -= currentAccel; }
player.velX *= friction; player.velY += gravity;
player.grounded = false; player.touchingWall = null;
player.x += player.velX; player.y += player.velY;
for (let i = 0; i < platforms.length; i++) {
let dir = checkCollision(player, platforms[i]);
if (dir === "l") { player.velX = 0; player.touchingWall = 'left'; }
else if (dir === "r") { player.velX = 0; player.touchingWall = 'right'; }
else if (dir === "b") { player.grounded = true; player.velY = 0; }
else if (dir === "t") { player.velY *= -1; }
}
// --- ENEMIES ---
for (let i = enemies.length - 1; i >= 0; i--) {
let e = enemies[i];
if (e.type === 'guard') {
e.velY += gravity;
e.y += e.velY;
e.x += e.velX;
e.grounded = false;
for (let j = 0; j < platforms.length; j++) {
let dir = checkCollision(e, platforms[j]);
if (dir === "b") { e.grounded = true; e.velY = 0; }
else if (dir === "l" || dir === "r") { e.velX *= -1; }
}
} else if (e.type === 'drone') {
e.hoverTimer++;
e.y = e.startY + Math.sin(e.hoverTimer * 0.05) * 25; // Sine wave hovering
e.x += e.velX;
}
let distToPlayer = Math.hypot((player.x - e.x), (player.y - e.y));
if (distToPlayer < 450) {
e.velX = 0;
e.state = 'aiming';
e.facingRight = player.x > e.x;
e.aimAngle = Math.atan2((player.y + player.height/2) - (e.y + e.height/2), (player.x + player.width/2) - (e.x + e.width/2));
if (e.cooldown <= 0) {
let projVel = e.type === 'drone' ? 8 : 12; // Drones shoot slightly slower projectiles
projectiles.push({
x: e.x + e.width/2 + Math.cos(e.aimAngle) * 20,
y: e.y + e.height/2 + Math.sin(e.aimAngle) * 20,
velX: Math.cos(e.aimAngle) * projVel,
velY: Math.sin(e.aimAngle) * projVel,
life: 80,
owner: 'enemy',
color: '#ff3333' // Now red theme
});
e.cooldown = e.type === 'drone' ? 120 : 100; // Drones fire slightly slower
}
} else {
e.state = e.type === 'drone' ? 'patrol' : 'idle';
}
if (e.cooldown > 0) e.cooldown--;
if (e.y > 800) enemies.splice(i, 1);
}
// --- PROJECTILES ---
for (let i = projectiles.length - 1; i >= 0; i--) {
let p = projectiles[i];
p.x += p.velX; p.y += p.velY; p.life--;
let hitTarget = false; let hitPlatform = false;
for (let j = 0; j < platforms.length; j++) {
let plat = platforms[j];
if (p.x > plat.x && p.x < plat.x + plat.w && p.y > plat.y && p.y < plat.y + plat.h) { hitPlatform = true; break; }
}
if (!hitPlatform) {
if (p.owner === 'player') {
for (let j = enemies.length - 1; j >= 0; j--) {
let e = enemies[j];
if (p.x > e.x && p.x < e.x + e.width && p.y > e.y && p.y < e.y + e.height) {
e.hp -= 25;
if (e.hp <= 0) {
createParticles(e.x + e.width/2, e.y + e.height/2, e.type);
if (Math.random() < 0.25) items.push(createMedKit(e.x + e.width/2 - 8, e.y + e.height/2 - 8));
enemies.splice(j, 1);
}
hitTarget = true; break;
}
}
} else if (p.owner === 'enemy') {
if (p.x > player.x && p.x < player.x + player.width && p.y > player.y && p.y < player.y + player.height) {
player.hp -= 15; hitTarget = true;
}
}
}
if (p.life <= 0 || hitPlatform || hitTarget) projectiles.splice(i, 1);
}
// Player Animation States
if (player.touchingWall && player.velY > 0 && !player.grounded) {
player.velY *= 0.7; player.state = 'wallSlide';
if(!player.hasBlasterEquipped) player.facingRight = (player.touchingWall === 'left');
} else if (!player.grounded && Math.abs(player.velY) > 0.5) { player.state = 'jumping'; }
else if (player.moveFrames >= 90) { player.state = 'sprinting'; }
else if (Math.abs(player.velX) > 0.5) { player.state = 'running'; }
else { player.state = 'idle'; }
if (player.x + 1600 > nextChunkX) { generateRoom(); cleanRooms(); }
distanceScore = Math.max(distanceScore, Math.floor(player.x / 10));
scoreBoard.innerText = `DISTANCE: ${distanceScore}m`;
camera.x = Math.max(0, player.x - canvas.width / 2 + player.width / 2);
}
// --- DRAWING FUNCTIONS ---
function drawBackgroundWall() {
ctx.strokeStyle = '#151520'; ctx.lineWidth = 2;
const startX = camera.x - (camera.x % 100);
for(let i = startX; i < camera.x + canvas.width; i += 100) { ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height); ctx.stroke(); }
for(let i = 0; i < canvas.height; i += 100) { ctx.beginPath(); ctx.moveTo(camera.x, i); ctx.lineTo(camera.x + canvas.width, i); ctx.stroke(); }
}
function drawScenery() {
const time = Date.now();
backgroundProps.forEach(prop => {
if (prop.x + prop.w > camera.x && prop.x < camera.x + canvas.width) {
if (prop.type === 'window') {
ctx.fillStyle = '#4a4e69'; ctx.fillRect(prop.x - 10, prop.y - 10, prop.w + 20, prop.h + 20);
let grad = ctx.createLinearGradient(prop.x, prop.y, prop.x, prop.y + prop.h);
grad.addColorStop(0, "#050510"); grad.addColorStop(1, "#1a1a3a");
ctx.fillStyle = grad; ctx.fillRect(prop.x, prop.y, prop.w, prop.h);
ctx.fillStyle = '#fff';
for(let i=0; i<15; i++) { ctx.fillRect(prop.x + ((prop.x + i * 47) % prop.w), prop.y + ((prop.x + i * 31) % prop.h), 2, 2); }
}
else if (prop.type === 'terminal') {
ctx.fillStyle = '#333'; ctx.fillRect(prop.x, prop.y + 20, prop.w, prop.h - 20);
ctx.fillStyle = '#222'; ctx.fillRect(prop.x + 10, prop.y, prop.w - 20, 20);
ctx.shadowBlur = 10; ctx.shadowColor = "#00ff41"; ctx.fillStyle = "#00ff41";
ctx.fillRect(prop.x + 15, prop.y + 5, prop.w - 30, 10); ctx.shadowBlur = 0;
}
else if (prop.type === 'table') {
ctx.fillStyle = '#666'; ctx.fillRect(prop.x + 40, prop.y + 10, 40, 5); ctx.fillRect(prop.x + 55, prop.y + 15, 10, 25);
ctx.fillStyle = '#888'; ctx.fillRect(prop.x + 10, prop.y + 25, 15, 15); ctx.fillRect(prop.x + 95, prop.y + 25, 15, 15);
}
else if (prop.type === 'pipes') {
ctx.strokeStyle = '#2b2b36'; ctx.lineWidth = 15; ctx.beginPath();
ctx.moveTo(prop.x, prop.y + 30); ctx.lineTo(prop.x + prop.w, prop.y + 30);
ctx.moveTo(prop.x, prop.y + 70); ctx.lineTo(prop.x + prop.w, prop.y + 70); ctx.stroke();
}
else if (prop.type === 'danger') {
ctx.fillStyle = '#e9c46a'; ctx.fillRect(prop.x, prop.y, prop.w, prop.h); ctx.fillStyle = '#222';
for(let i=0; i<prop.w; i+=30) {
ctx.beginPath(); ctx.moveTo(prop.x + i, prop.y); ctx.lineTo(prop.x + i + 15, prop.y + prop.h);
ctx.lineTo(prop.x + i + 5, prop.y + prop.h); ctx.lineTo(prop.x + i - 10, prop.y); ctx.fill();
}
}
else if (prop.type === 'serverRack') {
ctx.fillStyle = '#111'; ctx.fillRect(prop.x, prop.y, prop.w, prop.h);
ctx.strokeStyle = '#222'; ctx.lineWidth = 4; ctx.strokeRect(prop.x, prop.y, prop.w, prop.h);
for (let r = 10; r < prop.h - 15; r += 20) {
ctx.fillStyle = '#0a0a0a'; ctx.fillRect(prop.x + 5, prop.y + r, prop.w - 10, 14);
for (let c = 10; c < prop.w - 15; c += 15) {
if (Math.sin(time / (100 + r * c)) > 0.5) {
ctx.fillStyle = (r % 40 === 10) ? '#00f2ff' : '#00ff41';
ctx.fillRect(prop.x + 5 + c, prop.y + r + 4, 6, 6);
}
}
}
}
else if (prop.type === 'vent') {
ctx.fillStyle = '#151515'; ctx.fillRect(prop.x, prop.y, prop.w, prop.h);
ctx.strokeStyle = '#333'; ctx.lineWidth = 4; ctx.strokeRect(prop.x, prop.y, prop.w, prop.h);
ctx.strokeStyle = '#050505'; ctx.lineWidth = 3;
for(let i = 12; i < prop.h - 5; i += 12) { ctx.beginPath(); ctx.moveTo(prop.x + 5, prop.y + i); ctx.lineTo(prop.x + prop.w - 5, prop.y + i); ctx.stroke(); }
}
else if (prop.type === 'wires') {
ctx.strokeStyle = '#111'; ctx.lineWidth = 4;
ctx.beginPath(); ctx.moveTo(prop.x, prop.y); ctx.quadraticCurveTo(prop.x + prop.w/2, prop.y + prop.h, prop.x + prop.w, prop.y); ctx.stroke();
ctx.strokeStyle = '#1a1a24'; ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(prop.x + 30, prop.y); ctx.quadraticCurveTo(prop.x + prop.w/2 + 20, prop.y + prop.h + 30, prop.x + prop.w - 20, prop.y); ctx.stroke();
}
else if (prop.type === 'cautionStripe') {
ctx.fillStyle = '#111'; ctx.fillRect(prop.x, prop.y, prop.w, prop.h); ctx.fillStyle = '#d4a34b';
for(let i = 0; i < prop.w; i += 40) {
ctx.beginPath(); ctx.moveTo(prop.x + i, prop.y); ctx.lineTo(prop.x + i + 20, prop.y);
ctx.lineTo(prop.x + i + 10, prop.y + prop.h); ctx.lineTo(prop.x + i - 10, prop.y + prop.h); ctx.fill();
}
}
else if (prop.type === 'crates') {
ctx.fillStyle = '#3e2723'; ctx.fillRect(prop.x, prop.y, prop.w, prop.h);
ctx.strokeStyle = '#271714'; ctx.lineWidth = 4; ctx.strokeRect(prop.x, prop.y, prop.w, prop.h);
ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(prop.x, prop.y); ctx.lineTo(prop.x + prop.w, prop.y + prop.h); ctx.stroke();
ctx.beginPath(); ctx.moveTo(prop.x + prop.w, prop.y); ctx.lineTo(prop.x, prop.y + prop.h); ctx.stroke();
}
}
});
}
function drawItems() {
const time = Date.now() / 200;
items.forEach(item => {
if (item.x + item.width > camera.x && item.x < camera.x + canvas.width) {
const hoverOffset = item.grounded ? Math.sin(time + item.x) * 3 : 0;
ctx.save(); ctx.translate(item.x, item.y + hoverOffset);
if (item.type === 'medkit') {
ctx.fillStyle = '#eeeeee'; ctx.fillRect(0, 0, item.width, item.height);
ctx.strokeStyle = '#999999'; ctx.lineWidth = 1; ctx.strokeRect(0, 0, item.width, item.height);
ctx.fillStyle = '#e63946'; ctx.fillRect(item.width/2 - 2, 3, 4, item.height - 6); ctx.fillRect(3, item.height/2 - 2, item.width - 6, 4);
ctx.shadowBlur = 15; ctx.shadowColor = '#00ff41'; ctx.fillStyle = 'rgba(0, 255, 65, 0.15)';
ctx.fillRect(-5, -5, item.width + 10, item.height + 10);
}
ctx.restore();
}
});
}
function drawArmoredLimb(ctx, x, y, angle, length, suitColor, armorColor, isBlasterArm = false, glowColor = "#00f2ff") {
ctx.save(); ctx.translate(x, y); ctx.rotate(angle);
ctx.strokeStyle = suitColor; ctx.lineWidth = 8; ctx.lineCap = 'round';
ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(0, length); ctx.stroke();
ctx.fillStyle = armorColor; ctx.fillRect(-4, 4, 8, length - 8);
if (isBlasterArm) {
ctx.fillStyle = "#111"; ctx.fillRect(-5, length - 2, 10, 14);
ctx.fillStyle = "#4a4e69"; ctx.fillRect(-6, length, 2, 10);
ctx.fillStyle = "#222"; ctx.fillRect(-3, length + 12, 6, 8);
ctx.fillStyle = "#333"; ctx.fillRect(-4, length + 20, 8, 5);
ctx.shadowBlur = 10; ctx.shadowColor = glowColor; ctx.fillStyle = glowColor; ctx.fillRect(-2, length + 21, 4, 4); ctx.shadowBlur = 0;
}
ctx.restore();
}
function drawAgent() {
ctx.save();
const pCenterX = player.x + player.width / 2; const pCenterY = player.y + player.height / 2;
ctx.translate(pCenterX, pCenterY); if (!player.facingRight) ctx.scale(-1, 1);
const time = Date.now() / 1000;
let legAngle1 = 0, legAngle2 = 0, armAngle1 = 0, armAngle2 = 0, torsoOffset = 0, bodyTilt = 0;
if (player.state === 'sprinting') {
const sprintSpeed = 13;
legAngle1 = Math.sin(time * sprintSpeed) * 1.3; legAngle2 = Math.sin(time * sprintSpeed + Math.PI) * 1.3;
armAngle1 = Math.sin(time * sprintSpeed + Math.PI) * 1.3; armAngle2 = Math.sin(time * sprintSpeed) * 1.3;
torsoOffset = Math.abs(Math.sin(time * sprintSpeed)) * 4 + 2; bodyTilt = 0.35;
} else if (player.state === 'running') {
const runSpeed = 8;
legAngle1 = Math.sin(time * runSpeed) * 0.8; legAngle2 = Math.sin(time * runSpeed + Math.PI) * 0.8;
armAngle1 = Math.sin(time * runSpeed + Math.PI) * 0.8; armAngle2 = Math.sin(time * runSpeed) * 0.8;
torsoOffset = Math.abs(Math.sin(time * runSpeed)) * 3;
} else if (player.state === 'jumping') { legAngle1 = -0.6; legAngle2 = 0.4; armAngle1 = 1.5; armAngle2 = 2.2;
} else if (player.state === 'wallSlide') { armAngle1 = -1.5; armAngle2 = -1.5; legAngle1 = 0.5; legAngle2 = -0.5;
} else { torsoOffset = Math.sin(time * 2) * 2; armAngle1 = 0.1; armAngle2 = -0.1; }
if (player.hasBlasterEquipped) {
bodyTilt = 0;
const targetX = mouseX + camera.x;
let rawAim = Math.atan2(mouseY - pCenterY, targetX - pCenterX);
if (!player.facingRight) rawAim = Math.atan2(mouseY - pCenterY, -(targetX - pCenterX));
armAngle1 = rawAim - Math.PI / 2;
}
ctx.rotate(bodyTilt);
if ((player.state === 'jumping' || player.state === 'wallSlide') && player.velY < 0) {
ctx.shadowBlur = 15; ctx.shadowColor = "#00f2ff"; ctx.fillStyle = "#00f2ff";
ctx.beginPath(); ctx.moveTo(-10, torsoOffset - 5); ctx.lineTo(-20, torsoOffset + 15); ctx.lineTo(-5, torsoOffset + 15); ctx.fill(); ctx.shadowBlur = 0;
} else if (player.state === 'sprinting' && player.grounded) {
ctx.fillStyle = "rgba(0, 242, 255, 0.5)"; ctx.beginPath(); ctx.moveTo(-14, torsoOffset); ctx.lineTo(-30, torsoOffset + 10); ctx.lineTo(-14, torsoOffset + 15); ctx.fill();
}
drawArmoredLimb(ctx, 0, torsoOffset - 6, armAngle2, 16, '#222', '#333');
drawArmoredLimb(ctx, 0, torsoOffset + 8, legAngle2, 18, '#222', '#333');
ctx.fillStyle = '#444'; ctx.fillRect(-14, torsoOffset - 12, 8, 20); ctx.fillStyle = '#00f2ff'; ctx.fillRect(-15, torsoOffset - 8, 2, 8);
ctx.fillStyle = "#222"; ctx.beginPath(); ctx.roundRect ? ctx.roundRect(-8, torsoOffset - 12, 16, 24, 4) : ctx.fillRect(-8, torsoOffset - 12, 16, 24); ctx.fill();
ctx.fillStyle = "#e9c46a"; ctx.fillRect(-5, torsoOffset - 8, 12, 10);
ctx.fillStyle = "#4a4e69"; ctx.fillRect(-9, torsoOffset + 8, 18, 5);
ctx.fillStyle = '#111'; ctx.beginPath(); ctx.arc(0, torsoOffset - 18, 11, 0, Math.PI * 2); ctx.fill();
ctx.shadowBlur = 8; ctx.shadowColor = '#00f2ff'; ctx.fillStyle = '#00f2ff';
ctx.beginPath(); ctx.roundRect ? ctx.roundRect(0, torsoOffset - 22, 10, 7, 2) : ctx.fillRect(0, torsoOffset - 22, 10, 7); ctx.fill(); ctx.shadowBlur = 0;
drawArmoredLimb(ctx, 0, torsoOffset - 6, armAngle1, 16, '#333', '#5c5c6b', player.hasBlasterEquipped, "#00f2ff");
drawArmoredLimb(ctx, 0, torsoOffset + 8, legAngle1, 18, '#333', '#5c5c6b');
ctx.restore();
}
function drawEnemies() {
const time = Date.now() / 1000;
enemies.forEach(e => {
ctx.save();
ctx.translate(e.x + e.width / 2, e.y + e.height / 2);
if (e.type === 'guard') {
if (!e.facingRight) ctx.scale(-1, 1);
let armAngle = 0, torsoOffset = Math.sin(time * 2 + e.x) * 2;
if (e.state === 'aiming') {
let rawAim = e.aimAngle;
if (!e.facingRight) rawAim = Math.atan2(Math.sin(e.aimAngle), -Math.cos(e.aimAngle));
armAngle = rawAim - Math.PI / 2;
} else { armAngle = 0.2; }
drawArmoredLimb(ctx, 0, torsoOffset - 6, -0.2, 16, '#111', '#222');
drawArmoredLimb(ctx, 0, torsoOffset + 8, 0, 18, '#111', '#222');
ctx.fillStyle = "#222"; ctx.beginPath(); ctx.roundRect ? ctx.roundRect(-8, torsoOffset - 12, 16, 24, 4) : ctx.fillRect(-8, torsoOffset - 12, 16, 24); ctx.fill();
ctx.fillStyle = "#aa0000"; ctx.fillRect(-5, torsoOffset - 8, 12, 10);
ctx.fillStyle = '#111'; ctx.beginPath(); ctx.arc(0, torsoOffset - 18, 11, 0, Math.PI * 2); ctx.fill();
ctx.shadowBlur = 8; ctx.shadowColor = '#ff3333'; ctx.fillStyle = '#ff3333';
ctx.beginPath(); ctx.roundRect ? ctx.roundRect(0, torsoOffset - 22, 10, 7, 2) : ctx.fillRect(0, torsoOffset - 22, 10, 7); ctx.fill(); ctx.shadowBlur = 0;
drawArmoredLimb(ctx, 0, torsoOffset - 6, armAngle, 16, '#222', '#444', true, "#ff3333");
drawArmoredLimb(ctx, 0, torsoOffset + 8, 0.2, 18, '#222', '#444');
} else if (e.type === 'drone') {
// Drone Body
ctx.fillStyle = '#2b2d42';
ctx.beginPath();
ctx.moveTo(-12, 0); ctx.lineTo(0, -10); ctx.lineTo(12, 0); ctx.lineTo(8, 10); ctx.lineTo(-8, 10);
ctx.fill();
// Drone details
ctx.strokeStyle = '#111'; ctx.lineWidth = 2; ctx.stroke();
// Propeller Mount
ctx.fillStyle = '#222';
ctx.fillRect(-2, -14, 4, 4);
// Spinning Propeller Animation
ctx.save();
ctx.translate(0, -14);
ctx.rotate(time * 25); // Fast spin
ctx.fillStyle = '#888';
ctx.fillRect(-14, -2, 28, 4); // Main blade
ctx.fillStyle = '#ccc';
ctx.fillRect(-14, -1, 28, 2); // Highlight
ctx.restore();
// Drone Eye (Now Red)
let lookOffsetX = 0;
let lookOffsetY = 0;
if (e.state === 'aiming') {
lookOffsetX = Math.cos(e.aimAngle) * 3;
lookOffsetY = Math.sin(e.aimAngle) * 3;
}
ctx.fillStyle = '#111';
ctx.beginPath(); ctx.arc(0, 0, 6, 0, Math.PI * 2); ctx.fill();
ctx.shadowBlur = 10; ctx.shadowColor = '#ff3333'; ctx.fillStyle = '#ff3333';
ctx.beginPath(); ctx.arc(lookOffsetX, lookOffsetY, 3, 0, Math.PI * 2); ctx.fill();
ctx.shadowBlur = 0;
}
ctx.restore();
// HP Bar
let hpColor = '#ff3333'; // Now red to match the guard
ctx.fillStyle = '#333'; ctx.fillRect(e.x + e.width/2 - 20, e.y - 12, 40, 6);
ctx.fillStyle = hpColor; ctx.fillRect(e.x + e.width/2 - 20, e.y - 12, 40 * (e.hp / e.maxHp), 6);
});
}
function drawParticles() {
particles.forEach(p => {
ctx.save(); ctx.translate(p.x, p.y); ctx.rotate(p.rot); ctx.globalAlpha = Math.max(0, p.life);
ctx.fillStyle = p.color; ctx.fillRect(-p.size/2, -p.size/2, p.size, p.size); ctx.restore();
});
}
function drawUI() {
if (gameState !== 'PLAYING' && gameState !== 'DYING') return;
ctx.fillStyle = 'rgba(0,0,0,0.6)'; ctx.fillRect(20, 20, 204, 24);
ctx.fillStyle = '#e63946'; ctx.fillRect(22, 22, 200, 20);
ctx.fillStyle = '#00f2ff'; ctx.fillRect(22, 22, 200 * (Math.max(0, player.hp) / player.maxHp), 20);
ctx.fillStyle = '#fff'; ctx.font = "14px Courier"; ctx.textAlign = "left";
ctx.fillText(`INTEGRITY: ${Math.max(0, player.hp)}%`, 25, 37);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (gameState === 'MENU') {
ctx.fillStyle = "#2a9d8f"; ctx.font = "bold 70px Courier New"; ctx.textAlign = "center"; ctx.fillText("AGENT_007", canvas.width/2, 280);
ctx.font = "20px Courier New"; ctx.fillStyle = "white"; ctx.fillText("INTERIOR ESCAPE", canvas.width/2, 320);
if (Math.floor(Date.now() / 500) % 2) ctx.fillText("[ PRESS ENTER TO START ]", canvas.width/2, 400); return;
}
ctx.save(); ctx.translate(-camera.x, 0);
drawBackgroundWall(); drawScenery();
platforms.forEach(p => {
if (p.x + p.w > camera.x && p.x < camera.x + canvas.width) {
ctx.fillStyle = "#444"; ctx.fillRect(p.x, p.y, p.w, p.h);
ctx.fillStyle = "#666"; ctx.fillRect(p.x, p.y, p.w, 4);
ctx.strokeStyle = "#1a1a24"; ctx.lineWidth = 2; ctx.strokeRect(p.x, p.y, p.w, p.h);
}
});
projectiles.forEach(p => {
ctx.shadowBlur = 10; ctx.lineCap = "round"; ctx.lineWidth = 3;
ctx.shadowColor = p.color || "#00f2ff"; ctx.strokeStyle = p.color || "#00f2ff";
ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(p.x - p.velX * 1.5, p.y - p.velY * 1.5); ctx.stroke();
});
ctx.shadowBlur = 0;
if (gameState === 'PLAYING' || gameState === 'DYING' || gameState === 'GAMEOVER') {
drawItems(); drawEnemies();
if (gameState === 'PLAYING') drawAgent();
drawParticles();
}
ctx.restore(); drawUI();
if (gameState === 'GAMEOVER') {
ctx.fillStyle = "rgba(0,0,0,0.85)"; ctx.fillRect(0,0,800,600);
ctx.fillStyle = "#e63946"; ctx.font = "bold 50px Courier"; ctx.textAlign = "center"; ctx.fillText("AGENT LOST", 400, 250);
ctx.fillStyle = "#fff"; ctx.font = "20px Courier"; ctx.fillText(`FINAL DISTANCE: ${distanceScore}m`, 400, 310);
if (Math.floor(Date.now() / 500) % 2) { ctx.fillStyle = "#2a9d8f"; ctx.fillText("[ PRESS ENTER TO RESTART ]", 400, 400); }
}
}
function loop() { update(); draw(); requestAnimationFrame(loop); }
loop();
</script>
</body>
</html>