/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = (Math.random() - 0.5) * 3; self.speedY = (Math.random() - 0.5) * 3; self.health = 1; self.update = function () { self.x += self.speedX; self.y += self.speedY; // Bounce off screen edges if (self.x < 30 || self.x > 2018) { self.speedX *= -1; } if (self.y < 30 || self.y > 2702) { self.speedY *= -1; } }; return self; }); var FireProjectile = Container.expand(function () { var self = Container.call(this); var fireGraphics = self.attachAsset('fireProjectile', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = 0; self.speedY = 8; // Downward speed self.lifetime = 0; self.gravity = 0.3; self.update = function () { self.x += self.speedX; self.y += self.speedY; self.speedY += self.gravity; // Fire falls with gravity self.lifetime++; // Add fire effect with slight flickering fireGraphics.alpha = 0.8 + Math.sin(self.lifetime * 0.3) * 0.2; }; return self; }); var Jar = Container.expand(function () { var self = Container.call(this); var jarGraphics = self.attachAsset('jar', { anchorX: 0.5, anchorY: 1.0 }); self.broken = false; self.jumpTimer = Math.random() * 120 + 60; // Random jump intervals self.moveTimer = Math.random() * 180 + 120; // Random movement intervals self.moveDirection = Math.random() < 0.5 ? -1 : 1; // Random initial direction self.isJumping = false; self.originalY = 0; // Store original platform position self.update = function () { if (!self.broken && self.platform) { // Update timers self.jumpTimer--; self.moveTimer--; // Jump behavior if (self.jumpTimer <= 0 && !self.isJumping) { self.jump(); self.jumpTimer = Math.random() * 180 + 120; // Reset jump timer } // Movement behavior if (self.moveTimer <= 0) { self.moveDirection *= -1; // Change direction self.moveTimer = Math.random() * 240 + 180; // Reset move timer } // Horizontal movement within platform bounds var moveAmount = Math.sin(LK.ticks * 0.05) * 2 * self.moveDirection; var newX = self.platform.x + moveAmount; // Keep jar on platform var platformWidth = self.platform.width || 200; if (newX >= self.platform.x - platformWidth / 3 && newX <= self.platform.x + platformWidth / 3) { self.x = newX; } else { self.x = self.platform.x; } // Keep jar positioned on platform if (!self.isJumping) { self.y = self.platform.y - self.platform.height / 2; self.originalY = self.y; } // Fire attack if this jar can attack if (self.canAttack) { self.attackTimer--; if (self.attackTimer <= 0) { self.fireAttack(); self.attackTimer = Math.random() * 180 + 120; // Random fire intervals } } } }; self.jump = function () { if (!self.isJumping && !self.broken) { self.isJumping = true; var jumpHeight = 80 + Math.random() * 40; // Random jump height // Play jar sound when jumping LK.getSound('jar').play(); // Jump up tween(self, { y: self.originalY - jumpHeight }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Fall back down tween(self, { y: self.originalY }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { self.isJumping = false; } }); } }); } }; self.breakJar = function () { if (!self.broken) { self.broken = true; LK.getSound('jarBreak').play(); LK.setScore(LK.getScore() + 5); scoreTxt.setText(LK.getScore()); // Track jars broken for level progression jarsBrokenThisLevel++; // Check if all jars are broken (count remaining unbroken jars) var remainingJars = 0; for (var k = 0; k < jars.length; k++) { if (!jars[k].broken) { remainingJars++; } } // Level up only when all jars are broken (only 1 remaining = this jar that just broke) if (remainingJars <= 1) { // Level up! currentLevel++; jarsBrokenThisLevel = 0; levelTxt.setText('Level ' + currentLevel); // Flash screen gold for level up LK.effects.flashScreen(0xFFD700, 800); // Hide ninja temporarily for level transition ninja.alpha = 0; // Generate new platforms and jars for next level LK.setTimeout(function () { // Clear existing platforms and jars for (var i = platforms.length - 1; i >= 0; i--) { platforms[i].destroy(); } for (var j = jars.length - 1; j >= 0; j--) { if (!jars[j].broken) { jars[j].destroy(); } } platforms = []; jars = []; // Reset ninja position to starting position ninja.x = 1024; ninja.y = 2500; ninja.velocityY = 0; ninja.onGround = false; // Make platforms and ninja appear together generatePlatforms(); // Fade in ninja with platforms tween(ninja, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); }, 500); } // Create multiple jar fragments for breaking effect var fragments = []; for (var i = 0; i < 4; i++) { var fragment = LK.getAsset('jar', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, tint: 0x8b4513 }); fragment.x = self.x + (Math.random() - 0.5) * 20; fragment.y = self.y + (Math.random() - 0.5) * 20; fragments.push(fragment); game.addChild(fragment); // Animate each fragment var fragmentSpeedX = (Math.random() - 0.5) * 200; var fragmentSpeedY = -Math.random() * 150 - 50; var fragmentRotation = (Math.random() - 0.5) * 6; tween(fragment, { x: fragment.x + fragmentSpeedX, y: fragment.y + fragmentSpeedY + 100, rotation: fragmentRotation, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { fragment.destroy(); } }); } // Main jar breaking animation - shake and fade tween(jarGraphics, { rotation: 0.3 }, { duration: 50, easing: tween.easeInOut, onFinish: function onFinish() { tween(jarGraphics, { rotation: -0.3, scaleX: 1.2, scaleY: 0.8 }, { duration: 50, easing: tween.easeInOut, onFinish: function onFinish() { tween(jarGraphics, { scaleX: 0.1, scaleY: 0.1, alpha: 0, rotation: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); } }); } }); } }; self.fireAttack = function () { if (!self.broken && self.canAttack) { var fireProjectile = new FireProjectile(); fireProjectile.x = self.x; fireProjectile.y = self.y + 20; fireProjectile.speedX = (Math.random() - 0.5) * 2; // Slight horizontal variation fireProjectiles.push(fireProjectile); game.addChild(fireProjectile); LK.getSound('jarFire').play(); } }; return self; }); var Ninja = Container.expand(function () { var self = Container.call(this); var ninjaGraphics = self.attachAsset('ninja', { anchorX: 0.5, anchorY: 0.5 }); self.velocityY = 0; self.onGround = false; self.gravity = 0.8; self.jumpPower = -18; self.health = 5; self.attackCooldown = 0; self.update = function () { // Apply gravity if (!self.onGround) { self.velocityY += self.gravity; } self.y += self.velocityY; // Reset ground state self.onGround = false; // Check platform collisions for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; if (self.intersects(platform) && self.velocityY > 0) { var platformTop = platform.y - platform.height / 2; var ninjaBottom = self.y + self.height / 2; if (ninjaBottom > platformTop - 20) { self.y = platformTop - self.height / 2; self.velocityY = 0; self.onGround = true; // Move ninja with platform self.x += platform.moveSpeed * platform.moveDirection; break; } } } // Fall off screen check if (self.y > 2800) { LK.showGameOver(); } // Reduce attack cooldown if (self.attackCooldown > 0) { self.attackCooldown--; } }; self.jump = function () { if (self.onGround) { self.velocityY = self.jumpPower; self.onGround = false; LK.getSound('jump').play(); } }; self.attack = function (targetX, targetY) { if (self.attackCooldown <= 0) { var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); projectile.speedX = dx / distance * 24; projectile.speedY = dy / distance * 24; projectiles.push(projectile); game.addChild(projectile); self.attackCooldown = 20; LK.getSound('attack').play(); } }; self.moveLeft = function () { self.x -= ninjaSpeed; if (self.x < 50) self.x = 50; // Flip ninja to face left ninjaGraphics.scaleX = -1; }; self.moveRight = function () { self.x += ninjaSpeed; if (self.x > 1998) self.x = 1998; // Flip ninja to face right ninjaGraphics.scaleX = 1; }; return self; }); var Platform = Container.expand(function () { var self = Container.call(this); var platformGraphics = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); self.moveSpeed = Math.random() * 2 + 1; // Random speed between 1-3 self.moveDirection = Math.random() < 0.5 ? -1 : 1; // Random initial direction self.leftBound = 100; self.rightBound = 1948; self.update = function () { // Move platform horizontally self.x += self.moveSpeed * self.moveDirection; // Bounce off boundaries if (self.x <= self.leftBound || self.x >= self.rightBound) { self.moveDirection *= -1; } }; return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); var projectileGraphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = 0; self.speedY = 0; self.lifetime = 0; self.update = function () { self.x += self.speedX; self.y += self.speedY; self.lifetime++; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87ceeb }); /**** * Game Code ****/ // Create background var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 1.0, scaleY: 1.0 }); game.addChild(background); var ninja = new Ninja(); var platforms = []; var enemies = []; var projectiles = []; var jars = []; var fireProjectiles = []; var enemySpawnTimer = 0; var difficultyTimer = 0; var currentLevel = 1; var jarsPerLevel = 5; var jarsBrokenThisLevel = 0; var touchStartX = 0; var touchStartY = 0; var isSwipeMove = false; var ninjaSpeed = 8; var ninjaMaxHealth = 5; var ninjaCurrentHealth = 5; // Create score display var scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Create level display var levelTxt = new Text2('Level 1', { size: 60, fill: 0xFFD700 }); levelTxt.anchor.set(0, 0); levelTxt.x = 120; levelTxt.y = 20; LK.gui.topLeft.addChild(levelTxt); // Create health display var healthTxt = new Text2('Health: 5', { size: 60, fill: 0xff0000 }); healthTxt.anchor.set(1, 0); healthTxt.x = -20; healthTxt.y = 20; LK.gui.topRight.addChild(healthTxt); // Generate random platforms function generatePlatforms() { platforms = []; // Ground platform var groundPlatform = new Platform(); groundPlatform.x = 1024; groundPlatform.y = 2600; groundPlatform.width = 400; // Start platforms invisible for coordinated appearance groundPlatform.alpha = 0; platforms.push(groundPlatform); game.addChild(groundPlatform); // Animate ground platform appearance tween(groundPlatform, { alpha: 1 }, { duration: 600, easing: tween.easeInOut }); // Random platforms - increase count with level var platformCount = Math.min(8 + currentLevel, 15); for (var i = 0; i < platformCount; i++) { var platform = new Platform(); platform.x = Math.random() * 1600 + 200; platform.y = 2400 - i * 250 - Math.random() * 100; // Make platforms smaller at higher levels for more challenge platform.width = Math.max(150 + Math.random() * 100 - currentLevel * 5, 80); // Start platforms invisible for coordinated appearance platform.alpha = 0; platforms.push(platform); game.addChild(platform); // Animate platform appearance with staggered timing tween(platform, { alpha: 1 }, { duration: 600, delay: i * 100, easing: tween.easeInOut }); // Ensure enough jars are available for level completion var jarChance = Math.min(0.8, 0.6 + currentLevel * 0.05); if (Math.random() < jarChance) { var jar = new Jar(); jar.x = platform.x; jar.y = platform.y - platform.height / 2; jar.platform = platform; // Assign platform reference to jar // Start jars invisible for coordinated appearance jar.alpha = 0; // Randomly make one jar per few platforms a fire attacker jar.canAttack = Math.random() < 0.3; // 30% chance if (jar.canAttack) { jar.attackTimer = Math.random() * 180 + 60; } jars.push(jar); game.addChild(jar); // Animate jar appearance slightly after platform tween(jar, { alpha: 1 }, { duration: 400, delay: i * 100 + 200, easing: tween.easeInOut }); } } } function spawnEnemy() { var enemy = new Enemy(); // Spawn from random edge var side = Math.floor(Math.random() * 4); switch (side) { case 0: // Top enemy.x = Math.random() * 2048; enemy.y = -30; break; case 1: // Right enemy.x = 2078; enemy.y = Math.random() * 2732; break; case 2: // Bottom enemy.x = Math.random() * 2048; enemy.y = 2762; break; case 3: // Left enemy.x = -30; enemy.y = Math.random() * 2732; break; } enemies.push(enemy); game.addChild(enemy); } // Initialize game elements generatePlatforms(); ninja.x = 1024; ninja.y = 2500; game.addChild(ninja); // Touch controls game.down = function (x, y, obj) { touchStartX = x; touchStartY = y; isSwipeMove = false; }; game.up = function (x, y, obj) { if (!isSwipeMove) { // If touching near ninja, jump var dx = x - ninja.x; var dy = y - ninja.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 150) { ninja.jump(); } else { // Attack in direction of touch ninja.attack(x, y); } } }; game.move = function (x, y, obj) { var swipeDistanceX = x - touchStartX; var swipeDistanceY = Math.abs(y - touchStartY); // Detect horizontal swipe (minimum 50px horizontal, less than 100px vertical) if (Math.abs(swipeDistanceX) > 50 && swipeDistanceY < 100) { isSwipeMove = true; if (swipeDistanceX > 0) { // Swipe right ninja.moveRight(); } else { // Swipe left ninja.moveLeft(); } } }; game.update = function () { // Spawn enemies enemySpawnTimer++; difficultyTimer++; var spawnRate = Math.max(180 - Math.floor(difficultyTimer / 300), 60); if (enemySpawnTimer >= spawnRate) { spawnEnemy(); enemySpawnTimer = 0; } // Check projectile vs enemy collisions for (var p = projectiles.length - 1; p >= 0; p--) { var projectile = projectiles[p]; // Remove old projectiles if (projectile.lifetime > 180 || projectile.x < -50 || projectile.x > 2098 || projectile.y < -50 || projectile.y > 2782) { projectile.destroy(); projectiles.splice(p, 1); continue; } // Check enemy hits for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; if (projectile.intersects(enemy)) { // Hit enemy LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); LK.getSound('enemyHit').play(); LK.effects.flashObject(enemy, 0xffffff, 200); enemy.destroy(); enemies.splice(e, 1); projectile.destroy(); projectiles.splice(p, 1); break; } // Check jar hits for (var j = jars.length - 1; j >= 0; j--) { var jar = jars[j]; if (!jar.broken && projectile.intersects(jar)) { jar.breakJar(); jars.splice(j, 1); projectile.destroy(); projectiles.splice(p, 1); break; } } } } // Update jar positions to follow their platforms for (var j = 0; j < jars.length; j++) { var jar = jars[j]; if (!jar.broken && jar.platform) { jar.x = jar.platform.x; } } // Check ninja vs enemy collisions for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; if (ninja.intersects(enemy)) { ninja.health--; ninjaCurrentHealth = ninja.health; healthTxt.setText('Health: ' + ninjaCurrentHealth); LK.effects.flashObject(ninja, 0xff0000, 500); enemy.destroy(); enemies.splice(e, 1); if (ninja.health <= 0) { LK.showGameOver(); } } } // Update and check fire projectiles for (var f = fireProjectiles.length - 1; f >= 0; f--) { var fireProjectile = fireProjectiles[f]; // Remove fire projectiles that are off screen or too old if (fireProjectile.lifetime > 300 || fireProjectile.y > 2800 || fireProjectile.x < -50 || fireProjectile.x > 2098) { fireProjectile.destroy(); fireProjectiles.splice(f, 1); continue; } // Check fire projectile vs ninja collision if (fireProjectile.intersects(ninja)) { ninja.health--; ninjaCurrentHealth = ninja.health; healthTxt.setText('Health: ' + ninjaCurrentHealth); LK.effects.flashObject(ninja, 0xff4500, 500); fireProjectile.destroy(); fireProjectiles.splice(f, 1); if (ninja.health <= 0) { LK.showGameOver(); } } } // Keep ninja on screen horizontally if (ninja.x < 40) ninja.x = 40; if (ninja.x > 2008) ninja.x = 2008; }; // Play background music LK.playMusic('ninjai');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = (Math.random() - 0.5) * 3;
self.speedY = (Math.random() - 0.5) * 3;
self.health = 1;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Bounce off screen edges
if (self.x < 30 || self.x > 2018) {
self.speedX *= -1;
}
if (self.y < 30 || self.y > 2702) {
self.speedY *= -1;
}
};
return self;
});
var FireProjectile = Container.expand(function () {
var self = Container.call(this);
var fireGraphics = self.attachAsset('fireProjectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 8; // Downward speed
self.lifetime = 0;
self.gravity = 0.3;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
self.speedY += self.gravity; // Fire falls with gravity
self.lifetime++;
// Add fire effect with slight flickering
fireGraphics.alpha = 0.8 + Math.sin(self.lifetime * 0.3) * 0.2;
};
return self;
});
var Jar = Container.expand(function () {
var self = Container.call(this);
var jarGraphics = self.attachAsset('jar', {
anchorX: 0.5,
anchorY: 1.0
});
self.broken = false;
self.jumpTimer = Math.random() * 120 + 60; // Random jump intervals
self.moveTimer = Math.random() * 180 + 120; // Random movement intervals
self.moveDirection = Math.random() < 0.5 ? -1 : 1; // Random initial direction
self.isJumping = false;
self.originalY = 0; // Store original platform position
self.update = function () {
if (!self.broken && self.platform) {
// Update timers
self.jumpTimer--;
self.moveTimer--;
// Jump behavior
if (self.jumpTimer <= 0 && !self.isJumping) {
self.jump();
self.jumpTimer = Math.random() * 180 + 120; // Reset jump timer
}
// Movement behavior
if (self.moveTimer <= 0) {
self.moveDirection *= -1; // Change direction
self.moveTimer = Math.random() * 240 + 180; // Reset move timer
}
// Horizontal movement within platform bounds
var moveAmount = Math.sin(LK.ticks * 0.05) * 2 * self.moveDirection;
var newX = self.platform.x + moveAmount;
// Keep jar on platform
var platformWidth = self.platform.width || 200;
if (newX >= self.platform.x - platformWidth / 3 && newX <= self.platform.x + platformWidth / 3) {
self.x = newX;
} else {
self.x = self.platform.x;
}
// Keep jar positioned on platform
if (!self.isJumping) {
self.y = self.platform.y - self.platform.height / 2;
self.originalY = self.y;
}
// Fire attack if this jar can attack
if (self.canAttack) {
self.attackTimer--;
if (self.attackTimer <= 0) {
self.fireAttack();
self.attackTimer = Math.random() * 180 + 120; // Random fire intervals
}
}
}
};
self.jump = function () {
if (!self.isJumping && !self.broken) {
self.isJumping = true;
var jumpHeight = 80 + Math.random() * 40; // Random jump height
// Play jar sound when jumping
LK.getSound('jar').play();
// Jump up
tween(self, {
y: self.originalY - jumpHeight
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fall back down
tween(self, {
y: self.originalY
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.breakJar = function () {
if (!self.broken) {
self.broken = true;
LK.getSound('jarBreak').play();
LK.setScore(LK.getScore() + 5);
scoreTxt.setText(LK.getScore());
// Track jars broken for level progression
jarsBrokenThisLevel++;
// Check if all jars are broken (count remaining unbroken jars)
var remainingJars = 0;
for (var k = 0; k < jars.length; k++) {
if (!jars[k].broken) {
remainingJars++;
}
}
// Level up only when all jars are broken (only 1 remaining = this jar that just broke)
if (remainingJars <= 1) {
// Level up!
currentLevel++;
jarsBrokenThisLevel = 0;
levelTxt.setText('Level ' + currentLevel);
// Flash screen gold for level up
LK.effects.flashScreen(0xFFD700, 800);
// Hide ninja temporarily for level transition
ninja.alpha = 0;
// Generate new platforms and jars for next level
LK.setTimeout(function () {
// Clear existing platforms and jars
for (var i = platforms.length - 1; i >= 0; i--) {
platforms[i].destroy();
}
for (var j = jars.length - 1; j >= 0; j--) {
if (!jars[j].broken) {
jars[j].destroy();
}
}
platforms = [];
jars = [];
// Reset ninja position to starting position
ninja.x = 1024;
ninja.y = 2500;
ninja.velocityY = 0;
ninja.onGround = false;
// Make platforms and ninja appear together
generatePlatforms();
// Fade in ninja with platforms
tween(ninja, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut
});
}, 500);
}
// Create multiple jar fragments for breaking effect
var fragments = [];
for (var i = 0; i < 4; i++) {
var fragment = LK.getAsset('jar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
tint: 0x8b4513
});
fragment.x = self.x + (Math.random() - 0.5) * 20;
fragment.y = self.y + (Math.random() - 0.5) * 20;
fragments.push(fragment);
game.addChild(fragment);
// Animate each fragment
var fragmentSpeedX = (Math.random() - 0.5) * 200;
var fragmentSpeedY = -Math.random() * 150 - 50;
var fragmentRotation = (Math.random() - 0.5) * 6;
tween(fragment, {
x: fragment.x + fragmentSpeedX,
y: fragment.y + fragmentSpeedY + 100,
rotation: fragmentRotation,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
fragment.destroy();
}
});
}
// Main jar breaking animation - shake and fade
tween(jarGraphics, {
rotation: 0.3
}, {
duration: 50,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(jarGraphics, {
rotation: -0.3,
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 50,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(jarGraphics, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
}
});
}
});
}
};
self.fireAttack = function () {
if (!self.broken && self.canAttack) {
var fireProjectile = new FireProjectile();
fireProjectile.x = self.x;
fireProjectile.y = self.y + 20;
fireProjectile.speedX = (Math.random() - 0.5) * 2; // Slight horizontal variation
fireProjectiles.push(fireProjectile);
game.addChild(fireProjectile);
LK.getSound('jarFire').play();
}
};
return self;
});
var Ninja = Container.expand(function () {
var self = Container.call(this);
var ninjaGraphics = self.attachAsset('ninja', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityY = 0;
self.onGround = false;
self.gravity = 0.8;
self.jumpPower = -18;
self.health = 5;
self.attackCooldown = 0;
self.update = function () {
// Apply gravity
if (!self.onGround) {
self.velocityY += self.gravity;
}
self.y += self.velocityY;
// Reset ground state
self.onGround = false;
// Check platform collisions
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
if (self.intersects(platform) && self.velocityY > 0) {
var platformTop = platform.y - platform.height / 2;
var ninjaBottom = self.y + self.height / 2;
if (ninjaBottom > platformTop - 20) {
self.y = platformTop - self.height / 2;
self.velocityY = 0;
self.onGround = true;
// Move ninja with platform
self.x += platform.moveSpeed * platform.moveDirection;
break;
}
}
}
// Fall off screen check
if (self.y > 2800) {
LK.showGameOver();
}
// Reduce attack cooldown
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
self.jump = function () {
if (self.onGround) {
self.velocityY = self.jumpPower;
self.onGround = false;
LK.getSound('jump').play();
}
};
self.attack = function (targetX, targetY) {
if (self.attackCooldown <= 0) {
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
projectile.speedX = dx / distance * 24;
projectile.speedY = dy / distance * 24;
projectiles.push(projectile);
game.addChild(projectile);
self.attackCooldown = 20;
LK.getSound('attack').play();
}
};
self.moveLeft = function () {
self.x -= ninjaSpeed;
if (self.x < 50) self.x = 50;
// Flip ninja to face left
ninjaGraphics.scaleX = -1;
};
self.moveRight = function () {
self.x += ninjaSpeed;
if (self.x > 1998) self.x = 1998;
// Flip ninja to face right
ninjaGraphics.scaleX = 1;
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
var platformGraphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveSpeed = Math.random() * 2 + 1; // Random speed between 1-3
self.moveDirection = Math.random() < 0.5 ? -1 : 1; // Random initial direction
self.leftBound = 100;
self.rightBound = 1948;
self.update = function () {
// Move platform horizontally
self.x += self.moveSpeed * self.moveDirection;
// Bounce off boundaries
if (self.x <= self.leftBound || self.x >= self.rightBound) {
self.moveDirection *= -1;
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.lifetime = 0;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
self.lifetime++;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb
});
/****
* Game Code
****/
// Create background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1.0,
scaleY: 1.0
});
game.addChild(background);
var ninja = new Ninja();
var platforms = [];
var enemies = [];
var projectiles = [];
var jars = [];
var fireProjectiles = [];
var enemySpawnTimer = 0;
var difficultyTimer = 0;
var currentLevel = 1;
var jarsPerLevel = 5;
var jarsBrokenThisLevel = 0;
var touchStartX = 0;
var touchStartY = 0;
var isSwipeMove = false;
var ninjaSpeed = 8;
var ninjaMaxHealth = 5;
var ninjaCurrentHealth = 5;
// Create score display
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create level display
var levelTxt = new Text2('Level 1', {
size: 60,
fill: 0xFFD700
});
levelTxt.anchor.set(0, 0);
levelTxt.x = 120;
levelTxt.y = 20;
LK.gui.topLeft.addChild(levelTxt);
// Create health display
var healthTxt = new Text2('Health: 5', {
size: 60,
fill: 0xff0000
});
healthTxt.anchor.set(1, 0);
healthTxt.x = -20;
healthTxt.y = 20;
LK.gui.topRight.addChild(healthTxt);
// Generate random platforms
function generatePlatforms() {
platforms = [];
// Ground platform
var groundPlatform = new Platform();
groundPlatform.x = 1024;
groundPlatform.y = 2600;
groundPlatform.width = 400;
// Start platforms invisible for coordinated appearance
groundPlatform.alpha = 0;
platforms.push(groundPlatform);
game.addChild(groundPlatform);
// Animate ground platform appearance
tween(groundPlatform, {
alpha: 1
}, {
duration: 600,
easing: tween.easeInOut
});
// Random platforms - increase count with level
var platformCount = Math.min(8 + currentLevel, 15);
for (var i = 0; i < platformCount; i++) {
var platform = new Platform();
platform.x = Math.random() * 1600 + 200;
platform.y = 2400 - i * 250 - Math.random() * 100;
// Make platforms smaller at higher levels for more challenge
platform.width = Math.max(150 + Math.random() * 100 - currentLevel * 5, 80);
// Start platforms invisible for coordinated appearance
platform.alpha = 0;
platforms.push(platform);
game.addChild(platform);
// Animate platform appearance with staggered timing
tween(platform, {
alpha: 1
}, {
duration: 600,
delay: i * 100,
easing: tween.easeInOut
});
// Ensure enough jars are available for level completion
var jarChance = Math.min(0.8, 0.6 + currentLevel * 0.05);
if (Math.random() < jarChance) {
var jar = new Jar();
jar.x = platform.x;
jar.y = platform.y - platform.height / 2;
jar.platform = platform; // Assign platform reference to jar
// Start jars invisible for coordinated appearance
jar.alpha = 0;
// Randomly make one jar per few platforms a fire attacker
jar.canAttack = Math.random() < 0.3; // 30% chance
if (jar.canAttack) {
jar.attackTimer = Math.random() * 180 + 60;
}
jars.push(jar);
game.addChild(jar);
// Animate jar appearance slightly after platform
tween(jar, {
alpha: 1
}, {
duration: 400,
delay: i * 100 + 200,
easing: tween.easeInOut
});
}
}
}
function spawnEnemy() {
var enemy = new Enemy();
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -30;
break;
case 1:
// Right
enemy.x = 2078;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2762;
break;
case 3:
// Left
enemy.x = -30;
enemy.y = Math.random() * 2732;
break;
}
enemies.push(enemy);
game.addChild(enemy);
}
// Initialize game elements
generatePlatforms();
ninja.x = 1024;
ninja.y = 2500;
game.addChild(ninja);
// Touch controls
game.down = function (x, y, obj) {
touchStartX = x;
touchStartY = y;
isSwipeMove = false;
};
game.up = function (x, y, obj) {
if (!isSwipeMove) {
// If touching near ninja, jump
var dx = x - ninja.x;
var dy = y - ninja.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 150) {
ninja.jump();
} else {
// Attack in direction of touch
ninja.attack(x, y);
}
}
};
game.move = function (x, y, obj) {
var swipeDistanceX = x - touchStartX;
var swipeDistanceY = Math.abs(y - touchStartY);
// Detect horizontal swipe (minimum 50px horizontal, less than 100px vertical)
if (Math.abs(swipeDistanceX) > 50 && swipeDistanceY < 100) {
isSwipeMove = true;
if (swipeDistanceX > 0) {
// Swipe right
ninja.moveRight();
} else {
// Swipe left
ninja.moveLeft();
}
}
};
game.update = function () {
// Spawn enemies
enemySpawnTimer++;
difficultyTimer++;
var spawnRate = Math.max(180 - Math.floor(difficultyTimer / 300), 60);
if (enemySpawnTimer >= spawnRate) {
spawnEnemy();
enemySpawnTimer = 0;
}
// Check projectile vs enemy collisions
for (var p = projectiles.length - 1; p >= 0; p--) {
var projectile = projectiles[p];
// Remove old projectiles
if (projectile.lifetime > 180 || projectile.x < -50 || projectile.x > 2098 || projectile.y < -50 || projectile.y > 2782) {
projectile.destroy();
projectiles.splice(p, 1);
continue;
}
// Check enemy hits
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
if (projectile.intersects(enemy)) {
// Hit enemy
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
LK.getSound('enemyHit').play();
LK.effects.flashObject(enemy, 0xffffff, 200);
enemy.destroy();
enemies.splice(e, 1);
projectile.destroy();
projectiles.splice(p, 1);
break;
}
// Check jar hits
for (var j = jars.length - 1; j >= 0; j--) {
var jar = jars[j];
if (!jar.broken && projectile.intersects(jar)) {
jar.breakJar();
jars.splice(j, 1);
projectile.destroy();
projectiles.splice(p, 1);
break;
}
}
}
}
// Update jar positions to follow their platforms
for (var j = 0; j < jars.length; j++) {
var jar = jars[j];
if (!jar.broken && jar.platform) {
jar.x = jar.platform.x;
}
}
// Check ninja vs enemy collisions
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
if (ninja.intersects(enemy)) {
ninja.health--;
ninjaCurrentHealth = ninja.health;
healthTxt.setText('Health: ' + ninjaCurrentHealth);
LK.effects.flashObject(ninja, 0xff0000, 500);
enemy.destroy();
enemies.splice(e, 1);
if (ninja.health <= 0) {
LK.showGameOver();
}
}
}
// Update and check fire projectiles
for (var f = fireProjectiles.length - 1; f >= 0; f--) {
var fireProjectile = fireProjectiles[f];
// Remove fire projectiles that are off screen or too old
if (fireProjectile.lifetime > 300 || fireProjectile.y > 2800 || fireProjectile.x < -50 || fireProjectile.x > 2098) {
fireProjectile.destroy();
fireProjectiles.splice(f, 1);
continue;
}
// Check fire projectile vs ninja collision
if (fireProjectile.intersects(ninja)) {
ninja.health--;
ninjaCurrentHealth = ninja.health;
healthTxt.setText('Health: ' + ninjaCurrentHealth);
LK.effects.flashObject(ninja, 0xff4500, 500);
fireProjectile.destroy();
fireProjectiles.splice(f, 1);
if (ninja.health <= 0) {
LK.showGameOver();
}
}
}
// Keep ninja on screen horizontally
if (ninja.x < 40) ninja.x = 40;
if (ninja.x > 2008) ninja.x = 2008;
};
// Play background music
LK.playMusic('ninjai');
2d anime chibi style ninja hitam jepang. In-Game asset. 2d. High contrast. No shadows
2d anime image pemandangan gunung fuji jepang zaman kuno di kejauhan. wilayah hutan dan bukit bebatuan dari dekat. langit biru cerah. In-Game asset. 2d. High contrast. No shadows
2d anime chibi evil hitam jepang. In-Game asset. 2d. High contrast. No shadows
2d anime image tanah dan bebatuan. In-Game asset. 2d. High contrast. No shadows