User prompt
Make the power icon a little futlrther down on the upgrade cards
User prompt
Optimize affects to make the game less laggy
User prompt
Do the promp that was not finished
User prompt
Make a sound for when you die
User prompt
Make orbital objects move slower
User prompt
Add a sound for when orbital objects hit monsters
User prompt
Make orbital objects more futuristic ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add a sound when lazer is fired
User prompt
Change the word arrows on the upgrade cards to lazers
User prompt
Make the arrows lazers
User prompt
Move the title of the upgrade cards to over the colored icon
User prompt
MKe it so the upgrade cards description is not colliding with the name of the upgrade
User prompt
MKe it so if you have multiple orbital objects They do not colide
User prompt
Change the game naMe to space archers
User prompt
Make the orbiting shields energy barriers
User prompt
Make the stars vary in color
User prompt
Make the purple in the background darker
User prompt
MAke the background have just the slightest tint of purple ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add stars on the backround
User prompt
Change monster color to blue and purple
User prompt
Make monster bigger and change to yellow
User prompt
Make the player bigger
User prompt
Make the player bigger
User prompt
Make the spaceship more realistic ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highscore: 0, totalGamesPlayed: 0 }); /**** * Classes ****/ var Arrow = Container.expand(function () { var self = Container.call(this); // Create laser beam instead of arrow self.arrowBody = self.attachAsset('arrow', { anchorX: 0, anchorY: 0.5, scaleX: 2.0, scaleY: 0.5, tint: 0x33CCFF // Blue laser color }); self.arrowHead = self.attachAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, x: 80, scaleX: 0.6, scaleY: 0.6, tint: 0xFFFFFF // Bright white laser impact point }); // Add laser glow effect self.glow = LK.getAsset('arrow', { anchorX: 0, anchorY: 0.5, scaleX: 2.2, scaleY: 1.8, tint: 0x33CCFF, alpha: 0.5 }); self.addChildAt(self.glow, 0); self.speed = 20; // Faster for lasers self.damage = 10; self.pierce = 0; self.pierceCount = 0; self.hitEnemies = []; self.specialType = null; // To store special ability type (poison, explosive, etc.) self.specialData = {}; // For any special ability-specific data self.update = function () { // Handle homing behavior if enabled if (self.homingEnabled && monsters.length > 0) { // Initialize the homing start time if it's not set if (!self.homingStartTime) { self.homingStartTime = Date.now(); } // Check if the homing arrow has existed for more than 5 seconds if (Date.now() - self.homingStartTime > 5000) { self.needsRemoval = true; return; } // Find nearest monster that hasn't been hit yet var closestDist = Infinity; var closestMonster = null; for (var i = 0; i < monsters.length; i++) { if (self.hitEnemies.indexOf(monsters[i].id) === -1) { var dx = monsters[i].x - self.x; var dy = monsters[i].y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < closestDist) { closestDist = dist; closestMonster = monsters[i]; } } } // If we found a target, gradually adjust arrow direction if (closestMonster) { var targetAngle = Math.atan2(closestMonster.y - self.y, closestMonster.x - self.x); // Normalize angles while (targetAngle < -Math.PI) targetAngle += Math.PI * 2; while (targetAngle > Math.PI) targetAngle -= Math.PI * 2; while (self.rotation < -Math.PI) self.rotation += Math.PI * 2; while (self.rotation > Math.PI) self.rotation -= Math.PI * 2; // Find shortest direction to rotate var diff = targetAngle - self.rotation; if (diff > Math.PI) diff -= Math.PI * 2; if (diff < -Math.PI) diff += Math.PI * 2; // Adjust rotation gradually self.rotation += diff * self.homingSpeed; // Create trail effect for homing arrows - reduced frequency if (LK.ticks % 6 === 0) { var trail = new Container(); var trailParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0x33CCFF, alpha: 0.7 }); trail.x = self.x; trail.y = self.y; trail.addChild(trailParticle); game.addChild(trail); // Remove additional particles to improve performance // Fade out trail tween(trailParticle, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 200, onFinish: function onFinish() { if (trail.parent) { trail.parent.removeChild(trail); } } }); } } } // Move arrow in current direction self.x += Math.cos(self.rotation) * self.speed; self.y += Math.sin(self.rotation) * self.speed; // Standard arrow removal when off screen if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { self.needsRemoval = true; } }; self.canHit = function (monster) { if (self.hitEnemies.indexOf(monster.id) !== -1) { return false; } return true; }; self.hit = function (monster) { self.hitEnemies.push(monster.id); self.pierceCount++; // Create laser impact effect var impact = new Container(); var impactFlash = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, tint: 0xFFFFFF, alpha: 0.9 }); impact.x = monster.x; impact.y = monster.y; impact.addChild(impactFlash); game.addChild(impact); // Reduce particle count for better performance for (var i = 0; i < 3; i++) { var angle = Math.PI * 2 * Math.random(); var energyParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0x99FFFF, alpha: 0.8 }); energyParticle.x = Math.cos(angle) * 5; energyParticle.y = Math.sin(angle) * 5; impact.addChild(energyParticle); // Animate particle spread tween(energyParticle, { x: energyParticle.x * 8, y: energyParticle.y * 8, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 200 }); } // Animate impact flash tween(impactFlash, { scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (impact.parent) { impact.parent.removeChild(impact); } } }); // Apply special effects if (self.specialType === "poison") { // Apply poison effect to monster if (!monster.poisoned) { monster.poisoned = true; monster.poisonDamage = self.damage * 0.2; // 20% of arrow damage per tick monster.poisonTicks = 5; // Duration in ticks monster.body.tint = 0x00FF00; // Green tint for poisoned monsters // Create poison cloud visual effect var poisonCloud = new Container(); var cloudCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: 0x00FF00, alpha: 0.4 }); poisonCloud.x = monster.x; poisonCloud.y = monster.y; game.addChild(poisonCloud); poisonCloud.addChild(cloudCircle); // Animate the poison cloud tween(cloudCircle, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 1000, onFinish: function onFinish() { poisonCloud.parent.removeChild(poisonCloud); } }); } } else if (self.specialType === "explosive") { // Create explosion effect and damage nearby monsters LK.effects.flashObject(self, 0xFF9900, 200); // Create explosion visual effect var explosion = new Container(); var explosionCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3, tint: 0xFF9900, alpha: 0.7 }); explosionCircle.x = self.x; explosionCircle.y = self.y; game.addChild(explosion); explosion.addChild(explosionCircle); // Animate the explosion tween(explosionCircle, { scaleX: 5, scaleY: 5, alpha: 0 }, { duration: 500, onFinish: function onFinish() { explosion.parent.removeChild(explosion); } }); // Find and damage nearby monsters var now = Date.now(); for (var i = 0; i < monsters.length; i++) { var otherMonster = monsters[i]; if (otherMonster && otherMonster.id !== monster.id) { var dx = otherMonster.x - self.x; var dy = otherMonster.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 150) { // Explosion radius otherMonster.takeDamage(self.damage * 0.5); // 50% damage to nearby monsters } } } } else if (self.specialType === "freeze") { // Slow down monster monster.frozen = true; monster.originalSpeed = monster.speed; monster.speed = monster.speed * 0.5; // 50% slower monster.body.tint = 0x00FFFF; // Cyan tint for frozen monsters // Create ice/frost visual effect var iceEffect = new Container(); var frostCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3, tint: 0x00FFFF, alpha: 0.5 }); iceEffect.x = monster.x; iceEffect.y = monster.y; game.addChild(iceEffect); iceEffect.addChild(frostCircle); // Add ice crystal particles for (var i = 0; i < 6; i++) { var iceParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, tint: 0xCCFFFF }); iceParticle.x = Math.random() * 40 - 20; iceParticle.y = Math.random() * 40 - 20; iceEffect.addChild(iceParticle); } // Animate the ice effect tween(frostCircle, { alpha: 0.1 }, { duration: 2800, onFinish: function onFinish() { if (iceEffect.parent) { iceEffect.parent.removeChild(iceEffect); } } }); // Reset speed after 3 seconds LK.setTimeout(function () { if (monster && monster.frozen) { monster.frozen = false; monster.speed = monster.originalSpeed; monster.body.tint = 0xFFFFFF; // Reset tint } }, 3000); } else if (self.specialType === "fire") { // Apply burning effect to monster if (!monster.burning) { monster.burning = true; monster.burnDamage = self.damage * 0.3; // 30% of arrow damage per tick monster.burnTicks = 3; // Duration in ticks monster.body.tint = 0xFF3300; // Orange-red tint for burning monsters // Create fire visual effect var fireEffect = new Container(); var flameCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, tint: 0xFF3300, alpha: 0.7 }); fireEffect.x = monster.x; fireEffect.y = monster.y; game.addChild(fireEffect); fireEffect.addChild(flameCircle); // Animate the flames with flickering effect var fadeTime = 800; var startTime = Date.now(); var flameAnimation = LK.setInterval(function () { var elapsed = Date.now() - startTime; if (elapsed >= fadeTime) { LK.clearInterval(flameAnimation); if (fireEffect.parent) { fireEffect.parent.removeChild(fireEffect); } return; } // Make flames flicker by varying the alpha flameCircle.alpha = 0.7 * (1 - elapsed / fadeTime) * (0.7 + Math.random() * 0.3); // Move flame with monster if (monster && !monster.destroyed) { fireEffect.x = monster.x; fireEffect.y = monster.y; } }, 60); } } else if (self.specialType === "lightning") { // Chain lightning effect to nearby monsters LK.effects.flashObject(self, 0xFFFF00, 200); var chainedMonsters = []; chainedMonsters.push(monster.id); // Chain to up to 3 nearby monsters for (var i = 0; i < monsters.length && chainedMonsters.length < 4; i++) { var otherMonster = monsters[i]; if (otherMonster && chainedMonsters.indexOf(otherMonster.id) === -1) { var dx = otherMonster.x - monster.x; var dy = otherMonster.y - monster.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 200) { otherMonster.takeDamage(self.damage * 0.4); otherMonster.body.tint = 0xFFFF00; // Yellow tint for lightning chainedMonsters.push(otherMonster.id); // Create lightning effect between monsters var lightningStart = { x: monster.x, y: monster.y }; var lightningEnd = { x: otherMonster.x, y: otherMonster.y }; // Create lightning bolt visual effect var bolt = new Container(); var segment = LK.getAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: dist / 50, scaleY: 0.6, tint: 0xFFFF00 }); // Position and rotate the segment to connect the two points segment.x = (lightningStart.x + lightningEnd.x) / 2; segment.y = (lightningStart.y + lightningEnd.y) / 2; segment.rotation = Math.atan2(lightningEnd.y - lightningStart.y, lightningEnd.x - lightningStart.x); bolt.addChild(segment); game.addChild(bolt); // Animate the lightning bolt tween(segment, { alpha: 0.2 }, { duration: 300, onFinish: function onFinish() { if (bolt.parent) { bolt.parent.removeChild(bolt); } } }); LK.setTimeout(function () { if (otherMonster && otherMonster.body) { otherMonster.body.tint = 0xFFFFFF; } }, 300); } } } } else if (self.specialType === "vampiric") { // Heal player based on damage done if (player) { var healAmount = self.damage * 0.3; // Heal for 30% of damage dealt player.health = Math.min(player.health + healAmount, player.maxHealth); LK.effects.flashObject(player, 0xFF00FF, 200); // Purple flash for healing // Create vampiric drain visual effect var vampiricEffect = new Container(); var startPoint = { x: monster.x, y: monster.y }; var endPoint = { x: player.x, y: player.y }; var distance = Math.sqrt(Math.pow(endPoint.x - startPoint.x, 2) + Math.pow(endPoint.y - startPoint.y, 2)); var angleRad = Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x); // Create the drain beam var drainBeam = LK.getAsset('arrow', { anchorX: 0, anchorY: 0.5, scaleX: distance / 50, scaleY: 0.4, tint: 0xFF00FF, alpha: 0.7 }); drainBeam.x = startPoint.x; drainBeam.y = startPoint.y; drainBeam.rotation = angleRad; vampiricEffect.addChild(drainBeam); game.addChild(vampiricEffect); // Add life particles traveling along the beam for (var i = 0; i < 3; i++) { var lifeParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7, tint: 0xFF00FF, alpha: 0.8 }); lifeParticle.x = startPoint.x; lifeParticle.y = startPoint.y; vampiricEffect.addChild(lifeParticle); // Animate particles moving from monster to player tween(lifeParticle, { x: endPoint.x, y: endPoint.y, alpha: 0.3 }, { duration: 500 + i * 100, easing: tween.easeOut }); } // Fade out the beam tween(drainBeam, { alpha: 0 }, { duration: 700, onFinish: function onFinish() { if (vampiricEffect.parent) { vampiricEffect.parent.removeChild(vampiricEffect); } } }); } // No rebounding arrows functionality if (self.pierceCount > self.pierce) { self.needsRemoval = true; } } else if (self.specialType === "homing") { // Create homing visual effect var homingEffect = new Container(); var targetMarker = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, tint: 0xFF6600, alpha: 0.7 }); homingEffect.x = monster.x; homingEffect.y = monster.y; game.addChild(homingEffect); homingEffect.addChild(targetMarker); // Animate the homing effect tween(targetMarker, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 400, onFinish: function onFinish() { if (homingEffect.parent) { homingEffect.parent.removeChild(homingEffect); } } }); // Set arrow to homing mode if (!self.homingEnabled && monsters.length > 0) { self.homingEnabled = true; self.homingTarget = null; // Will be set in update method self.homingSpeed = 0.1; // Turning rate LK.effects.flashObject(self, 0xFF6600, 200); } } if (self.pierceCount > self.pierce && !self.hasRebounded && !self.homingEnabled) { self.needsRemoval = true; } }; return self; }); var Boss = Container.expand(function () { var self = Container.call(this); self.id = Date.now() + Math.floor(Math.random() * 10000); // Create larger body for boss monsters self.body = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3 }); // Health bar for boss self.healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5, y: -100, scaleX: 1.5, scaleY: 1 }); self.healthBar = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5, y: -100, x: -150, scaleX: 1.5 }); // Stats will be set when spawned self.maxHealth = 200; self.health = self.maxHealth; self.speed = 1; self.damage = 20; self.lastAttackTime = 0; self.attackCooldown = 1500; self.bossLevel = 1; // Will be set based on wave // Special abilities flags self.canSpawnMinions = false; self.lastMinionSpawnTime = 0; self.minionSpawnCooldown = 5000; self.shieldActive = false; self.update = function () { if (player && !paused) { var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } var now = Date.now(); // Attack player if (dist < 150) { // Larger attack range if (now - self.lastAttackTime > self.attackCooldown) { self.lastAttackTime = now; // Create attack effect var attackEffect = new Container(); var attackCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, tint: 0xFF0000, alpha: 0.5 }); attackEffect.x = self.x; attackEffect.y = self.y; game.addChild(attackEffect); attackEffect.addChild(attackCircle); // Animate attack tween(attackCircle, { scaleX: 4, scaleY: 4, alpha: 0 }, { duration: 500, onFinish: function onFinish() { if (attackEffect.parent) { attackEffect.parent.removeChild(attackEffect); } } }); if (player.takeDamage(self.damage)) { LK.getSound('death').play(); gameOver(); } } } // Spawn minions ability if (self.canSpawnMinions && now - self.lastMinionSpawnTime > self.minionSpawnCooldown) { self.lastMinionSpawnTime = now; // Create spawn effect var spawnEffect = new Container(); var spawnCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: 0x9900FF, alpha: 0.7 }); spawnEffect.x = self.x; spawnEffect.y = self.y; game.addChild(spawnEffect); spawnEffect.addChild(spawnCircle); // Animate spawn effect tween(spawnCircle, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 700, onFinish: function onFinish() { if (spawnEffect.parent) { spawnEffect.parent.removeChild(spawnEffect); } } }); // Spawn 2-3 minions around boss var minionCount = 2 + Math.floor(Math.random()); for (var i = 0; i < minionCount; i++) { var angle = Math.PI * 2 * (i / minionCount); var monster = new Monster(); monster.maxHealth = 15 + self.bossLevel * 2; monster.health = monster.maxHealth; monster.speed = 1.5 + self.bossLevel * 0.1; monster.body.tint = 0x9900FF; // Mark as boss minions monster.x = self.x + Math.cos(angle) * 150; monster.y = self.y + Math.sin(angle) * 150; monsters.push(monster); game.addChild(monster); } } // Update health bar var healthPercent = self.health / self.maxHealth; self.healthBar.width = 300 * healthPercent; // Handle poison damage over time if (self.poisoned && self.poisonTicks > 0) { if (!self.lastPoisonTime || now - self.lastPoisonTime > 500) { // Every 0.5 seconds self.lastPoisonTime = now; self.poisonTicks--; self.takeDamage(self.poisonDamage); // Create poison effect visuals var poisonEffect = new Container(); var poisonParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, tint: 0x00FF00, alpha: 0.7 }); poisonEffect.x = self.x + (Math.random() * 80 - 40); poisonEffect.y = self.y + (Math.random() * 80 - 40); poisonEffect.addChild(poisonParticle); game.addChild(poisonEffect); // Animate poison particles tween(poisonParticle, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 500, onFinish: function onFinish() { if (poisonEffect.parent) { poisonEffect.parent.removeChild(poisonEffect); } } }); // Remove poison effect when done if (self.poisonTicks <= 0) { self.poisoned = false; self.body.tint = 0xFFFFFF; // Reset tint } } } // Handle burning damage over time if (self.burning && self.burnTicks > 0) { if (!self.lastBurnTime || now - self.lastBurnTime > 400) { // Every 0.4 seconds self.lastBurnTime = now; self.burnTicks--; self.takeDamage(self.burnDamage); // Create fire effect visuals var fireEffect = new Container(); var fireParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, tint: 0xFF3300, alpha: 0.7 }); fireEffect.x = self.x + (Math.random() * 80 - 40); fireEffect.y = self.y + (Math.random() * 80 - 40); fireEffect.addChild(fireParticle); game.addChild(fireEffect); // Animate fire particles rising up tween(fireParticle, { y: fireParticle.y - 40, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 400, onFinish: function onFinish() { if (fireEffect.parent) { fireEffect.parent.removeChild(fireEffect); } } }); // Remove burn effect when done if (self.burnTicks <= 0) { self.burning = false; self.body.tint = 0xFFFFFF; // Reset tint } } } // Handle shield mechanic - activate shield at 50% health if (!self.shieldActive && self.health <= self.maxHealth * 0.5) { self.shieldActive = true; // Create shield visual self.shield = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.5, scaleY: 3.5, alpha: 0.4, tint: 0x3399FF }); self.addChild(self.shield); // Animate shield activation tween(self.shield, { alpha: 0.6 }, { duration: 1000, onFinish: function onFinish() { // Pulse animation function pulseShield() { if (self.shield && self.shield.parent) { tween(self.shield, { alpha: 0.3 }, { duration: 1000, onFinish: function onFinish() { tween(self.shield, { alpha: 0.6 }, { duration: 1000, onFinish: pulseShield }); } }); } } pulseShield(); } }); } } }; self.takeDamage = function (amount) { // Reduce damage if shield is active if (self.shieldActive) { amount = amount * 0.5; } self.health -= amount; // Flash red when hit LK.effects.flashObject(self, 0xFF0000, 200); LK.getSound('hit').play(); if (self.health <= 0) { return true; } return false; }; return self; }); var Monster = Container.expand(function () { var self = Container.call(this); self.id = Date.now() + Math.floor(Math.random() * 10000); self.body = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: 0x9900FF // Purple and blue color }); self.maxHealth = 20; self.health = self.maxHealth; self.speed = 2; self.damage = 10; self.lastAttackTime = 0; self.attackCooldown = 1000; self.update = function () { if (player && !paused) { var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } var now = Date.now(); // Attack player if (dist < 100) { if (now - self.lastAttackTime > self.attackCooldown) { self.lastAttackTime = now; if (player.takeDamage(self.damage)) { LK.getSound('death').play(); gameOver(); } } } // Handle poison damage over time if (self.poisoned && self.poisonTicks > 0) { if (!self.lastPoisonTime || now - self.lastPoisonTime > 500) { // Every 0.5 seconds self.lastPoisonTime = now; self.poisonTicks--; self.takeDamage(self.poisonDamage); // Create poison effect visuals var poisonEffect = new Container(); var poisonParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0x00FF00, alpha: 0.7 }); poisonEffect.x = self.x + (Math.random() * 40 - 20); poisonEffect.y = self.y + (Math.random() * 40 - 20); poisonEffect.addChild(poisonParticle); game.addChild(poisonEffect); // Animate poison particles tween(poisonParticle, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, onFinish: function onFinish() { if (poisonEffect.parent) { poisonEffect.parent.removeChild(poisonEffect); } } }); // Remove poison effect when done if (self.poisonTicks <= 0) { self.poisoned = false; self.body.tint = 0xFFFFFF; // Reset tint } } } // Handle burning damage over time if (self.burning && self.burnTicks > 0) { if (!self.lastBurnTime || now - self.lastBurnTime > 400) { // Every 0.4 seconds self.lastBurnTime = now; self.burnTicks--; self.takeDamage(self.burnDamage); // Create fire effect visuals var fireEffect = new Container(); var fireParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0xFF3300, alpha: 0.7 }); fireEffect.x = self.x + (Math.random() * 40 - 20); fireEffect.y = self.y + (Math.random() * 40 - 20); fireEffect.addChild(fireParticle); game.addChild(fireEffect); // Animate fire particles rising up tween(fireParticle, { y: fireParticle.y - 30, alpha: 0, scaleX: 1.2, scaleY: 1.2 }, { duration: 400, onFinish: function onFinish() { if (fireEffect.parent) { fireEffect.parent.removeChild(fireEffect); } } }); // Remove burn effect when done if (self.burnTicks <= 0) { self.burning = false; self.body.tint = 0xFFFFFF; // Reset tint } } } } }; self.takeDamage = function (amount) { self.health -= amount; // Flash red when hit LK.effects.flashObject(self, 0xFF0000, 200); LK.getSound('hit').play(); if (self.health <= 0) { return true; } return false; }; return self; }); var OrbitalObject = Container.expand(function (type, angle, distance) { var self = Container.call(this); self.type = type; self.angle = angle || 0; self.distance = distance || 300; // Increased distance from 200 to 300 to allow monsters to pass more easily self.speed = 0.01; // Reduced speed for orbital objects self.target = null; self.lastAttackTime = 0; // Set different properties based on type if (type === "shields") { self.damage = 5; self.attackCooldown = 800; // Faster attack rate } else if (type === "swords") { self.damage = 12; // Higher damage self.attackCooldown = 1200; } else if (type === "orbs") { self.damage = 8; self.attackCooldown = 1000; } else if (type === "flame_barrier") { self.damage = 7; self.attackCooldown = 600; // Very fast attack rate for flame barrier } else if (type === "ice_shards") { self.damage = 6; self.attackCooldown = 900; } else if (type === "spirit_guardians") { self.damage = 10; self.attackCooldown = 1100; } else { self.damage = 7; self.attackCooldown = 1000; } // Create the visual based on type if (type === "shields") { // Create holographic energy barrier visual self.body = self.attachAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); // Create energy barrier multilayer glow effect self.glow = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, alpha: 0.4 }); // Add inner energy core self.core = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, alpha: 0.9, tint: 0xFFFFFF }); self.addChild(self.core); self.addChildAt(self.glow, 0); self.body.tint = 0x33CCFF; // Bright blue energy barrier self.glow.tint = 0x33CCFF; // Matching glow color // Add futuristic pulse animation to the shield tween(self.glow, { scaleX: 1.1, scaleY: 1.1, alpha: 0.6 }, { duration: 1000, easing: tween.easeInOut, onFinish: function pulseShield() { tween(self.glow, { scaleX: 0.9, scaleY: 0.9, alpha: 0.3 }, { duration: 1000, easing: tween.easeInOut, onFinish: pulseShield }); } }); // Add rotating core animation tween(self.core, { rotation: Math.PI * 2 }, { duration: 3000, onFinish: function rotateCoreLoop() { self.core.rotation = 0; tween(self.core, { rotation: Math.PI * 2 }, { duration: 3000, onFinish: rotateCoreLoop }); } }); } else if (type === "swords") { // Create energy blade instead of physical sword self.body = self.attachAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.4 }); self.body.tint = 0xE6E6FF; // Glowing energy blade // Add energy core and hilt self.hilt = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, x: -30, tint: 0x666666 // Dark metal hilt }); self.addChild(self.hilt); // Add energy blade glow self.bladeGlow = LK.getAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.2, scaleY: 0.6, alpha: 0.4, tint: 0xE6E6FF }); self.addChildAt(self.bladeGlow, 0); // Add blade flicker animation tween(self.bladeGlow, { alpha: 0.6, scaleY: 0.7 }, { duration: 500 + Math.random() * 500, onFinish: function flickerBlade() { tween(self.bladeGlow, { alpha: 0.3, scaleY: 0.5 }, { duration: 500 + Math.random() * 500, onFinish: flickerBlade }); } }); } else if (type === "orbs") { // Create plasma orb with energy core self.body = self.attachAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); self.body.tint = 0xFFCC00; // Yellow plasma orb // Add energy core self.core = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, tint: 0xFFFFFF, alpha: 0.9 }); self.addChild(self.core); // Add outer energy field self.field = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7, alpha: 0.3, tint: 0xFFCC00 }); self.addChildAt(self.field, 0); // Add plasma fluctuation animation tween(self.field, { scaleX: 0.9, scaleY: 0.9, alpha: 0.4 }, { duration: 800, easing: tween.easeInOut, onFinish: function fluctuatePlasma() { tween(self.field, { scaleX: 0.7, scaleY: 0.7, alpha: 0.2 }, { duration: 800, easing: tween.easeInOut, onFinish: fluctuatePlasma }); } }); // Add energy arcs occasionally self.lastArcTime = 0; } else if (type === "flame_barrier") { // Create plasma flame visual self.body = self.attachAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); self.body.tint = 0xFF3300; // Orange-red for flame barrier // Add flame core self.flameCore = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0xFFFF00, // Yellow hot core alpha: 0.9 }); self.addChild(self.flameCore); // Add outer flame aura self.flameAura = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, alpha: 0.3, tint: 0xFF3300 }); self.addChildAt(self.flameAura, 0); // Add flame flicker animation tween(self.flameAura, { scaleX: 1.0, scaleY: 1.0, alpha: 0.5 }, { duration: 300 + Math.random() * 200, easing: tween.easeOut, onFinish: function flickerFlame() { tween(self.flameAura, { scaleX: 0.8, scaleY: 0.8, alpha: 0.2 }, { duration: 300 + Math.random() * 200, easing: tween.easeIn, onFinish: flickerFlame }); } }); } else if (type === "ice_shards") { // Create crystalline ice structure self.body = self.attachAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, tint: 0xDDEEFF // Light blue-white for ice crystals }); // Add crystal facets for (var i = 0; i < 3; i++) { var angle = Math.PI * 2 * (i / 3); var shard = LK.getAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.15, rotation: angle, tint: 0x99CCFF, alpha: 0.9 }); self.addChild(shard); } // Add frost aura self.frostAura = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9, alpha: 0.2, tint: 0xBBDDFF }); self.addChildAt(self.frostAura, 0); // Add ice crystal shimmer animation tween(self.body, { tint: 0xAACCFF }, { duration: 1500, onFinish: function shimmerIce() { tween(self.body, { tint: 0xDDEEFF }, { duration: 1500, onFinish: shimmerIce }); } }); } else if (type === "spirit_guardians") { // Create ethereal spirit form self.body = self.attachAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3 }); self.body.tint = 0xDDDDFF; // Pale blue-white for spirits // Add spirit core self.spiritCore = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF, alpha: 0.7 }); self.addChild(self.spiritCore); // Add ethereal aura self.spiritAura = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.5, scaleY: 3.5, alpha: 0.15, tint: 0xEEEEFF }); self.addChildAt(self.spiritAura, 0); // Add ethereal pulsing animation tween(self.spiritAura, { alpha: 0.25, scaleX: 3.7, scaleY: 3.7 }, { duration: 2000, easing: tween.easeInOut, onFinish: function pulseSpirit() { tween(self.spiritAura, { alpha: 0.15, scaleX: 3.5, scaleY: 3.5 }, { duration: 2000, easing: tween.easeInOut, onFinish: pulseSpirit }); } }); } self.update = function () { if (!player) return; // Track previous position for trail effect if (self.prevX === undefined) { self.prevX = self.x; self.prevY = self.y; self.lastTrailTime = Date.now(); } // Update position around player self.angle += self.speed; self.x = player.x + Math.cos(self.angle) * self.distance; self.y = player.y + Math.sin(self.angle) * self.distance; // Rotate object to face direction of movement if (self.type === "swords" || self.type === "ice_shards") { self.rotation = self.angle + Math.PI / 2; } // Create energy trail effect - reduced frequency for better performance var now = Date.now(); if (now - self.lastTrailTime > 250) { // Create trail less frequently (250ms instead of 100ms) self.lastTrailTime = now; // Only create trail if moved enough var dx = self.x - self.prevX; var dy = self.y - self.prevY; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 20) { //{dn} // Increased threshold for trail creation var trail = new Container(); var trailParticle; // Different trail effects for different orbital types if (self.type === "shields") { trailParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0x33CCFF, alpha: 0.5 }); } else if (self.type === "swords") { trailParticle = LK.getAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.2, rotation: self.rotation, tint: 0xE6E6FF, alpha: 0.4 }); } else if (self.type === "orbs") { trailParticle = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, tint: 0xFFCC00, alpha: 0.4 }); } else if (self.type === "flame_barrier") { trailParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0xFF3300, alpha: 0.5 }); } else if (self.type === "ice_shards") { trailParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, tint: 0x99CCFF, alpha: 0.4 }); } else if (self.type === "spirit_guardians") { trailParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0xDDDDFF, alpha: 0.3 }); } trail.x = self.x; trail.y = self.y; trail.addChild(trailParticle); game.addChild(trail); // Animate trail fadeout - shorter duration for better performance tween(trailParticle, { alpha: 0, scaleX: trailParticle.scaleX * 0.5, scaleY: trailParticle.scaleY * 0.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { if (trail.parent) { trail.parent.removeChild(trail); } } }); // Update previous position self.prevX = self.x; self.prevY = self.y; } } // Check for collisions with monsters for (var i = monsters.length - 1; i >= 0; i--) { var monster = monsters[i]; if (self.intersects(monster)) { var now = Date.now(); if (now - self.lastAttackTime > self.attackCooldown) { self.lastAttackTime = now; // Different effects based on type if (self.type === "shields") { // Shields push monsters back var dx = monster.x - player.x; var dy = monster.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { var pushX = dx / dist * 50; var pushY = dy / dist * 50; monster.x += pushX; monster.y += pushY; // Create energy barrier impact effect var shieldEffect = new Container(); var impactWave = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, tint: 0x33CCFF, alpha: 0.8 }); shieldEffect.x = self.x; shieldEffect.y = self.y; game.addChild(shieldEffect); shieldEffect.addChild(impactWave); // Add energy discharge particles for (var p = 0; p < 8; p++) { var angle = Math.PI * 2 * (p / 8); var particle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0x99EEFF, alpha: 0.9 }); particle.x = Math.cos(angle) * 20; particle.y = Math.sin(angle) * 20; impactWave.addChild(particle); // Animate particles outward tween(particle, { x: particle.x * 4, y: particle.y * 4, alpha: 0 }, { duration: 300, easing: tween.easeOut }); } // Animate shield effect with ripple tween(impactWave, { scaleX: 2.0, scaleY: 2.0, alpha: 0 }, { duration: 500, onFinish: function onFinish() { if (shieldEffect.parent) { shieldEffect.parent.removeChild(shieldEffect); } } }); } // Energy discharge effect LK.effects.flashObject(self, 0x33CCFF, 200); // Play orbital hit sound LK.getSound('orbital_hit').play(); // Create energy ripple effect var energyRipple = new Container(); var rippleWave = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0x33CCFF, alpha: 0.4 }); energyRipple.x = monster.x; energyRipple.y = monster.y; game.addChild(energyRipple); energyRipple.addChild(rippleWave); // Animate ripple tween(rippleWave, { scaleX: 2.0, scaleY: 2.0, alpha: 0 }, { duration: 400, onFinish: function onFinish() { if (energyRipple.parent) { energyRipple.parent.removeChild(energyRipple); } } }); monster.takeDamage(5); // Shields do a little damage } else if (self.type === "swords" || self.type === "orbs") { // Swords and orbs deal damage if (monster.takeDamage(self.damage)) { game.removeChild(monster); monsters.splice(i, 1); // Play pop sound when monster is killed LK.getSound('pop').play(); monstersKilled++; scoreText.setText("Score: " + monstersKilled); } if (self.type === "swords") { // Create sword slash effect var slashEffect = new Container(); var slash = LK.getAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 0.8, tint: 0xFFFFFF, alpha: 0.8 }); slashEffect.x = monster.x; slashEffect.y = monster.y; slashEffect.rotation = Math.random() * Math.PI; slashEffect.addChild(slash); game.addChild(slashEffect); // Add impact particles for (var p = 0; p < 5; p++) { var sparkParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0xFFCCCC, alpha: 0.9 }); sparkParticle.x = Math.random() * 30 - 15; sparkParticle.y = Math.random() * 30 - 15; slashEffect.addChild(sparkParticle); // Animate particles tween(sparkParticle, { x: sparkParticle.x * 3, y: sparkParticle.y * 3, alpha: 0 }, { duration: 300, easing: tween.easeOut }); } // Animate the slash tween(slash, { scaleX: 4, alpha: 0 }, { duration: 250, onFinish: function onFinish() { if (slashEffect.parent) { slashEffect.parent.removeChild(slashEffect); } } }); LK.effects.flashObject(self, 0xFF0000, 200); // Play orbital hit sound LK.getSound('orbital_hit').play(); } else { // Orbs have electricity effect // Create electricity effect var zapEffect = new Container(); var energyCore = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, tint: 0xFFFF00, alpha: 0.8 }); zapEffect.x = monster.x; zapEffect.y = monster.y; zapEffect.addChild(energyCore); game.addChild(zapEffect); // Add lightning bolts for (var b = 0; b < 4; b++) { var angle = Math.PI * 2 * (b / 4); var boltLength = 40 + Math.random() * 20; var bolt = LK.getAsset('arrow', { anchorX: 0, anchorY: 0.5, scaleX: boltLength / 50, scaleY: 0.3, tint: 0xFFFF99, alpha: 0.9 }); bolt.rotation = angle; zapEffect.addChild(bolt); // Animate each bolt tween(bolt, { scaleY: 0.1, alpha: 0 }, { duration: 200 + Math.random() * 200 }); } // Animate the energy core tween(energyCore, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 400, onFinish: function onFinish() { if (zapEffect.parent) { zapEffect.parent.removeChild(zapEffect); } } }); LK.effects.flashObject(self, 0xFFFF00, 200); // Play orbital hit sound LK.getSound('orbital_hit').play(); } } else if (self.type === "flame_barrier") { // Flame barrier burns enemies if (!monster.burning) { monster.burning = true; monster.burnDamage = self.damage * 0.2; monster.burnTicks = 3; monster.body.tint = 0xFF3300; // Create flame burst effect var flameBurst = new Container(); var burstCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0xFF3300, alpha: 0.7 }); flameBurst.x = monster.x; flameBurst.y = monster.y; game.addChild(flameBurst); flameBurst.addChild(burstCircle); // Add flame particles for (var f = 0; f < 6; f++) { var flameParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7, tint: 0xFF5500, alpha: 0.8 }); flameParticle.x = Math.random() * 40 - 20; flameParticle.y = Math.random() * 40 - 20; flameBurst.addChild(flameParticle); // Animate flame particle tween(flameParticle, { x: flameParticle.x * 2, y: flameParticle.y - 30, alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 600, easing: tween.easeOut }); } // Animate the flame burst tween(burstCircle, { scaleX: 2.0, scaleY: 2.0, alpha: 0 }, { duration: 700, onFinish: function onFinish() { if (flameBurst.parent) { flameBurst.parent.removeChild(flameBurst); } } }); } if (monster.takeDamage(self.damage)) { game.removeChild(monster); monsters.splice(i, 1); // Play pop sound when monster is killed LK.getSound('pop').play(); monstersKilled++; scoreText.setText("Score: " + monstersKilled); } LK.effects.flashObject(self, 0xFF3300, 200); // Play orbital hit sound LK.getSound('orbital_hit').play(); } else if (self.type === "ice_shards") { // Ice shards slow enemies monster.frozen = true; monster.originalSpeed = monster.speed; monster.speed = monster.speed * 0.7; monster.body.tint = 0x99CCFF; // Create frost burst effect var frostBurst = new Container(); var frostCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, tint: 0x99CCFF, alpha: 0.6 }); frostBurst.x = monster.x; frostBurst.y = monster.y; game.addChild(frostBurst); frostBurst.addChild(frostCircle); // Add crystal shards for (var s = 0; s < 8; s++) { var iceFragment = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0xCCEEFF, alpha: 0.9 }); var angle = Math.PI * 2 * (s / 8); var distance = 30; iceFragment.x = Math.cos(angle) * distance; iceFragment.y = Math.sin(angle) * distance; frostBurst.addChild(iceFragment); // Animate ice fragments expanding outward tween(iceFragment, { x: iceFragment.x * 2.5, y: iceFragment.y * 2.5, alpha: 0, rotation: Math.random() * Math.PI }, { duration: 800, easing: tween.easeOut }); } // Animate the frost circle tween(frostCircle, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 700, onFinish: function onFinish() { if (frostBurst.parent) { frostBurst.parent.removeChild(frostBurst); } } }); // Reset after 2 seconds LK.setTimeout(function () { if (monster && monster.frozen) { monster.frozen = false; monster.speed = monster.originalSpeed; monster.body.tint = 0xFFFFFF; } }, 2000); if (monster.takeDamage(self.damage)) { game.removeChild(monster); monsters.splice(i, 1); // Play pop sound when monster is killed LK.getSound('pop').play(); monstersKilled++; scoreText.setText("Score: " + monstersKilled); } LK.effects.flashObject(self, 0x99CCFF, 200); // Play orbital hit sound LK.getSound('orbital_hit').play(); } else if (self.type === "spirit_guardians") { // Spirit guardians pass through enemies if (monster.takeDamage(self.damage)) { game.removeChild(monster); monsters.splice(i, 1); // Play pop sound when monster is killed LK.getSound('pop').play(); monstersKilled++; scoreText.setText("Score: " + monstersKilled); } LK.effects.flashObject(self, 0xDDDDFF, 200); // Play orbital hit sound LK.getSound('orbital_hit').play(); // Spirits heal player slightly if (player) { player.health = Math.min(player.health + 1, player.maxHealth); // Create spirit healing effect var spiritEffect = new Container(); var spiritGlow = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5, tint: 0xDDDDFF, alpha: 0.7 }); spiritEffect.x = monster.x; spiritEffect.y = monster.y; spiritEffect.addChild(spiritGlow); game.addChild(spiritEffect); // Create healing link to player var healLink = LK.getAsset('arrow', { anchorX: 0, anchorY: 0.5, tint: 0xEEEEFF, alpha: 0.5 }); // Calculate distance and angle to player var dx = player.x - monster.x; var dy = player.y - monster.y; var distance = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); healLink.rotation = angle; healLink.scaleX = distance / 50; spiritEffect.addChild(healLink); // Add ethereal particles floating to player for (var h = 0; h < 3; h++) { var healParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0xFFFFFF, alpha: 0.8 }); healParticle.x = 0; healParticle.y = 0; spiritEffect.addChild(healParticle); // Animate particles along path to player tween(healParticle, { x: dx, y: dy, alpha: 0 }, { duration: 600 + h * 100, easing: tween.easeInOut }); } // Animate the spirit effect tween(spiritGlow, { scaleX: 3.5, scaleY: 3.5, alpha: 0 }, { duration: 800, onFinish: function onFinish() { if (spiritEffect.parent) { spiritEffect.parent.removeChild(spiritEffect); } } }); } } } } } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); self.body = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5 }); // Modify ship body appearance self.body.tint = 0x3366CC; // Deeper blue for ship body // Create cockpit self.cockpit = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, x: 20, tint: 0x99CCFF // Light blue cockpit }); self.addChild(self.cockpit); // Add thruster effect for spaceship with animation self.thruster = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 1.2, x: -40, tint: 0xFF6600 }); self.addChild(self.thruster); // Animate thruster pulsing effect tween(self.thruster, { scaleY: 0.8, alpha: 0.7 }, { duration: 500, onFinish: function pulseThruster() { tween(self.thruster, { scaleY: 1.2, alpha: 1 }, { duration: 500, onFinish: pulseThruster }); } }); // Add wing details with metallic appearance self.leftWing = LK.getAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.3, x: -20, y: 25, rotation: Math.PI / 4, tint: 0x99AACC }); self.addChild(self.leftWing); self.rightWing = LK.getAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.3, x: -20, y: -25, rotation: -Math.PI / 4, tint: 0x99AACC }); self.addChild(self.rightWing); // Add wing tip lights self.leftWingLight = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, x: self.leftWing.x + 30, y: self.leftWing.y + 15, tint: 0xFF3333 // Red navigation light }); self.addChild(self.leftWingLight); self.rightWingLight = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, x: self.rightWing.x + 30, y: self.rightWing.y - 15, tint: 0x33FF33 // Green navigation light }); self.addChild(self.rightWingLight); // Create thruster particle emitter function self.lastParticleTime = 0; self.maxHealth = 100; self.health = self.maxHealth; self.attackSpeed = 2; self.lastAttackTime = 0; self.movementSpeed = 0; self.abilities = []; self.shoot = function (targetX, targetY) { var now = Date.now(); if (now - self.lastAttackTime < 1000 / self.attackSpeed) { return null; } self.lastAttackTime = now; // Rotate spaceship to face the target smoothly var angle = Math.atan2(targetY - self.y, targetX - self.x); // Animate ship rotation for smoother movement tween(self, { rotation: angle }, { duration: 150, easing: tween.easeOut }); // Create recoil effect - ship moves slightly backward var recoilDistance = 10; var recoilX = self.x - Math.cos(angle) * recoilDistance; var recoilY = self.y - Math.sin(angle) * recoilDistance; tween(self, { x: recoilX, y: recoilY }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { // Return to original position tween(self, { x: self.x + Math.cos(angle) * recoilDistance, y: self.y + Math.sin(angle) * recoilDistance }, { duration: 300, easing: tween.easeOutElastic }); } }); // Create laser beam (arrow) var arrow = new Arrow(); arrow.x = self.x + Math.cos(angle) * 50; // Start from front of ship arrow.y = self.y + Math.sin(angle) * 50; arrow.rotation = angle; // Create muzzle flash effect for laser var laserFlash = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0x99FFFF, alpha: 0.9 }); laserFlash.x = self.x + Math.cos(angle) * 60; laserFlash.y = self.y + Math.sin(angle) * 60; game.addChild(laserFlash); // Animate laser flash tween(laserFlash, { scaleX: 0.2, scaleY: 0.2, alpha: 0 }, { duration: 150, onFinish: function onFinish() { if (laserFlash.parent) { laserFlash.parent.removeChild(laserFlash); } } }); // Create enhanced thruster animation effect var thrusterFlare = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 2.0, tint: 0xFF9900, alpha: 0.9 }); thrusterFlare.x = self.x - Math.cos(angle) * 40; thrusterFlare.y = self.y - Math.sin(angle) * 40; thrusterFlare.rotation = angle + Math.PI; game.addChild(thrusterFlare); // Animate thruster flare with more dynamic effect tween(thrusterFlare, { scaleX: 0.2, scaleY: 0.2, alpha: 0 }, { duration: 300, //{kk} // Reduced duration easing: tween.easeOut, onFinish: function onFinish() { if (thrusterFlare.parent) { thrusterFlare.parent.removeChild(thrusterFlare); } } }); // Create fewer thruster particles for (var i = 0; i < 2; i++) { //{kq} // Reduced from 5 to 2 var particleDelay = i * 50; // Stagger particle creation LK.setTimeout(function () { var thrusterParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4 + Math.random() * 0.3, scaleY: 0.4 + Math.random() * 0.3, tint: Math.random() > 0.5 ? 0xFF3300 : 0xFFAA00, alpha: 0.7 }); // Random offset from center of thruster var particleOffset = Math.random() * 15; var offsetAngle = angle + Math.PI + (Math.random() * 0.5 - 0.25); thrusterParticle.x = self.x - Math.cos(angle) * 40 + Math.cos(offsetAngle) * particleOffset; thrusterParticle.y = self.y - Math.sin(angle) * 40 + Math.sin(offsetAngle) * particleOffset; game.addChild(thrusterParticle); // Animate particle with varying speed - shorter duration tween(thrusterParticle, { x: thrusterParticle.x - Math.cos(angle) * (50 + Math.random() * 30), y: thrusterParticle.y - Math.sin(angle) * (50 + Math.random() * 30), scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 250, //{kA} // Fixed shorter duration easing: tween.easeOut, onFinish: function onFinish() { if (thrusterParticle.parent) { thrusterParticle.parent.removeChild(thrusterParticle); } } }); }, particleDelay); } // Play laser sound effect LK.getSound('shoot').play(); return arrow; }; self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { self.health = 0; // Play death sound when player dies LK.getSound('death').play(); return true; } return false; }; self.addAbility = function (ability) { self.abilities.push(ability); // Apply ability effects if (ability.type === "attackSpeed") { self.attackSpeed += ability.value; } else if (ability.type === "health") { self.maxHealth += ability.value; self.health = Math.min(self.health + ability.value, self.maxHealth); } else if (ability.type === "arrowDamage") { arrowDamage += ability.value; } else if (ability.type === "arrowPierce") { arrowPierce += ability.value; } else if (ability.type === "arrowSpeed") { arrowSpeed += ability.value; } else if (ability.type === "special") { console.log("Added special ability: " + ability.specialType); // Track special abilities if (["poison", "explosive", "freeze", "fire", "lightning", "vampiric", "multishot", "rebound", "homing"].indexOf(ability.specialType) !== -1) { // Track arrow special abilities acquiredSpecialTypes.push(ability.specialType); } // Create orbital objects for shield, sword, orb, etc. abilities if (["shields", "swords", "orbs", "flame_barrier", "ice_shards", "spirit_guardians"].indexOf(ability.specialType) !== -1) { // Track orbital special abilities acquiredOrbitalTypes.push(ability.specialType); // Create 3 objects for each type, evenly spaced around the player // Calculate an offset angle to prevent collision with existing orbital objects var baseOffset = 0; if (orbitalObjects.length > 0) { // If we already have orbital objects, offset the new ones to avoid overlap baseOffset = Math.PI / (3 + orbitalObjects.length / 3); // Stagger based on number of existing orbital groups } for (var i = 0; i < 3; i++) { var angle = Math.PI * 2 / 3 * i + baseOffset; var orbitalObj = new OrbitalObject(ability.specialType, angle); // Slightly vary the orbital distance to prevent collisions orbitalObj.distance = 300 + orbitalObjects.length / 3 * 20; // Increase orbit radius for each new group orbitalObjects.push(orbitalObj); game.addChild(orbitalObj); } } } }; // Add update method for continuous effects self.update = function () { // Generate idle thruster particles less frequently var now = Date.now(); if (now - self.lastParticleTime > 350) { // Emit particles less frequently (350ms instead of 150ms) self.lastParticleTime = now; // Create idle thruster particle var idleParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, tint: Math.random() > 0.3 ? 0xFF6600 : 0xFFAA00, alpha: 0.6 }); // Position behind ship based on current rotation var backAngle = self.rotation + Math.PI; var offset = Math.random() * 10 - 5; var perpendicular = backAngle + Math.PI / 2; idleParticle.x = self.x + Math.cos(backAngle) * 40 + Math.cos(perpendicular) * offset; idleParticle.y = self.y + Math.sin(backAngle) * 40 + Math.sin(perpendicular) * offset; game.addChild(idleParticle); // Animate particle with shorter duration tween(idleParticle, { x: idleParticle.x + Math.cos(backAngle) * 30, y: idleParticle.y + Math.sin(backAngle) * 30, scaleX: 0.1, scaleY: 0.1, alpha: 0 }, { duration: 250, //{lb} // Reduced duration easing: tween.easeOut, onFinish: function onFinish() { if (idleParticle.parent) { idleParticle.parent.removeChild(idleParticle); } } }); } // Blink wing tip lights less frequently if (LK.ticks % 120 === 0) { // Every 2 seconds (120 frames) instead of every second tween(self.leftWingLight, { alpha: 0.4 }, { duration: 300, onFinish: function onFinish() { tween(self.leftWingLight, { alpha: 1 }, { duration: 300 }); } }); // Blink opposite wing with delay LK.setTimeout(function () { tween(self.rightWingLight, { alpha: 0.4 }, { duration: 300, onFinish: function onFinish() { tween(self.rightWingLight, { alpha: 1 }, { duration: 300 }); } }); }, 500); } }; return self; }); var Star = Container.expand(function () { var self = Container.call(this); // Create star visual with random size and color var size = 1 + Math.random() * 3; // Generate random star colors ranging from blue to purple to white var colorChoice = Math.random(); var starTint; if (colorChoice < 0.3) { starTint = 0xCCCCFF; // Light blue/lavender } else if (colorChoice < 0.6) { starTint = 0xDDA0DD; // Light purple } else if (colorChoice < 0.8) { starTint = 0xFFFFFF; // White } else { starTint = 0xFFD700; // Gold/yellow for bright stars } self.body = self.attachAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 5, scaleY: size / 5, tint: starTint, alpha: 0.3 + Math.random() * 0.7 }); // Set random speed for parallax effect self.speed = 0.1 + Math.random() * 0.3; // Twinkle animation self.twinkle = function () { var targetAlpha = 0.3 + Math.random() * 0.7; var duration = 1000 + Math.random() * 2000; tween(self.body, { alpha: targetAlpha }, { duration: duration, onFinish: self.twinkle }); }; // Start twinkling self.twinkle(); // Update method for star movement self.update = function () { // Move star down slowly for parallax effect self.y += self.speed; // If star moves off screen, reset position if (self.y > 2832) { self.y = -20; self.x = Math.random() * 2048; } }; return self; }); var UpgradeCard = Container.expand(function (ability) { var self = Container.call(this); self.ability = ability; self.background = self.attachAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5 }); self.icon = self.attachAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, y: -120 }); // Title self.title = new Text2(ability.name, { size: 36, fill: 0x000000 }); self.title.anchor.set(0.5, 0.5); self.title.y = -180; // Move title position above the colored icon self.addChild(self.title); // Description self.description = new Text2(ability.description, { size: 24, fill: 0x333333, wordWrap: 350 }); self.description.anchor.set(0.5, 0.5); self.description.y = 80; // Increased y position to avoid collision with title self.addChild(self.description); self.down = function (x, y, obj) { tween(self, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); }; self.up = function (x, y, obj) { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, onFinish: function onFinish() { if (player) { player.addAbility(self.ability); hideUpgradeScreen(); startNextWave(); LK.getSound('levelup').play(); } } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 // Black background for Space Archers }); /**** * Game Code ****/ // Game variables var player; var monsters = []; var arrows = []; var upgradeCards = []; var orbitalObjects = []; var stars = []; // Array to hold background stars var paused = false; var currentWave = 0; var monstersKilled = 0; var lastSpawnTime = 0; var showingUpgrades = false; var bossSpawned = false; var bossDefeated = false; // Track acquired special abilities to prevent duplicates var acquiredSpecialTypes = []; var acquiredOrbitalTypes = []; // Arrow properties var arrowDamage = 20; var arrowPierce = 1; var arrowSpeed = 18; // Abilities pool var abilitiesPool = [{ name: "Quick Shot", description: "Increase attack speed by 15%", type: "attackSpeed", value: 0.15 }, { name: "Heavy Arrow", description: "Increase arrow damage by 5", type: "arrowDamage", value: 5 }, { name: "Fortify", description: "Increase max health by 20", type: "health", value: 20 }, { name: "Piercing Shot", description: "Arrows pierce through one additional enemy", type: "arrowPierce", value: 1 }, { name: "Swift Arrow", description: "Increase arrow speed by 2", type: "arrowSpeed", value: 2 }]; // Special power-up abilities var specialAbilitiesPool = [{ name: "Poison Lazers", description: "Lazers poison enemies, dealing damage over time", type: "special", specialType: "poison" }, { name: "Explosive Lazers", description: "Lazers explode on impact, damaging nearby enemies", type: "special", specialType: "explosive" }, { name: "Freezing Lazers", description: "Lazers slow down enemies for a short time", type: "special", specialType: "freeze" }, { name: "Fire Lazers", description: "Lazers burn enemies with intense flames", type: "special", specialType: "fire" }, { name: "Lightning Lazers", description: "Lazers chain lightning between nearby enemies", type: "special", specialType: "lightning" }, { name: "Vampiric Lazers", description: "Lazers steal health from enemies and heal you", type: "special", specialType: "vampiric" }, { name: "Multi-Shot Lazers", description: "Fire three lazers in a spread pattern with each shot (additional upgrades add more lazers)", type: "special", specialType: "multishot" }, { name: "Homing Lazers", description: "Lazers seek out nearby enemies after being fired", type: "special", specialType: "homing" }, { name: "Energy Barriers", description: "Three energy barriers orbit around you, pushing back and damaging enemies", type: "special", specialType: "shields" }, { name: "Rotating Swords", description: "Spinning swords that damage nearby enemies", type: "special", specialType: "swords" }, { name: "Energy Orbs", description: "Magical orbs circle you, zapping nearby enemies", type: "special", specialType: "orbs" }, { name: "Flame Barrier", description: "A ring of fire surrounds you, burning enemies", type: "special", specialType: "flame_barrier" }, { name: "Ice Shards", description: "Ice crystals orbit you, slowing nearby enemies", type: "special", specialType: "ice_shards" }, { name: "Spirit Guardians", description: "Ethereal spirits orbit and protect you", type: "special", specialType: "spirit_guardians" }]; // UI elements var waveText = new Text2("Wave: 1", { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); var scoreText = new Text2("Hits: 0", { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 150; scoreText.y = 10; LK.gui.topLeft.addChild(scoreText); var healthBarBg = LK.getAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); var healthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); healthBarBg.x = 2048 / 2; healthBarBg.y = 50; healthBar.x = healthBarBg.x - 100; healthBar.y = healthBarBg.y; LK.gui.addChild(healthBarBg); LK.gui.addChild(healthBar); var upgradeContainer = new Container(); upgradeContainer.visible = false; LK.gui.addChild(upgradeContainer); var upgradeTitle = new Text2("Choose One: Arrow Effect, Orbital, or Stat Upgrade", { size: 60, fill: 0xFFFFFF }); upgradeTitle.anchor.set(0, 0.5); upgradeTitle.x = 50; upgradeTitle.y = 200; upgradeContainer.addChild(upgradeTitle); function initGame() { currentWave = 0; monstersKilled = 0; paused = false; showingUpgrades = false; monsters = []; arrows = []; bossSpawned = false; // Track if boss is spawned for current wave bossDefeated = false; // Track if the current boss has been defeated // Reset acquired special abilities acquiredSpecialTypes = []; acquiredOrbitalTypes = []; // Clean up any existing orbital objects for (var i = 0; i < orbitalObjects.length; i++) { if (orbitalObjects[i].parent) { orbitalObjects[i].parent.removeChild(orbitalObjects[i]); } } orbitalObjects = []; // Clean up existing stars for (var i = 0; i < stars.length; i++) { if (stars[i].parent) { stars[i].parent.removeChild(stars[i]); } } stars = []; // Create starfield background with fewer stars (50 instead of 100) for (var i = 0; i < 50; i++) { var star = new Star(); star.x = Math.random() * 2048; // Random x position star.y = Math.random() * 2732; // Random y position stars.push(star); // Add stars to game at the beginning so they're behind everything else game.addChildAt(star, 0); } // Initialize player player = new Player(); player.x = 2048 / 2; player.y = 2732 / 2; game.addChild(player); // Reset arrow properties to stronger defaults arrowDamage = 20; arrowPierce = 1; arrowSpeed = 18; // Update UI waveText.setText("Wave: 1"); scoreText.setText("Score: 0"); // Start first wave startNextWave(); // Play music with initial intensity LK.playMusic('gameMusic', { fade: { start: 0, end: 1.0, duration: 2000 } }); } function startNextWave() { currentWave++; waveText.setText("Wave: " + currentWave); lastSpawnTime = Date.now(); // Reset boss flags at the start of every wave if (currentWave % 10 === 0) { // Boss wave starts with boss not spawned bossSpawned = false; bossDefeated = false; // Increase music intensity during boss waves LK.playMusic('gameMusic', { volume: 1.0, loop: true }); } else { // Non-boss wave bossSpawned = false; bossDefeated = true; // No boss to defeat on non-boss waves // Return to normal music intensity for regular waves if (currentWave > 10) { LK.playMusic('gameMusic', { volume: 0.8 }); } else { LK.playMusic('gameMusic', { volume: 0.7 }); } } // If boss wave, create special effect if (currentWave % 10 === 0) { // Create warning effect var warningEffect = new Container(); var warningBg = LK.getAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5, scaleX: 5, scaleY: 1, tint: 0xFF0000, alpha: 0.3 }); warningEffect.x = 2048 / 2; warningEffect.y = 2732 / 2; warningEffect.addChild(warningBg); LK.gui.addChild(warningEffect); // Add warning text var warningText = new Text2("BOSS INCOMING", { size: 100, fill: 0xFF0000 }); warningText.anchor.set(0.5, 0.5); warningEffect.addChild(warningText); // Pulse animation tween(warningBg, { alpha: 0.7 }, { duration: 500, onFinish: function onFinish() { tween(warningBg, { alpha: 0.3 }, { duration: 500, onFinish: function onFinish() { tween(warningBg, { alpha: 0.7 }, { duration: 500, onFinish: function onFinish() { tween(warningBg, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { if (warningEffect.parent) { warningEffect.parent.removeChild(warningEffect); } } }); } }); } }); } }); } } function spawnMonster() { // Check if we should spawn a boss (every 10 waves) if (currentWave % 10 === 0 && !bossSpawned) { var boss = new Boss(); // Scale boss stats based on which "boss wave" this is var bossLevel = currentWave / 10; boss.bossLevel = bossLevel; // Increase boss stats with each boss level boss.maxHealth = 200 + bossLevel * 300; // 500, 800, 1100, etc. boss.health = boss.maxHealth; boss.speed = 1 + bossLevel * 0.2; // Speed increases with level boss.damage = 20 + bossLevel * 10; // Damage increases with level // Add abilities at higher levels if (bossLevel >= 2) { boss.canSpawnMinions = true; boss.minionSpawnCooldown = 6000 - bossLevel * 500; // Spawn faster at higher levels } // Color based on boss level if (bossLevel === 1) { boss.body.tint = 0xFF0000; // Red for first boss } else if (bossLevel === 2) { boss.body.tint = 0x9900FF; // Purple for second boss } else if (bossLevel === 3) { boss.body.tint = 0x00FFFF; // Cyan for third boss } else { boss.body.tint = 0xFFFF00; // Yellow for higher level bosses } // Spawn boss at a random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top boss.x = Math.random() * 2048; boss.y = -150; break; case 1: // Right boss.x = 2198; boss.y = Math.random() * 2732; break; case 2: // Bottom boss.x = Math.random() * 2048; boss.y = 2882; break; case 3: // Left boss.x = -150; boss.y = Math.random() * 2732; break; } // Create boss entrance effect var bossEntranceEffect = new Container(); var entranceCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 5, scaleY: 5, tint: boss.body.tint, alpha: 0.8 }); bossEntranceEffect.x = boss.x; bossEntranceEffect.y = boss.y; game.addChild(bossEntranceEffect); bossEntranceEffect.addChild(entranceCircle); // Animate entrance tween(entranceCircle, { scaleX: 10, scaleY: 10, alpha: 0 }, { duration: 1500, onFinish: function onFinish() { if (bossEntranceEffect.parent) { bossEntranceEffect.parent.removeChild(bossEntranceEffect); } } }); // Add a boss text warning var bossText = new Text2("BOSS LEVEL " + bossLevel, { size: 120, fill: 0xFF0000 }); bossText.anchor.set(0.5, 0.5); bossText.x = 2048 / 2; bossText.y = 2732 / 2; LK.gui.addChild(bossText); // Animate the boss text tween(bossText, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2000, onFinish: function onFinish() { if (bossText.parent) { bossText.parent.removeChild(bossText); } } }); // Add boss to the game monsters.push(boss); game.addChild(boss); bossSpawned = true; return boss; } else { // Regular monster spawning var monster = new Monster(); // Scale difficulty with wave, but with reduced health to make monsters easier to defeat monster.maxHealth = 10 + currentWave * 3; monster.health = monster.maxHealth; monster.speed = 1.5 + currentWave * 0.05; monster.damage = 5 + currentWave; // Change monster color based on wave difficulty if (currentWave <= 5) { monster.body.tint = 0xff3333; // Default red for early waves } else if (currentWave <= 10) { monster.body.tint = 0xff9900; // Orange for medium waves } else if (currentWave <= 15) { monster.body.tint = 0xffcc00; // Yellow-orange for harder waves } else if (currentWave <= 20) { monster.body.tint = 0x9900ff; // Purple for very hard waves } else { monster.body.tint = 0x000000; // Black for super difficult waves // Add a white outline effect with a flash LK.effects.flashObject(monster, 0xffffff, 300); } // Spawn from random edge of screen var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top monster.x = Math.random() * 2048; monster.y = -100; break; case 1: // Right monster.x = 2148; monster.y = Math.random() * 2732; break; case 2: // Bottom monster.x = Math.random() * 2048; monster.y = 2832; break; case 3: // Left monster.x = -100; monster.y = Math.random() * 2732; break; } monsters.push(monster); game.addChild(monster); return monster; } } function showUpgradeScreen() { paused = true; showingUpgrades = true; upgradeContainer.visible = true; // Clear previous upgrade cards for (var i = 0; i < upgradeCards.length; i++) { if (upgradeCards[i] && upgradeCards[i].parent) { upgradeCards[i].parent.removeChild(upgradeCards[i]); } } upgradeCards = []; // Set boss reward title if we just defeated a boss if (currentWave % 10 === 0 && bossDefeated) { upgradeTitle.setText("BOSS REWARDS: Choose Two Powerful Upgrades"); } else { upgradeTitle.setText("Choose One: Arrow Effect, Orbital, or Stat Upgrade"); } // Prepare for card selection var cardOptions = []; // Filter out already acquired special abilities var arrowSpecials = []; var orbitalSpecials = []; var nonOrbitalSpecials = []; for (var i = 0; i < specialAbilitiesPool.length; i++) { var ability = specialAbilitiesPool[i]; // Check for orbital special types if (["shields", "swords", "orbs", "flame_barrier", "ice_shards", "spirit_guardians"].indexOf(ability.specialType) !== -1) { // Only add if we don't have this orbital type already if (acquiredOrbitalTypes.indexOf(ability.specialType) === -1) { orbitalSpecials.push(ability); } // Check for arrow special types } else if (["poison", "explosive", "freeze", "fire", "lightning", "vampiric", "multishot", "rebound", "homing"].indexOf(ability.specialType) !== -1) { // Only add if we don't have this arrow special already if (acquiredSpecialTypes.indexOf(ability.specialType) === -1) { arrowSpecials.push(ability); } } else { nonOrbitalSpecials.push(ability); } } // Create enhanced abilities for boss rewards var bossRewardPool = []; if (currentWave % 10 === 0 && bossDefeated) { bossRewardPool = [{ name: "Massive Health Boost", description: "Increase max health by 50", type: "health", value: 50 }, { name: "Devastating Arrows", description: "Increase arrow damage by 15", type: "arrowDamage", value: 15 }, { name: "Lightning Reflexes", description: "Increase attack speed by 40%", type: "attackSpeed", value: 0.4 }, { name: "Master Piercer", description: "Arrows pierce through 2 additional enemies", type: "arrowPierce", value: 2 }, { name: "Sniper Training", description: "Increase arrow speed by 5", type: "arrowSpeed", value: 5 }]; } // For boss rewards, offer better options (more choices or better upgrades) if (currentWave % 10 === 0 && bossDefeated) { // Offer two options from boss reward pool for (var i = 0; i < 2 && bossRewardPool.length > 0; i++) { var index = Math.floor(Math.random() * bossRewardPool.length); var ability = bossRewardPool.splice(index, 1)[0]; cardOptions.push(ability); } // Add 1-2 special abilities if available if (arrowSpecials.length > 0) { var arrowSpecialIndex = Math.floor(Math.random() * arrowSpecials.length); var arrowSpecialAbility = arrowSpecials[arrowSpecialIndex]; cardOptions.push(arrowSpecialAbility); } if (orbitalSpecials.length > 0 && cardOptions.length < 4) { var orbitalIndex = Math.floor(Math.random() * orbitalSpecials.length); var orbitalAbility = orbitalSpecials[orbitalIndex]; cardOptions.push(orbitalAbility); } // If we still don't have enough cards, add from regular abilities var availableAbilities = [].concat(abilitiesPool); while (cardOptions.length < 4 && availableAbilities.length > 0) { var index = Math.floor(Math.random() * availableAbilities.length); var ability = availableAbilities.splice(index, 1)[0]; cardOptions.push(ability); } } // For regular upgrades, use standard options else { // Always include one arrow special if available (separate card) if (arrowSpecials.length > 0) { var arrowSpecialIndex = Math.floor(Math.random() * arrowSpecials.length); var arrowSpecialAbility = arrowSpecials[arrowSpecialIndex]; cardOptions.push(arrowSpecialAbility); } // Always include one orbital special if available (separate card) if (orbitalSpecials.length > 0) { var orbitalIndex = Math.floor(Math.random() * orbitalSpecials.length); var orbitalAbility = orbitalSpecials[orbitalIndex]; cardOptions.push(orbitalAbility); } // Include one non-orbital special if available if (nonOrbitalSpecials.length > 0 && cardOptions.length < 2) { var nonOrbitalIndex = Math.floor(Math.random() * nonOrbitalSpecials.length); var nonOrbitalAbility = nonOrbitalSpecials[nonOrbitalIndex]; cardOptions.push(nonOrbitalAbility); } // Pick regular abilities for the remaining slots var availableAbilities = [].concat(abilitiesPool); while (cardOptions.length < 3 && availableAbilities.length > 0) { var index = Math.floor(Math.random() * availableAbilities.length); var ability = availableAbilities.splice(index, 1)[0]; cardOptions.push(ability); } } // Show cards var cardCount = cardOptions.length; var cardWidth = 400; // Width of each card var spacing = 20; // Reduced spacing between cards var cardScale = 0.8; // Smaller card scale to ensure all three fit var totalWidth = cardWidth * cardScale * cardCount + spacing * (cardCount - 1); var startX = (2048 - totalWidth) / 2 + cardWidth * cardScale / 2; // For boss rewards, allow selecting two cards var cardsToSelect = currentWave % 10 === 0 && bossDefeated ? 2 : 1; var selectedCards = 0; for (var i = 0; i < cardCount; i++) { var ability = cardOptions[i]; var card = new UpgradeCard(ability); // Save original up method var originalUpMethod = card.up; // Override up method for boss rewards to allow selecting multiple cards if (currentWave % 10 === 0 && bossDefeated) { card.up = function (x, y, obj) { var self = this; tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, onFinish: function onFinish() { if (player) { player.addAbility(self.ability); if (self.parent) { self.parent.removeChild(self); } // Increment selected cards count selectedCards++; // If we've selected enough cards, hide the screen and start next wave if (selectedCards >= cardsToSelect) { hideUpgradeScreen(); startNextWave(); LK.getSound('levelup').play(); } } } }); }; } // Set special icon colors for special abilities if (ability.type === "special") { if (ability.specialType === "poison") { card.icon.tint = 0x00FF00; // Green for poison } else if (ability.specialType === "explosive") { card.icon.tint = 0xFF9900; // Orange for explosive } else if (ability.specialType === "freeze") { card.icon.tint = 0x00FFFF; // Cyan for freeze } else if (ability.specialType === "fire") { card.icon.tint = 0xFF3300; // Orange-red for fire } else if (ability.specialType === "lightning") { card.icon.tint = 0xFFFF00; // Yellow for lightning } else if (ability.specialType === "vampiric") { card.icon.tint = 0xFF00FF; // Purple for vampiric } else if (ability.specialType === "multishot") { card.icon.tint = 0x3366FF; // Blue for multishot } else if (ability.specialType === "rebound") { card.icon.tint = 0x66CCFF; // Light blue for rebound } else if (ability.specialType === "homing") { card.icon.tint = 0xFF6600; // Orange for homing } else if (ability.specialType === "shields") { card.icon.tint = 0x3399FF; // Blue for shields } else if (ability.specialType === "swords") { card.icon.tint = 0xCCCCCC; // Silver for swords } else if (ability.specialType === "orbs") { card.icon.tint = 0xFFFF00; // Yellow for orbs } else if (ability.specialType === "flame_barrier") { card.icon.tint = 0xFF3300; // Orange-red for flame barrier } else if (ability.specialType === "ice_shards") { card.icon.tint = 0x99CCFF; // Light blue for ice shards } else if (ability.specialType === "spirit_guardians") { card.icon.tint = 0xDDDDFF; // Pale blue-white for spirits } } // Add boss reward indicator for enhanced abilities if (currentWave % 10 === 0 && bossDefeated && (ability.type !== "special" || ["shields", "swords", "orbs", "flame_barrier", "ice_shards", "spirit_guardians", "poison", "explosive", "freeze", "fire", "lightning", "vampiric", "multishot", "homing"].indexOf(ability.specialType) === -1)) { var bossRewardMarker = new Text2("BOSS REWARD", { size: 24, fill: 0xFF0000 }); bossRewardMarker.anchor.set(0.5, 0.5); bossRewardMarker.y = -190; card.addChild(bossRewardMarker); } // Position cards in a row var xPos = startX + i * (cardWidth * cardScale + spacing) - 200; // For 4 cards, make adjustment to fit screen if (cardCount === 4) { xPos = startX + i * (cardWidth * cardScale + 10) - 300; cardScale = 0.7; // Smaller for 4 cards } card.x = xPos; card.y = 2732 / 2; card.scale.set(cardScale, cardScale); upgradeCards.push(card); upgradeContainer.addChild(card); } } function hideUpgradeScreen() { paused = false; showingUpgrades = false; upgradeContainer.visible = false; } function updateHealthBar() { if (player) { var healthPercent = player.health / player.maxHealth; healthBar.width = 200 * healthPercent; } } function gameOver() { // Update high score in storage if (monstersKilled > storage.highscore) { storage.highscore = monstersKilled; } storage.totalGamesPlayed = (storage.totalGamesPlayed || 0) + 1; // Fade out music before game over LK.playMusic('gameMusic', { fade: { start: 0.7, end: 0, duration: 1000 } }); LK.showGameOver(); } function handleClick(x, y, obj) { if (paused || !player) return; // Check if player has multishot ability and count how many var multishotCount = 0; for (var i = 0; i < player.abilities.length; i++) { if (player.abilities[i].type === "special" && player.abilities[i].specialType === "multishot") { multishotCount++; } } // Get all available arrow special types var arrowSpecials = []; for (var i = 0; i < player.abilities.length; i++) { var ability = player.abilities[i]; if (ability.type === "special") { // Check if it's an arrow-type special ability (not orbital) if (["poison", "explosive", "freeze", "fire", "lightning", "vampiric", "rebound", "homing"].indexOf(ability.specialType) !== -1) { arrowSpecials.push(ability.specialType); } } } // If multishot, create arrows in a spread pattern based on how many upgrades if (multishotCount > 0) { // Center arrow var mainArrow = player.shoot(x, y); if (mainArrow) { applyArrowProperties(mainArrow, arrowSpecials); arrows.push(mainArrow); game.addChild(mainArrow); // Determine angle spread based on upgrade count - widen as we get more arrows var angleSpread = Math.PI / 12; // Base 15 degrees var totalArrows = 2 + multishotCount; // Base 3 arrows (1 + 2) plus 1 per upgrade // Create additional arrows in a spread pattern for (var i = 1; i <= totalArrows - 1; i++) { // Alternate between left and right side var isEven = i % 2 === 0; var arrowIndex = Math.ceil(i / 2); var sideAngle = angleSpread * arrowIndex; if (isEven) { // Right side arrow var rightAngle = mainArrow.rotation + sideAngle; var rightArrow = new Arrow(); rightArrow.x = player.x; rightArrow.y = player.y; rightArrow.rotation = rightAngle; rightArrow.damage = arrowDamage * 0.8; // Side arrows do less damage rightArrow.pierce = arrowPierce; rightArrow.speed = arrowSpeed; applyArrowProperties(rightArrow, arrowSpecials); arrows.push(rightArrow); game.addChild(rightArrow); } else { // Left side arrow var leftAngle = mainArrow.rotation - sideAngle; var leftArrow = new Arrow(); leftArrow.x = player.x; leftArrow.y = player.y; leftArrow.rotation = leftAngle; leftArrow.damage = arrowDamage * 0.8; // Side arrows do less damage leftArrow.pierce = arrowPierce; leftArrow.speed = arrowSpeed; applyArrowProperties(leftArrow, arrowSpecials); arrows.push(leftArrow); game.addChild(leftArrow); } } // Play sound only once LK.getSound('shoot').play(); } } else { // Regular single arrow shot var arrow = player.shoot(x, y); if (arrow) { applyArrowProperties(arrow, arrowSpecials); arrows.push(arrow); game.addChild(arrow); } } // Helper function to apply properties and special effects to arrows function applyArrowProperties(arrow, arrowSpecials) { arrow.damage = arrowDamage; arrow.pierce = arrowPierce; arrow.speed = arrowSpeed; // If we have special arrow types, randomly select one to apply if (arrowSpecials.length > 0) { var selectedSpecial = arrowSpecials[Math.floor(Math.random() * arrowSpecials.length)]; arrow.specialType = selectedSpecial; // Visual indicators for special arrows if (selectedSpecial === "poison") { arrow.arrowBody.tint = 0x00FF00; // Green for poison arrow.arrowHead.tint = 0x00FF00; } else if (selectedSpecial === "explosive") { arrow.arrowBody.tint = 0xFF9900; // Orange for explosive arrow.arrowHead.tint = 0xFF9900; } else if (selectedSpecial === "freeze") { arrow.arrowBody.tint = 0x00FFFF; // Cyan for freeze arrow.arrowHead.tint = 0x00FFFF; } else if (selectedSpecial === "fire") { arrow.arrowBody.tint = 0xFF3300; // Orange-red for fire arrow.arrowHead.tint = 0xFF3300; } else if (selectedSpecial === "lightning") { arrow.arrowBody.tint = 0xFFFF00; // Yellow for lightning arrow.arrowHead.tint = 0xFFFF00; } else if (selectedSpecial === "vampiric") { arrow.arrowBody.tint = 0xFF00FF; // Purple for vampiric arrow.arrowHead.tint = 0xFF00FF; } else if (selectedSpecial === "homing") { arrow.arrowBody.tint = 0xFF6600; // Orange for homing arrow.arrowHead.tint = 0xFF6600; } } } } game.down = handleClick; game.update = function () { if (paused) return; // Update player if exists if (player) { player.update(); } // Spawn monsters var now = Date.now(); var spawnInterval = Math.max(2000 - currentWave * 100, 500); var monstersPerWave = 2 + Math.floor(currentWave * 0.8); // Reduced monster count for shorter waves // For boss waves, only spawn the boss if (currentWave % 10 === 0) { if (!bossSpawned && !bossDefeated) { spawnMonster(); // This will spawn the boss } } // Regular waves spawn multiple monsters else if (now - lastSpawnTime > spawnInterval && monsters.length < monstersPerWave) { spawnMonster(); } // Check for boss defeat var hasBoss = false; for (var i = 0; i < monsters.length; i++) { if (monsters[i] instanceof Boss) { hasBoss = true; break; } } // If this is a boss wave and the boss has been spawned but is no longer present if (currentWave % 10 === 0 && bossSpawned && !hasBoss && !bossDefeated) { bossDefeated = true; // Create boss defeat celebration effect var defeatEffect = new Container(); var celebrationCircle = LK.getAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 10, scaleY: 10, tint: 0xFFFFFF, alpha: 0.2 }); defeatEffect.x = 2048 / 2; defeatEffect.y = 2732 / 2; game.addChild(defeatEffect); defeatEffect.addChild(celebrationCircle); // Add victory text var victoryText = new Text2("BOSS DEFEATED!", { size: 120, fill: 0x00FF00 }); victoryText.anchor.set(0.5, 0.5); defeatEffect.addChild(victoryText); // Animate the celebration effect tween(celebrationCircle, { scaleX: 20, scaleY: 20, alpha: 0 }, { duration: 2000, onFinish: function onFinish() { if (defeatEffect.parent) { defeatEffect.parent.removeChild(defeatEffect); } // Show special reward for defeating boss showUpgradeScreen(); } }); // No extra bonus points for defeating boss scoreText.setText("Hits: " + monstersKilled); // Boss defeat text animation var bonusText = new Text2("BOSS DEFEATED", { size: 80, fill: 0xFFFF00 }); bonusText.anchor.set(0.5, 0.5); bonusText.x = 2048 / 2; bonusText.y = 2732 / 2 + 150; LK.gui.addChild(bonusText); // Animate bonus text tween(bonusText, { y: bonusText.y - 100, alpha: 0 }, { duration: 2000, onFinish: function onFinish() { if (bonusText.parent) { bonusText.parent.removeChild(bonusText); } } }); } // Check if regular wave is complete - based on monsters killed rather than time if (!showingUpgrades && (currentWave % 10 !== 0 && (monstersKilled >= currentWave * 10 || monsters.length === 0 && now - lastSpawnTime > spawnInterval * 2) || currentWave % 10 === 0 && bossDefeated)) { if (!showingUpgrades) { showUpgradeScreen(); } } // Update arrows for (var i = arrows.length - 1; i >= 0; i--) { if (arrows[i].needsRemoval) { game.removeChild(arrows[i]); arrows.splice(i, 1); continue; } } // Check for collisions for (var i = arrows.length - 1; i >= 0; i--) { var arrow = arrows[i]; for (var j = monsters.length - 1; j >= 0; j--) { var monster = monsters[j]; if (arrow.canHit(monster) && arrow.intersects(monster)) { arrow.hit(monster); if (monster.takeDamage(arrow.damage)) { // Monster is defeated game.removeChild(monster); monsters.splice(j, 1); // Play pop sound when monster is killed LK.getSound('pop').play(); // Add one point regardless of monster type monstersKilled++; scoreText.setText("Score: " + monstersKilled); } if (arrow.needsRemoval) { game.removeChild(arrow); arrows.splice(i, 1); break; } } } } // Update orbital objects for (var i = orbitalObjects.length - 1; i >= 0; i--) { orbitalObjects[i].update(); } // Update stars for parallax effect for (var i = 0; i < stars.length; i++) { stars[i].update(); } // Update health bar updateHealthBar(); }; // Initialize background music with intense battle theme // Start the game initGame(); ;
===================================================================
--- original.js
+++ change.js
@@ -2266,9 +2266,9 @@
});
self.icon = self.attachAsset('powerIcon', {
anchorX: 0.5,
anchorY: 0.5,
- y: -150
+ y: -120
});
// Title
self.title = new Text2(ability.name, {
size: 36,