User prompt
MKe the players archer a spaceship
User prompt
I can't yere the pop or intense music and muly volume is full
User prompt
Add intense music in the background
User prompt
Add a popping sound for when monsters are killed
User prompt
Make them so they don't add additional points to kill count
User prompt
Make it so it does not increase your score when you kill a boss Ny more than a regular monster
User prompt
When I got upgrades for the boss level it sent me into level 16
User prompt
Make a boss upgrade that if you have multishot adds an arrow to the multishot abilities
User prompt
Make the chose one text on upgrade screen smaller to fit in screen and move to the left
User prompt
On the upgrade screen chose one text at the top add the end of the text under the chose one so it can fit in screen
User prompt
Make it so monsters can pass by orbital objects easier
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'removeChild')' in or related to this line: 'upgradeCards[i].parent.removeChild(upgradeCards[i]);' Line Number: 1941
User prompt
Move the chose one areow effect words at the top to the left
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'removeChild')' in or related to this line: 'upgradeCards[i].parent.removeChild(upgradeCards[i]);' Line Number: 1941
User prompt
Make a boss monsters with lots of heath every ten rounds and gets better every time
User prompt
Take out the rebounding arrows because c they are not working
User prompt
The rebound arrows do not bounce off walls
User prompt
Make it so the orbital objects do less damage
User prompt
The rebounding arrows are not rebounding off monsters
User prompt
Make the rebounding arrows rebound off monsters after they do the damage
User prompt
Makebit sobthe orbiting objects circle farther away from the archer
User prompt
Add visual effects to orbital objects
User prompt
Makebit so the rebound arrows rebond off all ofbthe walls on the screen
User prompt
Make homing arrows die after 5 seconds
User prompt
Add more arrow special abilities including multishot, rebond, etc
/**** * 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); self.arrowBody = self.attachAsset('arrow', { anchorX: 0, anchorY: 0.5 }); self.arrowHead = self.attachAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, x: 50 }); self.speed = 15; 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 if (LK.ticks % 3 === 0) { var trail = new Container(); var trailParticle = LK.getAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0xFF6600, alpha: 0.5 }); trail.x = self.x; trail.y = self.y; trail.addChild(trailParticle); game.addChild(trail); // Fade out trail tween(trailParticle, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 300, 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++; // 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 }); 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 || 200; // Increased distance from 120 to 200 self.speed = 0.03; 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") { self.body = self.attachAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2, scaleY: 0.2 }); self.body.tint = 0x3399FF; // Blue shields } else if (type === "swords") { self.body = self.attachAsset('arrow', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.4 }); self.body.tint = 0xCCCCCC; // Silver swords } else if (type === "orbs") { self.body = self.attachAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); self.body.tint = 0xFFCC00; // Yellow orbs } else if (type === "flame_barrier") { 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 } else if (type === "ice_shards") { self.body = self.attachAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.15, scaleY: 0.15 }); self.body.tint = 0x99CCFF; // Light blue for ice shards } else if (type === "spirit_guardians") { self.body = self.attachAsset('arrowHead', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3 }); self.body.tint = 0xDDDDFF; // Pale blue-white for spirits } self.update = function () { if (!player) return; // 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; } // 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 shield impact effect var shieldEffect = new Container(); var impactWave = LK.getAsset('powerIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, tint: 0x3399FF, alpha: 0.6 }); shieldEffect.x = self.x; shieldEffect.y = self.y; game.addChild(shieldEffect); shieldEffect.addChild(impactWave); // Animate shield effect tween(impactWave, { scaleX: 1.8, scaleY: 1.8, alpha: 0 }, { duration: 400, onFinish: function onFinish() { if (shieldEffect.parent) { shieldEffect.parent.removeChild(shieldEffect); } } }); } LK.effects.flashObject(self, 0x3399FF, 200); 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); 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); } 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); } } 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); monstersKilled++; scoreText.setText("Score: " + monstersKilled); } LK.effects.flashObject(self, 0xFF3300, 200); } 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); monstersKilled++; scoreText.setText("Score: " + monstersKilled); } LK.effects.flashObject(self, 0x99CCFF, 200); } else if (self.type === "spirit_guardians") { // Spirit guardians pass through enemies if (monster.takeDamage(self.damage)) { game.removeChild(monster); monsters.splice(i, 1); monstersKilled++; scoreText.setText("Score: " + monstersKilled); } LK.effects.flashObject(self, 0xDDDDFF, 200); // 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 }); 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; var arrow = new Arrow(); arrow.x = self.x; arrow.y = self.y; var angle = Math.atan2(targetY - self.y, targetX - self.x); arrow.rotation = angle; LK.getSound('shoot').play(); return arrow; }; self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { self.health = 0; 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 for (var i = 0; i < 3; i++) { var angle = Math.PI * 2 / 3 * i; var orbitalObj = new OrbitalObject(ability.specialType, angle); orbitalObjects.push(orbitalObj); game.addChild(orbitalObj); } } } }; 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: -150 }); // Title self.title = new Text2(ability.name, { size: 36, fill: 0x000000 }); self.title.anchor.set(0.5, 0.5); self.title.y = -50; 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 = 50; 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: 0x222222 }); /**** * Game Code ****/ // Game variables var player; var monsters = []; var arrows = []; var upgradeCards = []; var orbitalObjects = []; 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 Arrows", description: "Arrows poison enemies, dealing damage over time", type: "special", specialType: "poison" }, { name: "Explosive Arrows", description: "Arrows explode on impact, damaging nearby enemies", type: "special", specialType: "explosive" }, { name: "Freezing Arrows", description: "Arrows slow down enemies for a short time", type: "special", specialType: "freeze" }, { name: "Fire Arrows", description: "Arrows burn enemies with intense flames", type: "special", specialType: "fire" }, { name: "Lightning Arrows", description: "Arrows chain lightning between nearby enemies", type: "special", specialType: "lightning" }, { name: "Vampiric Arrows", description: "Arrows steal health from enemies and heal you", type: "special", specialType: "vampiric" }, { name: "Multi-Shot Arrows", description: "Fire three arrows in a spread pattern with each shot", type: "special", specialType: "multishot" }, { name: "Homing Arrows", description: "Arrows seek out nearby enemies after being fired", type: "special", specialType: "homing" }, { name: "Orbiting Shields", description: "Three shields circle around you, blocking projectiles", 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("Score: 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: 80, fill: 0xFFFFFF }); upgradeTitle.anchor.set(0.5, 0.5); upgradeTitle.x = 2048 / 2; 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 = []; // 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 LK.playMusic('gameMusic'); } 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; } else { // Non-boss wave bossSpawned = false; bossDefeated = true; // No boss to defeat on non-boss waves } // 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); 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; LK.showGameOver(); } function handleClick(x, y, obj) { if (paused || !player) return; // Check if player has multishot ability var hasMultishot = false; for (var i = 0; i < player.abilities.length; i++) { if (player.abilities[i].type === "special" && player.abilities[i].specialType === "multishot") { hasMultishot = true; break; } } // 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 three arrows in a spread pattern if (hasMultishot) { // Center arrow var mainArrow = player.shoot(x, y); if (mainArrow) { applyArrowProperties(mainArrow, arrowSpecials); arrows.push(mainArrow); game.addChild(mainArrow); // Create left and right arrows with slight angle adjustments var leftAngle = mainArrow.rotation - Math.PI / 12; // 15 degrees left var rightAngle = mainArrow.rotation + Math.PI / 12; // 15 degrees right // Create left arrow 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); // Create right arrow 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); // 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; // 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(); } }); // Award bonus points for defeating boss var bossBonus = currentWave * 5; monstersKilled += bossBonus; scoreText.setText("Score: " + monstersKilled); // Boss defeat text animation var bonusText = new Text2("+" + bossBonus + " BONUS", { 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); // Add extra points for bosses if (monster instanceof Boss) { monstersKilled += currentWave; } else { 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 health bar updateHealthBar(); }; // Start the game initGame();
===================================================================
--- original.js
+++ change.js
@@ -1906,9 +1906,11 @@
showingUpgrades = true;
upgradeContainer.visible = true;
// Clear previous upgrade cards
for (var i = 0; i < upgradeCards.length; i++) {
- upgradeCards[i].parent.removeChild(upgradeCards[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) {