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>CreedSimAssassin™ v5.0</title>
- <style>
- * {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
- }
- body {
- background: linear-gradient(135deg, #2c1810, #4a3426);
- font-family: 'Courier New', monospace;
- overflow: hidden;
- color: #f4e4c1;
- }
- #gameContainer {
- position: relative;
- width: 100vw;
- height: 100vh;
- overflow: hidden;
- }
- #gameCanvas {
- display: block;
- background: linear-gradient(180deg, #87ceeb 0%, #deb887 70%, #8b4513 100%);
- image-rendering: pixelated;
- }
- #ui {
- position: absolute;
- top: 10px;
- left: 10px;
- z-index: 100;
- color: #f4e4c1;
- text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
- }
- #controls {
- position: absolute;
- bottom: 10px;
- left: 10px;
- z-index: 100;
- font-size: 12px;
- background: rgba(0,0,0,0.7);
- padding: 10px;
- border-radius: 5px;
- max-width: 600px;
- }
- .title {
- position: absolute;
- top: 10px;
- right: 20px;
- font-size: 24px;
- font-weight: bold;
- text-shadow: 3px 3px 6px rgba(0,0,0,0.9);
- color: #d4af37;
- }
- #combo-display {
- position: absolute;
- top: 50px;
- right: 20px;
- font-size: 18px;
- color: #ff6b35;
- text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
- white-space: pre-line;
- text-align: right;
- }
- .context-menu {
- position: absolute;
- background: rgba(20, 20, 20, 0.95);
- border: 2px solid #d4af37;
- border-radius: 8px;
- padding: 10px;
- z-index: 1000;
- min-width: 200px;
- box-shadow: 0 4px 12px rgba(0,0,0,0.8);
- }
- .menu-item {
- padding: 8px 12px;
- cursor: pointer;
- color: #f4e4c1;
- border-radius: 4px;
- margin: 2px 0;
- transition: all 0.2s;
- font-size: 14px;
- }
- .menu-item:hover {
- background: #d4af37;
- color: #000;
- transform: translateX(5px);
- }
- .menu-title {
- color: #d4af37;
- font-weight: bold;
- text-align: center;
- border-bottom: 1px solid #d4af37;
- padding-bottom: 5px;
- margin-bottom: 8px;
- }
- .mode-indicator {
- position: absolute;
- top: 10px;
- left: 200px;
- background: rgba(0,0,0,0.8);
- padding: 5px 10px;
- border-radius: 5px;
- font-size: 14px;
- }
- #autoBuildBtn {
- position: absolute;
- top: 100px;
- right: 20px;
- background: #2ecc71;
- color: white;
- border: none;
- padding: 10px 20px;
- border-radius: 5px;
- font-family: 'Courier New', monospace;
- font-size: 14px;
- cursor: pointer;
- transition: all 0.3s;
- }
- #autoBuildBtn:hover {
- background: #27ae60;
- transform: translateY(-2px);
- }
- #autoBuildBtn:disabled {
- background: #7f8c8d;
- cursor: not-allowed;
- }
- .building-indicator {
- position: absolute;
- top: 150px;
- right: 20px;
- background: rgba(0,0,0,0.8);
- padding: 10px;
- border-radius: 5px;
- color: #f39c12;
- display: none;
- }
- </style>
- </head>
- <body>
- <div id="gameContainer">
- <canvas id="gameCanvas"></canvas>
- <div class="title">CreedSimAssassin™ v5.0</div>
- <div class="mode-indicator" id="modeIndicator">Play Mode</div>
- <button id="autoBuildBtn">Auto-Build Level</button>
- <div class="building-indicator" id="buildingIndicator">
- <div>đź”§ AI Building Level...</div>
- <div id="buildProgress">Analyzing terrain...</div>
- </div>
- <div id="ui">
- <div>Health: <span id="health">100</span></div>
- <div>Score: <span id="score">0</span></div>
- <div>Level: <span id="level">1</span></div>
- </div>
- <div id="combo-display"></div>
- <div id="controls">
- WASD/Arrow Keys: Move | Space: Jump/Wall Jump | E: Assassinate | Q: Stealth Attack | R: Combo Strike<br>
- <strong>Level Editor:</strong> Right-Click: Spawn Enemies | Alt+Right-Click: Platforms & Items | Ctrl+Right-Click: Walls & Hazards<br>
- <strong>Auto-Build:</strong> AI will generate and test levels automatically. Reach the green flag to advance!
- </div>
- </div>
- <script>
- class CreedSimAssassin {
- constructor() {
- this.canvas = document.getElementById('gameCanvas');
- this.ctx = this.canvas.getContext('2d');
- this.canvas.width = window.innerWidth;
- this.canvas.height = window.innerHeight;
- this.camera = { x: 0, y: 0 };
- this.keys = {};
- this.gameState = 'playing';
- this.mousePos = { x: 0, y: 0 };
- this.isAutoBuilding = false;
- this.buildStage = 0;
- this.buildTimer = 0;
- this.aiTestPlayer = null;
- this.testingLevel = false;
- this.lastTime = performance.now();
- this.player = {
- x: 100,
- y: 400,
- width: 20,
- height: 30,
- vx: 0,
- vy: 0,
- onGround: false,
- onWall: false,
- wallDirection: 0,
- health: 100,
- maxHealth: 100,
- facing: 1,
- isStealthed: false,
- stealthCooldown: 0,
- comboCount: 0,
- lastAttackTime: 0,
- color: '#8b4513',
- speed: 1,
- jumpBoost: 1,
- drunkeness: 0,
- inSand: false,
- damageMult: 1,
- gravityMult: 1,
- slowFactor: 1
- };
- this.enemies = [];
- this.platforms = [];
- this.walls = [];
- this.particles = [];
- this.items = [];
- this.hazards = [];
- this.flags = [];
- this.score = 0;
- this.level = 1;
- this.levelWidth = 2000;
- this.enemyTypes = {
- 'light': { health: 40, speed: 1.2, damage: 10, color: '#666', size: { w: 16, h: 26 } },
- 'archer': { health: 50, speed: 0.8, damage: 15, color: '#4a90e2', size: { w: 18, h: 28 } },
- 'priest': { health: 60, speed: 1.0, damage: 20, color: '#9b59b6', size: { w: 18, h: 30 } },
- 'paladin': { health: 100, speed: 0.9, damage: 25, color: '#f39c12', size: { w: 22, h: 32 } },
- 'berserker': { health: 80, speed: 1.8, damage: 30, color: '#e74c3c', size: { w: 20, h: 30 } },
- 'brute': { health: 150, speed: 0.6, damage: 40, color: '#2c3e50', size: { w: 26, h: 36 } }
- };
- this.initializeLevel();
- this.bindEvents();
- this.gameLoop();
- }
- initializeLevel() {
- // Create initial platforms
- this.platforms = [
- {x: 0, y: 550, width: 200, height: 20, type: 'medium'},
- {x: 250, y: 480, width: 150, height: 20, type: 'small'},
- {x: 450, y: 400, width: 120, height: 20, type: 'small'},
- {x: 620, y: 350, width: 100, height: 20, type: 'small'},
- {x: 800, y: 420, width: 180, height: 20, type: 'medium'},
- {x: 1050, y: 300, width: 150, height: 20, type: 'medium'},
- {x: 1300, y: 450, width: 200, height: 20, type: 'large'}
- ];
- // Create walls
- this.walls = [
- {x: 400, y: 200, width: 20, height: 200, type: 'stone', color: '#8b7355'},
- {x: 720, y: 150, width: 20, height: 200, type: 'wood', color: '#8b4513'},
- {x: 1200, y: 100, width: 20, height: 200, type: 'metal', color: '#708090'}
- ];
- // Create initial enemies with exact y positions
- const lightTemplate = this.enemyTypes['light'];
- const archerTemplate = this.enemyTypes['archer'];
- const paladinTemplate = this.enemyTypes['paladin'];
- this.enemies = [
- this.createEnemy('light', 300, this.platforms[1].y - lightTemplate.size.h),
- this.createEnemy('archer', 500, this.platforms[2].y - archerTemplate.size.h),
- this.createEnemy('paladin', 850, this.platforms[4].y - paladinTemplate.size.h)
- ];
- // Add level completion flag
- this.flags = [{
- x: 1450,
- y: 420,
- width: 20,
- height: 30,
- collected: false
- }];
- }
- createEnemy(type, x, y) {
- const template = this.enemyTypes[type];
- return {
- x: x,
- y: y,
- width: template.size.w,
- height: template.size.h,
- health: template.health,
- maxHealth: template.health,
- speed: template.speed,
- damage: template.damage,
- color: template.color,
- vx: 0,
- vy: 0,
- onGround: false,
- onWall: false,
- wallDirection: 0,
- facing: Math.random() > 0.5 ? 1 : -1,
- type: type,
- alertness: 0,
- patrolStart: x - 75,
- patrolEnd: x + 75,
- attackCooldown: 0,
- edgeCheckDistance: 10,
- chaseTime: 0
- };
- }
- bindEvents() {
- window.addEventListener('keydown', (e) => {
- this.keys[e.code] = true;
- this.handleKeyDown(e);
- });
- window.addEventListener('keyup', (e) => {
- this.keys[e.code] = false;
- });
- window.addEventListener('resize', () => {
- this.canvas.width = window.innerWidth;
- this.canvas.height = window.innerHeight;
- });
- // Mouse events for level editor
- this.canvas.addEventListener('mousemove', (e) => {
- const rect = this.canvas.getBoundingClientRect();
- this.mousePos.x = e.clientX - rect.left + this.camera.x;
- this.mousePos.y = e.clientY - rect.top + this.camera.y;
- });
- this.canvas.addEventListener('contextmenu', (e) => {
- e.preventDefault();
- this.handleRightClick(e);
- });
- // Auto-build button
- document.getElementById('autoBuildBtn').addEventListener('click', () => {
- this.startAutoBuilder();
- });
- // Close context menus when clicking elsewhere
- document.addEventListener('click', (e) => {
- if (!e.target.closest('.context-menu')) {
- this.closeAllMenus();
- }
- });
- }
- startAutoBuilder() {
- if (this.isAutoBuilding) return;
- // Reset player position and health regardless of state
- const startPlatform = this.platforms.find(p => p.x < 250) || { y: 550 };
- this.player.health = this.player.maxHealth;
- this.player.x = 100;
- this.player.y = startPlatform.y - this.player.height;
- this.player.vx = 0;
- this.player.vy = 0;
- this.player.onGround = true;
- this.isAutoBuilding = true;
- this.buildStage = 0;
- this.buildTimer = 0;
- document.getElementById('autoBuildBtn').disabled = true;
- document.getElementById('buildingIndicator').style.display = 'block';
- // Clear current level (except player spawn area)
- this.platforms = this.platforms.filter(p => p.x < 250);
- this.walls = [];
- this.enemies = [];
- this.items = [];
- this.hazards = [];
- this.flags = [];
- this.updateBuildProgress(`Auto-Building Level ${this.level}...`);
- }
- updateAutoBuilder(dt) {
- if (!this.isAutoBuilding) return;
- this.buildTimer += dt;
- switch(this.buildStage) {
- case 0: // Planning phase
- if (this.buildTimer > 1) {
- this.aiPlanLevel();
- this.buildStage = 1;
- this.buildTimer = 0;
- }
- break;
- case 1: // Building platforms
- if (this.buildTimer > 0.33 && this.platforms.length < this.platformCount) {
- this.aiBuildPlatforms();
- this.buildTimer = 0;
- }
- if (this.buildTimer > 5 || this.platforms.length >= this.platformCount) {
- this.buildStage = 2;
- this.buildTimer = 0;
- }
- break;
- case 2: // Adding walls and hazards
- if (this.buildTimer > 0.4 && (this.walls.length + this.hazards.length) < this.hazardCount) {
- this.aiBuildWallsHazards();
- this.buildTimer = 0;
- }
- if (this.buildTimer > 3.33 || (this.walls.length + this.hazards.length) >= this.hazardCount) {
- this.buildStage = 3;
- this.buildTimer = 0;
- }
- break;
- case 3: // Placing enemies
- if (this.buildTimer > 0.5 && this.enemies.length < this.enemyCount) {
- this.aiPlaceEnemies();
- this.buildTimer = 0;
- }
- if (this.buildTimer > 4 || this.enemies.length >= this.enemyCount) {
- this.buildStage = 4;
- this.buildTimer = 0;
- }
- break;
- case 4: // Adding items and flag
- if (this.buildTimer > 0.5 && this.items.length < this.itemCount) {
- this.aiPlaceItems();
- this.buildTimer = 0;
- }
- if (this.buildTimer > 2.5 || this.items.length >= this.itemCount) {
- this.aiPlaceFlag();
- this.buildStage = 5;
- this.buildTimer = 0;
- }
- break;
- case 5: // Testing level
- if (this.buildTimer === 0) {
- this.startLevelTest();
- }
- if (this.buildTimer > 5 || this.aiTestComplete()) {
- this.completeBuild();
- }
- break;
- }
- }
- aiPlanLevel() {
- this.updateBuildProgress(`AI analyzing optimal level structure for Level ${this.level}...`);
- this.levelDifficulty = Math.min(this.level * 0.3 + 0.7, 2.0);
- this.platformCount = Math.floor(12 + this.level * 3);
- this.enemyCount = Math.floor(4 + this.level * 2);
- this.itemCount = Math.floor(3 + this.level);
- this.hazardCount = Math.floor(2 + this.level * 0.5);
- }
- aiBuildPlatforms() {
- const x = 300 + Math.random() * (this.levelWidth - 600);
- const y = 200 + Math.random() * 300;
- const sizes = ['small', 'medium', 'large'];
- const dimensions = {
- small: [80, 15],
- medium: [150, 20],
- large: [220, 25]
- };
- const size = sizes[Math.floor(Math.random() * sizes.length)];
- const [width, height] = dimensions[size];
- // Check if platform placement is valid (tighter check to avoid overlap)
- let valid = true;
- this.platforms.forEach(existing => {
- if (Math.abs(x - existing.x) < (width + existing.width)/2 + 20 &&
- Math.abs(y - existing.y) < height + existing.height + 40) {
- valid = false;
- }
- });
- if (valid) {
- this.platforms.push({
- x: x - width/2,
- y: y,
- width: width,
- height: height,
- type: size
- });
- this.updateBuildProgress(`Building platforms... (${this.platforms.length}/${this.platformCount})`);
- }
- }
- aiBuildWallsHazards() {
- // AI strategically places walls near platforms
- if (Math.random() < 0.6 && this.platforms.length > 3) {
- const platform = this.platforms[Math.floor(Math.random() * this.platforms.length)];
- const wallTypes = ['stone', 'wood', 'metal'];
- const type = wallTypes[Math.floor(Math.random() * wallTypes.length)];
- const colors = { stone: '#8b7355', wood: '#8b4513', metal: '#708090' };
- // Check for overlap
- let valid = true;
- this.walls.forEach(w => {
- if (Math.abs(platform.x + platform.width + 10 - w.x) < 40 && Math.abs(platform.y - 60 - w.y) < 120) {
- valid = false;
- }
- });
- if (valid) {
- this.walls.push({
- x: platform.x + platform.width + 10,
- y: platform.y - 120,
- width: 20,
- height: 120,
- type: type,
- color: colors[type]
- });
- }
- }
- // Add hazards strategically
- if (Math.random() < 0.4) {
- const hazardTypes = ['spikes', 'bomb', 'sand', 'ladder'];
- const type = hazardTypes[Math.floor(Math.random() * hazardTypes.length)];
- this.aiPlaceHazardType(type);
- }
- this.updateBuildProgress('AI placing strategic obstacles...');
- }
- aiPlaceHazardType(type) {
- const hazardData = {
- spikes: { width: 30, height: 10, color: '#666', damage: 2.5 },
- bomb: { width: 20, height: 20, color: '#ff0000', damage: 9.5 },
- sand: { width: 60, height: 20, color: '#daa520', slowFactor: 0.5 },
- ladder: { width: 15, height: 80, color: '#8b4513', climbable: true }
- };
- const data = hazardData[type];
- if (!data) return;
- const x = 400 + Math.random() * (this.levelWidth - 800);
- const y = 300 + Math.random() * 200;
- // Check no overlap
- let valid = true;
- this.hazards.forEach(h => {
- if (Math.abs(x - h.x) < (data.width + h.width)/2 + 10 && Math.abs(y - h.y) < (data.height + h.height)/2 + 10) {
- valid = false;
- }
- });
- if (valid) {
- this.hazards.push({
- x: x,
- y: y,
- width: data.width,
- height: data.height,
- type: type,
- color: data.color,
- ...data
- });
- }
- }
- aiPlaceEnemies() {
- if (this.platforms.length < 3) return;
- const platformIndex = 1 + Math.floor(Math.random() * (this.platforms.length - 1));
- const platform = this.platforms[platformIndex];
- const enemyTypes = Object.keys(this.enemyTypes);
- const type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
- const template = this.enemyTypes[type];
- const spawnX = platform.x + Math.random() * (platform.width - template.size.w);
- const spawnY = platform.y - template.size.h;
- const enemy = this.createEnemy(type, spawnX, spawnY);
- // Jostle position to ensure on platform
- enemy.y -= 1; // Slight lift to prevent initial fall-through
- this.enemies.push(enemy);
- this.updateBuildProgress(`Deploying enemies... (${this.enemies.length}/${this.enemyCount})`);
- }
- aiPlaceItems() {
- const itemTypes = ['health', 'speed', 'jump', 'haste'];
- const type = itemTypes[Math.floor(Math.random() * itemTypes.length)];
- const colors = {
- health: '#ff4444',
- speed: '#44ff44',
- jump: '#4444ff',
- haste: '#ffff44'
- };
- if (this.platforms.length > 2) {
- const platform = this.platforms[Math.floor(Math.random() * this.platforms.length)];
- this.items.push({
- x: platform.x + Math.random() * platform.width,
- y: platform.y - 20,
- width: 15,
- height: 15,
- type: type,
- color: colors[type],
- collected: false
- });
- }
- this.updateBuildProgress(`Distributing power-ups... (${this.items.length}/${this.itemCount})`);
- }
- aiPlaceFlag() {
- // Place flag at the end of the level
- const endX = this.levelWidth - 200;
- let flagY = 450;
- // Find a suitable platform near the end
- let suitablePlatform = null;
- this.platforms.forEach(platform => {
- if (platform.x > endX - 200 && platform.x < endX + 200) {
- if (!suitablePlatform || Math.abs(platform.x - endX) < Math.abs(suitablePlatform.x - endX)) {
- suitablePlatform = platform;
- }
- }
- });
- if (suitablePlatform) {
- flagY = suitablePlatform.y - 30;
- }
- this.flags = [{
- x: endX,
- y: flagY,
- width: 20,
- height: 30,
- collected: false
- }];
- this.updateBuildProgress('Placing victory flag...');
- }
- startLevelTest() {
- this.updateBuildProgress('AI testing level playability...');
- this.testingLevel = true;
- // Create AI test player
- this.aiTestPlayer = {
- x: 100,
- y: 400,
- vx: 2,
- vy: 0,
- onGround: false,
- jumpCooldown: 0,
- testTime: 0
- };
- }
- aiTestComplete() {
- if (!this.testingLevel) return false;
- this.aiTestPlayer.testTime++;
- // Simple AI movement for testing
- this.aiTestPlayer.x += this.aiTestPlayer.vx;
- this.aiTestPlayer.y += this.aiTestPlayer.vy;
- this.aiTestPlayer.vy += 0.6;
- // Simple jump logic
- if (this.aiTestPlayer.jumpCooldown > 0) {
- this.aiTestPlayer.jumpCooldown--;
- }
- if (this.aiTestPlayer.onGround && this.aiTestPlayer.jumpCooldown === 0) {
- this.aiTestPlayer.vy = -10;
- this.aiTestPlayer.jumpCooldown = 30;
- this.aiTestPlayer.onGround = false;
- }
- // Check if AI reached flag or time limit
- const flagReached = this.flags.some(flag =>
- Math.abs(this.aiTestPlayer.x - flag.x) < 50);
- return flagReached || this.aiTestPlayer.testTime > 600;
- }
- completeBuild() {
- this.isAutoBuilding = false;
- this.testingLevel = false;
- this.aiTestPlayer = null;
- document.getElementById('autoBuildBtn').disabled = false;
- document.getElementById('buildingIndicator').style.display = 'none';
- document.getElementById('modeIndicator').textContent = `Level ${this.level} Ready!`;
- setTimeout(() => {
- document.getElementById('modeIndicator').textContent = 'Play Mode';
- }, 3000);
- }
- updateBuildProgress(text) {
- document.getElementById('buildProgress').textContent = text;
- }
- handleRightClick(e) {
- if (this.isAutoBuilding) return;
- const rect = this.canvas.getBoundingClientRect();
- const screenX = e.clientX - rect.left;
- const screenY = e.clientY - rect.top;
- if (e.ctrlKey) {
- this.showHazardMenu(screenX, screenY);
- } else if (e.altKey) {
- this.showPlatformMenu(screenX, screenY);
- } else {
- this.showEnemyMenu(screenX, screenY);
- }
- }
- showEnemyMenu(x, y) {
- this.closeAllMenus();
- const menu = document.createElement('div');
- menu.className = 'context-menu';
- menu.id = 'enemyMenu';
- menu.style.left = x + 'px';
- menu.style.top = y + 'px';
- const title = document.createElement('div');
- title.className = 'menu-title';
- title.textContent = 'Spawn Enemy';
- menu.appendChild(title);
- const enemies = [
- { name: 'Light Infantry', type: 'light', desc: 'Fast, weak guard' },
- { name: 'Archer', type: 'archer', desc: 'Ranged attacker' },
- { name: 'Priest', type: 'priest', desc: 'Magic abilities' },
- { name: 'Paladin', type: 'paladin', desc: 'Heavily armored' },
- { name: 'Berserker', type: 'berserker', desc: 'Fast and aggressive' },
- { name: 'Heavy Brute', type: 'brute', desc: 'Slow but powerful' }
- ];
- enemies.forEach(enemy => {
- const item = document.createElement('div');
- item.className = 'menu-item';
- item.innerHTML = `<strong>${enemy.name}</strong><br><small>${enemy.desc}</small>`;
- item.addEventListener('click', (e) => {
- e.stopPropagation();
- this.spawnEnemy(enemy.type);
- this.closeAllMenus();
- });
- menu.appendChild(item);
- });
- document.body.appendChild(menu);
- }
- showPlatformMenu(x, y) {
- this.closeAllMenus();
- const menu = document.createElement('div');
- menu.className = 'context-menu';
- menu.id = 'platformMenu';
- menu.style.left = x + 'px';
- menu.style.top = y + 'px';
- const title = document.createElement('div');
- title.className = 'menu-title';
- title.textContent = 'Platforms & Items';
- menu.appendChild(title);
- const items = [
- { name: 'Small Platform', action: () => this.placePlatform('small', 80, 15) },
- { name: 'Medium Platform', action: () => this.placePlatform('medium', 150, 20) },
- { name: 'Large Platform', action: () => this.placePlatform('large', 220, 25) },
- { name: 'Health Pack', action: () => this.placeItem('health') },
- { name: 'Speed Herb', action: () => this.placeItem('speed') },
- { name: 'Jump Spice', action: () => this.placeItem('jump') },
- { name: 'Haste Potion', action: () => this.placeItem('haste') },
- { name: 'Drunk Elixir', action: () => this.placeItem('drunk') }
- ];
- items.forEach(item => {
- const element = document.createElement('div');
- element.className = 'menu-item';
- element.textContent = item.name;
- element.addEventListener('click', (e) => {
- e.stopPropagation();
- item.action();
- this.closeAllMenus();
- });
- menu.appendChild(element);
- });
- document.body.appendChild(menu);
- }
- showHazardMenu(x, y) {
- this.closeAllMenus();
- const menu = document.createElement('div');
- menu.className = 'context-menu';
- menu.id = 'hazardMenu';
- menu.style.left = x + 'px';
- menu.style.top = y + 'px';
- const title = document.createElement('div');
- title.className = 'menu-title';
- title.textContent = 'Walls & Hazards';
- menu.appendChild(title);
- const items = [
- { name: 'Stone Wall', action: () => this.placeWall('stone') },
- { name: 'Wood Wall', action: () => this.placeWall('wood') },
- { name: 'Metal Wall', action: () => this.placeWall('metal') },
- { name: 'Spike Trap', action: () => this.placeHazard('spikes') },
- { name: 'Bomb', action: () => this.placeHazard('bomb') },
- { name: 'Sand Pit', action: () => this.placeHazard('sand') },
- { name: 'Ladder', action: () => this.placeHazard('ladder') }
- ];
- items.forEach(item => {
- const element = document.createElement('div');
- element.className = 'menu-item';
- element.textContent = item.name;
- element.addEventListener('click', (e) => {
- e.stopPropagation();
- item.action();
- this.closeAllMenus();
- });
- menu.appendChild(element);
- });
- document.body.appendChild(menu);
- }
- closeAllMenus() {
- const menus = document.querySelectorAll('.context-menu');
- menus.forEach(menu => menu.remove());
- }
- spawnEnemy(type) {
- const enemy = this.createEnemy(type, this.mousePos.x, this.mousePos.y);
- this.enemies.push(enemy);
- document.getElementById('modeIndicator').textContent = `Spawned: ${type}`;
- }
- placePlatform(size, width, height) {
- this.platforms.push({
- x: this.mousePos.x - width/2,
- y: this.mousePos.y,
- width: width,
- height: height,
- type: size
- });
- document.getElementById('modeIndicator').textContent = `Placed: ${size} platform`;
- }
- placeWall(type) {
- const colors = { stone: '#8b7355', wood: '#8b4513', metal: '#708090' };
- this.walls.push({
- x: this.mousePos.x,
- y: this.mousePos.y - 100,
- width: 20,
- height: 120,
- type: type,
- color: colors[type]
- });
- document.getElementById('modeIndicator').textContent = `Placed: ${type} wall`;
- }
- placeItem(type) {
- const colors = {
- health: '#ff4444',
- speed: '#44ff44',
- jump: '#4444ff',
- haste: '#ffff44',
- drunk: '#ff44ff'
- };
- this.items.push({
- x: this.mousePos.x,
- y: this.mousePos.y,
- width: 15,
- height: 15,
- type: type,
- color: colors[type],
- collected: false
- });
- document.getElementById('modeIndicator').textContent = `Placed: ${type} item`;
- }
- placeHazard(type) {
- const hazardData = {
- spikes: { width: 30, height: 10, color: '#666', damage: 2.5 },
- bomb: { width: 20, height: 20, color: '#ff0000', damage: 9.5 },
- sand: { width: 60, height: 20, color: '#daa520', slowFactor: 0.5 },
- ladder: { width: 15, height: 80, color: '#8b4513', climbable: true }
- };
- const data = hazardData[type];
- this.hazards.push({
- x: this.mousePos.x,
- y: this.mousePos.y,
- width: data.width,
- height: data.height,
- type: type,
- color: data.color,
- ...data
- });
- document.getElementById('modeIndicator').textContent = `Placed: ${type}`;
- }
- handleKeyDown(e) {
- switch(e.code) {
- case 'KeyE':
- this.performAssassination();
- break;
- case 'KeyQ':
- this.performStealthAttack();
- break;
- case 'KeyR':
- this.performComboStrike();
- break;
- }
- }
- update(dt) {
- if (this.isAutoBuilding) {
- this.updateAutoBuilder(dt);
- }
- this.updatePlayer(dt);
- this.updateEnemies(dt);
- this.updateParticles(dt);
- this.updateCamera(dt);
- this.checkCollisions();
- this.checkItemCollisions();
- this.checkHazardCollisions(dt);
- this.checkFlagCollisions();
- if (this.player.stealthCooldown > 0) {
- this.player.stealthCooldown -= dt;
- if (this.player.stealthCooldown < 0) this.player.stealthCooldown = 0;
- }
- if (this.player.drunkeness > 0) {
- this.player.drunkeness -= dt;
- if (this.player.drunkeness < 0) this.player.drunkeness = 0;
- }
- // Reset combo if too much time passes
- if (Date.now() - this.player.lastAttackTime > 3000) {
- this.player.comboCount = 0;
- }
- }
- checkFlagCollisions() {
- this.flags.forEach(flag => {
- if (!flag.collected && this.isColliding(this.player, flag)) {
- flag.collected = true;
- const completedLevel = this.level;
- this.level++;
- this.score += 500;
- this.createParticles(flag.x, flag.y, '#00ff00', 10);
- this.updateComboDisplay(`Level ${completedLevel} Completed\nLevel ${this.level} Auto-Building...`);
- // Auto-trigger level builder for next level
- setTimeout(() => {
- this.startAutoBuilder();
- }, 2000);
- }
- });
- }
- updatePlayer(dt) {
- const player = this.player;
- const baseSpeed = 240;
- const gravity = 800;
- const jumpPower = 600;
- const friction = 10;
- player.inSand = false;
- player.damageMult = 1;
- player.gravityMult = 1;
- player.slowFactor = 1;
- // Apply drunkeness effect
- let drunkModifier = 1;
- if (player.drunkeness > 0) {
- drunkModifier = 0.7 + Math.sin(Date.now() * 0.01) * 0.3;
- }
- let targetVx = 0;
- if (this.keys['KeyA'] || this.keys['ArrowLeft']) {
- targetVx = -baseSpeed * player.speed * drunkModifier * player.slowFactor;
- player.facing = -1;
- } else if (this.keys['KeyD'] || this.keys['ArrowRight']) {
- targetVx = baseSpeed * player.speed * drunkModifier * player.slowFactor;
- player.facing = 1;
- }
- // Accelerate towards target
- player.vx += (targetVx - player.vx) * friction * dt;
- // Jumping and wall jumping
- if ((this.keys['Space'] || this.keys['KeyW'] || this.keys['ArrowUp']) && (player.onGround || player.onWall)) {
- player.vy = -jumpPower * player.jumpBoost;
- if (player.onWall) {
- player.vx = player.wallDirection * baseSpeed * 1.5;
- }
- player.onGround = false;
- player.onWall = false;
- }
- // Apply gravity
- player.vy += gravity * player.gravityMult * dt;
- // Terminal velocity
- if (player.vy > 900) player.vy = 900;
- // Update position
- player.x += player.vx * dt;
- player.y += player.vy * dt;
- // Keep player in bounds
- if (player.x < 0) player.x = 0;
- if (player.x > this.levelWidth - player.width) player.x = this.levelWidth - player.width;
- if (player.y > this.canvas.height) {
- player.health -= 20;
- player.x = 100;
- player.y = 400;
- player.vx = 0;
- player.vy = 0;
- }
- }
- updateEnemies(dt) {
- const gravity = 800;
- const climbSpeed = 150;
- const ledgeLeapBoost = 1.3;
- this.enemies.forEach(enemy => {
- if (enemy.attackCooldown > 0) enemy.attackCooldown -= dt;
- enemy.onGround = false;
- enemy.onWall = false;
- // Enhanced AI based on enemy type to set vx
- const dx = this.player.x - enemy.x;
- const dy = this.player.y - enemy.y;
- const distToPlayer = Math.sqrt(dx * dx + dy * dy);
- // Edge detection - check if enemy is about to fall off platform
- let canContinue = this.checkEnemyCanContinue(enemy);
- if (distToPlayer < 200 && !this.player.isStealthed) {
- enemy.alertness = Math.min(1, enemy.alertness + dt / 2);
- enemy.chaseTime += dt;
- } else {
- enemy.alertness = Math.max(0, enemy.alertness - dt / 5);
- enemy.chaseTime = Math.max(0, enemy.chaseTime - dt);
- }
- let targetVx = 0;
- let chasing = enemy.alertness > 0.25;
- if (chasing) {
- const chargeDir = dx > 0 ? 1 : -1;
- enemy.facing = chargeDir;
- if (enemy.type === 'archer' && distToPlayer < 50) {
- // Archers flee close range
- const fleeDir = -chargeDir;
- targetVx = fleeDir * enemy.speed * 180;
- } else if (enemy.type === 'berserker') {
- // Berserkers charge relentlessly
- targetVx = chargeDir * enemy.speed * 300;
- } else {
- targetVx = chargeDir * enemy.speed * 240;
- }
- // Check edge while chasing
- if (!canContinue && distToPlayer > 30) {
- targetVx = 0;
- enemy.facing = -enemy.facing;
- }
- } else {
- // Patrol
- if (enemy.x <= enemy.patrolStart) {
- enemy.facing = 1;
- } else if (enemy.x >= enemy.patrolEnd) {
- enemy.facing = -1;
- }
- targetVx = enemy.facing * enemy.speed * 240;
- if (!canContinue) {
- enemy.facing = -enemy.facing;
- targetVx = enemy.facing * enemy.speed * 240;
- }
- // If far from patrol after chase
- if (enemy.alertness <= 0 && Math.abs(enemy.x - (enemy.patrolStart + 75)) > 150) {
- enemy.patrolStart = enemy.x - 75;
- enemy.patrolEnd = enemy.x + 75;
- }
- }
- // Accelerate
- enemy.vx += (targetVx - enemy.vx) * 10 * dt;
- // Apply gravity
- enemy.vy += gravity * dt;
- if (enemy.vy > 900) enemy.vy = 900;
- // Update position
- enemy.x += enemy.vx * dt;
- enemy.y += enemy.vy * dt;
- // Check collisions for this enemy
- this.checkEnemyCollisions(enemy, dt, dx, dy, chasing);
- // Ladder climbing
- this.hazards.forEach(hazard => {
- if (hazard.type === 'ladder' && this.isColliding(enemy, hazard) && chasing) {
- if (dy < -20) {
- enemy.vy = -climbSpeed;
- } else if (dy > 20) {
- enemy.vy = climbSpeed;
- }
- }
- });
- // Attack player if close enough
- if (distToPlayer < 30 && enemy.attackCooldown <= 0) {
- this.player.health -= enemy.damage;
- enemy.attackCooldown = 1;
- this.createParticles(this.player.x, this.player.y, '#ff0000', 5);
- }
- });
- // Remove dead enemies
- this.enemies = this.enemies.filter(enemy => enemy.health > 0);
- }
- checkEnemyCanContinue(enemy) {
- // Check if there's ground ahead of the enemy
- const checkDistance = enemy.edgeCheckDistance + Math.abs(enemy.vx) / 60;
- const checkX = enemy.x + (enemy.facing > 0 ? enemy.width / 2 + checkDistance : -checkDistance - enemy.width / 2);
- const checkY = enemy.y + enemy.height + 5;
- // Check if any platform supports this position
- return this.platforms.some(platform =>
- checkX >= platform.x && checkX <= platform.x + platform.width &&
- checkY >= platform.y - 20 && checkY <= platform.y + platform.height + 20
- );
- }
- checkEnemyCollisions(enemy, dt, dx, dy, chasing) {
- const climbSpeed = 150;
- const ledgeLeapBoost = 1.3;
- // Platform collisions with penetration resolve
- this.platforms.forEach(platform => {
- if (this.isColliding(enemy, platform)) {
- if (enemy.vy >= 0) {
- const overlap = (enemy.y + enemy.height) - platform.y;
- if (overlap > 0) {
- enemy.y -= overlap;
- enemy.vy = 0;
- enemy.onGround = true;
- }
- }
- }
- });
- // Wall collisions (reverse or climb)
- this.walls.forEach(wall => {
- if (this.isColliding(enemy, wall)) {
- // Resolve horizontal penetration
- if (enemy.vx > 0) {
- enemy.x = wall.x - enemy.width;
- enemy.wallDirection = -1;
- } else if (enemy.vx < 0) {
- enemy.x = wall.x + wall.width;
- enemy.wallDirection = 1;
- }
- enemy.onWall = true;
- // All enemies can climb if chasing or player above/below
- if (chasing || Math.abs(dy) > 20) {
- if (dy < 0) {
- enemy.vy = -climbSpeed;
- } else if (dy > 0) {
- enemy.vy = climbSpeed;
- }
- enemy.vx = enemy.wallDirection * 50; // Slight horizontal during climb
- } else {
- enemy.facing = -enemy.facing;
- enemy.vx = enemy.facing * enemy.speed * 240;
- }
- // Check if at top of wall for ledge leap
- const atTop = enemy.y + enemy.height <= wall.y + 10 && enemy.vy < 0;
- if (atTop && chasing) {
- enemy.vy -= 200 * dt; // Extra upward boost
- enemy.vx *= ledgeLeapBoost;
- enemy.onWall = false;
- }
- }
- });
- }
- checkItemCollisions() {
- this.items.forEach(item => {
- if (!item.collected && this.isColliding(this.player, item)) {
- item.collected = true;
- switch(item.type) {
- case 'health':
- this.player.health = Math.min(this.player.maxHealth, this.player.health + 30);
- break;
- case 'speed':
- this.player.speed = 1.5;
- setTimeout(() => this.player.speed = 1, 10000);
- break;
- case 'jump':
- this.player.jumpBoost = 1.4;
- setTimeout(() => this.player.jumpBoost = 1, 8000);
- break;
- case 'haste':
- this.player.speed = 1.8;
- this.player.jumpBoost = 1.3;
- setTimeout(() => {
- this.player.speed = 1;
- this.player.jumpBoost = 1;
- }, 6000);
- break;
- case 'drunk':
- this.player.drunkeness = 10; // 10 seconds
- break;
- }
- this.createParticles(item.x, item.y, item.color, 6);
- this.updateComboDisplay(`Got ${item.type}!`);
- }
- });
- // Remove collected items
- this.items = this.items.filter(item => !item.collected);
- }
- checkHazardCollisions(dt) {
- this.player.inSand = false;
- this.hazards.forEach(hazard => {
- if (this.isColliding(this.player, hazard)) {
- switch(hazard.type) {
- case 'spikes':
- this.player.health -= hazard.damage * dt;
- this.player.vx += (Math.random() - 0.5) * 200 * dt; // Veering knockback
- this.player.vy -= 100 * dt; // Slight bounce
- this.createParticles(this.player.x, this.player.y, '#ff0000', 3);
- break;
- case 'bomb':
- const bombDamage = Math.floor(Math.random() * 4 + 8);
- this.player.health -= bombDamage;
- this.createParticles(hazard.x, hazard.y, '#ff8800', 12);
- // Remove bomb after explosion
- this.hazards = this.hazards.filter(h => h !== hazard);
- break;
- case 'sand':
- this.player.inSand = true;
- this.player.slowFactor = hazard.slowFactor;
- this.player.damageMult = 0.5;
- this.player.gravityMult = 0.5;
- // Fall slower already via gravityMult
- break;
- case 'ladder':
- // Allow climbing
- if (this.keys['KeyW'] || this.keys['ArrowUp']) {
- this.player.vy = -150;
- this.player.onGround = true;
- } else if (this.keys['KeyS'] || this.keys['ArrowDown']) {
- this.player.vy = 150;
- }
- break;
- }
- }
- });
- }
- updateParticles(dt) {
- this.particles = this.particles.filter(particle => {
- particle.x += particle.vx * dt;
- particle.y += particle.vy * dt;
- particle.life -= dt;
- particle.opacity = particle.life / particle.maxLife;
- return particle.life > 0;
- });
- }
- updateCamera(dt) {
- // Follow player with some smoothing
- const targetX = this.player.x - this.canvas.width / 2;
- this.camera.x += (targetX - this.camera.x) * 5 * dt;
- // Keep camera in bounds
- if (this.camera.x < 0) this.camera.x = 0;
- if (this.camera.x > this.levelWidth - this.canvas.width) {
- this.camera.x = this.levelWidth - this.canvas.width;
- }
- }
- checkCollisions() {
- this.player.onGround = false;
- this.player.onWall = false;
- // Platform collisions with resolve
- this.platforms.forEach(platform => {
- if (this.isColliding(this.player, platform)) {
- if (this.player.vy >= 0) {
- const overlap = (this.player.y + this.player.height) - platform.y;
- if (overlap > 0) {
- this.player.y -= overlap;
- this.player.vy = 0;
- this.player.onGround = true;
- }
- }
- }
- });
- // Wall collisions with resolve
- this.walls.forEach(wall => {
- if (this.isColliding(this.player, wall)) {
- if (this.player.vx > 0) {
- const overlap = (this.player.x + this.player.width) - wall.x;
- this.player.x -= overlap;
- this.player.onWall = true;
- this.player.wallDirection = -1;
- } else if (this.player.vx < 0) {
- const overlap = wall.x + wall.width - this.player.x;
- this.player.x += overlap;
- this.player.onWall = true;
- this.player.wallDirection = 1;
- }
- this.player.vy *= 0.7;
- }
- });
- }
- isColliding(rect1, rect2) {
- return rect1.x < rect2.x + rect2.width &&
- rect1.x + rect1.width > rect2.x &&
- rect1.y < rect2.y + rect2.height &&
- rect1.y + rect1.height > rect2.y;
- }
- performAssassination() {
- const assassinRange = 40;
- let targetEnemy = null;
- this.enemies.forEach(enemy => {
- const distance = Math.sqrt(
- Math.pow(this.player.x - enemy.x, 2) +
- Math.pow(this.player.y - enemy.y, 2)
- );
- if (distance < assassinRange) {
- targetEnemy = enemy;
- }
- });
- if (targetEnemy) {
- // Instant kill, but reduce if in sand (half health instead?)
- if (this.player.inSand) {
- targetEnemy.health *= 0.5;
- } else {
- targetEnemy.health = 0;
- }
- this.score += 100 + (this.player.comboCount * 25);
- this.player.comboCount++;
- this.player.lastAttackTime = Date.now();
- // Create blood particles
- this.createParticles(targetEnemy.x, targetEnemy.y, '#ff0000', 8);
- this.updateComboDisplay('ASSASSINATION!');
- }
- }
- performStealthAttack() {
- if (this.player.stealthCooldown > 0) return;
- this.player.isStealthed = true;
- this.player.stealthCooldown = 3; // 3 seconds
- this.player.color = '#4a4a4a'; // Darker color when stealthed
- const stealthRange = 35;
- this.enemies.forEach(enemy => {
- const distance = Math.sqrt(
- Math.pow(this.player.x - enemy.x, 2) +
- Math.pow(this.player.y - enemy.y, 2)
- );
- if (distance < stealthRange) {
- const damage = 40 * this.player.damageMult;
- enemy.health -= damage;
- enemy.alertness = 0; // Reset alertness
- this.score += 75 + (this.player.comboCount * 20);
- this.player.comboCount++;
- this.player.lastAttackTime = Date.now();
- this.createParticles(enemy.x, enemy.y, '#9b59b6', 5);
- }
- });
- setTimeout(() => {
- this.player.isStealthed = false;
- this.player.color = '#8b4513';
- }, 1000);
- this.updateComboDisplay('STEALTH STRIKE!');
- }
- performComboStrike() {
- const comboRange = 50;
- let hitCount = 0;
- this.enemies.forEach(enemy => {
- const distance = Math.sqrt(
- Math.pow(this.player.x - enemy.x, 2) +
- Math.pow(this.player.y - enemy.y, 2)
- );
- if (distance < comboRange) {
- const baseDamage = 25 + (this.player.comboCount * 10);
- const damage = baseDamage * this.player.damageMult;
- enemy.health -= damage;
- hitCount++;
- this.createParticles(enemy.x, enemy.y, '#ffd700', 6);
- }
- });
- if (hitCount > 0) {
- this.player.comboCount += hitCount;
- this.player.lastAttackTime = Date.now();
- this.score += hitCount * 30;
- this.updateComboDisplay(`COMBO x${this.player.comboCount}!`);
- }
- }
- createParticles(x, y, color, count) {
- for (let i = 0; i < count; i++) {
- this.particles.push({
- x: x + Math.random() * 20 - 10,
- y: y + Math.random() * 20 - 10,
- vx: Math.random() * 360 - 180,
- vy: Math.random() * 360 - 180,
- color: color,
- life: 0.5,
- maxLife: 0.5,
- opacity: 1
- });
- }
- }
- updateComboDisplay(text) {
- const display = document.getElementById('combo-display');
- display.innerHTML = text;
- display.style.opacity = '1';
- setTimeout(() => {
- display.style.opacity = '0';
- }, 2000);
- }
- render() {
- // Clear canvas
- this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- // Save context for camera transform
- this.ctx.save();
- this.ctx.translate(-this.camera.x, -this.camera.y);
- // Draw platforms
- this.ctx.fillStyle = '#654321';
- this.platforms.forEach(platform => {
- this.ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
- // Platform type indicator
- this.ctx.fillStyle = '#8b7355';
- this.ctx.fillRect(platform.x, platform.y - 2, platform.width, 2);
- this.ctx.fillStyle = '#654321';
- });
- // Draw walls
- this.walls.forEach(wall => {
- this.ctx.fillStyle = wall.color || '#8b7355';
- this.ctx.fillRect(wall.x, wall.y, wall.width, wall.height);
- });
- // Draw hazards
- this.hazards.forEach(hazard => {
- this.ctx.fillStyle = hazard.color;
- if (hazard.type === 'ladder') {
- // Draw ladder rungs
- this.ctx.fillRect(hazard.x, hazard.y, hazard.width, hazard.height);
- for (let i = 0; i < hazard.height; i += 15) {
- this.ctx.fillStyle = '#654321';
- this.ctx.fillRect(hazard.x, hazard.y + i, hazard.width, 2);
- this.ctx.fillStyle = hazard.color;
- }
- } else {
- this.ctx.fillRect(hazard.x, hazard.y, hazard.width, hazard.height);
- }
- });
- // Draw items
- this.items.forEach(item => {
- if (!item.collected) {
- this.ctx.fillStyle = item.color;
- this.ctx.fillRect(item.x, item.y, item.width, item.height);
- // Item glow effect
- this.ctx.shadowColor = item.color;
- this.ctx.shadowBlur = 10;
- this.ctx.fillRect(item.x + 2, item.y + 2, item.width - 4, item.height - 4);
- this.ctx.shadowBlur = 0;
- }
- });
- // Draw flags
- this.flags.forEach(flag => {
- if (!flag.collected) {
- // Flag pole
- this.ctx.fillStyle = '#8b4513';
- this.ctx.fillRect(flag.x + 2, flag.y, 3, flag.height);
- // Flag
- this.ctx.fillStyle = '#00ff00';
- this.ctx.fillRect(flag.x + 5, flag.y, flag.width - 5, flag.height * 0.6);
- // Wave effect
- this.ctx.fillStyle = '#32cd32';
- const wave = Math.sin(Date.now() * 0.01) * 3;
- this.ctx.fillRect(flag.x + 15 + wave, flag.y + 5, 8, flag.height * 0.4);
- }
- });
- // Draw enemies
- this.enemies.forEach(enemy => {
- // Enemy body
- this.ctx.fillStyle = enemy.alertness > 0.5 ? '#ff4444' : enemy.color;
- this.ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
- // Health bar
- const healthRatio = enemy.health / enemy.maxHealth;
- this.ctx.fillStyle = '#ff0000';
- this.ctx.fillRect(enemy.x, enemy.y - 8, enemy.width, 3);
- this.ctx.fillStyle = '#00ff00';
- this.ctx.fillRect(enemy.x, enemy.y - 8, enemy.width * healthRatio, 3);
- // Alert indicator
- if (enemy.alertness > 0.25) {
- this.ctx.fillStyle = '#ffff00';
- this.ctx.fillRect(enemy.x + enemy.width/2 - 1, enemy.y - 15, 2, 2);
- }
- // Enemy type indicator
- this.ctx.fillStyle = '#ffffff';
- this.ctx.font = '8px monospace';
- this.ctx.fillText(enemy.type[0].toUpperCase(), enemy.x + 2, enemy.y - 16);
- });
- // Draw AI test player if testing
- if (this.testingLevel && this.aiTestPlayer) {
- this.ctx.fillStyle = '#ff00ff';
- this.ctx.globalAlpha = 0.7;
- this.ctx.fillRect(this.aiTestPlayer.x, this.aiTestPlayer.y, 20, 30);
- this.ctx.globalAlpha = 1;
- // AI indicator
- this.ctx.fillStyle = '#ffffff';
- this.ctx.font = '10px monospace';
- this.ctx.fillText('AI', this.aiTestPlayer.x, this.aiTestPlayer.y - 5);
- }
- // Draw player
- this.ctx.fillStyle = this.player.color;
- if (this.player.isStealthed) {
- this.ctx.globalAlpha = 0.5;
- }
- this.ctx.fillRect(this.player.x, this.player.y, this.player.width, this.player.height);
- // Player direction indicator
- this.ctx.fillStyle = '#ffffff';
- const eyeX = this.player.x + (this.player.facing > 0 ? 15 : 5);
- this.ctx.fillRect(eyeX, this.player.y + 5, 2, 2);
- // Player health bar
- const playerHealthRatio = this.player.health / this.player.maxHealth;
- this.ctx.fillStyle = '#ff0000';
- this.ctx.fillRect(this.player.x, this.player.y - 8, this.player.width, 3);
- this.ctx.fillStyle = '#00ff00';
- this.ctx.fillRect(this.player.x, this.player.y - 8, this.player.width * playerHealthRatio, 3);
- this.ctx.globalAlpha = 1;
- // Draw particles
- this.particles.forEach(particle => {
- this.ctx.fillStyle = particle.color;
- this.ctx.globalAlpha = particle.opacity;
- this.ctx.fillRect(particle.x, particle.y, 3, 3);
- });
- this.ctx.globalAlpha = 1;
- // Restore context
- this.ctx.restore();
- // Update UI
- document.getElementById('health').textContent = Math.max(0, Math.floor(this.player.health));
- document.getElementById('score').textContent = this.score;
- document.getElementById('level').textContent = this.level;
- // Game over check
- if (this.player.health <= 0) {
- this.ctx.fillStyle = 'rgba(0,0,0,0.8)';
- this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
- this.ctx.fillStyle = '#ff0000';
- this.ctx.font = '48px monospace';
- this.ctx.textAlign = 'center';
- this.ctx.fillText('GAME OVER', this.canvas.width/2, this.canvas.height/2);
- this.ctx.font = '24px monospace';
- this.ctx.fillText(`Final Score: ${this.score}`, this.canvas.width/2, this.canvas.height/2 + 60);
- this.ctx.fillText('Press F5 to restart', this.canvas.width/2, this.canvas.height/2 + 90);
- return;
- }
- }
- gameLoop() {
- const now = performance.now();
- const dt = Math.min((now - this.lastTime) / 1000, 0.05); // Cap at 20fps min
- this.lastTime = now;
- this.update(dt);
- this.render();
- requestAnimationFrame(() => this.gameLoop());
- }
- }
- // Start the game when page loads
- window.addEventListener('load', () => {
- new CreedSimAssassin();
- });
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment