/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Broom = Container.expand(function () { var self = Container.call(this); var broomGraphics = self.attachAsset('broom', { anchorX: 0.5, anchorY: 0.8, // Anchor at bottom for sweeping feel rotation: Math.PI / 4 // 45-degree angle }); // Trail to make the broom more visible self.lastPositions = []; self.maxTrailLength = 5; // Sweeping animation self.isSweeping = false; self.sweepAngle = Math.PI / 4; self.sweepDirection = 1; self.sweepSpeed = 0.1; self.startSweeping = function () { self.isSweeping = true; }; self.stopSweeping = function () { self.isSweeping = false; // Reset broom rotation tween(broomGraphics, { rotation: Math.PI / 4 }, { duration: 300, easing: tween.easeOut }); }; self.update = function () { // Update trail if (self.lastPositions.length >= self.maxTrailLength) { self.lastPositions.shift(); } self.lastPositions.push({ x: self.x, y: self.y }); // Update sweeping animation if (self.isSweeping) { broomGraphics.rotation += self.sweepDirection * self.sweepSpeed; if (broomGraphics.rotation > Math.PI / 3 || broomGraphics.rotation < Math.PI / 6) { self.sweepDirection *= -1; } } }; 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 }); // Ghost properties self.speed = 3; self.targetX = 0; self.targetY = 0; self.setTarget = function (x, y) { self.targetX = x; self.targetY = y; }; // Movement behavior self.moveRandomly = function () { if (Math.random() < 0.02 || Math.abs(self.x - self.targetX) < 10 && Math.abs(self.y - self.targetY) < 10) { self.setTarget(Math.random() * 1948 + 50, // Keep away from edges Math.random() * 2532 + 100 // Keep away from top ); } }; // Update every frame self.update = function () { // Move toward target var dx = self.targetX - self.x; var dy = self.targetY - 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; } // Bobbing animation self.y += Math.sin(LK.ticks / 20) * 0.5; // Fading in and out slightly ghostGraphics.alpha = 0.7 + Math.sin(LK.ticks / 30) * 0.3; }; // Flash and disappear when captured self.capture = function () { var captureEffect = LK.getAsset('captureEffect', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); self.addChild(captureEffect); // Capture animation tween(captureEffect, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { captureEffect.destroy(); } }); // Ghost disappearing animation tween(ghostGraphics, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500, easing: tween.easeIn }); // Play capture sound LK.getSound('ghostCapture').play(); return true; }; return self; }); var HUD = Container.expand(function () { var self = Container.call(this); // Score display self.scoreText = new Text2('0', { size: 120, fill: 0xFFFFFF }); self.scoreText.anchor.set(0.5, 0); // Level display self.levelText = new Text2('Level: 1', { size: 80, fill: 0xFFFFFF }); self.levelText.anchor.set(0, 0); // Lives display self.livesText = new Text2('Lives: 3', { size: 80, fill: 0xFFFFFF }); self.livesText.anchor.set(1, 0); // Update HUD self.updateScore = function (score) { self.scoreText.setText(score.toString()); }; self.updateLevel = function (level) { self.levelText.setText('Level: ' + level); }; self.updateLives = function (lives) { self.livesText.setText('Lives: ' + lives); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state variables var score = 0; var level = 1; var lives = 3; var ghostCaptureRadius = 100; // How close broom needs to be to ghost to capture it var ghostCount = 0; var maxGhosts = 5; // Starting max ghosts var ghostSpawnInterval = 3000; // ms between ghost spawns var lastGhostSpawnTime = 0; var isGameActive = true; // Set up background var houseBackground = LK.getAsset('house', { anchorX: 0, anchorY: 0 }); game.addChild(houseBackground); // Create player's broom var broom = new Broom(); game.addChild(broom); broom.x = 2048 / 2; broom.y = 2732 / 2; // Create ghosts array var ghosts = []; // Create HUD var hud = new HUD(); LK.gui.top.addChild(hud.scoreText); LK.gui.topLeft.addChild(hud.levelText); LK.gui.topRight.addChild(hud.livesText); // Position HUD elements hud.scoreText.x = 2048 / 2; hud.scoreText.y = 20; hud.levelText.x = 120; // Keep away from top-left menu icon hud.levelText.y = 20; hud.livesText.x = 2048 - 20; hud.livesText.y = 20; // Update HUD with initial values hud.updateScore(score); hud.updateLevel(level); hud.updateLives(lives); // Play background music LK.playMusic('hauntedMusic'); // Spawn a new ghost function spawnGhost() { if (ghosts.length >= maxGhosts || !isGameActive) return; var ghost = new Ghost(); // Decide spawn location (from edge of screen) var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top ghost.x = Math.random() * 2048; ghost.y = -100; break; case 1: // Right ghost.x = 2048 + 100; ghost.y = Math.random() * 2732; break; case 2: // Bottom ghost.x = Math.random() * 2048; ghost.y = 2732 + 100; break; case 3: // Left ghost.x = -100; ghost.y = Math.random() * 2732; break; } // Initial target is center of screen ghost.setTarget(Math.random() * 1948 + 50, Math.random() * 2532 + 100); // Speed increases with level ghost.speed = 3 + level * 0.5; ghosts.push(ghost); game.addChild(ghost); ghostCount++; // Play ghost appear sound LK.getSound('ghostAppear').play(); // Ghost fades in ghost.alpha = 0; tween(ghost, { alpha: 1 }, { duration: 500, easing: tween.easeIn }); } // Handle input for broom movement var isDragging = false; game.down = function (x, y) { isDragging = true; broom.x = x; broom.y = y; broom.startSweeping(); }; game.move = function (x, y) { if (isDragging) { broom.x = x; broom.y = y; } }; game.up = function () { isDragging = false; broom.stopSweeping(); }; // Check if player captured a ghost function checkGhostCapture() { for (var i = ghosts.length - 1; i >= 0; i--) { var ghost = ghosts[i]; var distance = Math.sqrt(Math.pow(ghost.x - broom.x, 2) + Math.pow(ghost.y - broom.y, 2)); if (distance < ghostCaptureRadius && isDragging) { // Capture the ghost if (ghost.capture()) { score += 10 * level; hud.updateScore(score); // Remove the ghost after capture animation LK.setTimeout(function () { game.removeChild(ghost); ghosts.splice(i, 1); ghostCount--; // Check for level completion if (ghostCount === 0 && isGameActive) { levelUp(); } }, 500); } } } } // Check if ghost hit the player function checkPlayerCollision() { if (!isDragging) return; // Only check collisions when player is active for (var i = 0; i < ghosts.length; i++) { var ghost = ghosts[i]; var distance = Math.sqrt(Math.pow(ghost.x - broom.x, 2) + Math.pow(ghost.y - broom.y, 2)); if (distance < 40 && !ghost.isBeingCaptured) { // Player hit by ghost LK.getSound('playerHit').play(); LK.effects.flashScreen(0xFF0000, 500); lives--; hud.updateLives(lives); // Check game over if (lives <= 0) { isGameActive = false; LK.setScore(score); LK.showGameOver(); } // Temporary invulnerability break; } } } // Level up function function levelUp() { level++; hud.updateLevel(level); // Increase difficulty maxGhosts = 5 + level; ghostSpawnInterval = Math.max(500, 3000 - level * 200); // Visual feedback for level up LK.effects.flashScreen(0x00FF00, 500); // Reset ghost counter ghostCount = 0; } // Main game update loop game.update = function () { // Update broom broom.update(); // Update all ghosts for (var i = 0; i < ghosts.length; i++) { var ghost = ghosts[i]; ghost.moveRandomly(); ghost.update(); } // Spawn ghosts on interval var currentTime = Date.now(); if (currentTime - lastGhostSpawnTime > ghostSpawnInterval && isGameActive) { spawnGhost(); lastGhostSpawnTime = currentTime; } // Check for ghost captures checkGhostCapture(); // Check for player collisions checkPlayerCollision(); }; // Initialize by spawning first ghost lastGhostSpawnTime = Date.now(); spawnGhost();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Broom = Container.expand(function () {
var self = Container.call(this);
var broomGraphics = self.attachAsset('broom', {
anchorX: 0.5,
anchorY: 0.8,
// Anchor at bottom for sweeping feel
rotation: Math.PI / 4 // 45-degree angle
});
// Trail to make the broom more visible
self.lastPositions = [];
self.maxTrailLength = 5;
// Sweeping animation
self.isSweeping = false;
self.sweepAngle = Math.PI / 4;
self.sweepDirection = 1;
self.sweepSpeed = 0.1;
self.startSweeping = function () {
self.isSweeping = true;
};
self.stopSweeping = function () {
self.isSweeping = false;
// Reset broom rotation
tween(broomGraphics, {
rotation: Math.PI / 4
}, {
duration: 300,
easing: tween.easeOut
});
};
self.update = function () {
// Update trail
if (self.lastPositions.length >= self.maxTrailLength) {
self.lastPositions.shift();
}
self.lastPositions.push({
x: self.x,
y: self.y
});
// Update sweeping animation
if (self.isSweeping) {
broomGraphics.rotation += self.sweepDirection * self.sweepSpeed;
if (broomGraphics.rotation > Math.PI / 3 || broomGraphics.rotation < Math.PI / 6) {
self.sweepDirection *= -1;
}
}
};
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
});
// Ghost properties
self.speed = 3;
self.targetX = 0;
self.targetY = 0;
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
};
// Movement behavior
self.moveRandomly = function () {
if (Math.random() < 0.02 || Math.abs(self.x - self.targetX) < 10 && Math.abs(self.y - self.targetY) < 10) {
self.setTarget(Math.random() * 1948 + 50,
// Keep away from edges
Math.random() * 2532 + 100 // Keep away from top
);
}
};
// Update every frame
self.update = function () {
// Move toward target
var dx = self.targetX - self.x;
var dy = self.targetY - 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;
}
// Bobbing animation
self.y += Math.sin(LK.ticks / 20) * 0.5;
// Fading in and out slightly
ghostGraphics.alpha = 0.7 + Math.sin(LK.ticks / 30) * 0.3;
};
// Flash and disappear when captured
self.capture = function () {
var captureEffect = LK.getAsset('captureEffect', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.addChild(captureEffect);
// Capture animation
tween(captureEffect, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
captureEffect.destroy();
}
});
// Ghost disappearing animation
tween(ghostGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn
});
// Play capture sound
LK.getSound('ghostCapture').play();
return true;
};
return self;
});
var HUD = Container.expand(function () {
var self = Container.call(this);
// Score display
self.scoreText = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
self.scoreText.anchor.set(0.5, 0);
// Level display
self.levelText = new Text2('Level: 1', {
size: 80,
fill: 0xFFFFFF
});
self.levelText.anchor.set(0, 0);
// Lives display
self.livesText = new Text2('Lives: 3', {
size: 80,
fill: 0xFFFFFF
});
self.livesText.anchor.set(1, 0);
// Update HUD
self.updateScore = function (score) {
self.scoreText.setText(score.toString());
};
self.updateLevel = function (level) {
self.levelText.setText('Level: ' + level);
};
self.updateLives = function (lives) {
self.livesText.setText('Lives: ' + lives);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state variables
var score = 0;
var level = 1;
var lives = 3;
var ghostCaptureRadius = 100; // How close broom needs to be to ghost to capture it
var ghostCount = 0;
var maxGhosts = 5; // Starting max ghosts
var ghostSpawnInterval = 3000; // ms between ghost spawns
var lastGhostSpawnTime = 0;
var isGameActive = true;
// Set up background
var houseBackground = LK.getAsset('house', {
anchorX: 0,
anchorY: 0
});
game.addChild(houseBackground);
// Create player's broom
var broom = new Broom();
game.addChild(broom);
broom.x = 2048 / 2;
broom.y = 2732 / 2;
// Create ghosts array
var ghosts = [];
// Create HUD
var hud = new HUD();
LK.gui.top.addChild(hud.scoreText);
LK.gui.topLeft.addChild(hud.levelText);
LK.gui.topRight.addChild(hud.livesText);
// Position HUD elements
hud.scoreText.x = 2048 / 2;
hud.scoreText.y = 20;
hud.levelText.x = 120; // Keep away from top-left menu icon
hud.levelText.y = 20;
hud.livesText.x = 2048 - 20;
hud.livesText.y = 20;
// Update HUD with initial values
hud.updateScore(score);
hud.updateLevel(level);
hud.updateLives(lives);
// Play background music
LK.playMusic('hauntedMusic');
// Spawn a new ghost
function spawnGhost() {
if (ghosts.length >= maxGhosts || !isGameActive) return;
var ghost = new Ghost();
// Decide spawn location (from edge of screen)
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
ghost.x = Math.random() * 2048;
ghost.y = -100;
break;
case 1:
// Right
ghost.x = 2048 + 100;
ghost.y = Math.random() * 2732;
break;
case 2:
// Bottom
ghost.x = Math.random() * 2048;
ghost.y = 2732 + 100;
break;
case 3:
// Left
ghost.x = -100;
ghost.y = Math.random() * 2732;
break;
}
// Initial target is center of screen
ghost.setTarget(Math.random() * 1948 + 50, Math.random() * 2532 + 100);
// Speed increases with level
ghost.speed = 3 + level * 0.5;
ghosts.push(ghost);
game.addChild(ghost);
ghostCount++;
// Play ghost appear sound
LK.getSound('ghostAppear').play();
// Ghost fades in
ghost.alpha = 0;
tween(ghost, {
alpha: 1
}, {
duration: 500,
easing: tween.easeIn
});
}
// Handle input for broom movement
var isDragging = false;
game.down = function (x, y) {
isDragging = true;
broom.x = x;
broom.y = y;
broom.startSweeping();
};
game.move = function (x, y) {
if (isDragging) {
broom.x = x;
broom.y = y;
}
};
game.up = function () {
isDragging = false;
broom.stopSweeping();
};
// Check if player captured a ghost
function checkGhostCapture() {
for (var i = ghosts.length - 1; i >= 0; i--) {
var ghost = ghosts[i];
var distance = Math.sqrt(Math.pow(ghost.x - broom.x, 2) + Math.pow(ghost.y - broom.y, 2));
if (distance < ghostCaptureRadius && isDragging) {
// Capture the ghost
if (ghost.capture()) {
score += 10 * level;
hud.updateScore(score);
// Remove the ghost after capture animation
LK.setTimeout(function () {
game.removeChild(ghost);
ghosts.splice(i, 1);
ghostCount--;
// Check for level completion
if (ghostCount === 0 && isGameActive) {
levelUp();
}
}, 500);
}
}
}
}
// Check if ghost hit the player
function checkPlayerCollision() {
if (!isDragging) return; // Only check collisions when player is active
for (var i = 0; i < ghosts.length; i++) {
var ghost = ghosts[i];
var distance = Math.sqrt(Math.pow(ghost.x - broom.x, 2) + Math.pow(ghost.y - broom.y, 2));
if (distance < 40 && !ghost.isBeingCaptured) {
// Player hit by ghost
LK.getSound('playerHit').play();
LK.effects.flashScreen(0xFF0000, 500);
lives--;
hud.updateLives(lives);
// Check game over
if (lives <= 0) {
isGameActive = false;
LK.setScore(score);
LK.showGameOver();
}
// Temporary invulnerability
break;
}
}
}
// Level up function
function levelUp() {
level++;
hud.updateLevel(level);
// Increase difficulty
maxGhosts = 5 + level;
ghostSpawnInterval = Math.max(500, 3000 - level * 200);
// Visual feedback for level up
LK.effects.flashScreen(0x00FF00, 500);
// Reset ghost counter
ghostCount = 0;
}
// Main game update loop
game.update = function () {
// Update broom
broom.update();
// Update all ghosts
for (var i = 0; i < ghosts.length; i++) {
var ghost = ghosts[i];
ghost.moveRandomly();
ghost.update();
}
// Spawn ghosts on interval
var currentTime = Date.now();
if (currentTime - lastGhostSpawnTime > ghostSpawnInterval && isGameActive) {
spawnGhost();
lastGhostSpawnTime = currentTime;
}
// Check for ghost captures
checkGhostCapture();
// Check for player collisions
checkPlayerCollision();
};
// Initialize by spawning first ghost
lastGhostSpawnTime = Date.now();
spawnGhost();