/****
* 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;
}
}
};