/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphic = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
self.health = 10;
self.attackCooldown = 0;
self.attackPattern = 0;
self.scoreValue = 2000;
self.active = true;
self.init = function (x, y) {
self.x = x;
self.y = y;
return self;
};
self.takeDamage = function () {
if (!self.active) {
return;
}
// Boss takes 1 damage per hit, dies after 5 hits
self.health -= 1;
// Flash boss
LK.effects.flashObject(self, 0xffffff, 300);
LK.getSound('hit').play();
if (self.health <= 0) {
self.die();
} else {
// Speed up as health decreases
self.speed = 1.5 + (5 - self.health) * 0.2;
}
};
self.die = function () {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('bossDeath').play();
// Flash and fade out
LK.effects.flashObject(self, 0xffff00, 500);
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
currentWave++;
bossDefeated = true;
nextWaveTimer = 180; // 3 seconds until next wave
return self.scoreValue;
};
self.update = function () {
if (!self.active) {
return;
}
// Move toward hero but more intelligently
if (hero) {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Different movement patterns
if (self.attackPattern === 0) {
// Direct approach
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
} else if (self.attackPattern === 1) {
// Circle around
var angle = Math.atan2(dy, dx) + Math.PI / 4;
self.x += Math.cos(angle) * self.speed * 1.5;
self.y += Math.sin(angle) * self.speed * 1.5;
} else if (self.attackPattern === 2) {
// Charge attack
if (self.attackCooldown > 30) {
self.x += dx / dist * self.speed * 2.5;
self.y += dy / dist * self.speed * 2.5;
} else {
// Wait
}
}
if (hero.invulnerable <= 0 && !hero.isDashing) {
if (self.intersects(hero)) {
hero.takeDamage();
}
}
}
// Attack pattern cooldown
self.attackCooldown--;
if (self.attackCooldown <= 0) {
self.attackPattern = (self.attackPattern + 1) % 3;
self.attackCooldown = 120; // 2 seconds per pattern
}
// Keep within screen bounds
self.x = Math.max(100, Math.min(2048 - 100, self.x));
self.y = Math.max(100, Math.min(2732 - 100, self.y));
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphic = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphic.tint = 0xffffff; // White bullet for the hero
self.speed = 15;
self.damage = 1;
self.active = true;
self.init = function (x, y, direction) {
self.x = x;
self.y = y;
self.direction = direction || {
x: 0,
y: -1
}; // Default direction is up
return self;
};
self.update = function () {
if (!self.active) {
return;
}
// Move in the specified direction
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Remove if off-screen
if (self.x < -50 || self.x > 2048 + 50 || self.y < -50 || self.y > 2732 + 50) {
self.destroy();
}
};
return self;
});
var DashTrail = Container.expand(function () {
var self = Container.call(this);
var trailGraphic = self.attachAsset('dashTrail', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.init = function (x, y) {
self.x = x;
self.y = y;
self.lifetime = 20; // frames the trail will live
return self;
};
self.update = function () {
self.lifetime--;
trailGraphic.alpha = self.lifetime / 20 * 0.7;
if (self.lifetime <= 0) {
self.destroy();
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphic = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.scoreValue = 100;
self.active = true;
self.init = function (x, y) {
self.x = x;
self.y = y;
// Initialize last position for rotation tracking
self.lastX = x;
self.lastY = y;
// Set initial rotation
if (hero) {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
enemyGraphic.rotation = Math.atan2(dy, dx);
}
return self;
};
self.takeDamage = function () {
if (!self.active) {
return;
}
// Enemies die from one hit
self.health = 0;
self.die();
};
self.die = function () {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('hit').play();
// Flash, fade out, and fall animation
tween(self, {
alpha: 0,
y: self.y + 300,
// Fall down animation
rotation: Math.random() * Math.PI * 2,
// Random rotation
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
}
});
return self.scoreValue;
};
self.update = function () {
if (!self.active) {
return;
}
// Move toward hero
if (hero && !hero.isDashing) {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
// Spider walking animation
if (LK.ticks % 10 === 0) {
// Spider leg movement animation
tween(enemyGraphic, {
scaleX: 1.1,
scaleY: 0.9,
rotation: Math.atan2(dy, dx) + (Math.random() * 0.2 - 0.1)
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyGraphic, {
scaleX: 1,
scaleY: 1,
rotation: Math.atan2(dy, dx)
}, {
duration: 200,
easing: tween.easeOut
});
}
});
}
}
}
// Check for collision with hero
if (hero && !hero.isDashing && hero.invulnerable <= 0) {
if (self.intersects(hero)) {
hero.takeDamage();
}
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphic = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
// Simple click handler that does nothing (placeholder)
self.down = function (x, y, obj) {
// No auto-shoot toggle functionality
};
self.shootSpeed = 15; // Bullet speed
self.fireRate = 15; // Frames between shots
self.shootCooldown = 0;
self.health = 3;
self.invulnerable = 0;
self.combo = 0;
self.score = 0;
self.powerBoost = 1; // Base power multiplier
self.shootBullet = function (targetX, targetY) {
if (self.shootCooldown > 0) {
return;
}
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize direction
var dirX = dx / distance;
var dirY = dy / distance;
// Create bullet
var bullet = new Bullet().init(self.x, self.y, {
x: dirX,
y: dirY
});
bullet.damage = self.powerBoost; // Apply power boost to bullet damage
// Visual shooting effect
tween(heroGraphic, {
scaleX: 1.2,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(heroGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut
});
}
});
// Set cooldown
self.shootCooldown = self.fireRate;
// Add bullet to game
game.addChild(bullet);
if (!bullets) {
bullets = [];
}
bullets.push(bullet);
// Play shoot sound
LK.getSound('hit').play();
};
self.takeDamage = function () {
if (self.invulnerable > 0) {
return;
}
self.health--;
self.invulnerable = 60; // invulnerable for 1 second
self.combo = 0; // Reset combo
// Flash hero red
LK.effects.flashObject(self, 0xff0000, 500);
// Play damage sound
LK.getSound('damage').play();
if (self.health <= 0) {
LK.showGameOver();
}
};
self.addScore = function (points) {
self.combo++;
var comboMultiplier = Math.min(4, 1 + self.combo * 0.1);
var scoreToAdd = Math.floor(points * comboMultiplier);
self.score += scoreToAdd;
LK.setScore(self.score);
};
self.update = function () {
// Keep within screen bounds
self.x = Math.max(50, Math.min(2048 - 50, self.x));
self.y = Math.max(50, Math.min(2732 - 50, self.y));
// Cooldowns
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (self.invulnerable > 0) {
self.invulnerable--;
// Flicker effect during invulnerability
heroGraphic.alpha = LK.ticks % 6 < 3 ? 0.4 : 1;
} else {
// Normal appearance when not invulnerable
heroGraphic.alpha = 1;
heroGraphic.tint = 0xFFFFFF; // Normal color
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphic = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'health'; // Default type
self.active = true;
self.lifetime = 300; // 5 seconds
self.init = function (x, y, type) {
self.x = x;
self.y = y;
self.type = type || self.type;
// Set color based on type
if (self.type === 'health') {
powerUpGraphic.tint = 0x2ecc71; // Green
} else if (self.type === 'speed') {
powerUpGraphic.tint = 0x3498db; // Blue
} else if (self.type === 'power') {
powerUpGraphic.tint = 0xe74c3c; // Red
} else if (self.type === 'strength') {
powerUpGraphic.tint = 0x9b59b6; // Purple
}
return self;
};
self.collect = function () {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('powerup').play();
if (self.type === 'health' && hero.health < 3) {
hero.health++;
} else if (self.type === 'speed') {
hero.dashSpeed = 80; // Even faster dash
hero.dashDistance = 900; // Even longer dash
// Visual indicator of speed boost
tween(hero, {
alpha: 0.7
}, {
duration: 200,
onFinish: function onFinish() {
tween(hero, {
alpha: 1
}, {
duration: 200
});
}
});
// Reset after 10 seconds
LK.setTimeout(function () {
if (hero) {
hero.dashSpeed = 60;
hero.dashDistance = 700;
}
}, 10000);
} else if (self.type === 'strength') {
// Increase hero's attack strength more permanently
hero.powerBoost = Math.min(5, hero.powerBoost + 1);
// Visual indicator of strength boost
tween(hero, {
tint: 0x9b59b6
}, {
duration: 500,
onFinish: function onFinish() {
tween(hero, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
// Show strength level
var strengthTxt = new Text2('STRENGTH +' + hero.powerBoost + '!', {
size: 80,
fill: 0x9b59b6
});
strengthTxt.anchor.set(0.5, 0.5);
strengthTxt.x = 2048 / 2;
strengthTxt.y = 2732 / 2;
game.addChild(strengthTxt);
// Fade out the text
tween(strengthTxt, {
alpha: 0,
y: strengthTxt.y - 100
}, {
duration: 1500,
onFinish: function onFinish() {
strengthTxt.destroy();
}
});
} else if (self.type === 'power') {
// Clear all enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].active) {
var points = enemies[i].die();
hero.addScore(points);
}
}
// Increase hero's attack strength
hero.powerBoost = 3;
// Visual indicator of power boost
tween(hero, {
tint: 0xFF0000
}, {
duration: 300
});
// Reset after 10 seconds
LK.setTimeout(function () {
if (hero) {
hero.powerBoost = 1;
tween(hero, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
}, 10000);
}
// Flash and fade out
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
};
self.update = function () {
if (!self.active) {
return;
}
// Floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
// Rotate slowly
powerUpGraphic.rotation += 0.02;
// Check for collision with hero
if (hero && (hero.intersects(self) || hero.isDashing && self.distanceTo(hero) < 100)) {
self.collect();
}
// Expire after lifetime
self.lifetime--;
if (self.lifetime <= 0) {
tween(self, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
self.active = false;
}
};
self.distanceTo = function (target) {
var dx = target.x - self.x;
var dy = target.y - self.y;
return Math.sqrt(dx * dx + dy * dy);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
// Game state variables
var hero;
var enemies = [];
var bullets = [];
var powerUps = [];
var trails = [];
var gameTime = 0;
var spawnTimer = 0;
var bossTimer = 0;
var currentWave = 0;
var bossDefeated = false;
var nextWaveTimer = 0;
var swipeStart = {
x: 0,
y: 0
};
var isSwipeActive = false;
// UI elements
var scoreTxt;
var timeTxt;
var healthTxt;
var comboTxt;
// Game constants
var SPAWN_INTERVAL = 90; // Frames between enemy spawns
var POWER_UP_CHANCE = 0.15; // Chance to spawn power-up on enemy defeat
var BOSS_INTERVAL = 1800; // Spawn boss every 30 seconds (60fps * 30)
var MAX_ENEMIES = 15; // Maximum enemies on screen at once
// Initialize game elements
function initGame() {
// Add background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create hero
hero = new Hero();
hero.x = 2048 / 2;
hero.y = 2732 / 2;
game.addChild(hero);
// Auto-shoot indicator removed
// Create UI elements
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
timeTxt = new Text2('Time: 0s', {
size: 60,
fill: 0xFFFFFF
});
timeTxt.anchor.set(1, 0);
LK.gui.top.addChild(timeTxt);
healthTxt = new Text2('❤️❤️❤️', {
size: 60,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
LK.gui.bottom.addChild(healthTxt);
comboTxt = new Text2('Combo: 0x', {
size: 60,
fill: 0xFFFFFF
});
comboTxt.anchor.set(1, 0);
LK.gui.bottom.addChild(comboTxt);
// Initialize game timers
gameTime = 0;
spawnTimer = 0;
bossTimer = BOSS_INTERVAL;
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.5,
duration: 1000
}
});
}
// This function is no longer needed in endless mode
// Spawn a regular enemy
function spawnEnemy() {
// Enforce a maximum number of enemies
if (enemies.length >= MAX_ENEMIES) {
return;
}
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (edge) {
case 0:
// Top
x = Math.random() * 2048;
y = -50;
break;
case 1:
// Right
x = 2048 + 50;
y = Math.random() * 2732;
break;
case 2:
// Bottom
x = Math.random() * 2048;
y = 2732 + 50;
break;
case 3:
// Left
x = -50;
y = Math.random() * 2732;
break;
}
var enemy = new Enemy().init(x, y);
var seconds = Math.floor(gameTime / 60);
// Speed increases with game time
enemy.speed = 2 + Math.min(3, seconds / 30);
enemy.health = 1; // Enemies always have 1 health
// Scale score value based on time played
enemy.scoreValue = 100 + Math.floor(seconds / 30) * 20;
game.addChild(enemy);
enemies.push(enemy);
}
// Spawn a boss
function spawnBoss() {
var boss = new Boss().init(2048 / 2, -200);
var seconds = Math.floor(gameTime / 60);
// Boss health and score scale with game time
boss.health = 5 + Math.floor(seconds / 60);
boss.scoreValue = 2000 + seconds * 50;
game.addChild(boss);
enemies.push(boss);
// Make a dramatic entrance
tween(boss, {
y: 400
}, {
duration: 2000,
easing: tween.bounceOut
});
}
// Spawn a power-up
function spawnPowerUp(x, y) {
var types = ['health', 'speed', 'power', 'strength'];
var type = types[Math.floor(Math.random() * types.length)];
var powerUp = new PowerUp().init(x, y, type);
game.addChild(powerUp);
powerUps.push(powerUp);
}
// Handle touch/mouse down
game.down = function (x, y, obj) {
isSwipeActive = true;
swipeStart.x = x;
swipeStart.y = y;
};
// Handle touch/mouse up
game.up = function (x, y, obj) {
if (isSwipeActive) {
// Instead of dashing, shoot in the direction of the swipe
var dx = x - swipeStart.x;
var dy = y - swipeStart.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If it's a significant swipe, shoot in that direction
if (distance > 20) {
// Calculate target point to shoot towards
var targetX = hero.x + dx * 10;
var targetY = hero.y + dy * 10;
hero.shootBullet(targetX, targetY);
} else {
// If it's just a tap, shoot upward
hero.shootBullet(hero.x, hero.y - 100);
}
isSwipeActive = false;
}
};
// Handle touch/mouse move
game.move = function (x, y, obj) {
// We'll use swipe up/down instead of tracking movement
};
// Main game update loop
game.update = function () {
// Update game time (in seconds)
gameTime++;
var seconds = Math.floor(gameTime / 60);
timeTxt.setText('Time: ' + seconds + 's');
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
comboTxt.setText('Combo: ' + hero.combo + 'x');
// Update health display
var healthStr = '';
for (var i = 0; i < hero.health; i++) {
healthStr += '❤️';
}
healthTxt.setText(healthStr);
// Spawn enemies - continuous spawn
spawnTimer++;
if (spawnTimer >= SPAWN_INTERVAL && enemies.length < MAX_ENEMIES) {
// Spawn rate increases over time
var difficulty = Math.min(0.9, 0.4 + seconds / 120);
// Higher chance to spawn as time goes on
if (Math.random() < difficulty) {
spawnEnemy();
}
spawnTimer = 0;
}
// Boss timer
bossTimer--;
if (bossTimer <= 0) {
spawnBoss();
bossTimer = BOSS_INTERVAL;
}
// Increase difficulty based on time
var speedIncrease = Math.min(3, seconds / 60);
var enemySpeedModifier = 1 + speedIncrease * 0.5;
// Update game objects
updateGameObjects();
// Detect collisions between bullets and enemies
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
if (!bullet.active) {
continue;
}
var hitEnemy = false;
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!enemy.active) {
continue;
}
if (bullet.intersects(enemy)) {
// Bullet hit enemy
hitEnemy = true;
// Call enemy's takeDamage method
enemy.takeDamage();
// Visual feedback
LK.effects.flashObject(enemy, 0xFFFFFF, 100);
// Award points if enemy dead
if (!enemy.active) {
hero.addScore(enemy.scoreValue);
// Maybe spawn a power-up
if (Math.random() < POWER_UP_CHANCE) {
spawnPowerUp(enemy.x, enemy.y);
}
}
break;
}
}
// Remove bullet if it hit something
if (hitEnemy) {
bullet.destroy();
bullets.splice(b, 1);
}
}
// Clean up destroyed objects
cleanupDestroyedObjects();
};
// Update all game objects
function updateGameObjects() {
// Update hero
if (hero) {
hero.update();
}
// Update enemies
for (var i = 0; i < enemies.length; i++) {
if (enemies[i]) {
enemies[i].update();
}
}
// Update power-ups
for (var i = 0; i < powerUps.length; i++) {
if (powerUps[i]) {
powerUps[i].update();
}
}
// Update bullets
for (var i = 0; i < bullets.length; i++) {
if (bullets[i]) {
bullets[i].update();
}
}
// Update trails
for (var i = 0; i < trails.length; i++) {
if (trails[i]) {
trails[i].update();
}
}
}
// Clean up destroyed objects
function cleanupDestroyedObjects() {
// Clean up destroyed enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (!enemies[i] || !enemies[i].parent) {
enemies.splice(i, 1);
}
}
// Clean up destroyed power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
if (!powerUps[i] || !powerUps[i].parent) {
powerUps.splice(i, 1);
}
}
// Clean up destroyed trails
for (var i = trails.length - 1; i >= 0; i--) {
if (!trails[i] || !trails[i].parent) {
trails.splice(i, 1);
}
}
// Clean up destroyed bullets
for (var i = bullets.length - 1; i >= 0; i--) {
if (!bullets[i] || !bullets[i].parent) {
bullets.splice(i, 1);
}
}
}
// Initialize the game
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphic = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
self.health = 10;
self.attackCooldown = 0;
self.attackPattern = 0;
self.scoreValue = 2000;
self.active = true;
self.init = function (x, y) {
self.x = x;
self.y = y;
return self;
};
self.takeDamage = function () {
if (!self.active) {
return;
}
// Boss takes 1 damage per hit, dies after 5 hits
self.health -= 1;
// Flash boss
LK.effects.flashObject(self, 0xffffff, 300);
LK.getSound('hit').play();
if (self.health <= 0) {
self.die();
} else {
// Speed up as health decreases
self.speed = 1.5 + (5 - self.health) * 0.2;
}
};
self.die = function () {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('bossDeath').play();
// Flash and fade out
LK.effects.flashObject(self, 0xffff00, 500);
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
self.destroy();
}
});
currentWave++;
bossDefeated = true;
nextWaveTimer = 180; // 3 seconds until next wave
return self.scoreValue;
};
self.update = function () {
if (!self.active) {
return;
}
// Move toward hero but more intelligently
if (hero) {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Different movement patterns
if (self.attackPattern === 0) {
// Direct approach
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
} else if (self.attackPattern === 1) {
// Circle around
var angle = Math.atan2(dy, dx) + Math.PI / 4;
self.x += Math.cos(angle) * self.speed * 1.5;
self.y += Math.sin(angle) * self.speed * 1.5;
} else if (self.attackPattern === 2) {
// Charge attack
if (self.attackCooldown > 30) {
self.x += dx / dist * self.speed * 2.5;
self.y += dy / dist * self.speed * 2.5;
} else {
// Wait
}
}
if (hero.invulnerable <= 0 && !hero.isDashing) {
if (self.intersects(hero)) {
hero.takeDamage();
}
}
}
// Attack pattern cooldown
self.attackCooldown--;
if (self.attackCooldown <= 0) {
self.attackPattern = (self.attackPattern + 1) % 3;
self.attackCooldown = 120; // 2 seconds per pattern
}
// Keep within screen bounds
self.x = Math.max(100, Math.min(2048 - 100, self.x));
self.y = Math.max(100, Math.min(2732 - 100, self.y));
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphic = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletGraphic.tint = 0xffffff; // White bullet for the hero
self.speed = 15;
self.damage = 1;
self.active = true;
self.init = function (x, y, direction) {
self.x = x;
self.y = y;
self.direction = direction || {
x: 0,
y: -1
}; // Default direction is up
return self;
};
self.update = function () {
if (!self.active) {
return;
}
// Move in the specified direction
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Remove if off-screen
if (self.x < -50 || self.x > 2048 + 50 || self.y < -50 || self.y > 2732 + 50) {
self.destroy();
}
};
return self;
});
var DashTrail = Container.expand(function () {
var self = Container.call(this);
var trailGraphic = self.attachAsset('dashTrail', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.init = function (x, y) {
self.x = x;
self.y = y;
self.lifetime = 20; // frames the trail will live
return self;
};
self.update = function () {
self.lifetime--;
trailGraphic.alpha = self.lifetime / 20 * 0.7;
if (self.lifetime <= 0) {
self.destroy();
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphic = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.health = 1;
self.scoreValue = 100;
self.active = true;
self.init = function (x, y) {
self.x = x;
self.y = y;
// Initialize last position for rotation tracking
self.lastX = x;
self.lastY = y;
// Set initial rotation
if (hero) {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
enemyGraphic.rotation = Math.atan2(dy, dx);
}
return self;
};
self.takeDamage = function () {
if (!self.active) {
return;
}
// Enemies die from one hit
self.health = 0;
self.die();
};
self.die = function () {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('hit').play();
// Flash, fade out, and fall animation
tween(self, {
alpha: 0,
y: self.y + 300,
// Fall down animation
rotation: Math.random() * Math.PI * 2,
// Random rotation
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
}
});
return self.scoreValue;
};
self.update = function () {
if (!self.active) {
return;
}
// Move toward hero
if (hero && !hero.isDashing) {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
// Spider walking animation
if (LK.ticks % 10 === 0) {
// Spider leg movement animation
tween(enemyGraphic, {
scaleX: 1.1,
scaleY: 0.9,
rotation: Math.atan2(dy, dx) + (Math.random() * 0.2 - 0.1)
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyGraphic, {
scaleX: 1,
scaleY: 1,
rotation: Math.atan2(dy, dx)
}, {
duration: 200,
easing: tween.easeOut
});
}
});
}
}
}
// Check for collision with hero
if (hero && !hero.isDashing && hero.invulnerable <= 0) {
if (self.intersects(hero)) {
hero.takeDamage();
}
}
};
return self;
});
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGraphic = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
// Simple click handler that does nothing (placeholder)
self.down = function (x, y, obj) {
// No auto-shoot toggle functionality
};
self.shootSpeed = 15; // Bullet speed
self.fireRate = 15; // Frames between shots
self.shootCooldown = 0;
self.health = 3;
self.invulnerable = 0;
self.combo = 0;
self.score = 0;
self.powerBoost = 1; // Base power multiplier
self.shootBullet = function (targetX, targetY) {
if (self.shootCooldown > 0) {
return;
}
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize direction
var dirX = dx / distance;
var dirY = dy / distance;
// Create bullet
var bullet = new Bullet().init(self.x, self.y, {
x: dirX,
y: dirY
});
bullet.damage = self.powerBoost; // Apply power boost to bullet damage
// Visual shooting effect
tween(heroGraphic, {
scaleX: 1.2,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(heroGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut
});
}
});
// Set cooldown
self.shootCooldown = self.fireRate;
// Add bullet to game
game.addChild(bullet);
if (!bullets) {
bullets = [];
}
bullets.push(bullet);
// Play shoot sound
LK.getSound('hit').play();
};
self.takeDamage = function () {
if (self.invulnerable > 0) {
return;
}
self.health--;
self.invulnerable = 60; // invulnerable for 1 second
self.combo = 0; // Reset combo
// Flash hero red
LK.effects.flashObject(self, 0xff0000, 500);
// Play damage sound
LK.getSound('damage').play();
if (self.health <= 0) {
LK.showGameOver();
}
};
self.addScore = function (points) {
self.combo++;
var comboMultiplier = Math.min(4, 1 + self.combo * 0.1);
var scoreToAdd = Math.floor(points * comboMultiplier);
self.score += scoreToAdd;
LK.setScore(self.score);
};
self.update = function () {
// Keep within screen bounds
self.x = Math.max(50, Math.min(2048 - 50, self.x));
self.y = Math.max(50, Math.min(2732 - 50, self.y));
// Cooldowns
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
if (self.invulnerable > 0) {
self.invulnerable--;
// Flicker effect during invulnerability
heroGraphic.alpha = LK.ticks % 6 < 3 ? 0.4 : 1;
} else {
// Normal appearance when not invulnerable
heroGraphic.alpha = 1;
heroGraphic.tint = 0xFFFFFF; // Normal color
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphic = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'health'; // Default type
self.active = true;
self.lifetime = 300; // 5 seconds
self.init = function (x, y, type) {
self.x = x;
self.y = y;
self.type = type || self.type;
// Set color based on type
if (self.type === 'health') {
powerUpGraphic.tint = 0x2ecc71; // Green
} else if (self.type === 'speed') {
powerUpGraphic.tint = 0x3498db; // Blue
} else if (self.type === 'power') {
powerUpGraphic.tint = 0xe74c3c; // Red
} else if (self.type === 'strength') {
powerUpGraphic.tint = 0x9b59b6; // Purple
}
return self;
};
self.collect = function () {
if (!self.active) {
return;
}
self.active = false;
LK.getSound('powerup').play();
if (self.type === 'health' && hero.health < 3) {
hero.health++;
} else if (self.type === 'speed') {
hero.dashSpeed = 80; // Even faster dash
hero.dashDistance = 900; // Even longer dash
// Visual indicator of speed boost
tween(hero, {
alpha: 0.7
}, {
duration: 200,
onFinish: function onFinish() {
tween(hero, {
alpha: 1
}, {
duration: 200
});
}
});
// Reset after 10 seconds
LK.setTimeout(function () {
if (hero) {
hero.dashSpeed = 60;
hero.dashDistance = 700;
}
}, 10000);
} else if (self.type === 'strength') {
// Increase hero's attack strength more permanently
hero.powerBoost = Math.min(5, hero.powerBoost + 1);
// Visual indicator of strength boost
tween(hero, {
tint: 0x9b59b6
}, {
duration: 500,
onFinish: function onFinish() {
tween(hero, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
});
// Show strength level
var strengthTxt = new Text2('STRENGTH +' + hero.powerBoost + '!', {
size: 80,
fill: 0x9b59b6
});
strengthTxt.anchor.set(0.5, 0.5);
strengthTxt.x = 2048 / 2;
strengthTxt.y = 2732 / 2;
game.addChild(strengthTxt);
// Fade out the text
tween(strengthTxt, {
alpha: 0,
y: strengthTxt.y - 100
}, {
duration: 1500,
onFinish: function onFinish() {
strengthTxt.destroy();
}
});
} else if (self.type === 'power') {
// Clear all enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].active) {
var points = enemies[i].die();
hero.addScore(points);
}
}
// Increase hero's attack strength
hero.powerBoost = 3;
// Visual indicator of power boost
tween(hero, {
tint: 0xFF0000
}, {
duration: 300
});
// Reset after 10 seconds
LK.setTimeout(function () {
if (hero) {
hero.powerBoost = 1;
tween(hero, {
tint: 0xFFFFFF
}, {
duration: 500
});
}
}, 10000);
}
// Flash and fade out
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
};
self.update = function () {
if (!self.active) {
return;
}
// Floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
// Rotate slowly
powerUpGraphic.rotation += 0.02;
// Check for collision with hero
if (hero && (hero.intersects(self) || hero.isDashing && self.distanceTo(hero) < 100)) {
self.collect();
}
// Expire after lifetime
self.lifetime--;
if (self.lifetime <= 0) {
tween(self, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
self.active = false;
}
};
self.distanceTo = function (target) {
var dx = target.x - self.x;
var dy = target.y - self.y;
return Math.sqrt(dx * dx + dy * dy);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
// Game state variables
var hero;
var enemies = [];
var bullets = [];
var powerUps = [];
var trails = [];
var gameTime = 0;
var spawnTimer = 0;
var bossTimer = 0;
var currentWave = 0;
var bossDefeated = false;
var nextWaveTimer = 0;
var swipeStart = {
x: 0,
y: 0
};
var isSwipeActive = false;
// UI elements
var scoreTxt;
var timeTxt;
var healthTxt;
var comboTxt;
// Game constants
var SPAWN_INTERVAL = 90; // Frames between enemy spawns
var POWER_UP_CHANCE = 0.15; // Chance to spawn power-up on enemy defeat
var BOSS_INTERVAL = 1800; // Spawn boss every 30 seconds (60fps * 30)
var MAX_ENEMIES = 15; // Maximum enemies on screen at once
// Initialize game elements
function initGame() {
// Add background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
// Create hero
hero = new Hero();
hero.x = 2048 / 2;
hero.y = 2732 / 2;
game.addChild(hero);
// Auto-shoot indicator removed
// Create UI elements
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
timeTxt = new Text2('Time: 0s', {
size: 60,
fill: 0xFFFFFF
});
timeTxt.anchor.set(1, 0);
LK.gui.top.addChild(timeTxt);
healthTxt = new Text2('❤️❤️❤️', {
size: 60,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
LK.gui.bottom.addChild(healthTxt);
comboTxt = new Text2('Combo: 0x', {
size: 60,
fill: 0xFFFFFF
});
comboTxt.anchor.set(1, 0);
LK.gui.bottom.addChild(comboTxt);
// Initialize game timers
gameTime = 0;
spawnTimer = 0;
bossTimer = BOSS_INTERVAL;
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.5,
duration: 1000
}
});
}
// This function is no longer needed in endless mode
// Spawn a regular enemy
function spawnEnemy() {
// Enforce a maximum number of enemies
if (enemies.length >= MAX_ENEMIES) {
return;
}
var edge = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (edge) {
case 0:
// Top
x = Math.random() * 2048;
y = -50;
break;
case 1:
// Right
x = 2048 + 50;
y = Math.random() * 2732;
break;
case 2:
// Bottom
x = Math.random() * 2048;
y = 2732 + 50;
break;
case 3:
// Left
x = -50;
y = Math.random() * 2732;
break;
}
var enemy = new Enemy().init(x, y);
var seconds = Math.floor(gameTime / 60);
// Speed increases with game time
enemy.speed = 2 + Math.min(3, seconds / 30);
enemy.health = 1; // Enemies always have 1 health
// Scale score value based on time played
enemy.scoreValue = 100 + Math.floor(seconds / 30) * 20;
game.addChild(enemy);
enemies.push(enemy);
}
// Spawn a boss
function spawnBoss() {
var boss = new Boss().init(2048 / 2, -200);
var seconds = Math.floor(gameTime / 60);
// Boss health and score scale with game time
boss.health = 5 + Math.floor(seconds / 60);
boss.scoreValue = 2000 + seconds * 50;
game.addChild(boss);
enemies.push(boss);
// Make a dramatic entrance
tween(boss, {
y: 400
}, {
duration: 2000,
easing: tween.bounceOut
});
}
// Spawn a power-up
function spawnPowerUp(x, y) {
var types = ['health', 'speed', 'power', 'strength'];
var type = types[Math.floor(Math.random() * types.length)];
var powerUp = new PowerUp().init(x, y, type);
game.addChild(powerUp);
powerUps.push(powerUp);
}
// Handle touch/mouse down
game.down = function (x, y, obj) {
isSwipeActive = true;
swipeStart.x = x;
swipeStart.y = y;
};
// Handle touch/mouse up
game.up = function (x, y, obj) {
if (isSwipeActive) {
// Instead of dashing, shoot in the direction of the swipe
var dx = x - swipeStart.x;
var dy = y - swipeStart.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If it's a significant swipe, shoot in that direction
if (distance > 20) {
// Calculate target point to shoot towards
var targetX = hero.x + dx * 10;
var targetY = hero.y + dy * 10;
hero.shootBullet(targetX, targetY);
} else {
// If it's just a tap, shoot upward
hero.shootBullet(hero.x, hero.y - 100);
}
isSwipeActive = false;
}
};
// Handle touch/mouse move
game.move = function (x, y, obj) {
// We'll use swipe up/down instead of tracking movement
};
// Main game update loop
game.update = function () {
// Update game time (in seconds)
gameTime++;
var seconds = Math.floor(gameTime / 60);
timeTxt.setText('Time: ' + seconds + 's');
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
comboTxt.setText('Combo: ' + hero.combo + 'x');
// Update health display
var healthStr = '';
for (var i = 0; i < hero.health; i++) {
healthStr += '❤️';
}
healthTxt.setText(healthStr);
// Spawn enemies - continuous spawn
spawnTimer++;
if (spawnTimer >= SPAWN_INTERVAL && enemies.length < MAX_ENEMIES) {
// Spawn rate increases over time
var difficulty = Math.min(0.9, 0.4 + seconds / 120);
// Higher chance to spawn as time goes on
if (Math.random() < difficulty) {
spawnEnemy();
}
spawnTimer = 0;
}
// Boss timer
bossTimer--;
if (bossTimer <= 0) {
spawnBoss();
bossTimer = BOSS_INTERVAL;
}
// Increase difficulty based on time
var speedIncrease = Math.min(3, seconds / 60);
var enemySpeedModifier = 1 + speedIncrease * 0.5;
// Update game objects
updateGameObjects();
// Detect collisions between bullets and enemies
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
if (!bullet.active) {
continue;
}
var hitEnemy = false;
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!enemy.active) {
continue;
}
if (bullet.intersects(enemy)) {
// Bullet hit enemy
hitEnemy = true;
// Call enemy's takeDamage method
enemy.takeDamage();
// Visual feedback
LK.effects.flashObject(enemy, 0xFFFFFF, 100);
// Award points if enemy dead
if (!enemy.active) {
hero.addScore(enemy.scoreValue);
// Maybe spawn a power-up
if (Math.random() < POWER_UP_CHANCE) {
spawnPowerUp(enemy.x, enemy.y);
}
}
break;
}
}
// Remove bullet if it hit something
if (hitEnemy) {
bullet.destroy();
bullets.splice(b, 1);
}
}
// Clean up destroyed objects
cleanupDestroyedObjects();
};
// Update all game objects
function updateGameObjects() {
// Update hero
if (hero) {
hero.update();
}
// Update enemies
for (var i = 0; i < enemies.length; i++) {
if (enemies[i]) {
enemies[i].update();
}
}
// Update power-ups
for (var i = 0; i < powerUps.length; i++) {
if (powerUps[i]) {
powerUps[i].update();
}
}
// Update bullets
for (var i = 0; i < bullets.length; i++) {
if (bullets[i]) {
bullets[i].update();
}
}
// Update trails
for (var i = 0; i < trails.length; i++) {
if (trails[i]) {
trails[i].update();
}
}
}
// Clean up destroyed objects
function cleanupDestroyedObjects() {
// Clean up destroyed enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (!enemies[i] || !enemies[i].parent) {
enemies.splice(i, 1);
}
}
// Clean up destroyed power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
if (!powerUps[i] || !powerUps[i].parent) {
powerUps.splice(i, 1);
}
}
// Clean up destroyed trails
for (var i = trails.length - 1; i >= 0; i--) {
if (!trails[i] || !trails[i].parent) {
trails.splice(i, 1);
}
}
// Clean up destroyed bullets
for (var i = bullets.length - 1; i >= 0; i--) {
if (!bullets[i] || !bullets[i].parent) {
bullets.splice(i, 1);
}
}
}
// Initialize the game
initGame();
colored spider. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
white silk web. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
amazing spider web on tree branch. anime image. green landscape Single Game Texture. In-Game asset. 2d.
flyn lady bug 2d cartoon objek. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
flyin evil woodpecker bird. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows