/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ var BloodParticle = Container.expand(function () { var self = Container.call(this); var bloodGraphics = self.attachAsset('blood', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2 + Math.random() * 0.6, scaleY: 0.2 + Math.random() * 0.6, alpha: 0.7 + Math.random() * 0.3 }); // Random velocity self.vx = (Math.random() - 0.5) * 40; self.vy = (Math.random() - 0.5) * 40; self.lifetime = 1000 + Math.random() * 2000; self.createdAt = Date.now(); self.update = function () { self.x += self.vx; self.y += self.vy; // Slow down self.vx *= 0.95; self.vy *= 0.95; // Fade out based on lifetime var age = Date.now() - self.createdAt; if (age > self.lifetime) { self.destroy(); var index = bloodParticles.indexOf(self); if (index !== -1) { bloodParticles.splice(index, 1); } } else if (age > self.lifetime * 0.7) { bloodGraphics.alpha = 1 - (age - self.lifetime * 0.7) / (self.lifetime * 0.3); } }; return self; }); var Monster = Container.expand(function () { var self = Container.call(this); var monsterGraphics = self.attachAsset('monster', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); self.health = 5; self.growthRate = 0.004; self.maxScale = 5; self.isDying = false; self.baseScale = 0.5; // Apply a random tint to make monsters look different var tints = [0x4A2B0F, 0x7E3500, 0x5D4037, 0x330000, 0x2D0C01]; monsterGraphics.tint = tints[Math.floor(Math.random() * tints.length)]; self.update = function () { if (self.isDying) { return; } // Grow the monster if (monsterGraphics.scale.x < self.maxScale) { monsterGraphics.scale.x += self.growthRate; monsterGraphics.scale.y += self.growthRate; } else { // Monster has grown too big - game over! gameOver(); } }; self.punch = function () { if (self.isDying) { return; } self.health--; // Temporarily shrink the monster when punched var currentScale = monsterGraphics.scale.x; tween(monsterGraphics.scale, { x: currentScale * 0.8, y: currentScale * 0.8 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { if (!self.isDying) { tween(monsterGraphics.scale, { x: currentScale, y: currentScale }, { duration: 300, easing: tween.easeOut }); } } }); // Flash red tween(monsterGraphics, { tint: 0xFF0000 }, { duration: 100, onFinish: function onFinish() { if (!self.isDying) { tween(monsterGraphics, { tint: monsterGraphics.tint }, { duration: 200 }); } } }); // Add blood spray createBloodSpray(self.x, self.y, 8); // Play punch sound LK.getSound('punch').play(); // Check if monster is dead if (self.health <= 0) { self.die(); } }; self.die = function () { self.isDying = true; LK.getSound('monsterDeath').play(); // Create a big blood explosion createBloodSpray(self.x, self.y, 20); // Shrink and fade out tween(monsterGraphics.scale, { x: 0.1, y: 0.1 }, { duration: 500, easing: tween.easeIn }); tween(monsterGraphics, { alpha: 0 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { self.destroy(); monstersKilled++; updateScore(); // Spawn next monster after delay if (monsterSpawnTimeout) { LK.clearTimeout(monsterSpawnTimeout); } monsterSpawnTimeout = LK.setTimeout(spawnMonster, 1000); } }); }; self.down = function (x, y, obj) { self.punch(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xD3D3D3 }); /**** * Game Code ****/ /* Supported Types: 1. Shape: - Simple geometric figures with these properties: * width: (required) pixel width of the shape. * height: (required) pixel height of the shape. * color: (required) color of the shape. * shape: (required) type of shape. Valid options: 'box', 'ellipse'. 2. Image: - Imported images with these properties: * width: (required) pixel resolution width. * height: (required) pixel resolution height. * id: (required) identifier for the image. * flipX: (optional) horizontal flip. Valid values: 0 (no flip), 1 (flip). * flipY: (optional) vertical flip. Valid values: 0 (no flip), 1 (flip). * orientation: (optional) rotation in multiples of 90 degrees, clockwise. Valid values: - 0: No rotation. - 1: Rotate 90 degrees. - 2: Rotate 180 degrees. - 3: Rotate 270 degrees. Note: Width and height remain unchanged upon flipping. 3. Sound: - Sound effects with these properties: * id: (required) identifier for the sound. * volume: (optional) custom volume. Valid values are a float from 0 to 1. 4. Music: - In contract to sound effects, only one music can be played at a time - Music is using the same API to initilize just like sound. - Music loops by default - Music with these config options: * id: (required) identifier for the sound. * volume: (optional) custom volume. Valid values are a float from 0 to 1. * start: (optional) a float from 0 to 1 used for cropping and indicates the start of the cropping * end: (optional) a float from 0 to 1 used for cropping and indicates the end of the cropping */ // Game variables //{0.1} var monster = null; var bloodParticles = []; var monstersKilled = 0; var monsterSpawnTimeout = null; var difficultyLevel = 1; var isGameActive = true; var lastMonsterKillTime = 0; // UI elements var scoreTxt = new Text2('0', { size: 100, fill: 0xFF0000 }); var highScoreTxt = new Text2('Best: 0', { size: 60, fill: 0xFFFFFF }); var waveTxt = new Text2('Wave: 1', { size: 60, fill: 0xFFFFFF }); function initializeGame() { // Set up background var backgroundSprite = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 1.0 // Ensure maximum brightness by setting alpha to maximum }); game.addChild(backgroundSprite); // Set up score display scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 50; LK.gui.top.addChild(scoreTxt); // Set up high score display highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = 150; LK.gui.top.addChild(highScoreTxt); updateHighScore(); // Set up wave display waveTxt.anchor.set(0.5, 0); waveTxt.y = 220; LK.gui.top.addChild(waveTxt); // Start background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); // Spawn the first monster spawnMonster(); } function spawnMonster() { if (!isGameActive) { return; } // Create new monster monster = new Monster(); monster.x = 2048 / 2; monster.y = 2732 / 2; // Increase difficulty based on monsters killed if (monstersKilled > 0) { // Every 3 monsters, increase difficulty level if (monstersKilled % 3 === 0) { difficultyLevel++; waveTxt.setText('Wave: ' + difficultyLevel); LK.effects.flashScreen(0x330000, 500); } // Adjust monster properties based on difficulty monster.health = 5 + Math.min(5, difficultyLevel); monster.growthRate = 0.008 + difficultyLevel * 0.001; monster.baseScale = 0.5 + difficultyLevel * 0.03; monster.speed = 1 + difficultyLevel * 0.1; // Increase speed based on difficulty // Update monster's initial appearance var monsterGraphics = LK.getAsset('jumpscare', { anchorX: 0.5, anchorY: 0.5 }); monster.addChild(monsterGraphics); monsterGraphics.scale.x = monster.baseScale; monsterGraphics.scale.y = monster.baseScale; } // Add monster to game game.addChild(monster); LK.getSound('monsterGrowl').play(); } function createBloodSpray(x, y, count) { for (var i = 0; i < count; i++) { var blood = new BloodParticle(); blood.x = x; blood.y = y; bloodParticles.push(blood); game.addChild(blood); } } function updateScore() { scoreTxt.setText(monstersKilled.toString()); // Check if high score needs updating if (monstersKilled > storage.highScore) { storage.highScore = monstersKilled; updateHighScore(); } lastMonsterKillTime = Date.now(); } function updateHighScore() { highScoreTxt.setText('Best: ' + storage.highScore); } function gameOver() { if (!isGameActive) { return; } isGameActive = false; // Play a loud and distorted grainy scream sound LK.getSound('jumpscare').play(); // Fade music out LK.playMusic('bgMusic', { fade: { start: 0.3, end: 0, duration: 500 } }); // Flash screen red LK.effects.flashScreen(0xFF0000, 800); // Show jumpscare sprite before game over screen var jumpscareSprite = LK.getAsset('jumpscare', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 1.0, tint: 0xFF0000 // Tint the sprite red }); game.addChild(jumpscareSprite); // Make the jumpscare sprite shake continuously and violently function shakeSprite(sprite) { tween(sprite, { x: sprite.x + 40 }, { duration: 15, easing: tween.easeInOut, onFinish: function onFinish() { tween(sprite, { x: sprite.x - 80 }, { duration: 15, easing: tween.easeInOut, onFinish: function onFinish() { tween(sprite, { x: sprite.x + 40 }, { duration: 15, easing: tween.easeInOut, onFinish: function onFinish() { shakeSprite(sprite); // Recursively call to continue shaking } }); } }); } }); } shakeSprite(jumpscareSprite); LK.setTimeout(function () { jumpscareSprite.destroy(); LK.showGameOver(); }, 800); } // Initialize game on startup initializeGame(); // Event handling game.down = function (x, y, obj) { // This handles general taps on game area (not on specific monsters) // Not needed for now as monsters have their own down handler }; // Game update loop game.update = function () { // Update all blood particles for (var i = bloodParticles.length - 1; i >= 0; i--) { // BloodParticle.update() handles its own removal from the array if needed // We don't need to do anything here } // Check if we should increase difficulty over time // If a monster hasn't been killed in a while, make it grow faster if (monster && lastMonsterKillTime > 0) { var timeSinceLastKill = Date.now() - lastMonsterKillTime; if (timeSinceLastKill > 5000) { // Gradually increase growth rate if player is taking too long monster.growthRate *= 1.002; } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var BloodParticle = Container.expand(function () {
var self = Container.call(this);
var bloodGraphics = self.attachAsset('blood', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + Math.random() * 0.6,
scaleY: 0.2 + Math.random() * 0.6,
alpha: 0.7 + Math.random() * 0.3
});
// Random velocity
self.vx = (Math.random() - 0.5) * 40;
self.vy = (Math.random() - 0.5) * 40;
self.lifetime = 1000 + Math.random() * 2000;
self.createdAt = Date.now();
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Slow down
self.vx *= 0.95;
self.vy *= 0.95;
// Fade out based on lifetime
var age = Date.now() - self.createdAt;
if (age > self.lifetime) {
self.destroy();
var index = bloodParticles.indexOf(self);
if (index !== -1) {
bloodParticles.splice(index, 1);
}
} else if (age > self.lifetime * 0.7) {
bloodGraphics.alpha = 1 - (age - self.lifetime * 0.7) / (self.lifetime * 0.3);
}
};
return self;
});
var Monster = Container.expand(function () {
var self = Container.call(this);
var monsterGraphics = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
self.health = 5;
self.growthRate = 0.004;
self.maxScale = 5;
self.isDying = false;
self.baseScale = 0.5;
// Apply a random tint to make monsters look different
var tints = [0x4A2B0F, 0x7E3500, 0x5D4037, 0x330000, 0x2D0C01];
monsterGraphics.tint = tints[Math.floor(Math.random() * tints.length)];
self.update = function () {
if (self.isDying) {
return;
}
// Grow the monster
if (monsterGraphics.scale.x < self.maxScale) {
monsterGraphics.scale.x += self.growthRate;
monsterGraphics.scale.y += self.growthRate;
} else {
// Monster has grown too big - game over!
gameOver();
}
};
self.punch = function () {
if (self.isDying) {
return;
}
self.health--;
// Temporarily shrink the monster when punched
var currentScale = monsterGraphics.scale.x;
tween(monsterGraphics.scale, {
x: currentScale * 0.8,
y: currentScale * 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
if (!self.isDying) {
tween(monsterGraphics.scale, {
x: currentScale,
y: currentScale
}, {
duration: 300,
easing: tween.easeOut
});
}
}
});
// Flash red
tween(monsterGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
if (!self.isDying) {
tween(monsterGraphics, {
tint: monsterGraphics.tint
}, {
duration: 200
});
}
}
});
// Add blood spray
createBloodSpray(self.x, self.y, 8);
// Play punch sound
LK.getSound('punch').play();
// Check if monster is dead
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
self.isDying = true;
LK.getSound('monsterDeath').play();
// Create a big blood explosion
createBloodSpray(self.x, self.y, 20);
// Shrink and fade out
tween(monsterGraphics.scale, {
x: 0.1,
y: 0.1
}, {
duration: 500,
easing: tween.easeIn
});
tween(monsterGraphics, {
alpha: 0
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
monstersKilled++;
updateScore();
// Spawn next monster after delay
if (monsterSpawnTimeout) {
LK.clearTimeout(monsterSpawnTimeout);
}
monsterSpawnTimeout = LK.setTimeout(spawnMonster, 1000);
}
});
};
self.down = function (x, y, obj) {
self.punch();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xD3D3D3
});
/****
* Game Code
****/
/*
Supported Types:
1. Shape:
- Simple geometric figures with these properties:
* width: (required) pixel width of the shape.
* height: (required) pixel height of the shape.
* color: (required) color of the shape.
* shape: (required) type of shape. Valid options: 'box', 'ellipse'.
2. Image:
- Imported images with these properties:
* width: (required) pixel resolution width.
* height: (required) pixel resolution height.
* id: (required) identifier for the image.
* flipX: (optional) horizontal flip. Valid values: 0 (no flip), 1 (flip).
* flipY: (optional) vertical flip. Valid values: 0 (no flip), 1 (flip).
* orientation: (optional) rotation in multiples of 90 degrees, clockwise. Valid values:
- 0: No rotation.
- 1: Rotate 90 degrees.
- 2: Rotate 180 degrees.
- 3: Rotate 270 degrees.
Note: Width and height remain unchanged upon flipping.
3. Sound:
- Sound effects with these properties:
* id: (required) identifier for the sound.
* volume: (optional) custom volume. Valid values are a float from 0 to 1.
4. Music:
- In contract to sound effects, only one music can be played at a time
- Music is using the same API to initilize just like sound.
- Music loops by default
- Music with these config options:
* id: (required) identifier for the sound.
* volume: (optional) custom volume. Valid values are a float from 0 to 1.
* start: (optional) a float from 0 to 1 used for cropping and indicates the start of the cropping
* end: (optional) a float from 0 to 1 used for cropping and indicates the end of the cropping
*/
// Game variables
//{0.1}
var monster = null;
var bloodParticles = [];
var monstersKilled = 0;
var monsterSpawnTimeout = null;
var difficultyLevel = 1;
var isGameActive = true;
var lastMonsterKillTime = 0;
// UI elements
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFF0000
});
var highScoreTxt = new Text2('Best: 0', {
size: 60,
fill: 0xFFFFFF
});
var waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
function initializeGame() {
// Set up background
var backgroundSprite = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 1.0 // Ensure maximum brightness by setting alpha to maximum
});
game.addChild(backgroundSprite);
// Set up score display
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 50;
LK.gui.top.addChild(scoreTxt);
// Set up high score display
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 150;
LK.gui.top.addChild(highScoreTxt);
updateHighScore();
// Set up wave display
waveTxt.anchor.set(0.5, 0);
waveTxt.y = 220;
LK.gui.top.addChild(waveTxt);
// Start background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Spawn the first monster
spawnMonster();
}
function spawnMonster() {
if (!isGameActive) {
return;
}
// Create new monster
monster = new Monster();
monster.x = 2048 / 2;
monster.y = 2732 / 2;
// Increase difficulty based on monsters killed
if (monstersKilled > 0) {
// Every 3 monsters, increase difficulty level
if (monstersKilled % 3 === 0) {
difficultyLevel++;
waveTxt.setText('Wave: ' + difficultyLevel);
LK.effects.flashScreen(0x330000, 500);
}
// Adjust monster properties based on difficulty
monster.health = 5 + Math.min(5, difficultyLevel);
monster.growthRate = 0.008 + difficultyLevel * 0.001;
monster.baseScale = 0.5 + difficultyLevel * 0.03;
monster.speed = 1 + difficultyLevel * 0.1; // Increase speed based on difficulty
// Update monster's initial appearance
var monsterGraphics = LK.getAsset('jumpscare', {
anchorX: 0.5,
anchorY: 0.5
});
monster.addChild(monsterGraphics);
monsterGraphics.scale.x = monster.baseScale;
monsterGraphics.scale.y = monster.baseScale;
}
// Add monster to game
game.addChild(monster);
LK.getSound('monsterGrowl').play();
}
function createBloodSpray(x, y, count) {
for (var i = 0; i < count; i++) {
var blood = new BloodParticle();
blood.x = x;
blood.y = y;
bloodParticles.push(blood);
game.addChild(blood);
}
}
function updateScore() {
scoreTxt.setText(monstersKilled.toString());
// Check if high score needs updating
if (monstersKilled > storage.highScore) {
storage.highScore = monstersKilled;
updateHighScore();
}
lastMonsterKillTime = Date.now();
}
function updateHighScore() {
highScoreTxt.setText('Best: ' + storage.highScore);
}
function gameOver() {
if (!isGameActive) {
return;
}
isGameActive = false;
// Play a loud and distorted grainy scream sound
LK.getSound('jumpscare').play();
// Fade music out
LK.playMusic('bgMusic', {
fade: {
start: 0.3,
end: 0,
duration: 500
}
});
// Flash screen red
LK.effects.flashScreen(0xFF0000, 800);
// Show jumpscare sprite before game over screen
var jumpscareSprite = LK.getAsset('jumpscare', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 1.0,
tint: 0xFF0000 // Tint the sprite red
});
game.addChild(jumpscareSprite);
// Make the jumpscare sprite shake continuously and violently
function shakeSprite(sprite) {
tween(sprite, {
x: sprite.x + 40
}, {
duration: 15,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(sprite, {
x: sprite.x - 80
}, {
duration: 15,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(sprite, {
x: sprite.x + 40
}, {
duration: 15,
easing: tween.easeInOut,
onFinish: function onFinish() {
shakeSprite(sprite); // Recursively call to continue shaking
}
});
}
});
}
});
}
shakeSprite(jumpscareSprite);
LK.setTimeout(function () {
jumpscareSprite.destroy();
LK.showGameOver();
}, 800);
}
// Initialize game on startup
initializeGame();
// Event handling
game.down = function (x, y, obj) {
// This handles general taps on game area (not on specific monsters)
// Not needed for now as monsters have their own down handler
};
// Game update loop
game.update = function () {
// Update all blood particles
for (var i = bloodParticles.length - 1; i >= 0; i--) {
// BloodParticle.update() handles its own removal from the array if needed
// We don't need to do anything here
}
// Check if we should increase difficulty over time
// If a monster hasn't been killed in a while, make it grow faster
if (monster && lastMonsterKillTime > 0) {
var timeSinceLastKill = Date.now() - lastMonsterKillTime;
if (timeSinceLastKill > 5000) {
// Gradually increase growth rate if player is taking too long
monster.growthRate *= 1.002;
}
}
};