/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy base class
var Enemy = Container.expand(function () {
var self = Container.call(this);
self.type = 'base';
self.speed = 4;
self.health = 3;
self.isFlashing = false;
self.flashTimeout = null;
self.update = function () {};
self.takeDamage = function () {
if (self.isFlashing) return;
self.health--;
self.isFlashing = true;
// Flash red for 200ms
LK.effects.flashObject(self, 0xff0000, 200);
if (self.flashTimeout) LK.clearTimeout(self.flashTimeout);
self.flashTimeout = LK.setTimeout(function () {
self.isFlashing = false;
}, 200);
};
return self;
});
// Enemy: Spinner (orbits the central target)
var EnemySpinner = Enemy.expand(function () {
var self = Enemy.call(this);
var asset = self.attachAsset('enemySpinner', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'spinner';
self.radius = 400 + Math.random() * 200;
self.angle = Math.random() * Math.PI * 2;
self.speed = 0.012 + Math.random() * 0.005;
self.update = function () {
self.angle += self.speed;
self.x = centralTarget.x + Math.cos(self.angle) * self.radius;
self.y = centralTarget.y + Math.sin(self.angle) * self.radius;
};
return self;
});
// Enemy: Seeker (moves toward player)
var EnemySeeker = Enemy.expand(function () {
var self = Enemy.call(this);
var asset = self.attachAsset('enemySeeker', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'seeker';
self.speed = 3;
self.update = function () {
// Move toward player
if (!player) return;
var dx = player.x - self.x;
var dy = player.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;
}
};
return self;
});
// Enemy: Dasher (dashes horizontally or vertically)
var EnemyDasher = Enemy.expand(function () {
var self = Enemy.call(this);
var asset = self.attachAsset('enemyDasher', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'dasher';
self.axis = Math.random() < 0.5 ? 'x' : 'y';
self.speed = 9 + Math.random() * 3;
self.direction = Math.random() < 0.5 ? 1 : -1;
self.update = function () {
if (self.axis === 'x') {
self.x += self.speed * self.direction;
} else {
self.y += self.speed * self.direction;
}
};
return self;
});
// Health Pickup
var HealthPickup = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0x2ecc71 // green tint for health
});
self.type = 'health';
self.update = function () {};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 700;
self.angle = -Math.PI / 2;
self.x = 0;
self.y = 0;
self.updatePosition = function () {
self.x = centralTarget.x + Math.cos(self.angle) * self.radius;
self.y = centralTarget.y + Math.sin(self.angle) * self.radius;
};
self.updatePosition();
return self;
});
// Player bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletAsset = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
// vx, vy will be set on creation
self.vx = 0;
self.vy = -32;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Shield Pickup
var ShieldPickup = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0xf1c40f // yellow tint for shield
});
self.type = 'shield';
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c20
});
/****
* Game Code
****/
// Central target
// Player character
// Player bullet
// Central target
// Enemy type 1: Seeker
// Enemy type 2: Spinner
// Enemy type 3: Dasher
// Sound for shooting
// Sound for enemy destroyed
// Sound for hit target
// Music
var centralTarget = LK.getAsset('centralTarget', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(centralTarget);
// Player
var player = new Player();
player.updatePosition();
game.addChild(player);
// Player health (3 hits to die)
var playerHealth = 3;
// Shield state
var playerShieldActive = false;
var playerShieldTimer = 0;
// Bullets and enemies arrays
var playerBullets = [];
var enemies = [];
// Score
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Track background state for multi-background progression
game.backgroundChanged = 0;
// Timer text (minutes:seconds)
var timerTxt = new Text2('0:00', {
size: 90,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0);
timerTxt.y = 110; // Place below score, avoid top 100px
LK.gui.top.addChild(timerTxt);
// Track elapsed time in frames
var elapsedFrames = 0;
// Difficulty and spawn control
var spawnTimer = 0;
var spawnInterval = 90; // frames
var enemyTypes = ['seeker'];
var difficultyTimer = 0;
// Stage control
var stage = 1;
var fireTimer = 0;
var fireInterval = 18; // frames between shots, default for stage 1
// Drag control
var dragging = false;
var dragStartAngle = 0;
var dragStartX = 0;
var dragStartY = 0;
// Helper: get angle from center to (x, y)
function getAngleToCenter(x, y) {
var dx = x - centralTarget.x;
var dy = y - centralTarget.y;
return Math.atan2(dy, dx);
}
// Move handler: drag around the central target
function handleMove(x, y, obj) {
if (!dragging) return;
var angle = getAngleToCenter(x, y);
player.angle = angle;
player.updatePosition();
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only start drag if not on top left 100x100
if (x < 100 && y < 100) return;
dragging = true;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragging = false;
};
// Shoot on tap anywhere (except top left)
game.down = function (x, y, obj) {
if (x < 100 && y < 100) return;
// Fire bullet toward tap/click
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.lastY = bullet.y;
bullet.lastIntersecting = false;
// Calculate direction from player to tap/click
var dx = x - player.x;
var dy = y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
var speed = 32;
bullet.vx = dx / dist * speed;
bullet.vy = dy / dist * speed;
// Rotate player to face shooting direction
player.angle = Math.atan2(dy, dx);
player.updatePosition();
} else {
bullet.vx = 0;
bullet.vy = -32;
}
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Also allow drag
dragging = true;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragging = false;
};
// Enemy spawn logic
function spawnEnemy() {
var type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy;
if (type === 'seeker') {
enemy = new EnemySeeker();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
enemy.x = Math.random() * 2048;
enemy.y = -100;
} else if (edge === 1) {
// right
enemy.x = 2048 + 100;
enemy.y = Math.random() * 2732;
} else if (edge === 2) {
// bottom
enemy.x = Math.random() * 2048;
enemy.y = 2732 + 100;
} else {
// left
enemy.x = -100;
enemy.y = Math.random() * 2732;
}
} else if (type === 'spinner') {
enemy = new EnemySpinner();
// Place at random angle
enemy.angle = Math.random() * Math.PI * 2;
enemy.radius = 400 + Math.random() * 400;
enemy.x = centralTarget.x + Math.cos(enemy.angle) * enemy.radius;
enemy.y = centralTarget.y + Math.sin(enemy.angle) * enemy.radius;
} else if (type === 'dasher') {
enemy = new EnemyDasher();
// Start at random edge
if (enemy.axis === 'x') {
enemy.x = enemy.direction > 0 ? -100 : 2048 + 100;
enemy.y = Math.random() * 2732;
} else {
enemy.x = Math.random() * 2048;
enemy.y = enemy.direction > 0 ? -100 : 2732 + 100;
}
}
if (enemy) {
enemy.lastIntersectingPlayer = false;
enemy.lastIntersectingTarget = false;
enemies.push(enemy);
game.addChild(enemy);
}
}
// Main update loop
game.update = function () {
// Update timer
elapsedFrames++;
var totalSeconds = Math.floor(elapsedFrames / 60);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var secondsStr = seconds < 10 ? '0' + seconds : '' + seconds;
timerTxt.setText(minutes + ':' + secondsStr);
// Difficulty scaling
difficultyTimer++;
if (difficultyTimer % 600 === 0 && enemyTypes.length < 3) {
if (enemyTypes.length === 1) {
enemyTypes.push('spinner');
} else if (enemyTypes.length === 2) {
enemyTypes.push('dasher');
}
if (spawnInterval > 40) spawnInterval -= 10;
}
// Stage progression: switch to stage 2 at score 30, and to stage 3 at score 70
if (stage === 1 && LK.getScore() >= 30) {
stage = 2;
spawnInterval = 90; // slower spawn for stage 2
fireInterval = 7; // faster fire rate
}
if (stage === 2 && LK.getScore() >= 70) {
stage = 3;
spawnInterval = 30; // much faster enemy spawn in stage 3
fireInterval = 4; // even faster fire rate
// Activate shield for 3 seconds (180 frames)
if (!playerShieldActive) {
playerShieldActive = true;
playerShieldTimer = 180;
// Optional: flash player or add effect
LK.effects.flashObject(player, 0xf1c40f, 3000);
}
}
// Handle shield timer
if (playerShieldActive) {
playerShieldTimer--;
if (playerShieldTimer <= 0) {
playerShieldActive = false;
playerShieldTimer = 0;
}
}
// Spawn enemies
spawnTimer++;
if (spawnTimer >= spawnInterval) {
// In stage 1, spawn 1 enemy per interval
if (stage === 1) {
spawnEnemy();
} else if (stage === 2) {
// In stage 2, spawn 1 enemy per interval (less than before)
spawnEnemy();
} else if (stage === 3) {
// In stage 3, spawn 4 enemies per interval (increase)
spawnEnemy();
spawnEnemy();
spawnEnemy();
spawnEnemy();
}
spawnTimer = 0;
}
// Automatic fire logic (increased fire rate in stage 2 and 3)
fireTimer++;
if (fireTimer >= fireInterval && dragging) {
// Fire bullet toward current player angle (in direction player is facing)
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.lastY = bullet.y;
bullet.lastIntersecting = false;
// Fire toward central target
var dx = centralTarget.x - player.x;
var dy = centralTarget.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
var speed = 32;
bullet.vx = dx / dist * speed;
bullet.vy = dy / dist * speed;
} else {
bullet.vx = 0;
bullet.vy = -32;
}
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
fireTimer = 0;
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
bullet.update();
// Remove if off screen
if (bullet.y < -60 || bullet.y > 2732 + 60 || bullet.x < -60 || bullet.x > 2048 + 60) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check hit on central target
var hitTarget = bullet.intersects(centralTarget);
if (!bullet.lastIntersecting && hitTarget) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('targetHit').play();
bullet.destroy();
playerBullets.splice(i, 1);
// Win condition: Instead of ending, change background and continue
if (LK.getScore() >= 100) {
if (!game.backgroundChanged) {
// First background change (after 100 points)
game.setBackgroundColor(0x1a1a40); // 2nd background color
game.backgroundChanged = 1;
// Remove all enemies from the game
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
// Reset score, stage, and timer
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 2nd background
enemyTypes = ['spinner', 'dasher'];
// Optionally, you could add a visual effect or message here
} else if (game.backgroundChanged === 1 && LK.getScore() >= 100) {
// Second background change (after 2nd 100 points)
game.setBackgroundColor(0x1a4020); // 3rd background color
game.backgroundChanged = 2;
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 3rd background
enemyTypes = ['seeker', 'dasher'];
} else if (game.backgroundChanged === 2 && LK.getScore() >= 100) {
// Third background change (after 3rd 100 points)
game.setBackgroundColor(0x40201a); // 4th background color
game.backgroundChanged = 3;
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 4th background
enemyTypes = ['seeker', 'spinner'];
} else if (game.backgroundChanged === 3 && LK.getScore() >= 100) {
// Fourth background change (after 4th 100 points)
game.setBackgroundColor(0x204040); // 5th background color
game.backgroundChanged = 4;
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 5th background
enemyTypes = ['dasher', 'spinner'];
}
}
continue;
}
bullet.lastIntersecting = hitTarget;
// Check hit on enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
LK.getSound('enemyDown').play();
bullet.destroy();
playerBullets.splice(i, 1);
enemy.takeDamage();
if (enemy.health <= 0) {
enemy.destroy();
enemies.splice(j, 1);
}
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Remove if off screen (for dashers)
if (enemy.x < -200 || enemy.x > 2048 + 200 || enemy.y < -200 || enemy.y > 2732 + 200) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
var hitPlayer = enemy.intersects(player);
if (!enemy.lastIntersectingPlayer && hitPlayer) {
if (!playerShieldActive) {
playerHealth--;
LK.effects.flashScreen(0xff0000, 800);
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
}
}
enemy.lastIntersectingPlayer = hitPlayer;
// Check collision with central target (optional: could penalize or just remove)
var hitTarget = enemy.intersects(centralTarget);
if (!enemy.lastIntersectingTarget && hitTarget) {
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
continue;
}
enemy.lastIntersectingTarget = hitTarget;
}
};
// Play background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 1200
}
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy base class
var Enemy = Container.expand(function () {
var self = Container.call(this);
self.type = 'base';
self.speed = 4;
self.health = 3;
self.isFlashing = false;
self.flashTimeout = null;
self.update = function () {};
self.takeDamage = function () {
if (self.isFlashing) return;
self.health--;
self.isFlashing = true;
// Flash red for 200ms
LK.effects.flashObject(self, 0xff0000, 200);
if (self.flashTimeout) LK.clearTimeout(self.flashTimeout);
self.flashTimeout = LK.setTimeout(function () {
self.isFlashing = false;
}, 200);
};
return self;
});
// Enemy: Spinner (orbits the central target)
var EnemySpinner = Enemy.expand(function () {
var self = Enemy.call(this);
var asset = self.attachAsset('enemySpinner', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'spinner';
self.radius = 400 + Math.random() * 200;
self.angle = Math.random() * Math.PI * 2;
self.speed = 0.012 + Math.random() * 0.005;
self.update = function () {
self.angle += self.speed;
self.x = centralTarget.x + Math.cos(self.angle) * self.radius;
self.y = centralTarget.y + Math.sin(self.angle) * self.radius;
};
return self;
});
// Enemy: Seeker (moves toward player)
var EnemySeeker = Enemy.expand(function () {
var self = Enemy.call(this);
var asset = self.attachAsset('enemySeeker', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'seeker';
self.speed = 3;
self.update = function () {
// Move toward player
if (!player) return;
var dx = player.x - self.x;
var dy = player.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;
}
};
return self;
});
// Enemy: Dasher (dashes horizontally or vertically)
var EnemyDasher = Enemy.expand(function () {
var self = Enemy.call(this);
var asset = self.attachAsset('enemyDasher', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = 'dasher';
self.axis = Math.random() < 0.5 ? 'x' : 'y';
self.speed = 9 + Math.random() * 3;
self.direction = Math.random() < 0.5 ? 1 : -1;
self.update = function () {
if (self.axis === 'x') {
self.x += self.speed * self.direction;
} else {
self.y += self.speed * self.direction;
}
};
return self;
});
// Health Pickup
var HealthPickup = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0x2ecc71 // green tint for health
});
self.type = 'health';
self.update = function () {};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = 700;
self.angle = -Math.PI / 2;
self.x = 0;
self.y = 0;
self.updatePosition = function () {
self.x = centralTarget.x + Math.cos(self.angle) * self.radius;
self.y = centralTarget.y + Math.sin(self.angle) * self.radius;
};
self.updatePosition();
return self;
});
// Player bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var bulletAsset = self.attachAsset('playerBullet', {
anchorX: 0.5,
anchorY: 0.5
});
// vx, vy will be set on creation
self.vx = 0;
self.vy = -32;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Shield Pickup
var ShieldPickup = Container.expand(function () {
var self = Container.call(this);
var asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0xf1c40f // yellow tint for shield
});
self.type = 'shield';
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c20
});
/****
* Game Code
****/
// Central target
// Player character
// Player bullet
// Central target
// Enemy type 1: Seeker
// Enemy type 2: Spinner
// Enemy type 3: Dasher
// Sound for shooting
// Sound for enemy destroyed
// Sound for hit target
// Music
var centralTarget = LK.getAsset('centralTarget', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(centralTarget);
// Player
var player = new Player();
player.updatePosition();
game.addChild(player);
// Player health (3 hits to die)
var playerHealth = 3;
// Shield state
var playerShieldActive = false;
var playerShieldTimer = 0;
// Bullets and enemies arrays
var playerBullets = [];
var enemies = [];
// Score
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Track background state for multi-background progression
game.backgroundChanged = 0;
// Timer text (minutes:seconds)
var timerTxt = new Text2('0:00', {
size: 90,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0);
timerTxt.y = 110; // Place below score, avoid top 100px
LK.gui.top.addChild(timerTxt);
// Track elapsed time in frames
var elapsedFrames = 0;
// Difficulty and spawn control
var spawnTimer = 0;
var spawnInterval = 90; // frames
var enemyTypes = ['seeker'];
var difficultyTimer = 0;
// Stage control
var stage = 1;
var fireTimer = 0;
var fireInterval = 18; // frames between shots, default for stage 1
// Drag control
var dragging = false;
var dragStartAngle = 0;
var dragStartX = 0;
var dragStartY = 0;
// Helper: get angle from center to (x, y)
function getAngleToCenter(x, y) {
var dx = x - centralTarget.x;
var dy = y - centralTarget.y;
return Math.atan2(dy, dx);
}
// Move handler: drag around the central target
function handleMove(x, y, obj) {
if (!dragging) return;
var angle = getAngleToCenter(x, y);
player.angle = angle;
player.updatePosition();
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only start drag if not on top left 100x100
if (x < 100 && y < 100) return;
dragging = true;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragging = false;
};
// Shoot on tap anywhere (except top left)
game.down = function (x, y, obj) {
if (x < 100 && y < 100) return;
// Fire bullet toward tap/click
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.lastY = bullet.y;
bullet.lastIntersecting = false;
// Calculate direction from player to tap/click
var dx = x - player.x;
var dy = y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
var speed = 32;
bullet.vx = dx / dist * speed;
bullet.vy = dy / dist * speed;
// Rotate player to face shooting direction
player.angle = Math.atan2(dy, dx);
player.updatePosition();
} else {
bullet.vx = 0;
bullet.vy = -32;
}
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Also allow drag
dragging = true;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragging = false;
};
// Enemy spawn logic
function spawnEnemy() {
var type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
var enemy;
if (type === 'seeker') {
enemy = new EnemySeeker();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
enemy.x = Math.random() * 2048;
enemy.y = -100;
} else if (edge === 1) {
// right
enemy.x = 2048 + 100;
enemy.y = Math.random() * 2732;
} else if (edge === 2) {
// bottom
enemy.x = Math.random() * 2048;
enemy.y = 2732 + 100;
} else {
// left
enemy.x = -100;
enemy.y = Math.random() * 2732;
}
} else if (type === 'spinner') {
enemy = new EnemySpinner();
// Place at random angle
enemy.angle = Math.random() * Math.PI * 2;
enemy.radius = 400 + Math.random() * 400;
enemy.x = centralTarget.x + Math.cos(enemy.angle) * enemy.radius;
enemy.y = centralTarget.y + Math.sin(enemy.angle) * enemy.radius;
} else if (type === 'dasher') {
enemy = new EnemyDasher();
// Start at random edge
if (enemy.axis === 'x') {
enemy.x = enemy.direction > 0 ? -100 : 2048 + 100;
enemy.y = Math.random() * 2732;
} else {
enemy.x = Math.random() * 2048;
enemy.y = enemy.direction > 0 ? -100 : 2732 + 100;
}
}
if (enemy) {
enemy.lastIntersectingPlayer = false;
enemy.lastIntersectingTarget = false;
enemies.push(enemy);
game.addChild(enemy);
}
}
// Main update loop
game.update = function () {
// Update timer
elapsedFrames++;
var totalSeconds = Math.floor(elapsedFrames / 60);
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var secondsStr = seconds < 10 ? '0' + seconds : '' + seconds;
timerTxt.setText(minutes + ':' + secondsStr);
// Difficulty scaling
difficultyTimer++;
if (difficultyTimer % 600 === 0 && enemyTypes.length < 3) {
if (enemyTypes.length === 1) {
enemyTypes.push('spinner');
} else if (enemyTypes.length === 2) {
enemyTypes.push('dasher');
}
if (spawnInterval > 40) spawnInterval -= 10;
}
// Stage progression: switch to stage 2 at score 30, and to stage 3 at score 70
if (stage === 1 && LK.getScore() >= 30) {
stage = 2;
spawnInterval = 90; // slower spawn for stage 2
fireInterval = 7; // faster fire rate
}
if (stage === 2 && LK.getScore() >= 70) {
stage = 3;
spawnInterval = 30; // much faster enemy spawn in stage 3
fireInterval = 4; // even faster fire rate
// Activate shield for 3 seconds (180 frames)
if (!playerShieldActive) {
playerShieldActive = true;
playerShieldTimer = 180;
// Optional: flash player or add effect
LK.effects.flashObject(player, 0xf1c40f, 3000);
}
}
// Handle shield timer
if (playerShieldActive) {
playerShieldTimer--;
if (playerShieldTimer <= 0) {
playerShieldActive = false;
playerShieldTimer = 0;
}
}
// Spawn enemies
spawnTimer++;
if (spawnTimer >= spawnInterval) {
// In stage 1, spawn 1 enemy per interval
if (stage === 1) {
spawnEnemy();
} else if (stage === 2) {
// In stage 2, spawn 1 enemy per interval (less than before)
spawnEnemy();
} else if (stage === 3) {
// In stage 3, spawn 4 enemies per interval (increase)
spawnEnemy();
spawnEnemy();
spawnEnemy();
spawnEnemy();
}
spawnTimer = 0;
}
// Automatic fire logic (increased fire rate in stage 2 and 3)
fireTimer++;
if (fireTimer >= fireInterval && dragging) {
// Fire bullet toward current player angle (in direction player is facing)
var bullet = new PlayerBullet();
bullet.x = player.x;
bullet.y = player.y;
bullet.lastY = bullet.y;
bullet.lastIntersecting = false;
// Fire toward central target
var dx = centralTarget.x - player.x;
var dy = centralTarget.y - player.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
var speed = 32;
bullet.vx = dx / dist * speed;
bullet.vy = dy / dist * speed;
} else {
bullet.vx = 0;
bullet.vy = -32;
}
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
fireTimer = 0;
}
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
var bullet = playerBullets[i];
bullet.update();
// Remove if off screen
if (bullet.y < -60 || bullet.y > 2732 + 60 || bullet.x < -60 || bullet.x > 2048 + 60) {
bullet.destroy();
playerBullets.splice(i, 1);
continue;
}
// Check hit on central target
var hitTarget = bullet.intersects(centralTarget);
if (!bullet.lastIntersecting && hitTarget) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
LK.getSound('targetHit').play();
bullet.destroy();
playerBullets.splice(i, 1);
// Win condition: Instead of ending, change background and continue
if (LK.getScore() >= 100) {
if (!game.backgroundChanged) {
// First background change (after 100 points)
game.setBackgroundColor(0x1a1a40); // 2nd background color
game.backgroundChanged = 1;
// Remove all enemies from the game
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
// Reset score, stage, and timer
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 2nd background
enemyTypes = ['spinner', 'dasher'];
// Optionally, you could add a visual effect or message here
} else if (game.backgroundChanged === 1 && LK.getScore() >= 100) {
// Second background change (after 2nd 100 points)
game.setBackgroundColor(0x1a4020); // 3rd background color
game.backgroundChanged = 2;
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 3rd background
enemyTypes = ['seeker', 'dasher'];
} else if (game.backgroundChanged === 2 && LK.getScore() >= 100) {
// Third background change (after 3rd 100 points)
game.setBackgroundColor(0x40201a); // 4th background color
game.backgroundChanged = 3;
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 4th background
enemyTypes = ['seeker', 'spinner'];
} else if (game.backgroundChanged === 3 && LK.getScore() >= 100) {
// Fourth background change (after 4th 100 points)
game.setBackgroundColor(0x204040); // 5th background color
game.backgroundChanged = 4;
for (var ei = enemies.length - 1; ei >= 0; ei--) {
enemies[ei].destroy();
}
enemies = [];
LK.setScore(0);
scoreTxt.setText('0');
stage = 1;
spawnInterval = 90;
fireInterval = 18;
elapsedFrames = 0;
difficultyTimer = 0;
// Set new enemy types for 5th background
enemyTypes = ['dasher', 'spinner'];
}
}
continue;
}
bullet.lastIntersecting = hitTarget;
// Check hit on enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
LK.getSound('enemyDown').play();
bullet.destroy();
playerBullets.splice(i, 1);
enemy.takeDamage();
if (enemy.health <= 0) {
enemy.destroy();
enemies.splice(j, 1);
}
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Remove if off screen (for dashers)
if (enemy.x < -200 || enemy.x > 2048 + 200 || enemy.y < -200 || enemy.y > 2732 + 200) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with player
var hitPlayer = enemy.intersects(player);
if (!enemy.lastIntersectingPlayer && hitPlayer) {
if (!playerShieldActive) {
playerHealth--;
LK.effects.flashScreen(0xff0000, 800);
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
}
}
enemy.lastIntersectingPlayer = hitPlayer;
// Check collision with central target (optional: could penalize or just remove)
var hitTarget = enemy.intersects(centralTarget);
if (!enemy.lastIntersectingTarget && hitTarget) {
// Remove enemy
enemy.destroy();
enemies.splice(i, 1);
continue;
}
enemy.lastIntersectingTarget = hitTarget;
}
};
// Play background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 1200
}
});