User prompt
High Score Chase: The endless waves and score tracking provide strong replayability. βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
User prompt
Adapt to New Threats: As waves progress, new, tougher enemy types appear, forcing players to adapt their strategies and prioritize different upgrades. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Upgrade Castle: Between waves, or when a good opportunity arises, they spend Scrap Metal to make their castle stronger. βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
User prompt
Collect Resources: During waves, they prioritize collecting Scrap Metal drops.
User prompt
Survive Waves: Players focus on dodging, shooting, and collecting power-ups to clear waves. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'setText')' in or related to this line: 'waveText.setText('Wave: ' + waveNumber);' Line Number: 2187
User prompt
UI Elements & Feedback To make these mechanics clear to the player, we'll need appropriate UI. Health Bar: Clearly visible castle health bar. Wave Counter: Displays the current wave number. Score Display: Shows the player's current score. Power-Up Icons: Visual indicators for collected power-ups and their remaining duration (if applicable). Resource Counter: Displays the amount of Scrap Metal collected. Upgrade Button/Menu: An accessible way to view and apply upgrades. Enemy Health Bars: Optional, but could be very helpful for players to prioritize targets. Incoming Threat Indicators: Small arrows or markers at the screen edges indicating where new enemies are spawning. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Upgrade Tiers: Each upgrade could have multiple tiers, becoming progressively more expensive but offering greater benefits. βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'bulletsPerShot')' in or related to this line: 'var bulletsText = new Text2('Bullets: ' + castle.bulletsPerShot, {' Line Number: 1479
User prompt
Special Abilities: Unlock new active abilities with cooldowns, like a temporary shield or a short-range shockwave. βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1, @upit/tween.v1
User prompt
Bullet Count (Passive): A separate upgrade from the Green Bullets power-up, allowing players to permanently fire more bullets per shot. βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
User prompt
Max Health: Increases the castle's maximum health. βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
User prompt
Movement Speed: Increases how fast the castle moves. βͺπ‘ Consider importing and using the following plugins: @upit/storage.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { scrapMetal: 0, fireRateLevel: 1, movementSpeedLevel: 1, healthLevel: 1, damageLevel: 1 }); /**** * Classes ****/ var Bomb = Container.expand(function () { var self = Container.call(this); var bombGraphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); bombGraphics.tint = 0xFF0000; self.explosionRadius = 1000; self.fuseTime = 120; // 2 seconds at 60fps self.blinkSpeed = 10; // Start with small scale self.scaleX = 0.3; self.scaleY = 0.3; self.update = function () { self.fuseTime--; // Blinking effect that gets faster as explosion approaches var blinkInterval = Math.max(5, Math.floor(self.fuseTime / 10)); if (LK.ticks % blinkInterval === 0) { bombGraphics.alpha = bombGraphics.alpha === 1 ? 0.3 : 1; } // Scale pulsing effect var pulseScale = 1 + Math.sin(LK.ticks * 0.3) * 0.1; self.scaleX = pulseScale; self.scaleY = pulseScale; // Explode when fuse runs out if (self.fuseTime <= 0) { self.explode(); } }; self.explode = function () { // Visual explosion effect LK.effects.flashScreen(0xFF4400, 600); // Scale up rapidly during explosion tween(self, { scaleX: 3, scaleY: 3 }, { duration: 300, easing: tween.easeOut }); // Damage enemies in radius for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.explosionRadius) { // Instant kill for enemies in blast radius enemy.takeDamage(999); } } // Remove bomb from game after explosion animation LK.setTimeout(function () { self.destroy(); for (var i = bombs.length - 1; i >= 0; i--) { if (bombs[i] === self) { bombs.splice(i, 1); break; } } }, 300); }; return self; }); var BombReward = Container.expand(function () { var self = Container.call(this); var rewardGraphics = self.attachAsset('bombReward', { anchorX: 0.5, anchorY: 0.5 }); // Make it visually distinct with orange tint rewardGraphics.tint = 0xFF8800; self.bobOffset = Math.random() * Math.PI * 2; self.baseY = 0; self.lifetime = 420; // 7 seconds at 60fps self.update = function () { // Gentle bobbing animation self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 5; // Decrease lifetime self.lifetime--; // Start fading when close to expiring if (self.lifetime <= 120) { var alpha = self.lifetime / 120; rewardGraphics.alpha = alpha; // Add flashing animation when close to expiring var flashInterval = Math.max(5, Math.floor(self.lifetime / 20)); if (LK.ticks % flashInterval === 0) { tween(rewardGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: flashInterval * 8, easing: tween.easeOut, onFinish: function onFinish() { tween(rewardGraphics, { scaleX: 1, scaleY: 1 }, { duration: flashInterval * 8, easing: tween.easeIn }); } }); } } // Remove when lifetime expires if (self.lifetime <= 0) { tween(self, { scaleX: 0, scaleY: 0 }, { duration: 200, onFinish: function onFinish() { self.destroy(); for (var i = bombRewards.length - 1; i >= 0; i--) { if (bombRewards[i] === self) { bombRewards.splice(i, 1); break; } } } }); } }; self.down = function (x, y, obj) { // Schedule bomb to appear at character position after 3 seconds var bombX = castle.x; var bombY = castle.y; LK.setTimeout(function () { var bomb = new Bomb(); bomb.x = bombX; bomb.y = bombY; bombs.push(bomb); game.addChild(bomb); }, 3000); LK.getSound('award').play(); LK.effects.flashObject(castle, 0xFF8800, 500); self.destroy(); for (var i = bombRewards.length - 1; i >= 0; i--) { if (bombRewards[i] === self) { bombRewards.splice(i, 1); break; } } }; return self; }); var BomberEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('bomberEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 3; self.maxHealth = 3; self.speed = 1.2; self.damage = 12; self.explosionRadius = 100; self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } var currentDistance = Math.sqrt(dx * dx + dy * dy); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.explode(); } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.explode = function () { LK.effects.flashScreen(0xFF8800, 300); // Damage nearby enemies for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy !== self) { var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.explosionRadius) { enemy.takeDamage(2); } } } self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 30); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); self.explode(); } }; return self; }); var BossEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('bossEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 15; self.maxHealth = 15; self.speed = 0.6; self.damage = 30; self.spawnCooldown = 0; self.spawnInterval = 300; // Spawns minions every 5 seconds self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } // Spawn minion enemies if (self.spawnCooldown > 0) { self.spawnCooldown--; } if (self.spawnCooldown <= 0 && enemies.length < 15) { var minion = new Enemy(); var angle = Math.random() * Math.PI * 2; minion.x = self.x + Math.cos(angle) * 60; minion.y = self.y + Math.sin(angle) * 60; minion.lastX = minion.x; minion.lastY = minion.y; var dx2 = castle.x - minion.x; var dy2 = castle.y - minion.y; minion.lastCastleDistance = Math.sqrt(dx2 * dx2 + dy2 * dy2); enemies.push(minion); game.addChild(minion); self.spawnCooldown = self.spawnInterval; } var currentDistance = Math.sqrt(dx * dx + dy * dy); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.takeDamage = function (damage) { var reducedDamage = damage * 0.7; // Boss takes 30% less damage self.health -= reducedDamage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 100); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); LK.effects.flashScreen(0x440044, 1000); // Drop multiple scrap metal (guaranteed) for (var c = 0; c < 3; c++) { var scrap = new ScrapMetalDrop(); var angle = Math.random() * Math.PI * 2; var distance = 20 + Math.random() * 40; scrap.x = self.x + Math.cos(angle) * distance; scrap.y = self.y + Math.sin(angle) * distance; scrap.baseY = scrap.y; scrap.value = 15; // High value for boss scrapMetalDrops.push(scrap); game.addChild(scrap); } self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 50; self.velocityX = 0; self.velocityY = 0; self.damage = 1.01; self.lastX = 0; self.lastY = 0; self.update = function () { self.lastX = self.x; self.lastY = self.y; self.x += self.velocityX; self.y += self.velocityY; // Add spinning animation self.rotation += 0.2; // Remove if off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.destroy(); for (var i = bullets.length - 1; i >= 0; i--) { if (bullets[i] === self) { bullets.splice(i, 1); break; } } } }; return self; }); var Castle = Container.expand(function () { var self = Container.call(this); var castleGraphics = self.attachAsset('castle', { anchorX: 0.5, anchorY: 0.5 }); // Set physical diameter to match image dimensions self.width = castleGraphics.width; self.height = castleGraphics.height; self.health = 100; self.maxHealth = 100; self.bulletsPerShot = 1; self.shootCooldown = 0; self.shootInterval = 60; // Fire every second at 60fps self.update = function () { if (self.shootCooldown > 0) { self.shootCooldown--; } // Auto-fire with priority targeting system if (self.shootCooldown <= 0 && enemies.length > 0) { var targetEnemy = null; var bestPriority = -1; var bestDistance = Infinity; // Priority levels: Higher number = higher priority var priorityMap = { 'SniperEnemy': 5, // Highest priority - long range damage 'BomberEnemy': 4, // High priority - explosive threat 'BossEnemy': 4, // High priority - powerful enemy 'FastEnemy': 3, // Medium-high priority - quick threat 'HealerEnemy': 3, // Medium-high priority - supports others 'TeleporterEnemy': 2, // Medium priority - unpredictable 'SplitterEnemy': 2, // Medium priority - creates more enemies 'TankEnemy': 1, // Low priority - slow but tough 'ShieldEnemy': 1, // Low priority - tough but manageable 'Enemy': 0 // Lowest priority - basic enemy }; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Determine enemy type by checking constructor name var enemyType = 'Enemy'; // Default if (enemy.constructor === SniperEnemy) enemyType = 'SniperEnemy';else if (enemy.constructor === BomberEnemy) enemyType = 'BomberEnemy';else if (enemy.constructor === BossEnemy) enemyType = 'BossEnemy';else if (enemy.constructor === FastEnemy) enemyType = 'FastEnemy';else if (enemy.constructor === HealerEnemy) enemyType = 'HealerEnemy';else if (enemy.constructor === TeleporterEnemy) enemyType = 'TeleporterEnemy';else if (enemy.constructor === SplitterEnemy) enemyType = 'SplitterEnemy';else if (enemy.constructor === TankEnemy) enemyType = 'TankEnemy';else if (enemy.constructor === ShieldEnemy) enemyType = 'ShieldEnemy'; var priority = priorityMap[enemyType] || 0; // Select target based on priority first, then distance if (priority > bestPriority || priority === bestPriority && distance < bestDistance) { bestPriority = priority; bestDistance = distance; targetEnemy = enemy; } } if (targetEnemy) { self.fireAtTarget(targetEnemy); self.shootCooldown = self.shootInterval; } } }; self.fireAtTarget = function (target) { var baseAngle = Math.atan2(target.y - self.y, target.x - self.x); var spreadAngle = Math.PI / 6; // 30 degrees spread for (var i = 0; i < self.bulletsPerShot; i++) { var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; // Apply upgraded damage (always use upgraded damage) bullet.damage = self.upgradedBulletDamage || bullet.damage; var offset = 0; if (self.bulletsPerShot > 1) { offset = (i - (self.bulletsPerShot - 1) / 2) * (spreadAngle / Math.max(1, self.bulletsPerShot - 1)); } var angle = baseAngle + offset; bullet.velocityX = Math.cos(angle) * bullet.speed; bullet.velocityY = Math.sin(angle) * bullet.speed; // Set bullet rotation to match direction bullet.rotation = angle; bullets.push(bullet); game.addChild(bullet); } LK.getSound('shoot').play(); }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFF0000, 300); if (self.health <= 0) { // Save collected scrap metal to persistent storage storage.scrapMetal += currentScrapMetal; LK.showGameOver(); } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 3; self.maxHealth = 3; self.speed = 1; self.damage = 10; self.lastX = 0; self.lastY = 0; self.lastCastleDistanceSquared = Infinity; self.update = function () { // Move toward castle var dx = castle.x - self.x; var dy = castle.y - self.y; var distanceSquared = dx * dx + dy * dy; if (distanceSquared > 3600) { // 60^2 = 3600 // Don't overlap with castle var distance = Math.sqrt(distanceSquared); var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } // Check if reached castle if (self.lastCastleDistanceSquared > 3600 && distanceSquared <= 3600) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistanceSquared = distanceSquared; }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 10); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); // Drop scrap metal (60% chance) if (Math.random() < 0.6) { var scrap = new ScrapMetalDrop(); scrap.x = self.x; scrap.y = self.y; scrap.baseY = scrap.y; scrap.value = 5; // Basic enemy drops 5 scrap metal scrapMetalDrops.push(scrap); game.addChild(scrap); } self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var FastEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('fastEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; self.maxHealth = 1; self.speed = 3; self.damage = 5; self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { // Move toward castle with higher speed var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } // Check if reached castle var currentDistance = Math.sqrt(dx * dx + dy * dy); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 15); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); // Drop scrap metal (50% chance, less than basic enemy) if (Math.random() < 0.5) { var scrap = new ScrapMetalDrop(); scrap.x = self.x; scrap.y = self.y; scrap.baseY = scrap.y; scrap.value = 3; // Less value than basic enemy scrapMetalDrops.push(scrap); game.addChild(scrap); } self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var HealerEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('healerEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 4; self.maxHealth = 4; self.speed = 0.8; self.damage = 8; self.healCooldown = 0; self.healInterval = 180; // Heals every 3 seconds self.healRadius = 150; self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } // Heal nearby enemies if (self.healCooldown > 0) { self.healCooldown--; } if (self.healCooldown <= 0) { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy !== self && enemy.health < enemy.maxHealth) { var dx2 = enemy.x - self.x; var dy2 = enemy.y - self.y; var healDistance = Math.sqrt(dx2 * dx2 + dy2 * dy2); if (healDistance <= self.healRadius) { enemy.health = Math.min(enemy.maxHealth, enemy.health + 1); LK.effects.flashObject(enemy, 0x88FF00, 200); } } } self.healCooldown = self.healInterval; } var currentDistance = Math.sqrt(dx * dx + dy * dy); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 35); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var Reward = Container.expand(function () { var self = Container.call(this); var rewardGraphics = self.attachAsset('reward', { anchorX: 0.5, anchorY: 0.5 }); self.bobOffset = Math.random() * Math.PI * 2; self.baseY = 0; self.lifetime = 420; // 7 seconds at 60fps self.update = function () { // Gentle bobbing animation self.y = self.baseY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 5; // Decrease lifetime self.lifetime--; // Start fading when close to expiring if (self.lifetime <= 120) { var alpha = self.lifetime / 120; rewardGraphics.alpha = alpha; // Add flashing animation when close to expiring var flashInterval = Math.max(5, Math.floor(self.lifetime / 20)); if (LK.ticks % flashInterval === 0) { tween(rewardGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: flashInterval * 8, easing: tween.easeOut, onFinish: function onFinish() { tween(rewardGraphics, { scaleX: 1, scaleY: 1 }, { duration: flashInterval * 8, easing: tween.easeIn }); } }); } } // Remove when lifetime expires if (self.lifetime <= 0) { tween(self, { scaleX: 0, scaleY: 0 }, { duration: 200, onFinish: function onFinish() { self.destroy(); for (var i = rewards.length - 1; i >= 0; i--) { if (rewards[i] === self) { rewards.splice(i, 1); break; } } } }); } }; self.down = function (x, y, obj) { // Define maximum bullet limit to prevent excessive stacking var maxBullets = 8; if (castle.bulletsPerShot < maxBullets) { // Add 1 bullet per reward (consistent increment) castle.bulletsPerShot++; bulletsText.setText('Bullets: ' + castle.bulletsPerShot); LK.getSound('award').play(); LK.effects.flashObject(castle, 0x00FF00, 500); // Visual feedback for bullet upgrade with scaling effect tween(castle, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(castle, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); } else { // At maximum bullets - give score bonus instead LK.setScore(LK.getScore() + 100); scoreText.setText(LK.getScore()); bulletsText.setText('Bullets: ' + castle.bulletsPerShot + ' (MAX)'); LK.getSound('award').play(); LK.effects.flashObject(castle, 0xFFD700, 500); // Gold flash for max bonus // Brief text notification var maxText = new Text2('+100 SCORE!', { size: 40, fill: 0xFFD700 }); maxText.anchor.set(0.5, 0.5); maxText.x = castle.x; maxText.y = castle.y - 60; game.addChild(maxText); // Animate and remove notification tween(maxText, { y: maxText.y - 50, alpha: 0 }, { duration: 1500, onFinish: function onFinish() { maxText.destroy(); } }); } self.destroy(); for (var i = rewards.length - 1; i >= 0; i--) { if (rewards[i] === self) { rewards.splice(i, 1); break; } } }; return self; }); var ScrapMetalDrop = Container.expand(function () { var self = Container.call(this); var scrapGraphics = self.attachAsset('reward', { anchorX: 0.5, anchorY: 0.5 }); // Make it metallic silver for scrap metal scrapGraphics.tint = 0xC0C0C0; scrapGraphics.scaleX = 0.6; scrapGraphics.scaleY = 0.6; self.value = 5; // Base scrap metal value self.bobOffset = Math.random() * Math.PI * 2; self.baseY = 0; self.lifetime = 600; // 10 seconds at 60fps self.magnetRange = 120; // Range at which scrap is attracted to castle self.update = function () { // Gentle bobbing animation self.y = self.baseY + Math.sin(LK.ticks * 0.15 + self.bobOffset) * 3; // Decrease lifetime self.lifetime--; // Magnet effect when castle is nearby var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.magnetRange) { var magnetSpeed = 3; var moveX = dx / distance * magnetSpeed; var moveY = dy / distance * magnetSpeed; self.x += moveX; self.y += moveY; } // Start fading when close to expiring if (self.lifetime <= 120) { var alpha = self.lifetime / 120; scrapGraphics.alpha = alpha; } // Remove when lifetime expires if (self.lifetime <= 0) { self.destroy(); for (var i = scrapMetalDrops.length - 1; i >= 0; i--) { if (scrapMetalDrops[i] === self) { scrapMetalDrops.splice(i, 1); break; } } } }; return self; }); var ShieldEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('shieldEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 6; self.maxHealth = 6; self.speed = 0.7; self.damage = 15; self.shieldRadius = 80; self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } var currentDistance = Math.sqrt(dx * dx + dy * dy); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 40); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var SniperEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('sniperEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 2; self.maxHealth = 2; self.speed = 1.5; self.damage = 15; self.shootCooldown = 0; self.shootInterval = 120; // Shoots every 2 seconds self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Move toward castle until in range (300 pixels) if (distance > 300) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } else { // In range, shoot at castle if (self.shootCooldown > 0) { self.shootCooldown--; } if (self.shootCooldown <= 0) { castle.takeDamage(self.damage); LK.effects.flashObject(self, 0x00FFFF, 300); self.shootCooldown = self.shootInterval; } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = distance; }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 20); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var SplitterEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('splitterEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 5; self.maxHealth = 5; self.speed = 0.9; self.damage = 18; self.splitCount = 2; self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } var currentDistance = Math.sqrt(dx * dx + dy * dy); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.split = function () { for (var i = 0; i < self.splitCount; i++) { var smallEnemy = new FastEnemy(); var angle = Math.PI * 2 / self.splitCount * i; smallEnemy.x = self.x + Math.cos(angle) * 30; smallEnemy.y = self.y + Math.sin(angle) * 30; smallEnemy.lastX = smallEnemy.x; smallEnemy.lastY = smallEnemy.y; var dx = castle.x - smallEnemy.x; var dy = castle.y - smallEnemy.y; smallEnemy.lastCastleDistance = Math.sqrt(dx * dx + dy * dy); enemies.push(smallEnemy); game.addChild(smallEnemy); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 50); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); self.split(); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var TankEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('tankEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 8; self.maxHealth = 8; self.speed = 0.5; self.damage = 20; self.armor = 0.5; // Takes 50% less damage self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { // Move toward castle slowly var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } // Check if reached castle var currentDistance = Math.sqrt(dx * dx + dy * dy); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.takeDamage = function (damage) { var reducedDamage = damage * (1 - self.armor); self.health -= reducedDamage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 25); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); var TeleporterEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('teleporterEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 2; self.maxHealth = 2; self.speed = 1; self.damage = 12; self.teleportCooldown = 0; self.teleportInterval = 240; // Teleports every 4 seconds self.lastX = 0; self.lastY = 0; self.lastCastleDistance = Infinity; self.update = function () { var dx = castle.x - self.x; var dy = castle.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 60) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } // Teleport randomly if (self.teleportCooldown > 0) { self.teleportCooldown--; } if (self.teleportCooldown <= 0) { var angle = Math.random() * Math.PI * 2; var teleportDistance = 200 + Math.random() * 200; self.x = castle.x + Math.cos(angle) * teleportDistance; self.y = castle.y + Math.sin(angle) * teleportDistance; // Keep within bounds self.x = Math.max(50, Math.min(1998, self.x)); self.y = Math.max(50, Math.min(2682, self.y)); LK.effects.flashObject(self, 0xFF00FF, 400); self.teleportCooldown = self.teleportInterval; } var currentDistance = Math.sqrt((castle.x - self.x) * (castle.x - self.x) + (castle.y - self.y) * (castle.y - self.y)); if (self.lastCastleDistance > 60 && currentDistance <= 60) { castle.takeDamage(self.damage); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } self.lastX = self.x; self.lastY = self.y; self.lastCastleDistance = currentDistance; }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { LK.setScore(LK.getScore() + 25); scoreText.setText(LK.getScore()); LK.getSound('hit').play(); self.destroy(); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2F4F2F }); /**** * Game Code ****/ // Game variables var castle; var enemies = []; var bullets = []; var rewards = []; var bombRewards = []; var bombs = []; var scrapMetalDrops = []; var waveTimer = 0; var enemySpawnTimer = 0; var rewardSpawnTimer = 0; var waveNumber = 1; var isMiniBossWave = false; var miniBossSpawned = false; // Initialize persistent storage with defaults // Current game scrap metal (resets each game) var currentScrapMetal = 0; // Create background pattern function createBackground() { var bgContainer = new Container(); // Create simpler grid pattern with fewer random calculations for (var x = 0; x <= 2048; x += 240) { for (var y = 0; y <= 2732; y += 240) { // Main background tiles with minimal randomization var tile = LK.getAsset('bgTile', { anchorX: 0.5, anchorY: 0.5 }); tile.x = x; tile.y = y; bgContainer.addChild(tile); // Reduce accent frequency for better performance if ((x + y) % 960 === 0) { // Every 4th tile instead of random var accent = LK.getAsset('bgAccent', { anchorX: 0.5, anchorY: 0.5 }); accent.x = x; accent.y = y; accent.alpha = 0.5; bgContainer.addChild(accent); } } } // Set background container to lowest z-index game.addChildAt(bgContainer, 0); } // Initialize background createBackground(); // UI elements var scoreText = new Text2('0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); var bulletsText = new Text2('Bullets: 1', { size: 50, fill: 0x00FF00 }); bulletsText.anchor.set(0, 0); bulletsText.x = 50; bulletsText.y = 50; LK.gui.topLeft.addChild(bulletsText); var healthText = new Text2('Health: 100', { size: 50, fill: 0xFF0000 }); healthText.anchor.set(1, 0); LK.gui.topRight.addChild(healthText); var scrapMetalText = new Text2('Scrap Metal: 0', { size: 45, fill: 0xC0C0C0 }); scrapMetalText.anchor.set(0, 0); scrapMetalText.x = 50; scrapMetalText.y = 120; LK.gui.topLeft.addChild(scrapMetalText); // Initialize castle castle = game.addChild(new Castle()); castle.x = 1024; castle.y = 1366; // Apply permanent upgrades from storage var fireRateBonus = (storage.fireRateLevel - 1) * 0.15; // 15% faster per level castle.shootInterval = Math.max(20, Math.floor(castle.shootInterval * (1 - fireRateBonus))); var healthBonus = (storage.healthLevel - 1) * 20; // +20 health per level castle.maxHealth += healthBonus; castle.health = castle.maxHealth; // Start with full health var damageBonus = (storage.damageLevel - 1) * 0.2; // +20% damage per level var baseBulletDamage = 1.01; // Store upgraded damage for bullets castle.upgradedBulletDamage = baseBulletDamage * (1 + damageBonus); // Movement speed will be applied in the movement system var movementSpeedBonus = (storage.movementSpeedLevel - 1) * 0.25; // 25% faster per level var baseCastleSpeed = 400; castleMovementSpeed = baseCastleSpeed * (1 + movementSpeedBonus); // Start background music LK.playMusic('bgmusic'); // Spawn functions function spawnEnemy() { var enemy; // Mini-boss wave logic - every 5th wave if (isMiniBossWave) { if (!miniBossSpawned) { // Always spawn a boss enemy as the first enemy of mini-boss wave enemy = new BossEnemy(); miniBossSpawned = true; } else { // Higher chance for tank/shield enemies in mini-boss waves var enemyType = Math.random(); if (enemyType < 0.4) { enemy = new TankEnemy(); } else if (enemyType < 0.7) { enemy = new ShieldEnemy(); } else if (enemyType < 0.85) { enemy = new BossEnemy(); } else { enemy = new BomberEnemy(); } } } else { // Normal wave enemy distribution var enemyType = Math.random(); if (enemyType < 0.3) { enemy = new Enemy(); } else if (enemyType < 0.45) { enemy = new FastEnemy(); } else if (enemyType < 0.55) { enemy = new TankEnemy(); } else if (enemyType < 0.65) { enemy = new SniperEnemy(); } else if (enemyType < 0.73) { enemy = new BomberEnemy(); } else if (enemyType < 0.8) { enemy = new HealerEnemy(); } else if (enemyType < 0.86) { enemy = new ShieldEnemy(); } else if (enemyType < 0.92) { enemy = new TeleporterEnemy(); } else if (enemyType < 0.97) { enemy = new SplitterEnemy(); } else { enemy = new BossEnemy(); } } // Spawn from random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top enemy.x = Math.random() * 2048; enemy.y = -20; break; case 1: // Right enemy.x = 2068; enemy.y = Math.random() * 2732; break; case 2: // Bottom enemy.x = Math.random() * 2048; enemy.y = 2752; break; case 3: // Left enemy.x = -20; enemy.y = Math.random() * 2732; break; } // Quantified enemy scaling: +5% health and +2% speed per wave var healthMultiplier = 1 + (waveNumber - 1) * 0.05; var speedMultiplier = 1 + (waveNumber - 1) * 0.02; var damageMultiplier = 1 + (waveNumber - 1) * 0.05; // Apply scaling with proper rounding enemy.health = Math.ceil(enemy.health * healthMultiplier); enemy.maxHealth = enemy.health; enemy.speed = enemy.speed * speedMultiplier; enemy.damage = Math.ceil(enemy.damage * damageMultiplier); // Extra scaling for mini-boss waves if (isMiniBossWave) { enemy.health = Math.ceil(enemy.health * 1.25); // 25% more health enemy.maxHealth = enemy.health; enemy.damage = Math.ceil(enemy.damage * 1.15); // 15% more damage } enemy.lastX = enemy.x; enemy.lastY = enemy.y; var dx = castle.x - enemy.x; var dy = castle.y - enemy.y; enemy.lastCastleDistanceSquared = dx * dx + dy * dy; enemies.push(enemy); game.addChild(enemy); } function spawnReward() { var rewardType = Math.random(); var reward; // 70% chance for regular reward, 30% chance for bomb reward if (rewardType < 0.7) { reward = new Reward(); rewards.push(reward); } else { reward = new BombReward(); bombRewards.push(reward); } // Spawn in accessible areas further away from the castle var angle = Math.random() * Math.PI * 2; var distance = 400 + Math.random() * 500; reward.x = castle.x + Math.cos(angle) * distance; reward.y = castle.y + Math.sin(angle) * distance; // Keep within bounds reward.x = Math.max(100, Math.min(1948, reward.x)); reward.y = Math.max(100, Math.min(2632, reward.y)); reward.baseY = reward.y; game.addChild(reward); // Add spawn animation with tween reward.scaleX = 0; reward.scaleY = 0; tween(reward, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); } // Castle movement system with configurable speed // Note: castleMovementSpeed is set above after applying movement speed bonus var targetX = 1024; var targetY = 1366; var isMoving = false; game.down = function (x, y, obj) { // Set new target position targetX = x; targetY = y; // Calculate distance to determine movement duration var dx = targetX - castle.x; var dy = targetY - castle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Calculate duration based on configurable movement speed var duration = Math.max(150, distance / castleMovementSpeed * 1000); // minimum 150ms for responsiveness // Stop any existing movement tween tween.stop(castle, { x: true, y: true }); isMoving = true; // Add walking animation with slight bobbing effect var originalY = castle.y; // Start smooth walking animation with subtle bounce tween(castle, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { isMoving = false; // Subtle landing effect tween(castle, { scaleX: 1.1, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(castle, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOut }); } }); } }); // Add walking bobbing animation during movement if (duration > 300) { var bobDuration = duration / 3; tween(castle, { scaleY: 1.05 }, { duration: bobDuration, easing: tween.easeInOut, onFinish: function onFinish() { tween(castle, { scaleY: 1 }, { duration: bobDuration, easing: tween.easeInOut }); } }); } }; game.move = function (x, y, obj) { // Update target while moving (allows for path correction) if (isMoving) { targetX = x; targetY = y; // Calculate new distance and duration var dx = targetX - castle.x; var dy = targetY - castle.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only update if the new target is significantly different if (distance > 40) { var duration = Math.max(150, distance / castleMovementSpeed * 1000); // Stop current tween and start new one tween.stop(castle, { x: true, y: true }); tween(castle, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { isMoving = false; } }); } } }; game.up = function (x, y, obj) { // Optional: Could add final position adjustment here if needed }; // Game update loop game.update = function () { // Update wave timer waveTimer++; // Spawn enemies enemySpawnTimer++; var spawnRate = Math.max(30, 120 - waveNumber * 5); // Faster spawning each wave // Increase spawn rate for mini-boss waves if (isMiniBossWave) { spawnRate = Math.max(20, spawnRate * 0.7); // 30% faster spawning in mini-boss waves } if (enemySpawnTimer >= spawnRate) { spawnEnemy(); enemySpawnTimer = 0; } // Spawn rewards occasionally - tied to enemy defeats for better balance rewardSpawnTimer++; var enemiesDefeated = Math.floor(LK.getScore() / 10); // Approximate enemies defeated based on score var rewardThreshold = 600 - Math.min(300, enemiesDefeated * 5); // Faster spawning as more enemies defeated if (rewardSpawnTimer >= rewardThreshold && rewards.length + bombRewards.length < 3) { // Spawn rate increases with progress, max 3 rewards total spawnReward(); rewardSpawnTimer = 0; } // Advance wave every 30 seconds if (waveTimer >= 1800) { // 30 seconds at 60fps waveNumber++; waveTimer = 0; miniBossSpawned = false; // Reset mini-boss spawn flag // Check if this is a mini-boss wave (every 5th wave) isMiniBossWave = waveNumber % 5 === 0; if (isMiniBossWave) { // Special visual effect for mini-boss waves LK.effects.flashScreen(0xFF4400, 1000); // Orange flash for mini-boss // Show wave announcement var waveText = new Text2('MINI-BOSS WAVE ' + waveNumber, { size: 80, fill: 0xFF4400 }); waveText.anchor.set(0.5, 0.5); waveText.x = 1024; waveText.y = 800; game.addChild(waveText); // Fade out wave text after 2 seconds tween(waveText, { alpha: 0 }, { duration: 2000, onFinish: function onFinish() { waveText.destroy(); } }); } else { // Normal wave flash LK.effects.flashScreen(0x0066FF, 500); } } // Check bullet-enemy collisions for (var b = bullets.length - 1; b >= 0; b--) { var bullet = bullets[b]; var bulletHit = false; // Early exit if bullet is off-screen if (bullet.x < -100 || bullet.x > 2148 || bullet.y < -100 || bullet.y > 2832) { bullet.destroy(); bullets.splice(b, 1); continue; } for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; if (bullet.intersects(enemy)) { enemy.takeDamage(bullet.damage); bullet.destroy(); bullets.splice(b, 1); bulletHit = true; break; } } if (bulletHit) continue; } // Check castle-reward collisions for pickup for (var r = rewards.length - 1; r >= 0; r--) { var reward = rewards[r]; if (castle.intersects(reward)) { // Define maximum bullet limit to prevent excessive stacking var maxBullets = 8; if (castle.bulletsPerShot < maxBullets) { // Add 1 bullet per reward (consistent increment) castle.bulletsPerShot++; bulletsText.setText('Bullets: ' + castle.bulletsPerShot); LK.getSound('award').play(); LK.effects.flashObject(castle, 0x00FF00, 500); // Visual feedback for bullet upgrade with scaling effect tween(castle, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(castle, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); } else { // At maximum bullets - give score bonus instead LK.setScore(LK.getScore() + 100); scoreText.setText(LK.getScore()); bulletsText.setText('Bullets: ' + castle.bulletsPerShot + ' (MAX)'); LK.getSound('award').play(); LK.effects.flashObject(castle, 0xFFD700, 500); // Gold flash for max bonus // Brief text notification var maxText = new Text2('+100 SCORE!', { size: 40, fill: 0xFFD700 }); maxText.anchor.set(0.5, 0.5); maxText.x = castle.x; maxText.y = castle.y - 60; game.addChild(maxText); // Animate and remove notification tween(maxText, { y: maxText.y - 50, alpha: 0 }, { duration: 1500, onFinish: function onFinish() { maxText.destroy(); } }); } reward.destroy(); rewards.splice(r, 1); } } // Check castle-bomb reward collisions for pickup for (var br = bombRewards.length - 1; br >= 0; br--) { var bombReward = bombRewards[br]; if (castle.intersects(bombReward)) { // Schedule bomb to appear at character position after 3 seconds var bombX = castle.x; var bombY = castle.y; LK.setTimeout(function () { var bomb = new Bomb(); bomb.x = bombX; bomb.y = bombY; bombs.push(bomb); game.addChild(bomb); }, 3000); LK.getSound('award').play(); LK.effects.flashObject(castle, 0xFF8800, 500); bombReward.destroy(); bombRewards.splice(br, 1); } } // Check castle-scrap metal collisions for pickup for (var cd = scrapMetalDrops.length - 1; cd >= 0; cd--) { var scrapMetalDrop = scrapMetalDrops[cd]; if (castle.intersects(scrapMetalDrop)) { currentScrapMetal += scrapMetalDrop.value; scrapMetalText.setText('Scrap Metal: ' + currentScrapMetal); LK.getSound('award').play(); LK.effects.flashObject(castle, 0xC0C0C0, 300); scrapMetalDrop.destroy(); scrapMetalDrops.splice(cd, 1); } } // Update UI healthText.setText('Health: ' + castle.health + '/' + castle.maxHealth); scoreText.setText(LK.getScore()); };
===================================================================
--- original.js
+++ change.js
@@ -1254,10 +1254,10 @@
// Apply permanent upgrades from storage
var fireRateBonus = (storage.fireRateLevel - 1) * 0.15; // 15% faster per level
castle.shootInterval = Math.max(20, Math.floor(castle.shootInterval * (1 - fireRateBonus)));
var healthBonus = (storage.healthLevel - 1) * 20; // +20 health per level
-castle.health += healthBonus;
-castle.maxHealth = castle.health;
+castle.maxHealth += healthBonus;
+castle.health = castle.maxHealth; // Start with full health
var damageBonus = (storage.damageLevel - 1) * 0.2; // +20% damage per level
var baseBulletDamage = 1.01;
// Store upgraded damage for bullets
castle.upgradedBulletDamage = baseBulletDamage * (1 + damageBonus);
@@ -1668,7 +1668,7 @@
scrapMetalDrops.splice(cd, 1);
}
}
// Update UI
- healthText.setText('Health: ' + castle.health);
+ healthText.setText('Health: ' + castle.health + '/' + castle.maxHealth);
scoreText.setText(LK.getScore());
};
\ No newline at end of file
square smiley face thick eyebrows. In-Game asset
pink stone round. In-Game asset
purple square crab. In-Game asset
round mouse face. In-Game asset
bright green fat star. In-Game asset
round red lava stone. In-Game asset
square yellow poisonous stone top view. In-Game asset
yellow flame ze poisonous bead top view. In-Game asset
grass top view. In-Game asset. 2d. High contrast. No shadows
bomb in green star