/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Create enemy visual using enemy image asset
var enemyBody = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.0,
scaleY: 4.0
});
// Create left leg
var leftLeg = self.attachAsset('enemyLeg', {
anchorX: 0.5,
anchorY: 0,
x: -80,
y: 200
});
// Create right leg
var rightLeg = self.attachAsset('enemyLeg', {
anchorX: 0.5,
anchorY: 0,
x: 160,
y: 200
});
// Create right hand
var rightHand = self.attachAsset('enemyRightHand', {
anchorX: 0.5,
anchorY: 0.5,
x: 140,
y: -20,
scaleX: 1.5,
scaleY: 1.5
});
// Create left hand
var leftHand = self.attachAsset('enemyLeftHand', {
anchorX: 0.5,
anchorY: 0.5,
x: -70,
y: -70,
scaleX: 1.5,
scaleY: 1.5
});
// Initialize animation timer for other effects
self.animationTimer = 0;
// Create enemy eyes
var leftEnemyEye = self.attachAsset('eyeWhite', {
anchorX: 0.5,
anchorY: 0.5,
x: 48,
y: -150,
scaleX: 0.4,
scaleY: 0.4
});
var rightEnemyEye = self.attachAsset('eyeWhite', {
anchorX: 0.5,
anchorY: 0.5,
x: 108,
y: -150,
scaleX: 0.4,
scaleY: 0.4
});
// Create black pupils inside the enemy white eyes
var leftEnemyPupil = self.attachAsset('eye', {
anchorX: 0.5,
anchorY: 0.5,
x: 48,
y: -150,
scaleX: 0.6,
scaleY: 0.6
});
var rightEnemyPupil = self.attachAsset('eye', {
anchorX: 0.5,
anchorY: 0.5,
x: 108,
y: -150,
scaleX: 0.6,
scaleY: 0.6
});
// Create enemy mouth
var enemyMouth = self.attachAsset('enemyMouthImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 78,
y: -100,
scaleX: 1.2,
scaleY: 1.2,
visible: false // Initially hidden
});
self.speed = 3;
self.baseSpeed = 3; // Store original speed
self.direction = 1;
self.shootTimer = 0;
self.speedChangeTimer = 0; // Timer for speed variations
self.shootInterval = 30; // Shoot every 0.5 seconds at 60fps (increased spawn rate)
self.walkAnimationTimer = 0;
self.isFrozen = false; // Track if enemy is frozen after hitting player with second projectile
self.freezeTimer = 0; // Timer for freeze duration
self.update = function () {
// Handle frozen state
if (self.isFrozen) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.isFrozen = false;
}
return; // Skip all movement and shooting when frozen
}
// Move horizontally
self.x += self.speed * self.direction;
// Bounce at screen edges
if (self.x <= 100 || self.x >= 1948) {
self.direction *= -1;
}
// Random direction change system - enemy can change direction at any time
if (Math.random() < 0.008) {
// 0.8% chance per frame to change direction
self.direction *= -1; // Reverse current direction
}
// Random speed variation system
self.speedChangeTimer++;
if (self.speedChangeTimer >= 120) {
// Every 2 seconds at 60fps
self.speedChangeTimer = 0;
// Randomly vary speed between 2 and 5
self.speed = self.baseSpeed + (Math.random() - 0.5) * 2;
if (self.speed < 1) self.speed = 1; // Minimum speed
if (self.speed > 6) self.speed = 6; // Maximum speed
}
// Walking animation - alternate leg positions
self.walkAnimationTimer++;
if (self.walkAnimationTimer >= 15) {
// Change leg position every 15 frames
self.walkAnimationTimer = 0;
// Animate left leg with walking motion (horizontal and vertical)
tween(leftLeg, {
x: leftLeg.x === -80 ? -100 : -80,
y: leftLeg.y === 200 ? 180 : 200
}, {
duration: 200,
easing: tween.easeInOut
});
// Animate right leg with walking motion (opposite to left leg)
tween(rightLeg, {
x: rightLeg.x === 160 ? 180 : 160,
y: rightLeg.y === 200 ? 180 : 200
}, {
duration: 200,
easing: tween.easeInOut
});
}
// Update enemy pupils to track player
if (player) {
// Calculate angle from enemy pupils to player
var leftEyeCenterX = self.x + 48;
var leftEyeCenterY = self.y - 150;
var rightEyeCenterX = self.x + 108;
var rightEyeCenterY = self.y - 150;
// Maximum distance pupils can move from center
var maxPupilDistance = 8;
// Calculate angles from eye centers to player
var leftAngle = Math.atan2(player.y - leftEyeCenterY, player.x - leftEyeCenterX);
var rightAngle = Math.atan2(player.y - rightEyeCenterY, player.x - rightEyeCenterX);
// Position pupils to track player within eye boundaries
leftEnemyPupil.x = 48 + Math.cos(leftAngle) * maxPupilDistance;
leftEnemyPupil.y = -150 + Math.sin(leftAngle) * maxPupilDistance;
rightEnemyPupil.x = 108 + Math.cos(rightAngle) * maxPupilDistance;
rightEnemyPupil.y = -150 + Math.sin(rightAngle) * maxPupilDistance;
}
// General animation timer for other effects
self.animationTimer++;
if (self.animationTimer >= 5) {
self.animationTimer = 0;
}
// Shooting logic
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shootTimer = 0;
// Randomly choose projectile type with increased probability for second projectile
if (Math.random() > 0.4) {
// Create regular projectile
var projectile = new EnemyProjectile();
projectile.x = self.x;
projectile.y = self.y + 50;
enemyProjectiles.push(projectile);
game.addChild(projectile);
} else {
// Only create EnemyProjectile2 if there are less than 4 on screen
if (enemyProjectiles2.length < 4) {
// Create new projectile type from right arm area
var projectile2 = new EnemyProjectile2();
projectile2.x = self.x + 140; // Position at right arm/hand area
projectile2.y = self.y + 50;
enemyProjectiles2.push(projectile2);
game.addChild(projectile2);
}
}
// Play enemy shooting sound
LK.getSound('enemyShoot').play();
}
};
// Method to show mouth when player is hit by second projectile
self.showMouth = function () {
enemyMouth.visible = true;
// Play enemy mouth sound
LK.getSound('enemyMouthSound').play();
// Hide mouth after 4 seconds using tween
tween(enemyMouth, {
alpha: 0
}, {
duration: 4000,
easing: tween.easeOut,
onFinish: function onFinish() {
enemyMouth.visible = false;
enemyMouth.alpha = 1; // Reset alpha for next time
}
});
};
// Method to freeze enemy when player is hit by second projectile
self.freezeEnemy = function () {
self.isFrozen = true;
self.freezeTimer = shieldDuration; // Freeze for same duration as shield (240 frames = 4 seconds)
// Play enemy laughing sound when frozen
LK.getSound('enemyLaugh').play();
// Add shaking animation while laughing
tween(self, {
x: self.x + 10
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
x: self.x - 20
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
x: self.x + 10
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
}
});
};
return self;
});
var EnemyProjectile = Container.expand(function () {
var self = Container.call(this);
var projectileBody = self.attachAsset('enemyProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
self.speed = 6;
self.update = function () {
self.y += self.speed * gameSpeedMultiplier;
};
return self;
});
var EnemyProjectile2 = Container.expand(function () {
var self = Container.call(this);
var projectileBody = self.attachAsset('enemyProjectile2', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 4;
self.sideSpeed = 2;
self.direction = Math.random() > 0.5 ? 1 : -1;
self.effectTimer = 0;
self.update = function () {
self.y += self.speed * gameSpeedMultiplier;
self.x += self.sideSpeed * self.direction * gameSpeedMultiplier;
// Bounce off screen edges
if (self.x <= 0 || self.x >= 2048) {
self.direction *= -1;
}
// Create flame and smoke effects
self.effectTimer++;
if (self.effectTimer >= 5) {
self.effectTimer = 0;
// Create flame particles
if (Math.random() < 0.03) {
var flameParticle = game.attachAsset('flame', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + 35 + (Math.random() - 0.5) * 30,
y: self.y - 50 + (Math.random() - 0.5) * 20,
scaleX: 0.5 + Math.random() * 0.3,
scaleY: 0.5 + Math.random() * 0.3,
alpha: 0.8 + Math.random() * 0.2
});
// Animate flame trailing behind projectile
tween(flameParticle, {
y: flameParticle.y + 40 + Math.random() * 30,
x: flameParticle.x + (Math.random() - 0.5) * 20,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
flameParticle.destroy();
}
});
}
// Create smoke particles
if (Math.random() < 0.02) {
var smokeParticle = game.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + (Math.random() - 0.5) * 25,
y: self.y + (Math.random() - 0.5) * 15,
scaleX: 0.3 + Math.random() * 0.2,
scaleY: 0.3 + Math.random() * 0.2,
alpha: 0.6
});
// Animate smoke trailing and dispersing
tween(smokeParticle, {
y: smokeParticle.y + 50 + Math.random() * 25,
x: smokeParticle.x + (Math.random() - 0.5) * 15,
alpha: 0,
scaleX: 0.8 + Math.random() * 0.4,
scaleY: 0.8 + Math.random() * 0.4
}, {
duration: 400 + Math.random() * 300,
easing: tween.easeOut,
onFinish: function onFinish() {
smokeParticle.destroy();
}
});
}
// Create additional particle effects
if (Math.random() < 0.01) {
var projectileParticle = new Particle();
projectileParticle.x = self.x + (Math.random() - 0.5) * 40;
projectileParticle.y = self.y - 10 + (Math.random() - 0.5) * 15;
projectileParticle.velocityX = (Math.random() - 0.5) * 6;
projectileParticle.velocityY = -Math.random() * 4 - 2;
particles.push(projectileParticle);
game.addChild(projectileParticle);
}
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
// Choose random particle color/type
var particleTypes = ['redParticle', 'orangeParticle', 'yellowParticle', 'whiteParticle', 'smoke'];
var randomType = particleTypes[Math.floor(Math.random() * particleTypes.length)];
var particleBody = self.attachAsset(randomType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + Math.random() * 0.6,
scaleY: 0.3 + Math.random() * 0.6
});
// Random particle properties
self.velocityX = (Math.random() - 0.5) * 8;
self.velocityY = -Math.random() * 6 - 2;
self.gravity = 0.2;
self.life = 90 + Math.random() * 60; // 1.5-2.5 seconds at 60fps
self.maxLife = self.life;
self.update = function () {
// Apply physics
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
// Fade out over time
self.life--;
self.alpha = self.life / self.maxLife;
// Remove when life is over or off screen
if (self.life <= 0 || self.y > 2732 + 100 || self.y < -100 || self.x < -100 || self.x > 2148) {
self.destroy();
// Remove from particles array
for (var p = particles.length - 1; p >= 0; p--) {
if (particles[p] === self) {
particles.splice(p, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8B4513
});
/****
* Game Code
****/
var backgroundImage = game.attachAsset('background', {
x: 0,
y: 0,
width: 2048,
height: 2732
});
// Add new background layer
var backgroundLayer = game.attachAsset('backgroundLayer', {
x: 0,
y: 0,
width: 2100,
height: 700
});
// Create ground terrain
var ground = game.attachAsset('ground', {
x: 0,
y: 2682,
// Position at bottom (2732 - 50 = 2682)
width: 2048,
height: 50
});
// Create and position player on the ground
var player = game.attachAsset('player', {
x: 2048 / 2,
// Center horizontally
y: 2682,
// Position touching the ground
scaleX: 1.0,
scaleY: 1.0,
anchorX: 0.5,
anchorY: 1.0
});
// Scale player to 3.0 using tween animation
tween(player, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 500,
easing: tween.easeOut
});
// Create white eyes on the player
var leftEye = game.attachAsset('eyeWhite', {
x: player.x - 50,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
var rightEye = game.attachAsset('eyeWhite', {
x: player.x + 10,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
// Create black pupils inside the white eyes
var leftPupil = game.attachAsset('eye', {
x: player.x - 50,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
var rightPupil = game.attachAsset('eye', {
x: player.x + 10,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
// Create player feet
var leftFoot = game.attachAsset('playerLeftFoot', {
x: player.x - 80,
y: player.y + 10,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
var rightFoot = game.attachAsset('playerRightFoot', {
x: player.x + 80,
y: player.y + 10,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
// Create player right hand
var playerRightHand = game.attachAsset('playerRightHand', {
x: player.x + 120,
y: player.y - 120,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Variables for player movement
var playerSpeed = 12;
var targetX = player.x;
var isMoving = false;
var playerLives = 3;
var isShielded = false;
var shieldTimer = 0;
var shieldDuration = 240; // 4 seconds at 60fps
var isFlying = false; // Track if player is currently flying after being hit
var shieldBubble = null; // Shield bubble visual element
var walkAnimationTimer = 0; // Timer for walking animation
// Game speed system
var gameSpeedMultiplier = 1.0;
var speedIncreaseTimer = 0;
var speedIncreaseInterval = 600; // Increase speed every 10 seconds at 60fps
// Create life hearts on the left side
var lifeHearts = [];
for (var h = 0; h < 3; h++) {
// Create heart using image asset
var heartImage = game.attachAsset('lifeHeartImage', {
x: 150 + h * 100,
y: 750,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
});
lifeHearts.push(heartImage);
}
// Create score display
var scoreText = new Text2('PUAN: 0', {
size: 120,
fill: 0xFFFFFF,
font: "'Montserrat', 'Open Sans', 'Lato', 'Source Sans Pro', 'Ubuntu', sans-serif"
});
scoreText.anchor.set(0.5, 0);
scoreText.x = 2048 / 2;
scoreText.y = 850;
game.addChild(scoreText);
// Enemy and projectile tracking
var enemy = null;
var enemyProjectiles = [];
var enemyProjectiles2 = [];
var particles = [];
// Create enemy at top of screen
enemy = game.addChild(new Enemy());
enemy.x = 1124; // Move slightly to the right
enemy.y = 450; // Moved up from previous position
// Touch/mouse controls for player movement
game.down = function (x, y, obj) {
// Set target position to touch/click location
targetX = x;
isMoving = true;
// Keep player within screen bounds
if (targetX < 0) targetX = 0;
if (targetX > 2048) targetX = 2048;
};
// Update player movement
game.update = function () {
// Update game speed system
speedIncreaseTimer++;
if (speedIncreaseTimer >= speedIncreaseInterval) {
speedIncreaseTimer = 0;
gameSpeedMultiplier += 0.1; // Increase speed by 10% every 10 seconds
// Cap maximum speed at 3x original speed
if (gameSpeedMultiplier > 3.0) {
gameSpeedMultiplier = 3.0;
}
}
if (isMoving) {
// Calculate distance to target
var distance = targetX - player.x;
// Move towards target
if (Math.abs(distance) > 5) {
if (distance > 0) {
player.x += playerSpeed;
} else {
player.x -= playerSpeed;
}
// Animate walking - move feet up and down alternately
walkAnimationTimer++;
if (walkAnimationTimer >= 10) {
walkAnimationTimer = 0;
// Animate left foot up and down
tween(leftFoot, {
y: leftFoot.y === player.y + 10 ? player.y - 10 : player.y + 10
}, {
duration: 150,
easing: tween.easeInOut
});
// Animate right foot up and down (opposite to left foot)
tween(rightFoot, {
y: rightFoot.y === player.y + 10 ? player.y - 10 : player.y + 10
}, {
duration: 150,
easing: tween.easeInOut
});
}
} else {
// Snap to target when close enough
player.x = targetX;
isMoving = false;
}
// Keep player within bounds
if (player.x < 0) player.x = 0;
if (player.x > 2048) player.x = 2048;
}
// Update feet positions to follow player
leftFoot.x = player.x - 80;
rightFoot.x = player.x + 80;
// Update right hand position to follow player
playerRightHand.x = player.x + 120;
playerRightHand.y = player.y - 120;
// Reset feet position when not moving
if (!isMoving) {
leftFoot.y = player.y + 10;
rightFoot.y = player.y + 10;
}
// Update shield system
if (isShielded) {
shieldTimer--;
// Hide player right hand when shield is active
playerRightHand.visible = false;
// Show shield bubble if not already visible
if (!shieldBubble) {
shieldBubble = game.attachAsset('shieldBubble', {
x: player.x,
y: player.y - 150,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1,
tint: 0xffffff
});
// Add pulsing animation to shield bubble
tween(shieldBubble, {
scaleX: 1.1,
scaleY: 1.1,
alpha: 0.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (shieldBubble) {
tween(shieldBubble, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.05
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
// Update shield bubble position to follow player
if (shieldBubble) {
shieldBubble.x = player.x;
shieldBubble.y = player.y - 150;
}
// Flash player blue to indicate shield
if (shieldTimer % 20 < 10) {
player.tint = 0x00ffff;
} else {
player.tint = 0xffffff;
}
// Deactivate shield when timer expires
if (shieldTimer <= 0) {
isShielded = false;
player.tint = 0xffffff;
// Show player right hand when shield is deactivated
playerRightHand.visible = true;
// Stop pupil spinning and reset rotation
tween.stop(leftPupil, {
rotation: true
});
tween.stop(rightPupil, {
rotation: true
});
leftPupil.rotation = 0;
rightPupil.rotation = 0;
// Remove shield bubble
if (shieldBubble) {
shieldBubble.destroy();
shieldBubble = null;
}
// Clear shield sound interval
if (game.shieldSoundInterval) {
LK.clearInterval(game.shieldSoundInterval);
game.shieldSoundInterval = null;
}
}
}
// Update eye positions to follow player
leftEye.x = player.x - 50;
leftEye.y = player.y - 245;
rightEye.x = player.x + 10;
rightEye.y = player.y - 245;
// Keep pupils centered when not shielded, but track falling projectiles
if (!isShielded) {
// Default pupil positions (eye centers)
var leftEyeCenterX = player.x - 50;
var leftEyeCenterY = player.y - 245;
var rightEyeCenterX = player.x + 10;
var rightEyeCenterY = player.y - 245;
// Maximum distance pupils can move from center
var maxPupilDistance = 8;
// Find closest falling projectile to track
var closestProjectile = null;
var closestDistance = Infinity;
// Check all enemy projectiles
for (var p = 0; p < enemyProjectiles.length; p++) {
var proj = enemyProjectiles[p];
var distance = Math.sqrt(Math.pow(proj.x - player.x, 2) + Math.pow(proj.y - player.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
closestProjectile = proj;
}
}
// Check all enemy projectiles type 2
for (var p2 = 0; p2 < enemyProjectiles2.length; p2++) {
var proj2 = enemyProjectiles2[p2];
var distance2 = Math.sqrt(Math.pow(proj2.x - player.x, 2) + Math.pow(proj2.y - player.y, 2));
if (distance2 < closestDistance) {
closestDistance = distance2;
closestProjectile = proj2;
}
}
if (closestProjectile) {
// Calculate angles from eye centers to the closest projectile
var leftAngle = Math.atan2(closestProjectile.y - leftEyeCenterY, closestProjectile.x - leftEyeCenterX);
var rightAngle = Math.atan2(closestProjectile.y - rightEyeCenterY, closestProjectile.x - rightEyeCenterX);
// Position pupils within eye boundaries, tracking the projectile
leftPupil.x = leftEyeCenterX + Math.cos(leftAngle) * maxPupilDistance;
leftPupil.y = leftEyeCenterY + Math.sin(leftAngle) * maxPupilDistance;
rightPupil.x = rightEyeCenterX + Math.cos(rightAngle) * maxPupilDistance;
rightPupil.y = rightEyeCenterY + Math.sin(rightAngle) * maxPupilDistance;
} else {
// No projectiles to track, center pupils
leftPupil.x = leftEyeCenterX;
leftPupil.y = leftEyeCenterY;
rightPupil.x = rightEyeCenterX;
rightPupil.y = rightEyeCenterY;
}
} else {
// When shield is active, make pupils move diagonally (down-left and diagonally)
var leftEyeCenterX = player.x - 50;
var leftEyeCenterY = player.y - 245;
var rightEyeCenterX = player.x + 10;
var rightEyeCenterY = player.y - 245;
var maxBounceDistance = 12;
// Use diagonal movement patterns based on time
var bounceTime = LK.ticks * 0.12;
// Create diagonal movements: down-left and cross-diagonal
var bounceX = Math.sin(bounceTime) * maxBounceDistance;
var bounceY = Math.cos(bounceTime * 0.8) * (maxBounceDistance * 0.7);
// Position pupils with diagonal movement patterns
leftPupil.x = leftEyeCenterX + bounceX;
leftPupil.y = leftEyeCenterY + bounceY;
rightPupil.x = rightEyeCenterX - bounceX; // Opposite X movement for variety
rightPupil.y = rightEyeCenterY + bounceY;
}
// Update enemy projectiles
for (var i = enemyProjectiles.length - 1; i >= 0; i--) {
var projectile = enemyProjectiles[i];
// Remove projectiles that go off screen (any direction)
if (projectile.y > 2732 + 100 || projectile.y < -100 || projectile.x < -100 || projectile.x > 2148) {
projectile.destroy();
enemyProjectiles.splice(i, 1);
continue;
}
// Check collision with player
if (projectile.intersects(player)) {
// Check if player is protected by shield
if (!isShielded) {
// Award points when hit by enemy projectile
LK.setScore(LK.getScore() + 10);
// Update score display
scoreText.setText('PUAN: ' + LK.getScore());
// Play sound effect for first projectile hit
LK.getSound('enemyProjectileHit').play();
// Create reduced particle burst effect at player's mouth area
for (var particleCount = 0; particleCount < 1; particleCount++) {
var particle = new Particle();
particle.x = player.x + (Math.random() - 0.5) * 40;
particle.y = player.y - 130 + (Math.random() - 0.5) * 30; // Position higher up from mouth area
// Reduce particle velocity for better performance
particle.velocityX = (Math.random() - 0.5) * 10;
particle.velocityY = -Math.random() * 8 - 3;
particles.push(particle);
game.addChild(particle);
}
// Simplified mouth trembling effect
tween(player, {
scaleX: 3.2,
scaleY: 2.8
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 150,
easing: tween.easeOut
});
}
});
}
projectile.destroy();
enemyProjectiles.splice(i, 1);
}
}
// Update enemy projectiles type 2
for (var j = enemyProjectiles2.length - 1; j >= 0; j--) {
var projectile2 = enemyProjectiles2[j];
// Remove projectiles that go off screen (any direction)
if (projectile2.y > 2732 + 100 || projectile2.y < -100 || projectile2.x < -100 || projectile2.x > 2148) {
projectile2.destroy();
enemyProjectiles2.splice(j, 1);
continue;
}
// Check collision with player
if (projectile2.intersects(player) && !isShielded && !isFlying) {
// Show enemy mouth when hit by second projectile
enemy.showMouth();
// Freeze enemy movement and shooting
enemy.freezeEnemy();
// Play sound effect for second projectile hit
LK.getSound('enemyProjectile2Hit').play();
// Stop current player movement
isMoving = false;
// Set flying state to true
isFlying = true;
// Player flies off screen to the left while spinning
tween(player, {
x: -200,
y: player.y - 800,
rotation: Math.PI * 4
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return player from right side after flying off
player.x = 2248;
player.y = 2682;
player.rotation = 0;
// Reset flying state
isFlying = false;
// Activate shield protection
isShielded = true;
shieldTimer = shieldDuration;
// Play shield activation sound
LK.getSound('shieldSound').play();
// Set up repeating shield sound during shield duration
var shieldSoundInterval = LK.setInterval(function () {
if (isShielded && shieldTimer > 0) {
LK.getSound('shieldSound').play();
}
}, 1750); // Play every 1750ms
// Store interval reference for cleanup
game.shieldSoundInterval = shieldSoundInterval;
// Shield bubble will be created in the shield update section
// Animate player returning to screen
tween(player, {
x: 2048 / 2
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Update feet positions during flight animation
tween(leftFoot, {
x: -330,
y: player.y - 790
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
leftFoot.x = 2168;
leftFoot.y = 2692;
tween(leftFoot, {
x: 2048 / 2 - 80
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(rightFoot, {
x: -120,
y: player.y - 790
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rightFoot.x = 2128;
rightFoot.y = 2692;
tween(rightFoot, {
x: 2048 / 2 + 80
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Update eye positions during flight animation
tween(leftEye, {
x: -250,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
leftEye.x = 2198;
leftEye.y = 2437;
tween(leftEye, {
x: 2048 / 2 - 50
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(rightEye, {
x: -190,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rightEye.x = 2258;
rightEye.y = 2437;
tween(rightEye, {
x: 2048 / 2 + 10
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(leftPupil, {
x: -250,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
leftPupil.x = 2198;
leftPupil.y = 2437;
tween(leftPupil, {
x: 2048 / 2 - 50
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(rightPupil, {
x: -190,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rightPupil.x = 2258;
rightPupil.y = 2437;
tween(rightPupil, {
x: 2048 / 2 + 10
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Update right hand position during flight animation
tween(playerRightHand, {
x: -80,
y: player.y - 920
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
playerRightHand.x = 2368;
playerRightHand.y = 2562;
tween(playerRightHand, {
x: 2048 / 2 + 120
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Create particle burst effect
for (var particleCount = 0; particleCount < 5; particleCount++) {
var particle = new Particle();
particle.x = player.x + (Math.random() - 0.5) * 80;
particle.y = player.y - 50 + (Math.random() - 0.5) * 70;
// Give particles more dramatic velocities for this bigger explosion
particle.velocityX = (Math.random() - 0.5) * 18;
particle.velocityY = -Math.random() * 12 - 4;
particles.push(particle);
game.addChild(particle);
}
// Create smoke and flame effects
for (var smokeCount = 0; smokeCount < 2; smokeCount++) {
var smoke = game.attachAsset('smoke', {
x: player.x + (Math.random() - 0.5) * 200,
y: player.y - 100 + (Math.random() - 0.5) * 100,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
// Animate smoke rising and fading
tween(smoke, {
y: smoke.y - 200 - Math.random() * 100,
alpha: 0,
scaleX: 2 + Math.random(),
scaleY: 2 + Math.random()
}, {
duration: 2000 + Math.random() * 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
smoke.destroy();
}
});
}
for (var flameCount = 0; flameCount < 1; flameCount++) {
var flame = game.attachAsset('flame', {
x: player.x + (Math.random() - 0.5) * 150,
y: player.y - 50 + (Math.random() - 0.5) * 80,
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
// Animate flames flickering and rising
tween(flame, {
y: flame.y - 150 - Math.random() * 50,
alpha: 0,
scaleX: 1.5 + Math.random() * 0.5,
scaleY: 1.5 + Math.random() * 0.5
}, {
duration: 1500 + Math.random() * 500,
easing: tween.easeOut,
onFinish: function onFinish() {
flame.destroy();
}
});
}
// Create fire animation effect on player
tween(player, {
tint: 0xff4500
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
});
// Reduce player lives
playerLives--;
// Update heart visibility based on remaining lives
for (var heartIndex = 0; heartIndex < lifeHearts.length; heartIndex++) {
if (heartIndex >= playerLives) {
// Hide heart with fade out animation
tween(lifeHearts[heartIndex], {
alpha: 0.2,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
}
}
if (playerLives <= 0) {
// Show game over when no lives left
LK.showGameOver();
}
projectile2.destroy();
enemyProjectiles2.splice(j, 1);
}
}
// Comprehensive cleanup system - remove all off-screen elements every frame
var allChildren = game.children.slice(); // Create a copy to avoid modification during iteration
var screenBuffer = 100; // Buffer zone around visible area
for (var c = 0; c < allChildren.length; c++) {
var child = allChildren[c];
// Skip essential game elements that should always stay
if (child === player || child === enemy || child === backgroundImage || child === backgroundLayer || child === ground || child === scoreText || child === leftEye || child === rightEye || child === leftPupil || child === rightPupil || child === leftFoot || child === rightFoot || child === playerRightHand || child === shieldBubble) {
continue;
}
// Skip life hearts
var isLifeHeart = false;
for (var h = 0; h < lifeHearts.length; h++) {
if (child === lifeHearts[h]) {
isLifeHeart = true;
break;
}
}
if (isLifeHeart) {
continue;
}
// Remove if off screen in any direction with buffer zone
if (child.x < -screenBuffer || child.x > 2048 + screenBuffer || child.y < -screenBuffer || child.y > 2732 + screenBuffer) {
child.destroy();
}
}
// Clean up particle arrays - remove destroyed particles
for (var p = particles.length - 1; p >= 0; p--) {
if (!particles[p] || particles[p].destroyed) {
particles.splice(p, 1);
}
}
// Clean up projectile arrays - ensure no destroyed projectiles remain
for (var ep = enemyProjectiles.length - 1; ep >= 0; ep--) {
if (!enemyProjectiles[ep] || enemyProjectiles[ep].destroyed) {
enemyProjectiles.splice(ep, 1);
}
}
for (var ep2 = enemyProjectiles2.length - 1; ep2 >= 0; ep2--) {
if (!enemyProjectiles2[ep2] || enemyProjectiles2[ep2].destroyed) {
enemyProjectiles2.splice(ep2, 1);
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Create enemy visual using enemy image asset
var enemyBody = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.0,
scaleY: 4.0
});
// Create left leg
var leftLeg = self.attachAsset('enemyLeg', {
anchorX: 0.5,
anchorY: 0,
x: -80,
y: 200
});
// Create right leg
var rightLeg = self.attachAsset('enemyLeg', {
anchorX: 0.5,
anchorY: 0,
x: 160,
y: 200
});
// Create right hand
var rightHand = self.attachAsset('enemyRightHand', {
anchorX: 0.5,
anchorY: 0.5,
x: 140,
y: -20,
scaleX: 1.5,
scaleY: 1.5
});
// Create left hand
var leftHand = self.attachAsset('enemyLeftHand', {
anchorX: 0.5,
anchorY: 0.5,
x: -70,
y: -70,
scaleX: 1.5,
scaleY: 1.5
});
// Initialize animation timer for other effects
self.animationTimer = 0;
// Create enemy eyes
var leftEnemyEye = self.attachAsset('eyeWhite', {
anchorX: 0.5,
anchorY: 0.5,
x: 48,
y: -150,
scaleX: 0.4,
scaleY: 0.4
});
var rightEnemyEye = self.attachAsset('eyeWhite', {
anchorX: 0.5,
anchorY: 0.5,
x: 108,
y: -150,
scaleX: 0.4,
scaleY: 0.4
});
// Create black pupils inside the enemy white eyes
var leftEnemyPupil = self.attachAsset('eye', {
anchorX: 0.5,
anchorY: 0.5,
x: 48,
y: -150,
scaleX: 0.6,
scaleY: 0.6
});
var rightEnemyPupil = self.attachAsset('eye', {
anchorX: 0.5,
anchorY: 0.5,
x: 108,
y: -150,
scaleX: 0.6,
scaleY: 0.6
});
// Create enemy mouth
var enemyMouth = self.attachAsset('enemyMouthImage', {
anchorX: 0.5,
anchorY: 0.5,
x: 78,
y: -100,
scaleX: 1.2,
scaleY: 1.2,
visible: false // Initially hidden
});
self.speed = 3;
self.baseSpeed = 3; // Store original speed
self.direction = 1;
self.shootTimer = 0;
self.speedChangeTimer = 0; // Timer for speed variations
self.shootInterval = 30; // Shoot every 0.5 seconds at 60fps (increased spawn rate)
self.walkAnimationTimer = 0;
self.isFrozen = false; // Track if enemy is frozen after hitting player with second projectile
self.freezeTimer = 0; // Timer for freeze duration
self.update = function () {
// Handle frozen state
if (self.isFrozen) {
self.freezeTimer--;
if (self.freezeTimer <= 0) {
self.isFrozen = false;
}
return; // Skip all movement and shooting when frozen
}
// Move horizontally
self.x += self.speed * self.direction;
// Bounce at screen edges
if (self.x <= 100 || self.x >= 1948) {
self.direction *= -1;
}
// Random direction change system - enemy can change direction at any time
if (Math.random() < 0.008) {
// 0.8% chance per frame to change direction
self.direction *= -1; // Reverse current direction
}
// Random speed variation system
self.speedChangeTimer++;
if (self.speedChangeTimer >= 120) {
// Every 2 seconds at 60fps
self.speedChangeTimer = 0;
// Randomly vary speed between 2 and 5
self.speed = self.baseSpeed + (Math.random() - 0.5) * 2;
if (self.speed < 1) self.speed = 1; // Minimum speed
if (self.speed > 6) self.speed = 6; // Maximum speed
}
// Walking animation - alternate leg positions
self.walkAnimationTimer++;
if (self.walkAnimationTimer >= 15) {
// Change leg position every 15 frames
self.walkAnimationTimer = 0;
// Animate left leg with walking motion (horizontal and vertical)
tween(leftLeg, {
x: leftLeg.x === -80 ? -100 : -80,
y: leftLeg.y === 200 ? 180 : 200
}, {
duration: 200,
easing: tween.easeInOut
});
// Animate right leg with walking motion (opposite to left leg)
tween(rightLeg, {
x: rightLeg.x === 160 ? 180 : 160,
y: rightLeg.y === 200 ? 180 : 200
}, {
duration: 200,
easing: tween.easeInOut
});
}
// Update enemy pupils to track player
if (player) {
// Calculate angle from enemy pupils to player
var leftEyeCenterX = self.x + 48;
var leftEyeCenterY = self.y - 150;
var rightEyeCenterX = self.x + 108;
var rightEyeCenterY = self.y - 150;
// Maximum distance pupils can move from center
var maxPupilDistance = 8;
// Calculate angles from eye centers to player
var leftAngle = Math.atan2(player.y - leftEyeCenterY, player.x - leftEyeCenterX);
var rightAngle = Math.atan2(player.y - rightEyeCenterY, player.x - rightEyeCenterX);
// Position pupils to track player within eye boundaries
leftEnemyPupil.x = 48 + Math.cos(leftAngle) * maxPupilDistance;
leftEnemyPupil.y = -150 + Math.sin(leftAngle) * maxPupilDistance;
rightEnemyPupil.x = 108 + Math.cos(rightAngle) * maxPupilDistance;
rightEnemyPupil.y = -150 + Math.sin(rightAngle) * maxPupilDistance;
}
// General animation timer for other effects
self.animationTimer++;
if (self.animationTimer >= 5) {
self.animationTimer = 0;
}
// Shooting logic
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shootTimer = 0;
// Randomly choose projectile type with increased probability for second projectile
if (Math.random() > 0.4) {
// Create regular projectile
var projectile = new EnemyProjectile();
projectile.x = self.x;
projectile.y = self.y + 50;
enemyProjectiles.push(projectile);
game.addChild(projectile);
} else {
// Only create EnemyProjectile2 if there are less than 4 on screen
if (enemyProjectiles2.length < 4) {
// Create new projectile type from right arm area
var projectile2 = new EnemyProjectile2();
projectile2.x = self.x + 140; // Position at right arm/hand area
projectile2.y = self.y + 50;
enemyProjectiles2.push(projectile2);
game.addChild(projectile2);
}
}
// Play enemy shooting sound
LK.getSound('enemyShoot').play();
}
};
// Method to show mouth when player is hit by second projectile
self.showMouth = function () {
enemyMouth.visible = true;
// Play enemy mouth sound
LK.getSound('enemyMouthSound').play();
// Hide mouth after 4 seconds using tween
tween(enemyMouth, {
alpha: 0
}, {
duration: 4000,
easing: tween.easeOut,
onFinish: function onFinish() {
enemyMouth.visible = false;
enemyMouth.alpha = 1; // Reset alpha for next time
}
});
};
// Method to freeze enemy when player is hit by second projectile
self.freezeEnemy = function () {
self.isFrozen = true;
self.freezeTimer = shieldDuration; // Freeze for same duration as shield (240 frames = 4 seconds)
// Play enemy laughing sound when frozen
LK.getSound('enemyLaugh').play();
// Add shaking animation while laughing
tween(self, {
x: self.x + 10
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
x: self.x - 20
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
x: self.x + 10
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
}
});
};
return self;
});
var EnemyProjectile = Container.expand(function () {
var self = Container.call(this);
var projectileBody = self.attachAsset('enemyProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
self.speed = 6;
self.update = function () {
self.y += self.speed * gameSpeedMultiplier;
};
return self;
});
var EnemyProjectile2 = Container.expand(function () {
var self = Container.call(this);
var projectileBody = self.attachAsset('enemyProjectile2', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 4;
self.sideSpeed = 2;
self.direction = Math.random() > 0.5 ? 1 : -1;
self.effectTimer = 0;
self.update = function () {
self.y += self.speed * gameSpeedMultiplier;
self.x += self.sideSpeed * self.direction * gameSpeedMultiplier;
// Bounce off screen edges
if (self.x <= 0 || self.x >= 2048) {
self.direction *= -1;
}
// Create flame and smoke effects
self.effectTimer++;
if (self.effectTimer >= 5) {
self.effectTimer = 0;
// Create flame particles
if (Math.random() < 0.03) {
var flameParticle = game.attachAsset('flame', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + 35 + (Math.random() - 0.5) * 30,
y: self.y - 50 + (Math.random() - 0.5) * 20,
scaleX: 0.5 + Math.random() * 0.3,
scaleY: 0.5 + Math.random() * 0.3,
alpha: 0.8 + Math.random() * 0.2
});
// Animate flame trailing behind projectile
tween(flameParticle, {
y: flameParticle.y + 40 + Math.random() * 30,
x: flameParticle.x + (Math.random() - 0.5) * 20,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
flameParticle.destroy();
}
});
}
// Create smoke particles
if (Math.random() < 0.02) {
var smokeParticle = game.attachAsset('smoke', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + (Math.random() - 0.5) * 25,
y: self.y + (Math.random() - 0.5) * 15,
scaleX: 0.3 + Math.random() * 0.2,
scaleY: 0.3 + Math.random() * 0.2,
alpha: 0.6
});
// Animate smoke trailing and dispersing
tween(smokeParticle, {
y: smokeParticle.y + 50 + Math.random() * 25,
x: smokeParticle.x + (Math.random() - 0.5) * 15,
alpha: 0,
scaleX: 0.8 + Math.random() * 0.4,
scaleY: 0.8 + Math.random() * 0.4
}, {
duration: 400 + Math.random() * 300,
easing: tween.easeOut,
onFinish: function onFinish() {
smokeParticle.destroy();
}
});
}
// Create additional particle effects
if (Math.random() < 0.01) {
var projectileParticle = new Particle();
projectileParticle.x = self.x + (Math.random() - 0.5) * 40;
projectileParticle.y = self.y - 10 + (Math.random() - 0.5) * 15;
projectileParticle.velocityX = (Math.random() - 0.5) * 6;
projectileParticle.velocityY = -Math.random() * 4 - 2;
particles.push(projectileParticle);
game.addChild(projectileParticle);
}
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
// Choose random particle color/type
var particleTypes = ['redParticle', 'orangeParticle', 'yellowParticle', 'whiteParticle', 'smoke'];
var randomType = particleTypes[Math.floor(Math.random() * particleTypes.length)];
var particleBody = self.attachAsset(randomType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + Math.random() * 0.6,
scaleY: 0.3 + Math.random() * 0.6
});
// Random particle properties
self.velocityX = (Math.random() - 0.5) * 8;
self.velocityY = -Math.random() * 6 - 2;
self.gravity = 0.2;
self.life = 90 + Math.random() * 60; // 1.5-2.5 seconds at 60fps
self.maxLife = self.life;
self.update = function () {
// Apply physics
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
// Fade out over time
self.life--;
self.alpha = self.life / self.maxLife;
// Remove when life is over or off screen
if (self.life <= 0 || self.y > 2732 + 100 || self.y < -100 || self.x < -100 || self.x > 2148) {
self.destroy();
// Remove from particles array
for (var p = particles.length - 1; p >= 0; p--) {
if (particles[p] === self) {
particles.splice(p, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8B4513
});
/****
* Game Code
****/
var backgroundImage = game.attachAsset('background', {
x: 0,
y: 0,
width: 2048,
height: 2732
});
// Add new background layer
var backgroundLayer = game.attachAsset('backgroundLayer', {
x: 0,
y: 0,
width: 2100,
height: 700
});
// Create ground terrain
var ground = game.attachAsset('ground', {
x: 0,
y: 2682,
// Position at bottom (2732 - 50 = 2682)
width: 2048,
height: 50
});
// Create and position player on the ground
var player = game.attachAsset('player', {
x: 2048 / 2,
// Center horizontally
y: 2682,
// Position touching the ground
scaleX: 1.0,
scaleY: 1.0,
anchorX: 0.5,
anchorY: 1.0
});
// Scale player to 3.0 using tween animation
tween(player, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 500,
easing: tween.easeOut
});
// Create white eyes on the player
var leftEye = game.attachAsset('eyeWhite', {
x: player.x - 50,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
var rightEye = game.attachAsset('eyeWhite', {
x: player.x + 10,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
// Create black pupils inside the white eyes
var leftPupil = game.attachAsset('eye', {
x: player.x - 50,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
var rightPupil = game.attachAsset('eye', {
x: player.x + 10,
y: player.y - 245,
anchorX: 0.5,
anchorY: 0.5
});
// Create player feet
var leftFoot = game.attachAsset('playerLeftFoot', {
x: player.x - 80,
y: player.y + 10,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
var rightFoot = game.attachAsset('playerRightFoot', {
x: player.x + 80,
y: player.y + 10,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
// Create player right hand
var playerRightHand = game.attachAsset('playerRightHand', {
x: player.x + 120,
y: player.y - 120,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Variables for player movement
var playerSpeed = 12;
var targetX = player.x;
var isMoving = false;
var playerLives = 3;
var isShielded = false;
var shieldTimer = 0;
var shieldDuration = 240; // 4 seconds at 60fps
var isFlying = false; // Track if player is currently flying after being hit
var shieldBubble = null; // Shield bubble visual element
var walkAnimationTimer = 0; // Timer for walking animation
// Game speed system
var gameSpeedMultiplier = 1.0;
var speedIncreaseTimer = 0;
var speedIncreaseInterval = 600; // Increase speed every 10 seconds at 60fps
// Create life hearts on the left side
var lifeHearts = [];
for (var h = 0; h < 3; h++) {
// Create heart using image asset
var heartImage = game.attachAsset('lifeHeartImage', {
x: 150 + h * 100,
y: 750,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
});
lifeHearts.push(heartImage);
}
// Create score display
var scoreText = new Text2('PUAN: 0', {
size: 120,
fill: 0xFFFFFF,
font: "'Montserrat', 'Open Sans', 'Lato', 'Source Sans Pro', 'Ubuntu', sans-serif"
});
scoreText.anchor.set(0.5, 0);
scoreText.x = 2048 / 2;
scoreText.y = 850;
game.addChild(scoreText);
// Enemy and projectile tracking
var enemy = null;
var enemyProjectiles = [];
var enemyProjectiles2 = [];
var particles = [];
// Create enemy at top of screen
enemy = game.addChild(new Enemy());
enemy.x = 1124; // Move slightly to the right
enemy.y = 450; // Moved up from previous position
// Touch/mouse controls for player movement
game.down = function (x, y, obj) {
// Set target position to touch/click location
targetX = x;
isMoving = true;
// Keep player within screen bounds
if (targetX < 0) targetX = 0;
if (targetX > 2048) targetX = 2048;
};
// Update player movement
game.update = function () {
// Update game speed system
speedIncreaseTimer++;
if (speedIncreaseTimer >= speedIncreaseInterval) {
speedIncreaseTimer = 0;
gameSpeedMultiplier += 0.1; // Increase speed by 10% every 10 seconds
// Cap maximum speed at 3x original speed
if (gameSpeedMultiplier > 3.0) {
gameSpeedMultiplier = 3.0;
}
}
if (isMoving) {
// Calculate distance to target
var distance = targetX - player.x;
// Move towards target
if (Math.abs(distance) > 5) {
if (distance > 0) {
player.x += playerSpeed;
} else {
player.x -= playerSpeed;
}
// Animate walking - move feet up and down alternately
walkAnimationTimer++;
if (walkAnimationTimer >= 10) {
walkAnimationTimer = 0;
// Animate left foot up and down
tween(leftFoot, {
y: leftFoot.y === player.y + 10 ? player.y - 10 : player.y + 10
}, {
duration: 150,
easing: tween.easeInOut
});
// Animate right foot up and down (opposite to left foot)
tween(rightFoot, {
y: rightFoot.y === player.y + 10 ? player.y - 10 : player.y + 10
}, {
duration: 150,
easing: tween.easeInOut
});
}
} else {
// Snap to target when close enough
player.x = targetX;
isMoving = false;
}
// Keep player within bounds
if (player.x < 0) player.x = 0;
if (player.x > 2048) player.x = 2048;
}
// Update feet positions to follow player
leftFoot.x = player.x - 80;
rightFoot.x = player.x + 80;
// Update right hand position to follow player
playerRightHand.x = player.x + 120;
playerRightHand.y = player.y - 120;
// Reset feet position when not moving
if (!isMoving) {
leftFoot.y = player.y + 10;
rightFoot.y = player.y + 10;
}
// Update shield system
if (isShielded) {
shieldTimer--;
// Hide player right hand when shield is active
playerRightHand.visible = false;
// Show shield bubble if not already visible
if (!shieldBubble) {
shieldBubble = game.attachAsset('shieldBubble', {
x: player.x,
y: player.y - 150,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1,
tint: 0xffffff
});
// Add pulsing animation to shield bubble
tween(shieldBubble, {
scaleX: 1.1,
scaleY: 1.1,
alpha: 0.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (shieldBubble) {
tween(shieldBubble, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.05
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
// Update shield bubble position to follow player
if (shieldBubble) {
shieldBubble.x = player.x;
shieldBubble.y = player.y - 150;
}
// Flash player blue to indicate shield
if (shieldTimer % 20 < 10) {
player.tint = 0x00ffff;
} else {
player.tint = 0xffffff;
}
// Deactivate shield when timer expires
if (shieldTimer <= 0) {
isShielded = false;
player.tint = 0xffffff;
// Show player right hand when shield is deactivated
playerRightHand.visible = true;
// Stop pupil spinning and reset rotation
tween.stop(leftPupil, {
rotation: true
});
tween.stop(rightPupil, {
rotation: true
});
leftPupil.rotation = 0;
rightPupil.rotation = 0;
// Remove shield bubble
if (shieldBubble) {
shieldBubble.destroy();
shieldBubble = null;
}
// Clear shield sound interval
if (game.shieldSoundInterval) {
LK.clearInterval(game.shieldSoundInterval);
game.shieldSoundInterval = null;
}
}
}
// Update eye positions to follow player
leftEye.x = player.x - 50;
leftEye.y = player.y - 245;
rightEye.x = player.x + 10;
rightEye.y = player.y - 245;
// Keep pupils centered when not shielded, but track falling projectiles
if (!isShielded) {
// Default pupil positions (eye centers)
var leftEyeCenterX = player.x - 50;
var leftEyeCenterY = player.y - 245;
var rightEyeCenterX = player.x + 10;
var rightEyeCenterY = player.y - 245;
// Maximum distance pupils can move from center
var maxPupilDistance = 8;
// Find closest falling projectile to track
var closestProjectile = null;
var closestDistance = Infinity;
// Check all enemy projectiles
for (var p = 0; p < enemyProjectiles.length; p++) {
var proj = enemyProjectiles[p];
var distance = Math.sqrt(Math.pow(proj.x - player.x, 2) + Math.pow(proj.y - player.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
closestProjectile = proj;
}
}
// Check all enemy projectiles type 2
for (var p2 = 0; p2 < enemyProjectiles2.length; p2++) {
var proj2 = enemyProjectiles2[p2];
var distance2 = Math.sqrt(Math.pow(proj2.x - player.x, 2) + Math.pow(proj2.y - player.y, 2));
if (distance2 < closestDistance) {
closestDistance = distance2;
closestProjectile = proj2;
}
}
if (closestProjectile) {
// Calculate angles from eye centers to the closest projectile
var leftAngle = Math.atan2(closestProjectile.y - leftEyeCenterY, closestProjectile.x - leftEyeCenterX);
var rightAngle = Math.atan2(closestProjectile.y - rightEyeCenterY, closestProjectile.x - rightEyeCenterX);
// Position pupils within eye boundaries, tracking the projectile
leftPupil.x = leftEyeCenterX + Math.cos(leftAngle) * maxPupilDistance;
leftPupil.y = leftEyeCenterY + Math.sin(leftAngle) * maxPupilDistance;
rightPupil.x = rightEyeCenterX + Math.cos(rightAngle) * maxPupilDistance;
rightPupil.y = rightEyeCenterY + Math.sin(rightAngle) * maxPupilDistance;
} else {
// No projectiles to track, center pupils
leftPupil.x = leftEyeCenterX;
leftPupil.y = leftEyeCenterY;
rightPupil.x = rightEyeCenterX;
rightPupil.y = rightEyeCenterY;
}
} else {
// When shield is active, make pupils move diagonally (down-left and diagonally)
var leftEyeCenterX = player.x - 50;
var leftEyeCenterY = player.y - 245;
var rightEyeCenterX = player.x + 10;
var rightEyeCenterY = player.y - 245;
var maxBounceDistance = 12;
// Use diagonal movement patterns based on time
var bounceTime = LK.ticks * 0.12;
// Create diagonal movements: down-left and cross-diagonal
var bounceX = Math.sin(bounceTime) * maxBounceDistance;
var bounceY = Math.cos(bounceTime * 0.8) * (maxBounceDistance * 0.7);
// Position pupils with diagonal movement patterns
leftPupil.x = leftEyeCenterX + bounceX;
leftPupil.y = leftEyeCenterY + bounceY;
rightPupil.x = rightEyeCenterX - bounceX; // Opposite X movement for variety
rightPupil.y = rightEyeCenterY + bounceY;
}
// Update enemy projectiles
for (var i = enemyProjectiles.length - 1; i >= 0; i--) {
var projectile = enemyProjectiles[i];
// Remove projectiles that go off screen (any direction)
if (projectile.y > 2732 + 100 || projectile.y < -100 || projectile.x < -100 || projectile.x > 2148) {
projectile.destroy();
enemyProjectiles.splice(i, 1);
continue;
}
// Check collision with player
if (projectile.intersects(player)) {
// Check if player is protected by shield
if (!isShielded) {
// Award points when hit by enemy projectile
LK.setScore(LK.getScore() + 10);
// Update score display
scoreText.setText('PUAN: ' + LK.getScore());
// Play sound effect for first projectile hit
LK.getSound('enemyProjectileHit').play();
// Create reduced particle burst effect at player's mouth area
for (var particleCount = 0; particleCount < 1; particleCount++) {
var particle = new Particle();
particle.x = player.x + (Math.random() - 0.5) * 40;
particle.y = player.y - 130 + (Math.random() - 0.5) * 30; // Position higher up from mouth area
// Reduce particle velocity for better performance
particle.velocityX = (Math.random() - 0.5) * 10;
particle.velocityY = -Math.random() * 8 - 3;
particles.push(particle);
game.addChild(particle);
}
// Simplified mouth trembling effect
tween(player, {
scaleX: 3.2,
scaleY: 2.8
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 150,
easing: tween.easeOut
});
}
});
}
projectile.destroy();
enemyProjectiles.splice(i, 1);
}
}
// Update enemy projectiles type 2
for (var j = enemyProjectiles2.length - 1; j >= 0; j--) {
var projectile2 = enemyProjectiles2[j];
// Remove projectiles that go off screen (any direction)
if (projectile2.y > 2732 + 100 || projectile2.y < -100 || projectile2.x < -100 || projectile2.x > 2148) {
projectile2.destroy();
enemyProjectiles2.splice(j, 1);
continue;
}
// Check collision with player
if (projectile2.intersects(player) && !isShielded && !isFlying) {
// Show enemy mouth when hit by second projectile
enemy.showMouth();
// Freeze enemy movement and shooting
enemy.freezeEnemy();
// Play sound effect for second projectile hit
LK.getSound('enemyProjectile2Hit').play();
// Stop current player movement
isMoving = false;
// Set flying state to true
isFlying = true;
// Player flies off screen to the left while spinning
tween(player, {
x: -200,
y: player.y - 800,
rotation: Math.PI * 4
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Return player from right side after flying off
player.x = 2248;
player.y = 2682;
player.rotation = 0;
// Reset flying state
isFlying = false;
// Activate shield protection
isShielded = true;
shieldTimer = shieldDuration;
// Play shield activation sound
LK.getSound('shieldSound').play();
// Set up repeating shield sound during shield duration
var shieldSoundInterval = LK.setInterval(function () {
if (isShielded && shieldTimer > 0) {
LK.getSound('shieldSound').play();
}
}, 1750); // Play every 1750ms
// Store interval reference for cleanup
game.shieldSoundInterval = shieldSoundInterval;
// Shield bubble will be created in the shield update section
// Animate player returning to screen
tween(player, {
x: 2048 / 2
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Update feet positions during flight animation
tween(leftFoot, {
x: -330,
y: player.y - 790
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
leftFoot.x = 2168;
leftFoot.y = 2692;
tween(leftFoot, {
x: 2048 / 2 - 80
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(rightFoot, {
x: -120,
y: player.y - 790
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rightFoot.x = 2128;
rightFoot.y = 2692;
tween(rightFoot, {
x: 2048 / 2 + 80
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Update eye positions during flight animation
tween(leftEye, {
x: -250,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
leftEye.x = 2198;
leftEye.y = 2437;
tween(leftEye, {
x: 2048 / 2 - 50
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(rightEye, {
x: -190,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rightEye.x = 2258;
rightEye.y = 2437;
tween(rightEye, {
x: 2048 / 2 + 10
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(leftPupil, {
x: -250,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
leftPupil.x = 2198;
leftPupil.y = 2437;
tween(leftPupil, {
x: 2048 / 2 - 50
}, {
duration: 800,
easing: tween.easeOut
});
}
});
tween(rightPupil, {
x: -190,
y: player.y - 1045
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rightPupil.x = 2258;
rightPupil.y = 2437;
tween(rightPupil, {
x: 2048 / 2 + 10
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Update right hand position during flight animation
tween(playerRightHand, {
x: -80,
y: player.y - 920
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
playerRightHand.x = 2368;
playerRightHand.y = 2562;
tween(playerRightHand, {
x: 2048 / 2 + 120
}, {
duration: 800,
easing: tween.easeOut
});
}
});
// Create particle burst effect
for (var particleCount = 0; particleCount < 5; particleCount++) {
var particle = new Particle();
particle.x = player.x + (Math.random() - 0.5) * 80;
particle.y = player.y - 50 + (Math.random() - 0.5) * 70;
// Give particles more dramatic velocities for this bigger explosion
particle.velocityX = (Math.random() - 0.5) * 18;
particle.velocityY = -Math.random() * 12 - 4;
particles.push(particle);
game.addChild(particle);
}
// Create smoke and flame effects
for (var smokeCount = 0; smokeCount < 2; smokeCount++) {
var smoke = game.attachAsset('smoke', {
x: player.x + (Math.random() - 0.5) * 200,
y: player.y - 100 + (Math.random() - 0.5) * 100,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
// Animate smoke rising and fading
tween(smoke, {
y: smoke.y - 200 - Math.random() * 100,
alpha: 0,
scaleX: 2 + Math.random(),
scaleY: 2 + Math.random()
}, {
duration: 2000 + Math.random() * 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
smoke.destroy();
}
});
}
for (var flameCount = 0; flameCount < 1; flameCount++) {
var flame = game.attachAsset('flame', {
x: player.x + (Math.random() - 0.5) * 150,
y: player.y - 50 + (Math.random() - 0.5) * 80,
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
// Animate flames flickering and rising
tween(flame, {
y: flame.y - 150 - Math.random() * 50,
alpha: 0,
scaleX: 1.5 + Math.random() * 0.5,
scaleY: 1.5 + Math.random() * 0.5
}, {
duration: 1500 + Math.random() * 500,
easing: tween.easeOut,
onFinish: function onFinish() {
flame.destroy();
}
});
}
// Create fire animation effect on player
tween(player, {
tint: 0xff4500
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(player, {
tint: 0xffffff
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
});
// Reduce player lives
playerLives--;
// Update heart visibility based on remaining lives
for (var heartIndex = 0; heartIndex < lifeHearts.length; heartIndex++) {
if (heartIndex >= playerLives) {
// Hide heart with fade out animation
tween(lifeHearts[heartIndex], {
alpha: 0.2,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
}
}
if (playerLives <= 0) {
// Show game over when no lives left
LK.showGameOver();
}
projectile2.destroy();
enemyProjectiles2.splice(j, 1);
}
}
// Comprehensive cleanup system - remove all off-screen elements every frame
var allChildren = game.children.slice(); // Create a copy to avoid modification during iteration
var screenBuffer = 100; // Buffer zone around visible area
for (var c = 0; c < allChildren.length; c++) {
var child = allChildren[c];
// Skip essential game elements that should always stay
if (child === player || child === enemy || child === backgroundImage || child === backgroundLayer || child === ground || child === scoreText || child === leftEye || child === rightEye || child === leftPupil || child === rightPupil || child === leftFoot || child === rightFoot || child === playerRightHand || child === shieldBubble) {
continue;
}
// Skip life hearts
var isLifeHeart = false;
for (var h = 0; h < lifeHearts.length; h++) {
if (child === lifeHearts[h]) {
isLifeHeart = true;
break;
}
}
if (isLifeHeart) {
continue;
}
// Remove if off screen in any direction with buffer zone
if (child.x < -screenBuffer || child.x > 2048 + screenBuffer || child.y < -screenBuffer || child.y > 2732 + screenBuffer) {
child.destroy();
}
}
// Clean up particle arrays - remove destroyed particles
for (var p = particles.length - 1; p >= 0; p--) {
if (!particles[p] || particles[p].destroyed) {
particles.splice(p, 1);
}
}
// Clean up projectile arrays - ensure no destroyed projectiles remain
for (var ep = enemyProjectiles.length - 1; ep >= 0; ep--) {
if (!enemyProjectiles[ep] || enemyProjectiles[ep].destroyed) {
enemyProjectiles.splice(ep, 1);
}
}
for (var ep2 = enemyProjectiles2.length - 1; ep2 >= 0; ep2--) {
if (!enemyProjectiles2[ep2] || enemyProjectiles2[ep2].destroyed) {
enemyProjectiles2.splice(ep2, 1);
}
}
};
3d köstebek. In-Game asset. 2d. High contrast. No shadows
Elinde havuç olan kızgın bir çiftçi. Karakter ayakkabısı siyah
Havuç. In-Game asset. 2d. High contrast. No shadows
Bomba. In-Game asset. 2d. High contrast. No shadows
Alev. In-Game asset. 2d. High contrast. No shadows
Agız. In-Game asset. 2d. High contrast. No shadows
Kalp 3d. In-Game asset. 2d. High contrast. No shadows