/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
var assetName = type === 'armored' ? 'armoredEnemy' : type === 'shooting' ? 'shootingEnemy' : 'basicEnemy';
var enemyGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0
});
self.type = type || 'basic';
self.speed = type === 'armored' ? -3 : type === 'shooting' ? -2 : -4;
self.maxHealth = type === 'shooting' ? 2 : 1;
self.health = self.maxHealth;
self.damage = type === 'armored' ? 30 : type === 'shooting' ? 25 : 20;
self.points = type === 'armored' ? 30 : type === 'shooting' ? 25 : 10;
// Direction tracking for all enemy types
self.direction = self.speed > 0 ? 1 : -1; // Track current direction for all enemies
self.lastDirection = self.direction; // Track last direction to detect changes
// Armored enemy movement properties
if (type === 'armored') {
self.baseSpeed = Math.abs(self.speed); // Store base speed value
self.direction = self.speed > 0 ? 1 : -1; // Track current direction
self.lastX = 0; // Track last position for direction changes
self.directionChangeTimer = 0;
self.directionChangeInterval = 120 + Math.random() * 120; // Random interval 2-4 seconds
}
// Shooting properties for shooting enemies
if (type === 'shooting') {
self.shootTimer = 0;
self.shootInterval = 60; // Shoot every 1 second (60 frames at 60fps)
self.bulletsShot = 0; // Track number of bullets fired
self.maxBullets = 1; // Maximum bullets this enemy can shoot - only one bullet per enemy
self.shoot = function () {
// Check if enemy has bullets remaining
if (self.bulletsShot >= self.maxBullets) {
return; // Don't shoot if limit reached
}
// Determine direction to shoot towards player
var direction = player.x > self.x ? 1 : -1;
var bullet = game.addChild(new EnemyBullet(direction));
bullet.x = self.x;
bullet.y = self.y - 40;
enemyBullets.push(bullet);
self.bulletsShot++; // Increment bullet count
};
}
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
LK.getSound('enemyHit').play();
if (self.health <= 0) {
// Create main explosion effect
var explosionEffect = game.addChild(LK.getAsset('slashEffect', {
anchorX: 0.5,
anchorY: 0.5
}));
explosionEffect.x = self.x;
explosionEffect.y = self.y - 40;
explosionEffect.tint = 0xff4500; // Orange explosion color
explosionEffect.alpha = 0.9;
explosionEffect.scaleX = 0.5;
explosionEffect.scaleY = 0.5;
// Animate main explosion effect
tween(explosionEffect, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Create fragmented explosion pieces
var fragmentCount = 6 + Math.floor(Math.random() * 4); // 6-9 fragments
for (var f = 0; f < fragmentCount; f++) {
var fragment = game.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
}));
fragment.x = self.x;
fragment.y = self.y - 40;
fragment.tint = 0xff6600; // Darker orange for fragments
fragment.alpha = 0.8;
fragment.scaleX = 0.3 + Math.random() * 0.4; // Random size 0.3-0.7
fragment.scaleY = 0.3 + Math.random() * 0.4;
fragment.rotation = Math.random() * Math.PI * 2; // Random rotation
// Calculate random scatter direction
var angle = Math.PI * 2 / fragmentCount * f + (Math.random() - 0.5) * 0.8;
var velocity = 150 + Math.random() * 100; // Random velocity 150-250
var targetX = fragment.x + Math.cos(angle) * velocity;
var targetY = fragment.y + Math.sin(angle) * velocity;
// Animate fragment scatter with rotation and fade
tween(fragment, {
x: targetX,
y: targetY,
rotation: fragment.rotation + (Math.random() - 0.5) * Math.PI * 4,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
// Random duration 600-1000ms
easing: tween.easeOut,
onFinish: function onFinish() {
fragment.destroy();
}
});
}
// Award points
LK.setScore(LK.getScore() + self.points);
// Chance to drop power-up
if (Math.random() < 0.15) {
var powerUp = game.addChild(new PowerUp());
powerUp.x = self.x;
powerUp.y = self.y - 20;
powerUps.push(powerUp);
}
// Remove from enemies array
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
self.destroy();
}
};
// Initialize jumping properties
self.baseY = 0;
self.isJumping = false;
self.jumpTimer = 0;
self.jumpInterval = 150 + Math.random() * 90; // Random jump interval between 2.5-4 seconds for long jumps
self.startJump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.baseY = self.y;
var baseX = self.x;
var jumpDistance = 200 + Math.random() * 150; // Long horizontal jump 200-350 pixels
var jumpHeight = 120 + Math.random() * 80; // Higher jump 120-200 pixels
// Jump direction based on movement direction
var jumpDirectionX = self.speed > 0 ? jumpDistance : -jumpDistance;
// Long jump with arc motion - jump up and forward
tween(self, {
y: self.baseY - jumpHeight,
x: baseX + jumpDirectionX
}, {
duration: 800,
// Longer duration for long jump
easing: tween.easeOut,
onFinish: function onFinish() {
// Fall back down while continuing forward motion
tween(self, {
y: self.baseY
}, {
duration: 600,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.update = function () {
// Handle armored enemy direction changes
if (self.type === 'armored') {
// Initialize lastX if not set
if (self.lastX === 0) self.lastX = self.x;
// Check for screen boundaries and change direction
if (self.x <= 100 && self.direction === -1 || self.x >= 1948 && self.direction === 1) {
self.direction *= -1; // Reverse direction
self.speed = self.baseSpeed * self.direction;
}
// Random direction changes
self.directionChangeTimer++;
if (self.directionChangeTimer >= self.directionChangeInterval) {
self.direction *= -1; // Reverse direction
self.speed = self.baseSpeed * self.direction;
self.directionChangeTimer = 0;
self.directionChangeInterval = 120 + Math.random() * 120; // Reset random interval
}
// Update lastX
self.lastX = self.x;
}
// Update direction based on speed for all enemy types (including armored)
self.direction = self.speed > 0 ? 1 : -1;
// Flip enemy image based on screen position for all enemy types
var targetScale;
if (self.x < 1024) {
// Left half of screen (2048/2 = 1024)
targetScale = -1; // Face left
} else {
// Right half of screen
targetScale = 1; // Face right
}
// Only animate if the scale needs to change
if (enemyGraphics.scaleX !== targetScale) {
tween(enemyGraphics, {
scaleX: targetScale
}, {
duration: 200,
easing: tween.easeOut
});
}
self.x += self.speed;
// Handle jumping
self.jumpTimer++;
if (self.jumpTimer >= self.jumpInterval && !self.isJumping) {
self.startJump();
self.jumpTimer = 0;
self.jumpInterval = 150 + Math.random() * 90; // Reset random interval for long jumps
}
// Handle shooting for shooting enemies
if (self.type === 'shooting') {
self.shootTimer++;
if (self.shootTimer >= self.shootInterval && self.bulletsShot < self.maxBullets) {
self.shoot();
self.shootTimer = 0;
}
}
// Check collision with player
if (self.intersects(player) && !player.invulnerable) {
player.takeDamage(self.damage);
}
};
return self;
});
var EnemyBullet = Container.expand(function (direction) {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 15;
self.direction = direction || 1; // 1 for right, -1 for left
self.update = function () {
self.x += self.speed * self.direction;
// Check collision with player
if (self.intersects(player) && !player.invulnerable) {
player.takeDamage(self.damage);
// Remove from enemyBullets array
var index = enemyBullets.indexOf(self);
if (index > -1) {
enemyBullets.splice(index, 1);
}
self.destroy();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.slashDamage = 1;
self.slashRange = 150;
self.isSlashing = false;
self.slashCooldown = 0;
self.isDashing = false;
self.dashCooldown = 0;
self.dashDistance = 200;
self.dashDuration = 300;
self.invulnerable = false;
self.invulnerabilityTime = 0;
self.facingDirection = 1; // 1 for right, -1 for left
self.isJumping = false;
self.baseY = 0;
self.dashSlash = function () {
if (self.slashCooldown <= 0 && self.dashCooldown <= 0 && !self.isSlashing && !self.isDashing) {
self.isDashing = true;
self.isSlashing = true;
self.slashCooldown = 60; // 1 second cooldown
self.dashCooldown = 60;
var startX = self.x;
var dashTargetX = self.facingDirection > 0 ? Math.min(startX + self.dashDistance, 2048 - 40) : Math.max(startX - self.dashDistance, 40);
// Dash forward with tween
tween(self, {
x: dashTargetX
}, {
duration: self.dashDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isDashing = false;
}
});
// Create extended slash effect during dash
var slashEffect = game.addChild(LK.getAsset('slashEffect', {
anchorX: 0.5,
anchorY: 0.5
}));
slashEffect.x = self.x + 75 * self.facingDirection;
slashEffect.y = self.y - 60;
slashEffect.alpha = 0.9;
slashEffect.scaleX = 2.0 * self.facingDirection;
slashEffect.scaleY = 1.5;
// Follow player during dash
var slashFollowInterval = LK.setInterval(function () {
if (self.isDashing) {
slashEffect.x = self.x + 75 * self.facingDirection;
}
}, 16);
// Animate slash effect
tween(slashEffect, {
scaleX: 2.5,
scaleY: 2.0,
alpha: 0
}, {
duration: self.dashDuration,
onFinish: function onFinish() {
LK.clearInterval(slashFollowInterval);
slashEffect.destroy();
}
});
LK.getSound('slash').play();
// Extended damage check during dash
var damageCheckInterval = LK.setInterval(function () {
if (self.isDashing) {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
var enemyInFront = self.facingDirection > 0 ? enemy.x > self.x - 50 && enemy.x < self.x + 150 : enemy.x < self.x + 50 && enemy.x > self.x - 150;
if (distance < self.slashRange * 1.5 && enemyInFront) {
enemy.takeDamage(self.slashDamage * 2); // Double damage during dash
}
}
// Check for enemy bullet hits during dash
for (var j = enemyBullets.length - 1; j >= 0; j--) {
var bullet = enemyBullets[j];
var bulletDistance = Math.sqrt(Math.pow(bullet.x - self.x, 2) + Math.pow(bullet.y - self.y, 2));
var bulletInFront = self.facingDirection > 0 ? bullet.x > self.x - 50 && bullet.x < self.x + 150 : bullet.x < self.x + 50 && bullet.x > self.x - 150;
if (bulletDistance < self.slashRange * 1.5 && bulletInFront) {
// Destroy the bullet
bullet.destroy();
enemyBullets.splice(j, 1);
// Award small points for destroying bullets
LK.setScore(LK.getScore() + 5);
}
}
}
}, 30);
LK.setTimeout(function () {
LK.clearInterval(damageCheckInterval);
self.isSlashing = false;
}, self.dashDuration);
}
};
self.slash = function () {
if (self.slashCooldown <= 0 && !self.isSlashing && !self.isDashing) {
self.isSlashing = true;
self.slashCooldown = 20; // 1/3 second at 60fps
// Create slash effect
var slashEffect = game.addChild(LK.getAsset('slashEffect', {
anchorX: 0.5,
anchorY: 0.5
}));
slashEffect.x = self.x + 75 * self.facingDirection;
slashEffect.y = self.y - 60;
slashEffect.alpha = 0.8;
slashEffect.scaleX = self.facingDirection;
// Animate slash effect
tween(slashEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
slashEffect.destroy();
}
});
LK.getSound('slash').play();
// Check for enemy hits
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
var enemyInRange = self.facingDirection > 0 ? enemy.x > self.x - 50 && enemy.x < self.x + self.slashRange : enemy.x < self.x + 50 && enemy.x > self.x - self.slashRange;
if (distance < self.slashRange && enemyInRange) {
enemy.takeDamage(self.slashDamage);
}
}
// Check for enemy bullet hits
for (var j = enemyBullets.length - 1; j >= 0; j--) {
var bullet = enemyBullets[j];
var bulletDistance = Math.sqrt(Math.pow(bullet.x - self.x, 2) + Math.pow(bullet.y - self.y, 2));
var bulletInRange = self.facingDirection > 0 ? bullet.x > self.x - 50 && bullet.x < self.x + self.slashRange : bullet.x < self.x + 50 && bullet.x > self.x - self.slashRange;
if (bulletDistance < self.slashRange && bulletInRange) {
// Destroy the bullet
bullet.destroy();
enemyBullets.splice(j, 1);
// Award small points for destroying bullets
LK.setScore(LK.getScore() + 5);
}
}
LK.setTimeout(function () {
self.isSlashing = false;
}, 200);
}
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.baseY = self.y;
var jumpHeight = 200; // Jump height in pixels
// Jump up with tween
tween(self, {
y: self.baseY - jumpHeight
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fall back down
tween(self, {
y: self.baseY
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.takeDamage = function (damage) {
if (!self.invulnerable) {
self.health -= damage;
self.invulnerable = true;
self.invulnerabilityTime = 120; // 2 seconds at 60fps
// Flash effect
LK.effects.flashObject(self, 0xff0000, 500);
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
}
};
self.update = function () {
// Update player image direction based on facing direction
playerGraphics.scaleX = self.facingDirection;
if (self.slashCooldown > 0) {
self.slashCooldown--;
}
if (self.dashCooldown > 0) {
self.dashCooldown--;
}
if (self.invulnerabilityTime > 0) {
self.invulnerabilityTime--;
if (self.invulnerabilityTime <= 0) {
self.invulnerable = false;
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.bobOffset = 0;
self.update = function () {
self.x += self.speed;
self.bobOffset += 0.1;
self.y += Math.sin(self.bobOffset) * 0.5;
// Check collision with player
if (self.intersects(player)) {
self.collect();
}
};
self.collect = function () {
LK.getSound('powerUpSound').play();
// Random power-up effect
var powerType = Math.floor(Math.random() * 3);
switch (powerType) {
case 0:
// Increased damage
player.slashDamage += 1;
break;
case 1:
// Wider slash range
player.slashRange += 50;
break;
case 2:
// Temporary invincibility
player.invulnerable = true;
player.invulnerabilityTime = 300; // 5 seconds
break;
}
// Remove from powerUps array
var index = powerUps.indexOf(self);
if (index > -1) {
powerUps.splice(index, 1);
}
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Game variables
var player;
var enemies = [];
var powerUps = [];
var enemyBullets = [];
var enemySpawnTimer = 0;
var enemySpawnRate = 180; // Start spawning every 3 seconds
var gameTime = 0;
var healthBar;
var healthBarBg;
// Create UI elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
// Create health bar background
healthBarBg = game.addChild(LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
}));
healthBarBg.x = 120;
healthBarBg.y = 120;
// Create health bar
healthBar = game.addChild(LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0
}));
healthBar.x = 122;
healthBar.y = 122;
// Create health text
var healthTxt = new Text2('Health', {
size: 40,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 120;
healthTxt.y = 160;
game.addChild(healthTxt);
// Create background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Create player
player = game.addChild(new Player());
player.x = 300;
player.y = 2732 - 150; // Near bottom of screen
// Start background music
LK.playMusic('bgmusic');
// Touch controls for movement and slashing
var isDragging = false;
var lastTouchX = 0;
var lastTouchY = 0;
var swipeThreshold = 100; // Minimum distance for swipe detection
game.down = function (x, y, obj) {
isDragging = true;
lastTouchX = x;
lastTouchY = y;
// Set facing direction based on touch position relative to player
if (x < player.x) {
player.facingDirection = -1; // Face left
} else {
player.facingDirection = 1; // Face right
}
player.dashSlash();
};
game.move = function (x, y, obj) {
if (isDragging) {
var deltaX = x - lastTouchX;
player.x += deltaX * 0.5; // Smooth movement multiplier
// Keep player within screen bounds
if (player.x < 40) player.x = 40;
if (player.x > 2048 - 40) player.x = 2048 - 40;
lastTouchX = x;
}
};
game.up = function (x, y, obj) {
if (isDragging) {
var deltaY = lastTouchY - y; // Upward swipe has positive deltaY
var deltaX = Math.abs(x - lastTouchX);
// Check for upward swipe (vertical movement greater than horizontal and above threshold)
if (deltaY > swipeThreshold && deltaY > deltaX) {
player.jump();
}
}
isDragging = false;
};
// Spawn enemies
function spawnEnemy() {
var rand = Math.random();
var enemyType = rand < 0.5 ? 'basic' : rand < 0.8 ? 'armored' : 'shooting';
var enemy = game.addChild(new Enemy(enemyType));
// Randomly spawn from left or right
var spawnFromRight = Math.random() < 0.5;
if (spawnFromRight) {
enemy.x = 2048 + 100; // Start off-screen right
enemy.speed = enemy.type === 'armored' ? -3 : enemy.type === 'shooting' ? -2 : -4; // Move left
} else {
enemy.x = -100; // Start off-screen left
enemy.speed = enemy.type === 'armored' ? 3 : enemy.type === 'shooting' ? 2 : 4; // Move right
}
enemy.y = 2732 - 150 + Math.random() * 100 - 50; // Vary height slightly
enemies.push(enemy);
}
// Main game loop
game.update = function () {
gameTime++;
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBar.width = 200 * healthPercent;
// Spawn enemies
enemySpawnTimer++;
if (enemySpawnTimer >= enemySpawnRate) {
spawnEnemy();
enemySpawnTimer = 0;
// Increase difficulty over time
if (enemySpawnRate > 60) {
// Minimum spawn rate
enemySpawnRate -= 0.5;
}
}
// Clean up off-screen enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.x < -100 || enemy.x > 2048 + 100) {
enemy.destroy();
enemies.splice(i, 1);
}
}
// Clean up off-screen power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.x < -100) {
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Clean up off-screen enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.x < -100 || bullet.x > 2048 + 100) {
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
var assetName = type === 'armored' ? 'armoredEnemy' : type === 'shooting' ? 'shootingEnemy' : 'basicEnemy';
var enemyGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0
});
self.type = type || 'basic';
self.speed = type === 'armored' ? -3 : type === 'shooting' ? -2 : -4;
self.maxHealth = type === 'shooting' ? 2 : 1;
self.health = self.maxHealth;
self.damage = type === 'armored' ? 30 : type === 'shooting' ? 25 : 20;
self.points = type === 'armored' ? 30 : type === 'shooting' ? 25 : 10;
// Direction tracking for all enemy types
self.direction = self.speed > 0 ? 1 : -1; // Track current direction for all enemies
self.lastDirection = self.direction; // Track last direction to detect changes
// Armored enemy movement properties
if (type === 'armored') {
self.baseSpeed = Math.abs(self.speed); // Store base speed value
self.direction = self.speed > 0 ? 1 : -1; // Track current direction
self.lastX = 0; // Track last position for direction changes
self.directionChangeTimer = 0;
self.directionChangeInterval = 120 + Math.random() * 120; // Random interval 2-4 seconds
}
// Shooting properties for shooting enemies
if (type === 'shooting') {
self.shootTimer = 0;
self.shootInterval = 60; // Shoot every 1 second (60 frames at 60fps)
self.bulletsShot = 0; // Track number of bullets fired
self.maxBullets = 1; // Maximum bullets this enemy can shoot - only one bullet per enemy
self.shoot = function () {
// Check if enemy has bullets remaining
if (self.bulletsShot >= self.maxBullets) {
return; // Don't shoot if limit reached
}
// Determine direction to shoot towards player
var direction = player.x > self.x ? 1 : -1;
var bullet = game.addChild(new EnemyBullet(direction));
bullet.x = self.x;
bullet.y = self.y - 40;
enemyBullets.push(bullet);
self.bulletsShot++; // Increment bullet count
};
}
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
LK.getSound('enemyHit').play();
if (self.health <= 0) {
// Create main explosion effect
var explosionEffect = game.addChild(LK.getAsset('slashEffect', {
anchorX: 0.5,
anchorY: 0.5
}));
explosionEffect.x = self.x;
explosionEffect.y = self.y - 40;
explosionEffect.tint = 0xff4500; // Orange explosion color
explosionEffect.alpha = 0.9;
explosionEffect.scaleX = 0.5;
explosionEffect.scaleY = 0.5;
// Animate main explosion effect
tween(explosionEffect, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Create fragmented explosion pieces
var fragmentCount = 6 + Math.floor(Math.random() * 4); // 6-9 fragments
for (var f = 0; f < fragmentCount; f++) {
var fragment = game.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
}));
fragment.x = self.x;
fragment.y = self.y - 40;
fragment.tint = 0xff6600; // Darker orange for fragments
fragment.alpha = 0.8;
fragment.scaleX = 0.3 + Math.random() * 0.4; // Random size 0.3-0.7
fragment.scaleY = 0.3 + Math.random() * 0.4;
fragment.rotation = Math.random() * Math.PI * 2; // Random rotation
// Calculate random scatter direction
var angle = Math.PI * 2 / fragmentCount * f + (Math.random() - 0.5) * 0.8;
var velocity = 150 + Math.random() * 100; // Random velocity 150-250
var targetX = fragment.x + Math.cos(angle) * velocity;
var targetY = fragment.y + Math.sin(angle) * velocity;
// Animate fragment scatter with rotation and fade
tween(fragment, {
x: targetX,
y: targetY,
rotation: fragment.rotation + (Math.random() - 0.5) * Math.PI * 4,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
// Random duration 600-1000ms
easing: tween.easeOut,
onFinish: function onFinish() {
fragment.destroy();
}
});
}
// Award points
LK.setScore(LK.getScore() + self.points);
// Chance to drop power-up
if (Math.random() < 0.15) {
var powerUp = game.addChild(new PowerUp());
powerUp.x = self.x;
powerUp.y = self.y - 20;
powerUps.push(powerUp);
}
// Remove from enemies array
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
self.destroy();
}
};
// Initialize jumping properties
self.baseY = 0;
self.isJumping = false;
self.jumpTimer = 0;
self.jumpInterval = 150 + Math.random() * 90; // Random jump interval between 2.5-4 seconds for long jumps
self.startJump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.baseY = self.y;
var baseX = self.x;
var jumpDistance = 200 + Math.random() * 150; // Long horizontal jump 200-350 pixels
var jumpHeight = 120 + Math.random() * 80; // Higher jump 120-200 pixels
// Jump direction based on movement direction
var jumpDirectionX = self.speed > 0 ? jumpDistance : -jumpDistance;
// Long jump with arc motion - jump up and forward
tween(self, {
y: self.baseY - jumpHeight,
x: baseX + jumpDirectionX
}, {
duration: 800,
// Longer duration for long jump
easing: tween.easeOut,
onFinish: function onFinish() {
// Fall back down while continuing forward motion
tween(self, {
y: self.baseY
}, {
duration: 600,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.update = function () {
// Handle armored enemy direction changes
if (self.type === 'armored') {
// Initialize lastX if not set
if (self.lastX === 0) self.lastX = self.x;
// Check for screen boundaries and change direction
if (self.x <= 100 && self.direction === -1 || self.x >= 1948 && self.direction === 1) {
self.direction *= -1; // Reverse direction
self.speed = self.baseSpeed * self.direction;
}
// Random direction changes
self.directionChangeTimer++;
if (self.directionChangeTimer >= self.directionChangeInterval) {
self.direction *= -1; // Reverse direction
self.speed = self.baseSpeed * self.direction;
self.directionChangeTimer = 0;
self.directionChangeInterval = 120 + Math.random() * 120; // Reset random interval
}
// Update lastX
self.lastX = self.x;
}
// Update direction based on speed for all enemy types (including armored)
self.direction = self.speed > 0 ? 1 : -1;
// Flip enemy image based on screen position for all enemy types
var targetScale;
if (self.x < 1024) {
// Left half of screen (2048/2 = 1024)
targetScale = -1; // Face left
} else {
// Right half of screen
targetScale = 1; // Face right
}
// Only animate if the scale needs to change
if (enemyGraphics.scaleX !== targetScale) {
tween(enemyGraphics, {
scaleX: targetScale
}, {
duration: 200,
easing: tween.easeOut
});
}
self.x += self.speed;
// Handle jumping
self.jumpTimer++;
if (self.jumpTimer >= self.jumpInterval && !self.isJumping) {
self.startJump();
self.jumpTimer = 0;
self.jumpInterval = 150 + Math.random() * 90; // Reset random interval for long jumps
}
// Handle shooting for shooting enemies
if (self.type === 'shooting') {
self.shootTimer++;
if (self.shootTimer >= self.shootInterval && self.bulletsShot < self.maxBullets) {
self.shoot();
self.shootTimer = 0;
}
}
// Check collision with player
if (self.intersects(player) && !player.invulnerable) {
player.takeDamage(self.damage);
}
};
return self;
});
var EnemyBullet = Container.expand(function (direction) {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 15;
self.direction = direction || 1; // 1 for right, -1 for left
self.update = function () {
self.x += self.speed * self.direction;
// Check collision with player
if (self.intersects(player) && !player.invulnerable) {
player.takeDamage(self.damage);
// Remove from enemyBullets array
var index = enemyBullets.indexOf(self);
if (index > -1) {
enemyBullets.splice(index, 1);
}
self.destroy();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.slashDamage = 1;
self.slashRange = 150;
self.isSlashing = false;
self.slashCooldown = 0;
self.isDashing = false;
self.dashCooldown = 0;
self.dashDistance = 200;
self.dashDuration = 300;
self.invulnerable = false;
self.invulnerabilityTime = 0;
self.facingDirection = 1; // 1 for right, -1 for left
self.isJumping = false;
self.baseY = 0;
self.dashSlash = function () {
if (self.slashCooldown <= 0 && self.dashCooldown <= 0 && !self.isSlashing && !self.isDashing) {
self.isDashing = true;
self.isSlashing = true;
self.slashCooldown = 60; // 1 second cooldown
self.dashCooldown = 60;
var startX = self.x;
var dashTargetX = self.facingDirection > 0 ? Math.min(startX + self.dashDistance, 2048 - 40) : Math.max(startX - self.dashDistance, 40);
// Dash forward with tween
tween(self, {
x: dashTargetX
}, {
duration: self.dashDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isDashing = false;
}
});
// Create extended slash effect during dash
var slashEffect = game.addChild(LK.getAsset('slashEffect', {
anchorX: 0.5,
anchorY: 0.5
}));
slashEffect.x = self.x + 75 * self.facingDirection;
slashEffect.y = self.y - 60;
slashEffect.alpha = 0.9;
slashEffect.scaleX = 2.0 * self.facingDirection;
slashEffect.scaleY = 1.5;
// Follow player during dash
var slashFollowInterval = LK.setInterval(function () {
if (self.isDashing) {
slashEffect.x = self.x + 75 * self.facingDirection;
}
}, 16);
// Animate slash effect
tween(slashEffect, {
scaleX: 2.5,
scaleY: 2.0,
alpha: 0
}, {
duration: self.dashDuration,
onFinish: function onFinish() {
LK.clearInterval(slashFollowInterval);
slashEffect.destroy();
}
});
LK.getSound('slash').play();
// Extended damage check during dash
var damageCheckInterval = LK.setInterval(function () {
if (self.isDashing) {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
var enemyInFront = self.facingDirection > 0 ? enemy.x > self.x - 50 && enemy.x < self.x + 150 : enemy.x < self.x + 50 && enemy.x > self.x - 150;
if (distance < self.slashRange * 1.5 && enemyInFront) {
enemy.takeDamage(self.slashDamage * 2); // Double damage during dash
}
}
// Check for enemy bullet hits during dash
for (var j = enemyBullets.length - 1; j >= 0; j--) {
var bullet = enemyBullets[j];
var bulletDistance = Math.sqrt(Math.pow(bullet.x - self.x, 2) + Math.pow(bullet.y - self.y, 2));
var bulletInFront = self.facingDirection > 0 ? bullet.x > self.x - 50 && bullet.x < self.x + 150 : bullet.x < self.x + 50 && bullet.x > self.x - 150;
if (bulletDistance < self.slashRange * 1.5 && bulletInFront) {
// Destroy the bullet
bullet.destroy();
enemyBullets.splice(j, 1);
// Award small points for destroying bullets
LK.setScore(LK.getScore() + 5);
}
}
}
}, 30);
LK.setTimeout(function () {
LK.clearInterval(damageCheckInterval);
self.isSlashing = false;
}, self.dashDuration);
}
};
self.slash = function () {
if (self.slashCooldown <= 0 && !self.isSlashing && !self.isDashing) {
self.isSlashing = true;
self.slashCooldown = 20; // 1/3 second at 60fps
// Create slash effect
var slashEffect = game.addChild(LK.getAsset('slashEffect', {
anchorX: 0.5,
anchorY: 0.5
}));
slashEffect.x = self.x + 75 * self.facingDirection;
slashEffect.y = self.y - 60;
slashEffect.alpha = 0.8;
slashEffect.scaleX = self.facingDirection;
// Animate slash effect
tween(slashEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
slashEffect.destroy();
}
});
LK.getSound('slash').play();
// Check for enemy hits
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
var enemyInRange = self.facingDirection > 0 ? enemy.x > self.x - 50 && enemy.x < self.x + self.slashRange : enemy.x < self.x + 50 && enemy.x > self.x - self.slashRange;
if (distance < self.slashRange && enemyInRange) {
enemy.takeDamage(self.slashDamage);
}
}
// Check for enemy bullet hits
for (var j = enemyBullets.length - 1; j >= 0; j--) {
var bullet = enemyBullets[j];
var bulletDistance = Math.sqrt(Math.pow(bullet.x - self.x, 2) + Math.pow(bullet.y - self.y, 2));
var bulletInRange = self.facingDirection > 0 ? bullet.x > self.x - 50 && bullet.x < self.x + self.slashRange : bullet.x < self.x + 50 && bullet.x > self.x - self.slashRange;
if (bulletDistance < self.slashRange && bulletInRange) {
// Destroy the bullet
bullet.destroy();
enemyBullets.splice(j, 1);
// Award small points for destroying bullets
LK.setScore(LK.getScore() + 5);
}
}
LK.setTimeout(function () {
self.isSlashing = false;
}, 200);
}
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.baseY = self.y;
var jumpHeight = 200; // Jump height in pixels
// Jump up with tween
tween(self, {
y: self.baseY - jumpHeight
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fall back down
tween(self, {
y: self.baseY
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.takeDamage = function (damage) {
if (!self.invulnerable) {
self.health -= damage;
self.invulnerable = true;
self.invulnerabilityTime = 120; // 2 seconds at 60fps
// Flash effect
LK.effects.flashObject(self, 0xff0000, 500);
if (self.health <= 0) {
self.health = 0;
LK.showGameOver();
}
}
};
self.update = function () {
// Update player image direction based on facing direction
playerGraphics.scaleX = self.facingDirection;
if (self.slashCooldown > 0) {
self.slashCooldown--;
}
if (self.dashCooldown > 0) {
self.dashCooldown--;
}
if (self.invulnerabilityTime > 0) {
self.invulnerabilityTime--;
if (self.invulnerabilityTime <= 0) {
self.invulnerable = false;
}
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.bobOffset = 0;
self.update = function () {
self.x += self.speed;
self.bobOffset += 0.1;
self.y += Math.sin(self.bobOffset) * 0.5;
// Check collision with player
if (self.intersects(player)) {
self.collect();
}
};
self.collect = function () {
LK.getSound('powerUpSound').play();
// Random power-up effect
var powerType = Math.floor(Math.random() * 3);
switch (powerType) {
case 0:
// Increased damage
player.slashDamage += 1;
break;
case 1:
// Wider slash range
player.slashRange += 50;
break;
case 2:
// Temporary invincibility
player.invulnerable = true;
player.invulnerabilityTime = 300; // 5 seconds
break;
}
// Remove from powerUps array
var index = powerUps.indexOf(self);
if (index > -1) {
powerUps.splice(index, 1);
}
self.destroy();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Game variables
var player;
var enemies = [];
var powerUps = [];
var enemyBullets = [];
var enemySpawnTimer = 0;
var enemySpawnRate = 180; // Start spawning every 3 seconds
var gameTime = 0;
var healthBar;
var healthBarBg;
// Create UI elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 120;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
// Create health bar background
healthBarBg = game.addChild(LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0
}));
healthBarBg.x = 120;
healthBarBg.y = 120;
// Create health bar
healthBar = game.addChild(LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0
}));
healthBar.x = 122;
healthBar.y = 122;
// Create health text
var healthTxt = new Text2('Health', {
size: 40,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 120;
healthTxt.y = 160;
game.addChild(healthTxt);
// Create background
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Create player
player = game.addChild(new Player());
player.x = 300;
player.y = 2732 - 150; // Near bottom of screen
// Start background music
LK.playMusic('bgmusic');
// Touch controls for movement and slashing
var isDragging = false;
var lastTouchX = 0;
var lastTouchY = 0;
var swipeThreshold = 100; // Minimum distance for swipe detection
game.down = function (x, y, obj) {
isDragging = true;
lastTouchX = x;
lastTouchY = y;
// Set facing direction based on touch position relative to player
if (x < player.x) {
player.facingDirection = -1; // Face left
} else {
player.facingDirection = 1; // Face right
}
player.dashSlash();
};
game.move = function (x, y, obj) {
if (isDragging) {
var deltaX = x - lastTouchX;
player.x += deltaX * 0.5; // Smooth movement multiplier
// Keep player within screen bounds
if (player.x < 40) player.x = 40;
if (player.x > 2048 - 40) player.x = 2048 - 40;
lastTouchX = x;
}
};
game.up = function (x, y, obj) {
if (isDragging) {
var deltaY = lastTouchY - y; // Upward swipe has positive deltaY
var deltaX = Math.abs(x - lastTouchX);
// Check for upward swipe (vertical movement greater than horizontal and above threshold)
if (deltaY > swipeThreshold && deltaY > deltaX) {
player.jump();
}
}
isDragging = false;
};
// Spawn enemies
function spawnEnemy() {
var rand = Math.random();
var enemyType = rand < 0.5 ? 'basic' : rand < 0.8 ? 'armored' : 'shooting';
var enemy = game.addChild(new Enemy(enemyType));
// Randomly spawn from left or right
var spawnFromRight = Math.random() < 0.5;
if (spawnFromRight) {
enemy.x = 2048 + 100; // Start off-screen right
enemy.speed = enemy.type === 'armored' ? -3 : enemy.type === 'shooting' ? -2 : -4; // Move left
} else {
enemy.x = -100; // Start off-screen left
enemy.speed = enemy.type === 'armored' ? 3 : enemy.type === 'shooting' ? 2 : 4; // Move right
}
enemy.y = 2732 - 150 + Math.random() * 100 - 50; // Vary height slightly
enemies.push(enemy);
}
// Main game loop
game.update = function () {
gameTime++;
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBar.width = 200 * healthPercent;
// Spawn enemies
enemySpawnTimer++;
if (enemySpawnTimer >= enemySpawnRate) {
spawnEnemy();
enemySpawnTimer = 0;
// Increase difficulty over time
if (enemySpawnRate > 60) {
// Minimum spawn rate
enemySpawnRate -= 0.5;
}
}
// Clean up off-screen enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.x < -100 || enemy.x > 2048 + 100) {
enemy.destroy();
enemies.splice(i, 1);
}
}
// Clean up off-screen power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.x < -100) {
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Clean up off-screen enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.x < -100 || bullet.x > 2048 + 100) {
bullet.destroy();
enemyBullets.splice(i, 1);
}
}
};
efek slash kepala naga petir ke depan. In-Game asset. 2d. High contrast. No shadows
side scroller blue steel armored ninja aksi koreographi dua tangan memegang pedang ke depan In-Game asset. 2d. No shadows
side scroller image fat orc samurai front holding big axe. In-Game asset. 2d. High contrast. No shadows
side scroller kepala evil kelinci berapi. In-Game asset. 2d. High contrast. No shadows
realistic 2d anime style front field old samurai palace temple with pig evil ornament at midnight. In-Game asset. 2d. High contrast. No shadows