XTaylorSpenceX

Creed Sim Assassin

Sep 17th, 2025
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
HTML 69.31 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>CreedSimAssassin™ v5.0</title>
  7.     <style>
  8.         * {
  9.             margin: 0;
  10.             padding: 0;
  11.             box-sizing: border-box;
  12.         }
  13.  
  14.         body {
  15.             background: linear-gradient(135deg, #2c1810, #4a3426);
  16.             font-family: 'Courier New', monospace;
  17.             overflow: hidden;
  18.             color: #f4e4c1;
  19.         }
  20.  
  21.         #gameContainer {
  22.             position: relative;
  23.             width: 100vw;
  24.             height: 100vh;
  25.             overflow: hidden;
  26.         }
  27.  
  28.         #gameCanvas {
  29.             display: block;
  30.             background: linear-gradient(180deg, #87ceeb 0%, #deb887 70%, #8b4513 100%);
  31.             image-rendering: pixelated;
  32.         }
  33.  
  34.         #ui {
  35.             position: absolute;
  36.             top: 10px;
  37.             left: 10px;
  38.             z-index: 100;
  39.             color: #f4e4c1;
  40.             text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
  41.         }
  42.  
  43.         #controls {
  44.             position: absolute;
  45.             bottom: 10px;
  46.             left: 10px;
  47.             z-index: 100;
  48.             font-size: 12px;
  49.             background: rgba(0,0,0,0.7);
  50.             padding: 10px;
  51.             border-radius: 5px;
  52.             max-width: 600px;
  53.         }
  54.  
  55.         .title {
  56.             position: absolute;
  57.             top: 10px;
  58.             right: 20px;
  59.             font-size: 24px;
  60.             font-weight: bold;
  61.             text-shadow: 3px 3px 6px rgba(0,0,0,0.9);
  62.             color: #d4af37;
  63.         }
  64.  
  65.         #combo-display {
  66.             position: absolute;
  67.             top: 50px;
  68.             right: 20px;
  69.             font-size: 18px;
  70.             color: #ff6b35;
  71.             text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
  72.             white-space: pre-line;
  73.             text-align: right;
  74.         }
  75.  
  76.         .context-menu {
  77.             position: absolute;
  78.             background: rgba(20, 20, 20, 0.95);
  79.             border: 2px solid #d4af37;
  80.             border-radius: 8px;
  81.             padding: 10px;
  82.             z-index: 1000;
  83.             min-width: 200px;
  84.             box-shadow: 0 4px 12px rgba(0,0,0,0.8);
  85.         }
  86.  
  87.         .menu-item {
  88.             padding: 8px 12px;
  89.             cursor: pointer;
  90.             color: #f4e4c1;
  91.             border-radius: 4px;
  92.             margin: 2px 0;
  93.             transition: all 0.2s;
  94.             font-size: 14px;
  95.         }
  96.  
  97.         .menu-item:hover {
  98.             background: #d4af37;
  99.             color: #000;
  100.             transform: translateX(5px);
  101.         }
  102.  
  103.         .menu-title {
  104.             color: #d4af37;
  105.             font-weight: bold;
  106.             text-align: center;
  107.             border-bottom: 1px solid #d4af37;
  108.             padding-bottom: 5px;
  109.             margin-bottom: 8px;
  110.         }
  111.  
  112.         .mode-indicator {
  113.             position: absolute;
  114.             top: 10px;
  115.             left: 200px;
  116.             background: rgba(0,0,0,0.8);
  117.             padding: 5px 10px;
  118.             border-radius: 5px;
  119.             font-size: 14px;
  120.         }
  121.  
  122.         #autoBuildBtn {
  123.             position: absolute;
  124.             top: 100px;
  125.             right: 20px;
  126.             background: #2ecc71;
  127.             color: white;
  128.             border: none;
  129.             padding: 10px 20px;
  130.             border-radius: 5px;
  131.             font-family: 'Courier New', monospace;
  132.             font-size: 14px;
  133.             cursor: pointer;
  134.             transition: all 0.3s;
  135.         }
  136.  
  137.         #autoBuildBtn:hover {
  138.             background: #27ae60;
  139.             transform: translateY(-2px);
  140.         }
  141.  
  142.         #autoBuildBtn:disabled {
  143.             background: #7f8c8d;
  144.             cursor: not-allowed;
  145.         }
  146.  
  147.         .building-indicator {
  148.             position: absolute;
  149.             top: 150px;
  150.             right: 20px;
  151.             background: rgba(0,0,0,0.8);
  152.             padding: 10px;
  153.             border-radius: 5px;
  154.             color: #f39c12;
  155.             display: none;
  156.         }
  157.     </style>
  158. </head>
  159. <body>
  160.     <div id="gameContainer">
  161.         <canvas id="gameCanvas"></canvas>
  162.         <div class="title">CreedSimAssassin™ v5.0</div>
  163.         <div class="mode-indicator" id="modeIndicator">Play Mode</div>
  164.         <button id="autoBuildBtn">Auto-Build Level</button>
  165.         <div class="building-indicator" id="buildingIndicator">
  166.             <div>đź”§ AI Building Level...</div>
  167.             <div id="buildProgress">Analyzing terrain...</div>
  168.         </div>
  169.         <div id="ui">
  170.             <div>Health: <span id="health">100</span></div>
  171.             <div>Score: <span id="score">0</span></div>
  172.             <div>Level: <span id="level">1</span></div>
  173.         </div>
  174.         <div id="combo-display"></div>
  175.         <div id="controls">
  176.             WASD/Arrow Keys: Move | Space: Jump/Wall Jump | E: Assassinate | Q: Stealth Attack | R: Combo Strike<br>
  177.             <strong>Level Editor:</strong> Right-Click: Spawn Enemies | Alt+Right-Click: Platforms & Items | Ctrl+Right-Click: Walls & Hazards<br>
  178.            <strong>Auto-Build:</strong> AI will generate and test levels automatically. Reach the green flag to advance!
  179.        </div>
  180.    </div>
  181.  
  182.    <script>
  183.        class CreedSimAssassin {
  184.            constructor() {
  185.                this.canvas = document.getElementById('gameCanvas');
  186.                 this.ctx = this.canvas.getContext('2d');
  187.                 this.canvas.width = window.innerWidth;
  188.                 this.canvas.height = window.innerHeight;
  189.  
  190.                 this.camera = { x: 0, y: 0 };
  191.                 this.keys = {};
  192.                 this.gameState = 'playing';
  193.                 this.mousePos = { x: 0, y: 0 };
  194.                 this.isAutoBuilding = false;
  195.                 this.buildStage = 0;
  196.                 this.buildTimer = 0;
  197.                 this.aiTestPlayer = null;
  198.                 this.testingLevel = false;
  199.                 this.lastTime = performance.now();
  200.                
  201.                 this.player = {
  202.                     x: 100,
  203.                     y: 400,
  204.                     width: 20,
  205.                     height: 30,
  206.                     vx: 0,
  207.                     vy: 0,
  208.                     onGround: false,
  209.                     onWall: false,
  210.                     wallDirection: 0,
  211.                     health: 100,
  212.                     maxHealth: 100,
  213.                     facing: 1,
  214.                     isStealthed: false,
  215.                     stealthCooldown: 0,
  216.                     comboCount: 0,
  217.                     lastAttackTime: 0,
  218.                     color: '#8b4513',
  219.                     speed: 1,
  220.                     jumpBoost: 1,
  221.                     drunkeness: 0,
  222.                     inSand: false,
  223.                     damageMult: 1,
  224.                     gravityMult: 1,
  225.                     slowFactor: 1
  226.                 };
  227.  
  228.                 this.enemies = [];
  229.                 this.platforms = [];
  230.                 this.walls = [];
  231.                 this.particles = [];
  232.                 this.items = [];
  233.                 this.hazards = [];
  234.                 this.flags = [];
  235.                
  236.                 this.score = 0;
  237.                 this.level = 1;
  238.                 this.levelWidth = 2000;
  239.                
  240.                 this.enemyTypes = {
  241.                     'light': { health: 40, speed: 1.2, damage: 10, color: '#666', size: { w: 16, h: 26 } },
  242.                     'archer': { health: 50, speed: 0.8, damage: 15, color: '#4a90e2', size: { w: 18, h: 28 } },
  243.                     'priest': { health: 60, speed: 1.0, damage: 20, color: '#9b59b6', size: { w: 18, h: 30 } },
  244.                     'paladin': { health: 100, speed: 0.9, damage: 25, color: '#f39c12', size: { w: 22, h: 32 } },
  245.                     'berserker': { health: 80, speed: 1.8, damage: 30, color: '#e74c3c', size: { w: 20, h: 30 } },
  246.                     'brute': { health: 150, speed: 0.6, damage: 40, color: '#2c3e50', size: { w: 26, h: 36 } }
  247.                 };
  248.                
  249.                 this.initializeLevel();
  250.                 this.bindEvents();
  251.                 this.gameLoop();
  252.             }
  253.  
  254.             initializeLevel() {
  255.                 // Create initial platforms
  256.                 this.platforms = [
  257.                     {x: 0, y: 550, width: 200, height: 20, type: 'medium'},
  258.                     {x: 250, y: 480, width: 150, height: 20, type: 'small'},
  259.                     {x: 450, y: 400, width: 120, height: 20, type: 'small'},
  260.                     {x: 620, y: 350, width: 100, height: 20, type: 'small'},
  261.                     {x: 800, y: 420, width: 180, height: 20, type: 'medium'},
  262.                     {x: 1050, y: 300, width: 150, height: 20, type: 'medium'},
  263.                     {x: 1300, y: 450, width: 200, height: 20, type: 'large'}
  264.                 ];
  265.  
  266.                 // Create walls
  267.                 this.walls = [
  268.                     {x: 400, y: 200, width: 20, height: 200, type: 'stone', color: '#8b7355'},
  269.                     {x: 720, y: 150, width: 20, height: 200, type: 'wood', color: '#8b4513'},
  270.                     {x: 1200, y: 100, width: 20, height: 200, type: 'metal', color: '#708090'}
  271.                 ];
  272.  
  273.                 // Create initial enemies with exact y positions
  274.                 const lightTemplate = this.enemyTypes['light'];
  275.                 const archerTemplate = this.enemyTypes['archer'];
  276.                 const paladinTemplate = this.enemyTypes['paladin'];
  277.                 this.enemies = [
  278.                     this.createEnemy('light', 300, this.platforms[1].y - lightTemplate.size.h),
  279.                     this.createEnemy('archer', 500, this.platforms[2].y - archerTemplate.size.h),
  280.                     this.createEnemy('paladin', 850, this.platforms[4].y - paladinTemplate.size.h)
  281.                 ];
  282.  
  283.                 // Add level completion flag
  284.                 this.flags = [{
  285.                     x: 1450,
  286.                     y: 420,
  287.                     width: 20,
  288.                     height: 30,
  289.                     collected: false
  290.                 }];
  291.             }
  292.  
  293.             createEnemy(type, x, y) {
  294.                 const template = this.enemyTypes[type];
  295.                 return {
  296.                     x: x,
  297.                     y: y,
  298.                     width: template.size.w,
  299.                     height: template.size.h,
  300.                     health: template.health,
  301.                     maxHealth: template.health,
  302.                     speed: template.speed,
  303.                     damage: template.damage,
  304.                     color: template.color,
  305.                     vx: 0,
  306.                     vy: 0,
  307.                     onGround: false,
  308.                     onWall: false,
  309.                     wallDirection: 0,
  310.                     facing: Math.random() > 0.5 ? 1 : -1,
  311.                     type: type,
  312.                     alertness: 0,
  313.                     patrolStart: x - 75,
  314.                     patrolEnd: x + 75,
  315.                     attackCooldown: 0,
  316.                     edgeCheckDistance: 10,
  317.                     chaseTime: 0
  318.                 };
  319.             }
  320.  
  321.             bindEvents() {
  322.                 window.addEventListener('keydown', (e) => {
  323.                     this.keys[e.code] = true;
  324.                     this.handleKeyDown(e);
  325.                 });
  326.  
  327.                 window.addEventListener('keyup', (e) => {
  328.                     this.keys[e.code] = false;
  329.                 });
  330.  
  331.                 window.addEventListener('resize', () => {
  332.                     this.canvas.width = window.innerWidth;
  333.                     this.canvas.height = window.innerHeight;
  334.                 });
  335.  
  336.                 // Mouse events for level editor
  337.                 this.canvas.addEventListener('mousemove', (e) => {
  338.                     const rect = this.canvas.getBoundingClientRect();
  339.                     this.mousePos.x = e.clientX - rect.left + this.camera.x;
  340.                     this.mousePos.y = e.clientY - rect.top + this.camera.y;
  341.                 });
  342.  
  343.                 this.canvas.addEventListener('contextmenu', (e) => {
  344.                     e.preventDefault();
  345.                     this.handleRightClick(e);
  346.                 });
  347.  
  348.                 // Auto-build button
  349.                 document.getElementById('autoBuildBtn').addEventListener('click', () => {
  350.                     this.startAutoBuilder();
  351.                 });
  352.  
  353.                 // Close context menus when clicking elsewhere
  354.                 document.addEventListener('click', (e) => {
  355.                     if (!e.target.closest('.context-menu')) {
  356.                         this.closeAllMenus();
  357.                     }
  358.                 });
  359.             }
  360.  
  361.             startAutoBuilder() {
  362.                 if (this.isAutoBuilding) return;
  363.                
  364.                 // Reset player position and health regardless of state
  365.                 const startPlatform = this.platforms.find(p => p.x < 250) || { y: 550 };
  366.                this.player.health = this.player.maxHealth;
  367.                this.player.x = 100;
  368.                this.player.y = startPlatform.y - this.player.height;
  369.                this.player.vx = 0;
  370.                this.player.vy = 0;
  371.                this.player.onGround = true;
  372.                
  373.                this.isAutoBuilding = true;
  374.                this.buildStage = 0;
  375.                this.buildTimer = 0;
  376.                
  377.                document.getElementById('autoBuildBtn').disabled = true;
  378.                document.getElementById('buildingIndicator').style.display = 'block';
  379.                
  380.                // Clear current level (except player spawn area)
  381.                this.platforms = this.platforms.filter(p => p.x < 250);
  382.                this.walls = [];
  383.                this.enemies = [];
  384.                this.items = [];
  385.                this.hazards = [];
  386.                this.flags = [];
  387.                
  388.                this.updateBuildProgress(`Auto-Building Level ${this.level}...`);
  389.            }
  390.  
  391.            updateAutoBuilder(dt) {
  392.                if (!this.isAutoBuilding) return;
  393.                
  394.                this.buildTimer += dt;
  395.                
  396.                switch(this.buildStage) {
  397.                    case 0: // Planning phase
  398.                        if (this.buildTimer > 1) {
  399.                             this.aiPlanLevel();
  400.                             this.buildStage = 1;
  401.                             this.buildTimer = 0;
  402.                         }
  403.                         break;
  404.                        
  405.                     case 1: // Building platforms
  406.                         if (this.buildTimer > 0.33 && this.platforms.length < this.platformCount) {
  407.                            this.aiBuildPlatforms();
  408.                             this.buildTimer = 0;
  409.                         }
  410.                         if (this.buildTimer > 5 || this.platforms.length >= this.platformCount) {
  411.                             this.buildStage = 2;
  412.                             this.buildTimer = 0;
  413.                         }
  414.                         break;
  415.                        
  416.                     case 2: // Adding walls and hazards
  417.                         if (this.buildTimer > 0.4 && (this.walls.length + this.hazards.length) < this.hazardCount) {
  418.                            this.aiBuildWallsHazards();
  419.                             this.buildTimer = 0;
  420.                         }
  421.                         if (this.buildTimer > 3.33 || (this.walls.length + this.hazards.length) >= this.hazardCount) {
  422.                             this.buildStage = 3;
  423.                             this.buildTimer = 0;
  424.                         }
  425.                         break;
  426.                        
  427.                     case 3: // Placing enemies
  428.                         if (this.buildTimer > 0.5 && this.enemies.length < this.enemyCount) {
  429.                            this.aiPlaceEnemies();
  430.                             this.buildTimer = 0;
  431.                         }
  432.                         if (this.buildTimer > 4 || this.enemies.length >= this.enemyCount) {
  433.                             this.buildStage = 4;
  434.                             this.buildTimer = 0;
  435.                         }
  436.                         break;
  437.                        
  438.                     case 4: // Adding items and flag
  439.                         if (this.buildTimer > 0.5 && this.items.length < this.itemCount) {
  440.                            this.aiPlaceItems();
  441.                             this.buildTimer = 0;
  442.                         }
  443.                         if (this.buildTimer > 2.5 || this.items.length >= this.itemCount) {
  444.                             this.aiPlaceFlag();
  445.                             this.buildStage = 5;
  446.                             this.buildTimer = 0;
  447.                         }
  448.                         break;
  449.                        
  450.                     case 5: // Testing level
  451.                         if (this.buildTimer === 0) {
  452.                             this.startLevelTest();
  453.                         }
  454.                         if (this.buildTimer > 5 || this.aiTestComplete()) {
  455.                             this.completeBuild();
  456.                         }
  457.                         break;
  458.                 }
  459.             }
  460.  
  461.             aiPlanLevel() {
  462.                 this.updateBuildProgress(`AI analyzing optimal level structure for Level ${this.level}...`);
  463.                 this.levelDifficulty = Math.min(this.level * 0.3 + 0.7, 2.0);
  464.                 this.platformCount = Math.floor(12 + this.level * 3);
  465.                 this.enemyCount = Math.floor(4 + this.level * 2);
  466.                 this.itemCount = Math.floor(3 + this.level);
  467.                 this.hazardCount = Math.floor(2 + this.level * 0.5);
  468.             }
  469.  
  470.             aiBuildPlatforms() {
  471.                 const x = 300 + Math.random() * (this.levelWidth - 600);
  472.                 const y = 200 + Math.random() * 300;
  473.                 const sizes = ['small', 'medium', 'large'];
  474.                 const dimensions = {
  475.                     small: [80, 15],
  476.                     medium: [150, 20],
  477.                     large: [220, 25]
  478.                 };
  479.                
  480.                 const size = sizes[Math.floor(Math.random() * sizes.length)];
  481.                 const [width, height] = dimensions[size];
  482.                
  483.                 // Check if platform placement is valid (tighter check to avoid overlap)
  484.                 let valid = true;
  485.                 this.platforms.forEach(existing => {
  486.                     if (Math.abs(x - existing.x) < (width + existing.width)/2 + 20 &&
  487.                        Math.abs(y - existing.y) < height + existing.height + 40) {
  488.                        valid = false;
  489.                    }
  490.                });
  491.                
  492.                if (valid) {
  493.                    this.platforms.push({
  494.                        x: x - width/2,
  495.                        y: y,
  496.                        width: width,
  497.                        height: height,
  498.                        type: size
  499.                    });
  500.                    this.updateBuildProgress(`Building platforms... (${this.platforms.length}/${this.platformCount})`);
  501.                }
  502.            }
  503.  
  504.            aiBuildWallsHazards() {
  505.                // AI strategically places walls near platforms
  506.                if (Math.random() < 0.6 && this.platforms.length > 3) {
  507.                     const platform = this.platforms[Math.floor(Math.random() * this.platforms.length)];
  508.                     const wallTypes = ['stone', 'wood', 'metal'];
  509.                     const type = wallTypes[Math.floor(Math.random() * wallTypes.length)];
  510.                     const colors = { stone: '#8b7355', wood: '#8b4513', metal: '#708090' };
  511.                    
  512.                     // Check for overlap
  513.                     let valid = true;
  514.                     this.walls.forEach(w => {
  515.                         if (Math.abs(platform.x + platform.width + 10 - w.x) < 40 && Math.abs(platform.y - 60 - w.y) < 120) {
  516.                            valid = false;
  517.                        }
  518.                    });
  519.                    
  520.                    if (valid) {
  521.                        this.walls.push({
  522.                            x: platform.x + platform.width + 10,
  523.                            y: platform.y - 120,
  524.                            width: 20,
  525.                            height: 120,
  526.                            type: type,
  527.                            color: colors[type]
  528.                        });
  529.                    }
  530.                }
  531.                
  532.                // Add hazards strategically
  533.                if (Math.random() < 0.4) {
  534.                    const hazardTypes = ['spikes', 'bomb', 'sand', 'ladder'];
  535.                    const type = hazardTypes[Math.floor(Math.random() * hazardTypes.length)];
  536.                    this.aiPlaceHazardType(type);
  537.                }
  538.                
  539.                this.updateBuildProgress('AI placing strategic obstacles...');
  540.            }
  541.  
  542.            aiPlaceHazardType(type) {
  543.                const hazardData = {
  544.                    spikes: { width: 30, height: 10, color: '#666', damage: 2.5 },
  545.                    bomb: { width: 20, height: 20, color: '#ff0000', damage: 9.5 },
  546.                    sand: { width: 60, height: 20, color: '#daa520', slowFactor: 0.5 },
  547.                    ladder: { width: 15, height: 80, color: '#8b4513', climbable: true }
  548.                };
  549.  
  550.                const data = hazardData[type];
  551.                if (!data) return;
  552.                
  553.                const x = 400 + Math.random() * (this.levelWidth - 800);
  554.                const y = 300 + Math.random() * 200;
  555.                
  556.                // Check no overlap
  557.                let valid = true;
  558.                this.hazards.forEach(h => {
  559.                     if (Math.abs(x - h.x) < (data.width + h.width)/2 + 10 && Math.abs(y - h.y) < (data.height + h.height)/2 + 10) {
  560.                        valid = false;
  561.                    }
  562.                });
  563.                
  564.                if (valid) {
  565.                    this.hazards.push({
  566.                        x: x,
  567.                        y: y,
  568.                        width: data.width,
  569.                        height: data.height,
  570.                        type: type,
  571.                        color: data.color,
  572.                        ...data
  573.                    });
  574.                }
  575.            }
  576.  
  577.            aiPlaceEnemies() {
  578.                if (this.platforms.length < 3) return;
  579.                
  580.                const platformIndex = 1 + Math.floor(Math.random() * (this.platforms.length - 1));
  581.                const platform = this.platforms[platformIndex];
  582.                const enemyTypes = Object.keys(this.enemyTypes);
  583.                const type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
  584.                
  585.                const template = this.enemyTypes[type];
  586.                const spawnX = platform.x + Math.random() * (platform.width - template.size.w);
  587.                const spawnY = platform.y - template.size.h;
  588.                
  589.                const enemy = this.createEnemy(type, spawnX, spawnY);
  590.                
  591.                // Jostle position to ensure on platform
  592.                enemy.y -= 1; // Slight lift to prevent initial fall-through
  593.                
  594.                this.enemies.push(enemy);
  595.                this.updateBuildProgress(`Deploying enemies... (${this.enemies.length}/${this.enemyCount})`);
  596.            }
  597.  
  598.            aiPlaceItems() {
  599.                const itemTypes = ['health', 'speed', 'jump', 'haste'];
  600.                const type = itemTypes[Math.floor(Math.random() * itemTypes.length)];
  601.                const colors = {
  602.                    health: '#ff4444',
  603.                    speed: '#44ff44',
  604.                    jump: '#4444ff',
  605.                    haste: '#ffff44'
  606.                };
  607.                
  608.                if (this.platforms.length > 2) {
  609.                     const platform = this.platforms[Math.floor(Math.random() * this.platforms.length)];
  610.                     this.items.push({
  611.                         x: platform.x + Math.random() * platform.width,
  612.                         y: platform.y - 20,
  613.                         width: 15,
  614.                         height: 15,
  615.                         type: type,
  616.                         color: colors[type],
  617.                         collected: false
  618.                     });
  619.                 }
  620.                
  621.                 this.updateBuildProgress(`Distributing power-ups... (${this.items.length}/${this.itemCount})`);
  622.             }
  623.  
  624.             aiPlaceFlag() {
  625.                 // Place flag at the end of the level
  626.                 const endX = this.levelWidth - 200;
  627.                 let flagY = 450;
  628.                
  629.                 // Find a suitable platform near the end
  630.                 let suitablePlatform = null;
  631.                 this.platforms.forEach(platform => {
  632.                     if (platform.x > endX - 200 && platform.x < endX + 200) {
  633.                        if (!suitablePlatform || Math.abs(platform.x - endX) < Math.abs(suitablePlatform.x - endX)) {
  634.                            suitablePlatform = platform;
  635.                         }
  636.                     }
  637.                 });
  638.                
  639.                 if (suitablePlatform) {
  640.                     flagY = suitablePlatform.y - 30;
  641.                 }
  642.                
  643.                 this.flags = [{
  644.                     x: endX,
  645.                     y: flagY,
  646.                     width: 20,
  647.                     height: 30,
  648.                     collected: false
  649.                 }];
  650.                
  651.                 this.updateBuildProgress('Placing victory flag...');
  652.             }
  653.  
  654.             startLevelTest() {
  655.                 this.updateBuildProgress('AI testing level playability...');
  656.                 this.testingLevel = true;
  657.                
  658.                 // Create AI test player
  659.                 this.aiTestPlayer = {
  660.                     x: 100,
  661.                     y: 400,
  662.                     vx: 2,
  663.                     vy: 0,
  664.                     onGround: false,
  665.                     jumpCooldown: 0,
  666.                     testTime: 0
  667.                 };
  668.             }
  669.  
  670.             aiTestComplete() {
  671.                 if (!this.testingLevel) return false;
  672.                
  673.                 this.aiTestPlayer.testTime++;
  674.                
  675.                 // Simple AI movement for testing
  676.                 this.aiTestPlayer.x += this.aiTestPlayer.vx;
  677.                 this.aiTestPlayer.y += this.aiTestPlayer.vy;
  678.                 this.aiTestPlayer.vy += 0.6;
  679.                
  680.                 // Simple jump logic
  681.                 if (this.aiTestPlayer.jumpCooldown > 0) {
  682.                     this.aiTestPlayer.jumpCooldown--;
  683.                 }
  684.                
  685.                 if (this.aiTestPlayer.onGround && this.aiTestPlayer.jumpCooldown === 0) {
  686.                    this.aiTestPlayer.vy = -10;
  687.                     this.aiTestPlayer.jumpCooldown = 30;
  688.                     this.aiTestPlayer.onGround = false;
  689.                 }
  690.                
  691.                 // Check if AI reached flag or time limit
  692.                 const flagReached = this.flags.some(flag =>
  693.                     Math.abs(this.aiTestPlayer.x - flag.x) < 50);
  694.                
  695.                return flagReached || this.aiTestPlayer.testTime > 600;
  696.             }
  697.  
  698.             completeBuild() {
  699.                 this.isAutoBuilding = false;
  700.                 this.testingLevel = false;
  701.                 this.aiTestPlayer = null;
  702.                
  703.                 document.getElementById('autoBuildBtn').disabled = false;
  704.                 document.getElementById('buildingIndicator').style.display = 'none';
  705.                 document.getElementById('modeIndicator').textContent = `Level ${this.level} Ready!`;
  706.                
  707.                 setTimeout(() => {
  708.                     document.getElementById('modeIndicator').textContent = 'Play Mode';
  709.                 }, 3000);
  710.             }
  711.  
  712.             updateBuildProgress(text) {
  713.                 document.getElementById('buildProgress').textContent = text;
  714.             }
  715.  
  716.             handleRightClick(e) {
  717.                 if (this.isAutoBuilding) return;
  718.                
  719.                 const rect = this.canvas.getBoundingClientRect();
  720.                 const screenX = e.clientX - rect.left;
  721.                 const screenY = e.clientY - rect.top;
  722.  
  723.                 if (e.ctrlKey) {
  724.                     this.showHazardMenu(screenX, screenY);
  725.                 } else if (e.altKey) {
  726.                     this.showPlatformMenu(screenX, screenY);
  727.                 } else {
  728.                     this.showEnemyMenu(screenX, screenY);
  729.                 }
  730.             }
  731.  
  732.             showEnemyMenu(x, y) {
  733.                 this.closeAllMenus();
  734.                
  735.                 const menu = document.createElement('div');
  736.                 menu.className = 'context-menu';
  737.                 menu.id = 'enemyMenu';
  738.                 menu.style.left = x + 'px';
  739.                 menu.style.top = y + 'px';
  740.  
  741.                 const title = document.createElement('div');
  742.                 title.className = 'menu-title';
  743.                 title.textContent = 'Spawn Enemy';
  744.                 menu.appendChild(title);
  745.  
  746.                 const enemies = [
  747.                     { name: 'Light Infantry', type: 'light', desc: 'Fast, weak guard' },
  748.                     { name: 'Archer', type: 'archer', desc: 'Ranged attacker' },
  749.                     { name: 'Priest', type: 'priest', desc: 'Magic abilities' },
  750.                     { name: 'Paladin', type: 'paladin', desc: 'Heavily armored' },
  751.                     { name: 'Berserker', type: 'berserker', desc: 'Fast and aggressive' },
  752.                     { name: 'Heavy Brute', type: 'brute', desc: 'Slow but powerful' }
  753.                 ];
  754.  
  755.                 enemies.forEach(enemy => {
  756.                     const item = document.createElement('div');
  757.                     item.className = 'menu-item';
  758.                     item.innerHTML = `<strong>${enemy.name}</strong><br><small>${enemy.desc}</small>`;
  759.                     item.addEventListener('click', (e) => {
  760.                         e.stopPropagation();
  761.                         this.spawnEnemy(enemy.type);
  762.                         this.closeAllMenus();
  763.                     });
  764.                     menu.appendChild(item);
  765.                 });
  766.  
  767.                 document.body.appendChild(menu);
  768.             }
  769.  
  770.             showPlatformMenu(x, y) {
  771.                 this.closeAllMenus();
  772.                
  773.                 const menu = document.createElement('div');
  774.                 menu.className = 'context-menu';
  775.                 menu.id = 'platformMenu';
  776.                 menu.style.left = x + 'px';
  777.                 menu.style.top = y + 'px';
  778.  
  779.                 const title = document.createElement('div');
  780.                 title.className = 'menu-title';
  781.                 title.textContent = 'Platforms & Items';
  782.                 menu.appendChild(title);
  783.  
  784.                 const items = [
  785.                     { name: 'Small Platform', action: () => this.placePlatform('small', 80, 15) },
  786.                     { name: 'Medium Platform', action: () => this.placePlatform('medium', 150, 20) },
  787.                     { name: 'Large Platform', action: () => this.placePlatform('large', 220, 25) },
  788.                     { name: 'Health Pack', action: () => this.placeItem('health') },
  789.                     { name: 'Speed Herb', action: () => this.placeItem('speed') },
  790.                     { name: 'Jump Spice', action: () => this.placeItem('jump') },
  791.                     { name: 'Haste Potion', action: () => this.placeItem('haste') },
  792.                     { name: 'Drunk Elixir', action: () => this.placeItem('drunk') }
  793.                 ];
  794.  
  795.                 items.forEach(item => {
  796.                     const element = document.createElement('div');
  797.                     element.className = 'menu-item';
  798.                     element.textContent = item.name;
  799.                     element.addEventListener('click', (e) => {
  800.                         e.stopPropagation();
  801.                         item.action();
  802.                         this.closeAllMenus();
  803.                     });
  804.                     menu.appendChild(element);
  805.                 });
  806.  
  807.                 document.body.appendChild(menu);
  808.             }
  809.  
  810.             showHazardMenu(x, y) {
  811.                 this.closeAllMenus();
  812.                
  813.                 const menu = document.createElement('div');
  814.                 menu.className = 'context-menu';
  815.                 menu.id = 'hazardMenu';
  816.                 menu.style.left = x + 'px';
  817.                 menu.style.top = y + 'px';
  818.  
  819.                 const title = document.createElement('div');
  820.                 title.className = 'menu-title';
  821.                 title.textContent = 'Walls & Hazards';
  822.                 menu.appendChild(title);
  823.  
  824.                 const items = [
  825.                     { name: 'Stone Wall', action: () => this.placeWall('stone') },
  826.                     { name: 'Wood Wall', action: () => this.placeWall('wood') },
  827.                     { name: 'Metal Wall', action: () => this.placeWall('metal') },
  828.                     { name: 'Spike Trap', action: () => this.placeHazard('spikes') },
  829.                     { name: 'Bomb', action: () => this.placeHazard('bomb') },
  830.                     { name: 'Sand Pit', action: () => this.placeHazard('sand') },
  831.                     { name: 'Ladder', action: () => this.placeHazard('ladder') }
  832.                 ];
  833.  
  834.                 items.forEach(item => {
  835.                     const element = document.createElement('div');
  836.                     element.className = 'menu-item';
  837.                     element.textContent = item.name;
  838.                     element.addEventListener('click', (e) => {
  839.                         e.stopPropagation();
  840.                         item.action();
  841.                         this.closeAllMenus();
  842.                     });
  843.                     menu.appendChild(element);
  844.                 });
  845.  
  846.                 document.body.appendChild(menu);
  847.             }
  848.  
  849.             closeAllMenus() {
  850.                 const menus = document.querySelectorAll('.context-menu');
  851.                 menus.forEach(menu => menu.remove());
  852.             }
  853.  
  854.             spawnEnemy(type) {
  855.                 const enemy = this.createEnemy(type, this.mousePos.x, this.mousePos.y);
  856.                 this.enemies.push(enemy);
  857.                 document.getElementById('modeIndicator').textContent = `Spawned: ${type}`;
  858.             }
  859.  
  860.             placePlatform(size, width, height) {
  861.                 this.platforms.push({
  862.                     x: this.mousePos.x - width/2,
  863.                     y: this.mousePos.y,
  864.                     width: width,
  865.                     height: height,
  866.                     type: size
  867.                 });
  868.                 document.getElementById('modeIndicator').textContent = `Placed: ${size} platform`;
  869.             }
  870.  
  871.             placeWall(type) {
  872.                 const colors = { stone: '#8b7355', wood: '#8b4513', metal: '#708090' };
  873.                 this.walls.push({
  874.                     x: this.mousePos.x,
  875.                     y: this.mousePos.y - 100,
  876.                     width: 20,
  877.                     height: 120,
  878.                     type: type,
  879.                     color: colors[type]
  880.                 });
  881.                 document.getElementById('modeIndicator').textContent = `Placed: ${type} wall`;
  882.             }
  883.  
  884.             placeItem(type) {
  885.                 const colors = {
  886.                     health: '#ff4444',
  887.                     speed: '#44ff44',
  888.                     jump: '#4444ff',
  889.                     haste: '#ffff44',
  890.                     drunk: '#ff44ff'
  891.                 };
  892.                
  893.                 this.items.push({
  894.                     x: this.mousePos.x,
  895.                     y: this.mousePos.y,
  896.                     width: 15,
  897.                     height: 15,
  898.                     type: type,
  899.                     color: colors[type],
  900.                     collected: false
  901.                 });
  902.                 document.getElementById('modeIndicator').textContent = `Placed: ${type} item`;
  903.             }
  904.  
  905.             placeHazard(type) {
  906.                 const hazardData = {
  907.                     spikes: { width: 30, height: 10, color: '#666', damage: 2.5 },
  908.                     bomb: { width: 20, height: 20, color: '#ff0000', damage: 9.5 },
  909.                     sand: { width: 60, height: 20, color: '#daa520', slowFactor: 0.5 },
  910.                     ladder: { width: 15, height: 80, color: '#8b4513', climbable: true }
  911.                 };
  912.  
  913.                 const data = hazardData[type];
  914.                 this.hazards.push({
  915.                     x: this.mousePos.x,
  916.                     y: this.mousePos.y,
  917.                     width: data.width,
  918.                     height: data.height,
  919.                     type: type,
  920.                     color: data.color,
  921.                     ...data
  922.                 });
  923.                 document.getElementById('modeIndicator').textContent = `Placed: ${type}`;
  924.             }
  925.  
  926.             handleKeyDown(e) {
  927.                 switch(e.code) {
  928.                     case 'KeyE':
  929.                         this.performAssassination();
  930.                         break;
  931.                     case 'KeyQ':
  932.                         this.performStealthAttack();
  933.                         break;
  934.                     case 'KeyR':
  935.                         this.performComboStrike();
  936.                         break;
  937.                 }
  938.             }
  939.  
  940.             update(dt) {
  941.                 if (this.isAutoBuilding) {
  942.                     this.updateAutoBuilder(dt);
  943.                 }
  944.                
  945.                 this.updatePlayer(dt);
  946.                 this.updateEnemies(dt);
  947.                 this.updateParticles(dt);
  948.                 this.updateCamera(dt);
  949.                 this.checkCollisions();
  950.                 this.checkItemCollisions();
  951.                 this.checkHazardCollisions(dt);
  952.                 this.checkFlagCollisions();
  953.                
  954.                 if (this.player.stealthCooldown > 0) {
  955.                     this.player.stealthCooldown -= dt;
  956.                     if (this.player.stealthCooldown < 0) this.player.stealthCooldown = 0;
  957.                }
  958.  
  959.                if (this.player.drunkeness > 0) {
  960.                     this.player.drunkeness -= dt;
  961.                     if (this.player.drunkeness < 0) this.player.drunkeness = 0;
  962.                }
  963.  
  964.                // Reset combo if too much time passes
  965.                if (Date.now() - this.player.lastAttackTime > 3000) {
  966.                     this.player.comboCount = 0;
  967.                 }
  968.             }
  969.  
  970.             checkFlagCollisions() {
  971.                 this.flags.forEach(flag => {
  972.                     if (!flag.collected && this.isColliding(this.player, flag)) {
  973.                        flag.collected = true;
  974.                         const completedLevel = this.level;
  975.                         this.level++;
  976.                         this.score += 500;
  977.                        
  978.                         this.createParticles(flag.x, flag.y, '#00ff00', 10);
  979.                         this.updateComboDisplay(`Level ${completedLevel} Completed\nLevel ${this.level} Auto-Building...`);
  980.                        
  981.                         // Auto-trigger level builder for next level
  982.                         setTimeout(() => {
  983.                             this.startAutoBuilder();
  984.                         }, 2000);
  985.                     }
  986.                 });
  987.             }
  988.  
  989.             updatePlayer(dt) {
  990.                 const player = this.player;
  991.                 const baseSpeed = 240;
  992.                 const gravity = 800;
  993.                 const jumpPower = 600;
  994.                 const friction = 10;
  995.  
  996.                 player.inSand = false;
  997.                 player.damageMult = 1;
  998.                 player.gravityMult = 1;
  999.                 player.slowFactor = 1;
  1000.  
  1001.                 // Apply drunkeness effect
  1002.                 let drunkModifier = 1;
  1003.                 if (player.drunkeness > 0) {
  1004.                     drunkModifier = 0.7 + Math.sin(Date.now() * 0.01) * 0.3;
  1005.                 }
  1006.  
  1007.                 let targetVx = 0;
  1008.                 if (this.keys['KeyA'] || this.keys['ArrowLeft']) {
  1009.                     targetVx = -baseSpeed * player.speed * drunkModifier * player.slowFactor;
  1010.                     player.facing = -1;
  1011.                 } else if (this.keys['KeyD'] || this.keys['ArrowRight']) {
  1012.                     targetVx = baseSpeed * player.speed * drunkModifier * player.slowFactor;
  1013.                     player.facing = 1;
  1014.                 }
  1015.  
  1016.                 // Accelerate towards target
  1017.                 player.vx += (targetVx - player.vx) * friction * dt;
  1018.  
  1019.                 // Jumping and wall jumping
  1020.                 if ((this.keys['Space'] || this.keys['KeyW'] || this.keys['ArrowUp']) && (player.onGround || player.onWall)) {
  1021.                    player.vy = -jumpPower * player.jumpBoost;
  1022.                     if (player.onWall) {
  1023.                         player.vx = player.wallDirection * baseSpeed * 1.5;
  1024.                     }
  1025.                     player.onGround = false;
  1026.                     player.onWall = false;
  1027.                 }
  1028.  
  1029.                 // Apply gravity
  1030.                 player.vy += gravity * player.gravityMult * dt;
  1031.                
  1032.                 // Terminal velocity
  1033.                 if (player.vy > 900) player.vy = 900;
  1034.  
  1035.                 // Update position
  1036.                 player.x += player.vx * dt;
  1037.                 player.y += player.vy * dt;
  1038.  
  1039.                 // Keep player in bounds
  1040.                 if (player.x < 0) player.x = 0;
  1041.                if (player.x > this.levelWidth - player.width) player.x = this.levelWidth - player.width;
  1042.                 if (player.y > this.canvas.height) {
  1043.                     player.health -= 20;
  1044.                     player.x = 100;
  1045.                     player.y = 400;
  1046.                     player.vx = 0;
  1047.                     player.vy = 0;
  1048.                 }
  1049.             }
  1050.  
  1051.             updateEnemies(dt) {
  1052.                 const gravity = 800;
  1053.                 const climbSpeed = 150;
  1054.                 const ledgeLeapBoost = 1.3;
  1055.  
  1056.                 this.enemies.forEach(enemy => {
  1057.                     if (enemy.attackCooldown > 0) enemy.attackCooldown -= dt;
  1058.  
  1059.                     enemy.onGround = false;
  1060.                     enemy.onWall = false;
  1061.  
  1062.                     // Enhanced AI based on enemy type to set vx
  1063.                     const dx = this.player.x - enemy.x;
  1064.                     const dy = this.player.y - enemy.y;
  1065.                     const distToPlayer = Math.sqrt(dx * dx + dy * dy);
  1066.  
  1067.                     // Edge detection - check if enemy is about to fall off platform
  1068.                     let canContinue = this.checkEnemyCanContinue(enemy);
  1069.  
  1070.                     if (distToPlayer < 200 && !this.player.isStealthed) {
  1071.                        enemy.alertness = Math.min(1, enemy.alertness + dt / 2);
  1072.                        enemy.chaseTime += dt;
  1073.                    } else {
  1074.                        enemy.alertness = Math.max(0, enemy.alertness - dt / 5);
  1075.                        enemy.chaseTime = Math.max(0, enemy.chaseTime - dt);
  1076.                    }
  1077.  
  1078.                    let targetVx = 0;
  1079.                    let chasing = enemy.alertness > 0.25;
  1080.  
  1081.                     if (chasing) {
  1082.                         const chargeDir = dx > 0 ? 1 : -1;
  1083.                         enemy.facing = chargeDir;
  1084.  
  1085.                         if (enemy.type === 'archer' && distToPlayer < 50) {
  1086.                            // Archers flee close range
  1087.                            const fleeDir = -chargeDir;
  1088.                             targetVx = fleeDir * enemy.speed * 180;
  1089.                         } else if (enemy.type === 'berserker') {
  1090.                             // Berserkers charge relentlessly
  1091.                             targetVx = chargeDir * enemy.speed * 300;
  1092.                         } else {
  1093.                             targetVx = chargeDir * enemy.speed * 240;
  1094.                         }
  1095.  
  1096.                         // Check edge while chasing
  1097.                         if (!canContinue && distToPlayer > 30) {
  1098.                            targetVx = 0;
  1099.                             enemy.facing = -enemy.facing;
  1100.                         }
  1101.                     } else {
  1102.                         // Patrol
  1103.                         if (enemy.x <= enemy.patrolStart) {
  1104.                            enemy.facing = 1;
  1105.                        } else if (enemy.x >= enemy.patrolEnd) {
  1106.                             enemy.facing = -1;
  1107.                         }
  1108.                         targetVx = enemy.facing * enemy.speed * 240;
  1109.  
  1110.                         if (!canContinue) {
  1111.                             enemy.facing = -enemy.facing;
  1112.                             targetVx = enemy.facing * enemy.speed * 240;
  1113.                         }
  1114.  
  1115.                         // If far from patrol after chase
  1116.                         if (enemy.alertness <= 0 && Math.abs(enemy.x - (enemy.patrolStart + 75)) > 150) {
  1117.                             enemy.patrolStart = enemy.x - 75;
  1118.                             enemy.patrolEnd = enemy.x + 75;
  1119.                         }
  1120.                     }
  1121.  
  1122.                     // Accelerate
  1123.                     enemy.vx += (targetVx - enemy.vx) * 10 * dt;
  1124.  
  1125.                     // Apply gravity
  1126.                     enemy.vy += gravity * dt;
  1127.                     if (enemy.vy > 900) enemy.vy = 900;
  1128.  
  1129.                     // Update position
  1130.                     enemy.x += enemy.vx * dt;
  1131.                     enemy.y += enemy.vy * dt;
  1132.  
  1133.                     // Check collisions for this enemy
  1134.                     this.checkEnemyCollisions(enemy, dt, dx, dy, chasing);
  1135.  
  1136.                     // Ladder climbing
  1137.                     this.hazards.forEach(hazard => {
  1138.                         if (hazard.type === 'ladder' && this.isColliding(enemy, hazard) && chasing) {
  1139.                            if (dy < -20) {
  1140.                                enemy.vy = -climbSpeed;
  1141.                             } else if (dy > 20) {
  1142.                                 enemy.vy = climbSpeed;
  1143.                             }
  1144.                         }
  1145.                     });
  1146.  
  1147.                     // Attack player if close enough
  1148.                     if (distToPlayer < 30 && enemy.attackCooldown <= 0) {
  1149.                        this.player.health -= enemy.damage;
  1150.                        enemy.attackCooldown = 1;
  1151.                        this.createParticles(this.player.x, this.player.y, '#ff0000', 5);
  1152.                    }
  1153.                });
  1154.  
  1155.                // Remove dead enemies
  1156.                this.enemies = this.enemies.filter(enemy => enemy.health > 0);
  1157.             }
  1158.  
  1159.             checkEnemyCanContinue(enemy) {
  1160.                 // Check if there's ground ahead of the enemy
  1161.                 const checkDistance = enemy.edgeCheckDistance + Math.abs(enemy.vx) / 60;
  1162.                 const checkX = enemy.x + (enemy.facing > 0 ? enemy.width / 2 + checkDistance : -checkDistance - enemy.width / 2);
  1163.                 const checkY = enemy.y + enemy.height + 5;
  1164.  
  1165.                 // Check if any platform supports this position
  1166.                 return this.platforms.some(platform =>
  1167.                     checkX >= platform.x && checkX <= platform.x + platform.width &&
  1168.                    checkY >= platform.y - 20 && checkY <= platform.y + platform.height + 20
  1169.                );
  1170.             }
  1171.  
  1172.             checkEnemyCollisions(enemy, dt, dx, dy, chasing) {
  1173.                 const climbSpeed = 150;
  1174.                 const ledgeLeapBoost = 1.3;
  1175.  
  1176.                 // Platform collisions with penetration resolve
  1177.                 this.platforms.forEach(platform => {
  1178.                     if (this.isColliding(enemy, platform)) {
  1179.                         if (enemy.vy >= 0) {
  1180.                             const overlap = (enemy.y + enemy.height) - platform.y;
  1181.                             if (overlap > 0) {
  1182.                                 enemy.y -= overlap;
  1183.                                 enemy.vy = 0;
  1184.                                 enemy.onGround = true;
  1185.                             }
  1186.                         }
  1187.                     }
  1188.                 });
  1189.  
  1190.                 // Wall collisions (reverse or climb)
  1191.                 this.walls.forEach(wall => {
  1192.                     if (this.isColliding(enemy, wall)) {
  1193.                         // Resolve horizontal penetration
  1194.                         if (enemy.vx > 0) {
  1195.                             enemy.x = wall.x - enemy.width;
  1196.                             enemy.wallDirection = -1;
  1197.                         } else if (enemy.vx < 0) {
  1198.                            enemy.x = wall.x + wall.width;
  1199.                            enemy.wallDirection = 1;
  1200.                        }
  1201.                        enemy.onWall = true;
  1202.  
  1203.                        // All enemies can climb if chasing or player above/below
  1204.                        if (chasing || Math.abs(dy) > 20) {
  1205.                             if (dy < 0) {
  1206.                                enemy.vy = -climbSpeed;
  1207.                            } else if (dy > 0) {
  1208.                                 enemy.vy = climbSpeed;
  1209.                             }
  1210.                             enemy.vx = enemy.wallDirection * 50; // Slight horizontal during climb
  1211.                         } else {
  1212.                             enemy.facing = -enemy.facing;
  1213.                             enemy.vx = enemy.facing * enemy.speed * 240;
  1214.                         }
  1215.  
  1216.                         // Check if at top of wall for ledge leap
  1217.                         const atTop = enemy.y + enemy.height <= wall.y + 10 && enemy.vy < 0;
  1218.                        if (atTop && chasing) {
  1219.                            enemy.vy -= 200 * dt; // Extra upward boost
  1220.                            enemy.vx *= ledgeLeapBoost;
  1221.                            enemy.onWall = false;
  1222.                        }
  1223.                    }
  1224.                });
  1225.            }
  1226.  
  1227.            checkItemCollisions() {
  1228.                this.items.forEach(item => {
  1229.                     if (!item.collected && this.isColliding(this.player, item)) {
  1230.                        item.collected = true;
  1231.                        
  1232.                         switch(item.type) {
  1233.                             case 'health':
  1234.                                 this.player.health = Math.min(this.player.maxHealth, this.player.health + 30);
  1235.                                 break;
  1236.                             case 'speed':
  1237.                                 this.player.speed = 1.5;
  1238.                                 setTimeout(() => this.player.speed = 1, 10000);
  1239.                                 break;
  1240.                             case 'jump':
  1241.                                 this.player.jumpBoost = 1.4;
  1242.                                 setTimeout(() => this.player.jumpBoost = 1, 8000);
  1243.                                 break;
  1244.                             case 'haste':
  1245.                                 this.player.speed = 1.8;
  1246.                                 this.player.jumpBoost = 1.3;
  1247.                                 setTimeout(() => {
  1248.                                     this.player.speed = 1;
  1249.                                     this.player.jumpBoost = 1;
  1250.                                 }, 6000);
  1251.                                 break;
  1252.                             case 'drunk':
  1253.                                 this.player.drunkeness = 10; // 10 seconds
  1254.                                 break;
  1255.                         }
  1256.                        
  1257.                         this.createParticles(item.x, item.y, item.color, 6);
  1258.                         this.updateComboDisplay(`Got ${item.type}!`);
  1259.                     }
  1260.                 });
  1261.                
  1262.                 // Remove collected items
  1263.                 this.items = this.items.filter(item => !item.collected);
  1264.             }
  1265.  
  1266.             checkHazardCollisions(dt) {
  1267.                 this.player.inSand = false;
  1268.  
  1269.                 this.hazards.forEach(hazard => {
  1270.                     if (this.isColliding(this.player, hazard)) {
  1271.                         switch(hazard.type) {
  1272.                             case 'spikes':
  1273.                                 this.player.health -= hazard.damage * dt;
  1274.                                 this.player.vx += (Math.random() - 0.5) * 200 * dt; // Veering knockback
  1275.                                 this.player.vy -= 100 * dt; // Slight bounce
  1276.                                 this.createParticles(this.player.x, this.player.y, '#ff0000', 3);
  1277.                                 break;
  1278.                             case 'bomb':
  1279.                                 const bombDamage = Math.floor(Math.random() * 4 + 8);
  1280.                                 this.player.health -= bombDamage;
  1281.                                 this.createParticles(hazard.x, hazard.y, '#ff8800', 12);
  1282.                                 // Remove bomb after explosion
  1283.                                 this.hazards = this.hazards.filter(h => h !== hazard);
  1284.                                 break;
  1285.                             case 'sand':
  1286.                                 this.player.inSand = true;
  1287.                                 this.player.slowFactor = hazard.slowFactor;
  1288.                                 this.player.damageMult = 0.5;
  1289.                                 this.player.gravityMult = 0.5;
  1290.                                 // Fall slower already via gravityMult
  1291.                                 break;
  1292.                             case 'ladder':
  1293.                                 // Allow climbing
  1294.                                 if (this.keys['KeyW'] || this.keys['ArrowUp']) {
  1295.                                     this.player.vy = -150;
  1296.                                     this.player.onGround = true;
  1297.                                 } else if (this.keys['KeyS'] || this.keys['ArrowDown']) {
  1298.                                     this.player.vy = 150;
  1299.                                 }
  1300.                                 break;
  1301.                         }
  1302.                     }
  1303.                 });
  1304.             }
  1305.  
  1306.             updateParticles(dt) {
  1307.                 this.particles = this.particles.filter(particle => {
  1308.                     particle.x += particle.vx * dt;
  1309.                     particle.y += particle.vy * dt;
  1310.                     particle.life -= dt;
  1311.                     particle.opacity = particle.life / particle.maxLife;
  1312.                     return particle.life > 0;
  1313.                 });
  1314.             }
  1315.  
  1316.             updateCamera(dt) {
  1317.                 // Follow player with some smoothing
  1318.                 const targetX = this.player.x - this.canvas.width / 2;
  1319.                 this.camera.x += (targetX - this.camera.x) * 5 * dt;
  1320.                
  1321.                 // Keep camera in bounds
  1322.                 if (this.camera.x < 0) this.camera.x = 0;
  1323.                if (this.camera.x > this.levelWidth - this.canvas.width) {
  1324.                     this.camera.x = this.levelWidth - this.canvas.width;
  1325.                 }
  1326.             }
  1327.  
  1328.             checkCollisions() {
  1329.                 this.player.onGround = false;
  1330.                 this.player.onWall = false;
  1331.  
  1332.                 // Platform collisions with resolve
  1333.                 this.platforms.forEach(platform => {
  1334.                     if (this.isColliding(this.player, platform)) {
  1335.                         if (this.player.vy >= 0) {
  1336.                             const overlap = (this.player.y + this.player.height) - platform.y;
  1337.                             if (overlap > 0) {
  1338.                                 this.player.y -= overlap;
  1339.                                 this.player.vy = 0;
  1340.                                 this.player.onGround = true;
  1341.                             }
  1342.                         }
  1343.                     }
  1344.                 });
  1345.  
  1346.                 // Wall collisions with resolve
  1347.                 this.walls.forEach(wall => {
  1348.                     if (this.isColliding(this.player, wall)) {
  1349.                         if (this.player.vx > 0) {
  1350.                             const overlap = (this.player.x + this.player.width) - wall.x;
  1351.                             this.player.x -= overlap;
  1352.                             this.player.onWall = true;
  1353.                             this.player.wallDirection = -1;
  1354.                         } else if (this.player.vx < 0) {
  1355.                            const overlap = wall.x + wall.width - this.player.x;
  1356.                            this.player.x += overlap;
  1357.                            this.player.onWall = true;
  1358.                            this.player.wallDirection = 1;
  1359.                        }
  1360.                        this.player.vy *= 0.7;
  1361.                    }
  1362.                });
  1363.            }
  1364.  
  1365.            isColliding(rect1, rect2) {
  1366.                return rect1.x < rect2.x + rect2.width &&
  1367.                       rect1.x + rect1.width > rect2.x &&
  1368.                       rect1.y < rect2.y + rect2.height &&
  1369.                       rect1.y + rect1.height > rect2.y;
  1370.             }
  1371.  
  1372.             performAssassination() {
  1373.                 const assassinRange = 40;
  1374.                 let targetEnemy = null;
  1375.  
  1376.                 this.enemies.forEach(enemy => {
  1377.                     const distance = Math.sqrt(
  1378.                         Math.pow(this.player.x - enemy.x, 2) +
  1379.                         Math.pow(this.player.y - enemy.y, 2)
  1380.                     );
  1381.                    
  1382.                     if (distance < assassinRange) {
  1383.                        targetEnemy = enemy;
  1384.                    }
  1385.                });
  1386.  
  1387.                if (targetEnemy) {
  1388.                    // Instant kill, but reduce if in sand (half health instead?)
  1389.                    if (this.player.inSand) {
  1390.                        targetEnemy.health *= 0.5;
  1391.                    } else {
  1392.                        targetEnemy.health = 0;
  1393.                    }
  1394.                    this.score += 100 + (this.player.comboCount * 25);
  1395.                    this.player.comboCount++;
  1396.                    this.player.lastAttackTime = Date.now();
  1397.                    
  1398.                    // Create blood particles
  1399.                    this.createParticles(targetEnemy.x, targetEnemy.y, '#ff0000', 8);
  1400.                    
  1401.                    this.updateComboDisplay('ASSASSINATION!');
  1402.                }
  1403.            }
  1404.  
  1405.            performStealthAttack() {
  1406.                if (this.player.stealthCooldown > 0) return;
  1407.  
  1408.                 this.player.isStealthed = true;
  1409.                 this.player.stealthCooldown = 3; // 3 seconds
  1410.                 this.player.color = '#4a4a4a'; // Darker color when stealthed
  1411.                
  1412.                 const stealthRange = 35;
  1413.                 this.enemies.forEach(enemy => {
  1414.                     const distance = Math.sqrt(
  1415.                         Math.pow(this.player.x - enemy.x, 2) +
  1416.                         Math.pow(this.player.y - enemy.y, 2)
  1417.                     );
  1418.                    
  1419.                     if (distance < stealthRange) {
  1420.                        const damage = 40 * this.player.damageMult;
  1421.                        enemy.health -= damage;
  1422.                        enemy.alertness = 0; // Reset alertness
  1423.                        this.score += 75 + (this.player.comboCount * 20);
  1424.                        this.player.comboCount++;
  1425.                        this.player.lastAttackTime = Date.now();
  1426.                        
  1427.                        this.createParticles(enemy.x, enemy.y, '#9b59b6', 5);
  1428.                    }
  1429.                });
  1430.  
  1431.                setTimeout(() => {
  1432.                     this.player.isStealthed = false;
  1433.                     this.player.color = '#8b4513';
  1434.                 }, 1000);
  1435.  
  1436.                 this.updateComboDisplay('STEALTH STRIKE!');
  1437.             }
  1438.  
  1439.             performComboStrike() {
  1440.                 const comboRange = 50;
  1441.                 let hitCount = 0;
  1442.  
  1443.                 this.enemies.forEach(enemy => {
  1444.                     const distance = Math.sqrt(
  1445.                         Math.pow(this.player.x - enemy.x, 2) +
  1446.                         Math.pow(this.player.y - enemy.y, 2)
  1447.                     );
  1448.                    
  1449.                     if (distance < comboRange) {
  1450.                        const baseDamage = 25 + (this.player.comboCount * 10);
  1451.                        const damage = baseDamage * this.player.damageMult;
  1452.                        enemy.health -= damage;
  1453.                        hitCount++;
  1454.                        
  1455.                        this.createParticles(enemy.x, enemy.y, '#ffd700', 6);
  1456.                    }
  1457.                });
  1458.  
  1459.                if (hitCount > 0) {
  1460.                     this.player.comboCount += hitCount;
  1461.                     this.player.lastAttackTime = Date.now();
  1462.                     this.score += hitCount * 30;
  1463.                    
  1464.                     this.updateComboDisplay(`COMBO x${this.player.comboCount}!`);
  1465.                 }
  1466.             }
  1467.  
  1468.             createParticles(x, y, color, count) {
  1469.                 for (let i = 0; i < count; i++) {
  1470.                    this.particles.push({
  1471.                        x: x + Math.random() * 20 - 10,
  1472.                        y: y + Math.random() * 20 - 10,
  1473.                        vx: Math.random() * 360 - 180,
  1474.                        vy: Math.random() * 360 - 180,
  1475.                        color: color,
  1476.                        life: 0.5,
  1477.                        maxLife: 0.5,
  1478.                        opacity: 1
  1479.                    });
  1480.                }
  1481.            }
  1482.  
  1483.            updateComboDisplay(text) {
  1484.                const display = document.getElementById('combo-display');
  1485.                display.innerHTML = text;
  1486.                display.style.opacity = '1';
  1487.                
  1488.                setTimeout(() => {
  1489.                     display.style.opacity = '0';
  1490.                 }, 2000);
  1491.             }
  1492.  
  1493.             render() {
  1494.                 // Clear canvas
  1495.                 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
  1496.                
  1497.                 // Save context for camera transform
  1498.                 this.ctx.save();
  1499.                 this.ctx.translate(-this.camera.x, -this.camera.y);
  1500.  
  1501.                 // Draw platforms
  1502.                 this.ctx.fillStyle = '#654321';
  1503.                 this.platforms.forEach(platform => {
  1504.                     this.ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
  1505.                     // Platform type indicator
  1506.                     this.ctx.fillStyle = '#8b7355';
  1507.                     this.ctx.fillRect(platform.x, platform.y - 2, platform.width, 2);
  1508.                     this.ctx.fillStyle = '#654321';
  1509.                 });
  1510.  
  1511.                 // Draw walls
  1512.                 this.walls.forEach(wall => {
  1513.                     this.ctx.fillStyle = wall.color || '#8b7355';
  1514.                     this.ctx.fillRect(wall.x, wall.y, wall.width, wall.height);
  1515.                 });
  1516.  
  1517.                 // Draw hazards
  1518.                 this.hazards.forEach(hazard => {
  1519.                     this.ctx.fillStyle = hazard.color;
  1520.                     if (hazard.type === 'ladder') {
  1521.                         // Draw ladder rungs
  1522.                         this.ctx.fillRect(hazard.x, hazard.y, hazard.width, hazard.height);
  1523.                         for (let i = 0; i < hazard.height; i += 15) {
  1524.                            this.ctx.fillStyle = '#654321';
  1525.                            this.ctx.fillRect(hazard.x, hazard.y + i, hazard.width, 2);
  1526.                            this.ctx.fillStyle = hazard.color;
  1527.                        }
  1528.                    } else {
  1529.                        this.ctx.fillRect(hazard.x, hazard.y, hazard.width, hazard.height);
  1530.                    }
  1531.                });
  1532.  
  1533.                // Draw items
  1534.                this.items.forEach(item => {
  1535.                     if (!item.collected) {
  1536.                         this.ctx.fillStyle = item.color;
  1537.                         this.ctx.fillRect(item.x, item.y, item.width, item.height);
  1538.                         // Item glow effect
  1539.                         this.ctx.shadowColor = item.color;
  1540.                         this.ctx.shadowBlur = 10;
  1541.                         this.ctx.fillRect(item.x + 2, item.y + 2, item.width - 4, item.height - 4);
  1542.                         this.ctx.shadowBlur = 0;
  1543.                     }
  1544.                 });
  1545.  
  1546.                 // Draw flags
  1547.                 this.flags.forEach(flag => {
  1548.                     if (!flag.collected) {
  1549.                         // Flag pole
  1550.                         this.ctx.fillStyle = '#8b4513';
  1551.                         this.ctx.fillRect(flag.x + 2, flag.y, 3, flag.height);
  1552.                        
  1553.                         // Flag
  1554.                         this.ctx.fillStyle = '#00ff00';
  1555.                         this.ctx.fillRect(flag.x + 5, flag.y, flag.width - 5, flag.height * 0.6);
  1556.                        
  1557.                         // Wave effect
  1558.                         this.ctx.fillStyle = '#32cd32';
  1559.                         const wave = Math.sin(Date.now() * 0.01) * 3;
  1560.                         this.ctx.fillRect(flag.x + 15 + wave, flag.y + 5, 8, flag.height * 0.4);
  1561.                     }
  1562.                 });
  1563.  
  1564.                 // Draw enemies
  1565.                 this.enemies.forEach(enemy => {
  1566.                     // Enemy body
  1567.                     this.ctx.fillStyle = enemy.alertness > 0.5 ? '#ff4444' : enemy.color;
  1568.                     this.ctx.fillRect(enemy.x, enemy.y, enemy.width, enemy.height);
  1569.                    
  1570.                     // Health bar
  1571.                     const healthRatio = enemy.health / enemy.maxHealth;
  1572.                     this.ctx.fillStyle = '#ff0000';
  1573.                     this.ctx.fillRect(enemy.x, enemy.y - 8, enemy.width, 3);
  1574.                     this.ctx.fillStyle = '#00ff00';
  1575.                     this.ctx.fillRect(enemy.x, enemy.y - 8, enemy.width * healthRatio, 3);
  1576.                    
  1577.                     // Alert indicator
  1578.                     if (enemy.alertness > 0.25) {
  1579.                         this.ctx.fillStyle = '#ffff00';
  1580.                         this.ctx.fillRect(enemy.x + enemy.width/2 - 1, enemy.y - 15, 2, 2);
  1581.                     }
  1582.  
  1583.                     // Enemy type indicator
  1584.                     this.ctx.fillStyle = '#ffffff';
  1585.                     this.ctx.font = '8px monospace';
  1586.                     this.ctx.fillText(enemy.type[0].toUpperCase(), enemy.x + 2, enemy.y - 16);
  1587.                 });
  1588.  
  1589.                 // Draw AI test player if testing
  1590.                 if (this.testingLevel && this.aiTestPlayer) {
  1591.                    this.ctx.fillStyle = '#ff00ff';
  1592.                     this.ctx.globalAlpha = 0.7;
  1593.                     this.ctx.fillRect(this.aiTestPlayer.x, this.aiTestPlayer.y, 20, 30);
  1594.                     this.ctx.globalAlpha = 1;
  1595.                    
  1596.                     // AI indicator
  1597.                     this.ctx.fillStyle = '#ffffff';
  1598.                     this.ctx.font = '10px monospace';
  1599.                     this.ctx.fillText('AI', this.aiTestPlayer.x, this.aiTestPlayer.y - 5);
  1600.                 }
  1601.  
  1602.                 // Draw player
  1603.                 this.ctx.fillStyle = this.player.color;
  1604.                 if (this.player.isStealthed) {
  1605.                     this.ctx.globalAlpha = 0.5;
  1606.                 }
  1607.                 this.ctx.fillRect(this.player.x, this.player.y, this.player.width, this.player.height);
  1608.                
  1609.                 // Player direction indicator
  1610.                 this.ctx.fillStyle = '#ffffff';
  1611.                 const eyeX = this.player.x + (this.player.facing > 0 ? 15 : 5);
  1612.                 this.ctx.fillRect(eyeX, this.player.y + 5, 2, 2);
  1613.                
  1614.                 // Player health bar
  1615.                 const playerHealthRatio = this.player.health / this.player.maxHealth;
  1616.                 this.ctx.fillStyle = '#ff0000';
  1617.                 this.ctx.fillRect(this.player.x, this.player.y - 8, this.player.width, 3);
  1618.                 this.ctx.fillStyle = '#00ff00';
  1619.                 this.ctx.fillRect(this.player.x, this.player.y - 8, this.player.width * playerHealthRatio, 3);
  1620.                
  1621.                 this.ctx.globalAlpha = 1;
  1622.  
  1623.                 // Draw particles
  1624.                 this.particles.forEach(particle => {
  1625.                     this.ctx.fillStyle = particle.color;
  1626.                     this.ctx.globalAlpha = particle.opacity;
  1627.                     this.ctx.fillRect(particle.x, particle.y, 3, 3);
  1628.                 });
  1629.                
  1630.                 this.ctx.globalAlpha = 1;
  1631.  
  1632.                 // Restore context
  1633.                 this.ctx.restore();
  1634.  
  1635.                 // Update UI
  1636.                 document.getElementById('health').textContent = Math.max(0, Math.floor(this.player.health));
  1637.                 document.getElementById('score').textContent = this.score;
  1638.                 document.getElementById('level').textContent = this.level;
  1639.  
  1640.                 // Game over check
  1641.                 if (this.player.health <= 0) {
  1642.                    this.ctx.fillStyle = 'rgba(0,0,0,0.8)';
  1643.                    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
  1644.                    this.ctx.fillStyle = '#ff0000';
  1645.                    this.ctx.font = '48px monospace';
  1646.                    this.ctx.textAlign = 'center';
  1647.                    this.ctx.fillText('GAME OVER', this.canvas.width/2, this.canvas.height/2);
  1648.                    this.ctx.font = '24px monospace';
  1649.                    this.ctx.fillText(`Final Score: ${this.score}`, this.canvas.width/2, this.canvas.height/2 + 60);
  1650.                    this.ctx.fillText('Press F5 to restart', this.canvas.width/2, this.canvas.height/2 + 90);
  1651.                    return;
  1652.                }
  1653.            }
  1654.  
  1655.            gameLoop() {
  1656.                const now = performance.now();
  1657.                const dt = Math.min((now - this.lastTime) / 1000, 0.05); // Cap at 20fps min
  1658.                this.lastTime = now;
  1659.  
  1660.                this.update(dt);
  1661.                this.render();
  1662.                requestAnimationFrame(() => this.gameLoop());
  1663.             }
  1664.         }
  1665.  
  1666.         // Start the game when page loads
  1667.         window.addEventListener('load', () => {
  1668.             new CreedSimAssassin();
  1669.         });
  1670.     </script>
  1671. </body>
  1672. </html>
  1673.  
Advertisement
Add Comment
Please, Sign In to add comment