User prompt
The Game Over screen should appear at the bottom part of the screen. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'health')' in or related to this line: 'var healthPercent = player.health / player.maxHealth;' Line Number: 1531
User prompt
When the main character dies and the explosion occurs, the character should be removed from the scene. At the top of the Game Over screen, display the last level reached before dying. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var localPos = gameOverContainer.toLocal(obj.parent.toGlobal(obj.position));' Line Number: 914
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'health')' in or related to this line: 'var healthPercent = player.health / player.maxHealth;' Line Number: 1553
User prompt
The "Play Again" text on the Game Over screen should be a clickable button.
User prompt
When the "Play Again" text on the game over screen is clicked, the game should restart from the very beginning.
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var localPos = gameOverContainer.toLocal(obj.parent.toGlobal(obj.position));' Line Number: 914
User prompt
The "Play Again" button should be clickable, and when clicked, the game should restart completely from level 1.
User prompt
When the "Play Again" button is clicked, the game should restart from the beginning.
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var localPos = gameOverContainer.toLocal(obj.parent.toGlobal(obj.position));' Line Number: 914
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'health')' in or related to this line: 'var healthPercent = player.health / player.maxHealth;' Line Number: 1553
User prompt
When the main character dies, trigger a smaller nova explosion that occurs only once. After the explosion, the character should be removed from the scene. Then, after 2 seconds, the Game Over screen should appear. On that screen, display "Game Over" at the top, show the last level reached before dying underneath, and below that, display a "Play Again" button. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the explosion effect after enemy death less noticeable. When the main character dies, trigger a nova explosion. After 3 seconds, the Game Over screen should appear, and below "Game Over," display the last level the player reached. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Reduce the explosion effect for regular enemies to make it more subtle. When the main character dies, trigger a large nova-style explosion. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
The explosion should be more noticeable and look more like a death animation. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When enemies die, there should be a small explosion. When bosses die, there should be a large explosion. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Each time I collect a coin, its current value should appear above it in green text with a plus sign (e.g., +2) and then disappear. The same should apply to diamonds — their value should also briefly appear above them in green (e.g., +6) when collected. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
The boss should shoot a bit more slowly. The boss’s health should be displayed as a health bar, just like the enemies. The boss’s health value should be three times the total health of the enemies defeated in the previous level. Every 10 levels, the boss’s attack power should increase by a certain percentage.
User prompt
When the shop screen opens, only the coins and diamonds on the ground at that moment should be attracted toward the main character. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When the game is over and restarted, reset all shop upgrade costs back to zero. The shop upgrade base costs should always start at 15 coins for each new game.
User prompt
During boss rounds, there should be no regular enemies—only the boss. All shop upgrade base costs should start at 15 coins. Every 5 levels, the value of coins increases by +2. Every 5 levels, the number of enemies increases by +2.
User prompt
Each time the shop screen opens and closes, the character’s HP should be fully restored. Make the shop screen texts darker, bold, and slightly larger to improve readability.
User prompt
Place the “Shop” title higher up on the shop screen, and move the “Close” button further to the right.
User prompt
Each time I upgrade a stat from the shop, the value shown in the top-right corner should display the increase in parentheses. For example, if the base attack is 5 and I upgrade it after level 5, it should appear as: Attack: 5 (+?) — where +? represents the total amount gained through upgrades.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
// Calculate total health of enemies from previous level (currentLevel - 1)
var previousLevel = Math.max(1, currentLevel - 1);
var enemyMaxHealth = 40 + (previousLevel - 1) * 8;
var enemiesInPreviousLevel = Math.floor(enemiesPerLevel * (1 + Math.floor((previousLevel - 1) / 5) * 0.4));
var totalEnemyHealth = enemyMaxHealth * enemiesInPreviousLevel;
self.maxHealth = totalEnemyHealth * 3;
self.health = self.maxHealth;
self.speed = 0.8 + (currentLevel - 10) * 0.1;
var baseDamage = 30;
var levelsSinceBoss1 = Math.floor((currentLevel - 10) / 10);
var damageMultiplier = 1 + levelsSinceBoss1 * 0.25; // 25% increase per 10 levels
self.damage = Math.floor(baseDamage * damageMultiplier);
self.lastAttack = 0;
// Create boss health bar background
self.healthBarBg = LK.getAsset('enemyHealthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 30
});
self.healthBarBg.y = -80;
self.addChild(self.healthBarBg);
// Create boss health bar fill
self.healthBarFill = LK.getAsset('enemyHealthBarFill', {
anchorX: 0,
anchorY: 0.5,
width: 196,
height: 26
});
self.healthBarFill.x = -98;
self.healthBarFill.y = -80;
self.addChild(self.healthBarFill);
self.attackCooldown = 120;
self.update = function () {
if (gameState === 'playing' && player) {
// Move towards player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Attack player on physical contact only
self.lastAttack++;
if (self.lastAttack >= self.attackCooldown) {
// Shoot bullet at player
var bullet = new BossBullet();
bullet.x = self.x;
bullet.y = self.y;
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.velocityX = dx / distance * 6;
bullet.velocityY = dy / distance * 6;
}
bossBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.lastAttack = 0;
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health < 0) self.health = 0;
// Update boss health bar
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 196 * healthPercent;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Create massive explosion effect with multiple waves of particles
for (var e = 0; e < 8; e++) {
var explosionCircle = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5,
width: 60 + e * 25,
height: 60 + e * 25,
alpha: 1.0 - e * 0.1,
tint: e === 0 ? 0xFF0000 : e === 1 ? 0xFF2200 : e === 2 ? 0xFF4500 : e === 3 ? 0xFF6600 : e === 4 ? 0xFF8800 : e === 5 ? 0xFFAA00 : e === 6 ? 0xFFCC00 : 0xFFFF00
});
explosionCircle.x = self.x + (Math.random() - 0.5) * 80;
explosionCircle.y = self.y + (Math.random() - 0.5) * 80;
game.addChild(explosionCircle);
// Animate explosion with dramatic scaling and movement
tween(explosionCircle, {
scaleX: 5 + e * 0.8,
scaleY: 5 + e * 0.8,
alpha: 0,
x: explosionCircle.x + (Math.random() - 0.5) * 200,
y: explosionCircle.y + (Math.random() - 0.5) * 200
}, {
duration: 800 + e * 150,
easing: e % 3 === 0 ? tween.easeOut : e % 3 === 1 ? tween.easeInOut : tween.bounceOut,
onFinish: function onFinish() {
explosionCircle.destroy();
}
});
}
// Drop multiple coins with value based on current level
for (var i = 0; i < 5; i++) {
var coin = new Coin();
coin.value = 2 + Math.floor((currentLevel - 1) / 5) * 2; // Boss coins use same formula as regular enemies
coin.x = self.x + (Math.random() - 0.5) * 100;
coin.y = self.y + (Math.random() - 0.5) * 100;
coins.push(coin);
game.addChild(coin);
}
// Drop diamond with 50% chance from boss
if (Math.random() < 0.50) {
var diamond = new Diamond();
diamond.value = (2 + Math.floor((currentLevel - 1) / 5) * 2) * 5; // Boss diamonds worth 5x current coin value
diamond.x = self.x + (Math.random() - 0.5) * 100;
diamond.y = self.y + (Math.random() - 0.5) * 100;
diamonds.push(diamond);
game.addChild(diamond);
}
currentBoss = null;
self.destroy();
// Show body part selection
showBodyPartSelection();
};
return self;
});
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 15;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.removeFromGame();
}
// Check collision with player using precise distance calculation
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collisionDistance = (player.width + self.width) / 8; // Tight collision bounds
if (distance <= collisionDistance) {
player.takeDamage(self.damage);
self.removeFromGame();
}
}
};
self.removeFromGame = function () {
for (var i = 0; i < bossBullets.length; i++) {
if (bossBullets[i] === self) {
bossBullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 10;
self.creationTime = LK.ticks; // Track when coin was created
self.expirationStarted = false;
// Start continuous Y-axis rotation
self.startRotation = function () {
tween(graphics, {
scaleX: -1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphics, {
scaleX: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.expirationStarted) {
self.startRotation();
}
}
});
}
});
};
self.startRotation();
self.update = function () {
// Check for expiration after 30 seconds (30 * 60 = 1800 ticks at 60 FPS)
if (!self.expirationStarted && LK.ticks - self.creationTime >= 1800) {
self.expirationStarted = true;
// Start fade out animation over 1 second
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
// Remove from coins array
for (var i = 0; i < coins.length; i++) {
if (coins[i] === self) {
coins.splice(i, 1);
break;
}
}
self.destroy();
}
});
}
if (player) {
// Check if coin is within player's attack range for magnetism
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collectionDistance = (player.width + self.width) / 4; // Tight collision bounds for collection
// Only collect on precise physical contact using distance calculation
if (distance <= collectionDistance) {
playerCoins += self.value;
coinText.setText('Coins: ' + playerCoins);
LK.getSound('coinCollect').play();
// Create floating value text
var valueText = new Text2('+' + self.value, {
size: 45,
fill: 0x00ff00
});
valueText.anchor.set(0.5, 0.5);
valueText.x = self.x;
valueText.y = self.y;
game.addChild(valueText);
// Animate the value text floating up and fading out
tween(valueText, {
y: valueText.y - 80,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
valueText.destroy();
}
});
// Remove from coins array
for (var i = 0; i < coins.length; i++) {
if (coins[i] === self) {
coins.splice(i, 1);
break;
}
}
self.destroy();
} else if (distance <= player.attackRange && distance > collectionDistance) {
// Coin is within attack range but not collected yet - apply magnetism
if (!self.magnetismActive) {
self.magnetismActive = true;
// Stop any existing movement tweens
tween.stop(self, {
x: true,
y: true
});
}
// Calculate target position slightly closer to player
var pullStrength = 0.04; // How strong the pull effect is (reduced from 0.08)
var targetX = self.x + dx * pullStrength;
var targetY = self.y + dy * pullStrength;
// Smoothly move toward player using tween
tween(self, {
x: targetX,
y: targetY
}, {
duration: 400,
easing: tween.easeOut
});
} else if (distance > player.attackRange) {
// Coin is outside attack range - stop magnetism
if (self.magnetismActive) {
self.magnetismActive = false;
tween.stop(self, {
x: true,
y: true
});
}
}
}
};
return self;
});
var Diamond = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('diamond', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 10;
self.creationTime = LK.ticks;
self.expirationStarted = false;
// Start continuous Y-axis rotation
self.startRotation = function () {
tween(graphics, {
scaleX: -1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphics, {
scaleX: 1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.expirationStarted) {
self.startRotation();
}
}
});
}
});
};
self.startRotation();
self.update = function () {
// Check for expiration after 45 seconds
if (!self.expirationStarted && LK.ticks - self.creationTime >= 2700) {
self.expirationStarted = true;
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
for (var i = 0; i < diamonds.length; i++) {
if (diamonds[i] === self) {
diamonds.splice(i, 1);
break;
}
}
self.destroy();
}
});
}
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collectionDistance = (player.width + self.width) / 4;
if (distance <= collectionDistance) {
playerCoins += self.value;
coinText.setText('Coins: ' + playerCoins);
LK.getSound('coinCollect').play();
// Create floating value text
var valueText = new Text2('+' + self.value, {
size: 45,
fill: 0x00ff00
});
valueText.anchor.set(0.5, 0.5);
valueText.x = self.x;
valueText.y = self.y;
game.addChild(valueText);
// Animate the value text floating up and fading out
tween(valueText, {
y: valueText.y - 80,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
valueText.destroy();
}
});
for (var i = 0; i < diamonds.length; i++) {
if (diamonds[i] === self) {
diamonds.splice(i, 1);
break;
}
}
self.destroy();
} else if (distance <= player.attackRange && distance > collectionDistance) {
if (!self.magnetismActive) {
self.magnetismActive = true;
tween.stop(self, {
x: true,
y: true
});
}
var pullStrength = 0.04;
var targetX = self.x + dx * pullStrength;
var targetY = self.y + dy * pullStrength;
tween(self, {
x: targetX,
y: targetY
}, {
duration: 400,
easing: tween.easeOut
});
} else if (distance > player.attackRange) {
if (self.magnetismActive) {
self.magnetismActive = false;
tween.stop(self, {
x: true,
y: true
});
}
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 40 + (currentLevel - 1) * 8;
self.health = self.maxHealth;
self.speed = 1.5 + (currentLevel - 1) * 0.15;
self.damage = 10 * (1 + (currentLevel - 1) * 0.1);
self.lastAttack = 0;
self.attackCooldown = 120;
// Create enemy health bar background (simple rectangle)
self.healthBarBg = LK.getAsset('enemyHealthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
self.healthBarBg.y = -70;
self.addChild(self.healthBarBg);
// Create enemy health bar fill (simple rectangle)
self.healthBarFill = LK.getAsset('enemyHealthBarFill', {
anchorX: 0,
anchorY: 0.5
});
self.healthBarFill.x = -59;
self.healthBarFill.y = -70;
self.addChild(self.healthBarFill);
self.update = function () {
if (gameState === 'playing' && player) {
// Move towards player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
// Check collision with other enemies
var canMove = true;
var collisionAngle = 0;
for (var e = 0; e < enemies.length; e++) {
var otherEnemy = enemies[e];
if (otherEnemy !== self) {
var otherDx = newX - otherEnemy.x;
var otherDy = newY - otherEnemy.y;
var otherDistance = Math.sqrt(otherDx * otherDx + otherDy * otherDy);
if (otherDistance < 80) {
// Minimum distance between enemies
canMove = false;
// Calculate avoidance angle
collisionAngle = Math.atan2(otherDy, otherDx);
break;
}
}
}
if (canMove) {
self.x = newX;
self.y = newY;
} else {
// Apply collision avoidance - move perpendicular to collision
var avoidanceAngle1 = collisionAngle + Math.PI / 2;
var avoidanceAngle2 = collisionAngle - Math.PI / 2;
// Try both perpendicular directions
var avoidX1 = self.x + Math.cos(avoidanceAngle1) * self.speed;
var avoidY1 = self.y + Math.sin(avoidanceAngle1) * self.speed;
var avoidX2 = self.x + Math.cos(avoidanceAngle2) * self.speed;
var avoidY2 = self.y + Math.sin(avoidanceAngle2) * self.speed;
// Check which avoidance direction is clearer
var canAvoid1 = true;
var canAvoid2 = true;
for (var e = 0; e < enemies.length; e++) {
var otherEnemy = enemies[e];
if (otherEnemy !== self) {
var dist1 = Math.sqrt((avoidX1 - otherEnemy.x) * (avoidX1 - otherEnemy.x) + (avoidY1 - otherEnemy.y) * (avoidY1 - otherEnemy.y));
var dist2 = Math.sqrt((avoidX2 - otherEnemy.x) * (avoidX2 - otherEnemy.x) + (avoidY2 - otherEnemy.y) * (avoidY2 - otherEnemy.y));
if (dist1 < 70) canAvoid1 = false;
if (dist2 < 70) canAvoid2 = false;
}
}
// Move in the clearest direction
if (canAvoid1) {
self.x = avoidX1;
self.y = avoidY1;
} else if (canAvoid2) {
self.x = avoidX2;
self.y = avoidY2;
} else {
// If both directions blocked, move slightly away from collision
self.x += Math.cos(collisionAngle) * (self.speed * 0.5);
self.y += Math.sin(collisionAngle) * (self.speed * 0.5);
}
}
}
// Attack player on physical contact only
self.lastAttack++;
if (self.lastAttack >= self.attackCooldown) {
// Only damage on direct collision using precise distance calculation
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collisionDistance = (player.width + self.width) / 8; // Much tighter collision bounds
if (distance <= collisionDistance) {
player.takeDamage(self.damage);
self.lastAttack = 0;
// Push back enemy by a small amount
var pushDistance = 30; // Small pushback distance
var pushbackX = self.x - dx / distance * pushDistance;
var pushbackY = self.y - dy / distance * pushDistance;
// Use tween to animate the pushback
tween(self, {
x: pushbackX,
y: pushbackY
}, {
duration: 150,
easing: tween.easeOut
});
}
}
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health < 0) self.health = 0;
// Update enemy health bar
var healthPercent = self.health / self.maxHealth;
self.healthBarFill.width = 118 * healthPercent;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Create subtle explosion effect with fewer particles
for (var e = 0; e < 3; e++) {
var explosionCircle = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5,
width: 20 + e * 8,
height: 20 + e * 8,
alpha: 0.6 - e * 0.15,
tint: e === 0 ? 0xFF4500 : e === 1 ? 0xFF6600 : 0xFF8800
});
explosionCircle.x = self.x + (Math.random() - 0.5) * 30;
explosionCircle.y = self.y + (Math.random() - 0.5) * 30;
game.addChild(explosionCircle);
// Animate explosion with subtle scaling and movement
tween(explosionCircle, {
scaleX: 1.5 + e * 0.3,
scaleY: 1.5 + e * 0.3,
alpha: 0,
x: explosionCircle.x + (Math.random() - 0.5) * 40,
y: explosionCircle.y + (Math.random() - 0.5) * 40
}, {
duration: 300 + e * 50,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionCircle.destroy();
}
});
}
// Calculate coin value - increases by +2 every 5 levels
var baseValue = 2 + Math.floor((currentLevel - 1) / 5) * 2;
var coinValue = baseValue;
// Drop coin with doubled value every 10 levels
var coin = new Coin();
coin.value = coinValue;
coin.x = self.x;
coin.y = self.y;
coins.push(coin);
game.addChild(coin);
// Rarely drop diamond (1% chance)
if (Math.random() < 0.01) {
var diamond = new Diamond();
diamond.value = coinValue * 3; // 3x current coin value
diamond.x = self.x + (Math.random() - 0.5) * 60;
diamond.y = self.y + (Math.random() - 0.5) * 60;
diamonds.push(diamond);
game.addChild(diamond);
}
// Rarely drop health pack (10% chance)
if (Math.random() < 0.10) {
var healthPack = new HealthPack();
healthPack.x = self.x + (Math.random() - 0.5) * 60;
healthPack.y = self.y + (Math.random() - 0.5) * 60;
healthPacks.push(healthPack);
game.addChild(healthPack);
}
// Remove from enemies array
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
enemiesKilled++;
remainingEnemies--;
};
return self;
});
var HealthPack = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('healthPack', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 0.15; // 15% of max health
self.creationTime = LK.ticks; // Track when health pack was created
self.expirationStarted = false;
// Start continuous Y-axis rotation
self.startRotation = function () {
tween(graphics, {
scaleX: -1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(graphics, {
scaleX: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.expirationStarted) {
self.startRotation();
}
}
});
}
});
};
self.startRotation();
self.update = function () {
// Check for expiration after 1 minute (60 * 60 = 3600 ticks at 60 FPS)
if (!self.expirationStarted && LK.ticks - self.creationTime >= 3600) {
self.expirationStarted = true;
// Start fade out animation over 1 second
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
// Remove from healthPacks array
for (var i = 0; i < healthPacks.length; i++) {
if (healthPacks[i] === self) {
healthPacks.splice(i, 1);
break;
}
}
self.destroy();
}
});
}
if (player && player.health < player.maxHealth) {
// Only collect on precise physical contact using distance calculation
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collectionDistance = (player.width + self.width) / 4; // Tight collision bounds for collection
if (distance <= collectionDistance) {
var healValue = Math.floor(player.maxHealth * self.healAmount);
player.health = Math.min(player.maxHealth, player.health + healValue);
LK.getSound('coinCollect').play();
// Remove from healthPacks array
for (var i = 0; i < healthPacks.length; i++) {
if (healthPacks[i] === self) {
healthPacks.splice(i, 1);
break;
}
}
self.destroy();
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Add attack range visual
var rangeGraphics = self.attachAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
width: 600,
// 300 radius * 2
height: 600 // 300 radius * 2
});
self.maxHealth = 30;
self.health = self.maxHealth;
self.speed = 2.0;
self.damage = 5;
self.attackSpeed = 120;
self.attackRange = 300; // Base attack range radius
self.lastShot = 0;
self.update = function () {
if (gameState === 'playing') {
// Move towards touch position if touching
if (touchPosition && (Math.abs(self.x - touchPosition.x) > 5 || Math.abs(self.y - touchPosition.y) > 5)) {
var dx = touchPosition.x - self.x;
var dy = touchPosition.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Make player face movement direction
if (dx > 0) {
graphics.scaleX = -Math.abs(graphics.scaleX); // Face right
} else if (dx < 0) {
graphics.scaleX = Math.abs(graphics.scaleX); // Face left
}
}
}
// Auto-shoot when enemies are in range
self.lastShot++;
var target = self.findNearestEnemy();
if (self.lastShot >= self.attackSpeed && target) {
self.shoot();
self.lastShot = 0;
}
}
};
self.shoot = function () {
var target = self.findNearestEnemy();
if (target) {
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y;
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = dx / distance * 8;
bullet.velocityY = dy / distance * 8;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
};
self.findNearestEnemy = function () {
if (currentBoss) {
var dx = currentBoss.x - self.x;
var dy = currentBoss.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange) return currentBoss;
}
var nearest = null;
var nearestDistance = Infinity;
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);
if (distance <= self.attackRange && distance < nearestDistance) {
nearestDistance = distance;
nearest = enemy;
}
}
return nearest;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health < 0) self.health = 0;
// Health bar now handled in GUI
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
// Create massive nova-style explosion when player dies
for (var e = 0; e < 12; e++) {
var explosionCircle = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5,
width: 80 + e * 40,
height: 80 + e * 40,
alpha: 1.0 - e * 0.08,
tint: e < 4 ? 0xFFFFFF : e < 8 ? 0xFFFF00 : 0xFF6600
});
explosionCircle.x = self.x + (Math.random() - 0.5) * 120;
explosionCircle.y = self.y + (Math.random() - 0.5) * 120;
game.addChild(explosionCircle);
// Animate massive explosion with dramatic nova effect
tween(explosionCircle, {
scaleX: 8 + e * 1.2,
scaleY: 8 + e * 1.2,
alpha: 0,
x: explosionCircle.x + (Math.random() - 0.5) * 400,
y: explosionCircle.y + (Math.random() - 0.5) * 400
}, {
duration: 1200 + e * 200,
easing: e % 3 === 0 ? tween.easeOut : e % 3 === 1 ? tween.easeInOut : tween.bounceOut,
onFinish: function onFinish() {
explosionCircle.destroy();
}
});
}
LK.showGameOver();
}
};
return self;
});
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 20;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.removeFromGame();
}
// Check collision with enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (self.intersects(enemy)) {
enemy.takeDamage(self.damage + player.damage);
LK.getSound('enemyHit').play();
self.removeFromGame();
return;
}
}
// Check collision with boss
if (currentBoss && self.intersects(currentBoss)) {
currentBoss.takeDamage(self.damage + player.damage);
LK.getSound('enemyHit').play();
self.removeFromGame();
}
};
self.removeFromGame = function () {
for (var i = 0; i < playerBullets.length; i++) {
if (playerBullets[i] === self) {
playerBullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Game variables
var gameState = 'playing'; // 'playing', 'shop', 'bodyPartSelection'
var currentLevel = 1;
var enemiesKilled = 0;
var enemiesPerLevel = 5;
var playerCoins = 0;
var touchPosition = null;
// Player upgrade levels and costs - reset to zero for each new game
var healthUpgradeLevel = 0;
var attackSpeedUpgradeLevel = 0;
var attackPowerUpgradeLevel = 0;
var movementSpeedUpgradeLevel = 0;
var attackRangeUpgradeLevel = 0;
// Game objects
var player = null;
var enemies = [];
var playerBullets = [];
var bossBullets = [];
var coins = [];
var diamonds = [];
var healthPacks = [];
var currentBoss = null;
var remainingEnemies = 0;
// UI elements
var levelText = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
levelText.x = 50;
levelText.y = 50;
LK.gui.topLeft.addChild(levelText);
var remainingText = new Text2('Enemies: 0', {
size: 45,
fill: 0xFFFFFF
});
remainingText.anchor.set(0, 0);
remainingText.x = 50;
remainingText.y = 120;
LK.gui.topLeft.addChild(remainingText);
// Create player health bar background (black square) at top of screen
var playerHealthBarBg = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0,
width: 320,
height: 55,
tint: 0x000000
});
playerHealthBarBg.x = 0;
playerHealthBarBg.y = 50;
LK.gui.top.addChild(playerHealthBarBg);
// Create player health bar fill (red square that fades to white)
var playerHealthBarFill = LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0,
width: 316,
height: 51,
tint: 0xff0000
});
playerHealthBarFill.x = -158;
playerHealthBarFill.y = 52;
LK.gui.top.addChild(playerHealthBarFill);
// Create player health text (white text)
var playerHealthBarText = new Text2('30', {
size: 38,
fill: 0xffffff
});
playerHealthBarText.anchor.set(0.5, 0.5);
playerHealthBarText.x = 0;
playerHealthBarText.y = 77;
LK.gui.top.addChild(playerHealthBarText);
var coinText = new Text2('Coins: 0', {
size: 40,
fill: 0xF1C40F
});
coinText.anchor.set(1, 0);
coinText.x = -50;
coinText.y = 50;
LK.gui.topRight.addChild(coinText);
// Stats display in top-right corner
var statsContainer = new Container();
var healthStatText = new Text2('Health: 30', {
size: 35,
fill: 0xE74C3C
});
healthStatText.anchor.set(1, 0);
healthStatText.x = -50;
healthStatText.y = 100;
var attackPowerStatText = new Text2('Attack: 5', {
size: 35,
fill: 0xFF6B6B
});
attackPowerStatText.anchor.set(1, 0);
attackPowerStatText.x = -50;
attackPowerStatText.y = 140;
var attackSpeedStatText = new Text2('A.Speed: 120', {
size: 35,
fill: 0x4ECDC4
});
attackSpeedStatText.anchor.set(1, 0);
attackSpeedStatText.x = -50;
attackSpeedStatText.y = 180;
var movementSpeedStatText = new Text2('M.Speed: 2.0', {
size: 35,
fill: 0x45B7D1
});
movementSpeedStatText.anchor.set(1, 0);
movementSpeedStatText.x = -50;
movementSpeedStatText.y = 220;
var attackRangeStatText = new Text2('Range: 600', {
size: 35,
fill: 0x9B59B6
});
attackRangeStatText.anchor.set(1, 0);
attackRangeStatText.x = -50;
attackRangeStatText.y = 260;
LK.gui.topRight.addChild(healthStatText);
LK.gui.topRight.addChild(attackPowerStatText);
LK.gui.topRight.addChild(attackSpeedStatText);
LK.gui.topRight.addChild(movementSpeedStatText);
LK.gui.topRight.addChild(attackRangeStatText);
// Shop UI (hidden initially)
var shopContainer = new Container();
var shopBackground = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 6,
x: 0,
y: 0
});
shopContainer.addChild(shopBackground);
var shopTitle = new Text2('SHOP', {
size: 120,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 0;
shopTitle.y = -400;
shopContainer.addChild(shopTitle);
var upgradeButtons = [];
var upgradeLabels = [];
var upgradePlusButtons = [];
var upgradeCostLabels = [];
var upgradeNames = ['Health +15', 'Attack Speed +', 'Attack Power +8', 'Move Speed +0.8', 'Range +20'];
for (var i = 0; i < 5; i++) {
var buttonBg = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: -200,
y: -220 + i * 130
});
shopContainer.addChild(buttonBg);
upgradeButtons.push(buttonBg);
var label = new Text2(upgradeNames[i], {
size: 30,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
label.anchor.set(0.5, 0.5);
label.x = -200;
label.y = -220 + i * 130;
shopContainer.addChild(label);
upgradeLabels.push(label);
// Plus button for each upgrade
var plusButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
x: 100,
y: -220 + i * 130,
tint: 0xff0000
});
shopContainer.addChild(plusButton);
upgradePlusButtons.push(plusButton);
// Plus text
var plusText = new Text2('+', {
size: 40,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
plusText.anchor.set(0.5, 0.5);
plusText.x = 100;
plusText.y = -220 + i * 130;
shopContainer.addChild(plusText);
// Cost label
var costLabel = new Text2('Cost: 0', {
size: 28,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
costLabel.anchor.set(0.5, 0.5);
costLabel.x = 280;
costLabel.y = -220 + i * 130;
shopContainer.addChild(costLabel);
upgradeCostLabels.push(costLabel);
}
var closeShopButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 500,
y: -250,
scaleX: 0.7,
scaleY: 0.7
});
shopContainer.addChild(closeShopButton);
var closeShopText = new Text2('CLOSE', {
size: 40,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
closeShopText.anchor.set(0.5, 0.5);
closeShopText.x = 500;
closeShopText.y = -250;
shopContainer.addChild(closeShopText);
shopContainer.x = 1024;
shopContainer.y = 1366;
shopContainer.visible = false;
// Body part selection UI (hidden initially)
var bodyPartContainer = new Container();
var bodyPartBackground = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 8,
x: 0,
y: 0
});
bodyPartContainer.addChild(bodyPartBackground);
var bodyPartTitle = new Text2('CHOOSE BODY PART', {
size: 70,
fill: 0xFFFFFF
});
bodyPartTitle.anchor.set(0.5, 0.5);
bodyPartTitle.x = 0;
bodyPartTitle.y = -250;
bodyPartContainer.addChild(bodyPartTitle);
var bodyPartButtons = [];
for (var i = 0; i < 3; i++) {
var partButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -100 + i * 120
});
bodyPartContainer.addChild(partButton);
bodyPartButtons.push(partButton);
}
bodyPartContainer.x = 1024;
bodyPartContainer.y = 1366;
bodyPartContainer.visible = false;
game.addChild(shopContainer);
game.addChild(bodyPartContainer);
// Initialize player
player = new Player();
player.x = 1024;
player.y = 1366;
game.addChild(player);
// Spawn initial enemies
function spawnEnemies() {
// No enemies during boss rounds (every 10 levels)
if (currentLevel % 10 === 0) {
return;
}
var enemiesToSpawn = Math.floor(enemiesPerLevel * (1 + Math.floor((currentLevel - 1) / 5) * 0.4));
remainingEnemies = enemiesToSpawn;
remainingText.setText('Enemies: ' + remainingEnemies);
for (var i = 0; i < enemiesToSpawn; i++) {
var enemy = new Enemy();
var validPosition = false;
var attempts = 0;
// Try to find non-overlapping position
while (!validPosition && attempts < 50) {
// Spawn from edges
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2782;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
break;
}
// Check if position overlaps with existing enemies
validPosition = true;
for (var j = 0; j < enemies.length; j++) {
var dx = enemy.x - enemies[j].x;
var dy = enemy.y - enemies[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 120) {
// Minimum distance between enemies
validPosition = false;
break;
}
}
attempts++;
}
enemies.push(enemy);
game.addChild(enemy);
}
}
function spawnBoss() {
currentBoss = new Boss();
currentBoss.x = 1024;
currentBoss.y = 200;
game.addChild(currentBoss);
}
function showShop() {
gameState = 'shop';
shopContainer.visible = true;
updateShopDisplay();
// Restore player health to full when entering shop
if (player) {
player.health = player.maxHealth;
}
// Attract all coins and diamonds currently on the ground toward player
for (var i = 0; i < coins.length; i++) {
var coin = coins[i];
// Stop any existing movement tweens
tween.stop(coin, {
x: true,
y: true
});
// Calculate duration based on distance to player
var dx = player.x - coin.x;
var dy = player.y - coin.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var duration = Math.min(1000, distance * 2); // Max 1 second, scale with distance
// Tween coin toward player
tween(coin, {
x: player.x,
y: player.y
}, {
duration: duration,
easing: tween.easeInOut
});
}
for (var i = 0; i < diamonds.length; i++) {
var diamond = diamonds[i];
// Stop any existing movement tweens
tween.stop(diamond, {
x: true,
y: true
});
// Calculate duration based on distance to player
var dx = player.x - diamond.x;
var dy = player.y - diamond.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var duration = Math.min(1000, distance * 2); // Max 1 second, scale with distance
// Tween diamond toward player
tween(diamond, {
x: player.x,
y: player.y
}, {
duration: duration,
easing: tween.easeInOut
});
}
// Hide player if within shop area to prevent overlap
if (player) {
player.visible = false;
}
// Pause all game objects
for (var i = 0; i < enemies.length; i++) {
enemies[i].visible = false;
}
}
function updateShopDisplay() {
var baseCosts = [15, 15, 15, 15, 15];
var upgradeLevels = [healthUpgradeLevel, attackSpeedUpgradeLevel, attackPowerUpgradeLevel, movementSpeedUpgradeLevel, attackRangeUpgradeLevel];
var anyAffordable = false;
for (var i = 0; i < 5; i++) {
var cost = Math.floor(baseCosts[i] * Math.pow(2.5, upgradeLevels[i]));
upgradeCostLabels[i].setText('Cost: ' + cost);
// Enable/disable plus button based on affordability
if (playerCoins >= cost) {
upgradePlusButtons[i].visible = true;
upgradePlusButtons[i].alpha = 1.0;
anyAffordable = true;
} else {
upgradePlusButtons[i].visible = true;
upgradePlusButtons[i].alpha = 0.3;
}
}
// Show/hide close button based on affordability
if (!anyAffordable) {
closeShopButton.visible = true;
closeShopText.visible = true;
} else {
closeShopButton.visible = true;
closeShopText.visible = true;
}
}
function hideShop() {
gameState = 'playing';
shopContainer.visible = false;
// Show player again
if (player) {
player.visible = true;
}
// Resume all game objects
for (var i = 0; i < enemies.length; i++) {
enemies[i].visible = true;
}
// Check if we need to spawn boss after shop
if (currentLevel % 10 === 0) {
spawnBoss();
}
}
function showBodyPartSelection() {
gameState = 'bodyPartSelection';
bodyPartContainer.visible = true;
}
function hideBodyPartSelection() {
gameState = 'playing';
bodyPartContainer.visible = false;
// Level progression is now handled in game.update
enemiesKilled = 0;
spawnEnemies();
}
// Event handlers
game.down = function (x, y, obj) {
touchPosition = {
x: x,
y: y
};
if (gameState === 'shop') {
// Convert screen coordinates to shop container coordinates
var shopPos = {
x: x - shopContainer.x,
y: y - shopContainer.y
};
// Check plus buttons
for (var i = 0; i < upgradePlusButtons.length; i++) {
var plusButton = upgradePlusButtons[i];
if (plusButton.visible && Math.abs(shopPos.x - plusButton.x) < 80 && Math.abs(shopPos.y - plusButton.y) < 40) {
var baseCosts = [15, 15, 15, 15, 15];
var upgradeLevels = [healthUpgradeLevel, attackSpeedUpgradeLevel, attackPowerUpgradeLevel, movementSpeedUpgradeLevel, attackRangeUpgradeLevel];
var cost = Math.floor(baseCosts[i] * Math.pow(2.5, upgradeLevels[i]));
if (playerCoins >= cost) {
purchaseUpgrade(i);
updateShopDisplay();
}
return;
}
}
// Check close button
if (Math.abs(shopPos.x - closeShopButton.x) < 120 && Math.abs(shopPos.y - closeShopButton.y) < 50) {
hideShop();
spawnEnemies();
}
}
if (gameState === 'bodyPartSelection') {
// Convert screen coordinates to body part container coordinates
var bodyPos = {
x: x - bodyPartContainer.x,
y: y - bodyPartContainer.y
};
for (var i = 0; i < bodyPartButtons.length; i++) {
var button = bodyPartButtons[i];
if (Math.abs(bodyPos.x - button.x) < 200 && Math.abs(bodyPos.y - button.y) < 80) {
selectBodyPart(i);
hideBodyPartSelection();
return;
}
}
}
};
game.move = function (x, y, obj) {
if (gameState === 'playing') {
touchPosition = {
x: x,
y: y
};
}
};
game.up = function (x, y, obj) {
touchPosition = null;
};
function purchaseUpgrade(upgradeIndex) {
var baseCosts = [15, 15, 15, 15, 15];
var upgradeLevels = [healthUpgradeLevel, attackSpeedUpgradeLevel, attackPowerUpgradeLevel, movementSpeedUpgradeLevel, attackRangeUpgradeLevel];
var cost = Math.floor(baseCosts[upgradeIndex] * Math.pow(2.5, upgradeLevels[upgradeIndex]));
if (playerCoins >= cost) {
playerCoins -= cost;
coinText.setText('Coins: ' + playerCoins);
switch (upgradeIndex) {
case 0:
// Health upgrade
healthUpgradeLevel++;
storage.healthUpgradeLevel = healthUpgradeLevel;
player.maxHealth += 15;
player.health = player.maxHealth;
break;
case 1:
// Attack Speed upgrade
attackSpeedUpgradeLevel++;
storage.attackSpeedUpgradeLevel = attackSpeedUpgradeLevel;
player.attackSpeed = Math.max(10, player.attackSpeed - 5);
break;
case 2:
// Attack Power upgrade
attackPowerUpgradeLevel++;
storage.attackPowerUpgradeLevel = attackPowerUpgradeLevel;
player.damage += 8;
break;
case 3:
// Movement Speed upgrade
movementSpeedUpgradeLevel++;
storage.movementSpeedUpgradeLevel = movementSpeedUpgradeLevel;
player.speed += 0.8;
break;
case 4:
// Attack Range upgrade
attackRangeUpgradeLevel++;
storage.attackRangeUpgradeLevel = attackRangeUpgradeLevel;
player.attackRange += 20;
// Update visual range circle
var rangeGraphics = player.children[1]; // Second child is the range circle
rangeGraphics.width = player.attackRange * 2;
rangeGraphics.height = player.attackRange * 2;
break;
}
updateStatsDisplay();
}
}
function updateStatsDisplay() {
healthStatText.setText('Health: ' + player.maxHealth);
attackPowerStatText.setText('Attack: ' + player.damage);
attackSpeedStatText.setText('A.Speed: ' + (130 - player.attackSpeed));
movementSpeedStatText.setText('M.Speed: ' + player.speed.toFixed(1));
attackRangeStatText.setText('Range: ' + player.attackRange * 2);
}
function selectBodyPart(partIndex) {
switch (partIndex) {
case 0:
// Strong Arms - More damage
player.damage += 25;
player.scaleX *= 1.1;
break;
case 1:
// Swift Legs - More speed
player.speed += 2;
player.scaleY *= 1.1;
break;
case 2:
// Tough Body - More health
player.maxHealth += 50;
player.health = player.maxHealth;
player.scaleX *= 1.05;
player.scaleY *= 1.05;
break;
}
}
// Spawn initial enemies
spawnEnemies();
game.update = function () {
// Update player health bar at top of screen
var healthPercent = player.health / player.maxHealth;
playerHealthBarFill.width = 316 * healthPercent;
// Fade from red to white based on health percentage
var redValue = Math.floor(255 * healthPercent + 255 * (1 - healthPercent));
var greenValue = Math.floor(0 * healthPercent + 255 * (1 - healthPercent));
var blueValue = Math.floor(0 * healthPercent + 255 * (1 - healthPercent));
var newColor = redValue << 16 | greenValue << 8 | blueValue;
playerHealthBarFill.tint = newColor;
playerHealthBarText.setText(player.health.toString());
// Update stats display
updateStatsDisplay();
// Update remaining enemies counter
if (currentBoss) {
remainingText.setText('Boss Fight!');
} else {
remainingText.setText('Enemies: ' + enemies.length);
}
// Check if all enemies are defeated
if (gameState === 'playing' && enemies.length === 0 && !currentBoss) {
// Progress to next level
currentLevel++;
levelText.setText('Level: ' + currentLevel);
enemiesKilled = 0;
if (currentLevel % 10 === 0) {
// Show shop before boss fight every 10 levels
showShop();
} else if (currentLevel % 5 === 0) {
// Shop level every 5 levels (but not on boss levels)
showShop();
} else {
// Regular level
spawnEnemies();
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -557,31 +557,31 @@
self.die();
}
};
self.die = function () {
- // Create enhanced explosion effect with multiple particles
- for (var e = 0; e < 5; e++) {
+ // Create subtle explosion effect with fewer particles
+ for (var e = 0; e < 3; e++) {
var explosionCircle = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5,
- width: 30 + e * 15,
- height: 30 + e * 15,
- alpha: 0.9 - e * 0.15,
- tint: e === 0 ? 0xFF0000 : e === 1 ? 0xFF4500 : e === 2 ? 0xFF6600 : e === 3 ? 0xFF8800 : 0xFFAA00
+ width: 20 + e * 8,
+ height: 20 + e * 8,
+ alpha: 0.6 - e * 0.15,
+ tint: e === 0 ? 0xFF4500 : e === 1 ? 0xFF6600 : 0xFF8800
});
- explosionCircle.x = self.x + (Math.random() - 0.5) * 60;
- explosionCircle.y = self.y + (Math.random() - 0.5) * 60;
+ explosionCircle.x = self.x + (Math.random() - 0.5) * 30;
+ explosionCircle.y = self.y + (Math.random() - 0.5) * 30;
game.addChild(explosionCircle);
- // Animate explosion with varied timing and movement
+ // Animate explosion with subtle scaling and movement
tween(explosionCircle, {
- scaleX: 3 + e * 0.5,
- scaleY: 3 + e * 0.5,
+ scaleX: 1.5 + e * 0.3,
+ scaleY: 1.5 + e * 0.3,
alpha: 0,
- x: explosionCircle.x + (Math.random() - 0.5) * 120,
- y: explosionCircle.y + (Math.random() - 0.5) * 120
+ x: explosionCircle.x + (Math.random() - 0.5) * 40,
+ y: explosionCircle.y + (Math.random() - 0.5) * 40
}, {
- duration: 500 + e * 80,
- easing: e % 2 === 0 ? tween.easeOut : tween.easeInOut,
+ duration: 300 + e * 50,
+ easing: tween.easeOut,
onFinish: function onFinish() {
explosionCircle.destroy();
}
});
@@ -793,8 +793,36 @@
if (self.health < 0) self.health = 0;
// Health bar now handled in GUI
LK.effects.flashObject(self, 0xff0000, 300);
if (self.health <= 0) {
+ // Create massive nova-style explosion when player dies
+ for (var e = 0; e < 12; e++) {
+ var explosionCircle = LK.getAsset('attackRange', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 80 + e * 40,
+ height: 80 + e * 40,
+ alpha: 1.0 - e * 0.08,
+ tint: e < 4 ? 0xFFFFFF : e < 8 ? 0xFFFF00 : 0xFF6600
+ });
+ explosionCircle.x = self.x + (Math.random() - 0.5) * 120;
+ explosionCircle.y = self.y + (Math.random() - 0.5) * 120;
+ game.addChild(explosionCircle);
+ // Animate massive explosion with dramatic nova effect
+ tween(explosionCircle, {
+ scaleX: 8 + e * 1.2,
+ scaleY: 8 + e * 1.2,
+ alpha: 0,
+ x: explosionCircle.x + (Math.random() - 0.5) * 400,
+ y: explosionCircle.y + (Math.random() - 0.5) * 400
+ }, {
+ duration: 1200 + e * 200,
+ easing: e % 3 === 0 ? tween.easeOut : e % 3 === 1 ? tween.easeInOut : tween.bounceOut,
+ onFinish: function onFinish() {
+ explosionCircle.destroy();
+ }
+ });
+ }
LK.showGameOver();
}
};
return self;
coin. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
health pack. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
scary toothed germ style enemy. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
cloud. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
emerald. In-Game asset. 2d. High contrast. No shadows
blue hand granade. In-Game asset. 2d. High contrast. No shadows
a green scary ball with metal teeth. In-Game asset. 2d. High contrast. No shadows
green poison ball. In-Game asset. 2d. High contrast. No shadows
machine gun. In-Game asset. 2d. High contrast. No shadows
machine gun bullet. In-Game asset. 2d. High contrast. No shadows
rocket launcher. In-Game asset. 2d. High contrast. No shadows
horizontal rocket bullet. In-Game asset. 2d. High contrast. No shadows
treasure chest. In-Game asset. 2d. High contrast. No shadows
vaccine with a plus sign. In-Game asset. 2d. High contrast. No shadows
blue steel vest. In-Game asset. 2d. High contrast. No shadows
u magnet. In-Game asset. 2d. High contrast. No shadows
red shoes with a 2x. In-Game asset. 2d. High contrast. No shadows
horizontal needle. In-Game asset. 2d. High contrast. No shadows
horizontal fire ball. In-Game asset. 2d. High contrast. No shadows
blue
pink blue black and orange mixed horizontal fire ball. In-Game asset. 2d. High contrast. No shadows
robot virüs. In-Game asset. 2d. High contrast. No shadows
artı işareti yeşil olsun bu artı işareti tedavi artı işareti yukarıda boyadığım kısım kırmızı olsun
metal kaplı görünsün
amber stone. In-Game asset. 2d. High contrast. No shadows
rengi mor olsun