User prompt
Please fix the bug: 'TypeError: null is not an object (evaluating 'this.chargeEffect.alpha = 0.5 + chargeProgress * 0.5')' in or related to this line: 'this.chargeEffect.alpha = 0.5 + chargeProgress * 0.5; // Fade in' Line Number: 1885
User prompt
Laser should charge for duration, fire for duration and then clean up all laser segments.
User prompt
After firing is complete. Clean up all laser segments.
User prompt
Remove slide duration from laser mechanic call in blaster class
User prompt
Remove slide duration from laser mechanic.
User prompt
Remove sliding from laser mechanic
User prompt
Remove start sliding and update sliding classes from laser mechanic.
User prompt
Remove the sliding portion of the laser mechanic. Keep the charging and firing and just clean up all laser segments after firing is complete.
User prompt
Add a check in laser mechanic update firing method to slide all laser segments off screen even if full length is not reached when adding segments.
User prompt
It might be freezing if the Blaster is destroyed while the laser segments are still stacking. Need to add a check if the laser doesn’t reach full length for some reason that the laser segments start sliding after
User prompt
That didn’t fix the problem. Lasers are still remaining on screen if Blaster is destroyed during firing phase.
User prompt
If a Blaster gets killed while the laser is still firing the laser can get orphaned on screen and not slide off. Please fix this.
User prompt
Make these changes to start firing laser mechanic: var segment = new LaserBeamSegment(); segment.parentBlaster = this.origin; // Store reference to Blaster segment.x = this.origin.x; segment.y = this.origin.y + (this.origin.height * 0.1); segment.rotation = this.rotation; game.addChild(segment); this.segments.push(segment); };
User prompt
Replace update firing laser mechanic with this: LaserMechanic.prototype.updateFiring = function () { if (!this.isFiring) return; var fireProgress = (LK.ticks - this.fireStartTime) / this.fireDuration; if (fireProgress <= 1) { if (this.segments.length < 20 && LK.ticks % 2 === 0) { var lastSegment = this.segments[this.segments.length - 1]; var segment = new LaserBeamSegment(); segment.parentBlaster = this.origin; // Store reference to Blaster segment.x = lastSegment.x - Math.sin(this.rotation) * segment.height; segment.y = lastSegment.y + Math.cos(this.rotation) * segment.height; segment.rotation = this.rotation; game.addChild(segment); this.segments.push(segment); } } else { this.isFiring = false; this.startSliding(); } };
User prompt
Make updates to Blaster class using this code block as appropriate: var LaserBeamSegment = Container.expand(function() { var self = Container.call(this); self.parentBlaster = null; // Will store reference to Blaster self.isSliding = false; self.slideSpeed = null; var laserGraphics = self.attachAsset('LaserBeam', { anchorX: 0.5, anchorY: 0, scaleX: 1, scaleY: 1 }); self.update = function() { // If parent Blaster is gone and we're not sliding, start sliding if (!self.parentBlaster && !self.isSliding) { self.isSliding = true; // Get rotation from graphics if needed self.slideSpeed = { x: -Math.sin(self.rotation) * 20, y: Math.cos(self.rotation) * 20 }; } // Handle sliding movement if (self.isSliding) { self.x += self.slideSpeed.x; self.y += self.slideSpeed.y; // Self-destroy if off screen if (self.y < -100 || self.y > 2148 || self.x < -100 || self.x > 2148) { game.removeChild(self); return true; // Signal destruction } }
User prompt
Add a timer to laser segments during update firing class. If the laser segments have not started sliding after 1 second, start all sliding.
User prompt
Add a check during the firing phase of the laser mechanic, if the origin becomes null then set all laser segments to start sliding.
User prompt
Please use this code block to make the appropriate changes to the Blaster class: // In Blaster's destroy/destruction code if (this.laserMechanic) { if (this.laserMechanic.isFiring && this.laserMechanic.segments) { // Force firing phase to end and start sliding this.laserMechanic.isFiring = false; this.laserMechanic.startSliding(); } this.laserMechanic.origin = null; // Detach from Blaster }
User prompt
Add to later mechanic update sliding class: LaserMechanic.prototype.updateSliding = function () { if (!this.isSliding || !this.segments) return; // Remove destroyed segments for (var i = this.segments.length - 1; i >= 0; i--) { var segment = this.segments[i]; if (segment.update()) { // If returns true, it was destroyed this.segments.splice(i, 1); } } // If all segments gone, mark complete if (this.segments.length === 0) { this.isSliding = false; this.attackComplete = true; } };
User prompt
Update laser mechanic start sliding function with this code: LaserMechanic.prototype.startSliding = function () { this.isSliding = true; this.slideStartTime = LK.ticks; // Set all segments to sliding mode if (this.segments) { for (var i = 0; i < this.segments.length; i++) { var segment = this.segments[i]; segment.isSliding = true; segment.slideSpeed = { x: -Math.sin(this.rotation) * 20, y: Math.cos(this.rotation) * 20 }; } } };
User prompt
Add to update part of laser segment class: // If sliding, move regardless of parent laser mechanic if (self.isSliding) { self.x += self.slideSpeed.x; self.y += self.slideSpeed.y; // Self-destroy if off screen if (self.y < -100 || self.y > 2148 || self.x < -100 || self.x > 2148) { game.removeChild(self); return true; // Signal destruction } } return false; // Not destroyed };
User prompt
Add these to the laser beam segment class: self.isSliding = false; self.slideSpeed = null;
User prompt
Update laser segment class using this code block: var LaserBeamSegment = Container.expand(function() { var self = Container.call(this); self.isSliding = false; self.slideSpeed = null; var laserGraphics = self.attachAsset('LaserBeam', { anchorX: 0.5, anchorY: 0, scaleX: 1, scaleY: 1 }); self.update = function() { // Handle collision if (CollisionManager.checkCollision(self, hero)) { // ... existing collision code ... } // Handle clone collisions for (var i = soldierClones.length - 1; i >= 0; i--) { // ... existing clone collision code ... } // If sliding, move regardless of parent laser mechanic if (self.isSliding) { self.x += self.slideSpeed.x; self.y += self.slideSpeed.y; // Self-destroy if off screen if (self.y < -100 || self.y > 2148 || self.x < -100 || self.x > 2148) { game.removeChild(self); return true; // Signal destruction } } return false; // Not destroyed }; return self; });
User prompt
Update Blaster class as appropriate using this code block: // In Blaster class (where destruction occurs) if (this.laserMechanic && this.laserMechanic.segments) { // Force all existing segments to start sliding for (var i = 0; i < this.laserMechanic.segments.length; i++) { var segment = this.laserMechanic.segments[i]; segment.isSliding = true; segment.slideSpeed = { x: -Math.sin(this.laserMechanic.rotation) * 20, y: Math.cos(this.laserMechanic.rotation) * 20 }; } }
User prompt
Use tv effect off instead of destroying clones in the laser array collision check with clones
/**** * Classes ****/ // Blaster class representing a shooting enemy var Blaster = Container.expand(function () { var self = Container.call(this); var blasterGraphics = self.attachAsset('Blaster', { anchorX: 0.5, anchorY: 0.5 }); self.HitPoints = 10; self.speed = 3; // Laser attack properties self.laserMechanic = new LaserMechanic(self, 60, 30, 39); // charge, fire, slide durations self.nextAttackTime = LK.ticks + randomRange(120, 240); // 2-4 seconds initial delay function randomRange(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } self.update = function () { // Update laser mechanic self.laserMechanic.update(); // Check if it's time for a laser attack if (LK.ticks >= self.nextAttackTime && !self.laserMechanic.isCharging && !self.laserMechanic.isFiring && !self.laserMechanic.isSliding && !self.laserMechanic.attackComplete) { self.laserMechanic.startCharge(); } // Handle movement based on state if (self.laserMechanic.isCharging) { // Only rotate to track player during charge var directionX = hero.x - self.x; var directionY = hero.y - self.y; blasterGraphics.rotation = Math.atan2(directionY, directionX) - Math.PI / 2; } else if (self.laserMechanic.isFiring || self.laserMechanic.isSliding) { // No movement during firing and sliding return; } else { // Normal movement when not attacking // Track player position var directionX = hero.x - self.x; var directionY = hero.y - self.y; var distance = Math.sqrt(directionX * directionX + directionY * directionY); if (distance > 0) { self.x += directionX / distance * self.speed; self.y += directionY / distance * self.speed; blasterGraphics.rotation = Math.atan2(directionY, directionX) - Math.PI / 2; } // Set next attack time after sliding is done if (self.laserMechanic.isAttackComplete()) { self.laserMechanic.resetAttack(); self.nextAttackTime = LK.ticks + randomRange(300, 480); } } // Destroy if out of screen if (CollisionManager.checkCollision(self, hero)) { // Handle collision with hero if (hero.shielded) { hero.shieldLevel -= 1; if (hero.shieldLevel <= 0) { hero.shielded = false; if (hero.shieldGraphics) { hero.shieldGraphics.destroy(); hero.shieldGraphics = null; } } else { hero.shieldGraphics.tint = 0xFFFFFF; if (hero.shieldLevel === 2) { hero.shieldGraphics.tint = 0xFFFF00; } else if (hero.shieldLevel === 1) { hero.shieldGraphics.tint = 0xFFFFFF; } } // Destroy the enemy generateEnemyPieces(self, BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']); self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } else { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } if (self.HitPoints <= 0) { // Create blaster pieces upon destruction generateEnemyPieces(self, BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']); // Check if there's an active laser beam if (self.laserMechanic.laserBeam) { // Detach and slide the laser beam self.laserMechanic.detachBeam(); } self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } else if (self.y > 2732) { self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); difficultyManager.onEnemyDestroyed(); } } }; }); // BlasterPiece class for blaster destruction effect var BlasterPiece = Container.expand(function (assetType) { var self = Container.call(this); var pieceGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5, alpha: 1 // Ensure pieces are fully visible on spawn }); self.speedX = (Math.random() - 0.5) * 12; // Random horizontal speed to move outwards self.speedY = (Math.random() - 0.5) * 12; // Random vertical speed to move outwards self.alphaDecay = 0.02; // Rate at which the piece fades out self.update = function () { self.x += self.speedX; self.y += self.speedY; pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation pieceGraphics.alpha -= self.alphaDecay; if (pieceGraphics.alpha <= 0) { self.destroy(); } }; }); var BombBlast = Container.expand(function () { var self = Container.call(this); var blastGraphics = self.attachAsset('BombBlast', { anchorX: 0.5, anchorY: 0.5 }); self.scaleX = 0.1; self.scaleY = 0.1; self.update = function () { self.scaleX += 0.5; self.scaleY += 0.5; blastGraphics.scale.x = self.scaleX; blastGraphics.scale.y = self.scaleY; if (self.scaleX >= 10) { self.destroy(); } }; }); // Bruiser class representing a tougher enemy var Bruiser = Container.expand(function () { var self = Container.call(this); var bruiserGraphics = self.attachAsset('Bruiser', { anchorX: 0.5, anchorY: 0.5 }); self.HitPoints = 20; // Double the hit points for Bruiser self.speed = 5; self.update = function () { // Implement mechanical stomping movement if (LK.ticks % 80 < 20) { bruiserGraphics.scale.x = 1.1; bruiserGraphics.scale.y = 0.9; bruiserGraphics.rotation = 0.1; // Tilt right } else if (LK.ticks % 80 < 40) { bruiserGraphics.scale.x = 1.0; bruiserGraphics.scale.y = 1.0; bruiserGraphics.rotation = 0; } else if (LK.ticks % 80 < 60) { bruiserGraphics.scale.x = 1.1; bruiserGraphics.scale.y = 0.9; bruiserGraphics.rotation = -0.1; // Tilt left } else { bruiserGraphics.scale.x = 1.0; bruiserGraphics.scale.y = 1.0; bruiserGraphics.rotation = 0; } // Move downwards only during tilt phases if (LK.ticks % 80 < 20 || LK.ticks % 80 >= 40 && LK.ticks % 80 < 60) { // Check for nearby enemies and adjust path to avoid collisions var nearbyEnemy = enemies.find(function (enemy) { return enemy !== self && Math.abs(enemy.x - self.x) < 100 && Math.abs(enemy.y - self.y) < 100; }); if (nearbyEnemy) { self.x += (self.x < nearbyEnemy.x ? -1 : 1) * self.speed; // Move away from the nearby enemy } else { self.y += self.speed; } } if (CollisionManager.checkCollision(self, hero)) { // Handle collision with hero if (hero.shielded) { hero.shieldLevel -= 1; if (hero.shieldLevel <= 0) { hero.shielded = false; if (hero.shieldGraphics) { hero.shieldGraphics.destroy(); hero.shieldGraphics = null; } } else { hero.shieldGraphics.tint = 0xFFFFFF; if (hero.shieldLevel === 2) { hero.shieldGraphics.tint = 0xFFFF00; } else if (hero.shieldLevel === 1) { hero.shieldGraphics.tint = 0xFFFFFF; } } // Destroy the enemy generateEnemyPieces(self, BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']); self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } else { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } if (self.HitPoints <= 0) { // Create bruiser pieces upon destruction generateEnemyPieces(self, BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']); self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } else if (self.y > 2732) { self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); difficultyManager.onEnemyDestroyed(); } } }; }); // BruiserPiece class for bruiser destruction effect var BruiserPiece = Container.expand(function (assetType) { var self = Container.call(this); var pieceGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); self.speedX = -Math.random() * 6 + 3; // Switch direction of horizontal speed self.speedY = Math.random() * 4 - 2; // Random vertical speed self.alphaDecay = 0.02; // Rate at which the piece fades out self.update = function () { self.x += self.speedX; self.y += self.speedY; pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation pieceGraphics.alpha -= self.alphaDecay; if (pieceGraphics.alpha <= 0) { self.destroy(); } }; }); var DifficultyManager = Container.expand(function () { var self = Container.call(this); self.currentDifficulty = 1; self.baseDifficulty = 1; self.activeEnemies = 0; self.maxEnemies = 30; self.roadStartX = 2048 * 0.21; self.roadWidth = 2048 * 0.55; // Wave patterns for different difficulty levels self.wavePatterns = { 1: { drones: Math.ceil(6 * 1.3), bruisers: 0, blasters: 1, raiders: 0 }, 2: { drones: Math.ceil(8 * 1.3), bruisers: 0, blasters: 0, raiders: 1 }, 3: { drones: Math.ceil(8 * 1.3), bruisers: 1, blasters: 0, raiders: 2 }, 4: { drones: Math.ceil(10 * 1.3), bruisers: 1, blasters: 1, raiders: 2 }, 5: { drones: Math.ceil(12 * 1.3), bruisers: 1, blasters: 1, raiders: 3 }, 6: { drones: Math.ceil(14 * 1.3), bruisers: 2, blasters: 1, raiders: 3 }, 7: { drones: Math.ceil(16 * 1.3), bruisers: 2, blasters: 2, raiders: 4 } }; // Spawn quota to track needed spawns self.spawnQuota = { drones: 0, bruisers: 0, blasters: 0, raiders: 0 }; self.update = function () { var timeScale = Math.floor(LK.ticks / 1800); self.baseDifficulty = 1 + timeScale * 0.2; if (LK.ticks % 300 === 0) { var powerLevel = self.calculatePlayerPower(); self.updateDifficulty(powerLevel); } // Fast spawn check for drones if (LK.ticks % 45 === 0 && self.canSpawnEnemy()) { // Twice as fast if (self.spawnQuota.drones > 0) { self.spawnEnemy('drone'); } } // Regular spawn check for other enemies if (LK.ticks % 60 === 0 && self.canSpawnEnemy()) { var enemyType = self.getNextEnemyType(); if (enemyType && enemyType !== 'drone') { self.spawnEnemy(enemyType); } } }; self.updateDifficulty = function (powerLevel) { var totalPower = powerLevel * self.baseDifficulty; // Match to wave pattern keys 1-7 if (totalPower >= 10) { self.currentDifficulty = 7; } else if (totalPower >= 8) { self.currentDifficulty = 6; } else if (totalPower >= 6) { self.currentDifficulty = 5; } else if (totalPower >= 4.5) { self.currentDifficulty = 4; } else if (totalPower >= 3) { self.currentDifficulty = 3; } else if (totalPower >= 2) { self.currentDifficulty = 2; } else { self.currentDifficulty = 1; } self.updateSpawnQuota(); }; self.getSpawnX = function () { return self.roadStartX + Math.random() * self.roadWidth; }; self.getEnemyStrength = function () { // Remove enemy HP scaling with difficulty return 1; }; self.updateSpawnQuota = function () { var pattern = self.wavePatterns[self.currentDifficulty] || self.wavePatterns[6]; // Default to highest if overflow self.spawnQuota.drones = pattern.drones; self.spawnQuota.bruisers = pattern.bruisers; self.spawnQuota.blasters = pattern.blasters; self.spawnQuota.raiders = pattern.raiders; }; this.calculatePlayerPower = function () { var power = 1; // Base power level // Weapon power contribution (max level is 3 now) if (powerUpManager && powerUpManager.currentPowerLevel) { power += (powerUpManager.currentPowerLevel - 1) * 0.5; // Reduced from 0.8 } // Clone contribution (max 4 clones) if (soldierClones && soldierClones.length) { power += soldierClones.length * 0.4; // Reduced from 0.6 } // Drone contribution (max 3 drones) if (playerDrones && playerDrones.length) { power += playerDrones.length * 0.4; // Reduced from 0.6 } return power; }; self.canSpawnEnemy = function () { return self.activeEnemies < self.maxEnemies; }; self.spawnEnemy = function (enemyType) { if (!self.canSpawnEnemy()) { return; } var enemy; switch (enemyType) { case 'drone': var redDroneChance = Math.min(self.currentDifficulty * 0.1, 0.3); if (Math.random() < redDroneChance) { enemy = new RedDrone(); } else { enemy = new Drone(); } break; case 'bruiser': enemy = new Bruiser(); break; case 'blaster': enemy = new Blaster(); break; case 'raider': enemy = new Raider(); var spawnSide = Math.random() < 0.5; enemy.x = spawnSide ? -100 : 2148; enemy.y = Math.random() * (2732 * 0.3); enemy.spawnSide = spawnSide ? 'left' : 'right'; // Tell Raider which side it spawned from break; default: return; } // Increase enemy HP based on difficulty enemy.HitPoints *= self.getEnemyStrength(); // Only set default spawn position for non-raiders if (enemyType !== 'raider') { enemy.x = self.getSpawnX(); enemy.y = -enemy.height; } enemies.push(enemy); game.addChild(enemy); self.activeEnemies++; }; self.getNextEnemyType = function () { // Get all available enemy types with remaining quota var availableTypes = []; if (self.spawnQuota.drones > 0) { availableTypes.push('drone'); } if (self.spawnQuota.bruisers > 0) { availableTypes.push('bruiser'); } if (self.spawnQuota.raiders > 0) { availableTypes.push('raider'); } if (self.spawnQuota.blasters > 0) { availableTypes.push('blaster'); } if (availableTypes.length === 0) { return null; } // Randomly select from available types var selectedType = availableTypes[Math.floor(Math.random() * availableTypes.length)]; // Decrease the appropriate quota self.spawnQuota[selectedType + 's']--; return selectedType; }; self.onEnemyDestroyed = function () { self.activeEnemies--; if (self.spawnQuota.drones === 0 && self.spawnQuota.bruisers === 0 && self.spawnQuota.blasters === 0 && self.spawnQuota.raiders === 0) { self.updateSpawnQuota(); } }; }); // Enemy class representing the enemy robots var Drone = Container.expand(function () { var self = Container.call(this); var droneGraphics = self.attachAsset('Drone', { anchorX: 0.5, anchorY: 0.5 }); self.HitPoints = 1; self.randomOffset = Math.random() * 100; // Random timing for each drone self.speed = 2.75; self.update = function () { // Add robotic hover effects // Check for nearby enemies and adjust path to avoid collisions var nearbyEnemy = enemies.find(function (enemy) { return enemy !== self && Math.abs(enemy.x - self.x) < 100 && Math.abs(enemy.y - self.y) < 100; }); if (nearbyEnemy) { self.x += (self.x < nearbyEnemy.x ? -1 : 1) * self.speed; // Move away from the nearby enemy } else { self.y += self.speed * (Math.random() > 0.1 ? 1 : 0); // Brief pauses in downward movement } self.x += Math.sin(LK.ticks / 10 + self.randomOffset) * 2; // Small jerky side-to-side movements if (Math.random() > 0.95) { self.x += Math.random() * 4 - 2; // Quick position corrections } // Pulse effect on hit if (self.pulseEffect) { droneGraphics.scale.x = 1.2; droneGraphics.scale.y = 1.2; self.pulseEffect = false; } else { droneGraphics.scale.x = 1.0; droneGraphics.scale.y = 1.0; } if (CollisionManager.checkCollision(self, hero)) { // Handle collision with hero if (hero.shielded) { hero.shieldLevel -= 1; if (hero.shieldLevel <= 0) { hero.shielded = false; if (hero.shieldGraphics) { hero.shieldGraphics.destroy(); hero.shieldGraphics = null; } } else { hero.shieldGraphics.tint = 0xFFFFFF; if (hero.shieldLevel === 2) { hero.shieldGraphics.tint = 0xFFFF00; } else if (hero.shieldLevel === 1) { hero.shieldGraphics.tint = 0xFFFFFF; } } // Destroy the enemy generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']); self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } else { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } if (self.HitPoints <= 0) { // Create drone pieces upon destruction generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']); self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } else if (self.y > 2732) { self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); difficultyManager.onEnemyDestroyed(); } } }; }); // DronePiece class for drone destruction effect var DronePiece = Container.expand(function (assetType) { var self = Container.call(this); var pieceGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); self.speedX = Math.random() * 6 - 3; // Increased random horizontal speed self.speedY = Math.random() * 4 - 2; // Random vertical speed self.alphaDecay = 0.02; // Rate at which the piece fades out self.update = function () { self.x += self.speedX; self.y += self.speedY; pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation pieceGraphics.alpha -= self.alphaDecay; if (pieceGraphics.alpha <= 0) { self.destroy(); } }; }); // Dust class for dust particles var Dust = Container.expand(function () { var self = Container.call(this); var dustGraphics = self.attachAsset('Dust', { anchorX: 0.5, anchorY: 0.5 }); dustGraphics.alpha = 0.75; self.speed = Math.random() * 3 + 1; self.rotationSpeed = Math.random() * 0.02 - 0.01; // Random rotation speed between -0.01 and 0.01 self.direction = Math.random() * Math.PI * 0.5; self.update = function () { self.y += self.speed; self.x += Math.sin(self.direction) * self.speed; // Add slight X travel based on direction self.rotation += self.rotationSpeed; // Add rotation dustGraphics.alpha -= 0.01; // fade out at a medium pace if (self.y > 2732 || dustGraphics.alpha <= 0) { self.destroy(); } }; }); // Assets will be automatically created and loaded by the LK engine based on their usage in the code. // Hero class representing the player's spaceship var Hero = Container.expand(function () { var self = Container.call(this); self.prevX = self.x; // Initialize prevX with the current x position var heroGraphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 10; self.soldierCloneCount = 0; // Initialize soldier clone count self.droneCount = 0; // Initialize drone count self.shielded = false; // Initialize shielded state self.shieldLevel = 0; // Initialize shield level self.shieldGraphics = null; // Placeholder for shield asset self.update = function () { if (self.shielded && self.shieldGraphics) { self.shieldGraphics.rotation += 0.01; // Rotate shield slowly // Adjust shield size to cover hero asset and increase by 10% var scaleFactor = Math.max(heroGraphics.width / self.shieldGraphics.width, heroGraphics.height / self.shieldGraphics.height) * 1.1; self.shieldGraphics.scale.x = scaleFactor; self.shieldGraphics.scale.y = scaleFactor; // Move shield graphics to the layer directly underneath hero self.addChildAt(self.shieldGraphics, 0); // Update shield tint based on shield level self.shieldGraphics.tint = 0xFF0000; // Red tint for level 1 if (self.shieldLevel === 2) { self.shieldGraphics.tint = 0xFFFF00; // Yellow tint for level 2 } else if (self.shieldLevel === 3) { self.shieldGraphics.tint = 0x00FF00; // Green tint for level 3 } } if (self.y > 2375) { self.y -= self.speed; } // Add rotation based on movement direction if (self.x > self.prevX) { self.rotation += Math.PI / 180 * 1; // Rotate 1 degree to the right if (self.rotation > Math.PI / 180 * 5) { self.rotation = Math.PI / 180 * 5; } } else if (self.x < self.prevX) { self.rotation -= Math.PI / 180 * 1; // Rotate 1 degree to the left if (self.rotation < Math.PI / 180 * -5) { self.rotation = Math.PI / 180 * -5; } } else { if (self.rotation > 0) { self.rotation -= Math.PI / 180 * 1; if (self.rotation < 0) { self.rotation = 0; } } else if (self.rotation < 0) { self.rotation += Math.PI / 180 * 1; if (self.rotation > 0) { self.rotation = 0; } } } self.prevX = self.x; // Store the current x position for the next frame // Removed pulsing scale effect for hero self.scale.x = 1.0; }; // Method to handle shooting self.shootBehavior = powerUpManager.defaultBehavior; // Set initial behavior // Method to handle shooting self.shoot = function () { self.shootBehavior.call(self); }; }); // Bullet class for hero's bullets var HeroBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('heroBullet1', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -15; self.Type = 'standard'; // Default type for bullets self.Power = powerUpManager.currentPowerLevel || 1; // Initialize power level from PowerUpManager self.update = function () { // Move in direction of rotation self.y += Math.cos(self.rotation) * self.speed; self.x += Math.sin(self.rotation) * self.speed; // Adjust out-of-bounds check for angled movement if (self.y < 0 || self.x < 0 || self.x > 2048) { self.destroy(); var index = heroBullets.indexOf(self); if (index > -1) { heroBullets.splice(index, 1); } } }; self.updateVisual = function () { if (bulletGraphics) { self.removeChild(bulletGraphics); } var assetId = 'heroBullet' + Math.min(self.Power, 3); console.log("Loading bullet asset: " + assetId); bulletGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); }; }); var LaserBeamSegment = Container.expand(function () { var self = Container.call(this); // Attach laser segment graphic var laserGraphics = self.attachAsset('LaserBeam', { anchorX: 0.5, anchorY: 0, scaleX: 1, scaleY: 1 // Each segment stays at normal scale }); // Handle own collision self.update = function () { if (CollisionManager.checkCollision(self, hero)) { if (hero.shielded) { hero.shieldLevel -= 1; if (hero.shieldLevel <= 0) { hero.shielded = false; if (hero.shieldGraphics) { hero.shieldGraphics.destroy(); hero.shieldGraphics = null; } } } else { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } // Check clone collisions for (var i = soldierClones.length - 1; i >= 0; i--) { var clone = soldierClones[i]; if (CollisionManager.checkCollision(self, clone)) { clone.health -= 1; if (clone.health <= 0) { // Play turn off effect before destroying var tvEffect = new TVTurnOffEffect(); tvEffect.soldierClone = clone; backgroundContainer.addChild(tvEffect); // Note: actual destruction happens after animation completes } break; } } }; return self; }); var PlayerDrone = Container.expand(function () { var self = Container.call(this); var droneGraphics = self.attachAsset('PlayerDrone', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9, scaleX: 0.75, scaleY: 0.75 }); self.angle = 0; // Orbital angle self.orbitRadius = 200; // Further increased distance from hero self.orbitSpeed = 0.03; // Increased speed of rotation self.health = 1; self.lastCollisionFrame = 0; self.update = function () { // Orbit around hero self.angle += self.orbitSpeed; self.x = hero.x + Math.cos(self.angle) * self.orbitRadius; self.y = hero.y + Math.sin(self.angle) * self.orbitRadius; // Check collisions if (LK.ticks > self.lastCollisionFrame) { for (var i = 0; i < enemies.length; i++) { if (CollisionManager.checkCollision(self, enemies[i])) { self.lastCollisionFrame = LK.ticks; self.destroy(); return; } } } // Auto-fire if (LK.ticks % 18 == 0) { self.shoot(); } }; self.shoot = function () { var bullet = new HeroBullet(); bullet.x = self.x; bullet.y = self.y; bullet.Power = 1; // Always power level 1 bullet.updateVisual(); heroBullets.push(bullet); game.addChild(bullet); }; }); // PowerUp class for handling power-up movement and collection var PowerUp = Container.expand(function (assetType, spawnSide) { var self = Container.call(this); var powerUpGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; // Increased speed at which the power-up moves down the screen self.movementPhase = 'ENTRY'; self.spawnSide = spawnSide; // Pass this in constructor self.update = function () { if (self.movementPhase === 'ENTRY') { self.x += self.spawnSide === 'LEFT' ? self.speed : -self.speed; if (Math.abs(self.x - 1024) < 5) { // Check if near center self.movementPhase = 'DESCENT'; self.x = 1024; // Snap to center } } else { self.y += self.speed; } // Check if the power-up is collected by the hero if (self.intersects(hero)) { switch (assetType) { case 'SoldierCloneIcon': if (soldierClones.length < 4) { hero.soldierCloneCount++; var newClone = new SoldierClone(); // Spawn directly in combat position ahead of hero newClone.x = 1024; newClone.y = hero.y - 200; newClone.zIndex = hero.zIndex - 1; // Add clone to background container backgroundContainer.addChild(newClone); // Create effect (will handle initial clone scaling) var tvEffect = new TVTurnOnEffect(); tvEffect.soldierClone = newClone; backgroundContainer.addChild(tvEffect); // Add to tracking array soldierClones.push(newClone); } break; case 'PlayerDroneIcon': if (hero.droneCount < 3) { // Maximum 3 drones hero.droneCount = hero.droneCount || 0; hero.droneCount++; var newDrone = new PlayerDrone(); // Set initial angle based on drone count newDrone.angle = (hero.droneCount - 1) * (Math.PI * 2 / 3); playerDrones.push(newDrone); backgroundContainer.addChild(newDrone); } break; case 'WeaponPowerIcon': powerUpManager.currentPowerLevel = Math.min(powerUpManager.currentPowerLevel + 1, 4); console.log("Power up collected. New level: " + powerUpManager.currentPowerLevel); break; case 'ShieldIcon': if (!hero.shielded) { hero.shielded = true; hero.shieldGraphics = hero.attachAsset('heroShield', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 // Set opacity to 50% }); } hero.shieldLevel = Math.min(hero.shieldLevel + 1, 3); // Increase shield level to a max of 3 // Change shield tint based on shield level hero.shieldGraphics.tint = 0xFF0000; // Red tint for level 1 if (hero.shieldLevel === 2) { hero.shieldGraphics.tint = 0xFFFF00; // Yellow tint for level 2 } else if (hero.shieldLevel === 3) { hero.shieldGraphics.tint = 0x00FF00; // Green tint for level 3 } break; case 'BombIcon': powerUpManager.switchPowerUp(PowerUpTypes.BOMB); powerUpManager.bombBehavior(); break; } hero.shootBehavior = powerUpManager.getCurrentPowerBehavior(); soldierClones.forEach(function (clone) { clone.shootBehavior = hero.shootBehavior; }); self.destroy(); } // Destroy if out of screen if (self.y > 2732) { self.destroy(); } }; }); var PowerUpSpawner = Container.expand(function () { var self = Container.call(this); var timeSinceStart = 0; var timeSinceLastSpawn = 0; var spawnSide = 'LEFT'; self.update = function () { timeSinceStart += 1; timeSinceLastSpawn += 1; // First spawn at 10 seconds (assuming 60 FPS) if (timeSinceStart === 600) { self.spawnPowerUp(self.getRandomPowerUp()); } // Subsequent spawns every 30 seconds else if (timeSinceLastSpawn >= 1800) { self.spawnPowerUp(self.getRandomPowerUp()); timeSinceLastSpawn = 0; } }; self.getRandomPowerUp = function () { var powerUps = ['SoldierCloneIcon', 'SoldierCloneIcon', 'SoldierCloneIcon', 'SoldierCloneIcon']; if (hero.shieldLevel === 3) { powerUps = powerUps.filter(function (powerUp) { return powerUp !== 'ShieldIcon'; }); } if (powerUpManager.currentPowerLevel === 3) { powerUps = powerUps.filter(function (powerUp) { return powerUp !== 'WeaponPowerIcon'; }); } if (difficultyManager.currentDifficulty > 4) { powerUps.push('BombIcon'); } return powerUps[Math.floor(Math.random() * powerUps.length)]; }; self.spawnPowerUp = function (assetType) { var currentSpawnSide = spawnSide; // Get current value before toggling var powerUp = new PowerUp(assetType, currentSpawnSide); powerUp.x = currentSpawnSide === 'LEFT' ? 0 : 2048; powerUp.y = 200; // Spawn above screen but not too high backgroundContainer.addChild(powerUp); spawnSide = currentSpawnSide === 'LEFT' ? 'RIGHT' : 'LEFT'; // Toggle for next spawn }; }); var Raider = Container.expand(function () { var self = Container.call(this); var raiderGraphics = self.attachAsset('BotBiker', { anchorX: 0.5, anchorY: 0.5 }); // Raider specific properties self.HitPoints = 5; self.speed = 3; self.shootInterval = 66; // Reduced firing frequency by 10% self.lastShotTime = 0; self.trackingDelay = 30; // Delay before adjusting aim self.trackingTime = 0; // Time since last tracking adjustment self.targetRotation = raiderGraphics.rotation; // Target rotation for tracking self.entryPhase = true; // Track if we're still in entry animation self.speed = 3; self.rotationSpeed = 0.05; self.horizontalSpeed = 6; // Faster horizontal movement during entry // Add scaling properties self.maxTilt = 0.3; // How much to scale for tilt effect self.baseScale = 1; // Normal scale self.spawnSide = Math.random() < 0.5 ? 'left' : 'right'; // Set initial rotation based on spawn side raiderGraphics.rotation = self.spawnSide === 'left' ? -Math.PI / 4 : Math.PI / 4; self.update = function () { if (self.entryPhase) { // Entry movement if (self.spawnSide === 'left') { self.x += self.horizontalSpeed; raiderGraphics.rotation = Math.min(raiderGraphics.rotation + self.rotationSpeed, 0); // Scale for tilt effect raiderGraphics.scale.x = self.baseScale - self.maxTilt; if (self.x >= 600) { // Move further onto road self.entryPhase = false; raiderGraphics.scale.x = self.baseScale; } } else { self.x -= self.horizontalSpeed; raiderGraphics.rotation = Math.max(raiderGraphics.rotation - self.rotationSpeed, 0); // Scale for tilt effect raiderGraphics.scale.x = self.baseScale - self.maxTilt; if (self.x <= 1448) { // 2048 - 600 self.entryPhase = false; raiderGraphics.scale.x = self.baseScale; } } } else { self.y += self.speed * 0.25; // Reduce speed by 75% after entry // Move back and forth across the road if (self.spawnSide === 'left') { self.x += self.horizontalSpeed; if (self.x >= 1448) { // Right boundary self.spawnSide = 'right'; } } else { self.x -= self.horizontalSpeed; if (self.x <= 600) { // Left boundary self.spawnSide = 'left'; } } // Tracking logic if (self.trackingTime >= self.trackingDelay) { var directionX = hero.x - self.x; var directionY = hero.y - self.y; self.targetRotation = Math.atan2(directionY, directionX) - Math.PI / 2; self.trackingTime = 0; } else { self.trackingTime++; } // Swivel towards target rotation raiderGraphics.rotation += (self.targetRotation - raiderGraphics.rotation) * 0.1; // Shooting logic if (LK.ticks - self.lastShotTime > self.shootInterval) { self.shoot(); self.lastShotTime = LK.ticks; } } // Keep collision handling if (CollisionManager.checkCollision(self, hero)) { // [Keep existing collision code] } // Keep destruction logic if (self.HitPoints <= 0) { // Create raider pieces upon destruction generateEnemyPieces(self, RaiderPiece, ['RaiderTop', 'RaiderBottom']); difficultyManager.onEnemyDestroyed(); } else if (self.y > 2732) { // [Keep existing out-of-bounds code] difficultyManager.onEnemyDestroyed(); } }; self.shoot = function () { if (self.spawnSide === 'left') { var bullet = new RaiderBullet(); bullet.x = self.x; bullet.y = self.y + raiderGraphics.height / 2; bullet.rotation = raiderGraphics.rotation; game.addChild(bullet); self.spawnSide = self.spawnSide === 'left' ? 'right' : 'left'; // Alternate sides } else { var rightShot = new RaiderBullet(); rightShot.x = self.x + raiderGraphics.width / 4; rightShot.y = self.y + raiderGraphics.height / 2; game.addChild(rightShot); self.spawnSide = 'left'; // Switch to left for next shot } }; }); var RaiderBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('heroBullet1', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -15 * 0.75; // 75% of hero bullet speed self.update = function () { self.y -= Math.cos(self.rotation) * self.speed; self.x += Math.sin(self.rotation) * self.speed; // Check collision with hero if (CollisionManager.checkCollision(self, hero)) { if (hero.shielded) { hero.shieldLevel -= 1; if (hero.shieldLevel <= 0) { hero.shielded = false; if (hero.shieldGraphics) { hero.shieldGraphics.destroy(); hero.shieldGraphics = null; } } else { hero.shieldGraphics.tint = 0xFFFFFF; if (hero.shieldLevel === 2) { hero.shieldGraphics.tint = 0xFFFF00; } else if (hero.shieldLevel === 1) { hero.shieldGraphics.tint = 0xFFFFFF; } } } else { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } self.destroy(); return; } // Check collision with soldier clones for (var i = soldierClones.length - 1; i >= 0; i--) { var clone = soldierClones[i]; if (CollisionManager.checkCollision(self, clone)) { clone.health -= 1; if (clone.health <= 0) { // Play turn off effect before destroying var tvEffect = new TVTurnOffEffect(); tvEffect.soldierClone = clone; backgroundContainer.addChild(tvEffect); // Note: actual destruction happens after animation completes hero.soldierCloneCount--; // Decrease hero clone count } self.destroy(); break; } } if (self.y > 2732) { self.destroy(); } }; }); // RaiderPiece class for raider destruction effect var RaiderPiece = Container.expand(function (assetType) { var self = Container.call(this); var pieceGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); self.speedX = (Math.random() - 0.5) * 10; // Random horizontal speed self.speedY = (Math.random() - 0.5) * 10; // Random vertical speed self.alphaDecay = 0.02; // Rate at which the piece fades out self.update = function () { self.x += self.speedX; self.y += self.speedY; pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation pieceGraphics.alpha -= self.alphaDecay; if (pieceGraphics.alpha <= 0) { self.destroy(); } }; }); // RailingLeft class for the scrolling left railing var RailingLeft = Container.expand(function (assetType) { var self = Container.call(this); var railingGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0 }); self.speed = 2; self.update = function () { self.y += self.speed; if (self.y >= 2732) { self.y = -2732; } }; }); // RailingRight class for the scrolling right railing var RailingRight = Container.expand(function (assetType) { var self = Container.call(this); var railingGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0 }); self.speed = 2; self.update = function () { self.y += self.speed; if (self.y >= 2732) { self.y = -2732; } }; }); var RedDrone = Container.expand(function () { var self = Container.call(this); var droneGraphics = self.attachAsset('Drone', { anchorX: 0.5, anchorY: 0.5 }); // Apply red tint to graphics droneGraphics.tint = 0xFF0000; // Set aggressive properties self.HitPoints = 4; self.randomOffset = Math.random() * 100; self.speed = 4; // Faster than regular drone self.update = function () { if (hero) { // Chase the hero if (self.x < hero.x) { self.x += self.speed; } else if (self.x > hero.x) { self.x -= self.speed; } self.y += self.speed; } // Keep pulse effect from base drone if (self.pulseEffect) { droneGraphics.scale.x = 1.2; droneGraphics.scale.y = 1.2; self.pulseEffect = false; } else { droneGraphics.scale.x = 1.0; droneGraphics.scale.y = 1.0; } // Copy collision checks from base Drone if (CollisionManager.checkCollision(self, hero)) { if (hero.shielded) { hero.shieldLevel -= 1; if (hero.shieldLevel <= 0) { hero.shielded = false; if (hero.shieldGraphics) { hero.shieldGraphics.destroy(); hero.shieldGraphics = null; } } else { hero.shieldGraphics.tint = 0xFFFFFF; if (hero.shieldLevel === 2) { hero.shieldGraphics.tint = 0xFFFF00; } else if (hero.shieldLevel === 1) { hero.shieldGraphics.tint = 0xFFFFFF; } } generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']); self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } else { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } if (self.HitPoints <= 0) { generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']); self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); difficultyManager.onEnemyDestroyed(); } } else if (self.y > 2732) { self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); difficultyManager.onEnemyDestroyed(); } } }; }); // RoadScroll class for the scrolling road var RoadScroll = Container.expand(function (assetType) { var self = Container.call(this); var roadGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0 }); if (assetType === 'CityBackgroundHD') { self.speed = 1; } else { self.speed = 2; } self.update = function () { self.y += self.speed; if (self.y >= 2732) { self.y = -2732; } }; }); // SoldierClone class representing a clone of the player's character var SoldierClone = Container.expand(function () { var self = Container.call(this); // Removed self.side property var cloneGraphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7, scaleX: 0.75, scaleY: 0.75 }); self.roadLeft = 2048 * 0.21; self.roadRight = 2048 * 0.76; self.moveSpeed = 4.5; self.health = 3; self.isImmune = false; self.dodgeSpeed = 5; self.dodgeCooldown = 0; self.forwardOffset = 150 + Math.random() * 50; // Variable distance ahead self.horizontalPhase = Math.random() * Math.PI * 2; // Different horizontal movement phase // Removed self.offset property self.update = function () { // Calculate desired x position based on index var index = soldierClones.indexOf(self); if (index !== -1) { // Create an offset pattern that varies with time var timeOffset = LK.ticks * 0.02; // Slow oscillation var xOffset = Math.sin(timeOffset + index * (Math.PI / 2)) * 400; // 400 pixel spread // Target position is center (1024) plus offset var targetX = 1024 + xOffset; // Smooth movement towards target self.x += (targetX - self.x) * 0.1; // Maintain vertical position ahead of hero var targetY = hero.y - 200 - index * 50; // Stack vertically with gaps self.y += (targetY - self.y) * 0.1; // Add flickering effect based on health if (self.health < 3) { var flickerIntensity = (3 - self.health) * 0.2; // Increase intensity as health decreases cloneGraphics.alpha = 0.5 + Math.sin(LK.ticks * 0.2) * flickerIntensity; } } // Handle dodge cooldown if (self.dodgeCooldown > 0) { self.dodgeCooldown--; } // Check for bullets to dodge var needsToDodge = false; // Check for bullets to dodge if (self.dodgeCooldown <= 0) { enemies.forEach(function (enemy) { if (enemy instanceof RaiderBullet) { // Create a projected position of where bullet will be var projectedY = enemy.y + (enemy.speed || 0) * 10; // Look 10 frames ahead var projectedBullet = { x: enemy.x, y: projectedY, width: enemy.width, height: enemy.height }; if (CollisionManager.checkCollision(self, projectedBullet)) { needsToDodge = true; // Dodge in opposite direction of bullet's X position relative to clone var dodgeDirection = enemy.x > self.x ? -1 : 1; var newX = self.x + self.dodgeSpeed * dodgeDirection; // Stay within road if (newX > self.roadLeft && newX < self.roadRight) { self.x = newX; } self.dodgeCooldown = 30; } } }); } // Only target enemies if not dodging // In the movement code if (!needsToDodge) { var nearestEnemy = null; var nearestDist = Infinity; enemies.forEach(function (enemy) { if (enemy.y > self.y) { return; } var dist = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2)); if (dist < nearestDist) { nearestDist = dist; nearestEnemy = enemy; } }); // If no target, maintain forward position with slight variation if (!nearestEnemy) { var horizontalWobble = Math.sin(LK.ticks * 0.03 + self.horizontalPhase) * 25; var targetX = Math.min(Math.max(hero.x + horizontalWobble, self.roadLeft), self.roadRight); var targetY = hero.y - self.forwardOffset; // Smooth movement towards target self.x += (targetX - self.x) * 0.1; self.y += (targetY - self.y) * 0.1; } else { // When targeting enemy, maintain some spacing between clones var targetX = nearestEnemy.x + Math.sin(LK.ticks * 0.03 + self.horizontalPhase) * 20; targetX = Math.min(Math.max(targetX, self.roadLeft), self.roadRight); self.x += (targetX - self.x) * 0.1; } } // Check collisions once per frame if (!self.isImmune && LK.ticks > self.lastCollisionFrame) { for (var i = 0; i < enemies.length; i++) { if (CollisionManager.checkCollision(self, enemies[i])) { self.lastCollisionFrame = LK.ticks; self.health--; if (self.health <= 0) { // Play turn off effect before destroying var tvEffect = new TVTurnOffEffect(); tvEffect.soldierClone = self; backgroundContainer.addChild(tvEffect); // Note: actual destruction happens after animation completes return; // Prevent further processing in this update cycle } return; } } } self.shoot(); }; self.destroy = function () { // Remove bullets associated with this clone for (var i = heroBullets.length - 1; i >= 0; i--) { if (heroBullets[i].x === self.x && heroBullets[i].y === self.y - self.height / 2) { heroBullets[i].destroy(); heroBullets.splice(i, 1); } } // Removed side-based immunity code // Update soldier clone count and array var index = soldierClones.indexOf(self); if (index > -1) { soldierClones.splice(index, 1); hero.soldierCloneCount--; } // Call original destroy method Container.prototype.destroy.call(self); }; // Method to handle shooting self.shoot = function () { if (LK.ticks % 18 == 0) { // Match hero's fire rate var newBullet = new HeroBullet(); // Use the same bullet type as the hero newBullet.x = self.x; // Create bullets at the clone's current position newBullet.y = self.y - self.height / 2; newBullet.Power = heroBullets.length > 0 ? heroBullets[0].Power : 1; // Match hero's bullet power newBullet.updateVisual(); // Update bullet visuals based on power heroBullets.push(newBullet); // Add the new bullets to the game's bullet array game.addChild(newBullet); } }; }); var TVTurnOffEffect = Container.expand(function () { var self = Container.call(this); self.startTime = LK.ticks; self.animationDuration = 40; var finalScaleX = 0.7; self.update = function () { var progress = (LK.ticks - self.startTime) / self.animationDuration; if (progress <= 1) { if (self.soldierClone) { if (progress <= 0.3) { // Quick shrink to line var shrinkProgress = progress / 0.3; self.soldierClone.alpha = 1; self.soldierClone.scaleX = finalScaleX; self.soldierClone.scaleY = finalScaleX * (1 - shrinkProgress) + 0.05; var graphics = self.soldierClone.getChildAt(0); if (graphics) { graphics.scale.x = finalScaleX; graphics.scale.y = self.soldierClone.scaleY; } } else { // Longer flickering line phase before disappearing self.soldierClone.alpha = 0.8 + Math.sin(progress * Math.PI * 10) * 0.2; self.soldierClone.scaleX = finalScaleX; self.soldierClone.scaleY = 0.05; var graphics = self.soldierClone.getChildAt(0); if (graphics) { graphics.scale.x = finalScaleX; graphics.scale.y = 0.05; } } } } else { // Remove clone from arrays and destroy if (self.soldierClone) { var index = soldierClones.indexOf(self.soldierClone); if (index !== -1) { soldierClones.splice(index, 1); } self.soldierClone.destroy(); } self.destroy(); } }; return self; }); var TVTurnOnEffect = Container.expand(function () { var self = Container.call(this); self.startTime = LK.ticks; self.animationDuration = 40; // Get the clone's intended final scale (should be smaller than hero) var finalScaleX = 0.7; // Adjust this value to match the clone's proper size var finalScaleY = 0.7; self.update = function () { var progress = (LK.ticks - self.startTime) / self.animationDuration; if (progress <= 1) { if (self.soldierClone) { if (progress <= 0.7) { // Line phase self.soldierClone.alpha = 0.8 + Math.sin(progress * Math.PI * 10) * 0.2; self.soldierClone.scaleX = finalScaleX; // Keep correct width self.soldierClone.scaleY = 0.05; // Thin line var graphics = self.soldierClone.getChildAt(0); if (graphics) { graphics.scale.x = finalScaleX; graphics.scale.y = 0.05; } } else { // Expand phase var expandProgress = (progress - 0.7) / 0.3; self.soldierClone.alpha = 1; self.soldierClone.scaleX = finalScaleX; self.soldierClone.scaleY = 0.05 + expandProgress * (finalScaleY - 0.05); var graphics = self.soldierClone.getChildAt(0); if (graphics) { graphics.scale.x = finalScaleX; graphics.scale.y = self.soldierClone.scaleY; } } } } else { if (self.soldierClone) { // Set final dimensions self.soldierClone.alpha = 1; self.soldierClone.scaleX = finalScaleX; self.soldierClone.scaleY = finalScaleY; var graphics = self.soldierClone.getChildAt(0); if (graphics) { graphics.scale.x = finalScaleX; graphics.scale.y = finalScaleY; } } self.destroy(); } }; return self; }); // TitleScreen class for the title screen var TitleScreen = Container.expand(function () { var self = Container.call(this); // Attach game logo var logo = self.attachAsset('GameLogoHD', { anchorX: 0.5, anchorY: 0.5 }); logo.x = 2048 / 2; logo.y = 2732 / 2 - 200; // Attach play button var playButton = self.attachAsset('PlayButton', { anchorX: 0.5, anchorY: 0.5 }); playButton.x = 2048 / 2; playButton.y = logo.y + logo.height / 2 + playButton.height / 2 + 50; // Event handler for play button playButton.down = function (x, y, obj) { isTitle = false; isStarted = true; self.destroy(); startGame(); }; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 //Init game with black background }); /**** * Game Code ****/ var playerDrones = []; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) { return t; } var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) { return i; } throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function generateEnemyPieces(enemy, pieceClass, pieceTypes) { pieceTypes.forEach(function (pieceType, index) { var piece = new pieceClass(pieceType); piece.x = enemy.x + (index - 1) * 40; // Spread pieces horizontally piece.y = enemy.y; game.addChild(piece); }); } var CollisionManager = { checkCollision: function checkCollision(obj1, obj2) { // Special case for laser beam if (obj1.constructor.name === 'LaserBeam' || obj1 === 'LaserBeam') { // Line segment collision var laser = activeLaserBeams.find(function (l) { return l.laserBeam === obj1; }); if (!laser) { return false; } // Get laser start and end points var laserLength = obj1.height * obj1.scaleY; var startX = obj1.x; var startY = obj1.y; var endX = startX - Math.sin(obj1.rotation) * laserLength; var endY = startY + Math.cos(obj1.rotation) * laserLength; // Get target radius for collision var targetRadius = Math.min(obj2.width, obj2.height) * 0.4; // Check if point-to-line distance is less than target radius var dx = endX - startX; var dy = endY - startY; var len = Math.sqrt(dx * dx + dy * dy); // Get point to line segment distance var t = ((obj2.x - startX) * dx + (obj2.y - startY) * dy) / (len * len); t = Math.max(0, Math.min(1, t)); var nearestX = startX + t * dx; var nearestY = startY + t * dy; // Check distance from nearest point var distance = Math.sqrt((obj2.x - nearestX) * (obj2.x - nearestX) + (obj2.y - nearestY) * (obj2.y - nearestY)); return distance < targetRadius; } return this.circleCollision(obj1, obj2); }, getObjectRadius: function getObjectRadius(obj) { var width = obj.width; var height = obj.height; return Math.min(width, height) * 0.4; }, circleCollision: function circleCollision(obj1, obj2) { var radius1 = this.getObjectRadius(obj1); var radius2 = this.getObjectRadius(obj2); var dx = obj1.x - obj2.x; var dy = obj1.y - obj2.y; var distance = Math.sqrt(dx * dx + dy * dy); return distance < radius1 + radius2; } }; var PowerUpTypes = { DEFAULT: 'DEFAULT', SOLDIER_CLONE: 'SOLDIER_CLONE', SHIELD: 'SHIELD', WEAPON_POWER: 'WEAPON_POWER', BOMB: 'BOMB', // Add bomb type PLAYER_DRONE: 'PLAYER_DRONE' // Add player drone type }; // PowerUpManager class to manage power-ups // Initialize the city background for parallax effect var PowerUpManager = function PowerUpManager() { this.currentPowerUp = PowerUpTypes.DEFAULT; this.currentPowerLevel = 1; // Initialize power level to default value this.switchPowerUp = function (newPowerUp) { if (PowerUpTypes[newPowerUp]) { this.currentPowerUp = newPowerUp; } }; this.getCurrentPowerBehavior = function () { switch (this.currentPowerUp) { case PowerUpTypes.SOLDIER_CLONE: return this.soldierCloneBehavior; case PowerUpTypes.BOMB: return this.bombBehavior; default: return this.defaultBehavior; } }; this.defaultBehavior = function () { var bullet = new HeroBullet(); bullet.x = hero.x; bullet.y = hero.y - hero.height / 2; bullet.Power = powerUpManager.currentPowerLevel || 1; // Use saved power level from PowerUpManager bullet.updateVisual(); // Update the visual after setting power heroBullets.push(bullet); game.addChild(bullet); console.log("Created bullet with power: " + bullet.Power); }; this.soldierCloneBehavior = function () { // Soldier clone behavior // Removed clone spawning logic }; this.bombBehavior = function () { var blast = new BombBlast(); blast.x = 1024; blast.y = 1366; blast.zIndex = 2000; // Ensure BombBlast is on a visible layer game.addChild(blast); // Kill enemies for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] && enemies[i].y > 0) { enemies[i].HitPoints = 0; // Generate pieces based on enemy type if (enemies[i] instanceof Drone || enemies[i] instanceof RedDrone) { generateEnemyPieces(enemies[i], DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']); } else if (enemies[i] instanceof Bruiser) { generateEnemyPieces(enemies[i], BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']); } else if (enemies[i] instanceof Blaster) { generateEnemyPieces(enemies[i], BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']); } else if (enemies[i] instanceof Raider) { generateEnemyPieces(enemies[i], RaiderPiece, ['RaiderTop', 'RaiderBottom']); } enemies[i].destroy(); enemies.splice(i, 1); difficultyManager.onEnemyDestroyed(); } } // Flash effect after blast is created LK.setTimeout(function () { var flash = new Container(); var flashGraphics = flash.attachAsset('whitePixel', { anchorX: 0.5, anchorY: 0.5, scaleX: 20, scaleY: 30 }); flash.x = game.width / 2; flash.y = game.height / 2; flash.alpha = 0.8; game.addChild(flash); LK.setTimeout(function () { flash.destroy(); }, 100); }, 100); // Screen shake var originalX = game.x; var shakeAmount = 30; // Increase shake intensity var shakeDuration = 100; // Increase shake duration game.x += shakeAmount; LK.setTimeout(function () { game.x -= shakeAmount * 2; LK.setTimeout(function () { game.x += shakeAmount; LK.setTimeout(function () { game.x -= shakeAmount * 2; LK.setTimeout(function () { game.x += shakeAmount; LK.setTimeout(function () { game.x = originalX; }, shakeDuration); }, shakeDuration); }, shakeDuration); }, shakeDuration); }, shakeDuration); }; }; // PowerUpTypes enum/constants var PowerUpTypes = { DEFAULT: 'DEFAULT', LASER_BEAM: 'LASER_BEAM', ROCKETS: 'ROCKETS', DRONE: 'DRONE', SOLDIER_CLONE: 'SOLDIER_CLONE', SHIELD: 'SHIELD', // Add shield power-up type WEAPON_POWER: 'WEAPON_POWER' }; var LaserMechanic = function LaserMechanic(origin, chargeDuration, fireDuration, slideOffDuration) { this.origin = origin; this.chargeDuration = chargeDuration; this.fireDuration = fireDuration; this.slideOffDuration = slideOffDuration; // State tracking this.isCharging = false; this.isFiring = false; this.isSliding = false; // Timing this.chargeStartTime = null; this.fireStartTime = null; this.slideStartTime = null; // Flag to indicate attack completion this.attackComplete = false; // Assets this.chargeEffect = null; this.laserBeam = null; var index = activeLaserBeams.indexOf(this); if (index > -1) { activeLaserBeams.splice(index, 1); } // Method to start charging LaserMechanic.prototype.startCharge = function () { if (this.isCharging || this.isFiring || this.isSliding) { return; } this.isCharging = true; this.chargeStartTime = LK.ticks; // Create charge effect this.chargeEffect = LK.getAsset('LaserCharge', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5, scale: { x: 0.5, y: 0.5 } }); if (this.origin) { this.chargeEffect.y += this.origin.height * 0.1; // Move charge effect down by 10% of origin's height this.origin.addChild(this.chargeEffect); } }; // Target this.targetX = null; this.targetY = null; // Method to update charge progress LaserMechanic.prototype.updateCharge = function () { if (!this.isCharging) { return; } var chargeProgress = (LK.ticks - this.chargeStartTime) / this.chargeDuration; // Update charge effect this.chargeEffect.alpha = 0.5 + chargeProgress * 0.5; // Fade in this.chargeEffect.scale.x = 0.5 + chargeProgress * 1.5; // Grow outward this.chargeEffect.scale.y = 0.5 + chargeProgress * 1.5; // Capture target position at 2/3 through charge if (chargeProgress >= 0.67 && !this.targetX) { this.targetX = hero.x; this.targetY = hero.y; } // Check if charge is complete if (chargeProgress >= 1) { if (this.origin && this.chargeEffect) { this.origin.removeChild(this.chargeEffect); } this.chargeEffect = null; this.isCharging = false; this.startFiring(); // Add this line to transition to firing phase } }; }; var cityBackground1 = game.addChild(new RoadScroll('CityBackgroundHD')); var cityBackground2 = game.addChild(new RoadScroll('CityBackgroundHD')); cityBackground1.x = 2048 / 2; cityBackground2.x = 2048 / 2; cityBackground1.y = 0; cityBackground2.y = -2732; // Initialize the road instances var road1 = game.addChild(new RoadScroll('Road')); var road2 = game.addChild(new RoadScroll('Road2')); road2.x = 2048 / 2; road1.x = 2048 / 2; road1.y = 0; road2.y = -2732; // Initialize the left railing instances var railingLeft1 = game.addChild(new RailingLeft('RailingStart')); var railingLeft2 = game.addChild(new RailingLeft('RailingEnd')); railingLeft1.x = 2048 * 0.16; railingLeft2.x = 2048 * 0.16; railingLeft1.y = 0; railingLeft2.y = -2732; // Initialize the right railing instances var railingRight1 = game.addChild(new RailingRight('RailingEnd')); var railingRight2 = game.addChild(new RailingRight('RailingStart')); railingRight1.x = 2048 * 0.81; // Moved left by 3% railingRight2.x = 2048 * 0.81; // Moved left by 3% railingRight1.y = 0; railingRight2.y = -2732; // Initialize game state variables var isTitle = true; var isStarted = false; // Initialize title screen if (isTitle) { var titleScreen = game.addChild(new TitleScreen()); } // Initialize variables var backgroundContainer; var hero; var enemies = []; var heroBullets = []; var soldierClones = []; var powerUpManager = new PowerUpManager(); // Initialize powerUpManager powerUpManager.currentPowerLevel = 1; // Track current power level at start // Function to start the game function startGame() { // Initialize background container backgroundContainer = new Container(); game.addChild(backgroundContainer); difficultyManager = new DifficultyManager(); difficultyManager.updateSpawnQuota(); // Initialize hero hero = game.addChild(new Hero()); // Initialize PowerUpSpawner var powerUpSpawner = new PowerUpSpawner(); backgroundContainer.addChild(powerUpSpawner); hero.soldierCloneCount = soldierClones.length; // Set initial soldier clone count hero.x = 2048 / 2; hero.y = 2732 + hero.height; hero.shielded = true; // Start hero with shield hero.shieldLevel = 3; // Set shield level to 3 hero.shieldGraphics = hero.attachAsset('heroShield', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 // Set opacity to 50% }); hero.shieldGraphics.tint = 0xFF0000; // Red tint for level 3 shield // Initialize enemies, bullets, and laser beams arrays enemies = []; heroBullets = []; activeLaserBeams = []; hero.shootBehavior = powerUpManager.getCurrentPowerBehavior().bind(hero); } // Function to handle game updates game.update = function () { soldierClones.forEach(function (clone) { clone.shootBehavior = hero.shootBehavior; }); cityBackground1.update(); cityBackground2.update(); // Update the road instances road1.update(); road2.update(); // Update the left railing instances railingLeft1.update(); railingLeft2.update(); // Update the right railing instances railingRight1.update(); railingRight2.update(); if (isStarted) { if (dragNode) { hero.x = dragNode.x; hero.y = dragNode.y; } // Update enemies if (!enemies) { return; } // Ensure enemies array is initialized for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i]) { enemies[i].update(); } if (enemies[i] && CollisionManager.checkCollision(enemies[i], hero)) { if (hero.shielded) { hero.shieldLevel -= 1; if (hero.shieldLevel <= 0) { hero.shielded = false; if (hero.shieldGraphics) { hero.shieldGraphics.destroy(); hero.shieldGraphics = null; } } else { // Update shield tint based on new shield level hero.shieldGraphics.tint = 0xFF0000; // Red tint for level 1 if (hero.shieldLevel === 2) { hero.shieldGraphics.tint = 0xFFFF00; // Yellow tint for level 2 } else if (hero.shieldLevel === 3) { hero.shieldGraphics.tint = 0x00FF00; // Green tint for level 3 } } // Destroy enemy into pieces if (enemies[i] instanceof Drone) { generateEnemyPieces(enemies[i], DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']); } else if (enemies[i] instanceof Bruiser) { generateEnemyPieces(enemies[i], BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']); } else if (enemies[i] instanceof Blaster) { generateEnemyPieces(enemies[i], BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']); } enemies[i].destroy(); enemies.splice(i, 1); difficultyManager.onEnemyDestroyed(); } else { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } for (var l = activeLaserBeams.length - 1; l >= 0; l--) { var laser = activeLaserBeams[l]; if (laser.segments) { // Check if it has segments // If origin (Blaster) is gone and not sliding yet, start sliding if (!laser.origin && !laser.isSliding && laser.segments.length > 0) { laser.startSliding(); } laser.update(); // Clean up if complete or all segments are off screen if (laser.isAttackComplete() || laser.segments.length === 0) { // Final cleanup of any remaining segments if (laser.segments) { for (var i = laser.segments.length - 1; i >= 0; i--) { game.removeChild(laser.segments[i]); } laser.segments = []; } activeLaserBeams.splice(l, 1); } } else { // No segments found, remove from active lasers activeLaserBeams.splice(l, 1); } } difficultyManager.update(); // Update hero bullets for (var j = heroBullets.length - 1; j >= 0; j--) { heroBullets[j].update(); for (var k = enemies.length - 1; k >= 0; k--) { if (heroBullets[j] && CollisionManager.checkCollision(heroBullets[j], enemies[k]) && enemies[k].y > 0) { enemies[k].HitPoints -= heroBullets[j].Power; // Apply an increased push back effect if (!(enemies[k] instanceof Bruiser)) { enemies[k].y -= 12; // Push the drone upwards slightly more } enemies[k].pulseEffect = true; // Trigger pulse effect // Removed tracerTail reference heroBullets[j].destroy(); heroBullets.splice(j, 1); if (enemies[k].HitPoints <= 0) { if (enemies[k] instanceof Drone || enemies[k] instanceof RedDrone) { generateEnemyPieces(enemies[k], DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']); } else if (enemies[k] instanceof Bruiser) { generateEnemyPieces(enemies[k], BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']); } else if (enemies[k] instanceof Blaster) { // In Blaster's destroy method or where Blaster destruction is handled if (this.laserMechanic) { this.laserMechanic.origin = null; // Detach from Blaster if (this.laserMechanic.isFiring) { // If it's firing, transition to sliding this.laserMechanic.startSliding(); } // Don't destroy the laser mechanic - let it clean itself up } generateEnemyPieces(enemies[k], BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']); } else if (enemies[k] instanceof Raider) { generateEnemyPieces(enemies[k], RaiderPiece, ['RaiderTop', 'RaiderBottom']); } enemies[k].destroy(); enemies.splice(k, 1); difficultyManager.onEnemyDestroyed(); LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); } break; } } } // Fire bullets using current power-up behavior if (LK.ticks % 18 == 0) { if (hero && hero.shootBehavior) { hero.shootBehavior(); } } // Generate dust particles in sync with player pulsing if (LK.ticks % 24 == 0) { var newDust = new Dust(); newDust.x = hero.x - hero.width / 4; // Move dust spawn position right half as much as the last move newDust.y = hero.y + hero.height / 2 * 0.93; // Move dust spawn point up 3% more game.addChild(newDust); } else if (LK.ticks % 24 == 12) { var newDust = new Dust(); newDust.x = hero.x + hero.width / 4; // Create another dust spawn point an equal distance in from the right of the player asset newDust.y = hero.y + hero.height / 2 * 0.93; // Move dust spawn point up 3% more game.addChild(newDust); } } }; // Handle touch input for hero movement var dragNode = null; game.down = function (x, y, obj) { if (isStarted) { dragNode = { x: hero.x, y: hero.y }; // Add a tilt-back effect during initial movement hero.rotation = x > hero.x ? Math.PI / 180 * -1 : Math.PI / 180 * 1; } }; game.move = function (x, y, obj) { if (isStarted && dragNode) { // Add a slight delay to the drag control to make the player feel like it has more weight dragNode.x += (x - dragNode.x) * 0.1; dragNode.y = hero.y; // Lock the y position to the hero's initial y position } }; game.up = function (x, y, obj) { dragNode = null; }; // Display score var scoreTxt = new Text2('0', { size: 150, fill: "#ffffff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.setText(LK.getScore()); LaserMechanic.prototype.startFiring = function () { if (!this.origin || !this.targetX || !this.targetY || this.isFiring) { return; } this.isFiring = true; this.fireStartTime = LK.ticks; // Calculate direction var dirX = this.targetX - this.origin.x; var dirY = this.targetY - this.origin.y; this.rotation = Math.atan2(dirY, dirX) - Math.PI / 2; // Create array to hold segments this.segments = []; // Create initial segment with adjusted position var segment = new LaserBeamSegment(); // Match the origin point of the Blaster segment.x = this.origin.x; segment.y = this.origin.y + this.origin.height * 0.1; // Small offset like original segment.rotation = this.rotation; game.addChild(segment); this.segments.push(segment); }; LaserMechanic.prototype.updateFiring = function () { if (!this.isFiring) { return; } var fireProgress = (LK.ticks - this.fireStartTime) / this.fireDuration; if (fireProgress <= 1) { // Add new segments quickly until desired length if (this.segments.length < 20 && LK.ticks % 2 === 0) { // Adjust segment count and speed var lastSegment = this.segments[this.segments.length - 1]; var segment = new LaserBeamSegment(); // Position new segment based on last one segment.x = lastSegment.x - Math.sin(this.rotation) * segment.height; segment.y = lastSegment.y + Math.cos(this.rotation) * segment.height; segment.rotation = this.rotation; game.addChild(segment); this.segments.push(segment); } } else { this.isFiring = false; this.startSliding(); } }; LaserMechanic.prototype.startSliding = function () { this.isSliding = true; this.slideStartTime = LK.ticks; }; LaserMechanic.prototype.updateSliding = function () { if (!this.isSliding) { return; } var slideProgress = (LK.ticks - this.slideStartTime) / this.slideOffDuration; if (slideProgress <= 1) { // Move all segments var allOffScreen = true; for (var i = 0; i < this.segments.length; i++) { var segment = this.segments[i]; segment.x += -Math.sin(this.rotation) * 20; segment.y += Math.cos(this.rotation) * 20; // Check if any segment is still on screen if (segment.y > 0 && segment.y < 2048 && segment.x > 0 && segment.x < 2048) { allOffScreen = false; } } // If all segments are off screen, clean up early if (allOffScreen) { slideProgress = 1; } } if (slideProgress > 1) { // Clean up all segments for (var i = this.segments.length - 1; i >= 0; i--) { game.removeChild(this.segments[i]); } this.segments = []; this.isSliding = false; this.attackComplete = true; } }; // Method to reset attack states LaserMechanic.prototype.resetAttack = function () { this.isCharging = false; this.isFiring = false; this.isSliding = false; this.targetX = null; this.targetY = null; this.attackComplete = false; return true; }; LaserMechanic.prototype.update = function () { // Add state validation if (!this.isCharging && !this.isFiring && !this.isSliding && this.laserBeam) { // If we have a beam but no state, force sliding state this.isSliding = true; this.slideStartTime = LK.ticks; } // Your existing debug marker var stateMarker = LK.getAsset('LaserCharge', { anchorX: 0.5, anchorY: 0.5, scale: { x: 1, y: 1 }, alpha: 0.8, tint: this.isCharging ? 0xFF0000 : this.isFiring ? 0x00FF00 : 0x0000FF }); stateMarker.x = 50; stateMarker.y = 50; game.addChild(stateMarker); if (this.isCharging) { this.updateCharge(); } else if (this.isFiring) { this.updateFiring(); } else if (this.isSliding || this.laserBeam) { this.updateSliding(); } }; // Method to check if the attack sequence is complete LaserMechanic.prototype.isAttackComplete = function () { return this.attackComplete; }; LaserMechanic.prototype.detachBeam = function () { if (this.laserBeam) { // Detach laser from origin this.origin = null; // Force laser into sliding phase this.startSliding(); } };
/****
* Classes
****/
// Blaster class representing a shooting enemy
var Blaster = Container.expand(function () {
var self = Container.call(this);
var blasterGraphics = self.attachAsset('Blaster', {
anchorX: 0.5,
anchorY: 0.5
});
self.HitPoints = 10;
self.speed = 3;
// Laser attack properties
self.laserMechanic = new LaserMechanic(self, 60, 30, 39); // charge, fire, slide durations
self.nextAttackTime = LK.ticks + randomRange(120, 240); // 2-4 seconds initial delay
function randomRange(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
self.update = function () {
// Update laser mechanic
self.laserMechanic.update();
// Check if it's time for a laser attack
if (LK.ticks >= self.nextAttackTime && !self.laserMechanic.isCharging && !self.laserMechanic.isFiring && !self.laserMechanic.isSliding && !self.laserMechanic.attackComplete) {
self.laserMechanic.startCharge();
}
// Handle movement based on state
if (self.laserMechanic.isCharging) {
// Only rotate to track player during charge
var directionX = hero.x - self.x;
var directionY = hero.y - self.y;
blasterGraphics.rotation = Math.atan2(directionY, directionX) - Math.PI / 2;
} else if (self.laserMechanic.isFiring || self.laserMechanic.isSliding) {
// No movement during firing and sliding
return;
} else {
// Normal movement when not attacking
// Track player position
var directionX = hero.x - self.x;
var directionY = hero.y - self.y;
var distance = Math.sqrt(directionX * directionX + directionY * directionY);
if (distance > 0) {
self.x += directionX / distance * self.speed;
self.y += directionY / distance * self.speed;
blasterGraphics.rotation = Math.atan2(directionY, directionX) - Math.PI / 2;
}
// Set next attack time after sliding is done
if (self.laserMechanic.isAttackComplete()) {
self.laserMechanic.resetAttack();
self.nextAttackTime = LK.ticks + randomRange(300, 480);
}
}
// Destroy if out of screen
if (CollisionManager.checkCollision(self, hero)) {
// Handle collision with hero
if (hero.shielded) {
hero.shieldLevel -= 1;
if (hero.shieldLevel <= 0) {
hero.shielded = false;
if (hero.shieldGraphics) {
hero.shieldGraphics.destroy();
hero.shieldGraphics = null;
}
} else {
hero.shieldGraphics.tint = 0xFFFFFF;
if (hero.shieldLevel === 2) {
hero.shieldGraphics.tint = 0xFFFF00;
} else if (hero.shieldLevel === 1) {
hero.shieldGraphics.tint = 0xFFFFFF;
}
}
// Destroy the enemy
generateEnemyPieces(self, BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']);
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
} else {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
if (self.HitPoints <= 0) {
// Create blaster pieces upon destruction
generateEnemyPieces(self, BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']);
// Check if there's an active laser beam
if (self.laserMechanic.laserBeam) {
// Detach and slide the laser beam
self.laserMechanic.detachBeam();
}
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
} else if (self.y > 2732) {
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
difficultyManager.onEnemyDestroyed();
}
}
};
});
// BlasterPiece class for blaster destruction effect
var BlasterPiece = Container.expand(function (assetType) {
var self = Container.call(this);
var pieceGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1 // Ensure pieces are fully visible on spawn
});
self.speedX = (Math.random() - 0.5) * 12; // Random horizontal speed to move outwards
self.speedY = (Math.random() - 0.5) * 12; // Random vertical speed to move outwards
self.alphaDecay = 0.02; // Rate at which the piece fades out
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation
pieceGraphics.alpha -= self.alphaDecay;
if (pieceGraphics.alpha <= 0) {
self.destroy();
}
};
});
var BombBlast = Container.expand(function () {
var self = Container.call(this);
var blastGraphics = self.attachAsset('BombBlast', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleX = 0.1;
self.scaleY = 0.1;
self.update = function () {
self.scaleX += 0.5;
self.scaleY += 0.5;
blastGraphics.scale.x = self.scaleX;
blastGraphics.scale.y = self.scaleY;
if (self.scaleX >= 10) {
self.destroy();
}
};
});
// Bruiser class representing a tougher enemy
var Bruiser = Container.expand(function () {
var self = Container.call(this);
var bruiserGraphics = self.attachAsset('Bruiser', {
anchorX: 0.5,
anchorY: 0.5
});
self.HitPoints = 20; // Double the hit points for Bruiser
self.speed = 5;
self.update = function () {
// Implement mechanical stomping movement
if (LK.ticks % 80 < 20) {
bruiserGraphics.scale.x = 1.1;
bruiserGraphics.scale.y = 0.9;
bruiserGraphics.rotation = 0.1; // Tilt right
} else if (LK.ticks % 80 < 40) {
bruiserGraphics.scale.x = 1.0;
bruiserGraphics.scale.y = 1.0;
bruiserGraphics.rotation = 0;
} else if (LK.ticks % 80 < 60) {
bruiserGraphics.scale.x = 1.1;
bruiserGraphics.scale.y = 0.9;
bruiserGraphics.rotation = -0.1; // Tilt left
} else {
bruiserGraphics.scale.x = 1.0;
bruiserGraphics.scale.y = 1.0;
bruiserGraphics.rotation = 0;
}
// Move downwards only during tilt phases
if (LK.ticks % 80 < 20 || LK.ticks % 80 >= 40 && LK.ticks % 80 < 60) {
// Check for nearby enemies and adjust path to avoid collisions
var nearbyEnemy = enemies.find(function (enemy) {
return enemy !== self && Math.abs(enemy.x - self.x) < 100 && Math.abs(enemy.y - self.y) < 100;
});
if (nearbyEnemy) {
self.x += (self.x < nearbyEnemy.x ? -1 : 1) * self.speed; // Move away from the nearby enemy
} else {
self.y += self.speed;
}
}
if (CollisionManager.checkCollision(self, hero)) {
// Handle collision with hero
if (hero.shielded) {
hero.shieldLevel -= 1;
if (hero.shieldLevel <= 0) {
hero.shielded = false;
if (hero.shieldGraphics) {
hero.shieldGraphics.destroy();
hero.shieldGraphics = null;
}
} else {
hero.shieldGraphics.tint = 0xFFFFFF;
if (hero.shieldLevel === 2) {
hero.shieldGraphics.tint = 0xFFFF00;
} else if (hero.shieldLevel === 1) {
hero.shieldGraphics.tint = 0xFFFFFF;
}
}
// Destroy the enemy
generateEnemyPieces(self, BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']);
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
} else {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
if (self.HitPoints <= 0) {
// Create bruiser pieces upon destruction
generateEnemyPieces(self, BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']);
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
} else if (self.y > 2732) {
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
difficultyManager.onEnemyDestroyed();
}
}
};
});
// BruiserPiece class for bruiser destruction effect
var BruiserPiece = Container.expand(function (assetType) {
var self = Container.call(this);
var pieceGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = -Math.random() * 6 + 3; // Switch direction of horizontal speed
self.speedY = Math.random() * 4 - 2; // Random vertical speed
self.alphaDecay = 0.02; // Rate at which the piece fades out
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation
pieceGraphics.alpha -= self.alphaDecay;
if (pieceGraphics.alpha <= 0) {
self.destroy();
}
};
});
var DifficultyManager = Container.expand(function () {
var self = Container.call(this);
self.currentDifficulty = 1;
self.baseDifficulty = 1;
self.activeEnemies = 0;
self.maxEnemies = 30;
self.roadStartX = 2048 * 0.21;
self.roadWidth = 2048 * 0.55;
// Wave patterns for different difficulty levels
self.wavePatterns = {
1: {
drones: Math.ceil(6 * 1.3),
bruisers: 0,
blasters: 1,
raiders: 0
},
2: {
drones: Math.ceil(8 * 1.3),
bruisers: 0,
blasters: 0,
raiders: 1
},
3: {
drones: Math.ceil(8 * 1.3),
bruisers: 1,
blasters: 0,
raiders: 2
},
4: {
drones: Math.ceil(10 * 1.3),
bruisers: 1,
blasters: 1,
raiders: 2
},
5: {
drones: Math.ceil(12 * 1.3),
bruisers: 1,
blasters: 1,
raiders: 3
},
6: {
drones: Math.ceil(14 * 1.3),
bruisers: 2,
blasters: 1,
raiders: 3
},
7: {
drones: Math.ceil(16 * 1.3),
bruisers: 2,
blasters: 2,
raiders: 4
}
};
// Spawn quota to track needed spawns
self.spawnQuota = {
drones: 0,
bruisers: 0,
blasters: 0,
raiders: 0
};
self.update = function () {
var timeScale = Math.floor(LK.ticks / 1800);
self.baseDifficulty = 1 + timeScale * 0.2;
if (LK.ticks % 300 === 0) {
var powerLevel = self.calculatePlayerPower();
self.updateDifficulty(powerLevel);
}
// Fast spawn check for drones
if (LK.ticks % 45 === 0 && self.canSpawnEnemy()) {
// Twice as fast
if (self.spawnQuota.drones > 0) {
self.spawnEnemy('drone');
}
}
// Regular spawn check for other enemies
if (LK.ticks % 60 === 0 && self.canSpawnEnemy()) {
var enemyType = self.getNextEnemyType();
if (enemyType && enemyType !== 'drone') {
self.spawnEnemy(enemyType);
}
}
};
self.updateDifficulty = function (powerLevel) {
var totalPower = powerLevel * self.baseDifficulty;
// Match to wave pattern keys 1-7
if (totalPower >= 10) {
self.currentDifficulty = 7;
} else if (totalPower >= 8) {
self.currentDifficulty = 6;
} else if (totalPower >= 6) {
self.currentDifficulty = 5;
} else if (totalPower >= 4.5) {
self.currentDifficulty = 4;
} else if (totalPower >= 3) {
self.currentDifficulty = 3;
} else if (totalPower >= 2) {
self.currentDifficulty = 2;
} else {
self.currentDifficulty = 1;
}
self.updateSpawnQuota();
};
self.getSpawnX = function () {
return self.roadStartX + Math.random() * self.roadWidth;
};
self.getEnemyStrength = function () {
// Remove enemy HP scaling with difficulty
return 1;
};
self.updateSpawnQuota = function () {
var pattern = self.wavePatterns[self.currentDifficulty] || self.wavePatterns[6]; // Default to highest if overflow
self.spawnQuota.drones = pattern.drones;
self.spawnQuota.bruisers = pattern.bruisers;
self.spawnQuota.blasters = pattern.blasters;
self.spawnQuota.raiders = pattern.raiders;
};
this.calculatePlayerPower = function () {
var power = 1; // Base power level
// Weapon power contribution (max level is 3 now)
if (powerUpManager && powerUpManager.currentPowerLevel) {
power += (powerUpManager.currentPowerLevel - 1) * 0.5; // Reduced from 0.8
}
// Clone contribution (max 4 clones)
if (soldierClones && soldierClones.length) {
power += soldierClones.length * 0.4; // Reduced from 0.6
}
// Drone contribution (max 3 drones)
if (playerDrones && playerDrones.length) {
power += playerDrones.length * 0.4; // Reduced from 0.6
}
return power;
};
self.canSpawnEnemy = function () {
return self.activeEnemies < self.maxEnemies;
};
self.spawnEnemy = function (enemyType) {
if (!self.canSpawnEnemy()) {
return;
}
var enemy;
switch (enemyType) {
case 'drone':
var redDroneChance = Math.min(self.currentDifficulty * 0.1, 0.3);
if (Math.random() < redDroneChance) {
enemy = new RedDrone();
} else {
enemy = new Drone();
}
break;
case 'bruiser':
enemy = new Bruiser();
break;
case 'blaster':
enemy = new Blaster();
break;
case 'raider':
enemy = new Raider();
var spawnSide = Math.random() < 0.5;
enemy.x = spawnSide ? -100 : 2148;
enemy.y = Math.random() * (2732 * 0.3);
enemy.spawnSide = spawnSide ? 'left' : 'right'; // Tell Raider which side it spawned from
break;
default:
return;
}
// Increase enemy HP based on difficulty
enemy.HitPoints *= self.getEnemyStrength();
// Only set default spawn position for non-raiders
if (enemyType !== 'raider') {
enemy.x = self.getSpawnX();
enemy.y = -enemy.height;
}
enemies.push(enemy);
game.addChild(enemy);
self.activeEnemies++;
};
self.getNextEnemyType = function () {
// Get all available enemy types with remaining quota
var availableTypes = [];
if (self.spawnQuota.drones > 0) {
availableTypes.push('drone');
}
if (self.spawnQuota.bruisers > 0) {
availableTypes.push('bruiser');
}
if (self.spawnQuota.raiders > 0) {
availableTypes.push('raider');
}
if (self.spawnQuota.blasters > 0) {
availableTypes.push('blaster');
}
if (availableTypes.length === 0) {
return null;
}
// Randomly select from available types
var selectedType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
// Decrease the appropriate quota
self.spawnQuota[selectedType + 's']--;
return selectedType;
};
self.onEnemyDestroyed = function () {
self.activeEnemies--;
if (self.spawnQuota.drones === 0 && self.spawnQuota.bruisers === 0 && self.spawnQuota.blasters === 0 && self.spawnQuota.raiders === 0) {
self.updateSpawnQuota();
}
};
});
// Enemy class representing the enemy robots
var Drone = Container.expand(function () {
var self = Container.call(this);
var droneGraphics = self.attachAsset('Drone', {
anchorX: 0.5,
anchorY: 0.5
});
self.HitPoints = 1;
self.randomOffset = Math.random() * 100; // Random timing for each drone
self.speed = 2.75;
self.update = function () {
// Add robotic hover effects
// Check for nearby enemies and adjust path to avoid collisions
var nearbyEnemy = enemies.find(function (enemy) {
return enemy !== self && Math.abs(enemy.x - self.x) < 100 && Math.abs(enemy.y - self.y) < 100;
});
if (nearbyEnemy) {
self.x += (self.x < nearbyEnemy.x ? -1 : 1) * self.speed; // Move away from the nearby enemy
} else {
self.y += self.speed * (Math.random() > 0.1 ? 1 : 0); // Brief pauses in downward movement
}
self.x += Math.sin(LK.ticks / 10 + self.randomOffset) * 2; // Small jerky side-to-side movements
if (Math.random() > 0.95) {
self.x += Math.random() * 4 - 2; // Quick position corrections
}
// Pulse effect on hit
if (self.pulseEffect) {
droneGraphics.scale.x = 1.2;
droneGraphics.scale.y = 1.2;
self.pulseEffect = false;
} else {
droneGraphics.scale.x = 1.0;
droneGraphics.scale.y = 1.0;
}
if (CollisionManager.checkCollision(self, hero)) {
// Handle collision with hero
if (hero.shielded) {
hero.shieldLevel -= 1;
if (hero.shieldLevel <= 0) {
hero.shielded = false;
if (hero.shieldGraphics) {
hero.shieldGraphics.destroy();
hero.shieldGraphics = null;
}
} else {
hero.shieldGraphics.tint = 0xFFFFFF;
if (hero.shieldLevel === 2) {
hero.shieldGraphics.tint = 0xFFFF00;
} else if (hero.shieldLevel === 1) {
hero.shieldGraphics.tint = 0xFFFFFF;
}
}
// Destroy the enemy
generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']);
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
} else {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
if (self.HitPoints <= 0) {
// Create drone pieces upon destruction
generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']);
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
} else if (self.y > 2732) {
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
difficultyManager.onEnemyDestroyed();
}
}
};
});
// DronePiece class for drone destruction effect
var DronePiece = Container.expand(function (assetType) {
var self = Container.call(this);
var pieceGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = Math.random() * 6 - 3; // Increased random horizontal speed
self.speedY = Math.random() * 4 - 2; // Random vertical speed
self.alphaDecay = 0.02; // Rate at which the piece fades out
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation
pieceGraphics.alpha -= self.alphaDecay;
if (pieceGraphics.alpha <= 0) {
self.destroy();
}
};
});
// Dust class for dust particles
var Dust = Container.expand(function () {
var self = Container.call(this);
var dustGraphics = self.attachAsset('Dust', {
anchorX: 0.5,
anchorY: 0.5
});
dustGraphics.alpha = 0.75;
self.speed = Math.random() * 3 + 1;
self.rotationSpeed = Math.random() * 0.02 - 0.01; // Random rotation speed between -0.01 and 0.01
self.direction = Math.random() * Math.PI * 0.5;
self.update = function () {
self.y += self.speed;
self.x += Math.sin(self.direction) * self.speed; // Add slight X travel based on direction
self.rotation += self.rotationSpeed; // Add rotation
dustGraphics.alpha -= 0.01; // fade out at a medium pace
if (self.y > 2732 || dustGraphics.alpha <= 0) {
self.destroy();
}
};
});
// Assets will be automatically created and loaded by the LK engine based on their usage in the code.
// Hero class representing the player's spaceship
var Hero = Container.expand(function () {
var self = Container.call(this);
self.prevX = self.x; // Initialize prevX with the current x position
var heroGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.soldierCloneCount = 0; // Initialize soldier clone count
self.droneCount = 0; // Initialize drone count
self.shielded = false; // Initialize shielded state
self.shieldLevel = 0; // Initialize shield level
self.shieldGraphics = null; // Placeholder for shield asset
self.update = function () {
if (self.shielded && self.shieldGraphics) {
self.shieldGraphics.rotation += 0.01; // Rotate shield slowly
// Adjust shield size to cover hero asset and increase by 10%
var scaleFactor = Math.max(heroGraphics.width / self.shieldGraphics.width, heroGraphics.height / self.shieldGraphics.height) * 1.1;
self.shieldGraphics.scale.x = scaleFactor;
self.shieldGraphics.scale.y = scaleFactor;
// Move shield graphics to the layer directly underneath hero
self.addChildAt(self.shieldGraphics, 0);
// Update shield tint based on shield level
self.shieldGraphics.tint = 0xFF0000; // Red tint for level 1
if (self.shieldLevel === 2) {
self.shieldGraphics.tint = 0xFFFF00; // Yellow tint for level 2
} else if (self.shieldLevel === 3) {
self.shieldGraphics.tint = 0x00FF00; // Green tint for level 3
}
}
if (self.y > 2375) {
self.y -= self.speed;
}
// Add rotation based on movement direction
if (self.x > self.prevX) {
self.rotation += Math.PI / 180 * 1; // Rotate 1 degree to the right
if (self.rotation > Math.PI / 180 * 5) {
self.rotation = Math.PI / 180 * 5;
}
} else if (self.x < self.prevX) {
self.rotation -= Math.PI / 180 * 1; // Rotate 1 degree to the left
if (self.rotation < Math.PI / 180 * -5) {
self.rotation = Math.PI / 180 * -5;
}
} else {
if (self.rotation > 0) {
self.rotation -= Math.PI / 180 * 1;
if (self.rotation < 0) {
self.rotation = 0;
}
} else if (self.rotation < 0) {
self.rotation += Math.PI / 180 * 1;
if (self.rotation > 0) {
self.rotation = 0;
}
}
}
self.prevX = self.x; // Store the current x position for the next frame
// Removed pulsing scale effect for hero
self.scale.x = 1.0;
};
// Method to handle shooting
self.shootBehavior = powerUpManager.defaultBehavior; // Set initial behavior
// Method to handle shooting
self.shoot = function () {
self.shootBehavior.call(self);
};
});
// Bullet class for hero's bullets
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('heroBullet1', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -15;
self.Type = 'standard'; // Default type for bullets
self.Power = powerUpManager.currentPowerLevel || 1; // Initialize power level from PowerUpManager
self.update = function () {
// Move in direction of rotation
self.y += Math.cos(self.rotation) * self.speed;
self.x += Math.sin(self.rotation) * self.speed;
// Adjust out-of-bounds check for angled movement
if (self.y < 0 || self.x < 0 || self.x > 2048) {
self.destroy();
var index = heroBullets.indexOf(self);
if (index > -1) {
heroBullets.splice(index, 1);
}
}
};
self.updateVisual = function () {
if (bulletGraphics) {
self.removeChild(bulletGraphics);
}
var assetId = 'heroBullet' + Math.min(self.Power, 3);
console.log("Loading bullet asset: " + assetId);
bulletGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
});
var LaserBeamSegment = Container.expand(function () {
var self = Container.call(this);
// Attach laser segment graphic
var laserGraphics = self.attachAsset('LaserBeam', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1,
scaleY: 1 // Each segment stays at normal scale
});
// Handle own collision
self.update = function () {
if (CollisionManager.checkCollision(self, hero)) {
if (hero.shielded) {
hero.shieldLevel -= 1;
if (hero.shieldLevel <= 0) {
hero.shielded = false;
if (hero.shieldGraphics) {
hero.shieldGraphics.destroy();
hero.shieldGraphics = null;
}
}
} else {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
// Check clone collisions
for (var i = soldierClones.length - 1; i >= 0; i--) {
var clone = soldierClones[i];
if (CollisionManager.checkCollision(self, clone)) {
clone.health -= 1;
if (clone.health <= 0) {
// Play turn off effect before destroying
var tvEffect = new TVTurnOffEffect();
tvEffect.soldierClone = clone;
backgroundContainer.addChild(tvEffect);
// Note: actual destruction happens after animation completes
}
break;
}
}
};
return self;
});
var PlayerDrone = Container.expand(function () {
var self = Container.call(this);
var droneGraphics = self.attachAsset('PlayerDrone', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9,
scaleX: 0.75,
scaleY: 0.75
});
self.angle = 0; // Orbital angle
self.orbitRadius = 200; // Further increased distance from hero
self.orbitSpeed = 0.03; // Increased speed of rotation
self.health = 1;
self.lastCollisionFrame = 0;
self.update = function () {
// Orbit around hero
self.angle += self.orbitSpeed;
self.x = hero.x + Math.cos(self.angle) * self.orbitRadius;
self.y = hero.y + Math.sin(self.angle) * self.orbitRadius;
// Check collisions
if (LK.ticks > self.lastCollisionFrame) {
for (var i = 0; i < enemies.length; i++) {
if (CollisionManager.checkCollision(self, enemies[i])) {
self.lastCollisionFrame = LK.ticks;
self.destroy();
return;
}
}
}
// Auto-fire
if (LK.ticks % 18 == 0) {
self.shoot();
}
};
self.shoot = function () {
var bullet = new HeroBullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.Power = 1; // Always power level 1
bullet.updateVisual();
heroBullets.push(bullet);
game.addChild(bullet);
};
});
// PowerUp class for handling power-up movement and collection
var PowerUp = Container.expand(function (assetType, spawnSide) {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5; // Increased speed at which the power-up moves down the screen
self.movementPhase = 'ENTRY';
self.spawnSide = spawnSide; // Pass this in constructor
self.update = function () {
if (self.movementPhase === 'ENTRY') {
self.x += self.spawnSide === 'LEFT' ? self.speed : -self.speed;
if (Math.abs(self.x - 1024) < 5) {
// Check if near center
self.movementPhase = 'DESCENT';
self.x = 1024; // Snap to center
}
} else {
self.y += self.speed;
}
// Check if the power-up is collected by the hero
if (self.intersects(hero)) {
switch (assetType) {
case 'SoldierCloneIcon':
if (soldierClones.length < 4) {
hero.soldierCloneCount++;
var newClone = new SoldierClone();
// Spawn directly in combat position ahead of hero
newClone.x = 1024;
newClone.y = hero.y - 200;
newClone.zIndex = hero.zIndex - 1;
// Add clone to background container
backgroundContainer.addChild(newClone);
// Create effect (will handle initial clone scaling)
var tvEffect = new TVTurnOnEffect();
tvEffect.soldierClone = newClone;
backgroundContainer.addChild(tvEffect);
// Add to tracking array
soldierClones.push(newClone);
}
break;
case 'PlayerDroneIcon':
if (hero.droneCount < 3) {
// Maximum 3 drones
hero.droneCount = hero.droneCount || 0;
hero.droneCount++;
var newDrone = new PlayerDrone();
// Set initial angle based on drone count
newDrone.angle = (hero.droneCount - 1) * (Math.PI * 2 / 3);
playerDrones.push(newDrone);
backgroundContainer.addChild(newDrone);
}
break;
case 'WeaponPowerIcon':
powerUpManager.currentPowerLevel = Math.min(powerUpManager.currentPowerLevel + 1, 4);
console.log("Power up collected. New level: " + powerUpManager.currentPowerLevel);
break;
case 'ShieldIcon':
if (!hero.shielded) {
hero.shielded = true;
hero.shieldGraphics = hero.attachAsset('heroShield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5 // Set opacity to 50%
});
}
hero.shieldLevel = Math.min(hero.shieldLevel + 1, 3); // Increase shield level to a max of 3
// Change shield tint based on shield level
hero.shieldGraphics.tint = 0xFF0000; // Red tint for level 1
if (hero.shieldLevel === 2) {
hero.shieldGraphics.tint = 0xFFFF00; // Yellow tint for level 2
} else if (hero.shieldLevel === 3) {
hero.shieldGraphics.tint = 0x00FF00; // Green tint for level 3
}
break;
case 'BombIcon':
powerUpManager.switchPowerUp(PowerUpTypes.BOMB);
powerUpManager.bombBehavior();
break;
}
hero.shootBehavior = powerUpManager.getCurrentPowerBehavior();
soldierClones.forEach(function (clone) {
clone.shootBehavior = hero.shootBehavior;
});
self.destroy();
}
// Destroy if out of screen
if (self.y > 2732) {
self.destroy();
}
};
});
var PowerUpSpawner = Container.expand(function () {
var self = Container.call(this);
var timeSinceStart = 0;
var timeSinceLastSpawn = 0;
var spawnSide = 'LEFT';
self.update = function () {
timeSinceStart += 1;
timeSinceLastSpawn += 1;
// First spawn at 10 seconds (assuming 60 FPS)
if (timeSinceStart === 600) {
self.spawnPowerUp(self.getRandomPowerUp());
}
// Subsequent spawns every 30 seconds
else if (timeSinceLastSpawn >= 1800) {
self.spawnPowerUp(self.getRandomPowerUp());
timeSinceLastSpawn = 0;
}
};
self.getRandomPowerUp = function () {
var powerUps = ['SoldierCloneIcon', 'SoldierCloneIcon', 'SoldierCloneIcon', 'SoldierCloneIcon'];
if (hero.shieldLevel === 3) {
powerUps = powerUps.filter(function (powerUp) {
return powerUp !== 'ShieldIcon';
});
}
if (powerUpManager.currentPowerLevel === 3) {
powerUps = powerUps.filter(function (powerUp) {
return powerUp !== 'WeaponPowerIcon';
});
}
if (difficultyManager.currentDifficulty > 4) {
powerUps.push('BombIcon');
}
return powerUps[Math.floor(Math.random() * powerUps.length)];
};
self.spawnPowerUp = function (assetType) {
var currentSpawnSide = spawnSide; // Get current value before toggling
var powerUp = new PowerUp(assetType, currentSpawnSide);
powerUp.x = currentSpawnSide === 'LEFT' ? 0 : 2048;
powerUp.y = 200; // Spawn above screen but not too high
backgroundContainer.addChild(powerUp);
spawnSide = currentSpawnSide === 'LEFT' ? 'RIGHT' : 'LEFT'; // Toggle for next spawn
};
});
var Raider = Container.expand(function () {
var self = Container.call(this);
var raiderGraphics = self.attachAsset('BotBiker', {
anchorX: 0.5,
anchorY: 0.5
});
// Raider specific properties
self.HitPoints = 5;
self.speed = 3;
self.shootInterval = 66; // Reduced firing frequency by 10%
self.lastShotTime = 0;
self.trackingDelay = 30; // Delay before adjusting aim
self.trackingTime = 0; // Time since last tracking adjustment
self.targetRotation = raiderGraphics.rotation; // Target rotation for tracking
self.entryPhase = true; // Track if we're still in entry animation
self.speed = 3;
self.rotationSpeed = 0.05;
self.horizontalSpeed = 6; // Faster horizontal movement during entry
// Add scaling properties
self.maxTilt = 0.3; // How much to scale for tilt effect
self.baseScale = 1; // Normal scale
self.spawnSide = Math.random() < 0.5 ? 'left' : 'right';
// Set initial rotation based on spawn side
raiderGraphics.rotation = self.spawnSide === 'left' ? -Math.PI / 4 : Math.PI / 4;
self.update = function () {
if (self.entryPhase) {
// Entry movement
if (self.spawnSide === 'left') {
self.x += self.horizontalSpeed;
raiderGraphics.rotation = Math.min(raiderGraphics.rotation + self.rotationSpeed, 0);
// Scale for tilt effect
raiderGraphics.scale.x = self.baseScale - self.maxTilt;
if (self.x >= 600) {
// Move further onto road
self.entryPhase = false;
raiderGraphics.scale.x = self.baseScale;
}
} else {
self.x -= self.horizontalSpeed;
raiderGraphics.rotation = Math.max(raiderGraphics.rotation - self.rotationSpeed, 0);
// Scale for tilt effect
raiderGraphics.scale.x = self.baseScale - self.maxTilt;
if (self.x <= 1448) {
// 2048 - 600
self.entryPhase = false;
raiderGraphics.scale.x = self.baseScale;
}
}
} else {
self.y += self.speed * 0.25; // Reduce speed by 75% after entry
// Move back and forth across the road
if (self.spawnSide === 'left') {
self.x += self.horizontalSpeed;
if (self.x >= 1448) {
// Right boundary
self.spawnSide = 'right';
}
} else {
self.x -= self.horizontalSpeed;
if (self.x <= 600) {
// Left boundary
self.spawnSide = 'left';
}
}
// Tracking logic
if (self.trackingTime >= self.trackingDelay) {
var directionX = hero.x - self.x;
var directionY = hero.y - self.y;
self.targetRotation = Math.atan2(directionY, directionX) - Math.PI / 2;
self.trackingTime = 0;
} else {
self.trackingTime++;
}
// Swivel towards target rotation
raiderGraphics.rotation += (self.targetRotation - raiderGraphics.rotation) * 0.1;
// Shooting logic
if (LK.ticks - self.lastShotTime > self.shootInterval) {
self.shoot();
self.lastShotTime = LK.ticks;
}
}
// Keep collision handling
if (CollisionManager.checkCollision(self, hero)) {
// [Keep existing collision code]
}
// Keep destruction logic
if (self.HitPoints <= 0) {
// Create raider pieces upon destruction
generateEnemyPieces(self, RaiderPiece, ['RaiderTop', 'RaiderBottom']);
difficultyManager.onEnemyDestroyed();
} else if (self.y > 2732) {
// [Keep existing out-of-bounds code]
difficultyManager.onEnemyDestroyed();
}
};
self.shoot = function () {
if (self.spawnSide === 'left') {
var bullet = new RaiderBullet();
bullet.x = self.x;
bullet.y = self.y + raiderGraphics.height / 2;
bullet.rotation = raiderGraphics.rotation;
game.addChild(bullet);
self.spawnSide = self.spawnSide === 'left' ? 'right' : 'left'; // Alternate sides
} else {
var rightShot = new RaiderBullet();
rightShot.x = self.x + raiderGraphics.width / 4;
rightShot.y = self.y + raiderGraphics.height / 2;
game.addChild(rightShot);
self.spawnSide = 'left'; // Switch to left for next shot
}
};
});
var RaiderBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('heroBullet1', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -15 * 0.75; // 75% of hero bullet speed
self.update = function () {
self.y -= Math.cos(self.rotation) * self.speed;
self.x += Math.sin(self.rotation) * self.speed;
// Check collision with hero
if (CollisionManager.checkCollision(self, hero)) {
if (hero.shielded) {
hero.shieldLevel -= 1;
if (hero.shieldLevel <= 0) {
hero.shielded = false;
if (hero.shieldGraphics) {
hero.shieldGraphics.destroy();
hero.shieldGraphics = null;
}
} else {
hero.shieldGraphics.tint = 0xFFFFFF;
if (hero.shieldLevel === 2) {
hero.shieldGraphics.tint = 0xFFFF00;
} else if (hero.shieldLevel === 1) {
hero.shieldGraphics.tint = 0xFFFFFF;
}
}
} else {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
self.destroy();
return;
}
// Check collision with soldier clones
for (var i = soldierClones.length - 1; i >= 0; i--) {
var clone = soldierClones[i];
if (CollisionManager.checkCollision(self, clone)) {
clone.health -= 1;
if (clone.health <= 0) {
// Play turn off effect before destroying
var tvEffect = new TVTurnOffEffect();
tvEffect.soldierClone = clone;
backgroundContainer.addChild(tvEffect);
// Note: actual destruction happens after animation completes
hero.soldierCloneCount--; // Decrease hero clone count
}
self.destroy();
break;
}
}
if (self.y > 2732) {
self.destroy();
}
};
});
// RaiderPiece class for raider destruction effect
var RaiderPiece = Container.expand(function (assetType) {
var self = Container.call(this);
var pieceGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = (Math.random() - 0.5) * 10; // Random horizontal speed
self.speedY = (Math.random() - 0.5) * 10; // Random vertical speed
self.alphaDecay = 0.02; // Rate at which the piece fades out
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
pieceGraphics.rotation += Math.random() * 0.1 - 0.05; // Add slight random rotation
pieceGraphics.alpha -= self.alphaDecay;
if (pieceGraphics.alpha <= 0) {
self.destroy();
}
};
});
// RailingLeft class for the scrolling left railing
var RailingLeft = Container.expand(function (assetType) {
var self = Container.call(this);
var railingGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0
});
self.speed = 2;
self.update = function () {
self.y += self.speed;
if (self.y >= 2732) {
self.y = -2732;
}
};
});
// RailingRight class for the scrolling right railing
var RailingRight = Container.expand(function (assetType) {
var self = Container.call(this);
var railingGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0
});
self.speed = 2;
self.update = function () {
self.y += self.speed;
if (self.y >= 2732) {
self.y = -2732;
}
};
});
var RedDrone = Container.expand(function () {
var self = Container.call(this);
var droneGraphics = self.attachAsset('Drone', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply red tint to graphics
droneGraphics.tint = 0xFF0000;
// Set aggressive properties
self.HitPoints = 4;
self.randomOffset = Math.random() * 100;
self.speed = 4; // Faster than regular drone
self.update = function () {
if (hero) {
// Chase the hero
if (self.x < hero.x) {
self.x += self.speed;
} else if (self.x > hero.x) {
self.x -= self.speed;
}
self.y += self.speed;
}
// Keep pulse effect from base drone
if (self.pulseEffect) {
droneGraphics.scale.x = 1.2;
droneGraphics.scale.y = 1.2;
self.pulseEffect = false;
} else {
droneGraphics.scale.x = 1.0;
droneGraphics.scale.y = 1.0;
}
// Copy collision checks from base Drone
if (CollisionManager.checkCollision(self, hero)) {
if (hero.shielded) {
hero.shieldLevel -= 1;
if (hero.shieldLevel <= 0) {
hero.shielded = false;
if (hero.shieldGraphics) {
hero.shieldGraphics.destroy();
hero.shieldGraphics = null;
}
} else {
hero.shieldGraphics.tint = 0xFFFFFF;
if (hero.shieldLevel === 2) {
hero.shieldGraphics.tint = 0xFFFF00;
} else if (hero.shieldLevel === 1) {
hero.shieldGraphics.tint = 0xFFFFFF;
}
}
generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']);
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
} else {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
if (self.HitPoints <= 0) {
generateEnemyPieces(self, DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']);
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
difficultyManager.onEnemyDestroyed();
}
} else if (self.y > 2732) {
self.destroy();
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
difficultyManager.onEnemyDestroyed();
}
}
};
});
// RoadScroll class for the scrolling road
var RoadScroll = Container.expand(function (assetType) {
var self = Container.call(this);
var roadGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0
});
if (assetType === 'CityBackgroundHD') {
self.speed = 1;
} else {
self.speed = 2;
}
self.update = function () {
self.y += self.speed;
if (self.y >= 2732) {
self.y = -2732;
}
};
});
// SoldierClone class representing a clone of the player's character
var SoldierClone = Container.expand(function () {
var self = Container.call(this);
// Removed self.side property
var cloneGraphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7,
scaleX: 0.75,
scaleY: 0.75
});
self.roadLeft = 2048 * 0.21;
self.roadRight = 2048 * 0.76;
self.moveSpeed = 4.5;
self.health = 3;
self.isImmune = false;
self.dodgeSpeed = 5;
self.dodgeCooldown = 0;
self.forwardOffset = 150 + Math.random() * 50; // Variable distance ahead
self.horizontalPhase = Math.random() * Math.PI * 2; // Different horizontal movement phase
// Removed self.offset property
self.update = function () {
// Calculate desired x position based on index
var index = soldierClones.indexOf(self);
if (index !== -1) {
// Create an offset pattern that varies with time
var timeOffset = LK.ticks * 0.02; // Slow oscillation
var xOffset = Math.sin(timeOffset + index * (Math.PI / 2)) * 400; // 400 pixel spread
// Target position is center (1024) plus offset
var targetX = 1024 + xOffset;
// Smooth movement towards target
self.x += (targetX - self.x) * 0.1;
// Maintain vertical position ahead of hero
var targetY = hero.y - 200 - index * 50; // Stack vertically with gaps
self.y += (targetY - self.y) * 0.1;
// Add flickering effect based on health
if (self.health < 3) {
var flickerIntensity = (3 - self.health) * 0.2; // Increase intensity as health decreases
cloneGraphics.alpha = 0.5 + Math.sin(LK.ticks * 0.2) * flickerIntensity;
}
}
// Handle dodge cooldown
if (self.dodgeCooldown > 0) {
self.dodgeCooldown--;
}
// Check for bullets to dodge
var needsToDodge = false;
// Check for bullets to dodge
if (self.dodgeCooldown <= 0) {
enemies.forEach(function (enemy) {
if (enemy instanceof RaiderBullet) {
// Create a projected position of where bullet will be
var projectedY = enemy.y + (enemy.speed || 0) * 10; // Look 10 frames ahead
var projectedBullet = {
x: enemy.x,
y: projectedY,
width: enemy.width,
height: enemy.height
};
if (CollisionManager.checkCollision(self, projectedBullet)) {
needsToDodge = true;
// Dodge in opposite direction of bullet's X position relative to clone
var dodgeDirection = enemy.x > self.x ? -1 : 1;
var newX = self.x + self.dodgeSpeed * dodgeDirection;
// Stay within road
if (newX > self.roadLeft && newX < self.roadRight) {
self.x = newX;
}
self.dodgeCooldown = 30;
}
}
});
}
// Only target enemies if not dodging
// In the movement code
if (!needsToDodge) {
var nearestEnemy = null;
var nearestDist = Infinity;
enemies.forEach(function (enemy) {
if (enemy.y > self.y) {
return;
}
var dist = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (dist < nearestDist) {
nearestDist = dist;
nearestEnemy = enemy;
}
});
// If no target, maintain forward position with slight variation
if (!nearestEnemy) {
var horizontalWobble = Math.sin(LK.ticks * 0.03 + self.horizontalPhase) * 25;
var targetX = Math.min(Math.max(hero.x + horizontalWobble, self.roadLeft), self.roadRight);
var targetY = hero.y - self.forwardOffset;
// Smooth movement towards target
self.x += (targetX - self.x) * 0.1;
self.y += (targetY - self.y) * 0.1;
} else {
// When targeting enemy, maintain some spacing between clones
var targetX = nearestEnemy.x + Math.sin(LK.ticks * 0.03 + self.horizontalPhase) * 20;
targetX = Math.min(Math.max(targetX, self.roadLeft), self.roadRight);
self.x += (targetX - self.x) * 0.1;
}
}
// Check collisions once per frame
if (!self.isImmune && LK.ticks > self.lastCollisionFrame) {
for (var i = 0; i < enemies.length; i++) {
if (CollisionManager.checkCollision(self, enemies[i])) {
self.lastCollisionFrame = LK.ticks;
self.health--;
if (self.health <= 0) {
// Play turn off effect before destroying
var tvEffect = new TVTurnOffEffect();
tvEffect.soldierClone = self;
backgroundContainer.addChild(tvEffect);
// Note: actual destruction happens after animation completes
return; // Prevent further processing in this update cycle
}
return;
}
}
}
self.shoot();
};
self.destroy = function () {
// Remove bullets associated with this clone
for (var i = heroBullets.length - 1; i >= 0; i--) {
if (heroBullets[i].x === self.x && heroBullets[i].y === self.y - self.height / 2) {
heroBullets[i].destroy();
heroBullets.splice(i, 1);
}
}
// Removed side-based immunity code
// Update soldier clone count and array
var index = soldierClones.indexOf(self);
if (index > -1) {
soldierClones.splice(index, 1);
hero.soldierCloneCount--;
}
// Call original destroy method
Container.prototype.destroy.call(self);
};
// Method to handle shooting
self.shoot = function () {
if (LK.ticks % 18 == 0) {
// Match hero's fire rate
var newBullet = new HeroBullet(); // Use the same bullet type as the hero
newBullet.x = self.x; // Create bullets at the clone's current position
newBullet.y = self.y - self.height / 2;
newBullet.Power = heroBullets.length > 0 ? heroBullets[0].Power : 1; // Match hero's bullet power
newBullet.updateVisual(); // Update bullet visuals based on power
heroBullets.push(newBullet); // Add the new bullets to the game's bullet array
game.addChild(newBullet);
}
};
});
var TVTurnOffEffect = Container.expand(function () {
var self = Container.call(this);
self.startTime = LK.ticks;
self.animationDuration = 40;
var finalScaleX = 0.7;
self.update = function () {
var progress = (LK.ticks - self.startTime) / self.animationDuration;
if (progress <= 1) {
if (self.soldierClone) {
if (progress <= 0.3) {
// Quick shrink to line
var shrinkProgress = progress / 0.3;
self.soldierClone.alpha = 1;
self.soldierClone.scaleX = finalScaleX;
self.soldierClone.scaleY = finalScaleX * (1 - shrinkProgress) + 0.05;
var graphics = self.soldierClone.getChildAt(0);
if (graphics) {
graphics.scale.x = finalScaleX;
graphics.scale.y = self.soldierClone.scaleY;
}
} else {
// Longer flickering line phase before disappearing
self.soldierClone.alpha = 0.8 + Math.sin(progress * Math.PI * 10) * 0.2;
self.soldierClone.scaleX = finalScaleX;
self.soldierClone.scaleY = 0.05;
var graphics = self.soldierClone.getChildAt(0);
if (graphics) {
graphics.scale.x = finalScaleX;
graphics.scale.y = 0.05;
}
}
}
} else {
// Remove clone from arrays and destroy
if (self.soldierClone) {
var index = soldierClones.indexOf(self.soldierClone);
if (index !== -1) {
soldierClones.splice(index, 1);
}
self.soldierClone.destroy();
}
self.destroy();
}
};
return self;
});
var TVTurnOnEffect = Container.expand(function () {
var self = Container.call(this);
self.startTime = LK.ticks;
self.animationDuration = 40;
// Get the clone's intended final scale (should be smaller than hero)
var finalScaleX = 0.7; // Adjust this value to match the clone's proper size
var finalScaleY = 0.7;
self.update = function () {
var progress = (LK.ticks - self.startTime) / self.animationDuration;
if (progress <= 1) {
if (self.soldierClone) {
if (progress <= 0.7) {
// Line phase
self.soldierClone.alpha = 0.8 + Math.sin(progress * Math.PI * 10) * 0.2;
self.soldierClone.scaleX = finalScaleX; // Keep correct width
self.soldierClone.scaleY = 0.05; // Thin line
var graphics = self.soldierClone.getChildAt(0);
if (graphics) {
graphics.scale.x = finalScaleX;
graphics.scale.y = 0.05;
}
} else {
// Expand phase
var expandProgress = (progress - 0.7) / 0.3;
self.soldierClone.alpha = 1;
self.soldierClone.scaleX = finalScaleX;
self.soldierClone.scaleY = 0.05 + expandProgress * (finalScaleY - 0.05);
var graphics = self.soldierClone.getChildAt(0);
if (graphics) {
graphics.scale.x = finalScaleX;
graphics.scale.y = self.soldierClone.scaleY;
}
}
}
} else {
if (self.soldierClone) {
// Set final dimensions
self.soldierClone.alpha = 1;
self.soldierClone.scaleX = finalScaleX;
self.soldierClone.scaleY = finalScaleY;
var graphics = self.soldierClone.getChildAt(0);
if (graphics) {
graphics.scale.x = finalScaleX;
graphics.scale.y = finalScaleY;
}
}
self.destroy();
}
};
return self;
});
// TitleScreen class for the title screen
var TitleScreen = Container.expand(function () {
var self = Container.call(this);
// Attach game logo
var logo = self.attachAsset('GameLogoHD', {
anchorX: 0.5,
anchorY: 0.5
});
logo.x = 2048 / 2;
logo.y = 2732 / 2 - 200;
// Attach play button
var playButton = self.attachAsset('PlayButton', {
anchorX: 0.5,
anchorY: 0.5
});
playButton.x = 2048 / 2;
playButton.y = logo.y + logo.height / 2 + playButton.height / 2 + 50;
// Event handler for play button
playButton.down = function (x, y, obj) {
isTitle = false;
isStarted = true;
self.destroy();
startGame();
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 //Init game with black background
});
/****
* Game Code
****/
var playerDrones = [];
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) {
return t;
}
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) {
return i;
}
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function generateEnemyPieces(enemy, pieceClass, pieceTypes) {
pieceTypes.forEach(function (pieceType, index) {
var piece = new pieceClass(pieceType);
piece.x = enemy.x + (index - 1) * 40; // Spread pieces horizontally
piece.y = enemy.y;
game.addChild(piece);
});
}
var CollisionManager = {
checkCollision: function checkCollision(obj1, obj2) {
// Special case for laser beam
if (obj1.constructor.name === 'LaserBeam' || obj1 === 'LaserBeam') {
// Line segment collision
var laser = activeLaserBeams.find(function (l) {
return l.laserBeam === obj1;
});
if (!laser) {
return false;
}
// Get laser start and end points
var laserLength = obj1.height * obj1.scaleY;
var startX = obj1.x;
var startY = obj1.y;
var endX = startX - Math.sin(obj1.rotation) * laserLength;
var endY = startY + Math.cos(obj1.rotation) * laserLength;
// Get target radius for collision
var targetRadius = Math.min(obj2.width, obj2.height) * 0.4;
// Check if point-to-line distance is less than target radius
var dx = endX - startX;
var dy = endY - startY;
var len = Math.sqrt(dx * dx + dy * dy);
// Get point to line segment distance
var t = ((obj2.x - startX) * dx + (obj2.y - startY) * dy) / (len * len);
t = Math.max(0, Math.min(1, t));
var nearestX = startX + t * dx;
var nearestY = startY + t * dy;
// Check distance from nearest point
var distance = Math.sqrt((obj2.x - nearestX) * (obj2.x - nearestX) + (obj2.y - nearestY) * (obj2.y - nearestY));
return distance < targetRadius;
}
return this.circleCollision(obj1, obj2);
},
getObjectRadius: function getObjectRadius(obj) {
var width = obj.width;
var height = obj.height;
return Math.min(width, height) * 0.4;
},
circleCollision: function circleCollision(obj1, obj2) {
var radius1 = this.getObjectRadius(obj1);
var radius2 = this.getObjectRadius(obj2);
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
return distance < radius1 + radius2;
}
};
var PowerUpTypes = {
DEFAULT: 'DEFAULT',
SOLDIER_CLONE: 'SOLDIER_CLONE',
SHIELD: 'SHIELD',
WEAPON_POWER: 'WEAPON_POWER',
BOMB: 'BOMB',
// Add bomb type
PLAYER_DRONE: 'PLAYER_DRONE' // Add player drone type
};
// PowerUpManager class to manage power-ups
// Initialize the city background for parallax effect
var PowerUpManager = function PowerUpManager() {
this.currentPowerUp = PowerUpTypes.DEFAULT;
this.currentPowerLevel = 1; // Initialize power level to default value
this.switchPowerUp = function (newPowerUp) {
if (PowerUpTypes[newPowerUp]) {
this.currentPowerUp = newPowerUp;
}
};
this.getCurrentPowerBehavior = function () {
switch (this.currentPowerUp) {
case PowerUpTypes.SOLDIER_CLONE:
return this.soldierCloneBehavior;
case PowerUpTypes.BOMB:
return this.bombBehavior;
default:
return this.defaultBehavior;
}
};
this.defaultBehavior = function () {
var bullet = new HeroBullet();
bullet.x = hero.x;
bullet.y = hero.y - hero.height / 2;
bullet.Power = powerUpManager.currentPowerLevel || 1; // Use saved power level from PowerUpManager
bullet.updateVisual(); // Update the visual after setting power
heroBullets.push(bullet);
game.addChild(bullet);
console.log("Created bullet with power: " + bullet.Power);
};
this.soldierCloneBehavior = function () {
// Soldier clone behavior
// Removed clone spawning logic
};
this.bombBehavior = function () {
var blast = new BombBlast();
blast.x = 1024;
blast.y = 1366;
blast.zIndex = 2000; // Ensure BombBlast is on a visible layer
game.addChild(blast);
// Kill enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] && enemies[i].y > 0) {
enemies[i].HitPoints = 0;
// Generate pieces based on enemy type
if (enemies[i] instanceof Drone || enemies[i] instanceof RedDrone) {
generateEnemyPieces(enemies[i], DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']);
} else if (enemies[i] instanceof Bruiser) {
generateEnemyPieces(enemies[i], BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']);
} else if (enemies[i] instanceof Blaster) {
generateEnemyPieces(enemies[i], BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']);
} else if (enemies[i] instanceof Raider) {
generateEnemyPieces(enemies[i], RaiderPiece, ['RaiderTop', 'RaiderBottom']);
}
enemies[i].destroy();
enemies.splice(i, 1);
difficultyManager.onEnemyDestroyed();
}
}
// Flash effect after blast is created
LK.setTimeout(function () {
var flash = new Container();
var flashGraphics = flash.attachAsset('whitePixel', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 30
});
flash.x = game.width / 2;
flash.y = game.height / 2;
flash.alpha = 0.8;
game.addChild(flash);
LK.setTimeout(function () {
flash.destroy();
}, 100);
}, 100);
// Screen shake
var originalX = game.x;
var shakeAmount = 30; // Increase shake intensity
var shakeDuration = 100; // Increase shake duration
game.x += shakeAmount;
LK.setTimeout(function () {
game.x -= shakeAmount * 2;
LK.setTimeout(function () {
game.x += shakeAmount;
LK.setTimeout(function () {
game.x -= shakeAmount * 2;
LK.setTimeout(function () {
game.x += shakeAmount;
LK.setTimeout(function () {
game.x = originalX;
}, shakeDuration);
}, shakeDuration);
}, shakeDuration);
}, shakeDuration);
}, shakeDuration);
};
};
// PowerUpTypes enum/constants
var PowerUpTypes = {
DEFAULT: 'DEFAULT',
LASER_BEAM: 'LASER_BEAM',
ROCKETS: 'ROCKETS',
DRONE: 'DRONE',
SOLDIER_CLONE: 'SOLDIER_CLONE',
SHIELD: 'SHIELD',
// Add shield power-up type
WEAPON_POWER: 'WEAPON_POWER'
};
var LaserMechanic = function LaserMechanic(origin, chargeDuration, fireDuration, slideOffDuration) {
this.origin = origin;
this.chargeDuration = chargeDuration;
this.fireDuration = fireDuration;
this.slideOffDuration = slideOffDuration;
// State tracking
this.isCharging = false;
this.isFiring = false;
this.isSliding = false;
// Timing
this.chargeStartTime = null;
this.fireStartTime = null;
this.slideStartTime = null;
// Flag to indicate attack completion
this.attackComplete = false;
// Assets
this.chargeEffect = null;
this.laserBeam = null;
var index = activeLaserBeams.indexOf(this);
if (index > -1) {
activeLaserBeams.splice(index, 1);
}
// Method to start charging
LaserMechanic.prototype.startCharge = function () {
if (this.isCharging || this.isFiring || this.isSliding) {
return;
}
this.isCharging = true;
this.chargeStartTime = LK.ticks;
// Create charge effect
this.chargeEffect = LK.getAsset('LaserCharge', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5,
scale: {
x: 0.5,
y: 0.5
}
});
if (this.origin) {
this.chargeEffect.y += this.origin.height * 0.1; // Move charge effect down by 10% of origin's height
this.origin.addChild(this.chargeEffect);
}
};
// Target
this.targetX = null;
this.targetY = null;
// Method to update charge progress
LaserMechanic.prototype.updateCharge = function () {
if (!this.isCharging) {
return;
}
var chargeProgress = (LK.ticks - this.chargeStartTime) / this.chargeDuration;
// Update charge effect
this.chargeEffect.alpha = 0.5 + chargeProgress * 0.5; // Fade in
this.chargeEffect.scale.x = 0.5 + chargeProgress * 1.5; // Grow outward
this.chargeEffect.scale.y = 0.5 + chargeProgress * 1.5;
// Capture target position at 2/3 through charge
if (chargeProgress >= 0.67 && !this.targetX) {
this.targetX = hero.x;
this.targetY = hero.y;
}
// Check if charge is complete
if (chargeProgress >= 1) {
if (this.origin && this.chargeEffect) {
this.origin.removeChild(this.chargeEffect);
}
this.chargeEffect = null;
this.isCharging = false;
this.startFiring(); // Add this line to transition to firing phase
}
};
};
var cityBackground1 = game.addChild(new RoadScroll('CityBackgroundHD'));
var cityBackground2 = game.addChild(new RoadScroll('CityBackgroundHD'));
cityBackground1.x = 2048 / 2;
cityBackground2.x = 2048 / 2;
cityBackground1.y = 0;
cityBackground2.y = -2732;
// Initialize the road instances
var road1 = game.addChild(new RoadScroll('Road'));
var road2 = game.addChild(new RoadScroll('Road2'));
road2.x = 2048 / 2;
road1.x = 2048 / 2;
road1.y = 0;
road2.y = -2732;
// Initialize the left railing instances
var railingLeft1 = game.addChild(new RailingLeft('RailingStart'));
var railingLeft2 = game.addChild(new RailingLeft('RailingEnd'));
railingLeft1.x = 2048 * 0.16;
railingLeft2.x = 2048 * 0.16;
railingLeft1.y = 0;
railingLeft2.y = -2732;
// Initialize the right railing instances
var railingRight1 = game.addChild(new RailingRight('RailingEnd'));
var railingRight2 = game.addChild(new RailingRight('RailingStart'));
railingRight1.x = 2048 * 0.81; // Moved left by 3%
railingRight2.x = 2048 * 0.81; // Moved left by 3%
railingRight1.y = 0;
railingRight2.y = -2732;
// Initialize game state variables
var isTitle = true;
var isStarted = false;
// Initialize title screen
if (isTitle) {
var titleScreen = game.addChild(new TitleScreen());
}
// Initialize variables
var backgroundContainer;
var hero;
var enemies = [];
var heroBullets = [];
var soldierClones = [];
var powerUpManager = new PowerUpManager(); // Initialize powerUpManager
powerUpManager.currentPowerLevel = 1; // Track current power level at start
// Function to start the game
function startGame() {
// Initialize background container
backgroundContainer = new Container();
game.addChild(backgroundContainer);
difficultyManager = new DifficultyManager();
difficultyManager.updateSpawnQuota();
// Initialize hero
hero = game.addChild(new Hero());
// Initialize PowerUpSpawner
var powerUpSpawner = new PowerUpSpawner();
backgroundContainer.addChild(powerUpSpawner);
hero.soldierCloneCount = soldierClones.length; // Set initial soldier clone count
hero.x = 2048 / 2;
hero.y = 2732 + hero.height;
hero.shielded = true; // Start hero with shield
hero.shieldLevel = 3; // Set shield level to 3
hero.shieldGraphics = hero.attachAsset('heroShield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5 // Set opacity to 50%
});
hero.shieldGraphics.tint = 0xFF0000; // Red tint for level 3 shield
// Initialize enemies, bullets, and laser beams arrays
enemies = [];
heroBullets = [];
activeLaserBeams = [];
hero.shootBehavior = powerUpManager.getCurrentPowerBehavior().bind(hero);
}
// Function to handle game updates
game.update = function () {
soldierClones.forEach(function (clone) {
clone.shootBehavior = hero.shootBehavior;
});
cityBackground1.update();
cityBackground2.update();
// Update the road instances
road1.update();
road2.update();
// Update the left railing instances
railingLeft1.update();
railingLeft2.update();
// Update the right railing instances
railingRight1.update();
railingRight2.update();
if (isStarted) {
if (dragNode) {
hero.x = dragNode.x;
hero.y = dragNode.y;
}
// Update enemies
if (!enemies) {
return;
} // Ensure enemies array is initialized
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i]) {
enemies[i].update();
}
if (enemies[i] && CollisionManager.checkCollision(enemies[i], hero)) {
if (hero.shielded) {
hero.shieldLevel -= 1;
if (hero.shieldLevel <= 0) {
hero.shielded = false;
if (hero.shieldGraphics) {
hero.shieldGraphics.destroy();
hero.shieldGraphics = null;
}
} else {
// Update shield tint based on new shield level
hero.shieldGraphics.tint = 0xFF0000; // Red tint for level 1
if (hero.shieldLevel === 2) {
hero.shieldGraphics.tint = 0xFFFF00; // Yellow tint for level 2
} else if (hero.shieldLevel === 3) {
hero.shieldGraphics.tint = 0x00FF00; // Green tint for level 3
}
}
// Destroy enemy into pieces
if (enemies[i] instanceof Drone) {
generateEnemyPieces(enemies[i], DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']);
} else if (enemies[i] instanceof Bruiser) {
generateEnemyPieces(enemies[i], BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']);
} else if (enemies[i] instanceof Blaster) {
generateEnemyPieces(enemies[i], BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']);
}
enemies[i].destroy();
enemies.splice(i, 1);
difficultyManager.onEnemyDestroyed();
} else {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
for (var l = activeLaserBeams.length - 1; l >= 0; l--) {
var laser = activeLaserBeams[l];
if (laser.segments) {
// Check if it has segments
// If origin (Blaster) is gone and not sliding yet, start sliding
if (!laser.origin && !laser.isSliding && laser.segments.length > 0) {
laser.startSliding();
}
laser.update();
// Clean up if complete or all segments are off screen
if (laser.isAttackComplete() || laser.segments.length === 0) {
// Final cleanup of any remaining segments
if (laser.segments) {
for (var i = laser.segments.length - 1; i >= 0; i--) {
game.removeChild(laser.segments[i]);
}
laser.segments = [];
}
activeLaserBeams.splice(l, 1);
}
} else {
// No segments found, remove from active lasers
activeLaserBeams.splice(l, 1);
}
}
difficultyManager.update();
// Update hero bullets
for (var j = heroBullets.length - 1; j >= 0; j--) {
heroBullets[j].update();
for (var k = enemies.length - 1; k >= 0; k--) {
if (heroBullets[j] && CollisionManager.checkCollision(heroBullets[j], enemies[k]) && enemies[k].y > 0) {
enemies[k].HitPoints -= heroBullets[j].Power;
// Apply an increased push back effect
if (!(enemies[k] instanceof Bruiser)) {
enemies[k].y -= 12; // Push the drone upwards slightly more
}
enemies[k].pulseEffect = true; // Trigger pulse effect
// Removed tracerTail reference
heroBullets[j].destroy();
heroBullets.splice(j, 1);
if (enemies[k].HitPoints <= 0) {
if (enemies[k] instanceof Drone || enemies[k] instanceof RedDrone) {
generateEnemyPieces(enemies[k], DronePiece, ['DroneCenterPart', 'DroneLeftPart', 'DroneRightPart']);
} else if (enemies[k] instanceof Bruiser) {
generateEnemyPieces(enemies[k], BruiserPiece, ['BruiserCenterPart', 'BruiserLeftPart', 'BruiserRightPart']);
} else if (enemies[k] instanceof Blaster) {
// In Blaster's destroy method or where Blaster destruction is handled
if (this.laserMechanic) {
this.laserMechanic.origin = null; // Detach from Blaster
if (this.laserMechanic.isFiring) {
// If it's firing, transition to sliding
this.laserMechanic.startSliding();
}
// Don't destroy the laser mechanic - let it clean itself up
}
generateEnemyPieces(enemies[k], BlasterPiece, ['BlasterBottomPiece', 'BlasterLeftPiece', 'BlasterRightPiece']);
} else if (enemies[k] instanceof Raider) {
generateEnemyPieces(enemies[k], RaiderPiece, ['RaiderTop', 'RaiderBottom']);
}
enemies[k].destroy();
enemies.splice(k, 1);
difficultyManager.onEnemyDestroyed();
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
}
break;
}
}
}
// Fire bullets using current power-up behavior
if (LK.ticks % 18 == 0) {
if (hero && hero.shootBehavior) {
hero.shootBehavior();
}
}
// Generate dust particles in sync with player pulsing
if (LK.ticks % 24 == 0) {
var newDust = new Dust();
newDust.x = hero.x - hero.width / 4; // Move dust spawn position right half as much as the last move
newDust.y = hero.y + hero.height / 2 * 0.93; // Move dust spawn point up 3% more
game.addChild(newDust);
} else if (LK.ticks % 24 == 12) {
var newDust = new Dust();
newDust.x = hero.x + hero.width / 4; // Create another dust spawn point an equal distance in from the right of the player asset
newDust.y = hero.y + hero.height / 2 * 0.93; // Move dust spawn point up 3% more
game.addChild(newDust);
}
}
};
// Handle touch input for hero movement
var dragNode = null;
game.down = function (x, y, obj) {
if (isStarted) {
dragNode = {
x: hero.x,
y: hero.y
};
// Add a tilt-back effect during initial movement
hero.rotation = x > hero.x ? Math.PI / 180 * -1 : Math.PI / 180 * 1;
}
};
game.move = function (x, y, obj) {
if (isStarted && dragNode) {
// Add a slight delay to the drag control to make the player feel like it has more weight
dragNode.x += (x - dragNode.x) * 0.1;
dragNode.y = hero.y; // Lock the y position to the hero's initial y position
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Display score
var scoreTxt = new Text2('0', {
size: 150,
fill: "#ffffff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.setText(LK.getScore());
LaserMechanic.prototype.startFiring = function () {
if (!this.origin || !this.targetX || !this.targetY || this.isFiring) {
return;
}
this.isFiring = true;
this.fireStartTime = LK.ticks;
// Calculate direction
var dirX = this.targetX - this.origin.x;
var dirY = this.targetY - this.origin.y;
this.rotation = Math.atan2(dirY, dirX) - Math.PI / 2;
// Create array to hold segments
this.segments = [];
// Create initial segment with adjusted position
var segment = new LaserBeamSegment();
// Match the origin point of the Blaster
segment.x = this.origin.x;
segment.y = this.origin.y + this.origin.height * 0.1; // Small offset like original
segment.rotation = this.rotation;
game.addChild(segment);
this.segments.push(segment);
};
LaserMechanic.prototype.updateFiring = function () {
if (!this.isFiring) {
return;
}
var fireProgress = (LK.ticks - this.fireStartTime) / this.fireDuration;
if (fireProgress <= 1) {
// Add new segments quickly until desired length
if (this.segments.length < 20 && LK.ticks % 2 === 0) {
// Adjust segment count and speed
var lastSegment = this.segments[this.segments.length - 1];
var segment = new LaserBeamSegment();
// Position new segment based on last one
segment.x = lastSegment.x - Math.sin(this.rotation) * segment.height;
segment.y = lastSegment.y + Math.cos(this.rotation) * segment.height;
segment.rotation = this.rotation;
game.addChild(segment);
this.segments.push(segment);
}
} else {
this.isFiring = false;
this.startSliding();
}
};
LaserMechanic.prototype.startSliding = function () {
this.isSliding = true;
this.slideStartTime = LK.ticks;
};
LaserMechanic.prototype.updateSliding = function () {
if (!this.isSliding) {
return;
}
var slideProgress = (LK.ticks - this.slideStartTime) / this.slideOffDuration;
if (slideProgress <= 1) {
// Move all segments
var allOffScreen = true;
for (var i = 0; i < this.segments.length; i++) {
var segment = this.segments[i];
segment.x += -Math.sin(this.rotation) * 20;
segment.y += Math.cos(this.rotation) * 20;
// Check if any segment is still on screen
if (segment.y > 0 && segment.y < 2048 && segment.x > 0 && segment.x < 2048) {
allOffScreen = false;
}
}
// If all segments are off screen, clean up early
if (allOffScreen) {
slideProgress = 1;
}
}
if (slideProgress > 1) {
// Clean up all segments
for (var i = this.segments.length - 1; i >= 0; i--) {
game.removeChild(this.segments[i]);
}
this.segments = [];
this.isSliding = false;
this.attackComplete = true;
}
};
// Method to reset attack states
LaserMechanic.prototype.resetAttack = function () {
this.isCharging = false;
this.isFiring = false;
this.isSliding = false;
this.targetX = null;
this.targetY = null;
this.attackComplete = false;
return true;
};
LaserMechanic.prototype.update = function () {
// Add state validation
if (!this.isCharging && !this.isFiring && !this.isSliding && this.laserBeam) {
// If we have a beam but no state, force sliding state
this.isSliding = true;
this.slideStartTime = LK.ticks;
}
// Your existing debug marker
var stateMarker = LK.getAsset('LaserCharge', {
anchorX: 0.5,
anchorY: 0.5,
scale: {
x: 1,
y: 1
},
alpha: 0.8,
tint: this.isCharging ? 0xFF0000 : this.isFiring ? 0x00FF00 : 0x0000FF
});
stateMarker.x = 50;
stateMarker.y = 50;
game.addChild(stateMarker);
if (this.isCharging) {
this.updateCharge();
} else if (this.isFiring) {
this.updateFiring();
} else if (this.isSliding || this.laserBeam) {
this.updateSliding();
}
};
// Method to check if the attack sequence is complete
LaserMechanic.prototype.isAttackComplete = function () {
return this.attackComplete;
};
LaserMechanic.prototype.detachBeam = function () {
if (this.laserBeam) {
// Detach laser from origin
this.origin = null;
// Force laser into sliding phase
this.startSliding();
}
};
View of a futuristic soldier from directly overhead. White armor with blue glowing cyberpunk details. Holding weapon forward.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
The lights of a futuristic city in the dark at night. Very high above it looking straight down like from an airplane or a map. Background for an endlessly scrolling game.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A big button that say Play to start playing a game. Use neon cyberpunk style.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Overhead view. A hovering robot with a tapered midsection with two bulky arms with claw like hands and a giant red “eye” on top of its body. Looking straight down. Cyberpunk, black with red glowing highlights.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Overhead view. A heavily armored attack robot. Two arms with large gauntlet type fists. Four large red glowing eyes. Three distinct parts, body and two arms. Symmetrical design. Birds Eye view above them looking down on their head. Simple shapes. Low detail. Cyberpunk, black with red glowing highlights.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A red glowing line. Bright red core with subtle outer glow. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A blue transparent dome type shield. Simple graphics. Low details. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A ring of nuclear fire seen from overhead. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A thin robot with goggles riding a hover-bike. Twin blaster guns mounted on front. Top down view. Birds Eye view. Cyberpunk with red glowing highlights... Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Battle drone, circular. White with blue glowing highlights. Birds Eye view from overhead. Cyberpunk. Simple shapes.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
GameTheme
Music
TitleTheme
Music
HeroBlaster
Sound effect
Explosion
Sound effect
PowerUp
Sound effect
CloneSoldier
Sound effect
WeaponPowerUp
Sound effect
Drone
Sound effect
BinaryStorm
Sound effect
LaserCharge
Sound effect
LaserFire
Sound effect
BruiserStomp
Sound effect
RaiderSwoop
Sound effect
ShieldLevelUp
Sound effect
HeroHit
Sound effect
HeroScream
Sound effect