User prompt
move player to topmost layer after start of every wave
User prompt
ensure player is top most asset layer
User prompt
player disappears at start of wave three
User prompt
player disappears at start of wave three
User prompt
player is disappearing where not dead
User prompt
make viewport background always below all other assets
User prompt
after each wave have the gamescreen fade out, choose a new viewport background, fade gamescreen back in and resume play ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
change viewport background to random backround asset on every game start
User prompt
drops should not overlap when spawned
User prompt
round to nearest whole number for all variables
User prompt
health drop should restore 25% of max health
User prompt
move helth bar background down 10 pixels
User prompt
make healthbar foreground 25% taller, move it up 5px
User prompt
make healthbar foreground 25% taller
User prompt
invert healthbar foreground function
User prompt
change the lifebar forground image to black
User prompt
change lifebar color to black ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
move healthbar background up 25% of lifebar height
User prompt
move healthbar background up 50% of lifebar height
User prompt
move healthbar background up 110% of lifebar height
User prompt
move healthbar background to center vertically on lifebar
User prompt
double height of healthbar background
User prompt
stretch healthbar background to fit width of viewport
User prompt
make healthbar background 50% taller
User prompt
reduce healthbar length 25% adjust position to remain centered on healthbar background
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 20;
self.damage = 10;
self.vx = 0;
self.vy = 0;
self.piercing = false;
self.maxDistance = 800;
self.travelDistance = 0;
self.update = function () {
var moveX = self.vx;
var moveY = self.vy;
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
self.travelDistance += distance;
self.x += moveX;
self.y += moveY;
};
return self;
});
var HealthPack = Container.expand(function () {
var self = Container.call(this);
var healthGraphics = self.attachAsset('healthPack', {
anchorX: 0.5,
anchorY: 0.5
});
healthGraphics.tint = 0xFF0000; // Bright red color
// Add + text on the healthpack with white color
var plusText = new Text2('+', {
size: 48,
fill: 0xFFFFFF,
font: "'Arial Black'"
});
plusText.anchor.set(0.5, 0.5);
self.addChild(plusText);
self.healAmount = 30;
self.spawnTime = LK.ticks;
self.update = function () {
// Pulsing effect using tween every 1 second
if (LK.ticks % 60 === 0) {
// Pulse every 1 second from 100% down to 70%
tween(self, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
// Keep + text readable during scaling
plusText.scaleX = 1 / Math.max(self.scaleX, 0.7);
plusText.scaleY = 1 / Math.max(self.scaleY, 0.7);
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.fireRate = 10;
self.fireCooldown = 0;
self.moveSpeed = 5;
self.invulnerable = 0;
self.takeDamage = function (amount) {
self.health -= amount;
self.invulnerable = 60;
LK.getSound('playerHurt').play();
LK.effects.flashObject(self, 0xFF0000, 500);
if (self.health <= 0) {
self.health = 0;
return true;
}
return false;
};
self.heal = function (amount) {
self.health = Math.min(self.health + amount, self.maxHealth);
};
self.update = function () {
if (self.fireCooldown > 0) self.fireCooldown--;
if (self.invulnerable > 0) {
self.invulnerable--;
self.alpha = Math.floor(self.invulnerable / 5) % 2 ? 0.5 : 1;
}
};
return self;
});
var PowerShotPowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerShot', {
anchorX: 0.5,
anchorY: 0.5
});
// Add P text on the powerup with white color
var pText = new Text2('P', {
size: 48,
fill: 0xFFFFFF,
font: "'Arial Black'"
});
pText.anchor.set(0.5, 0.5);
self.addChild(pText);
self.ammoAmount = 10;
self.spawnTime = LK.ticks;
self.update = function () {
// Large pulsing effect using tween every 1 second
if (LK.ticks % 60 === 0) {
// Pulse every 1 second from 100% down to 10%
tween(self, {
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
// Keep P text readable during scaling
pText.scaleX = 1 / Math.max(self.scaleX, 0.1);
pText.scaleY = 1 / Math.max(self.scaleY, 0.1);
};
return self;
});
var TripleShot = Container.expand(function () {
var self = Container.call(this);
// Create center bullet
var centerBullet = self.attachAsset('tripleShotBullet', {
anchorX: 0.5,
anchorY: 0.5
});
centerBullet.x = 0;
centerBullet.y = 0;
centerBullet.rotation = 0;
// Create left bullet tilted 10 degrees away
var leftBullet = self.attachAsset('tripleShotBullet', {
anchorX: 0.5,
anchorY: 0.5
});
leftBullet.x = -20;
leftBullet.y = -8;
leftBullet.rotation = -10 * Math.PI / 180; // -10 degrees in radians
// Create right bullet tilted 10 degrees away
var rightBullet = self.attachAsset('tripleShotBullet', {
anchorX: 0.5,
anchorY: 0.5
});
rightBullet.x = 20;
rightBullet.y = -8;
rightBullet.rotation = 10 * Math.PI / 180; // 10 degrees in radians
self.spawnTime = LK.ticks;
self.update = function () {
// Spinning animation like Zoin - rotate the entire powerup
self.rotation += 0.075;
// Add pulsing effect with tween every 2 seconds
if (LK.ticks % 120 === 0) {
// Pulse every 2 seconds
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
// Add floating effect
self.y += Math.sin(LK.ticks * 0.1) * 0.3;
};
return self;
});
var Zoin = Container.expand(function () {
var self = Container.call(this);
var zoinGraphics = self.attachAsset('zoin', {
anchorX: 0.5,
anchorY: 0.5
});
// Add Z text on the coin with neon green color
var zText = new Text2('Z', {
size: 24,
fill: 0x00ff00,
font: "'Arial Black'"
});
zText.anchor.set(0.5, 0.5);
self.addChild(zText);
self.spawnTime = LK.ticks;
self.spinPhase = 0; // Track spinning phase for coin flip effect
self.update = function () {
// Spinning animation - slower spin to show coin flip effect at 50% speed
self.spinPhase += 0.075;
// Scale X based on sine wave to create flipping effect
var scaleX = Math.abs(Math.sin(self.spinPhase));
self.scaleX = scaleX;
zText.scaleX = 1 / Math.max(scaleX, 0.1); // Keep Z readable and prevent division by 0
// Z is always visible on both sides - no longer hide it
zText.visible = true;
// Pulsing effect using tween
if (LK.ticks % 120 === 0) {
// Pulse every 2 seconds
tween(self, {
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
// Add floating effect
self.y += Math.sin(LK.ticks * 0.1) * 0.3;
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Randomly choose zombie visual from available assets
var zombieAssets = ['zombie', 'zombie2', 'zombie3', 'zombie4'];
var randomAsset = zombieAssets[Math.floor(Math.random() * zombieAssets.length)];
var zombieGraphics = self.attachAsset(randomAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.baseSpeed = 1.5;
var variation = self.baseSpeed * 0.25; // 25% variation
self.speed = self.baseSpeed + (Math.random() - 0.5) * 2 * variation; // Random between baseSpeed ± 25%
self.health = 20;
self.maxHealth = 20; // Store base health for wave scaling
self.damage = 1;
self.baseDamage = 1; // Store base damage for wave scaling
self.attackCooldown = 0;
self.isDead = false;
self.takeDamage = function (amount) {
if (self.isDead) return false;
self.health -= amount;
if (self.health <= 0) {
self.isDead = true;
LK.getSound('zombieHit').play();
return true;
}
zombieGraphics.tint = 0xFF0000;
tween(zombieGraphics, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
zombieGraphics.tint = 0xFFFFFF;
}
});
return false;
};
self.update = function () {
if (self.isDead) return;
if (self.attackCooldown > 0) self.attackCooldown--;
};
return self;
});
var ZombieHulk = Zombie.expand(function () {
var self = Zombie.call(this);
// Enhanced stats: 100% more life, 25% more speed, 50% larger
self.baseSpeed = 1.5 * 1.25; // 25% more speed
var variation = self.baseSpeed * 0.25; // 25% variation
self.speed = self.baseSpeed + (Math.random() - 0.5) * 2 * variation; // Random between baseSpeed ± 25%
self.health = 40; // 100% more life (20 * 2)
self.maxHealth = 40; // Store base health for wave scaling
// Make 50% larger by scaling the existing zombie graphics after brief delay to ensure visual is ready
LK.setTimeout(function () {
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut
});
}, 50);
// 50% chance to have a random powerup in inventory
self.hasPowerup = Math.random() < 0.5;
if (self.hasPowerup) {
var powerUpTypes = ['powerShotPowerUp', 'healthPack', 'tripleShot'];
self.powerupType = powerUpTypes[Math.floor(Math.random() * powerUpTypes.length)];
}
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var player;
var zombies = [];
var bullets = [];
var pickups = [];
var wave = 1;
var zombiesPerWave = 5;
var zombiesSpawned = 0;
var zombiesKilled = 0;
var spawnTimer = 0;
var scoreMultiplier = 1;
var killStreak = 0;
var powerAmmo = 0;
var lastPowerUpSpawn = 0;
var killsSinceLastPowerUp = 0;
var powerUpList = ['powerShotPowerUp', 'healthPack', 'tripleShot'];
var lastPowerUpType = null;
var usedPowerUps = []; // Track which powerups have been used in current cycle
var availablePowerUps = powerUpList.slice(); // Copy of powerups available for selection
var hasTripleShot = false;
var tripleShotLevel = 0;
var maxTripleShotLevel = 5;
var noZombiesTimer = 0;
var noZombiesThreshold = 600; // 10 seconds at 60fps
// Load persistent data from storage (removed reset statements)
// Load persistent data from storage without clearing
var zoins = storage.zoins || 0;
// Upgrade costs and levels - loaded from storage
var maxHealthUpgrades = storage.maxHealthUpgrades || 0;
var fireRateUpgrades = storage.fireRateUpgrades || 0;
var damageUpgrades = storage.damageUpgrades || 0;
// Save damage upgrades to storage whenever modified
storage.damageUpgrades = damageUpgrades;
// Load leaderboard data from storage
// Leaderboard data - reconstruct from flattened storage
var storedLeaderboard = storage.leaderboard || {};
var leaderboard = [];
if (storedLeaderboard.length) {
for (var i = 0; i < storedLeaderboard.length; i++) {
if (storedLeaderboard['name_' + i] !== undefined) {
leaderboard.push({
name: String(storedLeaderboard['name_' + i]),
score: Number(storedLeaderboard['score_' + i]),
wave: Number(storedLeaderboard['wave_' + i])
});
}
}
} else {
leaderboard = [];
}
// Arrays for generating player names
var adjectives = ['Brave', 'Swift', 'Mighty', 'Fierce', 'Bold', 'Quick', 'Strong', 'Wild', 'Sharp', 'Dark', 'Bright', 'Cold', 'Hot', 'Fast', 'Slow', 'Big', 'Small', 'Red', 'Blue', 'Green'];
var commonNames = ['Alex', 'Sam', 'Max', 'Jake', 'Mike', 'Tom', 'Ben', 'Dan', 'Joe', 'Ryan', 'Kyle', 'Luke', 'Dave', 'Jack', 'Matt', 'Nick', 'Chris', 'Josh', 'Mark', 'Paul', 'Emma', 'Sarah', 'Lisa', 'Amy', 'Kate', 'Anna', 'Maya', 'Zoe', 'Mia', 'Ella', 'Eva', 'Ava', 'Lily', 'Ruby', 'Nina', 'Rose', 'Lucy', 'Jade', 'Hope', 'Iris'];
// Load or generate player name
var currentPlayerName = storage.playerName || '';
if (!currentPlayerName) {
var randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
var randomName = commonNames[Math.floor(Math.random() * commonNames.length)];
currentPlayerName = randomAdjective + '-' + randomName;
storage.playerName = currentPlayerName;
}
var moveControl;
var moveStick;
var isDraggingMove = false;
// UI Elements
var waveText = new Text2('Wave: 1', {
size: 80,
fill: 0xFFFFFF
});
waveText.anchor.set(0, 0);
waveText.x = 150;
LK.gui.topLeft.addChild(waveText);
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = -150;
LK.gui.topRight.addChild(scoreText);
// Health bar black outline
var healthBarOutline = LK.getAsset('ammoBox', {
width: 1849,
height: 81,
anchorX: 0.5,
anchorY: 0
});
healthBarOutline.tint = 0x000000;
healthBarOutline.x = 1024;
healthBarOutline.y = 97;
game.addChildAt(healthBarOutline, 0);
// Health bar background using lifebar asset
var healthBarBg = LK.getAsset('lifebar', {
width: 2048,
height: 225,
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 1024;
healthBarBg.y = 100;
game.addChildAt(healthBarBg, 1);
// Health bar fill
var healthBarFill = LK.getAsset('ammoBox', {
width: 1382.25,
// Reduced by 25% (1843 * 0.75)
height: 37.5,
anchorX: 0.5,
anchorY: 0
});
healthBarFill.tint = 0x444444;
healthBarFill.x = 1024;
healthBarFill.y = 118.75; // Centered vertically on lifebar background (100 + (75-37.5)/2)
game.addChildAt(healthBarFill, 2);
// Health text display
var healthText = new Text2('100/100', {
size: 56,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 1024;
healthText.y = 137.5; // Center of health bar (100 + 75/2)
game.addChildAt(healthText, 3);
var powerAmmoText = new Text2('Power Ammo: 0', {
size: 45,
fill: 0xFFD700
});
powerAmmoText.anchor.set(1, 1);
powerAmmoText.x = -20;
powerAmmoText.y = -80;
LK.gui.bottomRight.addChild(powerAmmoText);
var zoinText = new Text2('Zoins: ' + zoins, {
size: 45,
fill: 0xFFD700
});
zoinText.anchor.set(1, 1);
zoinText.x = -20;
zoinText.y = -140;
LK.gui.bottomRight.addChild(zoinText);
// Leaderboard display as scrolling banner
var leaderboardText = new Text2('LEADERBOARD\n', {
size: 40,
fill: 0xFFFFFF
});
leaderboardText.anchor.set(0, 0);
leaderboardText.x = 2048; // Start off-screen to the right
leaderboardText.y = 200;
game.addChild(leaderboardText);
// Leaderboard scrolling variables
var leaderboardScrolling = false;
// Update leaderboard display
function updateLeaderboardDisplay() {
var displayText = 'LEADERBOARD ';
var sortedLeaderboard = leaderboard.slice().sort(function (a, b) {
return b.score - a.score;
});
// Show top 9 entries
for (var i = 0; i < Math.min(9, sortedLeaderboard.length); i++) {
var entry = sortedLeaderboard[i];
displayText += i + 1 + '. ' + entry.name + ' - ' + entry.score + ' (Wave ' + entry.wave + ') • ';
}
// Find current player's best entry and position
var playerBestEntry = null;
var playerPosition = -1;
for (var i = 0; i < sortedLeaderboard.length; i++) {
var entry = sortedLeaderboard[i];
if (entry.name === currentPlayerName && (!playerBestEntry || entry.score > playerBestEntry.score)) {
playerBestEntry = entry;
playerPosition = i + 1;
}
}
// If player has an entry and it's not in top 9, show their position
if (playerBestEntry && playerPosition > 9) {
displayText += '... ' + playerPosition + '. ' + playerBestEntry.name + ' - ' + playerBestEntry.score + ' (Wave ' + playerBestEntry.wave + ') • ';
}
leaderboardText.setText(displayText);
startLeaderboardScroll();
}
// Start leaderboard scrolling animation
function startLeaderboardScroll() {
if (leaderboardScrolling) return;
leaderboardScrolling = true;
// Stop any existing tween
tween.stop(leaderboardText, {
x: true
});
// Reset position to start from right
leaderboardText.x = 2048;
// Calculate scroll distance based on text width
var scrollDistance = leaderboardText.width + 2048;
// Start scrolling animation
tween(leaderboardText, {
x: -scrollDistance
}, {
duration: scrollDistance * 15,
// Adjust speed as needed
easing: tween.linear,
onFinish: function onFinish() {
leaderboardScrolling = false;
// Restart the scroll after a brief pause
LK.setTimeout(function () {
startLeaderboardScroll();
}, 2000);
}
});
}
updateLeaderboardDisplay();
// Function to submit score to leaderboard
function submitScoreToLeaderboard() {
// Player name is now persistently generated at game start
// Create simple literal object for leaderboard entry
var newEntry = {
name: currentPlayerName,
score: LK.getScore(),
wave: wave
};
// Create cleaned leaderboard first
var cleanLeaderboard = [];
for (var i = 0; i < leaderboard.length; i++) {
var entry = leaderboard[i];
cleanLeaderboard.push({
name: String(entry.name),
score: Number(entry.score),
wave: Number(entry.wave)
});
}
// Add new entry as literal object
cleanLeaderboard.push({
name: String(newEntry.name),
score: Number(newEntry.score),
wave: Number(newEntry.wave)
});
// Sort and limit to top 20
cleanLeaderboard.sort(function (a, b) {
return b.score - a.score;
});
if (cleanLeaderboard.length > 20) {
cleanLeaderboard = cleanLeaderboard.slice(0, 20);
}
// Update local leaderboard and save to storage
leaderboard = cleanLeaderboard;
// Flatten leaderboard for storage (storage only accepts literals and 1-level objects)
var flatLeaderboard = {};
for (var i = 0; i < cleanLeaderboard.length; i++) {
var entry = cleanLeaderboard[i];
flatLeaderboard['name_' + i] = String(entry.name);
flatLeaderboard['score_' + i] = Number(entry.score);
flatLeaderboard['wave_' + i] = Number(entry.wave);
}
flatLeaderboard.length = cleanLeaderboard.length;
storage.leaderboard = flatLeaderboard;
// Update display
updateLeaderboardDisplay();
}
// Initialize player
player = game.addChild(new Player());
player.maxHealth = (maxHealthUpgrades + 1) * 2;
player.health = player.maxHealth;
player.fireRate = 3 * Math.pow(1.1, fireRateUpgrades);
player.x = 1024;
player.y = 1366;
// Initialize controls
moveControl = LK.getAsset('moveControl', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
moveControl.x = 300;
moveControl.y = 2432;
game.addChild(moveControl);
moveStick = LK.getAsset('moveControl', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0
});
moveStick.x = moveControl.x;
moveStick.y = moveControl.y;
game.addChild(moveStick);
// Aim control removed - using automatic targeting
// Helper functions
function countHealthPacksOnScreen() {
var healthPackCount = 0;
for (var i = 0; i < pickups.length; i++) {
if (pickups[i] instanceof HealthPack) {
healthPackCount++;
}
}
return healthPackCount;
}
function calculateCumulativeCost(upgradeLevel) {
var totalCost = 0;
for (var i = 0; i <= upgradeLevel; i++) {
totalCost += Math.round((10 + i * 2) / 2);
}
return totalCost;
}
function resetGame() {
// Reset wave and zombies
wave = 1;
zombiesPerWave = 5;
zombiesSpawned = 0;
zombiesKilled = 0;
spawnTimer = 0;
hulksSpawned = 0; // Reset hulk spawn counter
// Reset score and multipliers
LK.setScore(0);
scoreMultiplier = 1;
killStreak = 0;
killsSinceLastPowerUp = 0;
lastPowerUpSpawn = 0;
lastPowerUpType = null;
// Reset powerup cycle tracking
usedPowerUps = [];
availablePowerUps = powerUpList.slice();
// Reset powerups
powerAmmo = 0;
hasTripleShot = false;
tripleShotLevel = 0;
// Reset player state with upgrades
player.maxHealth = (maxHealthUpgrades + 1) * 2;
player.health = player.maxHealth;
player.fireRate = 3 * Math.pow(1.1, fireRateUpgrades);
player.fireCooldown = 0;
player.invulnerable = 0;
player.alpha = 1;
// Clear all game objects
for (var i = zombies.length - 1; i >= 0; i--) {
zombies[i].destroy();
}
zombies = [];
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
bullets = [];
for (var i = pickups.length - 1; i >= 0; i--) {
pickups[i].destroy();
}
pickups = [];
// Update UI
waveText.setText('Wave: ' + wave);
scoreText.setText('Score: ' + LK.getScore());
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBarFill.width = 1382.25 * healthPercent;
healthBarFill.height = 37.5;
healthText.setText(player.health + '/' + player.maxHealth);
powerAmmoText.setText('Power Ammo: ' + powerAmmo);
powerAmmoText.visible = false;
// Reset player position
player.x = 1024;
player.y = 1366;
// Reset no zombies timer
noZombiesTimer = 0;
// Update powerup display
updatePowerupDisplay();
}
// Track hulks spawned for current wave
var hulksSpawned = 0;
function spawnZombie() {
var zombie;
// Spawn regular zombies first, then hulks at the end
var regularZombiesCount = zombiesPerWave - wave; // Total zombies minus hulks
if (zombiesSpawned < regularZombiesCount) {
zombie = new Zombie();
} else {
zombie = new ZombieHulk();
hulksSpawned++;
}
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
zombie.x = Math.random() * 2048;
zombie.y = -50;
break;
case 1:
// Right
zombie.x = 2098;
zombie.y = Math.random() * 2732;
break;
case 2:
// Bottom
zombie.x = Math.random() * 2048;
zombie.y = 2782;
break;
case 3:
// Left
zombie.x = -50;
zombie.y = Math.random() * 2732;
break;
}
zombie.speed = zombie.baseSpeed * (1 + wave * 0.1) + (Math.random() - 0.5) * 2 * (zombie.baseSpeed * 0.25);
zombie.health += wave * 5;
zombie.damage += Math.floor(wave * 2); // Increase damage by 2 per wave
zombie.scaleX = 0;
zombie.scaleY = 0;
zombie.alpha = 0;
zombies.push(zombie);
game.addChild(zombie);
zombiesSpawned++;
// Animate zombie spawn
tween(zombie, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
function spawnPickup(x, y, zombie) {
var shouldSpawn = false;
var pickup;
var powerUpType;
// Check if zombie hulk has powerup in inventory
if (zombie instanceof ZombieHulk && zombie.hasPowerup) {
shouldSpawn = true;
// If all powerups have been used, reset the cycle
if (availablePowerUps.length === 0) {
availablePowerUps = powerUpList.slice(); // Reset available powerups
usedPowerUps = []; // Clear used powerups
}
// Select random powerup from available list for hulk
var randomIndex = Math.floor(Math.random() * availablePowerUps.length);
powerUpType = availablePowerUps[randomIndex];
// Move selected powerup from available to used
usedPowerUps.push(powerUpType);
availablePowerUps.splice(randomIndex, 1);
if (powerUpType === 'powerShotPowerUp') {
pickup = new PowerShotPowerUp();
} else if (powerUpType === 'tripleShot') {
pickup = new TripleShot();
} else {
// Check if healthpack would be spawned and limit is reached
if (countHealthPacksOnScreen() >= 2) {
// Replace with 2 zoin drops side by side
var zoin1 = new Zoin();
zoin1.x = x - 30;
zoin1.y = y;
zoin1.scaleX = 0;
zoin1.scaleY = 0;
pickups.push(zoin1);
game.addChild(zoin1);
var zoin2 = new Zoin();
zoin2.x = x + 30;
zoin2.y = y;
zoin2.scaleX = 0;
zoin2.scaleY = 0;
pickups.push(zoin2);
game.addChild(zoin2);
// Animate both zoin spawns
tween(zoin1, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
tween(zoin2, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Don't create healthpack
pickup = null;
} else {
pickup = new HealthPack();
}
}
killsSinceLastPowerUp = 0;
if (pickup instanceof PowerShotPowerUp) {
lastPowerUpSpawn = LK.ticks;
}
}
// Force powerup spawn if 50+ kills without powerup (but not for normal zombies)
else if (killsSinceLastPowerUp >= 50) {
shouldSpawn = true;
// If all powerups have been used, reset the cycle
if (availablePowerUps.length === 0) {
availablePowerUps = powerUpList.slice(); // Reset available powerups
usedPowerUps = []; // Clear used powerups
}
// Select random powerup from available list
var randomIndex = Math.floor(Math.random() * availablePowerUps.length);
powerUpType = availablePowerUps[randomIndex];
// Move selected powerup from available to used
usedPowerUps.push(powerUpType);
availablePowerUps.splice(randomIndex, 1);
if (powerUpType === 'powerShotPowerUp') {
pickup = new PowerShotPowerUp();
} else if (powerUpType === 'tripleShot') {
pickup = new TripleShot();
} else {
// Check if healthpack would be spawned and limit is reached
if (countHealthPacksOnScreen() >= 2) {
// Replace with 2 zoin drops side by side
var zoin1 = new Zoin();
zoin1.x = x - 30;
zoin1.y = y;
zoin1.scaleX = 0;
zoin1.scaleY = 0;
pickups.push(zoin1);
game.addChild(zoin1);
var zoin2 = new Zoin();
zoin2.x = x + 30;
zoin2.y = y;
zoin2.scaleX = 0;
zoin2.scaleY = 0;
pickups.push(zoin2);
game.addChild(zoin2);
// Animate both zoin spawns
tween(zoin1, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
tween(zoin2, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Don't create healthpack
pickup = null;
} else {
pickup = new HealthPack();
}
}
killsSinceLastPowerUp = 0;
if (pickup instanceof PowerShotPowerUp) {
lastPowerUpSpawn = LK.ticks;
}
}
// No powerup spawn for normal zombies
if (shouldSpawn && pickup) {
pickup.x = x;
pickup.y = y;
pickup.scaleX = 0;
pickup.scaleY = 0;
pickups.push(pickup);
game.addChild(pickup);
// Animate pickup spawn with bounce effect
tween(pickup, {
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.elasticOut
});
}
}
function fireBullet() {
// Convert shots per second to ticks between shots (60 ticks = 1 second)
var shotsPerSecond = powerAmmo > 0 ? player.fireRate * 0.25 : player.fireRate; // Power ammo shoots at 25% of normal rate
var ticksBetweenShots = Math.max(1, Math.floor(60 / shotsPerSecond)); // Ensure at least 1 tick between shots
if (player.fireCooldown > 0) return;
// Find closest zombie in range
var closestZombie = null;
var closestDistance = 500; // Maximum firing range
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
if (!zombie || zombie.isDead) continue;
var dx = zombie.x - player.x;
var dy = zombie.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
closestZombie = zombie;
}
}
// Only fire if there's a zombie in range
if (!closestZombie) return;
var dx = closestZombie.x - player.x;
var dy = closestZombie.y - player.y;
var angle = Math.atan2(dy, dx);
// Player visual does not rotate - only bullets aim at target
// Create bullets based on triple shot level
var bulletCount = hasTripleShot ? 1 + tripleShotLevel * 2 : 1;
var angleOffsets = [0]; // Always include center bullet
if (hasTripleShot) {
// Add bullets in pairs at increasing angles
for (var level = 1; level <= tripleShotLevel; level++) {
var angleOffset = level * 10 * Math.PI / 180;
angleOffsets.push(-angleOffset); // Left bullet
angleOffsets.push(angleOffset); // Right bullet
}
}
// Consume power ammo once per shot, not per bullet
var usingPowerAmmo = powerAmmo > 0;
if (usingPowerAmmo) {
powerAmmo--; // Consume one power ammo per shot
}
for (var b = 0; b < bulletCount; b++) {
var bullet = new Bullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.damage = 10 + damageUpgrades * 5;
var bulletAngle = angle + angleOffsets[b];
bullet.vx = Math.cos(bulletAngle) * bullet.speed;
bullet.vy = Math.sin(bulletAngle) * bullet.speed;
bullet.rotation = bulletAngle;
if (usingPowerAmmo) {
bullet.piercing = true;
bullet.maxDistance = 1600; // 2x distance
bullet.damage = (10 + damageUpgrades * 5) * 5; // 5x normal damage
// Visual effect for power bullet
tween(bullet, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 100
});
bullet.tint = 0xFFD700;
} else {
bullet.maxDistance = 800;
}
bullets.push(bullet);
game.addChild(bullet);
}
player.fireCooldown = ticksBetweenShots;
LK.getSound('shoot').play();
}
// Event handlers
game.down = function (x, y, obj) {
// Prevent control stick activation in bottom 1/4 of viewport (below y = 2049)
if (y > 2049) return;
isDraggingMove = true;
// Move moveControl to click position
moveControl.x = x;
moveControl.y = y;
// Move moveStick to the same position (center of control)
moveStick.x = x;
moveStick.y = y;
// Show control stick with smooth animation
tween(moveControl, {
alpha: 0.3
}, {
duration: 200,
easing: tween.easeOut
});
tween(moveStick, {
alpha: 0.6
}, {
duration: 200,
easing: tween.easeOut
});
};
game.move = function (x, y, obj) {
if (isDraggingMove) {
moveStick.x = Math.max(moveControl.x - 150, Math.min(moveControl.x + 150, x));
moveStick.y = Math.max(moveControl.y - 150, Math.min(moveControl.y + 150, y));
}
};
game.up = function (x, y, obj) {
if (isDraggingMove) {
isDraggingMove = false;
moveStick.x = moveControl.x;
moveStick.y = moveControl.y;
// Hide control stick with smooth animation
tween(moveControl, {
alpha: 0
}, {
duration: 150,
easing: tween.easeIn
});
tween(moveStick, {
alpha: 0
}, {
duration: 150,
easing: tween.easeIn
});
}
};
// Main game loop
game.update = function () {
// Player movement
if (isDraggingMove) {
var moveX = (moveStick.x - moveControl.x) / 150;
var moveY = (moveStick.y - moveControl.y) / 150;
player.x += moveX * player.moveSpeed;
player.y += moveY * player.moveSpeed;
player.x = Math.max(40, Math.min(2008, player.x));
player.y = Math.max(40, Math.min(2692, player.y));
}
// Auto-fire (fireBullet now handles range checking internally)
fireBullet();
// Spawn zombies
if (zombiesSpawned < zombiesPerWave) {
spawnTimer++;
if (spawnTimer > 60) {
spawnZombie();
spawnTimer = 0;
}
} else if (zombies.length === 0) {
// Next wave
wave++;
zombiesPerWave = Math.min(5 + wave * 2, 30); // Cap at 30 zombies per wave to prevent performance issues
zombiesSpawned = 0;
zombiesKilled = 0;
spawnTimer = 0; // Reset spawn timer for new wave
noZombiesTimer = 0; // Reset no zombies timer
hulksSpawned = 0; // Reset hulk spawn counter for new wave
waveText.setText('Wave: ' + wave);
} else {
// Check if there are zombies in view area (within screen bounds plus margin)
var zombiesInView = false;
for (var z = 0; z < zombies.length; z++) {
var zombie = zombies[z];
if (!zombie || zombie.isDead) continue;
// Check if zombie is within view area (screen bounds + 200px margin)
if (zombie.x >= -200 && zombie.x <= 2248 && zombie.y >= -200 && zombie.y <= 2932) {
zombiesInView = true;
break;
}
}
// If no zombies in view, increment timer
if (!zombiesInView && zombiesSpawned >= zombiesPerWave) {
noZombiesTimer++;
// If 10 seconds have passed without zombies in view, advance to next wave
if (noZombiesTimer >= noZombiesThreshold) {
// Remove all remaining zombies
for (var i = zombies.length - 1; i >= 0; i--) {
zombies[i].destroy();
zombies.splice(i, 1);
}
// Next wave
wave++;
zombiesPerWave = Math.min(5 + wave * 2, 30); // Cap at 30 zombies per wave to prevent performance issues
zombiesSpawned = 0;
zombiesKilled = 0;
spawnTimer = 0; // Reset spawn timer for new wave
noZombiesTimer = 0; // Reset no zombies timer
hulksSpawned = 0; // Reset hulk spawn counter for new wave
waveText.setText('Wave: ' + wave);
}
} else {
// Reset timer if zombies are in view or we haven't spawned all zombies yet
noZombiesTimer = 0;
}
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
// Skip if zombie is undefined or dead
if (!zombie || zombie.isDead) continue;
// Move towards player
var dx = player.x - zombie.x;
var dy = player.y - zombie.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
zombie.x += dx / dist * zombie.speed;
zombie.y += dy / dist * zombie.speed;
// Removed zombie rotation to keep zombies upright
}
// Check collision with player
if (zombie.intersects(player) && zombie.attackCooldown === 0) {
var playerDied = player.takeDamage(zombie.damage);
zombie.attackCooldown = 60;
killStreak = 0;
scoreMultiplier = 1;
// Check if player died after taking damage
if (playerDied || player.health <= 0) {
// Submit score to leaderboard before game over
submitScoreToLeaderboard();
LK.showGameOver();
return; // Exit game loop immediately when player dies
}
}
}
// Check if player died during zombie processing - exit immediately
if (player.health <= 0) {
return;
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet is off screen or traveled max distance
if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782 || bullet.travelDistance > bullet.maxDistance) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with zombies
var bulletDestroyed = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (!zombie || zombie.isDead) continue;
if (bullet.intersects(zombie)) {
if (zombie.takeDamage(bullet.damage)) {
// Zombie died - add death animation
spawnPickup(zombie.x, zombie.y, zombie);
// 10% chance to drop 1 zoin when zombie dies
if (Math.random() < 0.1) {
var zoin = new Zoin();
zoin.x = zombie.x;
zoin.y = zombie.y;
zoin.scaleX = 0;
zoin.scaleY = 0;
pickups.push(zoin);
game.addChild(zoin);
// Animate zoin spawn
tween(zoin, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
var zombieToRemove = zombie; // Capture zombie reference for closure
var zombieIndex = j; // Capture index for closure
tween(zombie, {
scaleX: 0,
scaleY: 0,
alpha: 0,
rotation: zombie.rotation + Math.PI
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
zombieToRemove.destroy();
// Remove from zombies array after animation
var currentIndex = zombies.indexOf(zombieToRemove);
if (currentIndex !== -1) {
zombies.splice(currentIndex, 1);
}
}
});
zombiesKilled++;
killStreak++;
killsSinceLastPowerUp++;
if (killStreak % 5 === 0) {
scoreMultiplier = Math.min(scoreMultiplier + 1, 5);
}
// Give 2x score for ZombieHulk, regular score for normal zombies
var scoreToAdd = zombie instanceof ZombieHulk ? 20 * scoreMultiplier : 10 * scoreMultiplier;
LK.setScore(LK.getScore() + scoreToAdd);
scoreText.setText('Score: ' + LK.getScore());
}
if (!bullet.piercing) {
bullet.destroy();
bullets.splice(i, 1);
bulletDestroyed = true;
break;
}
}
}
if (bulletDestroyed) continue;
}
// Update pickups
for (var i = pickups.length - 1; i >= 0; i--) {
var pickup = pickups[i];
// Check if pickup has expired (10 seconds) - auto pickup non-healthpack items
var age = LK.ticks - pickup.spawnTime;
if (age >= pickup.lifespan) {
// Auto pickup all items except health packs when expiring - healthpacks are perpetual
if (pickup instanceof HealthPack) {
// HealthPack no longer expires - skip expiration logic completely
continue;
} else if (pickup instanceof PowerShotPowerUp) {
powerAmmo += pickup.ammoAmount;
LK.effects.flashObject(player, 0xFFD700, 500);
} else if (pickup instanceof TripleShot) {
if (!hasTripleShot) {
hasTripleShot = true;
tripleShotLevel = 1;
} else if (tripleShotLevel < maxTripleShotLevel) {
tripleShotLevel++;
}
LK.effects.flashObject(player, 0x00FF00, 500);
} else if (pickup instanceof Zoin) {
zoins++;
storage.zoins = zoins; // Persist zoin count
zoinText.setText('Zoins: ' + zoins);
LK.effects.flashObject(player, 0xFFD700, 300);
}
LK.getSound('pickup').play();
// Animate pickup collection
tween(pickup, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
pickup.destroy();
}
});
pickups.splice(i, 1);
continue;
}
// Manual pickup when touching
if (pickup.intersects(player)) {
if (pickup instanceof HealthPack) {
player.heal(pickup.healAmount);
} else if (pickup instanceof PowerShotPowerUp) {
powerAmmo += pickup.ammoAmount;
LK.effects.flashObject(player, 0xFFD700, 500);
} else if (pickup instanceof TripleShot) {
if (!hasTripleShot) {
hasTripleShot = true;
tripleShotLevel = 1;
} else if (tripleShotLevel < maxTripleShotLevel) {
tripleShotLevel++;
}
LK.effects.flashObject(player, 0x00FF00, 500);
} else if (pickup instanceof Zoin) {
zoins++;
storage.zoins = zoins; // Persist zoin count
zoinText.setText('Zoins: ' + zoins);
LK.effects.flashObject(player, 0xFFD700, 300);
}
LK.getSound('pickup').play();
// Animate pickup collection
tween(pickup, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
pickup.destroy();
}
});
pickups.splice(i, 1);
}
}
// Update UI
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBarFill.width = 1382.25 * healthPercent;
healthBarFill.height = 37.5;
healthText.setText(player.health + '/' + player.maxHealth);
powerAmmoText.setText('Power Ammo: ' + powerAmmo);
// Hide/show power ammo counter based on availability
powerAmmoText.visible = powerAmmo > 0;
// Update powerup display
updatePowerupDisplay();
// Update button state based on zoin availability
var currentPrice = calculateCumulativeCost(maxHealthUpgrades);
if (zoins < currentPrice) {
bottomLeftButton.alpha = 0.5; // Dim button when unaffordable
lifeLabel.alpha = 0.5;
priceLabel.alpha = 0.5;
} else {
bottomLeftButton.alpha = 1.0; // Full opacity when affordable
lifeLabel.alpha = 1.0;
priceLabel.alpha = 1.0;
}
// Update fire rate button state based on zoin availability
var fireRateCurrentPrice = calculateCumulativeCost(fireRateUpgrades);
if (zoins < fireRateCurrentPrice) {
secondButton.alpha = 0.5; // Dim button when unaffordable
fireRateLabel.alpha = 0.5;
fireRatePriceLabel.alpha = 0.5;
} else {
secondButton.alpha = 1.0; // Full opacity when affordable
fireRateLabel.alpha = 1.0;
fireRatePriceLabel.alpha = 1.0;
}
// Despawn distant pickups
for (var i = pickups.length - 1; i >= 0; i--) {
var pickup = pickups[i];
var dx = player.x - pickup.x;
var dy = player.y - pickup.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 2000) {
pickup.destroy();
pickups.splice(i, 1);
}
}
};
// Create bottom left button
var bottomLeftButton = LK.getAsset('ammoBox', {
width: 250,
height: 100,
anchorX: 0,
anchorY: 1
});
bottomLeftButton.tint = 0xFF0000; // Red background
bottomLeftButton.x = 20;
bottomLeftButton.y = -20;
LK.gui.bottomLeft.addChild(bottomLeftButton);
// Create black outline for button
var buttonOutline = LK.getAsset('ammoBox', {
width: 256,
height: 106,
anchorX: 0,
anchorY: 1
});
buttonOutline.tint = 0x000000; // Black outline
buttonOutline.x = 17;
buttonOutline.y = -17;
LK.gui.bottomLeft.addChildAt(buttonOutline, 0); // Add behind the button
// Create LIFE label for button
var lifeLabel = new Text2('LIFE', {
size: 36,
fill: 0xFFFFFF,
font: "'Arial Black'"
});
lifeLabel.anchor.set(0.5, 0.5);
lifeLabel.x = 145; // Center of button (250/2 + 20 offset for button position)
lifeLabel.y = -70; // Center of button (100/2 + 20 offset for button position)
LK.gui.bottomLeft.addChild(lifeLabel);
// Create price label for button with dynamic pricing
var maxLifePrice = calculateCumulativeCost(maxHealthUpgrades);
var priceLabel = new Text2(maxLifePrice + ' Zoin' + (maxLifePrice === 1 ? '' : 's'), {
size: 24,
fill: 0xFFD700
});
priceLabel.anchor.set(0.5, 1);
priceLabel.x = 145; // Center of button horizontally
priceLabel.y = -25; // Bottom of button
LK.gui.bottomLeft.addChild(priceLabel);
// Make button interactive
bottomLeftButton.buttonMode = true;
// Add button click handlers
bottomLeftButton.down = function (x, y, obj) {
var currentPrice = calculateCumulativeCost(maxHealthUpgrades);
if (zoins < currentPrice) {
return; // Don't allow clicking if insufficient zoins
}
// Change to white when pressed
tween(bottomLeftButton, {
tint: 0xFFFFFF
}, {
duration: 100,
easing: tween.easeOut
});
};
bottomLeftButton.up = function (x, y, obj) {
var currentPrice = calculateCumulativeCost(maxHealthUpgrades);
if (zoins < currentPrice) {
return; // Don't allow clicking if insufficient zoins
}
// Deduct cost from zoins
zoins = zoins - currentPrice;
storage.zoins = zoins; // Persist updated zoin count
zoinText.setText('Zoins: ' + zoins);
// Increment upgrade level
maxHealthUpgrades++;
storage.maxHealthUpgrades = maxHealthUpgrades; // Persist upgrade level
// Update player max health
player.maxHealth = (maxHealthUpgrades + 1) * 2;
player.health = player.maxHealth; // Full heal on upgrade
// Update health bar to reflect new maximum
var healthPercent = player.health / player.maxHealth;
healthBarFill.width = 1382.25 * healthPercent;
healthBarFill.height = 37.5;
healthText.setText(player.health + '/' + player.maxHealth);
// Update price label with new cost
var newPrice = calculateCumulativeCost(maxHealthUpgrades);
priceLabel.setText(newPrice + ' Zoin' + (newPrice === 1 ? '' : 's'));
// Update powerup display
updatePowerupDisplay();
// Change back to red when released
tween(bottomLeftButton, {
tint: 0xFF0000
}, {
duration: 100,
easing: tween.easeOut
});
};
// Create second button next to the max life button
var secondButton = LK.getAsset('ammoBox', {
width: 250,
height: 100,
anchorX: 0,
anchorY: 1
});
secondButton.tint = 0xFFFF00; // Yellow background
secondButton.x = 290; // Position next to max life button (20 + 250 + 20 space = 290)
secondButton.y = -20;
LK.gui.bottomLeft.addChild(secondButton);
// Create black outline for second button
var secondButtonOutline = LK.getAsset('ammoBox', {
width: 256,
height: 106,
anchorX: 0,
anchorY: 1
});
secondButtonOutline.tint = 0x000000; // Black outline
secondButtonOutline.x = 287; // Position outline behind second button
secondButtonOutline.y = -17;
LK.gui.bottomLeft.addChildAt(secondButtonOutline, 0); // Add behind the button
// Create FIRE RATE label for second button
var fireRateLabel = new Text2('FIRE RATE', {
size: 36,
fill: 0xFFFFFF,
font: "'Arial Black'"
});
fireRateLabel.anchor.set(0.5, 0.5);
fireRateLabel.x = 415; // Center of second button (250/2 + 290 offset for button position)
fireRateLabel.y = -70; // Center of button (100/2 + 20 offset for button position)
LK.gui.bottomLeft.addChild(fireRateLabel);
// Create price label for second button with dynamic pricing
var fireRatePrice = calculateCumulativeCost(fireRateUpgrades);
var fireRatePriceLabel = new Text2(fireRatePrice + ' Zoin' + (fireRatePrice === 1 ? '' : 's'), {
size: 24,
fill: 0xFFD700
});
fireRatePriceLabel.anchor.set(0.5, 1);
fireRatePriceLabel.x = 415; // Center of second button horizontally
fireRatePriceLabel.y = -25; // Bottom of button
LK.gui.bottomLeft.addChild(fireRatePriceLabel);
// Make second button interactive
secondButton.buttonMode = true;
// Add second button click handlers
secondButton.down = function (x, y, obj) {
var currentPrice = calculateCumulativeCost(fireRateUpgrades);
if (zoins < currentPrice) {
return; // Don't allow clicking if insufficient zoins
}
// Change to white when pressed
tween(secondButton, {
tint: 0xFFFFFF
}, {
duration: 100,
easing: tween.easeOut
});
};
secondButton.up = function (x, y, obj) {
var currentPrice = calculateCumulativeCost(fireRateUpgrades);
if (zoins < currentPrice) {
return; // Don't allow clicking if insufficient zoins
}
// Deduct cost from zoins
zoins = zoins - currentPrice;
storage.zoins = zoins; // Persist updated zoin count
zoinText.setText('Zoins: ' + zoins);
// Increment upgrade level
fireRateUpgrades++;
storage.fireRateUpgrades = fireRateUpgrades; // Persist upgrade level
// Update player fire rate
player.fireRate = 3 * Math.pow(1.1, fireRateUpgrades);
// Update price label with new cost
var newPrice = calculateCumulativeCost(fireRateUpgrades);
fireRatePriceLabel.setText(newPrice + ' Zoin' + (newPrice === 1 ? '' : 's'));
// Update powerup display
updatePowerupDisplay();
// Change back to yellow when released
tween(secondButton, {
tint: 0xFFFF00
}, {
duration: 100,
easing: tween.easeOut
});
};
// Create player name display at bottom center
var playerNameText = new Text2(currentPlayerName, {
size: 45,
fill: 0xFFFFFF
});
playerNameText.anchor.set(0.5, 0.5);
playerNameText.x = 0;
playerNameText.y = -70; // Center vertically with buttons (same as button labels)
playerNameText.buttonMode = true;
// Add click handler to randomize name
playerNameText.down = function (x, y, obj) {
// Generate new random name
var randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
var randomName = commonNames[Math.floor(Math.random() * commonNames.length)];
var newPlayerName = randomAdjective + '-' + randomName;
// Update current player name and save to storage
currentPlayerName = newPlayerName;
storage.playerName = currentPlayerName;
// Update display text
playerNameText.setText(currentPlayerName);
// Visual feedback - flash effect
tween(playerNameText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(playerNameText, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeOut
});
}
});
};
LK.gui.bottom.addChild(playerNameText);
// Create powerup display container
var powerupDisplay = new Container();
powerupDisplay.x = 1024; // Center horizontally
powerupDisplay.y = 280; // Below leaderboard area
game.addChild(powerupDisplay);
// Powerup display background removed
// Triple Shot powerup icon and level
var tripleShotIcon = new TripleShot();
tripleShotIcon.x = -540;
tripleShotIcon.y = 0;
tripleShotIcon.visible = false;
powerupDisplay.addChild(tripleShotIcon);
var tripleShotLevelText = new Text2('Lv.1', {
size: 72,
fill: 0xFFFFFF
});
tripleShotLevelText.anchor.set(0.5, 0.5);
tripleShotLevelText.x = -540;
tripleShotLevelText.y = 75;
tripleShotLevelText.visible = false;
powerupDisplay.addChild(tripleShotLevelText);
// Power Ammo display
var powerAmmoIcon = new PowerShotPowerUp();
powerAmmoIcon.x = -180;
powerAmmoIcon.y = 0;
powerAmmoIcon.visible = false;
powerupDisplay.addChild(powerAmmoIcon);
var powerAmmoCountText = new Text2('x0', {
size: 72,
fill: 0xFFD700
});
powerAmmoCountText.anchor.set(0.5, 0.5);
powerAmmoCountText.x = -180;
powerAmmoCountText.y = 75;
powerAmmoCountText.visible = false;
powerupDisplay.addChild(powerAmmoCountText);
// Update function for powerup display
function updatePowerupDisplay() {
// Update Triple Shot display
if (hasTripleShot) {
tripleShotIcon.visible = true;
tripleShotLevelText.visible = true;
tripleShotLevelText.setText('Lv.' + tripleShotLevel);
} else {
tripleShotIcon.visible = false;
tripleShotLevelText.visible = false;
}
// Update Power Ammo display
if (powerAmmo > 0) {
powerAmmoIcon.visible = true;
powerAmmoCountText.visible = true;
powerAmmoCountText.setText('x' + powerAmmo);
} else {
powerAmmoIcon.visible = false;
powerAmmoCountText.visible = false;
}
}
// Start music
LK.playMusic('battleMusic');
; ===================================================================
--- original.js
+++ change.js
@@ -432,9 +432,9 @@
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 1024;
-healthBarBg.y = -35;
+healthBarBg.y = 100;
game.addChildAt(healthBarBg, 1);
// Health bar fill
var healthBarFill = LK.getAsset('ammoBox', {
width: 1382.25,