Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Nano Bot Breach - Vascular Maze Navigator</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- body {
- background: linear-gradient(135deg, #0a0012 0%, #1a0033 50%, #0a0012 100%);
- font-family: 'Courier New', monospace;
- display: flex;
- justify-content: center;
- align-items: center;
- min-height: 100vh;
- overflow: hidden;
- position: relative;
- }
- body::before {
- content: '';
- position: absolute;
- width: 200%;
- height: 200%;
- background: radial-gradient(circle, transparent 0%, rgba(255,0,150,0.03) 50%, transparent 100%);
- animation: pulse 8s ease-in-out infinite;
- }
- @keyframes pulse {
- 0%, 100% { transform: scale(1) rotate(0deg); }
- 50% { transform: scale(1.2) rotate(180deg); }
- }
- #gameContainer {
- position: relative;
- border: 2px solid #ff00ff;
- box-shadow:
- 0 0 30px rgba(255,0,255,0.5),
- inset 0 0 30px rgba(0,255,255,0.1);
- background: #000;
- animation: borderGlow 2s ease-in-out infinite alternate;
- }
- @keyframes borderGlow {
- from { box-shadow: 0 0 30px rgba(255,0,255,0.5), inset 0 0 30px rgba(0,255,255,0.1); }
- to { box-shadow: 0 0 50px rgba(0,255,255,0.8), inset 0 0 50px rgba(255,0,255,0.2); }
- }
- canvas {
- display: block;
- image-rendering: crisp-edges;
- }
- #ui {
- position: absolute;
- top: 10px;
- left: 10px;
- color: #00ffff;
- text-shadow: 0 0 10px currentColor;
- font-size: 14px;
- user-select: none;
- z-index: 10;
- }
- .stat {
- margin: 5px 0;
- display: flex;
- align-items: center;
- gap: 10px;
- background: rgba(0,0,0,0.7);
- padding: 5px 10px;
- border: 1px solid rgba(0,255,255,0.3);
- border-radius: 3px;
- }
- .stat-label {
- color: #ff00ff;
- min-width: 100px;
- }
- .stat-value {
- color: #00ff00;
- font-weight: bold;
- }
- .bar {
- width: 100px;
- height: 8px;
- background: rgba(255,255,255,0.1);
- border: 1px solid rgba(0,255,255,0.5);
- position: relative;
- overflow: hidden;
- }
- .bar-fill {
- height: 100%;
- background: linear-gradient(90deg, #00ff00, #00ffff);
- transition: width 0.3s ease;
- box-shadow: 0 0 10px currentColor;
- }
- #controls {
- position: absolute;
- bottom: 10px;
- left: 50%;
- transform: translateX(-50%);
- display: flex;
- gap: 10px;
- z-index: 10;
- }
- button {
- padding: 10px 20px;
- background: linear-gradient(135deg, #ff00ff, #00ffff);
- border: none;
- color: white;
- font-weight: bold;
- cursor: pointer;
- border-radius: 5px;
- text-transform: uppercase;
- letter-spacing: 1px;
- transition: all 0.3s ease;
- box-shadow: 0 0 20px rgba(255,0,255,0.5);
- }
- button:hover {
- transform: translateY(-2px);
- box-shadow: 0 5px 30px rgba(0,255,255,0.7);
- }
- button:active {
- transform: translateY(0);
- }
- button:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- }
- #gameOver {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: rgba(0,0,0,0.9);
- padding: 30px;
- border: 2px solid #ff00ff;
- border-radius: 10px;
- text-align: center;
- display: none;
- z-index: 100;
- box-shadow: 0 0 50px rgba(255,0,255,0.8);
- }
- #gameOver h2 {
- color: #ff00ff;
- margin-bottom: 20px;
- text-shadow: 0 0 20px currentColor;
- animation: textPulse 1s ease-in-out infinite;
- }
- @keyframes textPulse {
- 0%, 100% { opacity: 1; }
- 50% { opacity: 0.7; }
- }
- #gameOver p {
- color: #00ffff;
- margin: 10px 0;
- }
- .particle {
- position: absolute;
- pointer-events: none;
- width: 2px;
- height: 2px;
- background: radial-gradient(circle, #00ffff, transparent);
- animation: particleFloat 2s ease-out forwards;
- }
- @keyframes particleFloat {
- 0% {
- opacity: 1;
- transform: translate(0, 0) scale(1);
- }
- 100% {
- opacity: 0;
- transform: translate(var(--dx), var(--dy)) scale(0);
- }
- }
- </style>
- </head>
- <body>
- <div id="gameContainer">
- <canvas id="gameCanvas"></canvas>
- <div id="ui">
- <div class="stat">
- <span class="stat-label">NANOBOTS:</span>
- <span class="stat-value" id="botCount">0</span>
- </div>
- <div class="stat">
- <span class="stat-label">ENERGY:</span>
- <div class="bar">
- <div class="bar-fill" id="energyBar"></div>
- </div>
- </div>
- <div class="stat">
- <span class="stat-label">WAVE:</span>
- <span class="stat-value" id="waveCount">1</span>
- </div>
- <div class="stat">
- <span class="stat-label">THREATS:</span>
- <span class="stat-value" id="threatCount">0</span>
- </div>
- <div class="stat">
- <span class="stat-label">CONTROLS:</span>
- <span style="color: #00ffff; font-size: 11px;">↑↓←→ or WASD to move</span>
- </div>
- </div>
- <div id="controls">
- <button id="splitBtn">SPLIT [SPACE]</button>
- <button id="mergeBtn">MERGE [M]</button>
- <button id="boostBtn">BOOST [B]</button>
- </div>
- <div id="gameOver">
- <h2>MISSION COMPLETE</h2>
- <p>Wave Reached: <span id="finalWave">0</span></p>
- <p>Nanobots Saved: <span id="finalBots">0</span></p>
- <button onclick="location.reload()">RESTART MISSION</button>
- </div>
- </div>
- <script>
- const canvas = document.getElementById('gameCanvas');
- const ctx = canvas.getContext('2d');
- const gameContainer = document.getElementById('gameContainer');
- // Game dimensions
- const CELL_SIZE = 20;
- const GRID_WIDTH = 40;
- const GRID_HEIGHT = 30;
- canvas.width = GRID_WIDTH * CELL_SIZE;
- canvas.height = GRID_HEIGHT * CELL_SIZE;
- gameContainer.style.width = canvas.width + 'px';
- gameContainer.style.height = canvas.height + 'px';
- // Game state
- const game = {
- grid: [],
- nanobots: [],
- immuneCells: [],
- obstacles: [],
- particles: [],
- energy: 100,
- maxEnergy: 100,
- wave: 1,
- target: null,
- selectedBots: [],
- mouseX: 0,
- mouseY: 0,
- splitCooldown: 0,
- mergeCooldown: 0,
- boostActive: false,
- boostCooldown: 0,
- keys: {
- up: false,
- down: false,
- left: false,
- right: false,
- w: false,
- a: false,
- s: false,
- d: false
- }
- };
- // Maze generation
- class MazeGenerator {
- static generate() {
- const grid = Array(GRID_HEIGHT).fill().map(() => Array(GRID_WIDTH).fill(0));
- // Create walls
- for (let y = 0; y < GRID_HEIGHT; y++) {
- for (let x = 0; x < GRID_WIDTH; x++) {
- if (x === 0 || x === GRID_WIDTH - 1 || y === 0 || y === GRID_HEIGHT - 1) {
- grid[y][x] = 1;
- }
- }
- }
- // Add random obstacles (vascular walls)
- const obstacleCount = 15 + game.wave * 2;
- for (let i = 0; i < obstacleCount; i++) {
- const x = Math.floor(Math.random() * (GRID_WIDTH - 4)) + 2;
- const y = Math.floor(Math.random() * (GRID_HEIGHT - 4)) + 2;
- const width = Math.floor(Math.random() * 3) + 2;
- const height = Math.floor(Math.random() * 3) + 2;
- for (let dy = 0; dy < height; dy++) {
- for (let dx = 0; dx < width; dx++) {
- if (x + dx < GRID_WIDTH - 1 && y + dy < GRID_HEIGHT - 1) {
- grid[y + dy][x + dx] = 1;
- }
- }
- }
- }
- // Ensure path exists
- grid[1][1] = 0;
- grid[GRID_HEIGHT - 2][GRID_WIDTH - 2] = 0;
- return grid;
- }
- }
- // A* Pathfinding
- class AStar {
- static findPath(start, end, grid) {
- const openSet = [start];
- const closedSet = new Set();
- const cameFrom = new Map();
- const gScore = new Map();
- const fScore = new Map();
- const key = (pos) => `${pos.x},${pos.y}`;
- gScore.set(key(start), 0);
- fScore.set(key(start), this.heuristic(start, end));
- while (openSet.length > 0) {
- let current = openSet.reduce((a, b) =>
- (fScore.get(key(a)) || Infinity) < (fScore.get(key(b)) || Infinity) ? a : b
- );
- if (current.x === end.x && current.y === end.y) {
- return this.reconstructPath(cameFrom, current);
- }
- openSet.splice(openSet.indexOf(current), 1);
- closedSet.add(key(current));
- const neighbors = this.getNeighbors(current, grid);
- for (const neighbor of neighbors) {
- const neighborKey = key(neighbor);
- if (closedSet.has(neighborKey)) continue;
- const tentativeGScore = (gScore.get(key(current)) || Infinity) + 1;
- if (!openSet.some(n => n.x === neighbor.x && n.y === neighbor.y)) {
- openSet.push(neighbor);
- } else if (tentativeGScore >= (gScore.get(neighborKey) || Infinity)) {
- continue;
- }
- cameFrom.set(neighborKey, current);
- gScore.set(neighborKey, tentativeGScore);
- fScore.set(neighborKey, tentativeGScore + this.heuristic(neighbor, end));
- }
- }
- return [];
- }
- static heuristic(a, b) {
- return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
- }
- static getNeighbors(pos, grid) {
- const neighbors = [];
- const dirs = [[0, -1], [1, 0], [0, 1], [-1, 0]];
- for (const [dx, dy] of dirs) {
- const x = pos.x + dx;
- const y = pos.y + dy;
- if (x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT && grid[y][x] === 0) {
- neighbors.push({x, y});
- }
- }
- return neighbors;
- }
- static reconstructPath(cameFrom, current) {
- const path = [current];
- const key = (pos) => `${pos.x},${pos.y}`;
- while (cameFrom.has(key(current))) {
- current = cameFrom.get(key(current));
- path.unshift(current);
- }
- return path;
- }
- }
- // Nanobot class
- class Nanobot {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.vx = 0;
- this.vy = 0;
- this.targetX = x;
- this.targetY = y;
- this.path = [];
- this.speed = 0.15;
- this.size = 3 + Math.random() * 2;
- this.energy = 50;
- this.health = 100;
- this.selected = false;
- this.merging = false;
- this.angle = Math.random() * Math.PI * 2;
- this.wobble = Math.random() * Math.PI * 2;
- this.attackCooldown = 0;
- }
- update() {
- this.wobble += 0.1;
- if (game.boostActive) {
- this.speed = 0.3;
- } else {
- this.speed = 0.15;
- }
- // Apply keyboard movement if selected or if no selection
- if (this.selected || game.selectedBots.length === 0) {
- this.vx *= 0.9; // Friction
- this.vy *= 0.9;
- if (game.keys.up || game.keys.w) this.vy -= this.speed * 0.1;
- if (game.keys.down || game.keys.s) this.vy += this.speed * 0.1;
- if (game.keys.left || game.keys.a) this.vx -= this.speed * 0.1;
- if (game.keys.right || game.keys.d) this.vx += this.speed * 0.1;
- // Apply velocity
- const nextX = this.x + this.vx;
- const nextY = this.y + this.vy;
- // Check collision with walls
- if (nextX >= 0 && nextX < GRID_WIDTH && nextY >= 0 && nextY < GRID_HEIGHT) {
- if (game.grid[Math.floor(nextY)][Math.floor(nextX)] === 0) {
- this.x = nextX;
- this.y = nextY;
- } else {
- this.vx = 0;
- this.vy = 0;
- }
- }
- }
- // Follow path if set
- if (this.path.length > 0) {
- const target = this.path[0];
- const dx = target.x - this.x;
- const dy = target.y - this.y;
- const dist = Math.sqrt(dx * dx + dy * dy);
- if (dist < 0.5) {
- this.path.shift();
- } else {
- this.x += (dx / dist) * this.speed;
- this.y += (dy / dist) * this.speed;
- this.angle = Math.atan2(dy, dx);
- }
- }
- // Swarm behavior
- let avgX = 0, avgY = 0, count = 0;
- for (const bot of game.nanobots) {
- if (bot !== this) {
- const dx = bot.x - this.x;
- const dy = bot.y - this.y;
- const dist = Math.sqrt(dx * dx + dy * dy);
- if (dist < 2 && dist > 0) {
- // Separation
- this.x -= (dx / dist) * 0.02;
- this.y -= (dy / dist) * 0.02;
- } else if (dist < 4) {
- // Cohesion
- avgX += bot.x;
- avgY += bot.y;
- count++;
- }
- }
- }
- if (count > 0) {
- avgX /= count;
- avgY /= count;
- this.x += (avgX - this.x) * 0.005;
- this.y += (avgY - this.y) * 0.005;
- }
- // Attack nearby immune cells
- if (this.attackCooldown > 0) this.attackCooldown--;
- for (let i = game.immuneCells.length - 1; i >= 0; i--) {
- const immune = game.immuneCells[i];
- const dx = immune.x - this.x;
- const dy = immune.y - this.y;
- const dist = Math.sqrt(dx * dx + dy * dy);
- // Attack if close enough
- if (dist < 2 && this.attackCooldown === 0) {
- immune.health -= 20;
- this.attackCooldown = 30;
- createParticles(immune.x * CELL_SIZE, immune.y * CELL_SIZE, '#00ff00');
- if (immune.health <= 0) {
- game.immuneCells.splice(i, 1);
- this.energy = Math.min(100, this.energy + 20);
- createParticles(immune.x * CELL_SIZE, immune.y * CELL_SIZE, '#ffff00');
- }
- }
- }
- // Stay within bounds
- this.x = Math.max(0.5, Math.min(GRID_WIDTH - 0.5, this.x));
- this.y = Math.max(0.5, Math.min(GRID_HEIGHT - 0.5, this.y));
- // Check health
- if (this.health <= 0) {
- createParticles(this.x * CELL_SIZE, this.y * CELL_SIZE, '#ff0000');
- return false;
- }
- // Regenerate energy and health slowly
- this.energy = Math.min(100, this.energy + 0.05);
- this.health = Math.min(100, this.health + 0.02);
- return true;
- }
- setTarget(x, y) {
- const start = {x: Math.floor(this.x), y: Math.floor(this.y)};
- const end = {x: Math.floor(x), y: Math.floor(y)};
- this.path = AStar.findPath(start, end, game.grid);
- }
- draw() {
- const screenX = this.x * CELL_SIZE;
- const screenY = this.y * CELL_SIZE;
- // Save context state
- ctx.save();
- // Glow effect
- ctx.shadowBlur = 20;
- ctx.shadowColor = this.selected ? '#ffff00' : '#00ffff';
- // Draw nanobot core
- const healthRatio = this.health / 100;
- ctx.fillStyle = this.selected ? '#ffff00' : `rgb(0, ${Math.floor(255 * healthRatio)}, 0)`;
- ctx.beginPath();
- ctx.arc(screenX, screenY, this.size, 0, Math.PI * 2);
- ctx.fill();
- // Inner glow
- ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
- ctx.beginPath();
- ctx.arc(screenX, screenY, this.size * 0.3, 0, Math.PI * 2);
- ctx.fill();
- // Health bar
- if (this.health < 100) {
- ctx.shadowBlur = 0;
- ctx.fillStyle = 'rgba(255, 0, 0, 0.3)';
- ctx.fillRect(screenX - 10, screenY - 12, 20, 3);
- ctx.fillStyle = `rgb(${Math.floor(255 * (1 - healthRatio))}, ${Math.floor(255 * healthRatio)}, 0)`;
- ctx.fillRect(screenX - 10, screenY - 12, 20 * healthRatio, 3);
- }
- // Energy indicator ring
- ctx.shadowBlur = 0;
- ctx.strokeStyle = `rgba(255, 255, 255, ${this.energy / 100})`;
- ctx.lineWidth = 1;
- ctx.beginPath();
- ctx.arc(screenX, screenY, this.size + 3 + Math.sin(this.wobble) * 2, 0, Math.PI * 2);
- ctx.stroke();
- // Attack indicator
- if (this.attackCooldown > 0) {
- ctx.strokeStyle = '#00ff00';
- ctx.lineWidth = 2;
- ctx.beginPath();
- ctx.arc(screenX, screenY, this.size + 6, 0, (1 - this.attackCooldown / 30) * Math.PI * 2);
- ctx.stroke();
- }
- // Restore context state
- ctx.restore();
- }
- }
- // Immune Cell class
- class ImmuneCell {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.targetX = x;
- this.targetY = y;
- this.speed = 0.02 + game.wave * 0.005;
- this.detectionRange = 3 + game.wave * 0.5;
- this.angle = 0;
- this.patrolPath = this.generatePatrolPath();
- this.patrolIndex = 0;
- this.learningFactor = 1;
- this.health = 100;
- this.attackCooldown = 0;
- }
- generatePatrolPath() {
- const path = [];
- const radius = 5 + Math.random() * 5;
- const steps = 8;
- for (let i = 0; i < steps; i++) {
- const angle = (i / steps) * Math.PI * 2;
- const px = this.x + Math.cos(angle) * radius;
- const py = this.y + Math.sin(angle) * radius;
- if (px > 1 && px < GRID_WIDTH - 1 && py > 1 && py < GRID_HEIGHT - 1) {
- path.push({x: px, y: py});
- }
- }
- return path;
- }
- update() {
- this.angle += 0.05;
- if (this.attackCooldown > 0) this.attackCooldown--;
- // Hunt nanobots
- let closestBot = null;
- let closestDist = Infinity;
- for (const bot of game.nanobots) {
- const dx = bot.x - this.x;
- const dy = bot.y - this.y;
- const dist = Math.sqrt(dx * dx + dy * dy);
- if (dist < this.detectionRange && dist < closestDist) {
- closestBot = bot;
- closestDist = dist;
- }
- }
- if (closestBot) {
- // Chase detected nanobot
- const dx = closestBot.x - this.x;
- const dy = closestBot.y - this.y;
- const dist = Math.sqrt(dx * dx + dy * dy);
- this.x += (dx / dist) * this.speed * this.learningFactor;
- this.y += (dy / dist) * this.speed * this.learningFactor;
- // Attack if close enough
- if (dist < 1.5 && this.attackCooldown === 0) {
- closestBot.health -= 15;
- this.attackCooldown = 20;
- createParticles(closestBot.x * CELL_SIZE, closestBot.y * CELL_SIZE, '#ff0000');
- // Learn from successful attacks
- this.learningFactor = Math.min(2, this.learningFactor + 0.05);
- }
- } else {
- // Patrol
- if (this.patrolPath.length > 0) {
- const target = this.patrolPath[this.patrolIndex];
- const dx = target.x - this.x;
- const dy = target.y - this.y;
- const dist = Math.sqrt(dx * dx + dy * dy);
- if (dist < 0.5) {
- this.patrolIndex = (this.patrolIndex + 1) % this.patrolPath.length;
- } else {
- this.x += (dx / dist) * this.speed;
- this.y += (dy / dist) * this.speed;
- }
- }
- }
- // Check walls and stay within bounds
- const gridX = Math.floor(this.x);
- const gridY = Math.floor(this.y);
- if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT) {
- if (game.grid[gridY][gridX] === 1) {
- // Back away from wall
- this.x = Math.max(1, Math.min(GRID_WIDTH - 1, this.x + (Math.random() - 0.5)));
- this.y = Math.max(1, Math.min(GRID_HEIGHT - 1, this.y + (Math.random() - 0.5)));
- }
- }
- // Stay within bounds
- this.x = Math.max(1, Math.min(GRID_WIDTH - 1, this.x));
- this.y = Math.max(1, Math.min(GRID_HEIGHT - 1, this.y));
- return this.health > 0;
- }
- draw() {
- const screenX = this.x * CELL_SIZE;
- const screenY = this.y * CELL_SIZE;
- // Detection range
- ctx.fillStyle = 'rgba(255, 0, 0, 0.1)';
- ctx.beginPath();
- ctx.arc(screenX, screenY, this.detectionRange * CELL_SIZE, 0, Math.PI * 2);
- ctx.fill();
- // Immune cell body
- ctx.shadowBlur = 20;
- ctx.shadowColor = '#ff0000';
- ctx.fillStyle = '#ff4444';
- ctx.beginPath();
- for (let i = 0; i < 6; i++) {
- const angle = this.angle + (i / 6) * Math.PI * 2;
- const r = i % 2 === 0 ? 8 : 5;
- const px = screenX + Math.cos(angle) * r;
- const py = screenY + Math.sin(angle) * r;
- if (i === 0) ctx.moveTo(px, py);
- else ctx.lineTo(px, py);
- }
- ctx.closePath();
- ctx.fill();
- ctx.shadowBlur = 0;
- }
- }
- // Particle effects
- function createParticles(x, y, color) {
- for (let i = 0; i < 10; i++) {
- game.particles.push({
- x: x,
- y: y,
- vx: (Math.random() - 0.5) * 4,
- vy: (Math.random() - 0.5) * 4,
- life: 1,
- color: color || '#00ffff'
- });
- }
- }
- // Initialize game
- function init() {
- game.grid = MazeGenerator.generate();
- game.nanobots = [];
- game.immuneCells = [];
- game.particles = [];
- // Spawn initial nanobots with better visibility
- for (let i = 0; i < 5; i++) {
- const bot = new Nanobot(2 + Math.random() * 2, 2 + Math.random() * 2);
- bot.size = 5; // Ensure minimum size
- game.nanobots.push(bot);
- }
- // Set target
- game.target = {x: GRID_WIDTH - 3, y: GRID_HEIGHT - 3};
- // Spawn immune cells
- const immuneCount = 2 + Math.floor(game.wave / 2);
- for (let i = 0; i < immuneCount; i++) {
- const x = Math.random() * (GRID_WIDTH - 10) + 5;
- const y = Math.random() * (GRID_HEIGHT - 10) + 5;
- game.immuneCells.push(new ImmuneCell(x, y));
- }
- // Debug log to verify nanobots exist
- console.log(`Initialized ${game.nanobots.length} nanobots`);
- updateUI();
- }
- // Update game state
- function update() {
- // Update cooldowns
- if (game.splitCooldown > 0) game.splitCooldown--;
- if (game.mergeCooldown > 0) game.mergeCooldown--;
- if (game.boostCooldown > 0) {
- game.boostCooldown--;
- if (game.boostCooldown === 0) game.boostActive = false;
- }
- // Update nanobots
- game.nanobots = game.nanobots.filter(bot => bot.update());
- // Update immune cells (remove dead ones)
- game.immuneCells = game.immuneCells.filter(immune => immune.update());
- // Update particles
- game.particles = game.particles.filter(p => {
- p.x += p.vx;
- p.y += p.vy;
- p.life -= 0.02;
- p.vx *= 0.98;
- p.vy *= 0.98;
- return p.life > 0;
- });
- // Check win condition - nanobots reach target
- let botsAtTarget = 0;
- for (const bot of game.nanobots) {
- const dx = bot.x - game.target.x;
- const dy = bot.y - game.target.y;
- if (Math.sqrt(dx * dx + dy * dy) < 2) {
- botsAtTarget++;
- }
- }
- if (botsAtTarget >= Math.min(3, game.nanobots.length) && game.nanobots.length > 0) {
- nextWave();
- }
- // Check lose condition
- if (game.nanobots.length === 0) {
- gameOver();
- }
- // Regenerate energy
- game.energy = Math.min(game.maxEnergy, game.energy + 0.1);
- updateUI();
- }
- // Draw game
- function draw() {
- // Clear canvas with solid black
- ctx.fillStyle = '#000000';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- // Draw grid/maze
- ctx.strokeStyle = 'rgba(128, 0, 255, 0.3)';
- ctx.lineWidth = 1;
- for (let y = 0; y <= GRID_HEIGHT; y++) {
- ctx.beginPath();
- ctx.moveTo(0, y * CELL_SIZE);
- ctx.lineTo(canvas.width, y * CELL_SIZE);
- ctx.stroke();
- }
- for (let x = 0; x <= GRID_WIDTH; x++) {
- ctx.beginPath();
- ctx.moveTo(x * CELL_SIZE, 0);
- ctx.lineTo(x * CELL_SIZE, canvas.height);
- ctx.stroke();
- }
- // Draw walls
- ctx.fillStyle = '#440066';
- for (let y = 0; y < GRID_HEIGHT; y++) {
- for (let x = 0; x < GRID_WIDTH; x++) {
- if (game.grid[y][x] === 1) {
- ctx.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
- // Wall glow
- ctx.strokeStyle = 'rgba(255, 0, 255, 0.2)';
- ctx.strokeRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
- }
- }
- }
- // Draw target
- const targetX = game.target.x * CELL_SIZE;
- const targetY = game.target.y * CELL_SIZE;
- const pulse = Math.sin(Date.now() * 0.003) * 10 + 20;
- ctx.save();
- ctx.shadowBlur = 30;
- ctx.shadowColor = '#00ff00';
- ctx.strokeStyle = '#00ff00';
- ctx.lineWidth = 2;
- ctx.beginPath();
- ctx.arc(targetX, targetY, pulse, 0, Math.PI * 2);
- ctx.stroke();
- ctx.beginPath();
- ctx.arc(targetX, targetY, pulse - 10, 0, Math.PI * 2);
- ctx.stroke();
- ctx.restore();
- // Draw particles
- for (const p of game.particles) {
- const alpha = Math.floor(p.life * 255).toString(16).padStart(2, '0');
- ctx.fillStyle = p.color + alpha;
- ctx.fillRect(p.x - 1, p.y - 1, 2, 2);
- }
- // Draw immune cells
- for (const immune of game.immuneCells) {
- immune.draw();
- }
- // Draw nanobots - ensure they're on top
- for (const bot of game.nanobots) {
- bot.draw();
- }
- // Draw selection box if dragging
- if (game.selecting) {
- ctx.strokeStyle = 'rgba(255, 255, 0, 0.5)';
- ctx.lineWidth = 2;
- ctx.setLineDash([5, 5]);
- ctx.strokeRect(
- Math.min(game.selectStart.x, game.mouseX),
- Math.min(game.selectStart.y, game.mouseY),
- Math.abs(game.mouseX - game.selectStart.x),
- Math.abs(game.mouseY - game.selectStart.y)
- );
- ctx.setLineDash([]);
- }
- }
- // Update UI
- function updateUI() {
- document.getElementById('botCount').textContent = game.nanobots.length;
- document.getElementById('energyBar').style.width = (game.energy / game.maxEnergy * 100) + '%';
- document.getElementById('waveCount').textContent = game.wave;
- document.getElementById('threatCount').textContent = game.immuneCells.length;
- // Update button states
- document.getElementById('splitBtn').disabled = game.splitCooldown > 0 || game.energy < 20 || game.nanobots.length === 0;
- document.getElementById('mergeBtn').disabled = game.mergeCooldown > 0 || game.nanobots.length < 2;
- document.getElementById('boostBtn').disabled = game.boostCooldown > 0 || game.energy < 30;
- }
- // Split nanobots
- function splitBots() {
- if (game.splitCooldown > 0 || game.energy < 20) return;
- const toSplit = game.selectedBots.length > 0 ? game.selectedBots : game.nanobots.slice(0, Math.min(3, game.nanobots.length));
- for (const bot of toSplit) {
- if (game.energy >= 20) {
- const newBot = new Nanobot(bot.x + Math.random() - 0.5, bot.y + Math.random() - 0.5);
- newBot.energy = bot.energy / 2;
- bot.energy = bot.energy / 2;
- game.nanobots.push(newBot);
- game.energy -= 20;
- createParticles(bot.x * CELL_SIZE, bot.y * CELL_SIZE, '#00ff00');
- }
- }
- game.splitCooldown = 60;
- }
- // Merge nanobots
- function mergeBots() {
- if (game.mergeCooldown > 0 || game.nanobots.length < 2) return;
- const toMerge = game.selectedBots.length >= 2 ? game.selectedBots : game.nanobots.slice(0, 2);
- if (toMerge.length >= 2) {
- const bot1 = toMerge[0];
- const bot2 = toMerge[1];
- bot1.energy = Math.min(100, bot1.energy + bot2.energy);
- bot1.size = Math.min(8, bot1.size + bot2.size / 2);
- const index = game.nanobots.indexOf(bot2);
- if (index > -1) {
- game.nanobots.splice(index, 1);
- }
- createParticles(bot1.x * CELL_SIZE, bot1.y * CELL_SIZE, '#ffff00');
- game.energy = Math.min(game.maxEnergy, game.energy + 10);
- game.mergeCooldown = 40;
- }
- }
- // Boost speed
- function boostSpeed() {
- if (game.boostCooldown > 0 || game.energy < 30) return;
- game.boostActive = true;
- game.boostCooldown = 180;
- game.energy -= 30;
- for (const bot of game.nanobots) {
- createParticles(bot.x * CELL_SIZE, bot.y * CELL_SIZE, '#ffffff');
- }
- }
- // Next wave
- function nextWave() {
- game.wave++;
- game.energy = game.maxEnergy;
- // Generate new maze
- game.grid = MazeGenerator.generate();
- // Add more immune cells
- const immuneCount = 2 + Math.floor(game.wave / 2);
- game.immuneCells = [];
- for (let i = 0; i < immuneCount; i++) {
- const x = Math.random() * (GRID_WIDTH - 10) + 5;
- const y = Math.random() * (GRID_HEIGHT - 10) + 5;
- game.immuneCells.push(new ImmuneCell(x, y));
- }
- // Reset bot positions
- for (const bot of game.nanobots) {
- bot.x = 2 + Math.random();
- bot.y = 2 + Math.random();
- bot.path = [];
- }
- // Create celebration particles
- for (let i = 0; i < 50; i++) {
- createParticles(
- Math.random() * canvas.width,
- Math.random() * canvas.height,
- ['#00ff00', '#00ffff', '#ff00ff'][Math.floor(Math.random() * 3)]
- );
- }
- }
- // Game over
- function gameOver() {
- document.getElementById('gameOver').style.display = 'block';
- document.getElementById('finalWave').textContent = game.wave;
- document.getElementById('finalBots').textContent = game.nanobots.length;
- }
- // Mouse controls
- canvas.addEventListener('mousedown', (e) => {
- const rect = canvas.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const y = e.clientY - rect.top;
- if (e.button === 0) { // Left click
- game.selecting = true;
- game.selectStart = {x, y};
- game.selectedBots = [];
- } else if (e.button === 2) { // Right click
- e.preventDefault();
- const gridX = x / CELL_SIZE;
- const gridY = y / CELL_SIZE;
- // Set target for selected bots or all bots
- const bots = game.selectedBots.length > 0 ? game.selectedBots : game.nanobots;
- for (const bot of bots) {
- bot.setTarget(gridX, gridY);
- }
- createParticles(x, y, '#ffffff');
- }
- });
- canvas.addEventListener('mousemove', (e) => {
- const rect = canvas.getBoundingClientRect();
- game.mouseX = e.clientX - rect.left;
- game.mouseY = e.clientY - rect.top;
- if (game.selecting) {
- // Update selection
- game.selectedBots = [];
- const minX = Math.min(game.selectStart.x, game.mouseX) / CELL_SIZE;
- const maxX = Math.max(game.selectStart.x, game.mouseX) / CELL_SIZE;
- const minY = Math.min(game.selectStart.y, game.mouseY) / CELL_SIZE;
- const maxY = Math.max(game.selectStart.y, game.mouseY) / CELL_SIZE;
- for (const bot of game.nanobots) {
- bot.selected = false;
- if (bot.x >= minX && bot.x <= maxX && bot.y >= minY && bot.y <= maxY) {
- game.selectedBots.push(bot);
- bot.selected = true;
- }
- }
- }
- });
- canvas.addEventListener('mouseup', (e) => {
- if (e.button === 0) {
- game.selecting = false;
- }
- });
- canvas.addEventListener('contextmenu', (e) => {
- e.preventDefault();
- });
- // Keyboard controls
- document.addEventListener('keydown', (e) => {
- // Movement keys
- switch(e.key.toLowerCase()) {
- case 'arrowup':
- e.preventDefault();
- game.keys.up = true;
- break;
- case 'arrowdown':
- e.preventDefault();
- game.keys.down = true;
- break;
- case 'arrowleft':
- e.preventDefault();
- game.keys.left = true;
- break;
- case 'arrowright':
- e.preventDefault();
- game.keys.right = true;
- break;
- case 'w':
- game.keys.w = true;
- break;
- case 'a':
- game.keys.a = true;
- break;
- case 's':
- game.keys.s = true;
- break;
- case 'd':
- game.keys.d = true;
- break;
- case ' ':
- e.preventDefault();
- splitBots();
- break;
- case 'm':
- mergeBots();
- break;
- case 'b':
- boostSpeed();
- break;
- case 'escape':
- // Deselect all
- game.selectedBots = [];
- game.nanobots.forEach(bot => bot.selected = false);
- break;
- }
- });
- document.addEventListener('keyup', (e) => {
- // Movement keys
- switch(e.key.toLowerCase()) {
- case 'arrowup':
- game.keys.up = false;
- break;
- case 'arrowdown':
- game.keys.down = false;
- break;
- case 'arrowleft':
- game.keys.left = false;
- break;
- case 'arrowright':
- game.keys.right = false;
- break;
- case 'w':
- game.keys.w = false;
- break;
- case 'a':
- game.keys.a = false;
- break;
- case 's':
- game.keys.s = false;
- break;
- case 'd':
- game.keys.d = false;
- break;
- }
- });
- // Button controls
- document.getElementById('splitBtn').addEventListener('click', splitBots);
- document.getElementById('mergeBtn').addEventListener('click', mergeBots);
- document.getElementById('boostBtn').addEventListener('click', boostSpeed);
- // Game loop
- function gameLoop() {
- update();
- draw();
- requestAnimationFrame(gameLoop);
- }
- // Start game
- init();
- gameLoop();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment