/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('attackEffect', { anchorX: 0.5, anchorY: 0.5 }); bulletGraphics.scaleX = 0.5; bulletGraphics.scaleY = 0.5; // Set bullet speed based on boost status self.speed = bulletSpeedBoostTimer > 0 ? baseBulletSpeed * 1.5 : baseBulletSpeed; self.targetX = 0; self.targetY = 0; self.damage = 0; self.isPlayerBullet = true; self.targetEnemy = null; // Specific enemy this bullet is targeting self.update = function () { // Check for direct collision with enemies/player regardless of distance if (self.isPlayerBullet) { // Only check collision with the specific target enemy if (self.targetEnemy && !self.targetEnemy.isRespawning && !self.targetEnemy.destroyed && self.intersects(self.targetEnemy)) { self.targetEnemy.takeDamage(self.damage); self.destroy(); return; } } else { // Enemy bullet hitting player if (!player.isRespawning && self.intersects(player)) { player.takeDamage(self.damage); self.destroy(); return; } } var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If reached target position, destroy bullet if (distance < self.speed) { self.destroy(); return; } // Move towards target var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; // Remove if out of bounds if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.destroy(); } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -60 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -60 }); self.maxHealth = 30; // Reduced health for faster kills like Vampire Survivors self.health = 30; self.speed = 2; self.attackRange = 120; self.attackDamage = 8; // Higher damage but less health overall self.attackCooldown = 0; self.isRespawning = false; self.respawnTimer = 0; self.aiTimer = 0; self.spawnImmunityTimer = 0; // Prevents attacking immediately after spawn self.targetX = 0; self.targetY = 0; self.takeDamage = function (damage) { if (self.isRespawning || self.health <= 0) return; // Don't take damage if already dead // Vampire Survivors style damage - each hit does significant damage but not instant kill self.health -= damage; if (self.health < 0) self.health = 0; self.updateHealthBar(); // Flash red when taking damage tween(enemyGraphics, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(enemyGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } }); if (self.health <= 0) { self.die(); } LK.getSound('hit').play(); }; self.updateHealthBar = function () { var healthPercent = self.health / self.maxHealth; healthBar.scaleX = healthPercent; if (healthPercent > 0.6) { healthBar.tint = 0x00FF00; } else if (healthPercent > 0.3) { healthBar.tint = 0xFFFF00; } else { healthBar.tint = 0xFF0000; } }; self.die = function () { self.isRespawning = true; self.respawnTimer = 180; // 3 seconds at 60fps enemyGraphics.visible = false; healthBar.visible = false; healthBarBg.visible = false; // Move enemy far off-screen during respawn to prevent collision issues self.x = -1000; self.y = -1000; // Increment killed count and spawn new enemy only if under limit enemiesKilled++; score++; scoreText.setText('Score: ' + score); if (enemies.length < maxEnemies) { spawnNewEnemy(); } }; self.respawn = function () { self.health = self.maxHealth; // Set position first before making visible self.x = 300 + Math.random() * 1400; self.y = 400 + Math.random() * 1800; // Reset spawn immunity self.spawnImmunityTimer = 60; // 1 second of spawn immunity // Then update state and make visible self.isRespawning = false; self.respawnTimer = 0; enemyGraphics.visible = true; enemyGraphics.alpha = 1; healthBar.visible = true; healthBarBg.visible = true; self.updateHealthBar(); LK.getSound('respawn').play(); }; self.attack = function () { if (self.attackCooldown > 0 || self.isRespawning || self.spawnImmunityTimer > 0) return; self.attackCooldown = 120; // 2 second cooldown if (!player.isRespawning) { // Create bullet projectile towards player var bullet = game.addChild(new Bullet()); bullet.x = self.x; bullet.y = self.y; bullet.targetX = player.x; bullet.targetY = player.y; bullet.damage = self.attackDamage; bullet.isPlayerBullet = false; bullets.push(bullet); } // Create attack effect var attackEffect = LK.getAsset('attackEffect', { anchorX: 0.5, anchorY: 0.5 }); attackEffect.x = self.x; attackEffect.y = self.y; attackEffect.alpha = 0.7; attackEffect.tint = 0xFF4444; attackEffect.scaleX = 0.8; attackEffect.scaleY = 0.8; game.addChild(attackEffect); // Scale and fade out attack effect tween(attackEffect, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 200, onFinish: function onFinish() { attackEffect.destroy(); } }); LK.getSound('attack').play(); }; self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } if (self.spawnImmunityTimer > 0) { self.spawnImmunityTimer--; // Flash during spawn immunity period if (self.spawnImmunityTimer % 10 < 5) { enemyGraphics.alpha = 0.7; } else { enemyGraphics.alpha = 1; } } if (self.isRespawning) { self.respawnTimer--; if (self.respawnTimer <= 0) { self.respawn(); } return; } // Chase player AI - move towards player and attack when close if (!player.isRespawning) { var dx = player.x - self.x; var dy = player.y - self.y; var playerDistance = Math.sqrt(dx * dx + dy * dy); // Move towards player with speed multiplier var currentSpeed = self.speed * enemySpeedMultiplier; if (playerDistance > currentSpeed) { // Create a formation around the player instead of direct chase // Update AI timer for random movement changes self.aiTimer++; if (self.aiTimer >= 60) { // Change direction every second self.aiTimer = 0; // Calculate a preferred distance from player (spread enemies out) var preferredDistance = 180 + Math.random() * 120; // 180-300 pixels from player // If too close to preferred distance, move in a circular pattern if (playerDistance < preferredDistance + 50) { // Move in circular pattern around player var circleAngle = Math.atan2(dy, dx) + (Math.random() - 0.5) * Math.PI; // Add large random offset var moveX = Math.cos(circleAngle) * currentSpeed * 0.7; // Slower circular movement var moveY = Math.sin(circleAngle) * currentSpeed * 0.7; } else { // Generate random offset angle (-60 to +60 degrees) for wider spread var offsetAngle = (Math.random() - 0.5) * Math.PI / 1.5; // ±60 degrees in radians var baseAngle = Math.atan2(dy, dx); var newAngle = baseAngle + offsetAngle; var moveX = Math.cos(newAngle) * currentSpeed * 0.8; // Slightly slower approach var moveY = Math.sin(newAngle) * currentSpeed * 0.8; } } else { // Use stored movement direction with slight randomness var moveX = dx / playerDistance * currentSpeed * 0.9; var moveY = dy / playerDistance * currentSpeed * 0.9; } self.x += moveX; self.y += moveY; } // Attack if close enough to player if (playerDistance <= self.attackRange) { self.attack(); } } // Check collision with other enemies and push away if overlapping for (var e = 0; e < enemies.length; e++) { var otherEnemy = enemies[e]; if (otherEnemy !== self && !otherEnemy.isRespawning && !self.isRespawning) { var dx = otherEnemy.x - self.x; var dy = otherEnemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var minDistance = 120; // Reduced minimum distance to allow enemies closer together if (distance < minDistance && distance > 0) { // Calculate push direction (away from other enemy) with reduced force var pushStrength = (minDistance - distance) * 1.2; // Reduced push force var pushX = -(dx / distance) * pushStrength; var pushY = -(dy / distance) * pushStrength; // Apply moderate push force to separate enemies self.x += pushX; self.y += pushY; // Also apply counter-force to the other enemy for better separation otherEnemy.x += dx / distance * pushStrength * 0.5; otherEnemy.y += dy / distance * pushStrength * 0.5; } } } // Keep enemy in bounds if (self.x < 150) self.x = 150; if (self.x > 1898) self.x = 1898; if (self.y < 150) self.y = 150; if (self.y > 2582) self.y = 2582; }; return self; }); var HealthPack = Container.expand(function () { var self = Container.call(this); var healthPackGraphics = self.attachAsset('healthPack', { anchorX: 0.5, anchorY: 0.5 }); self.healAmount = 25; // Heal amount (50% of max health for new system) self.lifetime = 600; // 10 seconds at 60fps // Pulse animation for visibility var pulseDirection = 1; var pulseSpeed = 0.02; self.update = function () { // Pulse animation healthPackGraphics.scaleX += pulseDirection * pulseSpeed; healthPackGraphics.scaleY += pulseDirection * pulseSpeed; if (healthPackGraphics.scaleX > 1.3 || healthPackGraphics.scaleX < 0.8) { pulseDirection *= -1; } // Reduce lifetime self.lifetime--; if (self.lifetime <= 0) { self.destroy(); } // Check collision with player if (!player.isRespawning && self.intersects(player)) { // Heal player var oldHealth = player.health; player.health = Math.min(player.maxHealth, player.health + self.healAmount); player.updateHealthBar(); // Give immunity for 2 seconds player.immunityTimer = 120; // Increase bullet speed for 3 seconds bulletSpeedBoostTimer = 180; // 3 seconds at 60fps // Visual effect tween(healthPackGraphics, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); LK.getSound('heal').play(); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); var healthBarBg = self.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -60 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -60 }); self.maxHealth = 50; // Reduced health like Vampire Survivors self.health = 50; self.speed = 8; self.attackRange = 100; self.attackDamage = 15; // Balanced damage for new health system self.attackCooldown = 0; self.isRespawning = false; self.respawnTimer = 0; self.immunityTimer = 0; // Immunity period after respawn self.takeDamage = function (damage, source) { if (self.isRespawning || self.immunityTimer > 0) return; // Prevent damage from explosions and enemy spawning if (source === 'explosion' || source === 'spawn') { return; } self.health -= damage; if (self.health < 0) self.health = 0; self.updateHealthBar(); // Flash red when taking damage tween(playerGraphics, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(playerGraphics, { tint: 0xFFFFFF }, { duration: 200 }); } }); if (self.health <= 0) { self.die(); } LK.getSound('hit').play(); }; self.updateHealthBar = function () { var healthPercent = self.health / self.maxHealth; healthBar.scaleX = healthPercent; if (healthPercent > 0.6) { healthBar.tint = 0x00FF00; } else if (healthPercent > 0.3) { healthBar.tint = 0xFFFF00; } else { healthBar.tint = 0xFF0000; } }; self.die = function () { // Show game over screen which will reset the game LK.showGameOver(); }; self.respawn = function () { self.health = self.maxHealth; self.isRespawning = false; self.respawnTimer = 0; playerGraphics.alpha = 1; healthBar.visible = true; healthBarBg.visible = true; self.updateHealthBar(); // Respawn at center position since map is reset self.x = 500; self.y = 1000; // Add brief immunity period after respawn self.immunityTimer = 180; // 3 seconds of immunity // Spawn initial enemies after map reset for (var i = 0; i < 3; i++) { spawnNewEnemy(); } LK.getSound('respawn').play(); }; // Vampire Survivors style automatic shooting - no manual attack method needed self.update = function () { if (self.attackCooldown > 0) { self.attackCooldown--; } if (self.immunityTimer > 0) { self.immunityTimer--; // Flash during immunity period if (self.immunityTimer % 10 < 5) { playerGraphics.alpha = 0.5; } else { playerGraphics.alpha = 1; } } if (self.isRespawning) { self.respawnTimer--; if (self.respawnTimer <= 0) { self.respawn(); } return; } // Vampire Survivors style automatic continuous shooting if (self.attackCooldown <= 0) { // Find closest enemy within range var closestEnemy = null; var closestDistance = Infinity; var maxRange = 400; // Maximum shooting range for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (!enemy.isRespawning) { var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2)); if (distance < closestDistance && distance <= maxRange) { closestDistance = distance; closestEnemy = enemy; } } } if (closestEnemy) { // Set attack cooldown based on fire rate var baseFireRate = 30; // Base frames between shots (faster than before) var fireRateReduction = Math.min(fireRateBoostLevel * 2, baseFireRate * 0.4); self.attackCooldown = baseFireRate - fireRateReduction; // Create bullet projectile var bullet = game.addChild(new Bullet()); bullet.x = self.x; bullet.y = self.y; bullet.targetX = closestEnemy.x; bullet.targetY = closestEnemy.y; bullet.damage = self.attackDamage; bullet.isPlayerBullet = true; bullet.targetEnemy = closestEnemy; bullets.push(bullet); // Create muzzle flash effect var attackEffect = LK.getAsset('attackEffect', { anchorX: 0.5, anchorY: 0.5 }); attackEffect.x = self.x; attackEffect.y = self.y; attackEffect.alpha = 0.7; attackEffect.tint = 0x4499FF; attackEffect.scaleX = 0.6; attackEffect.scaleY = 0.6; game.addChild(attackEffect); // Scale and fade out attack effect tween(attackEffect, { scaleX: 1.0, scaleY: 1.0, alpha: 0 }, { duration: 150, onFinish: function onFinish() { attackEffect.destroy(); } }); LK.getSound('attack').play(); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2C3E50 }); /**** * Game Code ****/ // Create arena background var arena = game.addChild(LK.getAsset('arena', { anchorX: 0.5, anchorY: 0.5 })); arena.x = 2048 / 2; arena.y = 2732 / 2; // Game variables var player; var enemies = []; var bullets = []; var moveUp = false; var moveDown = false; var moveLeft = false; var moveRight = false; var targetX = 0; var targetY = 0; var isMovingToTarget = false; var enemiesKilled = 0; var score = 0; var baseFireRate = 30; // Base attack cooldown - Vampire Survivors style faster firing var currentFireRate = 30; var fireRateBoostLevel = 0; // Tracks fire rate improvements var maxEnemies = 8; // Base maximum number of enemies allowed var enemySpeedMultiplier = 1.0; // Gradually increases enemy speed var explosionRadius = 150; // Radius for chain explosions var difficultyLevel = 0; // Current difficulty level var lastDifficultyScore = 0; // Score at last difficulty increase var healthPacks = []; var lastHealthPackSpawn = 0; var bulletSpeedBoostTimer = 0; // Timer for bullet speed boost var baseBulletSpeed = 15; // Base bullet speed var enemySpawnTimer = 0; // Timer for continuous enemy spawning var baseSpawnInterval = 120; // Base spawn interval (2 seconds at 60fps) var nextHealthRestoreScore = 1000; // Score threshold for next health restore var hasRestoredHealthAtThreshold = false; // Track if health was restored at current threshold // Start main music LK.playMusic('Main'); // Initialize music state tracking game.main1Playing = false; game.main3Playing = false; game.randomMusicPlaying = false; // Create player player = game.addChild(new Player()); player.x = 500; player.y = 1000; // Function to spawn new enemy from off-screen edges (Vampire Survivors style) function spawnNewEnemy() { var enemy = game.addChild(new Enemy()); // Choose random edge to spawn from (off-screen) var spawnSide = Math.floor(Math.random() * 4); // 0=top, 1=right, 2=bottom, 3=left var spawnOffset = Math.random(); // Random position along edge (0% to 100%) // Spawn outside visible area for true Vampire Survivors effect var offScreenDistance = 100; // Distance outside visible area switch (spawnSide) { case 0: // Top edge - spawn above screen enemy.x = spawnOffset * 2048; enemy.y = -offScreenDistance; break; case 1: // Right edge - spawn to the right of screen enemy.x = 2048 + offScreenDistance; enemy.y = spawnOffset * 2732; break; case 2: // Bottom edge - spawn below screen enemy.x = spawnOffset * 2048; enemy.y = 2732 + offScreenDistance; break; case 3: // Left edge - spawn to the left of screen enemy.x = -offScreenDistance; enemy.y = spawnOffset * 2732; break; } // Give new enemies a brief spawn immunity to prevent instant collision damage enemy.spawnImmunityTimer = 60; // 1 second of spawn immunity enemy.targetX = enemy.x; enemy.targetY = enemy.y; enemies.push(enemy); } // Initial enemies will be spawned by the continuous spawning system // No need for initial spawn - let the timer handle it // UI Elements var instructionText = new Text2('Tap to move - Auto attack enemies nearby', { size: 40, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0); LK.gui.top.addChild(instructionText); instructionText.y = 100; // Score display var scoreText = new Text2('Score: 0', { size: 50, fill: 0xFFD700 }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.y = 150; var dragNode = null; var lastTouchTime = 0; // Touch controls game.down = function (x, y, obj) { if (player.isRespawning) return; // Keep target position in bounds targetX = Math.max(150, Math.min(1898, x)); targetY = Math.max(150, Math.min(2582, y)); // Stop any current movement tween tween.stop(player, { x: true, y: true }); // Calculate distance for duration (closer = faster) var distance = Math.sqrt(Math.pow(targetX - player.x, 2) + Math.pow(targetY - player.y, 2)); var duration = Math.max(200, Math.min(1000, distance * 2)); // 200ms to 1000ms based on distance isMovingToTarget = true; // Tween player to target position tween(player, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeOut, onFinish: function onFinish() { isMovingToTarget = false; } }); }; game.move = function (x, y, obj) { // No drag movement needed - player moves via tween to clicked position }; game.up = function (x, y, obj) { // No action needed - player auto-attacks when moving }; // Main game loop game.update = function () { // Vampire Survivors style continuous enemy spawning enemySpawnTimer++; var currentSpawnInterval = Math.max(30, baseSpawnInterval - Math.floor(score / 50) * 10); // Faster spawning as score increases if (enemySpawnTimer >= currentSpawnInterval && enemies.length < maxEnemies + difficultyLevel) { spawnNewEnemy(); enemySpawnTimer = 0; // Reset spawn timer } // Music transitions based on score if (score >= 100 && score < 500) { // Play main1 music from score 100 to 500 if (!game.main1Playing) { LK.stopMusic(); LK.playMusic('main1'); game.main1Playing = true; } } else if (score >= 500 && score < 1500) { // Play main2 music from score 500 to 1500 if (game.main1Playing) { LK.stopMusic(); LK.playMusic('main2'); game.main1Playing = false; } } else if (score >= 1500 && score < 3000) { // Play main3 music from score 1500 to 3000 if (game.main1Playing || !game.main3Playing) { LK.stopMusic(); LK.playMusic('main3'); game.main1Playing = false; game.main3Playing = true; } } else if (score >= 3000) { // Random music rotation every 2000 points after 3000 var musicThreshold = 3000 + Math.floor((score - 3000) / 2000) * 2000; if (score >= musicThreshold && score < musicThreshold + 50 && !game.randomMusicPlaying) { // Choose random music var musicTracks = ['main1', 'Main', 'main2', 'main3']; var randomTrack = musicTracks[Math.floor(Math.random() * musicTracks.length)]; LK.stopMusic(); LK.playMusic(randomTrack); game.main1Playing = false; game.main3Playing = false; game.randomMusicPlaying = true; } else if (score >= musicThreshold + 50) { game.randomMusicPlaying = false; } } // Update bullet speed boost timer if (bulletSpeedBoostTimer > 0) { bulletSpeedBoostTimer--; } // Check for fire rate boost every 100 kills var newFireRateBoostLevel = Math.floor(enemiesKilled / 100); if (newFireRateBoostLevel > fireRateBoostLevel) { fireRateBoostLevel = newFireRateBoostLevel; // Visual feedback for fire rate increase LK.effects.flashScreen(0x0099FF, 300); // Blue flash to indicate fire rate boost } // Check for difficulty increase every 25 points if (score - lastDifficultyScore >= 25) { difficultyLevel++; lastDifficultyScore = score; // Systematic enemy speed progression based on score ranges if (score <= 100) { // Early game: minimal speed increase enemySpeedMultiplier = 1.0 + score * 0.005; // Max 1.5x at score 100 } else if (score <= 200) { // Mid game: moderate increase enemySpeedMultiplier = 1.5 + (score - 100) * 0.003; // 1.5x to 1.8x } else if (score <= 400) { // Late game: controlled increase enemySpeedMultiplier = 1.8 + (score - 200) * 0.002; // 1.8x to 2.2x } else { // End game: capped maximum speed enemySpeedMultiplier = Math.min(2.5, 2.2 + (score - 400) * 0.001); // Max 2.5x speed } // Spawn additional enemies based on difficulty level var enemiesToSpawn = Math.min(2, difficultyLevel); // Spawn 1-2 enemies per difficulty increase for (var d = 0; d < enemiesToSpawn && enemies.length < maxEnemies + difficultyLevel; d++) { spawnNewEnemy(); } // Visual feedback for difficulty increase LK.effects.flashScreen(0x00FF00, 500); // Green flash to indicate difficulty increase } // Health restore system - restore health at score milestones (1000, 2000, 3000, etc.) if (score >= nextHealthRestoreScore && !hasRestoredHealthAtThreshold) { // Restore player health to full player.health = player.maxHealth; player.updateHealthBar(); // Set flag to prevent multiple restores at same threshold hasRestoredHealthAtThreshold = true; // Set next threshold nextHealthRestoreScore += 1000; // Visual feedback for health restore LK.effects.flashScreen(0x00FF99, 800); // Green-cyan flash for health restore } // Reset health restore flag when score increases past threshold if (score >= nextHealthRestoreScore - 1000 && score < nextHealthRestoreScore) { hasRestoredHealthAtThreshold = false; } // Spawn health pack when player health is low (below 25%) if (player.health < player.maxHealth * 0.25 && LK.ticks - lastHealthPackSpawn > 300 && // 5 second cooldown healthPacks.length === 0) { // Only one health pack at a time var healthPack = game.addChild(new HealthPack()); // Spawn at random location healthPack.x = 300 + Math.random() * 1400; healthPack.y = 400 + Math.random() * 1800; healthPacks.push(healthPack); lastHealthPackSpawn = LK.ticks; } // Update all game objects player.update(); for (var i = 0; i < enemies.length; i++) { enemies[i].update(); } // Update health packs and clean up destroyed ones for (var i = healthPacks.length - 1; i >= 0; i--) { var healthPack = healthPacks[i]; if (healthPack.destroyed) { healthPacks.splice(i, 1); } else { healthPack.update(); } } // Update bullets and clean up destroyed ones for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.destroyed === true || !bullet.parent) { bullets.splice(i, 1); } else { bullet.update(); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('attackEffect', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphics.scaleX = 0.5;
bulletGraphics.scaleY = 0.5;
// Set bullet speed based on boost status
self.speed = bulletSpeedBoostTimer > 0 ? baseBulletSpeed * 1.5 : baseBulletSpeed;
self.targetX = 0;
self.targetY = 0;
self.damage = 0;
self.isPlayerBullet = true;
self.targetEnemy = null; // Specific enemy this bullet is targeting
self.update = function () {
// Check for direct collision with enemies/player regardless of distance
if (self.isPlayerBullet) {
// Only check collision with the specific target enemy
if (self.targetEnemy && !self.targetEnemy.isRespawning && !self.targetEnemy.destroyed && self.intersects(self.targetEnemy)) {
self.targetEnemy.takeDamage(self.damage);
self.destroy();
return;
}
} else {
// Enemy bullet hitting player
if (!player.isRespawning && self.intersects(player)) {
player.takeDamage(self.damage);
self.destroy();
return;
}
}
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If reached target position, destroy bullet
if (distance < self.speed) {
self.destroy();
return;
}
// Move towards target
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
self.x += moveX;
self.y += moveY;
// Remove if out of bounds
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.destroy();
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -60
});
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -60
});
self.maxHealth = 30; // Reduced health for faster kills like Vampire Survivors
self.health = 30;
self.speed = 2;
self.attackRange = 120;
self.attackDamage = 8; // Higher damage but less health overall
self.attackCooldown = 0;
self.isRespawning = false;
self.respawnTimer = 0;
self.aiTimer = 0;
self.spawnImmunityTimer = 0; // Prevents attacking immediately after spawn
self.targetX = 0;
self.targetY = 0;
self.takeDamage = function (damage) {
if (self.isRespawning || self.health <= 0) return; // Don't take damage if already dead
// Vampire Survivors style damage - each hit does significant damage but not instant kill
self.health -= damage;
if (self.health < 0) self.health = 0;
self.updateHealthBar();
// Flash red when taking damage
tween(enemyGraphics, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
if (self.health <= 0) {
self.die();
}
LK.getSound('hit').play();
};
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xFFFF00;
} else {
healthBar.tint = 0xFF0000;
}
};
self.die = function () {
self.isRespawning = true;
self.respawnTimer = 180; // 3 seconds at 60fps
enemyGraphics.visible = false;
healthBar.visible = false;
healthBarBg.visible = false;
// Move enemy far off-screen during respawn to prevent collision issues
self.x = -1000;
self.y = -1000;
// Increment killed count and spawn new enemy only if under limit
enemiesKilled++;
score++;
scoreText.setText('Score: ' + score);
if (enemies.length < maxEnemies) {
spawnNewEnemy();
}
};
self.respawn = function () {
self.health = self.maxHealth;
// Set position first before making visible
self.x = 300 + Math.random() * 1400;
self.y = 400 + Math.random() * 1800;
// Reset spawn immunity
self.spawnImmunityTimer = 60; // 1 second of spawn immunity
// Then update state and make visible
self.isRespawning = false;
self.respawnTimer = 0;
enemyGraphics.visible = true;
enemyGraphics.alpha = 1;
healthBar.visible = true;
healthBarBg.visible = true;
self.updateHealthBar();
LK.getSound('respawn').play();
};
self.attack = function () {
if (self.attackCooldown > 0 || self.isRespawning || self.spawnImmunityTimer > 0) return;
self.attackCooldown = 120; // 2 second cooldown
if (!player.isRespawning) {
// Create bullet projectile towards player
var bullet = game.addChild(new Bullet());
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = player.x;
bullet.targetY = player.y;
bullet.damage = self.attackDamage;
bullet.isPlayerBullet = false;
bullets.push(bullet);
}
// Create attack effect
var attackEffect = LK.getAsset('attackEffect', {
anchorX: 0.5,
anchorY: 0.5
});
attackEffect.x = self.x;
attackEffect.y = self.y;
attackEffect.alpha = 0.7;
attackEffect.tint = 0xFF4444;
attackEffect.scaleX = 0.8;
attackEffect.scaleY = 0.8;
game.addChild(attackEffect);
// Scale and fade out attack effect
tween(attackEffect, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
attackEffect.destroy();
}
});
LK.getSound('attack').play();
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.spawnImmunityTimer > 0) {
self.spawnImmunityTimer--;
// Flash during spawn immunity period
if (self.spawnImmunityTimer % 10 < 5) {
enemyGraphics.alpha = 0.7;
} else {
enemyGraphics.alpha = 1;
}
}
if (self.isRespawning) {
self.respawnTimer--;
if (self.respawnTimer <= 0) {
self.respawn();
}
return;
}
// Chase player AI - move towards player and attack when close
if (!player.isRespawning) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var playerDistance = Math.sqrt(dx * dx + dy * dy);
// Move towards player with speed multiplier
var currentSpeed = self.speed * enemySpeedMultiplier;
if (playerDistance > currentSpeed) {
// Create a formation around the player instead of direct chase
// Update AI timer for random movement changes
self.aiTimer++;
if (self.aiTimer >= 60) {
// Change direction every second
self.aiTimer = 0;
// Calculate a preferred distance from player (spread enemies out)
var preferredDistance = 180 + Math.random() * 120; // 180-300 pixels from player
// If too close to preferred distance, move in a circular pattern
if (playerDistance < preferredDistance + 50) {
// Move in circular pattern around player
var circleAngle = Math.atan2(dy, dx) + (Math.random() - 0.5) * Math.PI; // Add large random offset
var moveX = Math.cos(circleAngle) * currentSpeed * 0.7; // Slower circular movement
var moveY = Math.sin(circleAngle) * currentSpeed * 0.7;
} else {
// Generate random offset angle (-60 to +60 degrees) for wider spread
var offsetAngle = (Math.random() - 0.5) * Math.PI / 1.5; // ±60 degrees in radians
var baseAngle = Math.atan2(dy, dx);
var newAngle = baseAngle + offsetAngle;
var moveX = Math.cos(newAngle) * currentSpeed * 0.8; // Slightly slower approach
var moveY = Math.sin(newAngle) * currentSpeed * 0.8;
}
} else {
// Use stored movement direction with slight randomness
var moveX = dx / playerDistance * currentSpeed * 0.9;
var moveY = dy / playerDistance * currentSpeed * 0.9;
}
self.x += moveX;
self.y += moveY;
}
// Attack if close enough to player
if (playerDistance <= self.attackRange) {
self.attack();
}
}
// Check collision with other enemies and push away if overlapping
for (var e = 0; e < enemies.length; e++) {
var otherEnemy = enemies[e];
if (otherEnemy !== self && !otherEnemy.isRespawning && !self.isRespawning) {
var dx = otherEnemy.x - self.x;
var dy = otherEnemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDistance = 120; // Reduced minimum distance to allow enemies closer together
if (distance < minDistance && distance > 0) {
// Calculate push direction (away from other enemy) with reduced force
var pushStrength = (minDistance - distance) * 1.2; // Reduced push force
var pushX = -(dx / distance) * pushStrength;
var pushY = -(dy / distance) * pushStrength;
// Apply moderate push force to separate enemies
self.x += pushX;
self.y += pushY;
// Also apply counter-force to the other enemy for better separation
otherEnemy.x += dx / distance * pushStrength * 0.5;
otherEnemy.y += dy / distance * pushStrength * 0.5;
}
}
}
// Keep enemy in bounds
if (self.x < 150) self.x = 150;
if (self.x > 1898) self.x = 1898;
if (self.y < 150) self.y = 150;
if (self.y > 2582) self.y = 2582;
};
return self;
});
var HealthPack = Container.expand(function () {
var self = Container.call(this);
var healthPackGraphics = self.attachAsset('healthPack', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 25; // Heal amount (50% of max health for new system)
self.lifetime = 600; // 10 seconds at 60fps
// Pulse animation for visibility
var pulseDirection = 1;
var pulseSpeed = 0.02;
self.update = function () {
// Pulse animation
healthPackGraphics.scaleX += pulseDirection * pulseSpeed;
healthPackGraphics.scaleY += pulseDirection * pulseSpeed;
if (healthPackGraphics.scaleX > 1.3 || healthPackGraphics.scaleX < 0.8) {
pulseDirection *= -1;
}
// Reduce lifetime
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
}
// Check collision with player
if (!player.isRespawning && self.intersects(player)) {
// Heal player
var oldHealth = player.health;
player.health = Math.min(player.maxHealth, player.health + self.healAmount);
player.updateHealthBar();
// Give immunity for 2 seconds
player.immunityTimer = 120;
// Increase bullet speed for 3 seconds
bulletSpeedBoostTimer = 180; // 3 seconds at 60fps
// Visual effect
tween(healthPackGraphics, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
LK.getSound('heal').play();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -60
});
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -60
});
self.maxHealth = 50; // Reduced health like Vampire Survivors
self.health = 50;
self.speed = 8;
self.attackRange = 100;
self.attackDamage = 15; // Balanced damage for new health system
self.attackCooldown = 0;
self.isRespawning = false;
self.respawnTimer = 0;
self.immunityTimer = 0; // Immunity period after respawn
self.takeDamage = function (damage, source) {
if (self.isRespawning || self.immunityTimer > 0) return;
// Prevent damage from explosions and enemy spawning
if (source === 'explosion' || source === 'spawn') {
return;
}
self.health -= damage;
if (self.health < 0) self.health = 0;
self.updateHealthBar();
// Flash red when taking damage
tween(playerGraphics, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(playerGraphics, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
if (self.health <= 0) {
self.die();
}
LK.getSound('hit').play();
};
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xFFFF00;
} else {
healthBar.tint = 0xFF0000;
}
};
self.die = function () {
// Show game over screen which will reset the game
LK.showGameOver();
};
self.respawn = function () {
self.health = self.maxHealth;
self.isRespawning = false;
self.respawnTimer = 0;
playerGraphics.alpha = 1;
healthBar.visible = true;
healthBarBg.visible = true;
self.updateHealthBar();
// Respawn at center position since map is reset
self.x = 500;
self.y = 1000;
// Add brief immunity period after respawn
self.immunityTimer = 180; // 3 seconds of immunity
// Spawn initial enemies after map reset
for (var i = 0; i < 3; i++) {
spawnNewEnemy();
}
LK.getSound('respawn').play();
};
// Vampire Survivors style automatic shooting - no manual attack method needed
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.immunityTimer > 0) {
self.immunityTimer--;
// Flash during immunity period
if (self.immunityTimer % 10 < 5) {
playerGraphics.alpha = 0.5;
} else {
playerGraphics.alpha = 1;
}
}
if (self.isRespawning) {
self.respawnTimer--;
if (self.respawnTimer <= 0) {
self.respawn();
}
return;
}
// Vampire Survivors style automatic continuous shooting
if (self.attackCooldown <= 0) {
// Find closest enemy within range
var closestEnemy = null;
var closestDistance = Infinity;
var maxRange = 400; // Maximum shooting range
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (!enemy.isRespawning) {
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance < closestDistance && distance <= maxRange) {
closestDistance = distance;
closestEnemy = enemy;
}
}
}
if (closestEnemy) {
// Set attack cooldown based on fire rate
var baseFireRate = 30; // Base frames between shots (faster than before)
var fireRateReduction = Math.min(fireRateBoostLevel * 2, baseFireRate * 0.4);
self.attackCooldown = baseFireRate - fireRateReduction;
// Create bullet projectile
var bullet = game.addChild(new Bullet());
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = closestEnemy.x;
bullet.targetY = closestEnemy.y;
bullet.damage = self.attackDamage;
bullet.isPlayerBullet = true;
bullet.targetEnemy = closestEnemy;
bullets.push(bullet);
// Create muzzle flash effect
var attackEffect = LK.getAsset('attackEffect', {
anchorX: 0.5,
anchorY: 0.5
});
attackEffect.x = self.x;
attackEffect.y = self.y;
attackEffect.alpha = 0.7;
attackEffect.tint = 0x4499FF;
attackEffect.scaleX = 0.6;
attackEffect.scaleY = 0.6;
game.addChild(attackEffect);
// Scale and fade out attack effect
tween(attackEffect, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0
}, {
duration: 150,
onFinish: function onFinish() {
attackEffect.destroy();
}
});
LK.getSound('attack').play();
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2C3E50
});
/****
* Game Code
****/
// Create arena background
var arena = game.addChild(LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5
}));
arena.x = 2048 / 2;
arena.y = 2732 / 2;
// Game variables
var player;
var enemies = [];
var bullets = [];
var moveUp = false;
var moveDown = false;
var moveLeft = false;
var moveRight = false;
var targetX = 0;
var targetY = 0;
var isMovingToTarget = false;
var enemiesKilled = 0;
var score = 0;
var baseFireRate = 30; // Base attack cooldown - Vampire Survivors style faster firing
var currentFireRate = 30;
var fireRateBoostLevel = 0; // Tracks fire rate improvements
var maxEnemies = 8; // Base maximum number of enemies allowed
var enemySpeedMultiplier = 1.0; // Gradually increases enemy speed
var explosionRadius = 150; // Radius for chain explosions
var difficultyLevel = 0; // Current difficulty level
var lastDifficultyScore = 0; // Score at last difficulty increase
var healthPacks = [];
var lastHealthPackSpawn = 0;
var bulletSpeedBoostTimer = 0; // Timer for bullet speed boost
var baseBulletSpeed = 15; // Base bullet speed
var enemySpawnTimer = 0; // Timer for continuous enemy spawning
var baseSpawnInterval = 120; // Base spawn interval (2 seconds at 60fps)
var nextHealthRestoreScore = 1000; // Score threshold for next health restore
var hasRestoredHealthAtThreshold = false; // Track if health was restored at current threshold
// Start main music
LK.playMusic('Main');
// Initialize music state tracking
game.main1Playing = false;
game.main3Playing = false;
game.randomMusicPlaying = false;
// Create player
player = game.addChild(new Player());
player.x = 500;
player.y = 1000;
// Function to spawn new enemy from off-screen edges (Vampire Survivors style)
function spawnNewEnemy() {
var enemy = game.addChild(new Enemy());
// Choose random edge to spawn from (off-screen)
var spawnSide = Math.floor(Math.random() * 4); // 0=top, 1=right, 2=bottom, 3=left
var spawnOffset = Math.random(); // Random position along edge (0% to 100%)
// Spawn outside visible area for true Vampire Survivors effect
var offScreenDistance = 100; // Distance outside visible area
switch (spawnSide) {
case 0:
// Top edge - spawn above screen
enemy.x = spawnOffset * 2048;
enemy.y = -offScreenDistance;
break;
case 1:
// Right edge - spawn to the right of screen
enemy.x = 2048 + offScreenDistance;
enemy.y = spawnOffset * 2732;
break;
case 2:
// Bottom edge - spawn below screen
enemy.x = spawnOffset * 2048;
enemy.y = 2732 + offScreenDistance;
break;
case 3:
// Left edge - spawn to the left of screen
enemy.x = -offScreenDistance;
enemy.y = spawnOffset * 2732;
break;
}
// Give new enemies a brief spawn immunity to prevent instant collision damage
enemy.spawnImmunityTimer = 60; // 1 second of spawn immunity
enemy.targetX = enemy.x;
enemy.targetY = enemy.y;
enemies.push(enemy);
}
// Initial enemies will be spawned by the continuous spawning system
// No need for initial spawn - let the timer handle it
// UI Elements
var instructionText = new Text2('Tap to move - Auto attack enemies nearby', {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
LK.gui.top.addChild(instructionText);
instructionText.y = 100;
// Score display
var scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFD700
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 150;
var dragNode = null;
var lastTouchTime = 0;
// Touch controls
game.down = function (x, y, obj) {
if (player.isRespawning) return;
// Keep target position in bounds
targetX = Math.max(150, Math.min(1898, x));
targetY = Math.max(150, Math.min(2582, y));
// Stop any current movement tween
tween.stop(player, {
x: true,
y: true
});
// Calculate distance for duration (closer = faster)
var distance = Math.sqrt(Math.pow(targetX - player.x, 2) + Math.pow(targetY - player.y, 2));
var duration = Math.max(200, Math.min(1000, distance * 2)); // 200ms to 1000ms based on distance
isMovingToTarget = true;
// Tween player to target position
tween(player, {
x: targetX,
y: targetY
}, {
duration: duration,
easing: tween.easeOut,
onFinish: function onFinish() {
isMovingToTarget = false;
}
});
};
game.move = function (x, y, obj) {
// No drag movement needed - player moves via tween to clicked position
};
game.up = function (x, y, obj) {
// No action needed - player auto-attacks when moving
};
// Main game loop
game.update = function () {
// Vampire Survivors style continuous enemy spawning
enemySpawnTimer++;
var currentSpawnInterval = Math.max(30, baseSpawnInterval - Math.floor(score / 50) * 10); // Faster spawning as score increases
if (enemySpawnTimer >= currentSpawnInterval && enemies.length < maxEnemies + difficultyLevel) {
spawnNewEnemy();
enemySpawnTimer = 0; // Reset spawn timer
}
// Music transitions based on score
if (score >= 100 && score < 500) {
// Play main1 music from score 100 to 500
if (!game.main1Playing) {
LK.stopMusic();
LK.playMusic('main1');
game.main1Playing = true;
}
} else if (score >= 500 && score < 1500) {
// Play main2 music from score 500 to 1500
if (game.main1Playing) {
LK.stopMusic();
LK.playMusic('main2');
game.main1Playing = false;
}
} else if (score >= 1500 && score < 3000) {
// Play main3 music from score 1500 to 3000
if (game.main1Playing || !game.main3Playing) {
LK.stopMusic();
LK.playMusic('main3');
game.main1Playing = false;
game.main3Playing = true;
}
} else if (score >= 3000) {
// Random music rotation every 2000 points after 3000
var musicThreshold = 3000 + Math.floor((score - 3000) / 2000) * 2000;
if (score >= musicThreshold && score < musicThreshold + 50 && !game.randomMusicPlaying) {
// Choose random music
var musicTracks = ['main1', 'Main', 'main2', 'main3'];
var randomTrack = musicTracks[Math.floor(Math.random() * musicTracks.length)];
LK.stopMusic();
LK.playMusic(randomTrack);
game.main1Playing = false;
game.main3Playing = false;
game.randomMusicPlaying = true;
} else if (score >= musicThreshold + 50) {
game.randomMusicPlaying = false;
}
}
// Update bullet speed boost timer
if (bulletSpeedBoostTimer > 0) {
bulletSpeedBoostTimer--;
}
// Check for fire rate boost every 100 kills
var newFireRateBoostLevel = Math.floor(enemiesKilled / 100);
if (newFireRateBoostLevel > fireRateBoostLevel) {
fireRateBoostLevel = newFireRateBoostLevel;
// Visual feedback for fire rate increase
LK.effects.flashScreen(0x0099FF, 300); // Blue flash to indicate fire rate boost
}
// Check for difficulty increase every 25 points
if (score - lastDifficultyScore >= 25) {
difficultyLevel++;
lastDifficultyScore = score;
// Systematic enemy speed progression based on score ranges
if (score <= 100) {
// Early game: minimal speed increase
enemySpeedMultiplier = 1.0 + score * 0.005; // Max 1.5x at score 100
} else if (score <= 200) {
// Mid game: moderate increase
enemySpeedMultiplier = 1.5 + (score - 100) * 0.003; // 1.5x to 1.8x
} else if (score <= 400) {
// Late game: controlled increase
enemySpeedMultiplier = 1.8 + (score - 200) * 0.002; // 1.8x to 2.2x
} else {
// End game: capped maximum speed
enemySpeedMultiplier = Math.min(2.5, 2.2 + (score - 400) * 0.001); // Max 2.5x speed
}
// Spawn additional enemies based on difficulty level
var enemiesToSpawn = Math.min(2, difficultyLevel); // Spawn 1-2 enemies per difficulty increase
for (var d = 0; d < enemiesToSpawn && enemies.length < maxEnemies + difficultyLevel; d++) {
spawnNewEnemy();
}
// Visual feedback for difficulty increase
LK.effects.flashScreen(0x00FF00, 500); // Green flash to indicate difficulty increase
}
// Health restore system - restore health at score milestones (1000, 2000, 3000, etc.)
if (score >= nextHealthRestoreScore && !hasRestoredHealthAtThreshold) {
// Restore player health to full
player.health = player.maxHealth;
player.updateHealthBar();
// Set flag to prevent multiple restores at same threshold
hasRestoredHealthAtThreshold = true;
// Set next threshold
nextHealthRestoreScore += 1000;
// Visual feedback for health restore
LK.effects.flashScreen(0x00FF99, 800); // Green-cyan flash for health restore
}
// Reset health restore flag when score increases past threshold
if (score >= nextHealthRestoreScore - 1000 && score < nextHealthRestoreScore) {
hasRestoredHealthAtThreshold = false;
}
// Spawn health pack when player health is low (below 25%)
if (player.health < player.maxHealth * 0.25 && LK.ticks - lastHealthPackSpawn > 300 &&
// 5 second cooldown
healthPacks.length === 0) {
// Only one health pack at a time
var healthPack = game.addChild(new HealthPack());
// Spawn at random location
healthPack.x = 300 + Math.random() * 1400;
healthPack.y = 400 + Math.random() * 1800;
healthPacks.push(healthPack);
lastHealthPackSpawn = LK.ticks;
}
// Update all game objects
player.update();
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
}
// Update health packs and clean up destroyed ones
for (var i = healthPacks.length - 1; i >= 0; i--) {
var healthPack = healthPacks[i];
if (healthPack.destroyed) {
healthPacks.splice(i, 1);
} else {
healthPack.update();
}
}
// Update bullets and clean up destroyed ones
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.destroyed === true || !bullet.parent) {
bullets.splice(i, 1);
} else {
bullet.update();
}
}
};
bullet. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Face. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
face. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
green hearth. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Grey gound . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat