/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = 0; self.speedY = 0; self.rotation = 0; self.rotationSpeed = 0; self.update = function () { self.x += self.speedX; self.y += self.speedY; // Rotate bullet if it has rotation speed if (self.rotationSpeed !== 0) { bulletGraphics.rotation += self.rotationSpeed; } // Check if bullet is off-screen with padding if (self.x < -50 || self.x > 2048 + 50 || self.y < -50 || self.y > 2732 + 50) { return true; // Return true to indicate it should be removed } return false; }; return self; }); var Ghost = Container.expand(function () { var self = Container.call(this); var ghostGraphics = self.attachAsset('ghost', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); // Trail effect using small ghost shapes self.trail = []; self.trailMax = 5; self.trailInterval = 3; self.trailCounter = 0; self.invulnerable = false; self.invulnerableTime = 0; self.blinkInterval = 5; self.update = function () { // Handle trail effect self.trailCounter++; if (self.trailCounter >= self.trailInterval) { self.trailCounter = 0; // Add new trail element if (self.trail.length >= self.trailMax) { var oldest = self.trail.shift(); oldest.destroy(); } var trail = LK.getAsset('ghost', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, scaleX: 0.7, scaleY: 0.7, x: self.x, y: self.y }); game.addChildAt(trail, 0); self.trail.push(trail); // Fade out trail elements for (var i = 0; i < self.trail.length; i++) { var trailElement = self.trail[i]; tween(trailElement, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { // Cleanup happens in the shift above } }); } } // Handle invulnerability blinking if (self.invulnerable) { self.invulnerableTime--; if (self.invulnerableTime <= 0) { self.invulnerable = false; ghostGraphics.alpha = 0.8; } else { // Set transparency only during invulnerability ghostGraphics.alpha = 0.3; // Blink effect if (self.invulnerableTime % self.blinkInterval === 0) { ghostGraphics.alpha = ghostGraphics.alpha < 0.4 ? 0.8 : 0.3; } } } }; self.makeInvulnerable = function (time) { self.invulnerable = true; self.invulnerableTime = time; }; return self; }); var WaveIndicator = Container.expand(function () { var self = Container.call(this); var indicatorGraphic = self.attachAsset('wave_indicator', { anchorX: 0.5, anchorY: 0.5 }); self.active = false; self.setActive = function (isActive) { self.active = isActive; if (isActive) { tween(indicatorGraphic, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 300, onFinish: function onFinish() { tween(indicatorGraphic, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); } else { indicatorGraphic.alpha = 0.4; indicatorGraphic.scaleX = 1; indicatorGraphic.scaleY = 1; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000033 }); /**** * Game Code ****/ // Background var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(background); // Game state var state = { score: 0, wave: 1, waveTimer: 0, difficulty: 1, isGameOver: false, isPaused: false, highScore: storage.highScore || 0 }; // Player var ghost = new Ghost(); ghost.x = 2048 / 2; ghost.y = 2732 / 2; game.addChild(ghost); // Add wave indicators var waveIndicators = []; var indicatorSpacing = 30; var totalIndicators = 5; var indicatorStartX = (2048 - (totalIndicators - 1) * indicatorSpacing) / 2; for (var i = 0; i < totalIndicators; i++) { var indicator = new WaveIndicator(); indicator.x = indicatorStartX + i * indicatorSpacing; indicator.y = 50; indicator.setActive(i === 0); // Set first one active waveIndicators.push(indicator); LK.gui.top.addChild(indicator); } // Bullet management var bullets = []; var patterns = { // Wave patterns circle: function circle(centerX, centerY, count, speed, size) { var angleStep = Math.PI * 2 / count; for (var i = 0; i < count; i++) { var angle = i * angleStep; createBullet(centerX, centerY, Math.cos(angle) * speed, Math.sin(angle) * speed, size); } }, spiral: function spiral(centerX, centerY, count, speed, size) { var timeout = 0; var angleStep = Math.PI * 2 / count; for (var i = 0; i < count; i++) { (function (index) { LK.setTimeout(function () { var angle = index * angleStep; createBullet(centerX, centerY, Math.cos(angle) * speed, Math.sin(angle) * speed, size); }, timeout); timeout += 100; })(i); } }, random: function random(count, speed, size) { for (var i = 0; i < count; i++) { var side = Math.floor(Math.random() * 4); // 0:top, 1:right, 2:bottom, 3:left var startX, startY; var targetX = ghost.x + (Math.random() * 100 - 50); var targetY = ghost.y + (Math.random() * 100 - 50); switch (side) { case 0: // Top startX = Math.random() * 2048; startY = -50; break; case 1: // Right startX = 2048 + 50; startY = Math.random() * 2732; break; case 2: // Bottom startX = Math.random() * 2048; startY = 2732 + 50; break; case 3: // Left startX = -50; startY = Math.random() * 2732; break; } var angle = Math.atan2(targetY - startY, targetX - startX); createBullet(startX, startY, Math.cos(angle) * speed, Math.sin(angle) * speed, size, 0.05); } }, aimed: function aimed(count, speed, size) { var angleSpread = Math.PI / 4; // 45 degree spread for (var i = 0; i < count; i++) { var side = Math.floor(Math.random() * 4); var startX, startY; switch (side) { case 0: // Top startX = Math.random() * 2048; startY = -50; break; case 1: // Right startX = 2048 + 50; startY = Math.random() * 2732; break; case 2: // Bottom startX = Math.random() * 2048; startY = 2732 + 50; break; case 3: // Left startX = -50; startY = Math.random() * 2732; break; } var baseAngle = Math.atan2(ghost.y - startY, ghost.x - startX); var angleOffset = angleSpread * (Math.random() - 0.5); var angle = baseAngle + angleOffset; createBullet(startX, startY, Math.cos(angle) * speed, Math.sin(angle) * speed, size); } } }; function createBullet(x, y, speedX, speedY, scale, rotationSpeed) { var bullet = new Bullet(); bullet.x = x; bullet.y = y; bullet.speedX = speedX; bullet.speedY = speedY; bullet.rotationSpeed = rotationSpeed || 0; if (scale) { bullet.scale.set(scale); } bullets.push(bullet); game.addChild(bullet); return bullet; } function spawnWave() { var baseCount = 10; var baseSpeed = 3; // Update wave indicators var currentWaveIndex = (state.wave - 1) % totalIndicators; for (var i = 0; i < waveIndicators.length; i++) { waveIndicators[i].setActive(i === currentWaveIndex); } // Increase difficulty with each wave var count = Math.floor(baseCount * Math.sqrt(state.difficulty)); var speed = baseSpeed * (1 + (state.difficulty - 1) * 0.1); // Play wave complete sound if not the first wave if (state.wave > 1) { LK.getSound('wave_complete').play(); } // Choose pattern based on wave switch ((state.wave - 1) % 4) { case 0: patterns.circle(2048 / 2, 2732 / 2, count, speed, 1); break; case 1: patterns.spiral(2048 / 2, 2732 / 2, count, speed, 1); break; case 2: patterns.random(count, speed, 1); break; case 3: patterns.aimed(count, speed, 1); break; } // Make player briefly invulnerable after a new wave ghost.makeInvulnerable(60); // Increment difficulty state.difficulty += 0.2; // Set timer for next wave state.waveTimer = Math.max(180, 300 - state.wave * 10); // Decrease time between waves } // Initialize scoreboard var scoreTxt = new Text2('Score: 0', { size: 50, fill: 0xFFFFFF }); scoreTxt.anchor.set(1, 0); // Anchor to top-right LK.gui.topRight.addChild(scoreTxt); // Wave indicator text var waveTxt = new Text2('Wave: 1', { size: 50, fill: 0xFFFFFF }); waveTxt.anchor.set(0, 0); // Anchor to top-left LK.gui.top.addChild(waveTxt); // Allow dragging of the ghost var isDragging = false; // Handle input game.down = function (x, y) { if (!state.isPaused && !state.isGameOver) { // Teleport ghost to mouse position ghost.x = x; ghost.y = y; // Make ghost invulnerable and semi-transparent for 1 second ghost.makeInvulnerable(60); } }; game.move = function (x, y) { if (!state.isPaused && !state.isGameOver) { // Continuously update ghost position to follow the mouse ghost.x = x; ghost.y = y; } }; game.up = function () { // Removed dragging state reset }; // Main game update function game.update = function () { if (state.isGameOver) { return; } // Increment score state.score++; scoreTxt.setText('Score: ' + state.score); waveTxt.setText('Wave: ' + state.wave); // Wave timer logic if (state.waveTimer > 0) { state.waveTimer--; if (state.waveTimer === 0 || bullets.length === 0) { state.wave++; spawnWave(); } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.update()) { // Bullet is off-screen, remove it bullet.destroy(); bullets.splice(i, 1); continue; } // Check collision with ghost if (!ghost.invulnerable && bullet.intersects(ghost)) { // Handle collision LK.getSound('hit').play(); // Flash screen LK.effects.flashScreen(0xff0000, 500); // Game over state.isGameOver = true; // Update high score if (state.score > state.highScore) { state.highScore = state.score; storage.highScore = state.score; } // Play game over sound LK.getSound('game_over').play(); // Show game over screen after a short delay LK.setTimeout(function () { LK.showGameOver(); }, 1000); break; } } }; // Start first wave spawnWave(); // Play background music LK.playMusic('game_music', { fade: { start: 0, end: 0.4, duration: 1000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.rotation = 0;
self.rotationSpeed = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Rotate bullet if it has rotation speed
if (self.rotationSpeed !== 0) {
bulletGraphics.rotation += self.rotationSpeed;
}
// Check if bullet is off-screen with padding
if (self.x < -50 || self.x > 2048 + 50 || self.y < -50 || self.y > 2732 + 50) {
return true; // Return true to indicate it should be removed
}
return false;
};
return self;
});
var Ghost = Container.expand(function () {
var self = Container.call(this);
var ghostGraphics = self.attachAsset('ghost', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
// Trail effect using small ghost shapes
self.trail = [];
self.trailMax = 5;
self.trailInterval = 3;
self.trailCounter = 0;
self.invulnerable = false;
self.invulnerableTime = 0;
self.blinkInterval = 5;
self.update = function () {
// Handle trail effect
self.trailCounter++;
if (self.trailCounter >= self.trailInterval) {
self.trailCounter = 0;
// Add new trail element
if (self.trail.length >= self.trailMax) {
var oldest = self.trail.shift();
oldest.destroy();
}
var trail = LK.getAsset('ghost', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: 0.7,
scaleY: 0.7,
x: self.x,
y: self.y
});
game.addChildAt(trail, 0);
self.trail.push(trail);
// Fade out trail elements
for (var i = 0; i < self.trail.length; i++) {
var trailElement = self.trail[i];
tween(trailElement, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
// Cleanup happens in the shift above
}
});
}
}
// Handle invulnerability blinking
if (self.invulnerable) {
self.invulnerableTime--;
if (self.invulnerableTime <= 0) {
self.invulnerable = false;
ghostGraphics.alpha = 0.8;
} else {
// Set transparency only during invulnerability
ghostGraphics.alpha = 0.3;
// Blink effect
if (self.invulnerableTime % self.blinkInterval === 0) {
ghostGraphics.alpha = ghostGraphics.alpha < 0.4 ? 0.8 : 0.3;
}
}
}
};
self.makeInvulnerable = function (time) {
self.invulnerable = true;
self.invulnerableTime = time;
};
return self;
});
var WaveIndicator = Container.expand(function () {
var self = Container.call(this);
var indicatorGraphic = self.attachAsset('wave_indicator', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = false;
self.setActive = function (isActive) {
self.active = isActive;
if (isActive) {
tween(indicatorGraphic, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
onFinish: function onFinish() {
tween(indicatorGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
} else {
indicatorGraphic.alpha = 0.4;
indicatorGraphic.scaleX = 1;
indicatorGraphic.scaleY = 1;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000033
});
/****
* Game Code
****/
// Background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
// Game state
var state = {
score: 0,
wave: 1,
waveTimer: 0,
difficulty: 1,
isGameOver: false,
isPaused: false,
highScore: storage.highScore || 0
};
// Player
var ghost = new Ghost();
ghost.x = 2048 / 2;
ghost.y = 2732 / 2;
game.addChild(ghost);
// Add wave indicators
var waveIndicators = [];
var indicatorSpacing = 30;
var totalIndicators = 5;
var indicatorStartX = (2048 - (totalIndicators - 1) * indicatorSpacing) / 2;
for (var i = 0; i < totalIndicators; i++) {
var indicator = new WaveIndicator();
indicator.x = indicatorStartX + i * indicatorSpacing;
indicator.y = 50;
indicator.setActive(i === 0); // Set first one active
waveIndicators.push(indicator);
LK.gui.top.addChild(indicator);
}
// Bullet management
var bullets = [];
var patterns = {
// Wave patterns
circle: function circle(centerX, centerY, count, speed, size) {
var angleStep = Math.PI * 2 / count;
for (var i = 0; i < count; i++) {
var angle = i * angleStep;
createBullet(centerX, centerY, Math.cos(angle) * speed, Math.sin(angle) * speed, size);
}
},
spiral: function spiral(centerX, centerY, count, speed, size) {
var timeout = 0;
var angleStep = Math.PI * 2 / count;
for (var i = 0; i < count; i++) {
(function (index) {
LK.setTimeout(function () {
var angle = index * angleStep;
createBullet(centerX, centerY, Math.cos(angle) * speed, Math.sin(angle) * speed, size);
}, timeout);
timeout += 100;
})(i);
}
},
random: function random(count, speed, size) {
for (var i = 0; i < count; i++) {
var side = Math.floor(Math.random() * 4); // 0:top, 1:right, 2:bottom, 3:left
var startX, startY;
var targetX = ghost.x + (Math.random() * 100 - 50);
var targetY = ghost.y + (Math.random() * 100 - 50);
switch (side) {
case 0:
// Top
startX = Math.random() * 2048;
startY = -50;
break;
case 1:
// Right
startX = 2048 + 50;
startY = Math.random() * 2732;
break;
case 2:
// Bottom
startX = Math.random() * 2048;
startY = 2732 + 50;
break;
case 3:
// Left
startX = -50;
startY = Math.random() * 2732;
break;
}
var angle = Math.atan2(targetY - startY, targetX - startX);
createBullet(startX, startY, Math.cos(angle) * speed, Math.sin(angle) * speed, size, 0.05);
}
},
aimed: function aimed(count, speed, size) {
var angleSpread = Math.PI / 4; // 45 degree spread
for (var i = 0; i < count; i++) {
var side = Math.floor(Math.random() * 4);
var startX, startY;
switch (side) {
case 0:
// Top
startX = Math.random() * 2048;
startY = -50;
break;
case 1:
// Right
startX = 2048 + 50;
startY = Math.random() * 2732;
break;
case 2:
// Bottom
startX = Math.random() * 2048;
startY = 2732 + 50;
break;
case 3:
// Left
startX = -50;
startY = Math.random() * 2732;
break;
}
var baseAngle = Math.atan2(ghost.y - startY, ghost.x - startX);
var angleOffset = angleSpread * (Math.random() - 0.5);
var angle = baseAngle + angleOffset;
createBullet(startX, startY, Math.cos(angle) * speed, Math.sin(angle) * speed, size);
}
}
};
function createBullet(x, y, speedX, speedY, scale, rotationSpeed) {
var bullet = new Bullet();
bullet.x = x;
bullet.y = y;
bullet.speedX = speedX;
bullet.speedY = speedY;
bullet.rotationSpeed = rotationSpeed || 0;
if (scale) {
bullet.scale.set(scale);
}
bullets.push(bullet);
game.addChild(bullet);
return bullet;
}
function spawnWave() {
var baseCount = 10;
var baseSpeed = 3;
// Update wave indicators
var currentWaveIndex = (state.wave - 1) % totalIndicators;
for (var i = 0; i < waveIndicators.length; i++) {
waveIndicators[i].setActive(i === currentWaveIndex);
}
// Increase difficulty with each wave
var count = Math.floor(baseCount * Math.sqrt(state.difficulty));
var speed = baseSpeed * (1 + (state.difficulty - 1) * 0.1);
// Play wave complete sound if not the first wave
if (state.wave > 1) {
LK.getSound('wave_complete').play();
}
// Choose pattern based on wave
switch ((state.wave - 1) % 4) {
case 0:
patterns.circle(2048 / 2, 2732 / 2, count, speed, 1);
break;
case 1:
patterns.spiral(2048 / 2, 2732 / 2, count, speed, 1);
break;
case 2:
patterns.random(count, speed, 1);
break;
case 3:
patterns.aimed(count, speed, 1);
break;
}
// Make player briefly invulnerable after a new wave
ghost.makeInvulnerable(60);
// Increment difficulty
state.difficulty += 0.2;
// Set timer for next wave
state.waveTimer = Math.max(180, 300 - state.wave * 10); // Decrease time between waves
}
// Initialize scoreboard
var scoreTxt = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0); // Anchor to top-right
LK.gui.topRight.addChild(scoreTxt);
// Wave indicator text
var waveTxt = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFFFF
});
waveTxt.anchor.set(0, 0); // Anchor to top-left
LK.gui.top.addChild(waveTxt);
// Allow dragging of the ghost
var isDragging = false;
// Handle input
game.down = function (x, y) {
if (!state.isPaused && !state.isGameOver) {
// Teleport ghost to mouse position
ghost.x = x;
ghost.y = y;
// Make ghost invulnerable and semi-transparent for 1 second
ghost.makeInvulnerable(60);
}
};
game.move = function (x, y) {
if (!state.isPaused && !state.isGameOver) {
// Continuously update ghost position to follow the mouse
ghost.x = x;
ghost.y = y;
}
};
game.up = function () {
// Removed dragging state reset
};
// Main game update function
game.update = function () {
if (state.isGameOver) {
return;
}
// Increment score
state.score++;
scoreTxt.setText('Score: ' + state.score);
waveTxt.setText('Wave: ' + state.wave);
// Wave timer logic
if (state.waveTimer > 0) {
state.waveTimer--;
if (state.waveTimer === 0 || bullets.length === 0) {
state.wave++;
spawnWave();
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.update()) {
// Bullet is off-screen, remove it
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with ghost
if (!ghost.invulnerable && bullet.intersects(ghost)) {
// Handle collision
LK.getSound('hit').play();
// Flash screen
LK.effects.flashScreen(0xff0000, 500);
// Game over
state.isGameOver = true;
// Update high score
if (state.score > state.highScore) {
state.highScore = state.score;
storage.highScore = state.score;
}
// Play game over sound
LK.getSound('game_over').play();
// Show game over screen after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
break;
}
}
};
// Start first wave
spawnWave();
// Play background music
LK.playMusic('game_music', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});