/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bossEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1;
self.health = 10;
self.maxHealth = 10;
self.shootTimer = 0;
self.shootInterval = 60; // 1 second at 60fps
self.isBoss = true;
self.update = function () {
// Move toward player
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveX = dx / distance * self.speed * enemySpeedMultiplier * timeScale;
var moveY = dy / distance * self.speed * enemySpeedMultiplier * timeScale;
var newX = self.x + moveX;
var newY = self.y + moveY;
// Check collision with obstacles
var canMove = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (newX + 75 > obstacle.x - 60 && newX - 75 < obstacle.x + 60 && newY + 75 > obstacle.y - 60 && newY - 75 < obstacle.y + 60) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
}
}
// Shoot at player more frequently
self.shootTimer += timeScale;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
};
self.shoot = function () {
// Boss shoots 3 bullets in spread pattern
for (var i = -1; i <= 1; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x + i * 30;
bullet.y = self.y;
// Calculate base direction towards hero
var dx = hero.x - bullet.x;
var dy = hero.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var baseAngle = Math.atan2(dy, dx);
var spreadAngle = baseAngle + i * 0.3; // 0.3 radians spread
bullet.directionX = Math.cos(spreadAngle);
bullet.directionY = Math.sin(spreadAngle);
} else {
bullet.directionX = 0;
bullet.directionY = 1;
}
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
LK.effects.flashObject(self, 0xFFFFFF, 500);
LK.getSound('enemyHit').play();
return true; // Boss destroyed
}
// Flash red when taking damage
LK.effects.flashObject(self, 0xFF0000, 200);
return false;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.shootTimer = 0;
self.shootInterval = 120; // 2 seconds at 60fps
self.update = function () {
// Move toward player
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveX = dx / distance * self.speed * enemySpeedMultiplier * timeScale;
var moveY = dy / distance * self.speed * enemySpeedMultiplier * timeScale;
var newX = self.x + moveX;
var newY = self.y + moveY;
// Check collision with obstacles
var canMove = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (newX + 30 > obstacle.x - 60 && newX - 30 < obstacle.x + 60 && newY + 30 > obstacle.y - 60 && newY - 30 < obstacle.y + 60) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
}
}
// Shoot at player
self.shootTimer += timeScale;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction towards hero
var dx = hero.x - bullet.x;
var dy = hero.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.directionX = dx / distance;
bullet.directionY = dy / distance;
} else {
bullet.directionX = 0;
bullet.directionY = 1;
}
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
LK.effects.flashObject(self, 0xFFFFFF, 200);
LK.getSound('enemyHit').play();
return true; // Enemy destroyed
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 1;
self.directionX = 0;
self.directionY = 1;
self.update = function () {
self.x += self.directionX * self.speed * timeScale;
self.y += self.directionY * self.speed * timeScale;
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.shootTimer = 0;
self.shootInterval = 45; // Slower shooting
self.update = function () {
// Always shoot when enemies exist
if (enemies.length > 0) {
self.shootTimer += timeScale;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
}
};
self.shoot = function () {
var bullet;
if (rpgAmmo > 0) {
bullet = new RPGBullet();
rpgAmmo--;
updateRPGDisplay();
} else {
bullet = new HeroBullet();
}
bullet.x = self.x;
bullet.y = self.y - 40;
// Find nearest enemy to aim at
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Set bullet direction towards nearest enemy
if (nearestEnemy) {
var dx = nearestEnemy.x - bullet.x;
var dy = nearestEnemy.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.directionX = dx / distance;
bullet.directionY = dy / distance;
} else {
bullet.directionX = 0;
bullet.directionY = -1;
}
} else {
bullet.directionX = 0;
bullet.directionY = -1;
}
heroBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 500);
LK.getSound('playerHit').play();
// Use extra health if health drops to 20 or below and we have uses available
if (self.health <= 20 && extraHealthUses > 0) {
self.health += 50;
if (self.health > self.maxHealth) {
self.health = self.maxHealth;
}
extraHealthUses--;
updateExtraHealthDisplay();
LK.effects.flashObject(self, 0x00FF00, 800); // Green flash for healing
}
updateHealthBar();
if (self.health <= 0) {
LK.showGameOver();
}
};
return self;
});
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.damage = 1;
self.directionX = 0;
self.directionY = -1;
self.update = function () {
self.x += self.directionX * self.speed * timeScale;
self.y += self.directionY * self.speed * timeScale;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var RPGBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFF0000; // Red color for RPG
graphics.scaleX = 2;
graphics.scaleY = 2;
self.speed = 8;
self.damage = 999; // One shot kill
self.directionX = 0;
self.directionY = -1;
self.update = function () {
self.x += self.directionX * self.speed * timeScale;
self.y += self.directionY * self.speed * timeScale;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game variables
var enemies = [];
var enemyBullets = [];
var heroBullets = [];
var obstacles = [];
var timeScale = 1.0;
var isBulletTimeActive = false;
var bulletTimeEnergy = 100;
var maxBulletTimeEnergy = 100;
var bulletTimeDecayRate = 0.5;
var bulletTimeRechargeRate = 0.1;
var bulletTimeSlowFactor = 0.2;
var dragNode = null;
var enemySpawnTimer = 0;
var enemySpawnInterval = 180; // 3 seconds
var waveNumber = 1;
var enemiesPerWave = 3;
var extraHealthUses = 3;
var maxExtraHealthUses = 3;
var enemiesKilled = 0;
var bossActive = false;
var lastScoreCheck = 0;
var enemySpeedMultiplier = 1.0;
var bossesKilled = 0;
var rpgAmmo = 0;
// UI elements
var scoreTxt = new Text2('0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
var healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
var healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0
});
var bulletTimeBarBg = LK.getAsset('bulletTimeBarBg', {
anchorX: 0,
anchorY: 0
});
var bulletTimeBar = LK.getAsset('bulletTimeBar', {
anchorX: 0,
anchorY: 0
});
LK.gui.topLeft.addChild(healthBarBg);
LK.gui.topLeft.addChild(healthBar);
LK.gui.topLeft.addChild(bulletTimeBarBg);
LK.gui.topLeft.addChild(bulletTimeBar);
healthBarBg.x = 120;
healthBarBg.y = 20;
healthBar.x = 122;
healthBar.y = 22;
bulletTimeBarBg.x = 120;
bulletTimeBarBg.y = 60;
bulletTimeBar.x = 122;
bulletTimeBar.y = 62;
// Add extra health display text first
var extraHealthText = new Text2('Extra Health: 3', {
size: 40,
fill: 0x00FF00
});
extraHealthText.anchor.set(0, 0);
LK.gui.topLeft.addChild(extraHealthText);
extraHealthText.x = 120;
extraHealthText.y = 100;
// Add extra health icons after text on the right side
var extraHealthIcons = [];
for (var i = 0; i < maxExtraHealthUses; i++) {
var icon = LK.getAsset('extraHealthIcon', {
anchorX: 0.5,
anchorY: 0.5
});
LK.gui.topLeft.addChild(icon);
icon.x = 380 + i * 40;
icon.y = 115;
extraHealthIcons.push(icon);
}
// Add RPG ammo display
var rpgText = new Text2('RPG: 0', {
size: 40,
fill: 0xFF0000
});
rpgText.anchor.set(0, 0);
LK.gui.topLeft.addChild(rpgText);
rpgText.x = 120;
rpgText.y = 140;
// Add city background
var cityBg = LK.getAsset('cityBackground', {
anchorX: 0,
anchorY: 0
});
game.addChild(cityBg);
// Initialize hero
hero = game.addChild(new Hero());
hero.x = 1024;
hero.y = 2200;
// Create obstacles
for (var i = 0; i < 6; i++) {
var obstacle = new Obstacle();
obstacle.x = 400 + i % 3 * 600;
obstacle.y = 800 + Math.floor(i / 3) * 600;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Update score display
function updateScore() {
scoreTxt.setText(LK.getScore());
}
// Update health bar
function updateHealthBar() {
var healthPercent = hero.health / hero.maxHealth;
healthBar.width = 400 * healthPercent;
}
// Update bullet time bar
function updateBulletTimeBar() {
var energyPercent = bulletTimeEnergy / maxBulletTimeEnergy;
bulletTimeBar.width = 300 * energyPercent;
}
// Update extra health display
function updateExtraHealthDisplay() {
extraHealthText.setText('Extra Health: ' + extraHealthUses);
for (var i = 0; i < extraHealthIcons.length; i++) {
extraHealthIcons[i].alpha = i < extraHealthUses ? 1.0 : 0.3;
}
}
// Update RPG display
function updateRPGDisplay() {
rpgText.setText('RPG: ' + rpgAmmo);
}
// Spawn enemy
function spawnEnemy() {
var enemy = new Enemy();
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2098;
enemy.y = Math.random() * 1000 + 100;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 1000 + 100;
break;
}
// Scale enemy stats with wave number
enemy.speed = 2 + waveNumber * 0.3;
enemy.shootInterval = Math.max(60, 120 - waveNumber * 5);
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn boss enemy
function spawnBoss() {
var boss = new Boss();
boss.x = 1024; // Center horizontally
boss.y = 200; // Top of screen
enemies.push(boss);
game.addChild(boss);
bossActive = true;
}
// Spawn additional enemies based on score
function spawnScoreBasedEnemies() {
for (var i = 0; i < 2; i++) {
spawnEnemy();
}
}
// Create explosion effect
function createExplosion(x, y, isBigExplosion) {
var explosion = LK.getAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
explosion.x = x;
explosion.y = y;
explosion.tint = isBigExplosion ? 0xFFFF00 : 0xFF4400; // Yellow for big explosions, orange for normal
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
game.addChild(explosion);
// Animate explosion - scale up and fade out
var targetScale = isBigExplosion ? 5 : 3;
var duration = isBigExplosion ? 500 : 300;
tween(explosion, {
scaleX: targetScale,
scaleY: targetScale,
alpha: 0
}, {
duration: duration,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
}
// Increase enemy speed every 300 points
function checkEnemySpeedIncrease(currentScore) {
var speedLevel = Math.floor(currentScore / 300);
var newSpeedMultiplier = 1.0 + speedLevel * 0.5;
if (newSpeedMultiplier > enemySpeedMultiplier) {
enemySpeedMultiplier = newSpeedMultiplier;
}
}
// Handle bullet time activation
function activateBulletTime() {
if (bulletTimeEnergy > 0) {
isBulletTimeActive = true;
timeScale = bulletTimeSlowFactor;
}
}
// Handle bullet time deactivation
function deactivateBulletTime() {
isBulletTimeActive = false;
timeScale = 1.0;
}
// Event handlers
function handleMove(x, y, obj) {
if (dragNode) {
var newX = x;
var newY = y;
// Keep hero within bounds
newX = Math.max(40, Math.min(2008, newX));
newY = Math.max(40, Math.min(2692, newY));
// Check collision with obstacles
var canMove = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var tempHero = {
x: newX,
y: newY,
width: 80,
height: 80
};
if (newX + 40 > obstacle.x - 60 && newX - 40 < obstacle.x + 60 && newY + 40 > obstacle.y - 60 && newY - 40 < obstacle.y + 60) {
canMove = false;
break;
}
}
if (canMove) {
dragNode.x = newX;
dragNode.y = newY;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = hero;
activateBulletTime();
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
deactivateBulletTime();
};
// Main game update loop
game.update = function () {
// Update bullet time energy
if (isBulletTimeActive) {
bulletTimeEnergy -= bulletTimeDecayRate;
if (bulletTimeEnergy <= 0) {
bulletTimeEnergy = 0;
deactivateBulletTime();
}
} else {
bulletTimeEnergy += bulletTimeRechargeRate;
if (bulletTimeEnergy > maxBulletTimeEnergy) {
bulletTimeEnergy = maxBulletTimeEnergy;
}
}
updateBulletTimeBar();
// Check for score-based enemy spawning every 200 points
var currentScore = LK.getScore();
if (currentScore >= lastScoreCheck + 200) {
spawnScoreBasedEnemies();
lastScoreCheck = Math.floor(currentScore / 200) * 200;
}
// Check for enemy speed increase every 300 points
checkEnemySpeedIncrease(currentScore);
// Spawn enemies (but not when boss is active)
if (!bossActive) {
enemySpawnTimer += timeScale;
if (enemySpawnTimer >= enemySpawnInterval) {
if (enemies.length < enemiesPerWave) {
spawnEnemy();
}
enemySpawnTimer = 0;
// Start new wave if no enemies left
if (enemies.length === 0) {
waveNumber++;
enemiesPerWave = Math.min(8, 3 + Math.floor(waveNumber / 2));
enemySpawnInterval = Math.max(60, 180 - waveNumber * 10);
}
}
}
// Update hero bullets
for (var i = heroBullets.length - 1; i >= 0; i--) {
var bullet = heroBullets[i];
if (bullet.lastY === undefined) {
bullet.lastY = bullet.y;
}
// Remove bullets that go off screen
if (bullet.lastY >= -20 && bullet.y < -20) {
bullet.destroy();
heroBullets.splice(i, 1);
continue;
}
// Check collision with enemy bullets first
var hitEnemyBullet = false;
for (var k = enemyBullets.length - 1; k >= 0; k--) {
var enemyBullet = enemyBullets[k];
if (bullet.intersects(enemyBullet)) {
// Create big explosion at collision point
createExplosion((bullet.x + enemyBullet.x) / 2, (bullet.y + enemyBullet.y) / 2, true);
// Destroy both bullets
bullet.destroy();
enemyBullet.destroy();
heroBullets.splice(i, 1);
enemyBullets.splice(k, 1);
hitEnemyBullet = true;
break;
}
}
if (hitEnemyBullet) {
continue;
}
// Check collision with enemies
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Create explosion effect at enemy position
createExplosion(enemy.x, enemy.y);
if (enemy.takeDamage(bullet.damage)) {
// Enemy destroyed
enemy.destroy();
enemies.splice(j, 1);
// Award more points for boss
var points = enemy.isBoss ? 100 : 10;
LK.setScore(LK.getScore() + points);
updateScore();
// Track kills and spawn boss
if (!enemy.isBoss) {
enemiesKilled++;
if (enemiesKilled % 10 === 0 && !bossActive) {
spawnBoss();
}
} else {
bossActive = false;
bossesKilled++;
// Give RPG after every 2 bosses
if (bossesKilled % 2 === 0) {
rpgAmmo += 1;
updateRPGDisplay();
// Flash effect to show RPG gained
LK.effects.flashScreen(0xFF0000, 500);
}
}
// Refuel bullet time on enemy kill
bulletTimeEnergy += 20;
if (bulletTimeEnergy > maxBulletTimeEnergy) {
bulletTimeEnergy = maxBulletTimeEnergy;
}
}
bullet.destroy();
heroBullets.splice(i, 1);
hitEnemy = true;
break;
}
}
if (!hitEnemy) {
// Check collision with obstacles
for (var j = obstacles.length - 1; j >= 0; j--) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
heroBullets.splice(i, 1);
hitEnemy = true;
break;
}
}
}
if (!hitEnemy) {
bullet.lastY = bullet.y;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.lastY === undefined) {
bullet.lastY = bullet.y;
}
// Remove bullets that go off screen
if (bullet.lastY <= 2752 && bullet.y > 2752) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with hero
if (bullet.intersects(hero)) {
hero.takeDamage(bullet.damage);
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with obstacles
var hitObstacle = false;
for (var j = obstacles.length - 1; j >= 0; j--) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
enemyBullets.splice(i, 1);
hitObstacle = true;
break;
}
}
if (!hitObstacle) {
bullet.lastY = bullet.y;
}
}
// Check enemy collision with hero
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.intersects(hero)) {
hero.takeDamage(1);
enemy.destroy();
enemies.splice(i, 1);
}
}
};
// Initialize UI
updateScore();
updateHealthBar();
updateBulletTimeBar();
updateExtraHealthDisplay();
updateRPGDisplay(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('bossEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1;
self.health = 10;
self.maxHealth = 10;
self.shootTimer = 0;
self.shootInterval = 60; // 1 second at 60fps
self.isBoss = true;
self.update = function () {
// Move toward player
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveX = dx / distance * self.speed * enemySpeedMultiplier * timeScale;
var moveY = dy / distance * self.speed * enemySpeedMultiplier * timeScale;
var newX = self.x + moveX;
var newY = self.y + moveY;
// Check collision with obstacles
var canMove = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (newX + 75 > obstacle.x - 60 && newX - 75 < obstacle.x + 60 && newY + 75 > obstacle.y - 60 && newY - 75 < obstacle.y + 60) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
}
}
// Shoot at player more frequently
self.shootTimer += timeScale;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
};
self.shoot = function () {
// Boss shoots 3 bullets in spread pattern
for (var i = -1; i <= 1; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x + i * 30;
bullet.y = self.y;
// Calculate base direction towards hero
var dx = hero.x - bullet.x;
var dy = hero.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var baseAngle = Math.atan2(dy, dx);
var spreadAngle = baseAngle + i * 0.3; // 0.3 radians spread
bullet.directionX = Math.cos(spreadAngle);
bullet.directionY = Math.sin(spreadAngle);
} else {
bullet.directionX = 0;
bullet.directionY = 1;
}
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
LK.effects.flashObject(self, 0xFFFFFF, 500);
LK.getSound('enemyHit').play();
return true; // Boss destroyed
}
// Flash red when taking damage
LK.effects.flashObject(self, 0xFF0000, 200);
return false;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.shootTimer = 0;
self.shootInterval = 120; // 2 seconds at 60fps
self.update = function () {
// Move toward player
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveX = dx / distance * self.speed * enemySpeedMultiplier * timeScale;
var moveY = dy / distance * self.speed * enemySpeedMultiplier * timeScale;
var newX = self.x + moveX;
var newY = self.y + moveY;
// Check collision with obstacles
var canMove = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (newX + 30 > obstacle.x - 60 && newX - 30 < obstacle.x + 60 && newY + 30 > obstacle.y - 60 && newY - 30 < obstacle.y + 60) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
}
}
// Shoot at player
self.shootTimer += timeScale;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
};
self.shoot = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction towards hero
var dx = hero.x - bullet.x;
var dy = hero.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.directionX = dx / distance;
bullet.directionY = dy / distance;
} else {
bullet.directionX = 0;
bullet.directionY = 1;
}
enemyBullets.push(bullet);
game.addChild(bullet);
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
LK.effects.flashObject(self, 0xFFFFFF, 200);
LK.getSound('enemyHit').play();
return true; // Enemy destroyed
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 1;
self.directionX = 0;
self.directionY = 1;
self.update = function () {
self.x += self.directionX * self.speed * timeScale;
self.y += self.directionY * self.speed * timeScale;
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.shootTimer = 0;
self.shootInterval = 45; // Slower shooting
self.update = function () {
// Always shoot when enemies exist
if (enemies.length > 0) {
self.shootTimer += timeScale;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
}
};
self.shoot = function () {
var bullet;
if (rpgAmmo > 0) {
bullet = new RPGBullet();
rpgAmmo--;
updateRPGDisplay();
} else {
bullet = new HeroBullet();
}
bullet.x = self.x;
bullet.y = self.y - 40;
// Find nearest enemy to aim at
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Set bullet direction towards nearest enemy
if (nearestEnemy) {
var dx = nearestEnemy.x - bullet.x;
var dy = nearestEnemy.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.directionX = dx / distance;
bullet.directionY = dy / distance;
} else {
bullet.directionX = 0;
bullet.directionY = -1;
}
} else {
bullet.directionX = 0;
bullet.directionY = -1;
}
heroBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFF0000, 500);
LK.getSound('playerHit').play();
// Use extra health if health drops to 20 or below and we have uses available
if (self.health <= 20 && extraHealthUses > 0) {
self.health += 50;
if (self.health > self.maxHealth) {
self.health = self.maxHealth;
}
extraHealthUses--;
updateExtraHealthDisplay();
LK.effects.flashObject(self, 0x00FF00, 800); // Green flash for healing
}
updateHealthBar();
if (self.health <= 0) {
LK.showGameOver();
}
};
return self;
});
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.damage = 1;
self.directionX = 0;
self.directionY = -1;
self.update = function () {
self.x += self.directionX * self.speed * timeScale;
self.y += self.directionY * self.speed * timeScale;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var RPGBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = 0xFF0000; // Red color for RPG
graphics.scaleX = 2;
graphics.scaleY = 2;
self.speed = 8;
self.damage = 999; // One shot kill
self.directionX = 0;
self.directionY = -1;
self.update = function () {
self.x += self.directionX * self.speed * timeScale;
self.y += self.directionY * self.speed * timeScale;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game variables
var enemies = [];
var enemyBullets = [];
var heroBullets = [];
var obstacles = [];
var timeScale = 1.0;
var isBulletTimeActive = false;
var bulletTimeEnergy = 100;
var maxBulletTimeEnergy = 100;
var bulletTimeDecayRate = 0.5;
var bulletTimeRechargeRate = 0.1;
var bulletTimeSlowFactor = 0.2;
var dragNode = null;
var enemySpawnTimer = 0;
var enemySpawnInterval = 180; // 3 seconds
var waveNumber = 1;
var enemiesPerWave = 3;
var extraHealthUses = 3;
var maxExtraHealthUses = 3;
var enemiesKilled = 0;
var bossActive = false;
var lastScoreCheck = 0;
var enemySpeedMultiplier = 1.0;
var bossesKilled = 0;
var rpgAmmo = 0;
// UI elements
var scoreTxt = new Text2('0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50;
var healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
});
var healthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0
});
var bulletTimeBarBg = LK.getAsset('bulletTimeBarBg', {
anchorX: 0,
anchorY: 0
});
var bulletTimeBar = LK.getAsset('bulletTimeBar', {
anchorX: 0,
anchorY: 0
});
LK.gui.topLeft.addChild(healthBarBg);
LK.gui.topLeft.addChild(healthBar);
LK.gui.topLeft.addChild(bulletTimeBarBg);
LK.gui.topLeft.addChild(bulletTimeBar);
healthBarBg.x = 120;
healthBarBg.y = 20;
healthBar.x = 122;
healthBar.y = 22;
bulletTimeBarBg.x = 120;
bulletTimeBarBg.y = 60;
bulletTimeBar.x = 122;
bulletTimeBar.y = 62;
// Add extra health display text first
var extraHealthText = new Text2('Extra Health: 3', {
size: 40,
fill: 0x00FF00
});
extraHealthText.anchor.set(0, 0);
LK.gui.topLeft.addChild(extraHealthText);
extraHealthText.x = 120;
extraHealthText.y = 100;
// Add extra health icons after text on the right side
var extraHealthIcons = [];
for (var i = 0; i < maxExtraHealthUses; i++) {
var icon = LK.getAsset('extraHealthIcon', {
anchorX: 0.5,
anchorY: 0.5
});
LK.gui.topLeft.addChild(icon);
icon.x = 380 + i * 40;
icon.y = 115;
extraHealthIcons.push(icon);
}
// Add RPG ammo display
var rpgText = new Text2('RPG: 0', {
size: 40,
fill: 0xFF0000
});
rpgText.anchor.set(0, 0);
LK.gui.topLeft.addChild(rpgText);
rpgText.x = 120;
rpgText.y = 140;
// Add city background
var cityBg = LK.getAsset('cityBackground', {
anchorX: 0,
anchorY: 0
});
game.addChild(cityBg);
// Initialize hero
hero = game.addChild(new Hero());
hero.x = 1024;
hero.y = 2200;
// Create obstacles
for (var i = 0; i < 6; i++) {
var obstacle = new Obstacle();
obstacle.x = 400 + i % 3 * 600;
obstacle.y = 800 + Math.floor(i / 3) * 600;
obstacles.push(obstacle);
game.addChild(obstacle);
}
// Update score display
function updateScore() {
scoreTxt.setText(LK.getScore());
}
// Update health bar
function updateHealthBar() {
var healthPercent = hero.health / hero.maxHealth;
healthBar.width = 400 * healthPercent;
}
// Update bullet time bar
function updateBulletTimeBar() {
var energyPercent = bulletTimeEnergy / maxBulletTimeEnergy;
bulletTimeBar.width = 300 * energyPercent;
}
// Update extra health display
function updateExtraHealthDisplay() {
extraHealthText.setText('Extra Health: ' + extraHealthUses);
for (var i = 0; i < extraHealthIcons.length; i++) {
extraHealthIcons[i].alpha = i < extraHealthUses ? 1.0 : 0.3;
}
}
// Update RPG display
function updateRPGDisplay() {
rpgText.setText('RPG: ' + rpgAmmo);
}
// Spawn enemy
function spawnEnemy() {
var enemy = new Enemy();
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2098;
enemy.y = Math.random() * 1000 + 100;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 1000 + 100;
break;
}
// Scale enemy stats with wave number
enemy.speed = 2 + waveNumber * 0.3;
enemy.shootInterval = Math.max(60, 120 - waveNumber * 5);
enemies.push(enemy);
game.addChild(enemy);
}
// Spawn boss enemy
function spawnBoss() {
var boss = new Boss();
boss.x = 1024; // Center horizontally
boss.y = 200; // Top of screen
enemies.push(boss);
game.addChild(boss);
bossActive = true;
}
// Spawn additional enemies based on score
function spawnScoreBasedEnemies() {
for (var i = 0; i < 2; i++) {
spawnEnemy();
}
}
// Create explosion effect
function createExplosion(x, y, isBigExplosion) {
var explosion = LK.getAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
explosion.x = x;
explosion.y = y;
explosion.tint = isBigExplosion ? 0xFFFF00 : 0xFF4400; // Yellow for big explosions, orange for normal
explosion.scaleX = 0.1;
explosion.scaleY = 0.1;
game.addChild(explosion);
// Animate explosion - scale up and fade out
var targetScale = isBigExplosion ? 5 : 3;
var duration = isBigExplosion ? 500 : 300;
tween(explosion, {
scaleX: targetScale,
scaleY: targetScale,
alpha: 0
}, {
duration: duration,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
}
// Increase enemy speed every 300 points
function checkEnemySpeedIncrease(currentScore) {
var speedLevel = Math.floor(currentScore / 300);
var newSpeedMultiplier = 1.0 + speedLevel * 0.5;
if (newSpeedMultiplier > enemySpeedMultiplier) {
enemySpeedMultiplier = newSpeedMultiplier;
}
}
// Handle bullet time activation
function activateBulletTime() {
if (bulletTimeEnergy > 0) {
isBulletTimeActive = true;
timeScale = bulletTimeSlowFactor;
}
}
// Handle bullet time deactivation
function deactivateBulletTime() {
isBulletTimeActive = false;
timeScale = 1.0;
}
// Event handlers
function handleMove(x, y, obj) {
if (dragNode) {
var newX = x;
var newY = y;
// Keep hero within bounds
newX = Math.max(40, Math.min(2008, newX));
newY = Math.max(40, Math.min(2692, newY));
// Check collision with obstacles
var canMove = true;
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var tempHero = {
x: newX,
y: newY,
width: 80,
height: 80
};
if (newX + 40 > obstacle.x - 60 && newX - 40 < obstacle.x + 60 && newY + 40 > obstacle.y - 60 && newY - 40 < obstacle.y + 60) {
canMove = false;
break;
}
}
if (canMove) {
dragNode.x = newX;
dragNode.y = newY;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = hero;
activateBulletTime();
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
deactivateBulletTime();
};
// Main game update loop
game.update = function () {
// Update bullet time energy
if (isBulletTimeActive) {
bulletTimeEnergy -= bulletTimeDecayRate;
if (bulletTimeEnergy <= 0) {
bulletTimeEnergy = 0;
deactivateBulletTime();
}
} else {
bulletTimeEnergy += bulletTimeRechargeRate;
if (bulletTimeEnergy > maxBulletTimeEnergy) {
bulletTimeEnergy = maxBulletTimeEnergy;
}
}
updateBulletTimeBar();
// Check for score-based enemy spawning every 200 points
var currentScore = LK.getScore();
if (currentScore >= lastScoreCheck + 200) {
spawnScoreBasedEnemies();
lastScoreCheck = Math.floor(currentScore / 200) * 200;
}
// Check for enemy speed increase every 300 points
checkEnemySpeedIncrease(currentScore);
// Spawn enemies (but not when boss is active)
if (!bossActive) {
enemySpawnTimer += timeScale;
if (enemySpawnTimer >= enemySpawnInterval) {
if (enemies.length < enemiesPerWave) {
spawnEnemy();
}
enemySpawnTimer = 0;
// Start new wave if no enemies left
if (enemies.length === 0) {
waveNumber++;
enemiesPerWave = Math.min(8, 3 + Math.floor(waveNumber / 2));
enemySpawnInterval = Math.max(60, 180 - waveNumber * 10);
}
}
}
// Update hero bullets
for (var i = heroBullets.length - 1; i >= 0; i--) {
var bullet = heroBullets[i];
if (bullet.lastY === undefined) {
bullet.lastY = bullet.y;
}
// Remove bullets that go off screen
if (bullet.lastY >= -20 && bullet.y < -20) {
bullet.destroy();
heroBullets.splice(i, 1);
continue;
}
// Check collision with enemy bullets first
var hitEnemyBullet = false;
for (var k = enemyBullets.length - 1; k >= 0; k--) {
var enemyBullet = enemyBullets[k];
if (bullet.intersects(enemyBullet)) {
// Create big explosion at collision point
createExplosion((bullet.x + enemyBullet.x) / 2, (bullet.y + enemyBullet.y) / 2, true);
// Destroy both bullets
bullet.destroy();
enemyBullet.destroy();
heroBullets.splice(i, 1);
enemyBullets.splice(k, 1);
hitEnemyBullet = true;
break;
}
}
if (hitEnemyBullet) {
continue;
}
// Check collision with enemies
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
// Create explosion effect at enemy position
createExplosion(enemy.x, enemy.y);
if (enemy.takeDamage(bullet.damage)) {
// Enemy destroyed
enemy.destroy();
enemies.splice(j, 1);
// Award more points for boss
var points = enemy.isBoss ? 100 : 10;
LK.setScore(LK.getScore() + points);
updateScore();
// Track kills and spawn boss
if (!enemy.isBoss) {
enemiesKilled++;
if (enemiesKilled % 10 === 0 && !bossActive) {
spawnBoss();
}
} else {
bossActive = false;
bossesKilled++;
// Give RPG after every 2 bosses
if (bossesKilled % 2 === 0) {
rpgAmmo += 1;
updateRPGDisplay();
// Flash effect to show RPG gained
LK.effects.flashScreen(0xFF0000, 500);
}
}
// Refuel bullet time on enemy kill
bulletTimeEnergy += 20;
if (bulletTimeEnergy > maxBulletTimeEnergy) {
bulletTimeEnergy = maxBulletTimeEnergy;
}
}
bullet.destroy();
heroBullets.splice(i, 1);
hitEnemy = true;
break;
}
}
if (!hitEnemy) {
// Check collision with obstacles
for (var j = obstacles.length - 1; j >= 0; j--) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
heroBullets.splice(i, 1);
hitEnemy = true;
break;
}
}
}
if (!hitEnemy) {
bullet.lastY = bullet.y;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.lastY === undefined) {
bullet.lastY = bullet.y;
}
// Remove bullets that go off screen
if (bullet.lastY <= 2752 && bullet.y > 2752) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with hero
if (bullet.intersects(hero)) {
hero.takeDamage(bullet.damage);
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with obstacles
var hitObstacle = false;
for (var j = obstacles.length - 1; j >= 0; j--) {
var obstacle = obstacles[j];
if (bullet.intersects(obstacle)) {
bullet.destroy();
enemyBullets.splice(i, 1);
hitObstacle = true;
break;
}
}
if (!hitObstacle) {
bullet.lastY = bullet.y;
}
}
// Check enemy collision with hero
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.intersects(hero)) {
hero.takeDamage(1);
enemy.destroy();
enemies.splice(i, 1);
}
}
};
// Initialize UI
updateScore();
updateHealthBar();
updateBulletTimeBar();
updateExtraHealthDisplay();
updateRPGDisplay();
draw a man like max payne. In-Game asset. 2d. High contrast. No shadows
draw a mafia guy with gun. In-Game asset. 2d. High contrast. No shadows
draw a city road map. In-Game asset. 2d. High contrast. No shadows
draw a mafia guy like a monster with gun. In-Game asset. 2d. High contrast. No shadows
draw a skyscraper. In-Game asset. 2d. High contrast. No shadows
draw a pistol bullet 50 calibre. In-Game asset. 2d. High contrast. No shadows
draw a heart with pixel art. In-Game asset. 2d. High contrast. No shadows