User prompt
Damage: Increases the base damage of each bullet.
User prompt
Fire Rate: Decreases the time between shots (e.g., from 1 second to 0.9, then 0.8, etc.).
User prompt
Resource Drops: When enemies are defeated, they have a chance to drop "Scrap Metal" (a new collectible resource). Different enemy types could drop varying amounts of Scrap Metal, with Bosses dropping significant quantities.
User prompt
Please fix the bug: 'playerData is not defined' in or related to this line: 'var fireRateBonus = (playerData.fireRateLevel - 1) * 0.15; // 15% faster per level' Line Number: 1300 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Score & Economy: "Score increases based on enemy type defeated" is good. We could also introduce a currency dropped by enemies that players can collect (like the power-ups) to purchase permanent upgrades for their castle (e.g., increased fire rate, more health, faster movement speed, or unlocking new targeting priorities). This would add a much-needed long-term progression system beyond just survival. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Enemy Scaling: Instead of just "stronger, faster," let's specify. For example, "Enemies gain +5% health and +2% speed per wave."
User prompt
Reward Spawning & Expiration: "Maximum 3 rewards can exist on screen at once" and "Rewards expire after 7 seconds if not collected" are great constraints. We can introduce a visual cue for expiring rewards (e.g., flashing, fading). The spawn rate of rewards should be balanced – perhaps tied to enemy defeats or time elapsed. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Green Bullets Reward (70% chance): This is a direct damage increase. Let's define the increment: does it add 1 more bullet, or double the current bullets? We could also introduce a duration for this power-up, or make it a permanent upgrade that stacks. If it stacks, there should be a maximum number of bullets the castle can fire simultaneously.
User prompt
Enemy Waves & Progression: The 30-second wave timer is a good start. We can make the wave progression more engaging by introducing "mini-boss" waves every few waves (e.g., every 5th or 10th wave) where a Boss Enemy or a high concentration of Tank/Shield enemies appears. This provides a clear milestone for players. The "stronger, faster enemies" can be quantified: perhaps enemies gain a certain percentage more health and speed with each new wave.
User prompt
Castle Movement: The ability to move the castle is key for strategic positioning. The "smooth walking animation" is a great visual cue. We should also define the movement speed of the castle and consider if it can be upgraded. This movement will be crucial for dodging, collecting power-ups, and escaping high-density enemy areas. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Auto-Fire Defense: The core remains that your castle automatically shoots at the closest enemy every second. To add a layer of depth, let's introduce a targeting priority system. Instead of always the closest, the castle could prioritize enemies based on danger level (e.g., Snipers or Bombers first, then Fast Enemies, then Basic). This could even be a toggle or upgrade the player unlocks.
User prompt
Optimize the game's running speed
User prompt
add nickname login screen before game start ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
When you pick up the bomb, make its radius small and when it explodes, make its radius larger. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
the killing radius of the bomb is **1000 pixels**
User prompt
Add another reward item, when it is taken, a bomb will appear where the character is after 3 seconds, and only clear the enemies in a certain radius. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
increase movement speed
User prompt
Apply realistic walking effect to character. Don't move like teleportation ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
double the size of the grid
User prompt
make the grid in the background random and make it look natural as it looks crooked
User prompt
creating a visual for the game background
User prompt
add award receiving sound effect
User prompt
create simple 8 bit exciting theme music
User prompt
If no reward is received, it will disappear after 7 seconds.
User prompt
If no reward is received, it will disappear after 2 seconds. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * 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; } // 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); 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; 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) { 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(); 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(); 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; } // 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 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 waveTimer = 0; var enemySpawnTimer = 0; var rewardSpawnTimer = 0; var waveNumber = 1; var isMiniBossWave = false; var miniBossSpawned = false; // 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); // Initialize castle castle = game.addChild(new Castle()); castle.x = 1024; castle.y = 1366; // 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: 15% health increase, 8% speed increase, 10% damage increase per wave var healthMultiplier = 1 + (waveNumber - 1) * 0.15; var speedMultiplier = 1 + (waveNumber - 1) * 0.08; var damageMultiplier = 1 + (waveNumber - 1) * 0.10; // 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); } // Castle movement system with configurable speed var castleMovementSpeed = 400; // pixels per second - can be upgraded later 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 rewardSpawnTimer++; if (rewardSpawnTimer >= 600 && rewards.length + bombRewards.length < 3) { // Every 10 seconds, 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); } } // Update UI healthText.setText('Health: ' + castle.health); scoreText.setText(LK.getScore()); };
===================================================================
--- original.js
+++ change.js
@@ -641,12 +641,60 @@
});
}
};
self.down = function (x, y, obj) {
- castle.bulletsPerShot++;
- bulletsText.setText('Bullets: ' + castle.bulletsPerShot);
- LK.getSound('award').play();
- LK.effects.flashObject(castle, 0x00FF00, 500);
+ // 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);
@@ -1361,12 +1409,60 @@
// Check castle-reward collisions for pickup
for (var r = rewards.length - 1; r >= 0; r--) {
var reward = rewards[r];
if (castle.intersects(reward)) {
- castle.bulletsPerShot++;
- bulletsText.setText('Bullets: ' + castle.bulletsPerShot);
- LK.getSound('award').play();
- LK.effects.flashObject(castle, 0x00FF00, 500);
+ // 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);
}
}
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