/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var AmmoUI = Container.expand(function () {
var self = Container.call(this);
var ammoGraphics = self.attachAsset('ammo', {
anchorX: 0.5,
anchorY: 0.5
});
var ammoText = new Text2('0', {
size: 40,
fill: 0xFFFFFF
});
ammoText.anchor.set(0.5, 0.5);
ammoText.y = 60;
self.addChild(ammoText);
self.update = function (currentAmmo, maxAmmo) {
ammoText.setText(currentAmmo + '/' + maxAmmo);
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 1; // Default damage, will be set by player
self.update = function () {
self.y -= self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.speed = 3;
self.scoreValue = 10;
self.movePattern = 'direct'; // Can be 'direct', 'zigzag', 'circular'
self.moveCounter = 0;
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
return true; // Enemy is destroyed
}
// Flash white when hit
LK.effects.flashObject(self, 0xffffff, 200);
return false;
};
self.setType = function (type) {
if (type === 'basic') {
enemyGraphics.tint = 0xe74c3c; // Red
self.health = 1;
self.speed = 3;
self.scoreValue = 10;
self.movePattern = 'direct';
} else if (type === 'fast') {
enemyGraphics.tint = 0xe67e22; // Orange
self.health = 1;
self.speed = 6;
self.scoreValue = 15;
self.movePattern = 'zigzag';
} else if (type === 'tank') {
enemyGraphics.tint = 0x7f8c8d; // Gray
self.health = 3;
self.speed = 2;
self.scoreValue = 25;
self.movePattern = 'direct';
// Make it bigger
enemyGraphics.scale.set(2.0, 2.0);
}
};
self.update = function () {
self.moveCounter++;
if (self.movePattern === 'direct') {
self.y += self.speed;
} else if (self.movePattern === 'zigzag') {
self.y += self.speed;
self.x += Math.sin(self.moveCounter * 0.1) * 3;
} else if (self.movePattern === 'circular') {
self.y += self.speed * 0.8;
self.x += Math.sin(self.moveCounter * 0.05) * 4;
}
};
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.ammo = 30;
self.maxAmmo = 30;
self.fireRate = 10;
self.damage = 1;
self.lastFired = 0;
self.inFrenzyMode = false;
self.frenzyTimeLeft = 0;
self.frenzyDuration = 300; // Initial frenzy duration (5 seconds at 60fps)
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
}
// Flash red when taking damage
LK.effects.flashObject(self, 0xff0000, 300);
};
self.shoot = function () {
if (LK.ticks - self.lastFired < 60 / self.fireRate) {
return null;
}
if (self.ammo <= 0 && !self.inFrenzyMode) {
return null;
}
if (!self.inFrenzyMode) {
self.ammo--;
}
self.lastFired = LK.ticks;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y - 40;
bullet.damage = self.damage;
LK.getSound('shoot').play();
return bullet;
};
self.activateFrenzy = function () {
self.inFrenzyMode = true;
self.frenzyTimeLeft = self.frenzyDuration;
playerGraphics.tint = 0xf39c12; // Change color to orange in frenzy mode
};
self.reloadAmmo = function (amount) {
self.ammo = Math.min(self.maxAmmo, self.ammo + amount);
};
self.update = function () {
if (self.inFrenzyMode) {
self.frenzyTimeLeft--;
if (self.frenzyTimeLeft <= 0) {
self.inFrenzyMode = false;
playerGraphics.tint = 0xffffff; // Reset color
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'ammo'; // Can be 'ammo', 'frenzy', 'health'
self.speed = 2;
self.setType = function (newType) {
self.type = newType;
if (newType === 'ammo') {
powerupGraphics.tint = 0x2ecc71; // Green
} else if (newType === 'frenzy') {
powerupGraphics.tint = 0xf39c12; // Orange
} else if (newType === 'health') {
powerupGraphics.tint = 0x3498db; // Blue
}
};
self.update = function () {
self.y += self.speed;
// Slight wobble effect
self.x += Math.sin(LK.ticks * 0.05) * 0.5;
};
return self;
});
var Upgrade = Container.expand(function () {
var self = Container.call(this);
var upgradeGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeGraphics.tint = 0xFFD700; // Gold color for upgrades
// Text to display upgrade info
var nameText = new Text2('', {
size: 30,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.y = -50;
self.addChild(nameText);
var levelText = new Text2('', {
size: 24,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.y = -20;
self.addChild(levelText);
var costText = new Text2('', {
size: 24,
fill: 0xFFD700
});
costText.anchor.set(0.5, 0);
costText.y = 10;
self.addChild(costText);
self.type = '';
self.level = 1;
self.maxLevel = 5;
self.cost = 100;
self.name = '';
self.description = '';
self.setType = function (type) {
self.type = type;
if (type === 'fireRate') {
self.name = 'Fire Rate';
self.description = 'Increase firing speed';
upgradeGraphics.tint = 0xFF5500; // Orange-red
} else if (type === 'damage') {
self.name = 'Damage';
self.description = 'Increase bullet damage';
upgradeGraphics.tint = 0xFF0000; // Red
} else if (type === 'maxAmmo') {
self.name = 'Max Ammo';
self.description = 'Increase ammo capacity';
upgradeGraphics.tint = 0x00FF00; // Green
} else if (type === 'frenzyTime') {
self.name = 'Frenzy Time';
self.description = 'Extend frenzy duration';
upgradeGraphics.tint = 0xFFFF00; // Yellow
}
self.updateText();
};
self.updateText = function () {
nameText.setText(self.name);
levelText.setText('Level: ' + self.level + '/' + self.maxLevel);
costText.setText(self.cost + ' pts');
};
self.purchase = function () {
if (self.level >= self.maxLevel) {
return false;
}
self.level++;
self.cost = Math.floor(self.cost * 1.5);
self.updateText();
return true;
};
self.down = function (x, y, obj) {
// Handle clicking on this upgrade
LK.effects.flashObject(self, 0xFFFFFF, 200);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state variables
var player;
var bullets = [];
var enemies = [];
var powerups = [];
var score = 0;
var wave = 1;
var enemiesKilled = 0;
var enemiesThisWave = 10;
var enemySpawnRate = 120; // Frames between enemy spawns
var lastEnemySpawn = 0;
var isGameOver = false;
var isPaused = false;
var showingUpgradeMenu = false;
var upgrades = [];
// UI elements
var scoreText;
var waveText;
var healthBar;
var ammoUI;
var frenzyMeter;
var upgradeButton;
var upgradeMenu;
// Game boundaries
var gameBounds = {
left: 100,
right: 2048 - 100,
top: 100,
bottom: 2732 - 200
};
// Initialize player
function initPlayer() {
player = new Player();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
}
// Initialize UI
function initUI() {
// Score display
scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 120;
scoreText.y = 50;
LK.gui.addChild(scoreText);
// Wave display
waveText = new Text2('Wave 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 120;
waveText.y = 50;
LK.gui.addChild(waveText);
// Health bar
healthBar = game.addChild(LK.getAsset('player', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.3,
x: 120,
y: 130,
tint: 0x3498db
}));
// Ammo UI
ammoUI = new AmmoUI();
ammoUI.x = 200;
ammoUI.y = 200;
game.addChild(ammoUI);
ammoUI.update(player.ammo, player.maxAmmo); // Initialize with current ammo values
// Ammo Bar
var ammoBarBackground = game.addChild(LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.3,
x: 120,
y: 200,
tint: 0x333333
}));
ammoBar = game.addChild(LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.3,
x: 120,
y: 200,
tint: 0x2ecc71
}));
// Frenzy indicator
frenzyMeter = game.addChild(LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 150,
y: 150,
alpha: 0.3,
tint: 0xf39c12
}));
// Upgrade button
upgradeButton = new Text2('UPGRADES', {
size: 50,
fill: 0xFFD700
});
upgradeButton.anchor.set(0.5, 0.5);
upgradeButton.x = 2048 - 200;
upgradeButton.y = 250;
game.addChild(upgradeButton);
// Upgrade menu container (initially hidden)
upgradeMenu = new Container();
upgradeMenu.visible = false;
game.addChild(upgradeMenu);
// Semi-transparent background
var menuBg = LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0,
scaleX: 20,
scaleY: 25,
tint: 0x000000,
alpha: 0.7
});
upgradeMenu.addChild(menuBg);
// Menu title
var menuTitle = new Text2('UPGRADES', {
size: 80,
fill: 0xFFD700
});
menuTitle.anchor.set(0.5, 0);
menuTitle.x = 2048 / 2;
menuTitle.y = 100;
upgradeMenu.addChild(menuTitle);
// Close button
var closeButton = new Text2('CLOSE', {
size: 60,
fill: 0xFFFFFF
});
closeButton.anchor.set(0.5, 0.5);
closeButton.x = 2048 / 2;
closeButton.y = 2732 - 200;
closeButton.down = function () {
toggleUpgradeMenu();
};
upgradeMenu.addChild(closeButton);
// Create upgrade options
initUpgrades();
}
// Initialize upgrades
function initUpgrades() {
// Clear previous upgrades if any
for (var i = 0; i < upgrades.length; i++) {
upgrades[i].destroy();
}
upgrades = [];
// Create new upgrades
var upgradeTypes = ['fireRate', 'damage', 'maxAmmo', 'frenzyTime'];
var baseX = 2048 / 2;
var baseY = 600;
var spacing = 300;
for (var i = 0; i < upgradeTypes.length; i++) {
var upgrade = new Upgrade();
upgrade.setType(upgradeTypes[i]);
upgrade.x = baseX;
upgrade.y = baseY + i * spacing;
// Load saved level if exists
var upgradeKey = 'upgrade_' + upgradeTypes[i];
var savedLevel = storage[upgradeKey];
if (savedLevel !== undefined) {
upgrade.level = savedLevel;
upgrade.cost = Math.floor(100 * Math.pow(1.5, upgrade.level - 1));
upgrade.updateText();
}
upgrade.down = function () {
if (score >= this.cost && this.level < this.maxLevel) {
score -= this.cost;
scoreText.setText('Score: ' + score);
this.purchase();
// Apply upgrade effect
applyUpgrade(this.type, this.level);
// Save upgrade level
var upgradeKey = 'upgrade_' + this.type;
storage[upgradeKey] = this.level;
// Flash to indicate purchase
LK.effects.flashObject(this, 0x00FF00, 300);
} else {
// Flash red if can't afford
LK.effects.flashObject(this, 0xFF0000, 300);
}
};
upgradeMenu.addChild(upgrade);
upgrades.push(upgrade);
}
// Apply saved upgrades to player
for (var i = 0; i < upgrades.length; i++) {
applyUpgrade(upgrades[i].type, upgrades[i].level);
}
}
// Apply upgrade effects to player
function applyUpgrade(type, level) {
if (!player) {
return;
}
var baseValue, increment;
if (type === 'fireRate') {
baseValue = 10;
increment = 2;
player.fireRate = baseValue + (level - 1) * increment;
} else if (type === 'damage') {
baseValue = 1;
increment = 0.5;
player.damage = baseValue + (level - 1) * increment;
} else if (type === 'maxAmmo') {
baseValue = 30;
increment = 10;
var newMaxAmmo = baseValue + (level - 1) * increment;
player.ammo += newMaxAmmo - player.maxAmmo;
player.maxAmmo = newMaxAmmo;
} else if (type === 'frenzyTime') {
baseValue = 300;
increment = 60;
player.frenzyDuration = baseValue + (level - 1) * increment;
}
}
// Toggle upgrade menu visibility
function toggleUpgradeMenu() {
showingUpgradeMenu = !showingUpgradeMenu;
upgradeMenu.visible = showingUpgradeMenu;
}
// Spawn enemy
function spawnEnemy() {
var enemy = new Enemy();
// Randomize initial position (along top of screen)
enemy.x = gameBounds.left + Math.random() * (gameBounds.right - gameBounds.left);
enemy.y = -50;
// Determine enemy type based on wave and randomness
var rand = Math.random();
if (wave < 3) {
enemy.setType('basic');
} else if (wave < 5) {
if (rand < 0.7) {
enemy.setType('basic');
} else if (rand < 0.9) {
enemy.setType('fast');
} else {
enemy.setType('tank');
}
} else {
if (rand < 0.5) {
enemy.setType('basic');
} else if (rand < 0.8) {
enemy.setType('fast');
} else {
enemy.setType('tank');
}
}
game.addChild(enemy);
enemies.push(enemy);
}
// Spawn powerup
function spawnPowerup(x, y) {
// 20% chance to spawn a powerup when enemy dies
if (Math.random() > 0.2) {
return;
}
var powerup = new PowerUp();
powerup.x = x;
powerup.y = y;
// Determine powerup type
var rand = Math.random();
if (rand < 0.6) {
powerup.setType('ammo');
} else if (rand < 0.9) {
powerup.setType('health');
} else {
powerup.setType('frenzy');
}
game.addChild(powerup);
powerups.push(powerup);
}
// Handle player shooting
function handlePlayerShoot() {
var bullet = player.shoot();
if (bullet) {
game.addChild(bullet);
bullets.push(bullet);
}
}
// Update game state
function updateGameState() {
// Update player
player.update();
ammoUI.update(player.ammo, player.maxAmmo);
// Update health bar
healthBar.scale.x = player.health / 100 * 3;
// Update ammo bar
ammoBar.scale.x = player.ammo / player.maxAmmo * 3;
// Update frenzy meter
if (player.inFrenzyMode) {
frenzyMeter.alpha = 0.5 + Math.sin(LK.ticks * 0.2) * 0.5;
} else {
frenzyMeter.alpha = 0.3;
}
// Spawn enemies
if (LK.ticks - lastEnemySpawn >= enemySpawnRate && enemies.length < enemiesThisWave) {
spawnEnemy();
lastEnemySpawn = LK.ticks;
}
// Next wave logic
if (enemiesKilled >= enemiesThisWave) {
wave++;
enemiesKilled = 0;
enemiesThisWave = Math.min(50, 10 + wave * 5);
enemySpawnRate = Math.max(30, 120 - wave * 10);
waveText.setText('Wave ' + wave);
// Flash screen green for new wave
LK.effects.flashScreen(0x2ecc71, 500);
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
// Remove bullets that go off screen
if (bullet.y < -50) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check for bullet collisions with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Enemy takes damage
var destroyed = enemy.takeDamage(bullet.damage);
if (destroyed) {
// Add score
score += enemy.scoreValue;
scoreText.setText('Score: ' + score);
// Increment kill counter
enemiesKilled++;
// Spawn powerup
spawnPowerup(enemy.x, enemy.y);
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
}
// Remove bullet
bullet.destroy();
bullets.splice(i, 1);
// Play hit sound
LK.getSound('enemyHit').play();
// Break since bullet is gone
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Remove enemies that go off bottom of screen
if (enemy.y > gameBounds.bottom + 50) {
// Player takes damage when enemy passes
player.takeDamage(10);
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
// Play hit sound
LK.getSound('playerHit').play();
continue;
}
// Check collision with player
if (enemy.intersects(player)) {
// Player takes damage
player.takeDamage(20);
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
// Play hit sound
LK.getSound('playerHit').play();
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
powerup.update();
// Remove powerups that go off bottom of screen
if (powerup.y > gameBounds.bottom + 50) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collision with player
if (powerup.intersects(player)) {
if (powerup.type === 'ammo') {
player.reloadAmmo(15);
} else if (powerup.type === 'health') {
player.health = Math.min(100, player.health + 20);
} else if (powerup.type === 'frenzy') {
player.activateFrenzy();
}
// Play powerup sound
LK.getSound('powerupCollect').play();
// Remove powerup
powerup.destroy();
powerups.splice(i, 1);
}
}
// Check for game over
if (player.health <= 0 && !isGameOver) {
isGameOver = true;
LK.showGameOver();
}
// Auto-shoot in frenzy mode
if (player.inFrenzyMode && LK.ticks % 5 === 0) {
handlePlayerShoot();
}
}
// Initialize game
function initGame() {
// Reset game state
bullets = [];
enemies = [];
powerups = [];
showingUpgradeMenu = false;
score = 0;
wave = 1;
enemiesKilled = 0;
enemiesThisWave = 10;
enemySpawnRate = 120;
lastEnemySpawn = 0;
isGameOver = false;
// Set score
LK.setScore(0);
// Init player and UI
initPlayer();
initUI();
// Play music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
}
// Initialize the game
initGame();
// Touch/mouse handlers for player movement
var isDragging = false;
game.down = function (x, y, obj) {
// Check if upgrade button was clicked
if (obj.target === upgradeButton) {
toggleUpgradeMenu();
return;
}
// If upgrade menu is showing, don't process player movement/shooting
if (showingUpgradeMenu) {
return;
}
isDragging = true;
player.x = x;
// Constrain player to game bounds
player.x = Math.max(gameBounds.left, Math.min(gameBounds.right, player.x));
// Shoot on tap
handlePlayerShoot();
};
game.up = function (x, y, obj) {
isDragging = false;
};
game.move = function (x, y, obj) {
if (isDragging && !showingUpgradeMenu) {
player.x = x;
// Constrain player to game bounds
player.x = Math.max(gameBounds.left, Math.min(gameBounds.right, player.x));
}
};
// Main game loop
game.update = function () {
if (!isGameOver && !showingUpgradeMenu) {
updateGameState();
}
// Update score
LK.setScore(score);
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var AmmoUI = Container.expand(function () {
var self = Container.call(this);
var ammoGraphics = self.attachAsset('ammo', {
anchorX: 0.5,
anchorY: 0.5
});
var ammoText = new Text2('0', {
size: 40,
fill: 0xFFFFFF
});
ammoText.anchor.set(0.5, 0.5);
ammoText.y = 60;
self.addChild(ammoText);
self.update = function (currentAmmo, maxAmmo) {
ammoText.setText(currentAmmo + '/' + maxAmmo);
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 1; // Default damage, will be set by player
self.update = function () {
self.y -= self.speed;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.speed = 3;
self.scoreValue = 10;
self.movePattern = 'direct'; // Can be 'direct', 'zigzag', 'circular'
self.moveCounter = 0;
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
return true; // Enemy is destroyed
}
// Flash white when hit
LK.effects.flashObject(self, 0xffffff, 200);
return false;
};
self.setType = function (type) {
if (type === 'basic') {
enemyGraphics.tint = 0xe74c3c; // Red
self.health = 1;
self.speed = 3;
self.scoreValue = 10;
self.movePattern = 'direct';
} else if (type === 'fast') {
enemyGraphics.tint = 0xe67e22; // Orange
self.health = 1;
self.speed = 6;
self.scoreValue = 15;
self.movePattern = 'zigzag';
} else if (type === 'tank') {
enemyGraphics.tint = 0x7f8c8d; // Gray
self.health = 3;
self.speed = 2;
self.scoreValue = 25;
self.movePattern = 'direct';
// Make it bigger
enemyGraphics.scale.set(2.0, 2.0);
}
};
self.update = function () {
self.moveCounter++;
if (self.movePattern === 'direct') {
self.y += self.speed;
} else if (self.movePattern === 'zigzag') {
self.y += self.speed;
self.x += Math.sin(self.moveCounter * 0.1) * 3;
} else if (self.movePattern === 'circular') {
self.y += self.speed * 0.8;
self.x += Math.sin(self.moveCounter * 0.05) * 4;
}
};
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.ammo = 30;
self.maxAmmo = 30;
self.fireRate = 10;
self.damage = 1;
self.lastFired = 0;
self.inFrenzyMode = false;
self.frenzyTimeLeft = 0;
self.frenzyDuration = 300; // Initial frenzy duration (5 seconds at 60fps)
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
}
// Flash red when taking damage
LK.effects.flashObject(self, 0xff0000, 300);
};
self.shoot = function () {
if (LK.ticks - self.lastFired < 60 / self.fireRate) {
return null;
}
if (self.ammo <= 0 && !self.inFrenzyMode) {
return null;
}
if (!self.inFrenzyMode) {
self.ammo--;
}
self.lastFired = LK.ticks;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y - 40;
bullet.damage = self.damage;
LK.getSound('shoot').play();
return bullet;
};
self.activateFrenzy = function () {
self.inFrenzyMode = true;
self.frenzyTimeLeft = self.frenzyDuration;
playerGraphics.tint = 0xf39c12; // Change color to orange in frenzy mode
};
self.reloadAmmo = function (amount) {
self.ammo = Math.min(self.maxAmmo, self.ammo + amount);
};
self.update = function () {
if (self.inFrenzyMode) {
self.frenzyTimeLeft--;
if (self.frenzyTimeLeft <= 0) {
self.inFrenzyMode = false;
playerGraphics.tint = 0xffffff; // Reset color
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'ammo'; // Can be 'ammo', 'frenzy', 'health'
self.speed = 2;
self.setType = function (newType) {
self.type = newType;
if (newType === 'ammo') {
powerupGraphics.tint = 0x2ecc71; // Green
} else if (newType === 'frenzy') {
powerupGraphics.tint = 0xf39c12; // Orange
} else if (newType === 'health') {
powerupGraphics.tint = 0x3498db; // Blue
}
};
self.update = function () {
self.y += self.speed;
// Slight wobble effect
self.x += Math.sin(LK.ticks * 0.05) * 0.5;
};
return self;
});
var Upgrade = Container.expand(function () {
var self = Container.call(this);
var upgradeGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeGraphics.tint = 0xFFD700; // Gold color for upgrades
// Text to display upgrade info
var nameText = new Text2('', {
size: 30,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.y = -50;
self.addChild(nameText);
var levelText = new Text2('', {
size: 24,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.y = -20;
self.addChild(levelText);
var costText = new Text2('', {
size: 24,
fill: 0xFFD700
});
costText.anchor.set(0.5, 0);
costText.y = 10;
self.addChild(costText);
self.type = '';
self.level = 1;
self.maxLevel = 5;
self.cost = 100;
self.name = '';
self.description = '';
self.setType = function (type) {
self.type = type;
if (type === 'fireRate') {
self.name = 'Fire Rate';
self.description = 'Increase firing speed';
upgradeGraphics.tint = 0xFF5500; // Orange-red
} else if (type === 'damage') {
self.name = 'Damage';
self.description = 'Increase bullet damage';
upgradeGraphics.tint = 0xFF0000; // Red
} else if (type === 'maxAmmo') {
self.name = 'Max Ammo';
self.description = 'Increase ammo capacity';
upgradeGraphics.tint = 0x00FF00; // Green
} else if (type === 'frenzyTime') {
self.name = 'Frenzy Time';
self.description = 'Extend frenzy duration';
upgradeGraphics.tint = 0xFFFF00; // Yellow
}
self.updateText();
};
self.updateText = function () {
nameText.setText(self.name);
levelText.setText('Level: ' + self.level + '/' + self.maxLevel);
costText.setText(self.cost + ' pts');
};
self.purchase = function () {
if (self.level >= self.maxLevel) {
return false;
}
self.level++;
self.cost = Math.floor(self.cost * 1.5);
self.updateText();
return true;
};
self.down = function (x, y, obj) {
// Handle clicking on this upgrade
LK.effects.flashObject(self, 0xFFFFFF, 200);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state variables
var player;
var bullets = [];
var enemies = [];
var powerups = [];
var score = 0;
var wave = 1;
var enemiesKilled = 0;
var enemiesThisWave = 10;
var enemySpawnRate = 120; // Frames between enemy spawns
var lastEnemySpawn = 0;
var isGameOver = false;
var isPaused = false;
var showingUpgradeMenu = false;
var upgrades = [];
// UI elements
var scoreText;
var waveText;
var healthBar;
var ammoUI;
var frenzyMeter;
var upgradeButton;
var upgradeMenu;
// Game boundaries
var gameBounds = {
left: 100,
right: 2048 - 100,
top: 100,
bottom: 2732 - 200
};
// Initialize player
function initPlayer() {
player = new Player();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
}
// Initialize UI
function initUI() {
// Score display
scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 120;
scoreText.y = 50;
LK.gui.addChild(scoreText);
// Wave display
waveText = new Text2('Wave 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 120;
waveText.y = 50;
LK.gui.addChild(waveText);
// Health bar
healthBar = game.addChild(LK.getAsset('player', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.3,
x: 120,
y: 130,
tint: 0x3498db
}));
// Ammo UI
ammoUI = new AmmoUI();
ammoUI.x = 200;
ammoUI.y = 200;
game.addChild(ammoUI);
ammoUI.update(player.ammo, player.maxAmmo); // Initialize with current ammo values
// Ammo Bar
var ammoBarBackground = game.addChild(LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.3,
x: 120,
y: 200,
tint: 0x333333
}));
ammoBar = game.addChild(LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 3,
scaleY: 0.3,
x: 120,
y: 200,
tint: 0x2ecc71
}));
// Frenzy indicator
frenzyMeter = game.addChild(LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 150,
y: 150,
alpha: 0.3,
tint: 0xf39c12
}));
// Upgrade button
upgradeButton = new Text2('UPGRADES', {
size: 50,
fill: 0xFFD700
});
upgradeButton.anchor.set(0.5, 0.5);
upgradeButton.x = 2048 - 200;
upgradeButton.y = 250;
game.addChild(upgradeButton);
// Upgrade menu container (initially hidden)
upgradeMenu = new Container();
upgradeMenu.visible = false;
game.addChild(upgradeMenu);
// Semi-transparent background
var menuBg = LK.getAsset('bullet', {
anchorX: 0,
anchorY: 0,
scaleX: 20,
scaleY: 25,
tint: 0x000000,
alpha: 0.7
});
upgradeMenu.addChild(menuBg);
// Menu title
var menuTitle = new Text2('UPGRADES', {
size: 80,
fill: 0xFFD700
});
menuTitle.anchor.set(0.5, 0);
menuTitle.x = 2048 / 2;
menuTitle.y = 100;
upgradeMenu.addChild(menuTitle);
// Close button
var closeButton = new Text2('CLOSE', {
size: 60,
fill: 0xFFFFFF
});
closeButton.anchor.set(0.5, 0.5);
closeButton.x = 2048 / 2;
closeButton.y = 2732 - 200;
closeButton.down = function () {
toggleUpgradeMenu();
};
upgradeMenu.addChild(closeButton);
// Create upgrade options
initUpgrades();
}
// Initialize upgrades
function initUpgrades() {
// Clear previous upgrades if any
for (var i = 0; i < upgrades.length; i++) {
upgrades[i].destroy();
}
upgrades = [];
// Create new upgrades
var upgradeTypes = ['fireRate', 'damage', 'maxAmmo', 'frenzyTime'];
var baseX = 2048 / 2;
var baseY = 600;
var spacing = 300;
for (var i = 0; i < upgradeTypes.length; i++) {
var upgrade = new Upgrade();
upgrade.setType(upgradeTypes[i]);
upgrade.x = baseX;
upgrade.y = baseY + i * spacing;
// Load saved level if exists
var upgradeKey = 'upgrade_' + upgradeTypes[i];
var savedLevel = storage[upgradeKey];
if (savedLevel !== undefined) {
upgrade.level = savedLevel;
upgrade.cost = Math.floor(100 * Math.pow(1.5, upgrade.level - 1));
upgrade.updateText();
}
upgrade.down = function () {
if (score >= this.cost && this.level < this.maxLevel) {
score -= this.cost;
scoreText.setText('Score: ' + score);
this.purchase();
// Apply upgrade effect
applyUpgrade(this.type, this.level);
// Save upgrade level
var upgradeKey = 'upgrade_' + this.type;
storage[upgradeKey] = this.level;
// Flash to indicate purchase
LK.effects.flashObject(this, 0x00FF00, 300);
} else {
// Flash red if can't afford
LK.effects.flashObject(this, 0xFF0000, 300);
}
};
upgradeMenu.addChild(upgrade);
upgrades.push(upgrade);
}
// Apply saved upgrades to player
for (var i = 0; i < upgrades.length; i++) {
applyUpgrade(upgrades[i].type, upgrades[i].level);
}
}
// Apply upgrade effects to player
function applyUpgrade(type, level) {
if (!player) {
return;
}
var baseValue, increment;
if (type === 'fireRate') {
baseValue = 10;
increment = 2;
player.fireRate = baseValue + (level - 1) * increment;
} else if (type === 'damage') {
baseValue = 1;
increment = 0.5;
player.damage = baseValue + (level - 1) * increment;
} else if (type === 'maxAmmo') {
baseValue = 30;
increment = 10;
var newMaxAmmo = baseValue + (level - 1) * increment;
player.ammo += newMaxAmmo - player.maxAmmo;
player.maxAmmo = newMaxAmmo;
} else if (type === 'frenzyTime') {
baseValue = 300;
increment = 60;
player.frenzyDuration = baseValue + (level - 1) * increment;
}
}
// Toggle upgrade menu visibility
function toggleUpgradeMenu() {
showingUpgradeMenu = !showingUpgradeMenu;
upgradeMenu.visible = showingUpgradeMenu;
}
// Spawn enemy
function spawnEnemy() {
var enemy = new Enemy();
// Randomize initial position (along top of screen)
enemy.x = gameBounds.left + Math.random() * (gameBounds.right - gameBounds.left);
enemy.y = -50;
// Determine enemy type based on wave and randomness
var rand = Math.random();
if (wave < 3) {
enemy.setType('basic');
} else if (wave < 5) {
if (rand < 0.7) {
enemy.setType('basic');
} else if (rand < 0.9) {
enemy.setType('fast');
} else {
enemy.setType('tank');
}
} else {
if (rand < 0.5) {
enemy.setType('basic');
} else if (rand < 0.8) {
enemy.setType('fast');
} else {
enemy.setType('tank');
}
}
game.addChild(enemy);
enemies.push(enemy);
}
// Spawn powerup
function spawnPowerup(x, y) {
// 20% chance to spawn a powerup when enemy dies
if (Math.random() > 0.2) {
return;
}
var powerup = new PowerUp();
powerup.x = x;
powerup.y = y;
// Determine powerup type
var rand = Math.random();
if (rand < 0.6) {
powerup.setType('ammo');
} else if (rand < 0.9) {
powerup.setType('health');
} else {
powerup.setType('frenzy');
}
game.addChild(powerup);
powerups.push(powerup);
}
// Handle player shooting
function handlePlayerShoot() {
var bullet = player.shoot();
if (bullet) {
game.addChild(bullet);
bullets.push(bullet);
}
}
// Update game state
function updateGameState() {
// Update player
player.update();
ammoUI.update(player.ammo, player.maxAmmo);
// Update health bar
healthBar.scale.x = player.health / 100 * 3;
// Update ammo bar
ammoBar.scale.x = player.ammo / player.maxAmmo * 3;
// Update frenzy meter
if (player.inFrenzyMode) {
frenzyMeter.alpha = 0.5 + Math.sin(LK.ticks * 0.2) * 0.5;
} else {
frenzyMeter.alpha = 0.3;
}
// Spawn enemies
if (LK.ticks - lastEnemySpawn >= enemySpawnRate && enemies.length < enemiesThisWave) {
spawnEnemy();
lastEnemySpawn = LK.ticks;
}
// Next wave logic
if (enemiesKilled >= enemiesThisWave) {
wave++;
enemiesKilled = 0;
enemiesThisWave = Math.min(50, 10 + wave * 5);
enemySpawnRate = Math.max(30, 120 - wave * 10);
waveText.setText('Wave ' + wave);
// Flash screen green for new wave
LK.effects.flashScreen(0x2ecc71, 500);
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
// Remove bullets that go off screen
if (bullet.y < -50) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check for bullet collisions with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Enemy takes damage
var destroyed = enemy.takeDamage(bullet.damage);
if (destroyed) {
// Add score
score += enemy.scoreValue;
scoreText.setText('Score: ' + score);
// Increment kill counter
enemiesKilled++;
// Spawn powerup
spawnPowerup(enemy.x, enemy.y);
// Remove enemy
enemy.destroy();
enemies.splice(j, 1);
}
// Remove bullet
bullet.destroy();
bullets.splice(i, 1);
// Play hit sound
LK.getSound('enemyHit').play();
// Break since bullet is gone
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Remove enemies that go off bottom of screen
if (enemy.y > gameBounds.bottom + 50) {
// Player takes damage when enemy passes
player.takeDamage(10);
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
// Play hit sound
LK.getSound('playerHit').play();
continue;
}
// Check collision with player
if (enemy.intersects(player)) {
// Player takes damage
player.takeDamage(20);
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
// Play hit sound
LK.getSound('playerHit').play();
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
powerup.update();
// Remove powerups that go off bottom of screen
if (powerup.y > gameBounds.bottom + 50) {
powerup.destroy();
powerups.splice(i, 1);
continue;
}
// Check collision with player
if (powerup.intersects(player)) {
if (powerup.type === 'ammo') {
player.reloadAmmo(15);
} else if (powerup.type === 'health') {
player.health = Math.min(100, player.health + 20);
} else if (powerup.type === 'frenzy') {
player.activateFrenzy();
}
// Play powerup sound
LK.getSound('powerupCollect').play();
// Remove powerup
powerup.destroy();
powerups.splice(i, 1);
}
}
// Check for game over
if (player.health <= 0 && !isGameOver) {
isGameOver = true;
LK.showGameOver();
}
// Auto-shoot in frenzy mode
if (player.inFrenzyMode && LK.ticks % 5 === 0) {
handlePlayerShoot();
}
}
// Initialize game
function initGame() {
// Reset game state
bullets = [];
enemies = [];
powerups = [];
showingUpgradeMenu = false;
score = 0;
wave = 1;
enemiesKilled = 0;
enemiesThisWave = 10;
enemySpawnRate = 120;
lastEnemySpawn = 0;
isGameOver = false;
// Set score
LK.setScore(0);
// Init player and UI
initPlayer();
initUI();
// Play music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
}
// Initialize the game
initGame();
// Touch/mouse handlers for player movement
var isDragging = false;
game.down = function (x, y, obj) {
// Check if upgrade button was clicked
if (obj.target === upgradeButton) {
toggleUpgradeMenu();
return;
}
// If upgrade menu is showing, don't process player movement/shooting
if (showingUpgradeMenu) {
return;
}
isDragging = true;
player.x = x;
// Constrain player to game bounds
player.x = Math.max(gameBounds.left, Math.min(gameBounds.right, player.x));
// Shoot on tap
handlePlayerShoot();
};
game.up = function (x, y, obj) {
isDragging = false;
};
game.move = function (x, y, obj) {
if (isDragging && !showingUpgradeMenu) {
player.x = x;
// Constrain player to game bounds
player.x = Math.max(gameBounds.left, Math.min(gameBounds.right, player.x));
}
};
// Main game loop
game.update = function () {
if (!isGameOver && !showingUpgradeMenu) {
updateGameState();
}
// Update score
LK.setScore(score);
};