/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0,
tint: 0xFFD700,
// Gold tint for boss
scaleX: 2.0,
// Much larger size
scaleY: 2.0
});
self.health = 300; // Very high health
self.maxHealth = 300;
self.damage = 50; // High damage
self.moveSpeed = -1.5; // Moderate speed
self.lastDamageTime = 0;
self.attackTimer = 0;
self.specialAttackTimer = 0;
self.phase = 1; // Boss phase (1 or 2)
self.phaseChanged = false; // Track if phase has changed
self.isDying = false; // Track if boss is in death animation
self.takeDamage = function (damage) {
if (self.isDying) return; // Don't take damage during death animation
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
// Phase 2 transition when health drops to 50%
if (self.health <= self.maxHealth / 2 && self.phase === 1) {
self.phase = 2;
self.phaseChanged = true;
// Flash red to indicate phase change
LK.effects.flashObject(self, 0xFF0000, 1000);
// Increase speed and aggression
self.moveSpeed = -2.5;
self.damage = 70;
// Scale up for phase 2
tween(self, {
scaleX: 2.3,
scaleY: 2.3,
tint: 0xFF4500
}, {
duration: 800,
easing: tween.easeOut
});
LK.getSound('special').play(); // Phase change sound
}
if (self.health <= 0) {
self.isDying = true;
self.health = 0;
// Death animation
tween(self, {
alpha: 0,
rotation: Math.PI * 2,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 500); // Huge points for boss
scoreTxt.setText(LK.getScore());
levelComplete = true;
LK.showYouWin(); // Player wins when boss is defeated
}
});
LK.getSound('hit').play(); // Death sound
}
};
self.update = function () {
if (self.isDying) return; // Don't update during death animation
if (player) {
// More intelligent movement
var moveX = player.x - self.x;
var moveY = player.y - self.y;
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
// Boss attacks based on phase
self.attackTimer++;
self.specialAttackTimer++;
// Phase 1: Only punch attacks when close
if (self.phase === 1) {
if (distance <= 150 && self.attackTimer >= 120) {
self.attackTimer = 0;
player.takeDamage(self.damage);
// Flash boss to show punch attack
LK.effects.flashObject(self, 0xFFD700, 300);
// Punch animation
tween(enemyGraphics, {
x: 20
}, {
duration: 100,
easing: tween.easeOut
});
tween(enemyGraphics, {
x: 0
}, {
duration: 100,
easing: tween.easeIn
});
LK.getSound('hit').play(); // Punch sound
}
}
// Phase 2: Punch attacks + kick attacks
else if (self.phase === 2) {
if (distance <= 150 && self.attackTimer >= 90) {
self.attackTimer = 0;
player.takeDamage(self.damage);
// Alternating punch and kick attacks
if (Math.random() < 0.5) {
// Punch attack
LK.effects.flashObject(self, 0xFF4500, 300);
tween(enemyGraphics, {
x: 25
}, {
duration: 80,
easing: tween.easeOut
});
tween(enemyGraphics, {
x: 0
}, {
duration: 80,
easing: tween.easeIn
});
LK.getSound('hit').play(); // Punch sound
} else {
// Kick attack
LK.effects.flashObject(self, 0xFF0000, 400);
tween(enemyGraphics, {
rotation: 0.3,
y: enemyGraphics.y - 30
}, {
duration: 120,
easing: tween.easeOut
});
tween(enemyGraphics, {
rotation: 0,
y: enemyGraphics.y
}, {
duration: 120,
easing: tween.easeIn
});
LK.getSound('slash').play(); // Kick sound
}
}
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0
});
self.health = 50;
self.damage = 20;
self.moveSpeed = -2;
self.lastDamageTime = 0;
self.attackCooldown = 0;
self.attackRange = 100;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Spawn a new enemy when this one is defeated
spawnEnemy();
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (player) {
// Simple movement towards player
var moveX = player.x - self.x;
var moveY = player.y - self.y;
// Normalize movement speed
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
// Attack if close enough and cooldown is ready
if (distance <= 150 && self.attackTimer <= 0) {
self.attackTimer = 120; // 2 second cooldown for close attacks
player.takeDamage(self.damage);
// Flash boss to show attack
LK.effects.flashObject(self, 0xFF0000, 300);
LK.getSound('hit').play(); // Enemy attack sound
}
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
} else {
self.x += self.moveSpeed;
}
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0,
tint: 0x00FF00 // Green tint to distinguish from regular enemy
});
self.health = 25; // Lower health
self.damage = 15; // Lower damage
self.moveSpeed = -4; // Faster movement
self.lastDamageTime = 0;
self.attackCooldown = 0;
self.attackRange = 80;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 15); // More points for faster enemy
scoreTxt.setText(LK.getScore());
// Spawn a new enemy when this one is defeated
spawnEnemy();
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (player) {
// Simple movement towards player
var moveX = player.x - self.x;
var moveY = player.y - self.y;
// Normalize movement speed
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
// Attack if close enough and cooldown is ready
if (distance <= self.attackRange && self.attackCooldown <= 0) {
self.attackCooldown = 60; // 1 second cooldown (faster than regular enemy)
player.takeDamage(self.damage);
// Flash enemy to show attack
LK.effects.flashObject(self, 0x00FF00, 200);
LK.getSound('slash').play(); // FastEnemy attack sound
}
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
} else {
self.x += self.moveSpeed;
}
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
return self;
});
var HealthPickup = Container.expand(function () {
var self = Container.call(this);
self.isLarge = false;
self.healAmount = 0;
self.init = function (isLarge) {
self.isLarge = isLarge;
if (isLarge) {
var graphics = self.attachAsset('largeHealthPickup', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 100; // Full heal
} else {
var graphics = self.attachAsset('smallHealthPickup', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 50; // Half heal
}
};
self.update = function () {
// Simple floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
};
return self;
});
var HeavyAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('heavyAttack', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 50;
self.lifetime = 25; // Slightly longer than quick attack
self.hasHit = false;
self.update = function () {
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.health = 100;
self.maxHealth = 100;
self.moveSpeed = 3;
self.isAttacking = false;
self.attackCooldown = 0;
self.specialCooldown = 0;
self.maxSpecialCooldown = 180; // 3 seconds at 60fps
// Energy system for sword attacks
self.maxEnergy = 100;
self.energy = 100;
self.energyCostPerAttack = 25; // Cost 25 energy per sword attack
self.energyRechargeDelay = 180; // 3 seconds before recharge starts
self.energyRechargeTimer = 0; // Timer for recharge delay
self.energyRechargeRate = 2; // Energy points per frame when recharging
// Combo system properties
self.punchCombo = 0; // 0, 1, 2 for combo stages
self.kickCombo = 0; // 0, 1, 2 for combo stages
self.punchComboTimer = 0; // Reset timer for punch combo
self.kickComboTimer = 0; // Reset timer for kick combo
self.comboResetTime = 60; // 1 second at 60fps
// Jump and gravity properties
self.velocityY = 0;
self.gravity = 0.8;
self.jumpPower = -18;
self.groundY = 1800; // Lower ground level
self.isOnGround = true;
// Smooth movement properties
self.velocityX = 0;
self.acceleration = 0.5;
self.deceleration = 0.8;
self.maxSpeed = 5;
self.runMultiplier = 1.8;
self.isRunning = false;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
} else {
LK.effects.flashObject(self, 0xFF0000, 300);
}
};
self.quickAttack = function () {
if (self.attackCooldown > 0 || self.isAttacking) return;
self.isAttacking = true;
// Reset combo timer
self.punchComboTimer = self.comboResetTime;
var attack;
var animationDuration;
var cooldownTime;
// Execute combo based on current stage
if (self.punchCombo === 0) {
// First hit - quick punch
self.attackCooldown = 15; // Quick cooldown
animationDuration = 150;
// Quick punch animation
tween(playerGraphics, {
x: 10
}, {
duration: 75,
easing: tween.easeOut
});
tween(playerGraphics, {
x: 0
}, {
duration: 75,
easing: tween.easeIn
});
attack = new QuickAttack();
LK.getSound('hit').play();
} else if (self.punchCombo === 1) {
// Second hit - spinning punch
self.attackCooldown = 25; // Medium cooldown
animationDuration = 300;
// Spinning animation
tween(playerGraphics, {
rotation: Math.PI * 2,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeIn
});
attack = new SpinAttack();
LK.getSound('hit').play();
} else {
// Third hit - heavy punch
self.attackCooldown = 40; // Long cooldown
animationDuration = 500;
// Heavy punch animation
tween(playerGraphics, {
x: 25,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut
});
tween(playerGraphics, {
x: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
attack = new HeavyAttack();
LK.getSound('hit').play();
}
// Find closest enemy to determine attack direction
var closestEnemy = null;
var closestDistance = Infinity;
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));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
if (closestEnemy) {
if (closestEnemy.x < self.x) {
// Enemy is to the left, attack left
attack.x = self.x - 60;
} else {
// Enemy is to the right, attack right
attack.x = self.x + 60;
}
} else {
attack.x = self.x + 60;
}
attack.y = self.y - 40;
attacks.push(attack);
game.addChild(attack);
// Advance combo
self.punchCombo = (self.punchCombo + 1) % 3;
LK.setTimeout(function () {
self.isAttacking = false;
}, animationDuration);
};
self.heavyAttack = function () {
if (self.attackCooldown > 0 || self.isAttacking) return;
self.isAttacking = true;
// Reset combo timer
self.kickComboTimer = self.comboResetTime;
var attack;
var animationDuration;
var cooldownTime;
// Execute combo based on current stage
if (self.kickCombo === 0) {
// First hit - quick kick
self.attackCooldown = 20; // Quick cooldown
animationDuration = 200;
// Quick kick animation
tween(playerGraphics, {
rotation: 0.1
}, {
duration: 100,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0
}, {
duration: 100,
easing: tween.easeIn
});
attack = new QuickAttack();
LK.getSound('hit').play();
} else if (self.kickCombo === 1) {
// Second hit - spinning kick
self.attackCooldown = 35; // Medium cooldown
animationDuration = 400;
// Spinning kick animation
tween(playerGraphics, {
rotation: Math.PI * 2,
y: playerGraphics.y - 20
}, {
duration: 250,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0,
y: playerGraphics.y
}, {
duration: 150,
easing: tween.easeIn
});
attack = new SpinAttack();
LK.getSound('hit').play();
} else {
// Third hit - heavy kick
self.attackCooldown = 50; // Long cooldown
animationDuration = 600;
// Heavy kick animation
tween(playerGraphics, {
rotation: 0.3,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 350,
easing: tween.easeIn
});
attack = new HeavyAttack();
LK.getSound('hit').play();
}
// Find closest enemy to determine attack direction
var closestEnemy = null;
var closestDistance = Infinity;
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));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
if (closestEnemy) {
if (closestEnemy.x < self.x) {
// Enemy is to the left, attack left
attack.x = self.x - 75;
} else {
// Enemy is to the right, attack right
attack.x = self.x + 75;
}
} else {
attack.x = self.x + 75;
}
attack.y = self.y - 50;
attacks.push(attack);
game.addChild(attack);
// Advance combo
self.kickCombo = (self.kickCombo + 1) % 3;
LK.setTimeout(function () {
self.isAttacking = false;
}, animationDuration);
};
self.specialAttack = function () {
if (self.specialCooldown > 0 || self.isAttacking || self.energy < self.energyCostPerAttack) return;
self.isAttacking = true;
self.specialCooldown = self.maxSpecialCooldown;
// Consume energy for sword attack
self.energy -= self.energyCostPerAttack;
self.energyRechargeTimer = self.energyRechargeDelay; // Reset recharge timer
// Sword swing animation - dramatic sweep
tween(playerGraphics, {
rotation: -0.5,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0.5
}, {
duration: 200,
easing: tween.easeInOut
});
tween(playerGraphics, {
rotation: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
var attack = new SpecialAttack();
// Find closest enemy to determine attack direction
var closestEnemy = null;
var closestDistance = Infinity;
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));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
if (closestEnemy) {
if (closestEnemy.x < self.x) {
// Enemy is to the left, attack left
attack.x = self.x - 100;
attack.flipLeft = true;
} else {
// Enemy is to the right, attack right
attack.x = self.x + 100;
attack.flipLeft = false;
}
} else {
attack.x = self.x + 100;
attack.flipLeft = false;
}
attack.y = self.y - 60;
attacks.push(attack);
game.addChild(attack);
LK.getSound('slash').play();
LK.setTimeout(function () {
self.isAttacking = false;
}, 600);
};
self.moveUp = function () {
self.y -= self.moveSpeed * 3;
if (self.y < 120) self.y = 120;
};
self.moveDown = function () {
self.y += self.moveSpeed * 3;
if (self.y > 2612) self.y = 2612;
};
self.moveLeft = function () {
self.x -= self.moveSpeed * 3;
if (self.x < 40) self.x = 40;
};
self.moveRight = function () {
self.x += self.moveSpeed * 3;
if (self.x > 2008) self.x = 2008;
};
self.jump = function () {
if (self.isOnGround) {
self.velocityY = self.jumpPower;
self.isOnGround = false;
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.specialCooldown > 0) {
self.specialCooldown--;
}
// Update energy system
if (self.energyRechargeTimer > 0) {
self.energyRechargeTimer--;
} else if (self.energy < self.maxEnergy) {
// Recharge energy after delay
self.energy += self.energyRechargeRate;
if (self.energy > self.maxEnergy) {
self.energy = self.maxEnergy;
}
}
// Update combo timers
if (self.punchComboTimer > 0) {
self.punchComboTimer--;
if (self.punchComboTimer === 0) {
self.punchCombo = 0; // Reset punch combo
}
}
if (self.kickComboTimer > 0) {
self.kickComboTimer--;
if (self.kickComboTimer === 0) {
self.kickCombo = 0; // Reset kick combo
}
}
// Apply gravity with more realistic physics
self.velocityY += self.gravity;
self.y += self.velocityY;
// Check ground collision
if (self.y >= self.groundY) {
self.y = self.groundY;
self.velocityY = 0;
self.isOnGround = true;
}
// Horizontal movement with acceleration/deceleration
var targetSpeed = 0;
self.isRunning = false;
if (isLeftPressed) {
targetSpeed = -self.maxSpeed;
if (leftPressTime > 30) {
// Running after half second
self.isRunning = true;
targetSpeed *= self.runMultiplier;
}
}
if (isRightPressed) {
targetSpeed = self.maxSpeed;
if (rightPressTime > 30) {
// Running after half second
self.isRunning = true;
targetSpeed *= self.runMultiplier;
}
}
// Apply acceleration or deceleration
if (targetSpeed !== 0) {
// Accelerate towards target speed
if (self.velocityX < targetSpeed) {
self.velocityX += self.acceleration;
if (self.velocityX > targetSpeed) self.velocityX = targetSpeed;
} else if (self.velocityX > targetSpeed) {
self.velocityX -= self.acceleration;
if (self.velocityX < targetSpeed) self.velocityX = targetSpeed;
}
} else {
// Decelerate to stop
if (self.velocityX > 0) {
self.velocityX -= self.deceleration;
if (self.velocityX < 0) self.velocityX = 0;
} else if (self.velocityX < 0) {
self.velocityX += self.deceleration;
if (self.velocityX > 0) self.velocityX = 0;
}
}
// Apply horizontal movement
self.x += self.velocityX;
// Boundary checks
if (self.x < 40) {
self.x = 40;
self.velocityX = 0;
}
if (self.x > 2008) {
self.x = 2008;
self.velocityX = 0;
}
// Jump when up is pressed
if (isUpPressed) {
self.jump();
}
};
return self;
});
var QuickAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('quickAttack', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 25;
self.lifetime = 15; // Quarter second at 60fps
self.hasHit = false;
self.update = function () {
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var SpecialAttack = Container.expand(function () {
var self = Container.call(this);
self.attackGraphics = null;
self.flipLeft = false;
self.damage = 100;
self.lifetime = 35; // Longest lasting attack
self.hasHit = false;
self.setDirection = function (flipLeft) {
if (self.attackGraphics) {
self.removeChild(self.attackGraphics);
}
if (flipLeft) {
self.attackGraphics = self.attachAsset('specialAttack', {
anchorX: 0.5,
anchorY: 0.5,
flipX: 0
});
} else {
self.attackGraphics = self.attachAsset('specialAttack', {
anchorX: 0.5,
anchorY: 0.5,
flipX: 1
});
}
};
// Initialize with default direction (right)
self.setDirection(false);
self.update = function () {
// Update direction if flipLeft property changes
if (self.flipLeft !== undefined && self.attackGraphics) {
self.setDirection(self.flipLeft);
self.flipLeft = undefined; // Reset to avoid repeated updates
}
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var SpinAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('specialAttack', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00FFFF // Cyan tint for spin attack
});
self.damage = 35; // Medium damage
self.lifetime = 20; // Medium duration
self.hasHit = false;
self.update = function () {
self.lifetime--;
// Spinning animation
attackGraphics.rotation += 0.3;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var TankEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0,
tint: 0xFF0000,
// Red tint to distinguish from regular enemy
scaleX: 1.3,
// Larger size
scaleY: 1.3
});
self.health = 100; // Higher health
self.damage = 35; // Higher damage
self.moveSpeed = -1; // Slower movement
self.lastDamageTime = 0;
self.attackCooldown = 0;
self.attackRange = 120;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 25); // More points for tough enemy
scoreTxt.setText(LK.getScore());
// Spawn a new enemy when this one is defeated
spawnEnemy();
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (player) {
// Simple movement towards player
var moveX = player.x - self.x;
var moveY = player.y - self.y;
// Normalize movement speed
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
// Attack if close enough and cooldown is ready
if (distance <= self.attackRange && self.attackCooldown <= 0) {
self.attackCooldown = 120; // 2 second cooldown (slower but more powerful)
player.takeDamage(self.damage);
// Flash enemy to show attack
LK.effects.flashObject(self, 0xFF0000, 300);
LK.getSound('special').play(); // TankEnemy heavy attack sound
}
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
} else {
self.x += self.moveSpeed;
}
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F4F
});
/****
* Game Code
****/
// Create background - only bottom background covering full screen
var bottomBackground = LK.getAsset('backgroundBottom', {
width: 2048,
height: 2732,
// Full screen height
color: 0x228B22,
shape: 'box',
x: 0,
y: 0
});
game.addChild(bottomBackground);
var player;
var enemies = [];
var attacks = [];
var spawnTimer = 0;
var gameSpeed = 1;
var isUpPressed = false;
var isDownPressed = false;
var isLeftPressed = false;
var isRightPressed = false;
var leftPressTime = 0;
var rightPressTime = 0;
var healthPickups = [];
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -50;
scoreTxt.y = 50;
var healthBarBg = LK.getAsset('healthBarBg', {
width: 300,
height: 30,
color: 0xFF0000,
shape: 'box',
x: 150,
y: 80
});
LK.gui.topLeft.addChild(healthBarBg);
var healthBar = LK.getAsset('healthBar', {
width: 300,
height: 30,
color: 0x00FF00,
shape: 'box',
x: 150,
y: 80
});
LK.gui.topLeft.addChild(healthBar);
var healthTxt = new Text2('Health', {
size: 40,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 150;
healthTxt.y = 50;
LK.gui.topLeft.addChild(healthTxt);
var specialCooldownTxt = new Text2('Special Ready', {
size: 40,
fill: 0x9932CC
});
specialCooldownTxt.anchor.set(0, 0);
specialCooldownTxt.x = 50;
specialCooldownTxt.y = 170;
LK.gui.topLeft.addChild(specialCooldownTxt);
// Energy bar for sword attacks
var energyBarBg = LK.getAsset('healthBarBg', {
width: 300,
height: 25,
color: 0x4B0082,
shape: 'box',
x: 150,
y: 130
});
LK.gui.topLeft.addChild(energyBarBg);
var energyBar = LK.getAsset('healthBar', {
width: 300,
height: 25,
color: 0x9932CC,
shape: 'box',
x: 150,
y: 130
});
LK.gui.topLeft.addChild(energyBar);
var energyTxt = new Text2('Energy', {
size: 35,
fill: 0x9932CC
});
energyTxt.anchor.set(0, 0);
energyTxt.x = 150;
energyTxt.y = 100;
LK.gui.topLeft.addChild(energyTxt);
// Boss health bar (initially hidden)
var bossHealthBarBg = LK.getAsset('healthBarBg', {
width: 600,
height: 40,
color: 0xFF0000,
shape: 'box',
x: 1024,
y: 150
});
bossHealthBarBg.anchor.set(0.5, 0);
bossHealthBarBg.visible = false;
game.addChild(bossHealthBarBg);
var bossHealthBar = LK.getAsset('healthBar', {
width: 600,
height: 40,
color: 0xFFD700,
shape: 'box',
x: 1024,
y: 150
});
bossHealthBar.anchor.set(0.5, 0);
bossHealthBar.visible = false;
game.addChild(bossHealthBar);
var bossHealthTxt = new Text2('BOSS', {
size: 60,
fill: 0xFFD700
});
bossHealthTxt.anchor.set(0.5, 1);
bossHealthTxt.x = 1024;
bossHealthTxt.y = 140;
bossHealthTxt.visible = false;
game.addChild(bossHealthTxt);
// Movement arrow buttons (bottom left)
var upBtn = new Text2('↑', {
size: 200,
fill: 0xFFFFFF
});
upBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(upBtn);
upBtn.x = 120;
upBtn.y = -180;
var downBtn = new Text2('↓', {
size: 200,
fill: 0xFFFFFF
});
downBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(downBtn);
downBtn.x = 120;
downBtn.y = -50;
var leftBtn = new Text2('←', {
size: 200,
fill: 0xFFFFFF
});
leftBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(leftBtn);
leftBtn.x = 50;
leftBtn.y = -115;
var rightBtn = new Text2('→', {
size: 200,
fill: 0xFFFFFF
});
rightBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(rightBtn);
rightBtn.x = 190;
rightBtn.y = -115;
// Attack buttons (bottom right)
var quickBtn = new Text2('Golpe', {
size: 80,
fill: 0xFFD700
});
quickBtn.anchor.set(1, 1);
LK.gui.bottomRight.addChild(quickBtn);
quickBtn.x = -50;
quickBtn.y = -50;
var heavyBtn = new Text2('Patada', {
size: 80,
fill: 0xFF6347
});
heavyBtn.anchor.set(1, 1);
LK.gui.bottomRight.addChild(heavyBtn);
heavyBtn.x = -50;
heavyBtn.y = -150;
var specialBtn = new Text2('Espada', {
size: 80,
fill: 0x9932CC
});
specialBtn.anchor.set(1, 1);
LK.gui.bottomRight.addChild(specialBtn);
specialBtn.x = -50;
specialBtn.y = -250;
// Level progression variables
var levelDistance = 0;
var maxLevelDistance = 1000; // Distance to complete level
var bossSpawned = false;
var levelComplete = false;
// Initialize player
player = game.addChild(new Player());
player.x = 1024; // Center horizontally (2048/2)
player.y = 1800; // Lower position
// Level progress UI
var progressTxt = new Text2('Progress: 0%', {
size: 50,
fill: 0xFFFFFF
});
progressTxt.anchor.set(0.5, 0);
progressTxt.x = 0;
progressTxt.y = 50;
LK.gui.top.addChild(progressTxt);
// Button event handlers
quickBtn.down = function (x, y, obj) {
if (player) {
player.quickAttack();
}
};
heavyBtn.down = function (x, y, obj) {
if (player) {
player.heavyAttack();
}
};
specialBtn.down = function (x, y, obj) {
if (player) {
player.specialAttack();
}
};
// Movement button event handlers - single press movement
upBtn.down = function (x, y, obj) {
isUpPressed = true;
};
downBtn.down = function (x, y, obj) {
if (player) {
var newY = player.y + player.moveSpeed * 10;
if (newY > 2612) newY = 2612;
player.y = newY;
}
};
leftBtn.down = function (x, y, obj) {
isLeftPressed = true;
leftPressTime = 0;
};
rightBtn.down = function (x, y, obj) {
isRightPressed = true;
rightPressTime = 0;
};
// Spawn enemies only
function spawnEnemy() {
// Don't spawn regular enemies if boss is spawned or level is complete
if (bossSpawned || levelComplete) return;
// Randomly choose enemy type
var enemyType = Math.random();
var enemy;
if (enemyType < 0.5) {
enemy = new Enemy(); // Regular enemy - 50% chance
} else if (enemyType < 0.8) {
enemy = new FastEnemy(); // Fast enemy - 30% chance
} else {
enemy = new TankEnemy(); // Tank enemy - 20% chance
}
// Spawn enemy from right side
if (Math.random() < 0.8) {
enemy.x = 2048 + 100;
enemy.y = 1800 - Math.random() * 600 + 300; // Lower area spawn
// Start enemy invisible and fade in gradually
enemy.alpha = 0;
tween(enemy, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeIn
});
enemies.push(enemy);
game.addChild(enemy);
} else {
// Spawn enemy from left side
enemy.x = -100;
enemy.y = 1800 - Math.random() * 600 + 300; // Lower area spawn
enemy.moveSpeed = Math.abs(enemy.moveSpeed); // Make moveSpeed positive for left-to-right movement
// Start enemy invisible and fade in gradually
enemy.alpha = 0;
tween(enemy, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeIn
});
enemies.push(enemy);
game.addChild(enemy);
}
}
// Spawn initial enemies at game start
function spawnInitialEnemies() {
for (var i = 0; i < 2; i++) {
spawnEnemy();
}
}
// Call initial enemy spawn
spawnInitialEnemies();
// Start background music
LK.playMusic('Sonidofondo');
function spawnBoss() {
if (bossSpawned) return;
bossSpawned = true;
var boss = new Boss();
boss.x = 2048 + 200; // Spawn from right side
boss.y = 1800; // Lower position
boss.alpha = 0;
// Dramatic boss entrance
tween(boss, {
alpha: 1,
x: 1800
}, {
duration: 2000,
easing: tween.easeOut
});
// Show boss health bar
bossHealthBarBg.visible = true;
bossHealthBar.visible = true;
bossHealthTxt.visible = true;
enemies.push(boss);
game.addChild(boss);
}
game.update = function () {
// Update press time tracking for running
if (isLeftPressed) leftPressTime++;
if (isRightPressed) rightPressTime++;
// Update game speed over time
gameSpeed += 0.001;
// Update level progression
if (!levelComplete) {
levelDistance += gameSpeed;
var progressPercent = Math.min(100, levelDistance / maxLevelDistance * 100);
progressTxt.setText('Progress: ' + Math.floor(progressPercent) + '%');
// Spawn boss after 2 minutes (7200 ticks at 60fps)
if (LK.ticks >= 7200 && !bossSpawned) {
spawnBoss();
}
}
// Enemy spawning is now handled when enemies are defeated (see Enemy class takeDamage method)
// Update UI
if (player) {
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBar.width = 300 * healthPercent;
healthBar.tint = healthPercent > 0.5 ? 0x00FF00 : healthPercent > 0.25 ? 0xFFFF00 : 0xFF0000;
// Update energy bar
var energyPercent = player.energy / player.maxEnergy;
energyBar.width = 300 * energyPercent;
energyBar.tint = energyPercent > 0.6 ? 0x9932CC : energyPercent > 0.3 ? 0xFFFF00 : 0xFF0000;
if (player.specialCooldown > 0) {
specialCooldownTxt.setText('Special: ' + Math.ceil(player.specialCooldown / 60) + 's');
} else if (player.energy < player.energyCostPerAttack) {
specialCooldownTxt.setText('Low Energy');
} else {
specialCooldownTxt.setText('Special Ready');
}
// Check if health is zero for game over
if (player.health <= 0) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
// Update boss health bar
if (bossSpawned && enemies.length > 0) {
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].maxHealth && enemies[i].maxHealth === 300) {
// Boss detection
var boss = enemies[i];
var bossHealthPercent = boss.health / boss.maxHealth;
bossHealthBar.width = 600 * bossHealthPercent;
bossHealthBar.tint = bossHealthPercent > 0.5 ? 0xFFD700 : bossHealthPercent > 0.25 ? 0xFFFF00 : 0xFF0000;
break;
}
}
}
// Enemy attacks are now handled in their individual update methods
// Spawn health pickups when player health is low
if (player && player.health < 30 && Math.random() < 0.002) {
var isLarge = Math.random() < 0.3; // 30% chance for large pickup
var pickup = new HealthPickup();
pickup.init(isLarge);
pickup.x = 200 + Math.random() * 1600;
pickup.y = 200 + Math.random() * 2200;
healthPickups.push(pickup);
game.addChild(pickup);
}
// Check collisions between player and health pickups
for (var i = healthPickups.length - 1; i >= 0; i--) {
var pickup = healthPickups[i];
if (player && player.intersects(pickup)) {
if (pickup.isLarge) {
player.health = player.maxHealth; // Full heal
} else {
player.health = Math.min(player.maxHealth, player.health + 50); // Half heal
}
pickup.destroy();
healthPickups.splice(i, 1);
}
}
// Check collisions between attacks and enemies
for (var i = attacks.length - 1; i >= 0; i--) {
var attack = attacks[i];
if (attack.hasHit) continue;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (attack.intersects(enemy)) {
enemy.takeDamage(attack.damage);
attack.hasHit = true;
// Special visual effect for sword attacks
if (attack.damage >= 100) {
LK.effects.flashObject(enemy, 0x9932CC, 500);
tween(enemy, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
tween(enemy, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
break;
}
}
}
// Score is only updated when enemies are defeated
};
leftBtn.up = function (x, y, obj) {
isLeftPressed = false;
leftPressTime = 0;
};
rightBtn.up = function (x, y, obj) {
isRightPressed = false;
rightPressTime = 0;
};
upBtn.up = function (x, y, obj) {
isUpPressed = false;
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0,
tint: 0xFFD700,
// Gold tint for boss
scaleX: 2.0,
// Much larger size
scaleY: 2.0
});
self.health = 300; // Very high health
self.maxHealth = 300;
self.damage = 50; // High damage
self.moveSpeed = -1.5; // Moderate speed
self.lastDamageTime = 0;
self.attackTimer = 0;
self.specialAttackTimer = 0;
self.phase = 1; // Boss phase (1 or 2)
self.phaseChanged = false; // Track if phase has changed
self.isDying = false; // Track if boss is in death animation
self.takeDamage = function (damage) {
if (self.isDying) return; // Don't take damage during death animation
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
// Phase 2 transition when health drops to 50%
if (self.health <= self.maxHealth / 2 && self.phase === 1) {
self.phase = 2;
self.phaseChanged = true;
// Flash red to indicate phase change
LK.effects.flashObject(self, 0xFF0000, 1000);
// Increase speed and aggression
self.moveSpeed = -2.5;
self.damage = 70;
// Scale up for phase 2
tween(self, {
scaleX: 2.3,
scaleY: 2.3,
tint: 0xFF4500
}, {
duration: 800,
easing: tween.easeOut
});
LK.getSound('special').play(); // Phase change sound
}
if (self.health <= 0) {
self.isDying = true;
self.health = 0;
// Death animation
tween(self, {
alpha: 0,
rotation: Math.PI * 2,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 500); // Huge points for boss
scoreTxt.setText(LK.getScore());
levelComplete = true;
LK.showYouWin(); // Player wins when boss is defeated
}
});
LK.getSound('hit').play(); // Death sound
}
};
self.update = function () {
if (self.isDying) return; // Don't update during death animation
if (player) {
// More intelligent movement
var moveX = player.x - self.x;
var moveY = player.y - self.y;
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
// Boss attacks based on phase
self.attackTimer++;
self.specialAttackTimer++;
// Phase 1: Only punch attacks when close
if (self.phase === 1) {
if (distance <= 150 && self.attackTimer >= 120) {
self.attackTimer = 0;
player.takeDamage(self.damage);
// Flash boss to show punch attack
LK.effects.flashObject(self, 0xFFD700, 300);
// Punch animation
tween(enemyGraphics, {
x: 20
}, {
duration: 100,
easing: tween.easeOut
});
tween(enemyGraphics, {
x: 0
}, {
duration: 100,
easing: tween.easeIn
});
LK.getSound('hit').play(); // Punch sound
}
}
// Phase 2: Punch attacks + kick attacks
else if (self.phase === 2) {
if (distance <= 150 && self.attackTimer >= 90) {
self.attackTimer = 0;
player.takeDamage(self.damage);
// Alternating punch and kick attacks
if (Math.random() < 0.5) {
// Punch attack
LK.effects.flashObject(self, 0xFF4500, 300);
tween(enemyGraphics, {
x: 25
}, {
duration: 80,
easing: tween.easeOut
});
tween(enemyGraphics, {
x: 0
}, {
duration: 80,
easing: tween.easeIn
});
LK.getSound('hit').play(); // Punch sound
} else {
// Kick attack
LK.effects.flashObject(self, 0xFF0000, 400);
tween(enemyGraphics, {
rotation: 0.3,
y: enemyGraphics.y - 30
}, {
duration: 120,
easing: tween.easeOut
});
tween(enemyGraphics, {
rotation: 0,
y: enemyGraphics.y
}, {
duration: 120,
easing: tween.easeIn
});
LK.getSound('slash').play(); // Kick sound
}
}
}
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0
});
self.health = 50;
self.damage = 20;
self.moveSpeed = -2;
self.lastDamageTime = 0;
self.attackCooldown = 0;
self.attackRange = 100;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Spawn a new enemy when this one is defeated
spawnEnemy();
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (player) {
// Simple movement towards player
var moveX = player.x - self.x;
var moveY = player.y - self.y;
// Normalize movement speed
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
// Attack if close enough and cooldown is ready
if (distance <= 150 && self.attackTimer <= 0) {
self.attackTimer = 120; // 2 second cooldown for close attacks
player.takeDamage(self.damage);
// Flash boss to show attack
LK.effects.flashObject(self, 0xFF0000, 300);
LK.getSound('hit').play(); // Enemy attack sound
}
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
} else {
self.x += self.moveSpeed;
}
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0,
tint: 0x00FF00 // Green tint to distinguish from regular enemy
});
self.health = 25; // Lower health
self.damage = 15; // Lower damage
self.moveSpeed = -4; // Faster movement
self.lastDamageTime = 0;
self.attackCooldown = 0;
self.attackRange = 80;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 15); // More points for faster enemy
scoreTxt.setText(LK.getScore());
// Spawn a new enemy when this one is defeated
spawnEnemy();
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (player) {
// Simple movement towards player
var moveX = player.x - self.x;
var moveY = player.y - self.y;
// Normalize movement speed
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
// Attack if close enough and cooldown is ready
if (distance <= self.attackRange && self.attackCooldown <= 0) {
self.attackCooldown = 60; // 1 second cooldown (faster than regular enemy)
player.takeDamage(self.damage);
// Flash enemy to show attack
LK.effects.flashObject(self, 0x00FF00, 200);
LK.getSound('slash').play(); // FastEnemy attack sound
}
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
} else {
self.x += self.moveSpeed;
}
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
return self;
});
var HealthPickup = Container.expand(function () {
var self = Container.call(this);
self.isLarge = false;
self.healAmount = 0;
self.init = function (isLarge) {
self.isLarge = isLarge;
if (isLarge) {
var graphics = self.attachAsset('largeHealthPickup', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 100; // Full heal
} else {
var graphics = self.attachAsset('smallHealthPickup', {
anchorX: 0.5,
anchorY: 0.5
});
self.healAmount = 50; // Half heal
}
};
self.update = function () {
// Simple floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
};
return self;
});
var HeavyAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('heavyAttack', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 50;
self.lifetime = 25; // Slightly longer than quick attack
self.hasHit = false;
self.update = function () {
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.health = 100;
self.maxHealth = 100;
self.moveSpeed = 3;
self.isAttacking = false;
self.attackCooldown = 0;
self.specialCooldown = 0;
self.maxSpecialCooldown = 180; // 3 seconds at 60fps
// Energy system for sword attacks
self.maxEnergy = 100;
self.energy = 100;
self.energyCostPerAttack = 25; // Cost 25 energy per sword attack
self.energyRechargeDelay = 180; // 3 seconds before recharge starts
self.energyRechargeTimer = 0; // Timer for recharge delay
self.energyRechargeRate = 2; // Energy points per frame when recharging
// Combo system properties
self.punchCombo = 0; // 0, 1, 2 for combo stages
self.kickCombo = 0; // 0, 1, 2 for combo stages
self.punchComboTimer = 0; // Reset timer for punch combo
self.kickComboTimer = 0; // Reset timer for kick combo
self.comboResetTime = 60; // 1 second at 60fps
// Jump and gravity properties
self.velocityY = 0;
self.gravity = 0.8;
self.jumpPower = -18;
self.groundY = 1800; // Lower ground level
self.isOnGround = true;
// Smooth movement properties
self.velocityX = 0;
self.acceleration = 0.5;
self.deceleration = 0.8;
self.maxSpeed = 5;
self.runMultiplier = 1.8;
self.isRunning = false;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
} else {
LK.effects.flashObject(self, 0xFF0000, 300);
}
};
self.quickAttack = function () {
if (self.attackCooldown > 0 || self.isAttacking) return;
self.isAttacking = true;
// Reset combo timer
self.punchComboTimer = self.comboResetTime;
var attack;
var animationDuration;
var cooldownTime;
// Execute combo based on current stage
if (self.punchCombo === 0) {
// First hit - quick punch
self.attackCooldown = 15; // Quick cooldown
animationDuration = 150;
// Quick punch animation
tween(playerGraphics, {
x: 10
}, {
duration: 75,
easing: tween.easeOut
});
tween(playerGraphics, {
x: 0
}, {
duration: 75,
easing: tween.easeIn
});
attack = new QuickAttack();
LK.getSound('hit').play();
} else if (self.punchCombo === 1) {
// Second hit - spinning punch
self.attackCooldown = 25; // Medium cooldown
animationDuration = 300;
// Spinning animation
tween(playerGraphics, {
rotation: Math.PI * 2,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeIn
});
attack = new SpinAttack();
LK.getSound('hit').play();
} else {
// Third hit - heavy punch
self.attackCooldown = 40; // Long cooldown
animationDuration = 500;
// Heavy punch animation
tween(playerGraphics, {
x: 25,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut
});
tween(playerGraphics, {
x: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
attack = new HeavyAttack();
LK.getSound('hit').play();
}
// Find closest enemy to determine attack direction
var closestEnemy = null;
var closestDistance = Infinity;
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));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
if (closestEnemy) {
if (closestEnemy.x < self.x) {
// Enemy is to the left, attack left
attack.x = self.x - 60;
} else {
// Enemy is to the right, attack right
attack.x = self.x + 60;
}
} else {
attack.x = self.x + 60;
}
attack.y = self.y - 40;
attacks.push(attack);
game.addChild(attack);
// Advance combo
self.punchCombo = (self.punchCombo + 1) % 3;
LK.setTimeout(function () {
self.isAttacking = false;
}, animationDuration);
};
self.heavyAttack = function () {
if (self.attackCooldown > 0 || self.isAttacking) return;
self.isAttacking = true;
// Reset combo timer
self.kickComboTimer = self.comboResetTime;
var attack;
var animationDuration;
var cooldownTime;
// Execute combo based on current stage
if (self.kickCombo === 0) {
// First hit - quick kick
self.attackCooldown = 20; // Quick cooldown
animationDuration = 200;
// Quick kick animation
tween(playerGraphics, {
rotation: 0.1
}, {
duration: 100,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0
}, {
duration: 100,
easing: tween.easeIn
});
attack = new QuickAttack();
LK.getSound('hit').play();
} else if (self.kickCombo === 1) {
// Second hit - spinning kick
self.attackCooldown = 35; // Medium cooldown
animationDuration = 400;
// Spinning kick animation
tween(playerGraphics, {
rotation: Math.PI * 2,
y: playerGraphics.y - 20
}, {
duration: 250,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0,
y: playerGraphics.y
}, {
duration: 150,
easing: tween.easeIn
});
attack = new SpinAttack();
LK.getSound('hit').play();
} else {
// Third hit - heavy kick
self.attackCooldown = 50; // Long cooldown
animationDuration = 600;
// Heavy kick animation
tween(playerGraphics, {
rotation: 0.3,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 350,
easing: tween.easeIn
});
attack = new HeavyAttack();
LK.getSound('hit').play();
}
// Find closest enemy to determine attack direction
var closestEnemy = null;
var closestDistance = Infinity;
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));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
if (closestEnemy) {
if (closestEnemy.x < self.x) {
// Enemy is to the left, attack left
attack.x = self.x - 75;
} else {
// Enemy is to the right, attack right
attack.x = self.x + 75;
}
} else {
attack.x = self.x + 75;
}
attack.y = self.y - 50;
attacks.push(attack);
game.addChild(attack);
// Advance combo
self.kickCombo = (self.kickCombo + 1) % 3;
LK.setTimeout(function () {
self.isAttacking = false;
}, animationDuration);
};
self.specialAttack = function () {
if (self.specialCooldown > 0 || self.isAttacking || self.energy < self.energyCostPerAttack) return;
self.isAttacking = true;
self.specialCooldown = self.maxSpecialCooldown;
// Consume energy for sword attack
self.energy -= self.energyCostPerAttack;
self.energyRechargeTimer = self.energyRechargeDelay; // Reset recharge timer
// Sword swing animation - dramatic sweep
tween(playerGraphics, {
rotation: -0.5,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
tween(playerGraphics, {
rotation: 0.5
}, {
duration: 200,
easing: tween.easeInOut
});
tween(playerGraphics, {
rotation: 0,
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
var attack = new SpecialAttack();
// Find closest enemy to determine attack direction
var closestEnemy = null;
var closestDistance = Infinity;
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));
if (distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
if (closestEnemy) {
if (closestEnemy.x < self.x) {
// Enemy is to the left, attack left
attack.x = self.x - 100;
attack.flipLeft = true;
} else {
// Enemy is to the right, attack right
attack.x = self.x + 100;
attack.flipLeft = false;
}
} else {
attack.x = self.x + 100;
attack.flipLeft = false;
}
attack.y = self.y - 60;
attacks.push(attack);
game.addChild(attack);
LK.getSound('slash').play();
LK.setTimeout(function () {
self.isAttacking = false;
}, 600);
};
self.moveUp = function () {
self.y -= self.moveSpeed * 3;
if (self.y < 120) self.y = 120;
};
self.moveDown = function () {
self.y += self.moveSpeed * 3;
if (self.y > 2612) self.y = 2612;
};
self.moveLeft = function () {
self.x -= self.moveSpeed * 3;
if (self.x < 40) self.x = 40;
};
self.moveRight = function () {
self.x += self.moveSpeed * 3;
if (self.x > 2008) self.x = 2008;
};
self.jump = function () {
if (self.isOnGround) {
self.velocityY = self.jumpPower;
self.isOnGround = false;
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.specialCooldown > 0) {
self.specialCooldown--;
}
// Update energy system
if (self.energyRechargeTimer > 0) {
self.energyRechargeTimer--;
} else if (self.energy < self.maxEnergy) {
// Recharge energy after delay
self.energy += self.energyRechargeRate;
if (self.energy > self.maxEnergy) {
self.energy = self.maxEnergy;
}
}
// Update combo timers
if (self.punchComboTimer > 0) {
self.punchComboTimer--;
if (self.punchComboTimer === 0) {
self.punchCombo = 0; // Reset punch combo
}
}
if (self.kickComboTimer > 0) {
self.kickComboTimer--;
if (self.kickComboTimer === 0) {
self.kickCombo = 0; // Reset kick combo
}
}
// Apply gravity with more realistic physics
self.velocityY += self.gravity;
self.y += self.velocityY;
// Check ground collision
if (self.y >= self.groundY) {
self.y = self.groundY;
self.velocityY = 0;
self.isOnGround = true;
}
// Horizontal movement with acceleration/deceleration
var targetSpeed = 0;
self.isRunning = false;
if (isLeftPressed) {
targetSpeed = -self.maxSpeed;
if (leftPressTime > 30) {
// Running after half second
self.isRunning = true;
targetSpeed *= self.runMultiplier;
}
}
if (isRightPressed) {
targetSpeed = self.maxSpeed;
if (rightPressTime > 30) {
// Running after half second
self.isRunning = true;
targetSpeed *= self.runMultiplier;
}
}
// Apply acceleration or deceleration
if (targetSpeed !== 0) {
// Accelerate towards target speed
if (self.velocityX < targetSpeed) {
self.velocityX += self.acceleration;
if (self.velocityX > targetSpeed) self.velocityX = targetSpeed;
} else if (self.velocityX > targetSpeed) {
self.velocityX -= self.acceleration;
if (self.velocityX < targetSpeed) self.velocityX = targetSpeed;
}
} else {
// Decelerate to stop
if (self.velocityX > 0) {
self.velocityX -= self.deceleration;
if (self.velocityX < 0) self.velocityX = 0;
} else if (self.velocityX < 0) {
self.velocityX += self.deceleration;
if (self.velocityX > 0) self.velocityX = 0;
}
}
// Apply horizontal movement
self.x += self.velocityX;
// Boundary checks
if (self.x < 40) {
self.x = 40;
self.velocityX = 0;
}
if (self.x > 2008) {
self.x = 2008;
self.velocityX = 0;
}
// Jump when up is pressed
if (isUpPressed) {
self.jump();
}
};
return self;
});
var QuickAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('quickAttack', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 25;
self.lifetime = 15; // Quarter second at 60fps
self.hasHit = false;
self.update = function () {
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var SpecialAttack = Container.expand(function () {
var self = Container.call(this);
self.attackGraphics = null;
self.flipLeft = false;
self.damage = 100;
self.lifetime = 35; // Longest lasting attack
self.hasHit = false;
self.setDirection = function (flipLeft) {
if (self.attackGraphics) {
self.removeChild(self.attackGraphics);
}
if (flipLeft) {
self.attackGraphics = self.attachAsset('specialAttack', {
anchorX: 0.5,
anchorY: 0.5,
flipX: 0
});
} else {
self.attackGraphics = self.attachAsset('specialAttack', {
anchorX: 0.5,
anchorY: 0.5,
flipX: 1
});
}
};
// Initialize with default direction (right)
self.setDirection(false);
self.update = function () {
// Update direction if flipLeft property changes
if (self.flipLeft !== undefined && self.attackGraphics) {
self.setDirection(self.flipLeft);
self.flipLeft = undefined; // Reset to avoid repeated updates
}
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var SpinAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('specialAttack', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00FFFF // Cyan tint for spin attack
});
self.damage = 35; // Medium damage
self.lifetime = 20; // Medium duration
self.hasHit = false;
self.update = function () {
self.lifetime--;
// Spinning animation
attackGraphics.rotation += 0.3;
if (self.lifetime <= 0) {
self.destroy();
for (var i = attacks.length - 1; i >= 0; i--) {
if (attacks[i] === self) {
attacks.splice(i, 1);
break;
}
}
}
};
return self;
});
var TankEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0,
tint: 0xFF0000,
// Red tint to distinguish from regular enemy
scaleX: 1.3,
// Larger size
scaleY: 1.3
});
self.health = 100; // Higher health
self.damage = 35; // Higher damage
self.moveSpeed = -1; // Slower movement
self.lastDamageTime = 0;
self.attackCooldown = 0;
self.attackRange = 120;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
LK.setScore(LK.getScore() + 25); // More points for tough enemy
scoreTxt.setText(LK.getScore());
// Spawn a new enemy when this one is defeated
spawnEnemy();
}
};
self.update = function () {
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (player) {
// Simple movement towards player
var moveX = player.x - self.x;
var moveY = player.y - self.y;
// Normalize movement speed
var distance = Math.sqrt(moveX * moveX + moveY * moveY);
// Attack if close enough and cooldown is ready
if (distance <= self.attackRange && self.attackCooldown <= 0) {
self.attackCooldown = 120; // 2 second cooldown (slower but more powerful)
player.takeDamage(self.damage);
// Flash enemy to show attack
LK.effects.flashObject(self, 0xFF0000, 300);
LK.getSound('special').play(); // TankEnemy heavy attack sound
}
if (distance > 0) {
moveX = moveX / distance * Math.abs(self.moveSpeed);
moveY = moveY / distance * Math.abs(self.moveSpeed);
}
self.x += moveX;
self.y += moveY;
} else {
self.x += self.moveSpeed;
}
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F4F
});
/****
* Game Code
****/
// Create background - only bottom background covering full screen
var bottomBackground = LK.getAsset('backgroundBottom', {
width: 2048,
height: 2732,
// Full screen height
color: 0x228B22,
shape: 'box',
x: 0,
y: 0
});
game.addChild(bottomBackground);
var player;
var enemies = [];
var attacks = [];
var spawnTimer = 0;
var gameSpeed = 1;
var isUpPressed = false;
var isDownPressed = false;
var isLeftPressed = false;
var isRightPressed = false;
var leftPressTime = 0;
var rightPressTime = 0;
var healthPickups = [];
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -50;
scoreTxt.y = 50;
var healthBarBg = LK.getAsset('healthBarBg', {
width: 300,
height: 30,
color: 0xFF0000,
shape: 'box',
x: 150,
y: 80
});
LK.gui.topLeft.addChild(healthBarBg);
var healthBar = LK.getAsset('healthBar', {
width: 300,
height: 30,
color: 0x00FF00,
shape: 'box',
x: 150,
y: 80
});
LK.gui.topLeft.addChild(healthBar);
var healthTxt = new Text2('Health', {
size: 40,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
healthTxt.x = 150;
healthTxt.y = 50;
LK.gui.topLeft.addChild(healthTxt);
var specialCooldownTxt = new Text2('Special Ready', {
size: 40,
fill: 0x9932CC
});
specialCooldownTxt.anchor.set(0, 0);
specialCooldownTxt.x = 50;
specialCooldownTxt.y = 170;
LK.gui.topLeft.addChild(specialCooldownTxt);
// Energy bar for sword attacks
var energyBarBg = LK.getAsset('healthBarBg', {
width: 300,
height: 25,
color: 0x4B0082,
shape: 'box',
x: 150,
y: 130
});
LK.gui.topLeft.addChild(energyBarBg);
var energyBar = LK.getAsset('healthBar', {
width: 300,
height: 25,
color: 0x9932CC,
shape: 'box',
x: 150,
y: 130
});
LK.gui.topLeft.addChild(energyBar);
var energyTxt = new Text2('Energy', {
size: 35,
fill: 0x9932CC
});
energyTxt.anchor.set(0, 0);
energyTxt.x = 150;
energyTxt.y = 100;
LK.gui.topLeft.addChild(energyTxt);
// Boss health bar (initially hidden)
var bossHealthBarBg = LK.getAsset('healthBarBg', {
width: 600,
height: 40,
color: 0xFF0000,
shape: 'box',
x: 1024,
y: 150
});
bossHealthBarBg.anchor.set(0.5, 0);
bossHealthBarBg.visible = false;
game.addChild(bossHealthBarBg);
var bossHealthBar = LK.getAsset('healthBar', {
width: 600,
height: 40,
color: 0xFFD700,
shape: 'box',
x: 1024,
y: 150
});
bossHealthBar.anchor.set(0.5, 0);
bossHealthBar.visible = false;
game.addChild(bossHealthBar);
var bossHealthTxt = new Text2('BOSS', {
size: 60,
fill: 0xFFD700
});
bossHealthTxt.anchor.set(0.5, 1);
bossHealthTxt.x = 1024;
bossHealthTxt.y = 140;
bossHealthTxt.visible = false;
game.addChild(bossHealthTxt);
// Movement arrow buttons (bottom left)
var upBtn = new Text2('↑', {
size: 200,
fill: 0xFFFFFF
});
upBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(upBtn);
upBtn.x = 120;
upBtn.y = -180;
var downBtn = new Text2('↓', {
size: 200,
fill: 0xFFFFFF
});
downBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(downBtn);
downBtn.x = 120;
downBtn.y = -50;
var leftBtn = new Text2('←', {
size: 200,
fill: 0xFFFFFF
});
leftBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(leftBtn);
leftBtn.x = 50;
leftBtn.y = -115;
var rightBtn = new Text2('→', {
size: 200,
fill: 0xFFFFFF
});
rightBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(rightBtn);
rightBtn.x = 190;
rightBtn.y = -115;
// Attack buttons (bottom right)
var quickBtn = new Text2('Golpe', {
size: 80,
fill: 0xFFD700
});
quickBtn.anchor.set(1, 1);
LK.gui.bottomRight.addChild(quickBtn);
quickBtn.x = -50;
quickBtn.y = -50;
var heavyBtn = new Text2('Patada', {
size: 80,
fill: 0xFF6347
});
heavyBtn.anchor.set(1, 1);
LK.gui.bottomRight.addChild(heavyBtn);
heavyBtn.x = -50;
heavyBtn.y = -150;
var specialBtn = new Text2('Espada', {
size: 80,
fill: 0x9932CC
});
specialBtn.anchor.set(1, 1);
LK.gui.bottomRight.addChild(specialBtn);
specialBtn.x = -50;
specialBtn.y = -250;
// Level progression variables
var levelDistance = 0;
var maxLevelDistance = 1000; // Distance to complete level
var bossSpawned = false;
var levelComplete = false;
// Initialize player
player = game.addChild(new Player());
player.x = 1024; // Center horizontally (2048/2)
player.y = 1800; // Lower position
// Level progress UI
var progressTxt = new Text2('Progress: 0%', {
size: 50,
fill: 0xFFFFFF
});
progressTxt.anchor.set(0.5, 0);
progressTxt.x = 0;
progressTxt.y = 50;
LK.gui.top.addChild(progressTxt);
// Button event handlers
quickBtn.down = function (x, y, obj) {
if (player) {
player.quickAttack();
}
};
heavyBtn.down = function (x, y, obj) {
if (player) {
player.heavyAttack();
}
};
specialBtn.down = function (x, y, obj) {
if (player) {
player.specialAttack();
}
};
// Movement button event handlers - single press movement
upBtn.down = function (x, y, obj) {
isUpPressed = true;
};
downBtn.down = function (x, y, obj) {
if (player) {
var newY = player.y + player.moveSpeed * 10;
if (newY > 2612) newY = 2612;
player.y = newY;
}
};
leftBtn.down = function (x, y, obj) {
isLeftPressed = true;
leftPressTime = 0;
};
rightBtn.down = function (x, y, obj) {
isRightPressed = true;
rightPressTime = 0;
};
// Spawn enemies only
function spawnEnemy() {
// Don't spawn regular enemies if boss is spawned or level is complete
if (bossSpawned || levelComplete) return;
// Randomly choose enemy type
var enemyType = Math.random();
var enemy;
if (enemyType < 0.5) {
enemy = new Enemy(); // Regular enemy - 50% chance
} else if (enemyType < 0.8) {
enemy = new FastEnemy(); // Fast enemy - 30% chance
} else {
enemy = new TankEnemy(); // Tank enemy - 20% chance
}
// Spawn enemy from right side
if (Math.random() < 0.8) {
enemy.x = 2048 + 100;
enemy.y = 1800 - Math.random() * 600 + 300; // Lower area spawn
// Start enemy invisible and fade in gradually
enemy.alpha = 0;
tween(enemy, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeIn
});
enemies.push(enemy);
game.addChild(enemy);
} else {
// Spawn enemy from left side
enemy.x = -100;
enemy.y = 1800 - Math.random() * 600 + 300; // Lower area spawn
enemy.moveSpeed = Math.abs(enemy.moveSpeed); // Make moveSpeed positive for left-to-right movement
// Start enemy invisible and fade in gradually
enemy.alpha = 0;
tween(enemy, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeIn
});
enemies.push(enemy);
game.addChild(enemy);
}
}
// Spawn initial enemies at game start
function spawnInitialEnemies() {
for (var i = 0; i < 2; i++) {
spawnEnemy();
}
}
// Call initial enemy spawn
spawnInitialEnemies();
// Start background music
LK.playMusic('Sonidofondo');
function spawnBoss() {
if (bossSpawned) return;
bossSpawned = true;
var boss = new Boss();
boss.x = 2048 + 200; // Spawn from right side
boss.y = 1800; // Lower position
boss.alpha = 0;
// Dramatic boss entrance
tween(boss, {
alpha: 1,
x: 1800
}, {
duration: 2000,
easing: tween.easeOut
});
// Show boss health bar
bossHealthBarBg.visible = true;
bossHealthBar.visible = true;
bossHealthTxt.visible = true;
enemies.push(boss);
game.addChild(boss);
}
game.update = function () {
// Update press time tracking for running
if (isLeftPressed) leftPressTime++;
if (isRightPressed) rightPressTime++;
// Update game speed over time
gameSpeed += 0.001;
// Update level progression
if (!levelComplete) {
levelDistance += gameSpeed;
var progressPercent = Math.min(100, levelDistance / maxLevelDistance * 100);
progressTxt.setText('Progress: ' + Math.floor(progressPercent) + '%');
// Spawn boss after 2 minutes (7200 ticks at 60fps)
if (LK.ticks >= 7200 && !bossSpawned) {
spawnBoss();
}
}
// Enemy spawning is now handled when enemies are defeated (see Enemy class takeDamage method)
// Update UI
if (player) {
// Update health bar
var healthPercent = player.health / player.maxHealth;
healthBar.width = 300 * healthPercent;
healthBar.tint = healthPercent > 0.5 ? 0x00FF00 : healthPercent > 0.25 ? 0xFFFF00 : 0xFF0000;
// Update energy bar
var energyPercent = player.energy / player.maxEnergy;
energyBar.width = 300 * energyPercent;
energyBar.tint = energyPercent > 0.6 ? 0x9932CC : energyPercent > 0.3 ? 0xFFFF00 : 0xFF0000;
if (player.specialCooldown > 0) {
specialCooldownTxt.setText('Special: ' + Math.ceil(player.specialCooldown / 60) + 's');
} else if (player.energy < player.energyCostPerAttack) {
specialCooldownTxt.setText('Low Energy');
} else {
specialCooldownTxt.setText('Special Ready');
}
// Check if health is zero for game over
if (player.health <= 0) {
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
// Update boss health bar
if (bossSpawned && enemies.length > 0) {
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].maxHealth && enemies[i].maxHealth === 300) {
// Boss detection
var boss = enemies[i];
var bossHealthPercent = boss.health / boss.maxHealth;
bossHealthBar.width = 600 * bossHealthPercent;
bossHealthBar.tint = bossHealthPercent > 0.5 ? 0xFFD700 : bossHealthPercent > 0.25 ? 0xFFFF00 : 0xFF0000;
break;
}
}
}
// Enemy attacks are now handled in their individual update methods
// Spawn health pickups when player health is low
if (player && player.health < 30 && Math.random() < 0.002) {
var isLarge = Math.random() < 0.3; // 30% chance for large pickup
var pickup = new HealthPickup();
pickup.init(isLarge);
pickup.x = 200 + Math.random() * 1600;
pickup.y = 200 + Math.random() * 2200;
healthPickups.push(pickup);
game.addChild(pickup);
}
// Check collisions between player and health pickups
for (var i = healthPickups.length - 1; i >= 0; i--) {
var pickup = healthPickups[i];
if (player && player.intersects(pickup)) {
if (pickup.isLarge) {
player.health = player.maxHealth; // Full heal
} else {
player.health = Math.min(player.maxHealth, player.health + 50); // Half heal
}
pickup.destroy();
healthPickups.splice(i, 1);
}
}
// Check collisions between attacks and enemies
for (var i = attacks.length - 1; i >= 0; i--) {
var attack = attacks[i];
if (attack.hasHit) continue;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (attack.intersects(enemy)) {
enemy.takeDamage(attack.damage);
attack.hasHit = true;
// Special visual effect for sword attacks
if (attack.damage >= 100) {
LK.effects.flashObject(enemy, 0x9932CC, 500);
tween(enemy, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
tween(enemy, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
break;
}
}
}
// Score is only updated when enemies are defeated
};
leftBtn.up = function (x, y, obj) {
isLeftPressed = false;
leftPressTime = 0;
};
rightBtn.up = function (x, y, obj) {
isRightPressed = false;
rightPressTime = 0;
};
upBtn.up = function (x, y, obj) {
isUpPressed = false;
};
pandillero que quiere pelear a puño limpio. In-Game asset. 2d. High contrast. No shadows
cristal verde de tamaño grande. In-Game asset. 2d. High contrast. No shadows
una calle de una ciudad peligrosa con edificios viejos en la parte de arriba. In-Game asset. 2d. High contrast. No shadows
barra de salud de color verde. In-Game asset. 2d. High contrast. No shadows
hombre con camisa negra de manga larga, pantalones negros, botas negras y guantes negros y un pasamontañas negro dando un puñetazo.. In-Game asset. 2d. High contrast. No shadows
hombre con camisa negra de manga larga, pantalones negros, botas negras y guantes negros y un pasamontañas negro dando una patada de artes marciales In-Game asset. 2d. High contrast. No shadows
hombre con camisa negra de manga larga, pantalones negros, botas negras y guantes negros y un pasamontañas negro atacando con una katana con un mango negro. In-Game asset. 2d. High contrast. No shadows
hombre con camisa negra de manga larga, pantalones negros, botas negras y guantes negros y un pasamontañas negro en posicion de combate. In-Game asset. 2d. High contrast. No shadows