Code edit (1 edits merged)
Please save this source code
User prompt
Programa lo q te pedi porfa
User prompt
Programa lo q te pedi porfa
User prompt
Cámbialo a CubikWarrior: Choque de Titanes
User prompt
Quiero que cambies el título del juego a Warrior la pelea cúbica.
User prompt
Ok dale más detalle a todo los niveles
User prompt
Agregale más detalles a la pausa
User prompt
Agregale animaciones a la interfaz ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Agregale animaciones al selector de niveles ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Pero q en la tienda se vean todas las skins y q todas tengan animaciones ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Agregale animaciones a la tienda ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Agregale más opciones al botón de configuración y agregale más detalles ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Agregale más detalles al selector de niveles
User prompt
Agregale más detalles al botón de configuración
User prompt
Dale más detalle a los niveles, dale más detalle a las plataformas, a los fondos, a las bases, a todo, pero a todos los niveles completos.
User prompt
Pero también agrega las skins de los bosses y los enemigos voladores y los enemigos normales a la tienda. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Organiza las skins para que estén dentro de la interfaz ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Quiero que mejores el estilo de la tienda, que esté más detallado.
User prompt
Quiero que la interfaz del inicio sea más detallada.
User prompt
necesito que en la tienda cuando presione el apartado del el simbolo dice voces me aparezcan en despliegue me aparezcan todas las skins de los voces ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Necesito q cuando presione el botón de salto el asset Warrior salte
User prompt
No salta aaaaa
User prompt
Has q cuando presiona el botón jump o saltar warrior salte
User prompt
No salta cuando presionó el botón jump has que Warrior salte al presionarlo
User prompt
Ya ni siquiera salta de cuánto presiona el botón jump
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
unlockedLevels: 1,
musicVolume: 1,
soundVolume: 1,
highScore: 0,
lastCompletedLevel: 0,
coins: 0,
selectedSkin: "default",
ownedSkins: ["default"],
selectedEnemySkin: "default",
ownedEnemySkins: ["default"],
selectedBossSkin: "default",
ownedBossSkins: ["default"],
selectedFlyingSkin: "default",
ownedFlyingSkins: ["default"],
selectedVoiceSkin: "default",
ownedVoiceSkins: ["default"]
});
/****
* Classes
****/
var Arrow = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifetime = 240; // 4 seconds at 60fps
self.damage = 20;
self.update = function () {
// Homing behavior - track nearest enemy
var nearestEnemy = null;
var nearestDistance = Infinity;
// Find nearest enemy
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
// Adjust velocity towards nearest enemy if found
if (nearestEnemy && nearestDistance > 50) {
var dx = nearestEnemy.x - self.x;
var dy = nearestEnemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize direction vector
var dirX = dx / distance;
var dirY = dy / distance;
// Homing strength (how aggressively it turns)
var homingStrength = 0.3;
var arrowSpeed = 12;
// Blend current velocity with target direction using tween for smooth turning
self.velocityX += (dirX * arrowSpeed - self.velocityX) * homingStrength;
self.velocityY += (dirY * arrowSpeed - self.velocityY) * homingStrength;
// Maintain consistent speed
var currentSpeed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
if (currentSpeed > 0) {
self.velocityX = self.velocityX / currentSpeed * arrowSpeed;
self.velocityY = self.velocityY / currentSpeed * arrowSpeed;
}
}
self.x += self.velocityX;
self.y += self.velocityY;
self.lifetime--;
// Rotate arrow to match trajectory
if (self.velocityX !== 0) {
graphics.rotation = Math.atan2(self.velocityY, self.velocityX);
}
// Calculate distance traveled from start position
if (self.startX === undefined) {
self.startX = self.x;
self.startY = self.y;
}
var distanceTraveled = Math.sqrt(Math.pow(self.x - self.startX, 2) + Math.pow(self.y - self.startY, 2));
var maxRange = 400; // Short range limit
// Remove if lifetime expired, off screen, or exceeded range
if (self.lifetime <= 0 || self.x < -50 || self.x > 2098 || self.y > 2100 || distanceTraveled > maxRange) {
self.destroy();
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
// Apply selected boss skin
var skinColor = storage.selectedBossSkin || 'default';
var bossSkinColors = {
'default': {
main: 0x800080,
accent: 0x9932CC
},
'crimson': {
main: 0xDC143C,
accent: 0xFF6347
},
'shadow': {
main: 0x2F2F2F,
accent: 0x696969
},
'golden': {
main: 0xFFD700,
accent: 0xFFA500
},
'emerald': {
main: 0x50C878,
accent: 0x32CD32
},
'cyber': {
main: 0x00CED1,
accent: 0x00FFFF
}
};
var currentSkin = bossSkinColors[skinColor] || bossSkinColors['default'];
var graphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 1.0,
tint: currentSkin.main
});
// Health bar
var healthBarBg = self.attachAsset('platform', {
width: 160,
height: 20,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -220,
tint: 0x000000
});
var healthBarFill = self.attachAsset('platform', {
width: 156,
height: 16,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -220,
tint: currentSkin.accent || 0xFF0000
});
self.health = 150;
self.maxHealth = 150;
self.velocityX = 0;
self.velocityY = 0;
self.onGround = false;
self.speed = 3;
self.direction = 1;
self.attackCooldown = 0;
self.animationTick = 0;
self.phase = 1; // Boss has different phases
self.epicMusicTriggered = false;
self.update = function () {
// Apply gravity
if (!self.onGround) {
self.velocityY += 0.8;
}
// Enhanced Boss AI - different behavior based on health with range detection
var distanceToPlayer = Math.abs(self.x - player.x);
var verticalDistance = Math.abs(self.y - player.y);
var healthPercent = self.health / self.maxHealth;
var attackRange = healthPercent > 0.5 ? 250 : 350; // Longer range in phase 2
var aggressiveRange = healthPercent > 0.5 ? 120 : 180;
if (healthPercent > 0.5) {
// Phase 1: Enhanced chase and attack with range detection
if (distanceToPlayer < attackRange && distanceToPlayer > aggressiveRange) {
// Pursuit mode - faster movement when player detected
var pursuitSpeed = self.speed * 1.3;
if (player.x > self.x) {
self.direction = 1;
self.velocityX = pursuitSpeed;
} else {
self.direction = -1;
self.velocityX = -pursuitSpeed;
}
// Boss can jump to platforms to reach player
if (player.y < self.y - 100 && self.onGround && Math.random() < 0.02) {
// Find platform in direction of player
var targetPlatform = null;
var targetDirection = player.x > self.x ? 1 : -1;
for (var p = 0; p < platforms.length; p++) {
var platform = platforms[p];
var platformInDirection = targetDirection > 0 ? platform.x > self.x : platform.x < self.x;
var platformDistance = Math.abs(platform.x - self.x);
if (platformInDirection && platform.y < self.y && platform.y > player.y - 100 && platformDistance < 400) {
targetPlatform = platform;
break;
}
}
if (targetPlatform) {
self.velocityY = -20; // Powerful boss jump
self.velocityX = targetDirection * (self.speed + 3);
self.onGround = false;
LK.getSound('boss_attack').play(); // Use unique boss attack sound for boss jump
}
}
} else {
self.velocityX = 0;
if (self.attackCooldown <= 0) {
self.groundSlam();
self.attackCooldown = 120;
}
}
} else {
// Phase 2: Faster, more aggressive - trigger epic music transition
if (!self.epicMusicTriggered) {
self.epicMusicTriggered = true;
// Play boss music with higher intensity for final phase
LK.playMusic('boss_music', {
fade: {
start: storage.musicVolume,
end: storage.musicVolume * 1.2,
duration: 500
}
});
}
self.speed = 4;
if (distanceToPlayer < attackRange && distanceToPlayer > aggressiveRange) {
// Enhanced pursuit in phase 2 with speed boost
var phase2Speed = self.speed * 1.5;
if (player.x > self.x) {
self.direction = 1;
self.velocityX = phase2Speed;
} else {
self.direction = -1;
self.velocityX = -phase2Speed;
}
// Highly aggressive platform jumping and pursuit in phase 2
if (self.onGround && (verticalDistance > 60 || Math.random() < 0.06)) {
// Find best platform for tactical advantage
var bestPlatform = null;
var bestScore = Infinity;
for (var p = 0; p < platforms.length; p++) {
var platform = platforms[p];
var platformDistance = Math.abs(platform.x - self.x);
var platformToPlayerDistance = Math.abs(platform.x - player.x);
var tacticalScore = platformDistance + platformToPlayerDistance * 0.3;
if (platform.y < self.y && platform.y > player.y - 120 && platformDistance < 500) {
if (tacticalScore < bestScore) {
bestScore = tacticalScore;
bestPlatform = platform;
}
}
}
if (bestPlatform) {
// Powerful strategic jump towards best platform
var jumpDirection = bestPlatform.x > self.x ? 1 : -1;
self.velocityY = -24; // Even more powerful jump
self.velocityX = jumpDirection * (self.speed + 5);
self.onGround = false;
LK.getSound('boss_attack').play();
} else {
// Direct pursuit jump if no platforms available
var targetDirection = player.x > self.x ? 1 : -1;
self.velocityY = -22;
self.velocityX = targetDirection * (self.speed + 4);
self.onGround = false;
LK.getSound('boss_attack').play();
}
// Create screen shake effect for dramatic boss jump
tween(game, {
x: game.x + (Math.random() - 0.5) * 20
}, {
duration: 100,
onFinish: function onFinish() {
tween(game, {
x: 0
}, {
duration: 100
});
}
});
}
} else {
self.velocityX = 0;
if (self.attackCooldown <= 0) {
self.multiAttack();
self.attackCooldown = 180;
}
}
}
// Apply velocity
self.x += self.velocityX;
self.y += self.velocityY;
self.onGround = false;
// Update cooldowns
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
self.animationTick++;
// Face direction
graphics.scaleX = self.direction > 0 ? 1 : -1;
// Breathing animation
var breathCycle = Math.sin(self.animationTick * 0.05);
graphics.scaleY = 1 + breathCycle * 0.05;
};
self.groundSlam = function () {
// Ground slam attack
tween(graphics, {
scaleY: 0.8
}, {
duration: 200,
onFinish: function onFinish() {
tween(graphics, {
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(graphics, {
scaleY: 1
}, {
duration: 200
});
// Damage player if close
if (Math.abs(self.x - player.x) < 120) {
player.takeDamage(20);
player.velocityX += (player.x > self.x ? 1 : -1) * 15;
player.velocityY = -10;
}
}
});
}
});
LK.getSound('boss_attack').play();
};
self.multiAttack = function () {
// Multiple projectiles in different directions
for (var i = 0; i < 5; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y - 100;
var angle = (i - 2) * 0.5; // Spread shots
bullet.velocityX = Math.cos(angle) * 8 * self.direction;
bullet.velocityY = Math.sin(angle) * 8 - 2;
enemyBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('boss_attack').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
// Update health bar
var healthPercent = Math.max(0, self.health / self.maxHealth);
healthBarFill.width = 156 * healthPercent;
// Flash effect
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 150,
onFinish: function onFinish() {
tween(graphics, {
tint: 0x800080
}, {
duration: 150
});
}
});
LK.getSound('enemy_hit').play();
if (self.health <= 0) {
LK.getSound('enemy_death').play();
self.destroy();
return true;
}
return false;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifetime = 180;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.lifetime--;
// Calculate distance traveled from start position
if (self.startX === undefined) {
self.startX = self.x;
self.startY = self.y;
}
var distanceTraveled = Math.sqrt(Math.pow(self.x - self.startX, 2) + Math.pow(self.y - self.startY, 2));
var maxRange = 300; // Short range limit for enemy bullets
// Remove if lifetime expired, off screen, or exceeded range
if (self.lifetime <= 0 || self.x < -50 || self.x > 2098 || self.y > 2800 || self.y < -100 || distanceTraveled > maxRange) {
self.destroy();
}
};
return self;
});
var FireBoss = Container.expand(function () {
var self = Container.call(this);
// Apply selected boss skin for fire boss
var skinColor = storage.selectedBossSkin || 'default';
var fireBossSkinColors = {
'default': {
main: 0xFF4500,
flame: 0xFF6347
},
'crimson': {
main: 0xDC143C,
flame: 0xFF1493
},
'shadow': {
main: 0x8B008B,
flame: 0xFF00FF
},
'golden': {
main: 0xFFD700,
flame: 0xFFA500
},
'emerald': {
main: 0x32CD32,
flame: 0x00FF00
},
'cyber': {
main: 0x00CED1,
flame: 0x00BFFF
}
};
var currentSkin = fireBossSkinColors[skinColor] || fireBossSkinColors['default'];
var graphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 1.0,
tint: currentSkin.main
});
// Health bar
var healthBarBg = self.attachAsset('platform', {
width: 160,
height: 20,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -220,
tint: 0x000000
});
var healthBarFill = self.attachAsset('platform', {
width: 156,
height: 16,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -220,
tint: currentSkin.flame || 0xFF6347
});
self.health = 250;
self.maxHealth = 250;
self.velocityX = 0;
self.velocityY = 0;
self.onGround = false;
self.speed = 2;
self.direction = 1;
self.attackCooldown = 0;
self.chargeAttackCooldown = 0;
self.animationTick = 0;
self.update = function () {
// Apply gravity
if (!self.onGround) {
self.velocityY += 0.8;
}
var distanceToPlayer = Math.abs(self.x - player.x);
var healthPercent = self.health / self.maxHealth;
// Charge attack
if (self.chargeAttackCooldown <= 0 && distanceToPlayer < 400) {
self.chargeAttack();
self.chargeAttackCooldown = 400;
}
// Fire rain attack
if (self.attackCooldown <= 0) {
self.fireRain();
self.attackCooldown = healthPercent > 0.5 ? 180 : 120;
}
// Slower but powerful movement
if (player.x > self.x) {
self.direction = 1;
self.velocityX = self.speed;
} else {
self.direction = -1;
self.velocityX = -self.speed;
}
// Apply velocity
self.x += self.velocityX;
self.y += self.velocityY;
self.onGround = false;
// Update cooldowns
if (self.attackCooldown > 0) self.attackCooldown--;
if (self.chargeAttackCooldown > 0) self.chargeAttackCooldown--;
self.animationTick++;
// Fire glow effect
var glowIntensity = 0.8 + Math.sin(self.animationTick * 0.15) * 0.2;
graphics.scaleY = glowIntensity;
graphics.scaleX = self.direction > 0 ? glowIntensity : -glowIntensity;
};
self.chargeAttack = function () {
// Powerful charge towards player
var chargeDirection = player.x > self.x ? 1 : -1;
self.velocityX = chargeDirection * 15;
self.velocityY = -5;
// Visual effect
tween(graphics, {
tint: 0xFFFF00
}, {
duration: 300,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFF4500
}, {
duration: 200
});
}
});
LK.getSound('fire_boss_attack').play();
};
self.fireRain = function () {
// Create multiple fire projectiles falling from above
for (var i = 0; i < 5; i++) {
var bullet = new EnemyBullet();
bullet.x = player.x + (i - 2) * 100 + Math.random() * 50 - 25;
bullet.y = player.y - 400;
bullet.velocityX = Math.random() * 2 - 1;
bullet.velocityY = 8;
// Make fire bullets orange/red
bullet.tint = 0xFF6347;
enemyBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('fire_boss_attack').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
var healthPercent = Math.max(0, self.health / self.maxHealth);
healthBarFill.width = 156 * healthPercent;
// Flash effect
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 150,
onFinish: function onFinish() {
tween(graphics, {
tint: 0xFF4500
}, {
duration: 150
});
}
});
LK.getSound('enemy_hit').play();
if (self.health <= 0) {
LK.getSound('enemy_death').play();
self.destroy();
return true;
}
return false;
};
return self;
});
var FlyingEnemy = Container.expand(function () {
var self = Container.call(this);
// Create flying enemy body parts with skin-specific assets
var skinColor = storage.selectedFlyingSkin || 'default';
var flyingSkinAssetMap = {
'default': {
body: 'flying_default_body',
wing: 'enemy_limb',
tint: 0x8B4513
},
'black': {
body: 'flying_black_body',
wing: 'flying_black_wing',
tint: 0x2f2f2f
},
'silver': {
body: 'flying_silver_body',
wing: 'flying_silver_wing',
tint: 0xc0c0c0
},
'golden': {
body: 'flying_golden_body',
wing: 'flying_golden_wing',
tint: 0xffd700
},
'red': {
body: 'flying_red_body',
wing: 'flying_red_wing',
tint: 0xff4500
},
'blue': {
body: 'flying_blue_body',
wing: 'flying_blue_wing',
tint: 0x1e90ff
}
};
var currentSkinAssets = flyingSkinAssetMap[skinColor] || flyingSkinAssetMap['default'];
var body = self.attachAsset(currentSkinAssets.body, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
width: 40,
height: 40,
tint: currentSkinAssets.tint
});
var leftWing = self.attachAsset(currentSkinAssets.wing, {
anchorX: 0.5,
anchorY: 0.5,
x: -25,
y: 0,
width: 20,
height: 8
});
var rightWing = self.attachAsset(currentSkinAssets.wing, {
anchorX: 0.5,
anchorY: 0.5,
x: 25,
y: 0,
width: 20,
height: 8
});
// Health bar
var healthBarBg = self.attachAsset('platform', {
width: 60,
height: 8,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35,
tint: 0x000000
});
var healthBarFill = self.attachAsset('platform', {
width: 56,
height: 6,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -35,
tint: 0x00FF00
});
self.health = 30;
self.maxHealth = 30;
self.speed = 2;
self.direction = 1;
self.attackRange = 120;
self.attackCooldown = 0;
self.animationTick = 0;
self.floatOffset = 0;
self.shootCooldown = 0;
self.update = function () {
// Floating movement
self.floatOffset += 0.1;
self.y += Math.sin(self.floatOffset) * 0.5;
// Wing flapping animation
var flapSpeed = 0.3;
var flapCycle = Math.sin(self.animationTick * flapSpeed);
leftWing.rotation = flapCycle * 0.8;
rightWing.rotation = -flapCycle * 0.8;
leftWing.y = Math.sin(self.animationTick * flapSpeed * 2) * 2;
rightWing.y = Math.sin(self.animationTick * flapSpeed * 2) * 2;
// Enhanced AI behavior - chase player and shoot with range detection
var distanceToPlayer = Math.abs(self.x - player.x);
var verticalDistance = Math.abs(self.y - player.y);
var attackRange = 400; // Flying enemies have longer detection range
var aggressiveRange = 200; // Close range for aggressive behavior
if (distanceToPlayer < attackRange && verticalDistance < 200) {
// Enhanced chase behavior with speed adjustment based on range
var isInAggressiveRange = distanceToPlayer < aggressiveRange;
var chaseSpeed = isInAggressiveRange ? self.speed * 1.8 : self.speed * 1.2;
// Chase player
if (player.x > self.x) {
self.direction = 1;
self.x += chaseSpeed;
} else {
self.direction = -1;
self.x -= chaseSpeed;
}
// Vertical pursuit - move towards player's Y position more aggressively
if (Math.abs(player.y - self.y) > 30) {
if (player.y > self.y) {
self.y += chaseSpeed * 0.7;
} else {
self.y -= chaseSpeed * 0.7;
}
}
// Strategic platform landing when pursuing player
if (Math.random() < 0.015 || isInAggressiveRange && Math.random() < 0.03) {
// Higher chance to land on platforms when in aggressive range
var nearestPlatform = null;
var nearestDistance = Infinity;
var bestStrategicPlatform = null;
var bestStrategicValue = Infinity;
for (var p = 0; p < platforms.length; p++) {
var platform = platforms[p];
var platformDistance = Math.sqrt(Math.pow(platform.x - self.x, 2) + Math.pow(platform.y - self.y, 2));
var platformToPlayerDistance = Math.abs(platform.x - player.x);
// Calculate strategic value (closer to player is better)
var strategicValue = platformDistance + platformToPlayerDistance;
if (platformDistance < 200) {
if (platformDistance < nearestDistance) {
nearestDistance = platformDistance;
nearestPlatform = platform;
}
if (strategicValue < bestStrategicValue && platformToPlayerDistance < 150) {
bestStrategicValue = strategicValue;
bestStrategicPlatform = platform;
}
}
}
// Prefer strategic platform over nearest if available
var targetPlatform = bestStrategicPlatform || nearestPlatform;
if (targetPlatform) {
// Aggressive dive towards platform
var dx = targetPlatform.x - self.x;
var dy = targetPlatform.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
self.x += dx / distance * (chaseSpeed + 1);
self.y += dy / distance * (chaseSpeed + 0.8);
}
}
// Shoot projectiles at player
if (self.shootCooldown <= 0 && distanceToPlayer < 200) {
self.shootAtPlayer();
self.shootCooldown = 90; // 1.5 seconds
}
} else {
// Patrol movement
self.x += self.speed * self.direction;
if (self.x < 50 || self.x > 1998) {
self.direction *= -1;
}
// Occasionally move towards platforms while patrolling
if (Math.random() < 0.003) {
// Random platform seeking
var randomPlatform = platforms[Math.floor(Math.random() * platforms.length)];
if (randomPlatform && Math.abs(randomPlatform.x - self.x) < 300) {
var dx = randomPlatform.x - self.x;
var dy = randomPlatform.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 50) {
// Don't get too close
self.x += dx / distance * self.speed * 0.5;
self.y += dy / distance * self.speed * 0.3;
}
}
}
}
// Update cooldowns
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
self.animationTick++;
// Face direction
if (self.direction > 0) {
body.scaleX = 1;
leftWing.scaleX = 1;
rightWing.scaleX = 1;
} else {
body.scaleX = -1;
leftWing.scaleX = -1;
rightWing.scaleX = -1;
}
};
self.shootAtPlayer = function () {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
// Calculate direction to player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = dx / distance * 6;
bullet.velocityY = dy / distance * 6;
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('attack').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
// Update health bar
var healthPercent = Math.max(0, self.health / self.maxHealth);
healthBarFill.width = 56 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00;
} else {
healthBarFill.tint = 0xFF0000;
}
// Flash effect
var originalTint = body.tint; // Store original skin color
tween(body, {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(body, {
tint: originalTint // Restore original skin color
}, {
duration: 100
});
}
});
LK.getSound('enemy_hit').play();
if (self.health <= 0) {
LK.getSound('enemy_death').play();
self.destroy();
return true;
}
return false;
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifetime = 180; // 3 seconds at 60fps
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.lifetime--;
// Calculate distance traveled from start position
if (self.startX === undefined) {
self.startX = self.x;
self.startY = self.y;
}
var distanceTraveled = Math.sqrt(Math.pow(self.x - self.startX, 2) + Math.pow(self.y - self.startY, 2));
var maxRange = 350; // Short range limit for projectiles
// Remove if lifetime expired, off screen, or exceeded range
if (self.lifetime <= 0 || self.x < -50 || self.x > 2098 || self.y > 2800 || distanceTraveled > maxRange) {
self.destroy();
}
};
return self;
});
var ShadowBoss = Container.expand(function () {
var self = Container.call(this);
// Apply selected boss skin for shadow boss
var skinColor = storage.selectedBossSkin || 'default';
var shadowBossSkinColors = {
'default': {
main: 0x2F2F2F,
glow: 0x8A2BE2
},
'crimson': {
main: 0x8B0000,
glow: 0xFF0000
},
'shadow': {
main: 0x1C1C1C,
glow: 0x4B0082
},
'golden': {
main: 0xB8860B,
glow: 0xFFD700
},
'emerald': {
main: 0x006400,
glow: 0x00FF00
},
'cyber': {
main: 0x008B8B,
glow: 0x00FFFF
}
};
var currentSkin = shadowBossSkinColors[skinColor] || shadowBossSkinColors['default'];
var graphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 1.0,
tint: currentSkin.main
});
// Health bar
var healthBarBg = self.attachAsset('platform', {
width: 160,
height: 20,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -220,
tint: 0x000000
});
var healthBarFill = self.attachAsset('platform', {
width: 156,
height: 16,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -220,
tint: currentSkin.glow || 0x8A2BE2
});
self.health = 200;
self.maxHealth = 200;
self.velocityX = 0;
self.velocityY = 0;
self.onGround = false;
self.speed = 4;
self.direction = 1;
self.attackCooldown = 0;
self.teleportCooldown = 0;
self.animationTick = 0;
self.phase = 1;
self.update = function () {
// Apply gravity
if (!self.onGround) {
self.velocityY += 0.8;
}
var distanceToPlayer = Math.abs(self.x - player.x);
var healthPercent = self.health / self.maxHealth;
// Teleport ability
if (self.teleportCooldown <= 0 && distanceToPlayer > 300) {
self.teleportToPlayer();
self.teleportCooldown = 300;
}
// Shadow projectile attack
if (self.attackCooldown <= 0) {
self.shadowBlast();
self.attackCooldown = healthPercent > 0.5 ? 150 : 100;
}
// Movement
if (player.x > self.x) {
self.direction = 1;
self.velocityX = self.speed;
} else {
self.direction = -1;
self.velocityX = -self.speed;
}
// Apply velocity
self.x += self.velocityX;
self.y += self.velocityY;
self.onGround = false;
// Update cooldowns
if (self.attackCooldown > 0) self.attackCooldown--;
if (self.teleportCooldown > 0) self.teleportCooldown--;
self.animationTick++;
// Shadow flickering effect
graphics.alpha = 0.7 + Math.sin(self.animationTick * 0.1) * 0.3;
graphics.scaleX = self.direction > 0 ? 1 : -1;
};
self.teleportToPlayer = function () {
// Fade out
tween(graphics, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
// Teleport behind player
self.x = player.x + (Math.random() > 0.5 ? 200 : -200);
self.y = player.y;
// Fade in
tween(graphics, {
alpha: 0.7
}, {
duration: 200
});
}
});
};
self.shadowBlast = function () {
for (var i = 0; i < 3; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y - 100;
var angle = (i - 1) * 0.8;
bullet.velocityX = Math.cos(angle) * 6 * self.direction;
bullet.velocityY = Math.sin(angle) * 6 - 3;
// Make shadow bullets darker
bullet.tint = 0x4B0082;
enemyBullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shadow_boss_attack').play();
};
self.takeDamage = function (damage) {
self.health -= damage;
var healthPercent = Math.max(0, self.health / self.maxHealth);
healthBarFill.width = 156 * healthPercent;
// Flash effect
tween(graphics, {
tint: 0xFFFFFF
}, {
duration: 150,
onFinish: function onFinish() {
tween(graphics, {
tint: 0x2F2F2F
}, {
duration: 150
});
}
});
LK.getSound('enemy_hit').play();
if (self.health <= 0) {
LK.getSound('enemy_death').play();
self.destroy();
return true;
}
return false;
};
return self;
});
var Stickman = Container.expand(function () {
var self = Container.call(this);
// Get selected skin and determine asset and tint
var selectedSkin = storage.selectedSkin || 'default';
var skinAssetMap = {
'default': {
asset: 'warrior',
tint: 0xFFFFFF
},
'red': {
asset: 'warrior_red',
tint: 0xFF4444
},
'blue': {
asset: 'warrior_blue',
tint: 0x4444FF
},
'green': {
asset: 'warrior_green',
tint: 0x44FF44
},
'purple': {
asset: 'warrior_purple',
tint: 0x8844FF
},
'gold': {
asset: 'warrior_gold',
tint: 0xFFD700
},
'orange': {
asset: 'warrior_orange',
tint: 0xFF8844
},
'cyan': {
asset: 'warrior_cyan',
tint: 0x44FFFF
},
'magenta': {
asset: 'warrior_magenta',
tint: 0xFF44FF
},
'silver': {
asset: 'warrior_silver',
tint: 0xC0C0C0
},
'lime': {
asset: 'warrior_lime',
tint: 0x88FF44
},
'bronze': {
asset: 'warrior_bronze',
tint: 0xCD7F32
}
};
var skinConfig = skinAssetMap[selectedSkin] || skinAssetMap['default'];
// Create detailed warrior with body parts for enhanced animations using selected skin
var warriorBody = self.attachAsset(skinConfig.asset, {
anchorX: 0.5,
anchorY: 1.0,
x: 0,
y: 0,
tint: skinConfig.tint
});
// Create separate limbs for detailed animation
var leftArm = self.attachAsset('enemy_limb', {
anchorX: 0.5,
anchorY: 0,
x: -15,
y: -80,
tint: 0x8B4513
});
var rightArm = self.attachAsset('enemy_limb', {
anchorX: 0.5,
anchorY: 0,
x: 15,
y: -80,
tint: 0x8B4513
});
var leftLeg = self.attachAsset('enemy_limb', {
anchorX: 0.5,
anchorY: 0,
x: -8,
y: -40,
height: 40,
tint: 0x654321
});
var rightLeg = self.attachAsset('enemy_limb', {
anchorX: 0.5,
anchorY: 0,
x: 8,
y: -40,
height: 40,
tint: 0x654321
});
// No sword - warrior fights with hands
// Movement properties
self.velocityX = 0;
self.velocityY = 0;
self.onGround = false;
self.speed = 8;
self.jumpPower = -18;
self.health = 100;
self.maxHealth = 100;
self.facingRight = true;
// Combat properties
self.canAttack = true;
self.attackCooldown = 0;
self.lastAttackTime = 0;
// Ammunition properties
self.maxAmmo = 5;
self.currentAmmo = 5;
self.reloadTime = 180; // 3 seconds at 60fps
self.timeSinceLastShot = 0;
// Animation properties
self.animationTick = 0;
self.isAttacking = false;
self.isJumping = false;
self.wasOnGround = true;
self.update = function () {
// Apply gravity
if (!self.onGround) {
self.velocityY += 0.8;
}
// Apply velocity
self.x += self.velocityX;
self.y += self.velocityY;
// Apply friction
self.velocityX *= 0.85;
// Reset ground detection
self.onGround = false;
// Reset double jump when touching ground later in collision detection
// Update attack cooldown
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Update ammunition reload system
self.timeSinceLastShot++;
if (self.currentAmmo < self.maxAmmo && self.timeSinceLastShot >= self.reloadTime) {
self.currentAmmo++;
self.timeSinceLastShot = 0;
// Update ammunition display when reloading
if (ammoText) {
ammoText.setText('Arrows: ' + self.currentAmmo + '/' + self.maxAmmo);
ammoText.tint = self.currentAmmo > 0 ? 0xFFFFFF : 0xFF4444;
}
}
// Animation updates
self.animationTick++;
// Check if jumping state changed
if (!self.onGround && self.wasOnGround) {
self.isJumping = true;
}
if (self.onGround && !self.wasOnGround) {
self.isJumping = false;
}
self.wasOnGround = self.onGround;
// Handle enhanced animations with detailed warrior body parts and sword movements
if (self.isAttacking) {
// Attack animation is handled in attack method - keep sword position
} else if (self.isJumping || !self.onGround) {
// Enhanced jump animation with dynamic rotation and detailed limb movement
var jumpPhase = self.velocityY < 0 ? 0 : 1; // 0 for going up, 1 for falling
if (jumpPhase === 0) {
// Going up - dynamic backward tilt based on jump velocity
var tiltAmount = Math.min(Math.abs(self.velocityY) * 0.02, 0.4);
warriorBody.rotation = -tiltAmount;
// Slight scale animation for jump power
warriorBody.scaleY = 1.1 - Math.abs(self.velocityY) * 0.005;
// Detailed leg positioning during upward jump - legs bent for power
leftLeg.rotation = -0.3;
rightLeg.rotation = -0.2;
leftLeg.y = -35;
rightLeg.y = -35;
// Arm positioning during jump - spread for balance
leftArm.rotation = -0.4;
rightArm.rotation = 0.2;
// No sword - hands positioned during jump for balance
} else {
// Falling down - forward tilt increases with fall speed
var fallTilt = Math.min(self.velocityY * 0.01, 0.3);
warriorBody.rotation = fallTilt;
// Slight stretch when falling fast
warriorBody.scaleY = 1 + self.velocityY * 0.002;
// Detailed leg positioning during fall - preparing for landing
leftLeg.rotation = 0.2;
rightLeg.rotation = 0.1;
leftLeg.y = -38;
rightLeg.y = -38;
// Arm positioning for landing preparation
leftArm.rotation = -0.2;
rightArm.rotation = 0.3;
// No sword - hands positioned for landing
// Add landing preparation animation when close to ground
if (self.velocityY > 8) {
tween(warriorBody, {
scaleY: 0.95
}, {
duration: 100,
easing: tween.easeOut
});
tween(leftLeg, {
rotation: 0.4,
y: -42
}, {
duration: 100,
easing: tween.easeOut
});
tween(rightLeg, {
rotation: 0.3,
y: -42
}, {
duration: 100,
easing: tween.easeOut
});
}
}
} else if (Math.abs(self.velocityX) > 0.1) {
// Enhanced walking/running animation with detailed warrior body parts and realistic limb movement
var walkSpeed = Math.abs(self.velocityX) > 4 ? 0.4 : 0.3; // Faster animation for running
var walkCycle = Math.sin(self.animationTick * walkSpeed);
var walkCycleDouble = Math.sin(self.animationTick * walkSpeed * 2);
var walkCycleOffset = Math.sin(self.animationTick * walkSpeed + Math.PI);
// Dynamic rotation based on movement speed
var speedFactor = Math.min(Math.abs(self.velocityX) / self.speed, 1);
var isRunning = Math.abs(self.velocityX) > 4;
warriorBody.rotation = walkCycle * (isRunning ? 0.12 : 0.08) * speedFactor;
// Vertical bob with speed-based intensity - more pronounced when running
warriorBody.y = Math.abs(walkCycleDouble) * (isRunning ? 6 : 4) * speedFactor;
// Subtle scale animation for dynamic feel
warriorBody.scaleY = 1 - Math.abs(walkCycleDouble) * (isRunning ? 0.03 : 0.02) * speedFactor;
// Slight horizontal sway
warriorBody.x = walkCycle * (isRunning ? 3 : 2) * speedFactor;
// Detailed leg animation - alternating steps with realistic movement
var leftLegCycle = Math.sin(self.animationTick * walkSpeed);
var rightLegCycle = Math.sin(self.animationTick * walkSpeed + Math.PI);
// Left leg movement
leftLeg.rotation = leftLegCycle * (isRunning ? 0.6 : 0.4);
leftLeg.y = -40 + Math.abs(leftLegCycle) * (isRunning ? 8 : 5);
// Right leg movement (opposite phase)
rightLeg.rotation = rightLegCycle * (isRunning ? 0.6 : 0.4);
rightLeg.y = -40 + Math.abs(rightLegCycle) * (isRunning ? 8 : 5);
// Detailed arm animation - natural swinging motion opposite to legs
leftArm.rotation = rightLegCycle * (isRunning ? 0.5 : 0.3);
leftArm.y = -80 + Math.abs(rightLegCycle) * (isRunning ? 4 : 2);
rightArm.rotation = leftLegCycle * (isRunning ? 0.3 : 0.2); // Right arm holds sword, less movement
rightArm.y = -80 + Math.abs(leftLegCycle) * (isRunning ? 3 : 1);
// Both arms swing naturally during movement
// Add realistic stride animation using tween for smoother transitions
if (Math.floor(self.animationTick * walkSpeed) !== Math.floor((self.animationTick - 1) * walkSpeed)) {
// Trigger on each step cycle for enhanced leg animation
var stepDirection = leftLegCycle > 0 ? 1 : -1;
tween(warriorBody, {
scaleY: isRunning ? 0.96 : 0.98
}, {
duration: isRunning ? 80 : 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(warriorBody, {
scaleY: isRunning ? 1.04 : 1.02
}, {
duration: isRunning ? 80 : 100,
easing: tween.easeIn
});
}
});
}
// Add slight lean forward when walking/running
var leanDirection = self.facingRight ? 1 : -1;
warriorBody.rotation += leanDirection * speedFactor * (isRunning ? 0.08 : 0.05);
} else {
// Enhanced idle animation with breathing effect and detailed body part movement
var breathCycle = Math.sin(self.animationTick * 0.05);
var idleCycle = Math.sin(self.animationTick * 0.03);
var weightShiftCycle = Math.sin(self.animationTick * 0.02);
warriorBody.rotation = breathCycle * 0.01;
warriorBody.y = breathCycle * 1;
warriorBody.scaleY = 1 + breathCycle * 0.01;
warriorBody.x = 0;
// Detailed idle leg positioning with subtle weight shifting
leftLeg.rotation = weightShiftCycle * 0.08;
rightLeg.rotation = -weightShiftCycle * 0.06;
leftLeg.y = -40 + Math.abs(idleCycle) * 1;
rightLeg.y = -40 + Math.abs(weightShiftCycle) * 1;
// Detailed idle arm positioning with breathing
leftArm.rotation = breathCycle * 0.03;
leftArm.y = -80 + breathCycle * 0.5;
rightArm.rotation = breathCycle * 0.02; // Right arm holds sword
rightArm.y = -80 + breathCycle * 0.3;
// Arms in natural idle position with breathing
// Add periodic subtle repositioning animation
if (self.animationTick % 180 === 0) {
// Every 3 seconds - subtle weight shift
var shiftDirection = Math.random() > 0.5 ? 1 : -1;
tween(leftLeg, {
rotation: shiftDirection * 0.1
}, {
duration: 300,
easing: tween.easeInOut
});
tween(rightLeg, {
rotation: -shiftDirection * 0.08
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(leftLeg, {
rotation: 0
}, {
duration: 500,
easing: tween.easeOut
});
tween(rightLeg, {
rotation: 0
}, {
duration: 500,
easing: tween.easeOut
});
}
});
}
}
// Face direction with smooth transition for all body parts
if (self.facingRight) {
warriorBody.scaleX = 1;
leftArm.scaleX = 1;
rightArm.scaleX = 1;
leftLeg.scaleX = 1;
rightLeg.scaleX = 1;
} else {
warriorBody.scaleX = -1;
leftArm.scaleX = -1;
rightArm.scaleX = -1;
leftLeg.scaleX = -1;
rightLeg.scaleX = -1;
}
// Keep player in bounds for horizontal layout
if (self.x < 30) self.x = 30;
if (self.x > 2018) self.x = 2018;
// Reset skew if character is not moving or attacking
if (Math.abs(self.velocityX) < 0.1 && !self.isAttacking && !self.isJumping) {
// Smoothly reset skew values
if (Math.abs(warriorBody.skewX) > 0.001) {
warriorBody.skewX *= 0.9;
}
if (Math.abs(warriorBody.skewY) > 0.001) {
warriorBody.skewY *= 0.9;
}
}
};
self.moveLeft = function () {
self.velocityX = -self.speed;
if (self.facingRight) {
// Add quick turn animation when changing direction
self.facingRight = false;
tween(warriorBody, {
scaleX: -1.2,
skewX: 0.1
}, {
duration: 100,
onFinish: function onFinish() {
tween(warriorBody, {
scaleX: -1,
skewX: 0
}, {
duration: 100
});
}
});
// No sword position changes needed
} else {
self.facingRight = false;
}
// Add quick leg push animation for movement start
tween(warriorBody, {
skewY: -0.05
}, {
duration: 50,
onFinish: function onFinish() {
tween(warriorBody, {
skewY: 0
}, {
duration: 100
});
}
});
};
self.moveRight = function () {
self.velocityX = self.speed;
if (!self.facingRight) {
// Add quick turn animation when changing direction
self.facingRight = true;
tween(warriorBody, {
scaleX: 1.2,
skewX: -0.1
}, {
duration: 100,
onFinish: function onFinish() {
tween(warriorBody, {
scaleX: 1,
skewX: 0
}, {
duration: 100
});
}
});
// No sword position changes needed
} else {
self.facingRight = true;
}
// Add quick leg push animation for movement start
tween(warriorBody, {
skewY: 0.05
}, {
duration: 50,
onFinish: function onFinish() {
tween(warriorBody, {
skewY: 0
}, {
duration: 100
});
}
});
};
self.jump = function () {
console.log("Jump called! onGround:", self.onGround, "velocityY:", self.velocityY);
if (self.onGround || Math.abs(self.velocityY) < 2) {
// First jump with squash animation
self.velocityY = self.jumpPower;
self.onGround = false;
LK.getSound('jump').play();
// Enhanced squash and stretch animation for jump with sword movement
tween(warriorBody, {
scaleY: 0.7,
scaleX: self.facingRight ? 1.3 : -1.3
}, {
duration: 100,
onFinish: function onFinish() {
tween(warriorBody, {
scaleY: 1.2,
scaleX: self.facingRight ? 0.9 : -0.9
}, {
duration: 150,
onFinish: function onFinish() {
tween(warriorBody, {
scaleY: 1,
scaleX: self.facingRight ? 1 : -1
}, {
duration: 200
});
}
});
}
});
// Arms raised during jump for balance
tween(leftArm, {
y: -85,
rotation: -0.4
}, {
duration: 100,
onFinish: function onFinish() {
tween(leftArm, {
y: -80,
rotation: 0
}, {
duration: 300
});
}
});
console.log("Jump executed! New velocityY:", self.velocityY);
} else if (self.canDoubleJump && !self.hasUsedDoubleJump) {
// Double jump with spin animation
self.velocityY = self.jumpPower * 0.8;
self.hasUsedDoubleJump = true;
self.canDoubleJump = false;
LK.getSound('jump').play();
// Enhanced spin animation for double jump with sword trail effect
var startRotation = warriorBody.rotation;
tween(warriorBody, {
rotation: startRotation + (self.facingRight ? Math.PI * 2 : -Math.PI * 2)
}, {
duration: 400,
onFinish: function onFinish() {
warriorBody.rotation = 0;
}
});
// Arms spinning during double jump
tween(leftArm, {
rotation: leftArm.rotation + (self.facingRight ? Math.PI * 2 : -Math.PI * 2)
}, {
duration: 400,
onFinish: function onFinish() {
leftArm.rotation = 0;
}
});
tween(rightArm, {
rotation: rightArm.rotation + (self.facingRight ? Math.PI * 2 : -Math.PI * 2)
}, {
duration: 400,
onFinish: function onFinish() {
rightArm.rotation = 0;
}
});
console.log("Double jump executed!");
} else {
console.log("Cannot jump - not on ground and no double jump available");
}
};
self.attack = function () {
if (self.attackCooldown <= 0) {
self.attackCooldown = 30;
self.isAttacking = true;
LK.getSound('attack').play();
// Hand-to-hand combat - punch animation
var originalWarriorRotation = warriorBody.rotation;
// Wind up phase - pull right arm back for powerful punch
tween(rightArm, {
rotation: -0.8,
x: self.facingRight ? 5 : -5,
y: -75
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Powerful punch forward
tween(rightArm, {
rotation: 0.6,
x: self.facingRight ? 25 : -25,
y: -80
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
// Recovery - return arm to normal position
tween(rightArm, {
rotation: 0,
x: self.facingRight ? 15 : -15,
y: -80
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isAttacking = false;
}
});
}
});
// Warrior body movement during punch
tween(warriorBody, {
rotation: originalWarriorRotation + (self.facingRight ? 0.2 : -0.2),
skewY: self.facingRight ? 0.1 : -0.1
}, {
duration: 150,
onFinish: function onFinish() {
tween(warriorBody, {
rotation: originalWarriorRotation - (self.facingRight ? 0.1 : -0.1),
skewY: self.facingRight ? -0.05 : 0.05
}, {
duration: 200,
onFinish: function onFinish() {
tween(warriorBody, {
rotation: originalWarriorRotation,
skewY: 0
}, {
duration: 250
});
}
});
}
});
}
});
// Add warrior body lean and step forward during punch
LK.setTimeout(function () {
// Dynamic body movement during attack
tween(warriorBody, {
scaleX: self.facingRight ? 1.1 : -1.1,
x: self.facingRight ? 8 : -8
}, {
duration: 100,
onFinish: function onFinish() {
tween(warriorBody, {
scaleX: self.facingRight ? 1 : -1,
x: 0
}, {
duration: 150
});
}
});
}, 150);
// Check for enemy hits during forward punch
LK.setTimeout(function () {
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Calculate punch position based on warrior's facing direction and arm extension
var punchReach = 80; // Increased reach for better hit detection
var punchX = self.x + (self.facingRight ? punchReach : -punchReach);
var punchY = self.y - 60; // Adjusted height to match enemy center
// Calculate distance from punch position to enemy
var distanceToEnemy = Math.abs(punchX - enemy.x);
var verticalDistance = Math.abs(punchY - enemy.y);
// Improved hit-box - larger rectangular area in front of warrior
var hitBoxWidth = 70; // Increased width of punch hit area
var hitBoxHeight = 80; // Increased height of punch hit area
if (distanceToEnemy < hitBoxWidth && verticalDistance < hitBoxHeight) {
// Additional check: enemy must be in front of warrior
var enemyInFront = self.facingRight ? enemy.x > self.x : enemy.x < self.x;
if (enemyInFront) {
if (enemy.takeDamage(25)) {
// Enemy damaged - check if destroyed
enemies.splice(i, 1);
LK.setScore(LK.getScore() + 100);
storage.coins += 5; // Reward coins for melee kills
scoreText.setText('Score: ' + LK.getScore());
}
// Enhanced knockback effect based on punch direction
var knockbackDirection = self.facingRight ? 1 : -1;
enemy.velocityX += knockbackDirection * 10;
enemy.velocityY = -4; // Slightly stronger upward knockback
}
}
}
}, 120);
}
};
self.shootArrow = function () {
if (self.attackCooldown <= 0 && self.currentAmmo > 0) {
self.attackCooldown = 20; // Shorter cooldown for ranged attacks
self.currentAmmo--; // Consume ammunition
self.timeSinceLastShot = 0; // Reset reload timer
LK.getSound('bow_shoot').play();
// Update ammunition display immediately
if (ammoText) {
ammoText.setText('Arrows: ' + self.currentAmmo + '/' + self.maxAmmo);
ammoText.tint = self.currentAmmo > 0 ? 0xFFFFFF : 0xFF4444;
}
// Create arrow
var arrow = new Arrow();
arrow.x = self.x + (self.facingRight ? 40 : -40);
arrow.y = self.y - 80;
// Set arrow velocity based on facing direction
var arrowSpeed = 12;
arrow.velocityX = self.facingRight ? arrowSpeed : -arrowSpeed;
arrow.velocityY = 0; // Straight horizontal trajectory
// Add arrow to game
arrows.push(arrow);
game.addChild(arrow);
// Bow shooting animation - pull left arm back then forward
var originalLeftArmX = leftArm.x;
var originalLeftArmY = leftArm.y;
var originalLeftArmRotation = leftArm.rotation;
// Pull back animation (drawing the bow)
tween(leftArm, {
rotation: -0.5,
x: self.facingRight ? -20 : 20,
y: -85
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Release animation (shooting the arrow)
tween(leftArm, {
rotation: 0.3,
x: self.facingRight ? -10 : 10,
y: -75
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
// Return to normal position
tween(leftArm, {
rotation: originalLeftArmRotation,
x: originalLeftArmX,
y: originalLeftArmY
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
});
// Slight body lean during bow shot
var originalWarriorRotation = warriorBody.rotation;
tween(warriorBody, {
rotation: originalWarriorRotation + (self.facingRight ? -0.1 : 0.1),
x: self.facingRight ? -5 : 5
}, {
duration: 100,
onFinish: function onFinish() {
tween(warriorBody, {
rotation: originalWarriorRotation,
x: 0
}, {
duration: 200
});
}
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.getSound('hit').play();
// Update health bar if it exists
if (healthText && healthText.healthBarFill) {
var healthPercent = Math.max(0, self.health / self.maxHealth);
healthText.healthBarFill.width = 210 * healthPercent;
healthText.healthBarFill.tint = self.health < 30 ? 0xFF0000 : 0x00FF00;
}
// Enhanced damage flash effect on warrior and sword
var originalSkinTint = skinConfig.tint; // Store the original skin tint
tween(warriorBody, {
tint: 0xFF0066
}, {
duration: 150,
onFinish: function onFinish() {
tween(warriorBody, {
tint: originalSkinTint // Restore original skin tint instead of white
}, {
duration: 150
});
}
});
// Flash arms as well
tween(leftArm, {
tint: 0xFF0066
}, {
duration: 150,
onFinish: function onFinish() {
tween(leftArm, {
tint: 0x8B4513
}, {
duration: 150
});
}
});
tween(rightArm, {
tint: 0xFF0066
}, {
duration: 150,
onFinish: function onFinish() {
tween(rightArm, {
tint: 0x8B4513
}, {
duration: 150
});
}
});
if (self.health <= 0) {
LK.showGameOver();
}
};
return self;
});
var StickmanEnemy = Container.expand(function () {
var self = Container.call(this);
// Apply selected enemy skin with specific assets
var skinColor = storage.selectedEnemySkin || 'default';
var skinAssetMap = {
'default': {
head: 'enemy_head',
body: 'enemy_body',
limb: 'enemy_limb',
sword: 'enemy_sword'
},
'dark': {
head: 'enemy_dark_head',
body: 'enemy_dark_body',
limb: 'enemy_dark_limb',
sword: 'enemy_dark_sword'
},
'green': {
head: 'enemy_green_head',
body: 'enemy_green_body',
limb: 'enemy_green_limb',
sword: 'enemy_green_sword'
},
'purple': {
head: 'enemy_purple_head',
body: 'enemy_purple_body',
limb: 'enemy_purple_limb',
sword: 'enemy_purple_sword'
},
'orange': {
head: 'enemy_orange_head',
body: 'enemy_orange_body',
limb: 'enemy_orange_limb',
sword: 'enemy_orange_sword'
},
'blue': {
head: 'enemy_blue_head',
body: 'enemy_blue_body',
limb: 'enemy_blue_limb',
sword: 'enemy_blue_sword'
}
};
var currentSkinAssets = skinAssetMap[skinColor] || skinAssetMap['default'];
// Create stickman body parts with skin-specific assets
var head = self.attachAsset(currentSkinAssets.head, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -65
});
var body = self.attachAsset(currentSkinAssets.body, {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: -50
});
var leftArm = self.attachAsset(currentSkinAssets.limb, {
anchorX: 0.5,
anchorY: 0,
x: -10,
y: -40
});
var rightArm = self.attachAsset(currentSkinAssets.limb, {
anchorX: 0.5,
anchorY: 0,
x: 10,
y: -40
});
var leftLeg = self.attachAsset(currentSkinAssets.limb, {
anchorX: 0.5,
anchorY: 0,
x: -5,
y: -20
});
var rightLeg = self.attachAsset(currentSkinAssets.limb, {
anchorX: 0.5,
anchorY: 0,
x: 5,
y: -20
});
var sword = self.attachAsset(currentSkinAssets.sword, {
anchorX: 0.5,
anchorY: 1,
x: 15,
y: -30
});
self.velocityX = 0;
self.velocityY = 0;
self.health = 50;
self.maxHealth = 50;
// Create health bar background
var healthBarBg = self.attachAsset('platform', {
width: 60,
height: 8,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -90,
tint: 0x000000
});
// Create health bar fill
var healthBarFill = self.attachAsset('platform', {
width: 56,
height: 6,
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -90,
tint: 0x00FF00
});
self.speed = 1.5;
self.direction = 1;
self.onGround = false;
self.attackRange = 80;
self.attackCooldown = 0;
self.animationTick = 0;
self.isAttacking = false;
self.lastPlayerDistance = 999;
self.update = function () {
// Apply gravity
if (!self.onGround) {
self.velocityY += 0.8;
}
// AI behavior - chase player if close enough with enhanced range detection
var distanceToPlayer = Math.abs(self.x - player.x);
var verticalDistance = Math.abs(self.y - player.y);
var attackRange = 300; // Increased detection range
var aggressiveRange = 150; // Close range for aggressive behavior
if (distanceToPlayer < attackRange && verticalDistance < 150) {
// Enhanced chase behavior with speed boost when close
var isInAggressiveRange = distanceToPlayer < aggressiveRange;
var chaseSpeed = isInAggressiveRange ? self.speed * 1.5 : self.speed;
// Chase player
if (player.x > self.x) {
self.direction = 1;
self.velocityX = chaseSpeed;
} else {
self.direction = -1;
self.velocityX = -chaseSpeed;
}
// Aggressive platform jumping when pursuing player
if (self.onGround && (verticalDistance > 60 || Math.abs(player.y - self.y) > 50)) {
// Look for platforms to reach player more aggressively
var nearestPlatform = null;
var nearestDistance = Infinity;
var targetDirection = player.x > self.x ? 1 : -1;
// Check all platforms for strategic positioning
for (var p = 0; p < platforms.length; p++) {
var platform = platforms[p];
var platformDistance = Math.abs(platform.x - self.x);
var platformToPlayerDistance = Math.abs(platform.x - player.x);
// Prefer platforms that are closer to player or provide better positioning
var strategicValue = platformDistance + platformToPlayerDistance * 0.5;
if (platform.y < self.y && platform.y > player.y - 100 && platformDistance < 400) {
if (strategicValue < nearestDistance) {
nearestDistance = strategicValue;
nearestPlatform = platform;
}
}
}
// Jump towards the platform or directly towards player
if (nearestPlatform) {
// Calculate jump direction towards platform
var jumpDirection = nearestPlatform.x > self.x ? 1 : -1;
self.velocityY = -16; // Stronger jump for pursuit
self.velocityX = jumpDirection * (self.speed + 3); // Extra horizontal velocity
self.onGround = false;
LK.getSound('jump').play();
} else if (verticalDistance > 100) {
// Direct jump towards player if no platforms available
self.velocityY = -14;
self.velocityX = targetDirection * (self.speed + 2);
self.onGround = false;
LK.getSound('jump').play();
}
}
// Attack if in range
if (distanceToPlayer < self.attackRange && self.attackCooldown <= 0) {
self.attackPlayer();
}
} else {
// Simple patrol movement
self.velocityX = self.speed * self.direction;
// Random jump towards platforms while patrolling
if (self.onGround && Math.random() < 0.005) {
// 0.5% chance per frame when on ground
// Look for platforms to jump to
var targetPlatform = null;
var patrolDirection = self.direction;
for (var p = 0; p < platforms.length; p++) {
var platform = platforms[p];
var platformDistance = Math.abs(platform.x - self.x);
var platformInDirection = patrolDirection > 0 ? platform.x > self.x : platform.x < self.x;
if (platformInDirection && platform.y < self.y && platformDistance < 200 && platformDistance > 50) {
targetPlatform = platform;
break;
}
}
if (targetPlatform) {
self.velocityY = -12; // Smaller jump for patrol
self.velocityX = patrolDirection * (self.speed + 1);
self.onGround = false;
}
}
}
// Apply velocity
self.x += self.velocityX;
self.y += self.velocityY;
// Reset ground detection
self.onGround = false;
// Reverse direction at edges
if (self.x < 50 || self.x > 1998) {
self.direction *= -1;
}
// Update attack cooldown
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Animation
self.animationTick++;
if (!self.isAttacking) {
// Walking animation with more realistic movement
var walkSpeed = Math.abs(self.velocityX) > 0.1 ? 0.25 : 0.1;
var walkCycle = Math.sin(self.animationTick * walkSpeed);
var walkCycleOffset = Math.sin(self.animationTick * walkSpeed + Math.PI);
// Leg animation - alternate walking
leftLeg.rotation = walkCycle * 0.4;
rightLeg.rotation = walkCycleOffset * 0.4;
// Arm animation - opposite to legs (natural walking)
leftArm.rotation = walkCycleOffset * 0.3;
rightArm.rotation = walkCycle * 0.3;
// Body bob animation when walking
if (Math.abs(self.velocityX) > 0.1) {
body.y = -50 + Math.abs(Math.sin(self.animationTick * walkSpeed * 2)) * 2;
head.y = -65 + Math.abs(Math.sin(self.animationTick * walkSpeed * 2)) * 2;
} else {
// Return to normal position when not walking
body.y = -50;
head.y = -65;
}
}
// Face direction
if (self.direction > 0) {
head.scaleX = 1;
body.scaleX = 1;
leftArm.scaleX = 1;
rightArm.scaleX = 1;
leftLeg.scaleX = 1;
rightLeg.scaleX = 1;
sword.scaleX = 1;
sword.x = 15;
} else {
head.scaleX = -1;
body.scaleX = -1;
leftArm.scaleX = -1;
rightArm.scaleX = -1;
leftLeg.scaleX = -1;
rightLeg.scaleX = -1;
sword.scaleX = -1;
sword.x = -15;
}
self.lastPlayerDistance = distanceToPlayer;
};
self.attackPlayer = function () {
if (self.attackCooldown <= 0) {
self.attackCooldown = 120; // 2 seconds at 60fps
self.isAttacking = true;
// Sword attack animation - swing from back to front
tween(sword, {
rotation: -1.8
}, {
duration: 150,
onFinish: function onFinish() {
// Swing forward
tween(sword, {
rotation: 0.8
}, {
duration: 200,
onFinish: function onFinish() {
// Return to normal position
tween(sword, {
rotation: 0
}, {
duration: 250,
onFinish: function onFinish() {
self.isAttacking = false;
}
});
}
});
// Check if player is hit during forward swing (after 150ms delay)
LK.setTimeout(function () {
var distanceToPlayer = Math.abs(self.x - player.x);
if (distanceToPlayer < self.attackRange) {
player.takeDamage(10);
// Knockback effect
var knockbackDirection = player.x > self.x ? 1 : -1;
player.velocityX += knockbackDirection * 8;
}
}, 150);
}
});
}
};
self.takeDamage = function (damage) {
self.health -= damage;
// Update health bar
var healthPercent = Math.max(0, self.health / self.maxHealth);
healthBarFill.width = 56 * healthPercent;
// Change health bar color based on health
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00; // Yellow
} else {
healthBarFill.tint = 0xFF0000; // Red
}
// Damage flash effect on all body parts
var bodyParts = [head, body, leftArm, rightArm, leftLeg, rightLeg];
for (var i = 0; i < bodyParts.length; i++) {
var part = bodyParts[i];
tween(part, {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(part, {
tint: 0xFF0000
}, {
duration: 100
});
}
});
}
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122,
// Dark digital blue background
width: 2732,
// Optimized for mobile landscape
height: 2048 // Optimized for mobile landscape
});
/****
* Game Code
****/
// Shadow Lord boss attack sound
// Fire Demon boss attack sound
// Purple Giant boss attack sound
// Load current level from storage
// Game state management
// StickmanEnemy skin variations
// FlyingEnemy skin variations
if (storage.currentLevel) {
currentLevel = storage.currentLevel;
}
var gameState = 'menu'; // 'menu', 'mapSelect', 'settings', 'playing', 'shop'
var shopContainer;
var currentShopCategory = 'guardior'; // Global variable for shop category state
var currentLevel = 1;
var enemyBullets = [];
// Input state variables
var leftPressed = false;
var rightPressed = false;
var currentTouchId = null;
// Level progression system - 3 levels + 1 boss, repeating
function getCurrentLevelConfig() {
var levelIndex = (currentLevel - 1) % 4;
var cycleNumber = Math.floor((currentLevel - 1) / 4);
var isBossLevel = levelIndex === 3;
if (isBossLevel) {
return getBossLevel(cycleNumber);
} else {
return getRegularLevel(levelIndex, cycleNumber);
}
}
function getRegularLevel(levelIndex, cycleNumber) {
var baseConfigs = [
// Level 1 theme - Forest Plains
{
name: "Forest Plains",
backgroundColor: 0x228B22,
musicTrack: 'menu_music',
basePlatforms: [{
x: 400,
y: 1800
}, {
x: 800,
y: 1600
}, {
x: 1200,
y: 1700
}, {
x: 1600,
y: 1500
}, {
x: 1900,
y: 1800
}],
baseEnemies: [{
type: 'stickman',
x: 900,
y: 1550
}, {
type: 'stickman',
x: 1400,
y: 1650
}]
},
// Level 2 theme - Sky Temple
{
name: "Sky Temple",
backgroundColor: 0x4169E1,
musicTrack: 'menu_music',
basePlatforms: [{
x: 300,
y: 1700
}, {
x: 600,
y: 1500
}, {
x: 900,
y: 1300
}, {
x: 1300,
y: 1200
}, {
x: 1700,
y: 1400
}, {
x: 2000,
y: 1600
}],
baseEnemies: [{
type: 'flying',
x: 500,
y: 1400
}, {
type: 'stickman',
x: 1000,
y: 1250
}, {
type: 'flying',
x: 1500,
y: 1300
}]
},
// Level 3 theme - Desert Ruins
{
name: "Desert Ruins",
backgroundColor: 0xDEB887,
musicTrack: 'menu_music',
basePlatforms: [{
x: 200,
y: 1900
}, {
x: 500,
y: 1700
}, {
x: 800,
y: 1400
}, {
x: 1200,
y: 1600
}, {
x: 1550,
y: 1300
}, {
x: 1900,
y: 1700
}],
baseEnemies: [{
type: 'stickman',
x: 600,
y: 1650
}, {
type: 'flying',
x: 1000,
y: 1300
}, {
type: 'stickman',
x: 1400,
y: 1250
}, {
type: 'flying',
x: 1750,
y: 1600
}]
}];
var config = baseConfigs[levelIndex];
var scaledConfig = {
name: config.name + " " + (cycleNumber + 1),
backgroundColor: config.backgroundColor,
musicTrack: config.musicTrack,
platforms: [],
enemies: []
};
// Scale platforms and add variety
for (var i = 0; i < config.basePlatforms.length; i++) {
var platform = config.basePlatforms[i];
scaledConfig.platforms.push({
x: platform.x + (cycleNumber * 50 - 25),
y: platform.y - cycleNumber * 30
});
}
// Add more platforms for higher cycles
if (cycleNumber > 0) {
scaledConfig.platforms.push({
x: 250 + cycleNumber * 100,
y: 2350 - cycleNumber * 40
});
scaledConfig.platforms.push({
x: 1750 - cycleNumber * 100,
y: 2150 - cycleNumber * 20
});
}
// Scale enemies and add more for difficulty
for (var i = 0; i < config.baseEnemies.length; i++) {
var enemy = config.baseEnemies[i];
scaledConfig.enemies.push({
type: enemy.type,
x: enemy.x + (cycleNumber * 30 - 15),
y: enemy.y - cycleNumber * 30
});
}
// Add extra enemies for higher cycles
var extraEnemies = Math.min(cycleNumber, 3);
for (var i = 0; i < extraEnemies; i++) {
var enemyType = Math.random() < 0.6 ? 'stickman' : 'flying';
scaledConfig.enemies.push({
type: enemyType,
x: 300 + i * 400 + Math.random() * 200,
y: 2300 - Math.random() * 200
});
}
return scaledConfig;
}
function getBossLevel(cycleNumber) {
var bossTypes = [
// Boss 1 - Purple Giant
{
name: "Purple Giant's Lair",
backgroundColor: 0x8B0000,
musicTrack: 'boss_music',
bossType: 'boss',
platforms: [{
x: 600,
y: 1700
}, {
x: 1024,
y: 1800
}, {
x: 1600,
y: 1700
}],
minions: [{
type: 'stickman',
x: 700,
y: 1650
}, {
type: 'stickman',
x: 1500,
y: 1650
}]
},
// Boss 2 - Shadow Lord
{
name: "Shadow Lord's Domain",
backgroundColor: 0x2F2F2F,
musicTrack: 'boss_music',
bossType: 'shadowBoss',
platforms: [{
x: 400,
y: 1600
}, {
x: 800,
y: 1400
}, {
x: 1200,
y: 1500
}, {
x: 1600,
y: 1300
}, {
x: 1900,
y: 1700
}],
minions: [{
type: 'flying',
x: 600,
y: 1300
}, {
type: 'flying',
x: 1400,
y: 1200
}, {
type: 'stickman',
x: 1000,
y: 1350
}]
},
// Boss 3 - Fire Demon
{
name: "Fire Demon's Inferno",
backgroundColor: 0xFF4500,
musicTrack: 'boss_music',
bossType: 'fireBoss',
platforms: [{
x: 500,
y: 1800
}, {
x: 900,
y: 1600
}, {
x: 1300,
y: 1400
}, {
x: 1700,
y: 1700
}],
minions: [{
type: 'flying',
x: 700,
y: 1500
}, {
type: 'stickman',
x: 1100,
y: 1350
}, {
type: 'flying',
x: 1500,
y: 1600
}, {
type: 'stickman',
x: 1900,
y: 1650
}]
}];
var bossIndex = cycleNumber % bossTypes.length;
var bossConfig = bossTypes[bossIndex];
return {
name: bossConfig.name + " " + (cycleNumber + 1),
backgroundColor: bossConfig.backgroundColor,
musicTrack: bossConfig.musicTrack,
platforms: bossConfig.platforms,
enemies: [{
type: bossConfig.bossType,
x: 1024,
y: 2450
}].concat(bossConfig.minions)
};
}
// Game arrays
var platforms = [];
var enemies = [];
var projectiles = [];
var arrows = [];
// UI Elements
var menuContainer, mapSelectContainer, settingsContainer;
// Game objects (will be created when starting game)
var ground, player, staminaBarBackground, staminaBar;
function createMenu() {
menuContainer = new Container();
game.addChild(menuContainer);
// Background decorative elements
// Create background warrior silhouettes
for (var i = 0; i < 8; i++) {
var bgWarrior = LK.getAsset('warrior', {
width: 60,
height: 70,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1,
tint: 0x444444
});
bgWarrior.x = 200 + i * 200 + Math.random() * 100;
bgWarrior.y = 800 + Math.random() * 400;
bgWarrior.rotation = Math.random() * Math.PI * 2;
menuContainer.addChild(bgWarrior);
}
// Animated floating swords
for (var i = 0; i < 5; i++) {
var bgSword = LK.getAsset('sword', {
width: 40,
height: 40,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.15,
tint: 0x666666
});
bgSword.x = 300 + i * 300 + Math.random() * 100;
bgSword.y = 600 + Math.random() * 300;
bgSword.startRotation = Math.random() * Math.PI * 2;
bgSword.rotationSpeed = (Math.random() - 0.5) * 0.02;
menuContainer.addChild(bgSword);
// Animate floating swords
tween(bgSword, {
y: bgSword.y - 50,
alpha: 0.05
}, {
duration: 3000 + Math.random() * 2000,
easing: tween.easeInOut,
repeat: -1,
yoyo: true
});
}
// Decorative border elements
var topBorder = LK.getAsset('platform', {
width: 2048,
height: 8,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
topBorder.x = 1024;
topBorder.y = 100;
menuContainer.addChild(topBorder);
var bottomBorder = LK.getAsset('platform', {
width: 2048,
height: 8,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
bottomBorder.x = 1024;
bottomBorder.y = 1300;
menuContainer.addChild(bottomBorder);
// Title with enhanced styling
var titleShadow = new Text2('STICKMAN WARRIOR', {
size: 70,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 1028;
titleShadow.y = 404;
menuContainer.addChild(titleShadow);
var title = new Text2('STICKMAN WARRIOR', {
size: 70,
fill: 0xFFD700
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 400;
menuContainer.addChild(title);
// Subtitle
var subtitle = new Text2('EPIC BATTLES AWAIT', {
size: 28,
fill: 0xCCCCCC
});
subtitle.anchor.set(0.5, 0.5);
subtitle.x = 1024;
subtitle.y = 480;
menuContainer.addChild(subtitle);
// Animated title glow effect
var glowCycle = 0;
var titleGlowInterval = LK.setInterval(function () {
glowCycle += 0.05;
var glow = 0.8 + Math.sin(glowCycle) * 0.2;
title.alpha = glow;
}, 50);
// Play button with enhanced styling - larger for Android phones
var playButtonShadow = LK.getAsset('platform', {
width: 408,
height: 128,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000
});
playButtonShadow.x = 1028;
playButtonShadow.y = 604;
menuContainer.addChild(playButtonShadow);
var playButton = LK.getAsset('platform', {
width: 400,
height: 120,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x4444FF
});
playButton.x = 1024;
playButton.y = 600;
playButton.interactive = true;
playButton.buttonMode = true;
menuContainer.addChild(playButton);
var playButtonBorder = LK.getAsset('platform', {
width: 408,
height: 128,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700,
alpha: 0.3
});
playButtonBorder.x = 1024;
playButtonBorder.y = 600;
menuContainer.addChild(playButtonBorder);
var playText = new Text2('PLAY', {
size: 45,
fill: 0xFFFFFF
});
playText.anchor.set(0.5, 0.5);
playText.x = playButton.x;
playText.y = playButton.y;
menuContainer.addChild(playText);
// Add button glow animation
var playButtonGlow = 0;
var playGlowInterval = LK.setInterval(function () {
playButtonGlow += 0.03;
var glowAlpha = 0.3 + Math.sin(playButtonGlow) * 0.1;
playButtonBorder.alpha = glowAlpha;
}, 50);
playButton.down = function () {
LK.getSound('button_click').play();
// Always start at level 1 for the Play button
currentLevel = 1;
storage.currentLevel = currentLevel;
gameState = 'mapSelect';
menuContainer.visible = false;
createMapSelect();
};
// Settings button with enhanced styling - larger for Android phones
var settingsButtonShadow = LK.getAsset('platform', {
width: 408,
height: 128,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000
});
settingsButtonShadow.x = 1028;
settingsButtonShadow.y = 784;
menuContainer.addChild(settingsButtonShadow);
var settingsButton = LK.getAsset('platform', {
width: 400,
height: 120,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44FF44
});
settingsButton.x = 1024;
settingsButton.y = 780;
settingsButton.interactive = true;
settingsButton.buttonMode = true;
menuContainer.addChild(settingsButton);
var settingsButtonBorder = LK.getAsset('platform', {
width: 408,
height: 128,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x88FF88,
alpha: 0.3
});
settingsButtonBorder.x = 1024;
settingsButtonBorder.y = 780;
menuContainer.addChild(settingsButtonBorder);
var settingsText = new Text2('SETTINGS', {
size: 45,
fill: 0xFFFFFF
});
settingsText.anchor.set(0.5, 0.5);
settingsText.x = settingsButton.x;
settingsText.y = settingsButton.y;
menuContainer.addChild(settingsText);
settingsButton.down = function () {
LK.getSound('button_click').play();
gameState = 'settings';
menuContainer.visible = false;
createSettings();
};
// Shop button with enhanced styling - repositioned for better layout
var shopButtonShadow = LK.getAsset('platform', {
width: 308,
height: 88,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000
});
shopButtonShadow.x = 604; // Better positioning
shopButtonShadow.y = 704;
menuContainer.addChild(shopButtonShadow);
var shopButton = LK.getAsset('platform', {
width: 300,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
shopButton.x = 600; // Better positioning
shopButton.y = 700;
shopButton.interactive = true;
shopButton.buttonMode = true;
menuContainer.addChild(shopButton);
var shopButtonBorder = LK.getAsset('platform', {
width: 308,
height: 88,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFFF88,
alpha: 0.4
});
shopButtonBorder.x = 600;
shopButtonBorder.y = 700;
menuContainer.addChild(shopButtonBorder);
var shopText = new Text2('SHOP', {
size: 36,
fill: 0x000000
});
shopText.anchor.set(0.5, 0.5);
shopText.x = shopButton.x;
shopText.y = shopButton.y;
menuContainer.addChild(shopText);
// Add map select button for better navigation
var mapButtonShadow = LK.getAsset('platform', {
width: 308,
height: 88,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000
});
mapButtonShadow.x = 1444;
mapButtonShadow.y = 704;
menuContainer.addChild(mapButtonShadow);
var mapButton = LK.getAsset('platform', {
width: 300,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF8844
});
mapButton.x = 1440;
mapButton.y = 700;
mapButton.interactive = true;
mapButton.buttonMode = true;
menuContainer.addChild(mapButton);
var mapButtonBorder = LK.getAsset('platform', {
width: 308,
height: 88,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFAA66,
alpha: 0.4
});
mapButtonBorder.x = 1440;
mapButtonBorder.y = 700;
menuContainer.addChild(mapButtonBorder);
var mapText = new Text2('LEVELS', {
size: 32,
fill: 0xFFFFFF
});
mapText.anchor.set(0.5, 0.5);
mapText.x = mapButton.x;
mapText.y = mapButton.y;
menuContainer.addChild(mapText);
mapButton.down = function () {
LK.getSound('button_click').play();
gameState = 'mapSelect';
menuContainer.visible = false;
createMapSelect();
};
shopButton.down = function () {
LK.getSound('button_click').play();
gameState = 'shop';
menuContainer.visible = false;
createShop();
};
// Statistics panel background
var statsPanel = LK.getAsset('platform', {
width: 600,
height: 280,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x1a1a1a,
alpha: 0.8
});
statsPanel.x = 1024;
statsPanel.y = 1000;
menuContainer.addChild(statsPanel);
// Statistics title
var statsTitle = new Text2('STATISTICS', {
size: 36,
fill: 0xFFD700
});
statsTitle.anchor.set(0.5, 0.5);
statsTitle.x = 1024;
statsTitle.y = 880;
menuContainer.addChild(statsTitle);
// High score display with enhanced styling
var highScoreLabel = new Text2('HIGH SCORE', {
size: 24,
fill: 0xCCCCCC
});
highScoreLabel.anchor.set(0.5, 0.5);
highScoreLabel.x = 1024;
highScoreLabel.y = 920;
menuContainer.addChild(highScoreLabel);
var highScoreText = new Text2(storage.highScore.toString(), {
size: 32,
fill: 0xFFD700
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 1024;
highScoreText.y = 950;
menuContainer.addChild(highScoreText);
// Levels completed display
var levelsLabel = new Text2('LEVELS UNLOCKED', {
size: 24,
fill: 0xCCCCCC
});
levelsLabel.anchor.set(0.5, 0.5);
levelsLabel.x = 1024;
levelsLabel.y = 990;
menuContainer.addChild(levelsLabel);
var levelsText = new Text2(storage.unlockedLevels.toString(), {
size: 32,
fill: 0x44FF44
});
levelsText.anchor.set(0.5, 0.5);
levelsText.x = 1024;
levelsText.y = 1020;
menuContainer.addChild(levelsText);
// Coins display
var coinsLabel = new Text2('COINS EARNED', {
size: 24,
fill: 0xCCCCCC
});
coinsLabel.anchor.set(0.5, 0.5);
coinsLabel.x = 1024;
coinsLabel.y = 1060;
menuContainer.addChild(coinsLabel);
var coinsText = new Text2(storage.coins.toString(), {
size: 32,
fill: 0xFFD700
});
coinsText.anchor.set(0.5, 0.5);
coinsText.x = 1024;
coinsText.y = 1090;
menuContainer.addChild(coinsText);
// Current skin display
var skinLabel = new Text2('CURRENT SKIN', {
size: 24,
fill: 0xCCCCCC
});
skinLabel.anchor.set(0.5, 0.5);
skinLabel.x = 1024;
skinLabel.y = 1130;
menuContainer.addChild(skinLabel);
var skinName = storage.selectedSkin.charAt(0).toUpperCase() + storage.selectedSkin.slice(1);
var skinText = new Text2(skinName, {
size: 28,
fill: 0x8844FF
});
skinText.anchor.set(0.5, 0.5);
skinText.x = 1024;
skinText.y = 1160;
menuContainer.addChild(skinText);
// Version and credits
var versionText = new Text2('v1.0 - Made with LK Engine', {
size: 20,
fill: 0x666666
});
versionText.anchor.set(0.5, 0.5);
versionText.x = 1024;
versionText.y = 1220;
menuContainer.addChild(versionText);
// Animated background particles
for (var i = 0; i < 15; i++) {
var particle = LK.getAsset('bullet', {
width: 4,
height: 4,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
tint: 0xFFD700
});
particle.x = Math.random() * 2048;
particle.y = Math.random() * 1400;
particle.velocityX = (Math.random() - 0.5) * 2;
particle.velocityY = (Math.random() - 0.5) * 2;
menuContainer.addChild(particle);
// Animate particles
var _animateParticle = function animateParticle(p) {
tween(p, {
x: p.x + p.velocityX * 100,
y: p.y + p.velocityY * 100,
alpha: 0.1
}, {
duration: 2000 + Math.random() * 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
p.x = Math.random() * 2048;
p.y = Math.random() * 1400;
p.alpha = 0.3;
_animateParticle(p);
}
});
};
_animateParticle(particle);
}
// Animated menu background warrior showcase
var showcaseWarrior = LK.getAsset('warrior', {
width: 200,
height: 240,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.4,
tint: 0x4444FF
});
showcaseWarrior.x = 1600;
showcaseWarrior.y = 700;
menuContainer.addChild(showcaseWarrior);
// Animate showcase warrior
var _breatheAnimation = function breatheAnimation() {
tween(showcaseWarrior, {
scaleY: 1.05,
y: showcaseWarrior.y - 10
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(showcaseWarrior, {
scaleY: 1.0,
y: showcaseWarrior.y + 10
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: _breatheAnimation
});
}
});
};
_breatheAnimation();
}
function createMapSelect() {
// Hide all other containers first
if (menuContainer) menuContainer.visible = false;
if (settingsContainer) settingsContainer.visible = false;
if (mapSelectContainer) mapSelectContainer.destroy();
mapSelectContainer = new Container();
game.addChild(mapSelectContainer);
// Title
var title = new Text2('SELECT LEVEL', {
size: 55,
fill: 0xFFFFFF
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 250;
mapSelectContainer.addChild(title);
// Show current progress
var progressText = new Text2('Progress: ' + storage.unlockedLevels + ' levels unlocked', {
size: 32,
fill: 0xFFD700
});
progressText.anchor.set(0.5, 0.5);
progressText.x = 1024;
progressText.y = 350;
mapSelectContainer.addChild(progressText);
// Current level display
var levelText = new Text2('Current Level: ' + currentLevel, {
size: 36,
fill: 0xFFD700
});
levelText.anchor.set(0.5, 0.5);
levelText.x = 1024;
levelText.y = 430;
mapSelectContainer.addChild(levelText);
// Check if current level is unlocked
var isLevelUnlocked = currentLevel <= storage.unlockedLevels;
if (isLevelUnlocked) {
// Level preview for unlocked levels
var previewConfig = getCurrentLevelConfig();
var previewText = new Text2(previewConfig.name, {
size: 32,
fill: 0xFFFFFF
});
previewText.anchor.set(0.5, 0.5);
previewText.x = 1024;
previewText.y = 520;
mapSelectContainer.addChild(previewText);
// Level type indicator
var levelType = (currentLevel - 1) % 4 === 3 ? 'BOSS LEVEL!' : 'Regular Level';
var typeText = new Text2(levelType, {
size: 28,
fill: (currentLevel - 1) % 4 === 3 ? 0xFF4444 : 0x44FF44
});
typeText.anchor.set(0.5, 0.5);
typeText.x = 1024;
typeText.y = 590;
mapSelectContainer.addChild(typeText);
} else {
// Show locked level message
var lockedText = new Text2('LEVEL LOCKED', {
size: 36,
fill: 0xFF4444
});
lockedText.anchor.set(0.5, 0.5);
lockedText.x = 1024;
lockedText.y = 520;
mapSelectContainer.addChild(lockedText);
// Show pixel-style lock
var lockGraphic = LK.getAsset('lock', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x888888
});
lockGraphic.x = 1024;
lockGraphic.y = 610;
mapSelectContainer.addChild(lockGraphic);
// Show unlock requirement
var requirementText = new Text2('Complete Level ' + (currentLevel - 1) + ' to unlock', {
size: 24,
fill: 0xCCCCCC
});
requirementText.anchor.set(0.5, 0.5);
requirementText.x = 1024;
requirementText.y = 700;
mapSelectContainer.addChild(requirementText);
}
// Play current level button (only interactive if unlocked)
var playButton = LK.getAsset('platform', {
width: 320,
height: 90,
anchorX: 0.5,
anchorY: 0.5,
tint: isLevelUnlocked ? getCurrentLevelConfig().backgroundColor || 0x4444FF : 0x666666
});
playButton.x = 1024;
playButton.y = 820;
playButton.interactive = isLevelUnlocked;
playButton.buttonMode = isLevelUnlocked;
mapSelectContainer.addChild(playButton);
var playText = new Text2(isLevelUnlocked ? 'PLAY LEVEL ' + currentLevel : 'LOCKED', {
size: 28,
fill: isLevelUnlocked ? 0xFFFFFF : 0x888888
});
playText.anchor.set(0.5, 0.5);
playText.x = playButton.x;
playText.y = playButton.y;
mapSelectContainer.addChild(playText);
if (isLevelUnlocked) {
playButton.down = function () {
LK.getSound('button_click').play();
startGame();
};
}
// Level navigation buttons
if (currentLevel > 1) {
var prevButton = LK.getAsset('platform', {
width: 150,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x666666
});
prevButton.x = 700;
prevButton.y = 850;
prevButton.interactive = true;
prevButton.buttonMode = true;
mapSelectContainer.addChild(prevButton);
var prevText = new Text2('PREV', {
size: 25,
fill: 0xFFFFFF
});
prevText.anchor.set(0.5, 0.5);
prevText.x = prevButton.x;
prevText.y = prevButton.y;
mapSelectContainer.addChild(prevText);
prevButton.down = function () {
LK.getSound('button_click').play();
currentLevel = Math.max(1, currentLevel - 1);
storage.currentLevel = currentLevel;
mapSelectContainer.visible = false;
createMapSelect();
};
}
// Next level button (only if unlocked)
if (currentLevel < storage.unlockedLevels) {
var nextButton = LK.getAsset('platform', {
width: 150,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x666666
});
nextButton.x = 1348;
nextButton.y = 850;
nextButton.interactive = true;
nextButton.buttonMode = true;
mapSelectContainer.addChild(nextButton);
var nextText = new Text2('NEXT', {
size: 25,
fill: 0xFFFFFF
});
nextText.anchor.set(0.5, 0.5);
nextText.x = nextButton.x;
nextText.y = nextButton.y;
mapSelectContainer.addChild(nextText);
nextButton.down = function () {
LK.getSound('button_click').play();
currentLevel++;
storage.currentLevel = currentLevel;
mapSelectContainer.visible = false;
createMapSelect();
};
}
// Back button
var backButton = LK.getAsset('platform', {
width: 220,
height: 70,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
backButton.x = 1024;
backButton.y = 980;
backButton.interactive = true;
backButton.buttonMode = true;
mapSelectContainer.addChild(backButton);
var backText = new Text2('BACK', {
size: 28,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = backButton.x;
backText.y = backButton.y;
mapSelectContainer.addChild(backText);
backButton.down = function () {
LK.getSound('button_click').play();
gameState = 'menu';
if (mapSelectContainer) mapSelectContainer.destroy();
if (shopContainer) shopContainer.visible = false;
if (!menuContainer) createMenu();
menuContainer.visible = true;
};
}
function createShop() {
// Hide all other containers first
if (menuContainer) menuContainer.visible = false;
if (mapSelectContainer) mapSelectContainer.visible = false;
if (settingsContainer) settingsContainer.visible = false;
if (shopContainer) shopContainer.destroy();
shopContainer = new Container();
game.addChild(shopContainer);
// Enhanced background decorations
for (var i = 0; i < 12; i++) {
var bgDecor = LK.getAsset('warrior', {
width: 40,
height: 50,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.05,
tint: 0x333333
});
bgDecor.x = 100 + i * 150 + Math.random() * 80;
bgDecor.y = 300 + Math.random() * 800;
bgDecor.rotation = Math.random() * Math.PI * 2;
shopContainer.addChild(bgDecor);
}
// Decorative border frame
var topBorder = LK.getAsset('platform', {
width: 1900,
height: 12,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
topBorder.x = 1024;
topBorder.y = 120;
shopContainer.addChild(topBorder);
var bottomBorder = LK.getAsset('platform', {
width: 1900,
height: 12,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
bottomBorder.x = 1024;
bottomBorder.y = 1150;
shopContainer.addChild(bottomBorder);
// Left and right decorative borders
var leftBorder = LK.getAsset('platform', {
width: 12,
height: 1000,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
leftBorder.x = 80;
leftBorder.y = 635;
shopContainer.addChild(leftBorder);
var rightBorder = LK.getAsset('platform', {
width: 12,
height: 1000,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
rightBorder.x = 1968;
rightBorder.y = 635;
shopContainer.addChild(rightBorder);
// Title - dynamic based on current category with enhanced styling
var categoryTitles = {
guardior: 'GUARDIOR SHOP',
enemies: 'ENEMY SKINS',
bosses: 'BOSS SKINS',
flying: 'FLYING ENEMY SKINS',
voces: 'VOICE SKINS'
};
// Title shadow for depth
var titleShadow = new Text2(categoryTitles[currentShopCategory] || 'SHOP', {
size: 64,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 1028;
titleShadow.y = 204;
shopContainer.addChild(titleShadow);
var title = new Text2(categoryTitles[currentShopCategory] || 'SHOP', {
size: 60,
fill: 0xFFD700
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 200;
shopContainer.addChild(title);
// Animated title glow effect
var titleGlowCycle = 0;
var titleGlowInterval = LK.setInterval(function () {
titleGlowCycle += 0.03;
var glow = 0.9 + Math.sin(titleGlowCycle) * 0.1;
title.alpha = glow;
}, 60);
// Enhanced coins display with icon and styling
var coinsBg = LK.getAsset('platform', {
width: 400,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x1a1a1a,
alpha: 0.8
});
coinsBg.x = 1024;
coinsBg.y = 280;
shopContainer.addChild(coinsBg);
var coinsBorder = LK.getAsset('platform', {
width: 408,
height: 88,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700,
alpha: 0.3
});
coinsBorder.x = 1024;
coinsBorder.y = 280;
shopContainer.addChild(coinsBorder);
// Coin icon
var coinIcon = LK.getAsset('platform', {
width: 32,
height: 32,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
coinIcon.x = 900;
coinIcon.y = 280;
shopContainer.addChild(coinIcon);
var coinsText = new Text2('Coins: ' + storage.coins, {
size: 42,
fill: 0xFFD700
});
coinsText.anchor.set(0.5, 0.5);
coinsText.x = 1024;
coinsText.y = 280;
shopContainer.addChild(coinsText);
// Animated coin counter effect
var coinsGlowCycle = 0;
var coinsGlowInterval = LK.setInterval(function () {
coinsGlowCycle += 0.05;
var glow = 0.8 + Math.sin(coinsGlowCycle) * 0.2;
coinIcon.alpha = glow;
coinsBorder.alpha = 0.3 + Math.sin(coinsGlowCycle * 1.2) * 0.1;
}, 50);
// Shop category tabs with enhanced styling - use global variable to maintain state
if (typeof currentShopCategory === 'undefined') {
var currentShopCategory = 'guardior'; // Initialize if not already set
}
// Tab container background
var tabsBackground = LK.getAsset('platform', {
width: 900,
height: 120,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x1a1a1a,
alpha: 0.7
});
tabsBackground.x = 1024;
tabsBackground.y = 360;
shopContainer.addChild(tabsBackground);
// Enhanced tab styling with shadows and borders
var tabData = [{
key: 'guardior',
text: 'GUARDIOR',
x: 450,
width: 180
}, {
key: 'enemies',
text: 'ENEMIES',
x: 650,
width: 160
}, {
key: 'bosses',
text: 'BOSSES',
x: 830,
width: 140
}, {
key: 'flying',
text: 'FLYING',
x: 990,
width: 120
}, {
key: 'voces',
text: 'VOCES',
x: 1130,
width: 120
}];
var tabButtons = {};
var tabTexts = {};
for (var t = 0; t < tabData.length; t++) {
var tab = tabData[t];
var isActive = currentShopCategory === tab.key;
// Tab shadow
var tabShadow = LK.getAsset('platform', {
width: tab.width + 8,
height: 68,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000,
alpha: 0.5
});
tabShadow.x = tab.x + 3;
tabShadow.y = 363;
shopContainer.addChild(tabShadow);
// Tab background
var tabButton = LK.getAsset('platform', {
width: tab.width,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: isActive ? 0x44FF44 : 0x555555
});
tabButton.x = tab.x;
tabButton.y = 360;
tabButton.interactive = true;
tabButton.buttonMode = true;
shopContainer.addChild(tabButton);
// Tab border glow for active tab
if (isActive) {
var tabGlow = LK.getAsset('platform', {
width: tab.width + 8,
height: 68,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x88FF88,
alpha: 0.4
});
tabGlow.x = tab.x;
tabGlow.y = 360;
shopContainer.addChild(tabGlow);
}
// Tab text
var tabText = new Text2(tab.text, {
size: tab.width > 150 ? 24 : 20,
fill: isActive ? 0x000000 : 0xFFFFFF
});
tabText.anchor.set(0.5, 0.5);
tabText.x = tab.x;
tabText.y = 360;
shopContainer.addChild(tabText);
// Store references for event handlers
tabButtons[tab.key] = tabButton;
tabTexts[tab.key] = tabText;
// Add hover effects
(function (button, text, key, originalTint) {
button.down = function () {
LK.getSound('button_click').play();
currentShopCategory = key;
createShop();
};
})(tabButton, tabText, tab.key, isActive ? 0x44FF44 : 0x555555);
}
// Create legacy tab variables for compatibility with existing event handlers
var guardiorTab = tabButtons.guardior;
var enemyTab = tabButtons.enemies;
var bossTab = tabButtons.bosses;
var flyingTab = tabButtons.flying;
var voicesTab = tabButtons.voces;
// Tab event handlers
guardiorTab.down = function () {
LK.getSound('button_click').play();
currentShopCategory = 'guardior';
createShop();
};
enemyTab.down = function () {
LK.getSound('button_click').play();
currentShopCategory = 'enemies';
createShop();
};
bossTab.down = function () {
LK.getSound('button_click').play();
currentShopCategory = 'bosses';
createShop();
};
flyingTab.down = function () {
LK.getSound('button_click').play();
currentShopCategory = 'flying';
createShop();
};
voicesTab.down = function () {
LK.getSound('button_click').play();
currentShopCategory = 'voces';
createShop();
};
// Define available skins first
var skins = [{
id: 'default',
name: 'Classic Guardior',
price: 0,
color: 0xFFFFFF,
description: 'The original warrior'
}, {
id: 'red',
name: 'Fire Warrior',
price: 100,
color: 0xFF4444,
description: 'Blazing red armor'
}, {
id: 'blue',
name: 'Ice Knight',
price: 150,
color: 0x4444FF,
description: 'Frozen blue steel'
}, {
id: 'green',
name: 'Forest Guardian',
price: 200,
color: 0x44FF44,
description: 'Nature\'s protector'
}, {
id: 'purple',
name: 'Shadow Lord',
price: 300,
color: 0x8844FF,
description: 'Master of darkness'
}, {
id: 'gold',
name: 'Golden Hero',
price: 500,
color: 0xFFD700,
description: 'Legendary champion'
}, {
id: 'orange',
name: 'Sunset Warrior',
price: 120,
color: 0xFF8844,
description: 'Strength of dusk'
}, {
id: 'cyan',
name: 'Ocean Guardian',
price: 180,
color: 0x44FFFF,
description: 'Master of tides'
}, {
id: 'magenta',
name: 'Mystic Knight',
price: 250,
color: 0xFF44FF,
description: 'Arcane protector'
}, {
id: 'silver',
name: 'Steel Champion',
price: 350,
color: 0xC0C0C0,
description: 'Unbreakable will'
}, {
id: 'lime',
name: 'Toxic Avenger',
price: 280,
color: 0x88FF44,
description: 'Venomous fighter'
}, {
id: 'bronze',
name: 'Ancient Warrior',
price: 400,
color: 0xCD7F32,
description: 'Timeless legend'
}];
// Define skin categories
var skinCategories = {
guardior: skins,
enemies: [{
id: 'default',
name: 'Red Warrior',
price: 0,
color: 0xFF0000,
description: 'Standard enemy'
}, {
id: 'dark',
name: 'Shadow Fighter',
price: 80,
color: 0x444444,
description: 'Dark assassin'
}, {
id: 'green',
name: 'Forest Bandit',
price: 120,
color: 0x228B22,
description: 'Woodland raider'
}, {
id: 'purple',
name: 'Void Warrior',
price: 150,
color: 0x8B008B,
description: 'Corrupted fighter'
}, {
id: 'orange',
name: 'Fire Soldier',
price: 100,
color: 0xFF6347,
description: 'Burning berserker'
}, {
id: 'blue',
name: 'Ice Guardian',
price: 140,
color: 0x4169E1,
description: 'Frozen sentinel'
}],
bosses: [{
id: 'default',
name: 'Purple Giant',
price: 0,
color: 0x800080,
description: 'Original boss'
}, {
id: 'crimson',
name: 'Crimson Overlord',
price: 200,
color: 0xDC143C,
description: 'Blood tyrant'
}, {
id: 'shadow',
name: 'Dark Emperor',
price: 300,
color: 0x2F2F2F,
description: 'Shadow master'
}, {
id: 'golden',
name: 'Golden Titan',
price: 400,
color: 0xFFD700,
description: 'Legendary ruler'
}, {
id: 'emerald',
name: 'Emerald Colossus',
price: 350,
color: 0x50C878,
description: 'Nature\'s wrath'
}, {
id: 'cyber',
name: 'Cyber Destroyer',
price: 450,
color: 0x00CED1,
description: 'Tech nightmare'
}, {
id: 'inferno',
name: 'Inferno Lord',
price: 500,
color: 0xFF4500,
description: 'Master of flames'
}, {
id: 'frost',
name: 'Frost Monarch',
price: 480,
color: 0x87CEEB,
description: 'Ruler of ice'
}, {
id: 'void',
name: 'Void Emperor',
price: 520,
color: 0x1C1C1C,
description: 'Lord of nothingness'
}],
flying: [{
id: 'default',
name: 'Brown Flyer',
price: 0,
color: 0x8B4513,
description: 'Standard flying enemy'
}, {
id: 'black',
name: 'Night Flyer',
price: 90,
color: 0x2F2F2F,
description: 'Shadow wings'
}, {
id: 'silver',
name: 'Steel Wing',
price: 130,
color: 0xC0C0C0,
description: 'Metal predator'
}, {
id: 'golden',
name: 'Golden Eagle',
price: 180,
color: 0xFFD700,
description: 'Sky sovereign'
}, {
id: 'red',
name: 'Fire Bird',
price: 110,
color: 0xFF4500,
description: 'Burning flyer'
}, {
id: 'blue',
name: 'Storm Wing',
price: 160,
color: 0x1E90FF,
description: 'Lightning fast'
}, {
id: 'toxic',
name: 'Toxic Wasp',
price: 140,
color: 0x9ACD32,
description: 'Poisonous hunter'
}, {
id: 'crystal',
name: 'Crystal Flyer',
price: 200,
color: 0xE6E6FA,
description: 'Crystalline predator'
}, {
id: 'shadow',
name: 'Shadow Bat',
price: 170,
color: 0x404040,
description: 'Darkness incarnate'
}],
voces: [{
id: 'default',
name: 'Classic Voice',
price: 0,
color: 0xFFFFFF,
description: 'Original warrior voice'
}, {
id: 'deep',
name: 'Deep Voice',
price: 80,
color: 0x4169E1,
description: 'Commanding tone'
}, {
id: 'heroic',
name: 'Heroic Voice',
price: 120,
color: 0xFFD700,
description: 'Noble warrior'
}, {
id: 'mysterious',
name: 'Mysterious Voice',
price: 150,
color: 0x8B008B,
description: 'Enigmatic whispers'
}, {
id: 'robotic',
name: 'Robotic Voice',
price: 100,
color: 0x00CED1,
description: 'Cyber warrior'
}, {
id: 'ancient',
name: 'Ancient Voice',
price: 180,
color: 0xCD7F32,
description: 'Wise elder'
}]
};
var currentSkins = skinCategories[currentShopCategory];
var storageKeys = {
guardior: {
selected: 'selectedSkin',
owned: 'ownedSkins'
},
enemies: {
selected: 'selectedEnemySkin',
owned: 'ownedEnemySkins'
},
bosses: {
selected: 'selectedBossSkin',
owned: 'ownedBossSkins'
},
flying: {
selected: 'selectedFlyingSkin',
owned: 'ownedFlyingSkins'
},
voces: {
selected: 'selectedVoiceSkin',
owned: 'ownedVoiceSkins'
}
};
// Create organized skins display container with proper bounds
var skinsDisplayArea = new Container();
shopContainer.addChild(skinsDisplayArea);
// Create background area for skins display
var skinsAreaBg = LK.getAsset('platform', {
width: 1600,
height: 600,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x1a1a1a,
alpha: 0.3
});
skinsAreaBg.x = 1024;
skinsAreaBg.y = 700;
skinsDisplayArea.addChild(skinsAreaBg);
// Create skins grid container with proper organization
var skinsGrid = new Container();
skinsDisplayArea.addChild(skinsGrid);
// Enhanced grid layout with better organization
var itemsPerRow = 4; // Better fit for screen width
var itemWidth = 220; // Slightly smaller for better fit
var itemHeight = 180; // Compact height
var spacingX = 60;
var spacingY = 40;
var startX = 300; // Start position for grid
var startY = 500;
var maxRows = 3; // Limit visible rows
// Calculate grid bounds for container organization
var gridWidth = itemsPerRow * (itemWidth + spacingX) - spacingX;
var gridHeight = maxRows * (itemHeight + spacingY) - spacingY;
// Center the grid within the display area
var gridStartX = 1024 - gridWidth / 2 + itemWidth / 2;
var gridStartY = 550;
for (var i = 0; i < currentSkins.length; i++) {
var skin = currentSkins[i];
var row = Math.floor(i / itemsPerRow);
var col = i % itemsPerRow;
var itemX = gridStartX + col * (itemWidth + spacingX);
var itemY = gridStartY + row * (itemHeight + spacingY);
// Only show skins that fit in visible area
if (row >= maxRows) {
continue; // Skip items that don't fit in visible area
}
var isOwned = storage[storageKeys[currentShopCategory].owned].indexOf(skin.id) !== -1;
var isEquipped = storage[storageKeys[currentShopCategory].selected] === skin.id;
// Create individual skin container for better organization
var skinItemContainer = new Container();
skinsGrid.addChild(skinItemContainer);
// Enhanced skin preview background with shadow and depth
var skinShadow = LK.getAsset('platform', {
width: itemWidth + 8,
height: itemHeight + 8,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000,
alpha: 0.4
});
skinShadow.x = itemX + 4;
skinShadow.y = itemY + 4;
skinItemContainer.addChild(skinShadow);
// Main skin background with enhanced colors and better organization
var bgColor = isEquipped ? 0x44FF44 : isOwned ? 0x4444FF : 0x555555;
var skinBg = LK.getAsset('platform', {
width: itemWidth,
height: itemHeight,
anchorX: 0.5,
anchorY: 0.5,
tint: bgColor
});
skinBg.x = itemX;
skinBg.y = itemY;
skinBg.interactive = true;
skinBg.buttonMode = true;
skinItemContainer.addChild(skinBg);
// Enhanced border for special states with better visual hierarchy
if (isEquipped) {
var equippedBorder = LK.getAsset('platform', {
width: itemWidth + 12,
height: itemHeight + 12,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x88FF88,
alpha: 0.8
});
equippedBorder.x = itemX;
equippedBorder.y = itemY;
skinItemContainer.addChild(equippedBorder);
// Add equipped indicator
var equippedIcon = new Text2('★', {
size: 24,
fill: 0x00FF00
});
equippedIcon.anchor.set(0.5, 0.5);
equippedIcon.x = itemX + itemWidth / 2 - 20;
equippedIcon.y = itemY - itemHeight / 2 + 20;
skinItemContainer.addChild(equippedIcon);
} else if (isOwned) {
var ownedBorder = LK.getAsset('platform', {
width: itemWidth + 8,
height: itemHeight + 8,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x8888FF,
alpha: 0.6
});
ownedBorder.x = itemX;
ownedBorder.y = itemY;
skinItemContainer.addChild(ownedBorder);
// Add owned indicator
var ownedIcon = new Text2('✓', {
size: 20,
fill: 0xFFD700
});
ownedIcon.anchor.set(0.5, 0.5);
ownedIcon.x = itemX + itemWidth / 2 - 20;
ownedIcon.y = itemY - itemHeight / 2 + 20;
skinItemContainer.addChild(ownedIcon);
}
// Inner content area with better organization
var contentBg = LK.getAsset('platform', {
width: itemWidth - 20,
height: itemHeight - 20,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x2a2a2a,
alpha: 0.9
});
contentBg.x = itemX;
contentBg.y = itemY;
skinItemContainer.addChild(contentBg);
// Enhanced skin preview with category-appropriate assets
var previewAssets = {
guardior: 'warrior',
enemies: 'enemy_body',
bosses: 'boss',
flying: 'enemy_body',
voces: 'warrior'
};
var previewSize = currentShopCategory === 'bosses' ? {
w: 70,
h: 80
} : {
w: 60,
h: 70
};
var skinPreview = LK.getAsset(previewAssets[currentShopCategory], {
width: previewSize.w,
height: previewSize.h,
anchorX: 0.5,
anchorY: 0.5,
tint: skin.color
});
skinPreview.x = itemX;
skinPreview.y = itemY - 25;
skinItemContainer.addChild(skinPreview);
// Skin name with better sizing and organization
var skinName = new Text2(skin.name, {
size: 18,
fill: 0xFFFFFF
});
skinName.anchor.set(0.5, 0.5);
skinName.x = itemX;
skinName.y = itemY + 20;
skinItemContainer.addChild(skinName);
// Enhanced description text with better fit
var skinDesc = new Text2(skin.description, {
size: 14,
fill: 0xCCCCCC
});
skinDesc.anchor.set(0.5, 0.5);
skinDesc.x = itemX;
skinDesc.y = itemY + 40;
skinItemContainer.addChild(skinDesc);
// Price or status with enhanced styling and better organization
var statusText;
if (isOwned) {
if (isEquipped) {
statusText = new Text2('EQUIPPED', {
size: 16,
fill: 0x00FF00
});
} else {
statusText = new Text2('OWNED', {
size: 16,
fill: 0xFFD700
});
}
} else {
var canAfford = storage.coins >= skin.price;
statusText = new Text2(skin.price + ' COINS', {
size: 16,
fill: canAfford ? 0xFFFFFF : 0xFF4444
});
// Add coin icon for price display with better positioning
if (!isOwned) {
var priceIcon = LK.getAsset('platform', {
width: 16,
height: 16,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
priceIcon.x = itemX - 30;
priceIcon.y = itemY + 60;
skinItemContainer.addChild(priceIcon);
}
}
statusText.anchor.set(0.5, 0.5);
statusText.x = itemX;
statusText.y = itemY + 60;
skinItemContainer.addChild(statusText);
// Add rarity indicator for expensive items with better organization
if (skin.price >= 300) {
var rarityBadge = LK.getAsset('platform', {
width: 35,
height: 18,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700
});
rarityBadge.x = itemX - itemWidth / 2 + 25;
rarityBadge.y = itemY - itemHeight / 2 + 15;
skinItemContainer.addChild(rarityBadge);
var rarityText = new Text2('RARE', {
size: 10,
fill: 0x000000
});
rarityText.anchor.set(0.5, 0.5);
rarityText.x = rarityBadge.x;
rarityText.y = rarityBadge.y;
skinItemContainer.addChild(rarityText);
}
// Create organized click handler with proper scope
(function (skinData, background, container) {
background.down = function () {
LK.getSound('button_click').play();
var owned = storage[storageKeys[currentShopCategory].owned].indexOf(skinData.id) !== -1;
if (owned) {
// Equip the skin
storage[storageKeys[currentShopCategory].selected] = skinData.id;
// Refresh the shop display
shopContainer.visible = false;
createShop();
} else if (storage.coins >= skinData.price) {
// Purchase the skin
storage.coins -= skinData.price;
storage[storageKeys[currentShopCategory].owned].push(skinData.id);
storage[storageKeys[currentShopCategory].selected] = skinData.id;
// Refresh the shop display
shopContainer.visible = false;
createShop();
} else {
// Not enough coins - flash red with better feedback
tween(background, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(background, {
tint: bgColor
}, {
duration: 200
});
}
});
// Shake the container for better feedback
tween(container, {
x: container.x + 10
}, {
duration: 50,
onFinish: function onFinish() {
tween(container, {
x: container.x - 20
}, {
duration: 50,
onFinish: function onFinish() {
tween(container, {
x: container.x + 10
}, {
duration: 50
});
}
});
}
});
}
};
})(skin, skinBg, skinItemContainer);
}
// Add navigation indicators if there are more skins than can be displayed
var totalRows = Math.ceil(currentSkins.length / itemsPerRow);
if (totalRows > maxRows) {
// Add scroll indicator
var scrollIndicator = new Text2('More skins available - ' + (currentSkins.length - maxRows * itemsPerRow) + ' more', {
size: 24,
fill: 0xFFD700
});
scrollIndicator.anchor.set(0.5, 0.5);
scrollIndicator.x = 1024;
scrollIndicator.y = gridStartY + maxRows * (itemHeight + spacingY) + 30;
skinsDisplayArea.addChild(scrollIndicator);
// Add blinking effect to scroll indicator
var scrollBlinkCycle = 0;
var scrollBlinkInterval = LK.setInterval(function () {
scrollBlinkCycle += 0.05;
var alpha = 0.5 + Math.sin(scrollBlinkCycle) * 0.5;
scrollIndicator.alpha = alpha;
}, 50);
}
// Add category-specific organization message
var organizationMessage = new Text2('Organized by: ' + (currentShopCategory === 'guardior' ? 'Hero Skins' : currentShopCategory === 'enemies' ? 'Enemy Variations' : currentShopCategory === 'bosses' ? 'Boss Themes' : currentShopCategory === 'flying' ? 'Flying Units' : 'Voice Packs'), {
size: 20,
fill: 0xCCCCCC
});
organizationMessage.anchor.set(0.5, 0.5);
organizationMessage.x = 1024;
organizationMessage.y = 460;
skinsDisplayArea.addChild(organizationMessage);
// Enhanced back button with styling - positioned for organized layout
var backButtonShadow = LK.getAsset('platform', {
width: 228,
height: 78,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000,
alpha: 0.5
});
backButtonShadow.x = 1028;
backButtonShadow.y = 1064; // Positioned below organized skins area
shopContainer.addChild(backButtonShadow);
var backButton = LK.getAsset('platform', {
width: 220,
height: 70,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
backButton.x = 1024;
backButton.y = 1060; // Positioned below organized skins area
backButton.interactive = true;
backButton.buttonMode = true;
shopContainer.addChild(backButton);
var backButtonBorder = LK.getAsset('platform', {
width: 228,
height: 78,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF8888,
alpha: 0.4
});
backButtonBorder.x = 1024;
backButtonBorder.y = 1200;
shopContainer.addChild(backButtonBorder);
var backText = new Text2('BACK TO MENU', {
size: 28,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = backButton.x;
backText.y = backButton.y;
shopContainer.addChild(backText);
// Add button hover effect
var backButtonGlow = 0;
var backGlowInterval = LK.setInterval(function () {
backButtonGlow += 0.04;
var glowAlpha = 0.4 + Math.sin(backButtonGlow) * 0.1;
backButtonBorder.alpha = glowAlpha;
}, 60);
backButton.down = function () {
LK.getSound('button_click').play();
gameState = 'menu';
if (shopContainer) shopContainer.destroy();
if (!menuContainer) createMenu();
menuContainer.visible = true;
};
}
function createSettings() {
// Hide all other containers first
if (menuContainer) menuContainer.visible = false;
if (mapSelectContainer) mapSelectContainer.visible = false;
if (settingsContainer) settingsContainer.destroy();
settingsContainer = new Container();
game.addChild(settingsContainer);
// Title
var title = new Text2('SETTINGS', {
size: 60,
fill: 0xFFFFFF
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 350;
settingsContainer.addChild(title);
// Music volume
var musicLabel = new Text2('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%', {
size: 40,
fill: 0xFFFFFF
});
musicLabel.anchor.set(0.5, 0.5);
musicLabel.x = 1024;
musicLabel.y = 550;
settingsContainer.addChild(musicLabel);
var musicDown = LK.getAsset('platform', {
width: 60,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
musicDown.x = 700;
musicDown.y = 550;
musicDown.interactive = true;
musicDown.buttonMode = true;
settingsContainer.addChild(musicDown);
var musicDownText = new Text2('-', {
size: 40,
fill: 0xFFFFFF
});
musicDownText.anchor.set(0.5, 0.5);
musicDownText.x = musicDown.x;
musicDownText.y = musicDown.y;
settingsContainer.addChild(musicDownText);
var musicUp = LK.getAsset('platform', {
width: 60,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44FF44
});
musicUp.x = 1348;
musicUp.y = 550;
musicUp.interactive = true;
musicUp.buttonMode = true;
settingsContainer.addChild(musicUp);
var musicUpText = new Text2('+', {
size: 40,
fill: 0xFFFFFF
});
musicUpText.anchor.set(0.5, 0.5);
musicUpText.x = musicUp.x;
musicUpText.y = musicUp.y;
settingsContainer.addChild(musicUpText);
musicDown.down = function () {
storage.musicVolume = Math.max(0, storage.musicVolume - 0.1);
musicLabel.setText('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%');
LK.getSound('button_click').play();
};
musicUp.down = function () {
storage.musicVolume = Math.min(1, storage.musicVolume + 0.1);
musicLabel.setText('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%');
LK.getSound('button_click').play();
};
// Sound volume
var soundLabel = new Text2('Sound Volume: ' + Math.round(storage.soundVolume * 100) + '%', {
size: 40,
fill: 0xFFFFFF
});
soundLabel.anchor.set(0.5, 0.5);
soundLabel.x = 1024;
soundLabel.y = 700;
settingsContainer.addChild(soundLabel);
var soundDown = LK.getAsset('platform', {
width: 60,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
soundDown.x = 700;
soundDown.y = 750;
soundDown.interactive = true;
soundDown.buttonMode = true;
settingsContainer.addChild(soundDown);
var soundDownText = new Text2('-', {
size: 40,
fill: 0xFFFFFF
});
soundDownText.anchor.set(0.5, 0.5);
soundDownText.x = soundDown.x;
soundDownText.y = soundDown.y;
settingsContainer.addChild(soundDownText);
var soundUp = LK.getAsset('platform', {
width: 60,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44FF44
});
soundUp.x = 1348;
soundUp.y = 750;
soundUp.interactive = true;
soundUp.buttonMode = true;
settingsContainer.addChild(soundUp);
var soundUpText = new Text2('+', {
size: 40,
fill: 0xFFFFFF
});
soundUpText.anchor.set(0.5, 0.5);
soundUpText.x = soundUp.x;
soundUpText.y = soundUp.y;
settingsContainer.addChild(soundUpText);
soundDown.down = function () {
storage.soundVolume = Math.max(0, storage.soundVolume - 0.1);
soundLabel.setText('Sound Volume: ' + Math.round(storage.soundVolume * 100) + '%');
LK.getSound('button_click').play();
};
soundUp.down = function () {
storage.soundVolume = Math.min(1, storage.soundVolume + 0.1);
soundLabel.setText('Sound Volume: ' + Math.round(storage.soundVolume * 100) + '%');
LK.getSound('button_click').play();
};
// Back button
var backButton = LK.getAsset('platform', {
width: 200,
height: 60,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
backButton.x = 1024;
backButton.y = 1000;
backButton.interactive = true;
backButton.buttonMode = true;
settingsContainer.addChild(backButton);
var backText = new Text2('BACK', {
size: 30,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = backButton.x;
backText.y = backButton.y;
settingsContainer.addChild(backText);
backButton.down = function () {
LK.getSound('button_click').play();
gameState = 'menu';
if (settingsContainer) settingsContainer.destroy();
if (shopContainer) shopContainer.visible = false;
if (!menuContainer) createMenu();
menuContainer.visible = true;
};
}
function startGame() {
// Check if level is unlocked before starting
if (currentLevel > storage.unlockedLevels) {
// Level is locked, return to map select
LK.getSound('button_click').play();
return;
}
gameState = 'playing';
// Hide all menu containers to prevent text overlap
if (menuContainer) menuContainer.visible = false;
if (mapSelectContainer) mapSelectContainer.visible = false;
if (settingsContainer) settingsContainer.visible = false;
// Get current level configuration
var levelConfig = getCurrentLevelConfig();
// Set background color for current level
game.setBackgroundColor(levelConfig.backgroundColor);
// Create ground
ground = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 1.0,
x: 1024,
y: 2048
}));
// Create platforms based on current level
platforms = [];
var platformData = levelConfig.platforms;
for (var i = 0; i < platformData.length; i++) {
var platform = new Platform();
platform.x = platformData[i].x;
platform.y = platformData[i].y;
platforms.push(platform);
game.addChild(platform);
}
// Create player
player = new Stickman();
player.x = 200; // Start from left side for horizontal progression
player.y = 1900;
player.canDoubleJump = true;
player.hasUsedDoubleJump = false;
// Apply selected skin
var skinColors = {
'default': 0xFFFFFF,
'red': 0xFF4444,
'blue': 0x4444FF,
'green': 0x44FF44,
'purple': 0x8844FF,
'gold': 0xFFD700
};
if (skinColors[storage.selectedSkin]) {
// Apply tint to all player body parts
player.children.forEach(function (child) {
if (child.tint !== undefined) {
child.tint = skinColors[storage.selectedSkin];
}
});
}
game.addChild(player);
// Create stamina bar
staminaBarBackground = LK.getAsset('platform', {
width: 120,
height: 20,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000
});
game.addChild(staminaBarBackground);
staminaBar = LK.getAsset('platform', {
width: 110,
height: 14,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFFFFF
});
game.addChild(staminaBar);
// Clear arrows array
arrows = [];
// Create enemies based on current level
enemies = [];
var enemyData = levelConfig.enemies;
// Create enemies with automatically applied skins from class constructors
for (var i = 0; i < enemyData.length; i++) {
var enemy;
switch (enemyData[i].type) {
case 'stickman':
enemy = new StickmanEnemy();
break;
case 'flying':
enemy = new FlyingEnemy();
break;
case 'boss':
enemy = new Boss();
break;
case 'shadowBoss':
enemy = new ShadowBoss();
break;
case 'fireBoss':
enemy = new FireBoss();
break;
}
enemy.x = enemyData[i].x;
enemy.y = enemyData[i].y;
enemies.push(enemy);
game.addChild(enemy);
}
// Play level-specific music with configurable volume
var currentMusicTrack = levelConfig.musicTrack;
LK.playMusic(currentMusicTrack, {
fade: {
start: 0,
end: storage.musicVolume,
duration: 1000
}
});
// Clear any existing GUI elements first to prevent overlap
if (healthText && healthText.parent) healthText.destroy();
if (scoreText && scoreText.parent) scoreText.destroy();
if (ammoText && ammoText.parent) ammoText.destroy();
if (pauseButton && pauseButton.parent) pauseButton.destroy();
// Clear existing control buttons to prevent overlap
if (leftButton && leftButton.parent) leftButton.destroy();
if (rightButton && rightButton.parent) rightButton.destroy();
if (jumpButton && jumpButton.parent) jumpButton.destroy();
if (attackButton && attackButton.parent) attackButton.destroy();
if (bowButton && bowButton.parent) bowButton.destroy();
// Clear all GUI children to ensure clean state
while (LK.gui.children.length > 0) {
LK.gui.children[0].destroy();
}
// Show game UI
createGameUI();
// Create game controls for mobile interaction
createGameControls();
}
function createGameUI() {
// UI Elements - optimized for Android phone readability
healthText = new Text2('Health: 100', {
size: 50,
fill: 0x00FF00
});
healthText.anchor.set(0, 0);
LK.gui.left.addChild(healthText);
healthText.y = 150; // Position below top-left menu area
// Health bar background
var healthBarBg = LK.getAsset('platform', {
width: 220,
height: 25,
anchorX: 0,
anchorY: 0,
tint: 0x000000
});
healthBarBg.x = 20;
healthBarBg.y = 200;
LK.gui.addChild(healthBarBg);
// Health bar fill
var healthBarFill = LK.getAsset('platform', {
width: 210,
height: 20,
anchorX: 0,
anchorY: 0,
tint: 0x00FF00
});
healthBarFill.x = 25;
healthBarFill.y = 202.5;
LK.gui.addChild(healthBarFill);
// Store references for updating
healthText.healthBarFill = healthBarFill;
scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 50;
// Pause button - positioned in top-right, below the platform menu icon
pauseButton = LK.getAsset('platform', {
width: 120,
height: 80,
anchorX: 1.0,
anchorY: 0.0,
tint: 0x666666
});
pauseButton.x = LK.gui.right.x - 20;
pauseButton.y = 150; // Below the top-left menu area
pauseButton.interactive = true;
pauseButton.buttonMode = true;
LK.gui.addChild(pauseButton);
var pauseText = new Text2('PAUSE', {
size: 28,
fill: 0xFFFFFF
});
pauseText.anchor.set(0.5, 0.5);
pauseText.x = pauseButton.x - pauseButton.width / 2;
pauseText.y = pauseButton.y + pauseButton.height / 2;
LK.gui.addChild(pauseText);
pauseButton.down = function () {
LK.getSound('button_click').play();
showPauseMenu();
};
// Level display - larger text for Android
var levelConfig = getCurrentLevelConfig();
var levelText = new Text2('Level: ' + currentLevel, {
size: 45,
fill: 0xFFD700
});
levelText.anchor.set(1, 0);
levelText.x = LK.gui.right.x - 30;
levelText.y = 230;
LK.gui.addChild(levelText);
// Level name display - larger text for Android
var levelNameText = new Text2(levelConfig.name, {
size: 35,
fill: 0xFFD700
});
levelNameText.anchor.set(1, 0);
levelNameText.x = LK.gui.right.x - 30;
levelNameText.y = 290;
LK.gui.addChild(levelNameText);
// Boss level indicator - larger text for Android
if ((currentLevel - 1) % 4 === 3) {
var bossText = new Text2('BOSS FIGHT!', {
size: 35,
fill: 0xFF4444
});
bossText.anchor.set(1, 0);
bossText.x = LK.gui.right.x - 30;
bossText.y = 340;
LK.gui.addChild(bossText);
}
// Ammunition display - larger text for Android
ammoText = new Text2('Arrows: 5/5', {
size: 50,
fill: 0xFFFFFF
});
ammoText.anchor.set(0, 0);
LK.gui.left.addChild(ammoText);
ammoText.y = 220; // Position below health text
}
// Global UI variables
var healthText, scoreText, ammoText, pauseButton;
var pauseMenuContainer;
var gamePaused = false;
function showPauseMenu() {
if (gamePaused) return; // Prevent multiple pause menus
gamePaused = true;
// Create pause menu container
pauseMenuContainer = new Container();
game.addChild(pauseMenuContainer);
// Dark overlay
var overlay = LK.getAsset('platform', {
width: 2732,
height: 2048,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000,
alpha: 0.7
});
overlay.x = 1366;
overlay.y = 1024;
pauseMenuContainer.addChild(overlay);
// Pause menu background
var menuBg = LK.getAsset('platform', {
width: 600,
height: 800,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x222222
});
menuBg.x = 1366;
menuBg.y = 1024;
pauseMenuContainer.addChild(menuBg);
// Pause title
var pauseTitle = new Text2('GAME PAUSED', {
size: 60,
fill: 0xFFFFFF
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.x = 1366;
pauseTitle.y = 700;
pauseMenuContainer.addChild(pauseTitle);
// Resume button
var resumeButton = LK.getAsset('platform', {
width: 400,
height: 100,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44FF44
});
resumeButton.x = 1366;
resumeButton.y = 850;
resumeButton.interactive = true;
resumeButton.buttonMode = true;
pauseMenuContainer.addChild(resumeButton);
var resumeText = new Text2('RESUME', {
size: 40,
fill: 0xFFFFFF
});
resumeText.anchor.set(0.5, 0.5);
resumeText.x = resumeButton.x;
resumeText.y = resumeButton.y;
pauseMenuContainer.addChild(resumeText);
resumeButton.down = function () {
LK.getSound('button_click').play();
hidePauseMenu();
};
// Settings button
var pauseSettingsButton = LK.getAsset('platform', {
width: 400,
height: 100,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x4444FF
});
pauseSettingsButton.x = 1366;
pauseSettingsButton.y = 1000;
pauseSettingsButton.interactive = true;
pauseSettingsButton.buttonMode = true;
pauseMenuContainer.addChild(pauseSettingsButton);
var pauseSettingsText = new Text2('SETTINGS', {
size: 40,
fill: 0xFFFFFF
});
pauseSettingsText.anchor.set(0.5, 0.5);
pauseSettingsText.x = pauseSettingsButton.x;
pauseSettingsText.y = pauseSettingsButton.y;
pauseMenuContainer.addChild(pauseSettingsText);
pauseSettingsButton.down = function () {
LK.getSound('button_click').play();
hidePauseMenu();
showPauseSettings();
};
// Main menu button
var mainMenuButton = LK.getAsset('platform', {
width: 400,
height: 100,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
mainMenuButton.x = 1366;
mainMenuButton.y = 1150;
mainMenuButton.interactive = true;
mainMenuButton.buttonMode = true;
pauseMenuContainer.addChild(mainMenuButton);
var mainMenuText = new Text2('MAIN MENU', {
size: 40,
fill: 0xFFFFFF
});
mainMenuText.anchor.set(0.5, 0.5);
mainMenuText.x = mainMenuButton.x;
mainMenuText.y = mainMenuButton.y;
pauseMenuContainer.addChild(mainMenuText);
mainMenuButton.down = function () {
LK.getSound('button_click').play();
returnToMainMenu();
};
}
function hidePauseMenu() {
if (pauseMenuContainer) {
pauseMenuContainer.destroy();
pauseMenuContainer = null;
}
gamePaused = false;
}
function showPauseSettings() {
// Create pause settings container
var pauseSettingsContainer = new Container();
game.addChild(pauseSettingsContainer);
// Dark overlay
var overlay = LK.getAsset('platform', {
width: 2732,
height: 2048,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000,
alpha: 0.7
});
overlay.x = 1366;
overlay.y = 1024;
pauseSettingsContainer.addChild(overlay);
// Settings menu background
var settingsBg = LK.getAsset('platform', {
width: 700,
height: 900,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x222222
});
settingsBg.x = 1366;
settingsBg.y = 1024;
pauseSettingsContainer.addChild(settingsBg);
// Settings title
var settingsTitle = new Text2('SETTINGS', {
size: 60,
fill: 0xFFFFFF
});
settingsTitle.anchor.set(0.5, 0.5);
settingsTitle.x = 1366;
settingsTitle.y = 650;
pauseSettingsContainer.addChild(settingsTitle);
// Music volume
var musicLabel = new Text2('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%', {
size: 40,
fill: 0xFFFFFF
});
musicLabel.anchor.set(0.5, 0.5);
musicLabel.x = 1366;
musicLabel.y = 800;
pauseSettingsContainer.addChild(musicLabel);
var musicDown = LK.getAsset('platform', {
width: 80,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
musicDown.x = 1150;
musicDown.y = 850;
musicDown.interactive = true;
musicDown.buttonMode = true;
pauseSettingsContainer.addChild(musicDown);
var musicDownText = new Text2('-', {
size: 50,
fill: 0xFFFFFF
});
musicDownText.anchor.set(0.5, 0.5);
musicDownText.x = musicDown.x;
musicDownText.y = musicDown.y;
pauseSettingsContainer.addChild(musicDownText);
var musicUp = LK.getAsset('platform', {
width: 80,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44FF44
});
musicUp.x = 1582;
musicUp.y = 850;
musicUp.interactive = true;
musicUp.buttonMode = true;
pauseSettingsContainer.addChild(musicUp);
var musicUpText = new Text2('+', {
size: 50,
fill: 0xFFFFFF
});
musicUpText.anchor.set(0.5, 0.5);
musicUpText.x = musicUp.x;
musicUpText.y = musicUp.y;
pauseSettingsContainer.addChild(musicUpText);
musicDown.down = function () {
storage.musicVolume = Math.max(0, storage.musicVolume - 0.1);
musicLabel.setText('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%');
LK.getSound('button_click').play();
};
musicUp.down = function () {
storage.musicVolume = Math.min(1, storage.musicVolume + 0.1);
musicLabel.setText('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%');
LK.getSound('button_click').play();
};
// Sound volume
var soundLabel = new Text2('Sound Volume: ' + Math.round(storage.soundVolume * 100) + '%', {
size: 40,
fill: 0xFFFFFF
});
soundLabel.anchor.set(0.5, 0.5);
soundLabel.x = 1366;
soundLabel.y = 1000;
pauseSettingsContainer.addChild(soundLabel);
var soundDown = LK.getAsset('platform', {
width: 80,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
soundDown.x = 1150;
soundDown.y = 1050;
soundDown.interactive = true;
soundDown.buttonMode = true;
pauseSettingsContainer.addChild(soundDown);
var soundDownText = new Text2('-', {
size: 50,
fill: 0xFFFFFF
});
soundDownText.anchor.set(0.5, 0.5);
soundDownText.x = soundDown.x;
soundDownText.y = soundDown.y;
pauseSettingsContainer.addChild(soundDownText);
var soundUp = LK.getAsset('platform', {
width: 80,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44FF44
});
soundUp.x = 1582;
soundUp.y = 1050;
soundUp.interactive = true;
soundUp.buttonMode = true;
pauseSettingsContainer.addChild(soundUp);
var soundUpText = new Text2('+', {
size: 50,
fill: 0xFFFFFF
});
soundUpText.anchor.set(0.5, 0.5);
soundUpText.x = soundUp.x;
soundUpText.y = soundUp.y;
pauseSettingsContainer.addChild(soundUpText);
soundDown.down = function () {
storage.soundVolume = Math.max(0, storage.soundVolume - 0.1);
soundLabel.setText('Sound Volume: ' + Math.round(storage.soundVolume * 100) + '%');
LK.getSound('button_click').play();
};
soundUp.down = function () {
storage.soundVolume = Math.min(1, storage.soundVolume + 0.1);
soundLabel.setText('Sound Volume: ' + Math.round(storage.soundVolume * 100) + '%');
LK.getSound('button_click').play();
};
// Back button
var backButton = LK.getAsset('platform', {
width: 300,
height: 80,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4444
});
backButton.x = 1366;
backButton.y = 1250;
backButton.interactive = true;
backButton.buttonMode = true;
pauseSettingsContainer.addChild(backButton);
var backText = new Text2('BACK', {
size: 40,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = backButton.x;
backText.y = backButton.y;
pauseSettingsContainer.addChild(backText);
backButton.down = function () {
LK.getSound('button_click').play();
pauseSettingsContainer.destroy();
showPauseMenu();
};
}
function returnToMainMenu() {
// Clean up current game
hidePauseMenu();
// Clear GUI elements first to prevent overlap
if (healthText && healthText.parent) healthText.destroy();
if (scoreText && scoreText.parent) scoreText.destroy();
if (ammoText && ammoText.parent) ammoText.destroy();
if (pauseButton && pauseButton.parent) pauseButton.destroy();
// Clear control buttons if they exist
if (leftButton && leftButton.parent) leftButton.destroy();
if (rightButton && rightButton.parent) rightButton.destroy();
if (jumpButton && jumpButton.parent) jumpButton.destroy();
if (attackButton && attackButton.parent) attackButton.destroy();
if (bowButton && bowButton.parent) bowButton.destroy();
// Clear any GUI children that might be lingering
while (LK.gui.children.length > 0) {
LK.gui.children[0].destroy();
}
// Reset control variables
leftButton = null;
rightButton = null;
jumpButton = null;
attackButton = null;
bowButton = null;
// Clean up game objects
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child !== menuContainer && child !== mapSelectContainer && child !== settingsContainer) {
child.destroy();
}
}
// Clear game arrays
platforms = [];
enemies = [];
arrows = [];
projectiles = [];
enemyBullets = [];
// Reset global UI variables
healthText = null;
scoreText = null;
ammoText = null;
pauseButton = null;
// Reset game state
gameState = 'menu';
gamePaused = false;
// Show menu and play menu music
if (menuContainer) {
menuContainer.visible = true;
} else {
createMenu();
}
// Reset camera position
game.x = 0;
game.y = 0;
// Play menu music
LK.playMusic('menu_music', {
volume: storage.musicVolume || 0.6
});
}
// Unlock cinematic function
function showUnlockCinematic(newLevel, callback) {
// Create cinematic container
var cinematicContainer = new Container();
game.addChild(cinematicContainer);
// Dark overlay
var overlay = LK.getAsset('platform', {
width: 2048,
height: 2732,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000,
alpha: 0.8
});
overlay.x = 1024;
overlay.y = 1366;
cinematicContainer.addChild(overlay);
// Level unlock text
var unlockText = new Text2('LEVEL ' + newLevel + ' UNLOCKED!', {
size: 60,
fill: 0xFFD700
});
unlockText.anchor.set(0.5, 0.5);
unlockText.x = 1024;
unlockText.y = 1100;
unlockText.alpha = 0;
cinematicContainer.addChild(unlockText);
// Create pixel-style lock
var lockGraphic = LK.getAsset('lock', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x666666,
scaleX: 3,
scaleY: 3
});
lockGraphic.x = 1024;
lockGraphic.y = 1366;
cinematicContainer.addChild(lockGraphic);
// Create pixel-style key
var keyGraphic = LK.getAsset('key', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFD700,
scaleX: 2,
scaleY: 2
});
keyGraphic.x = 600; // Start from left side
keyGraphic.y = 1366;
cinematicContainer.addChild(keyGraphic);
// Animation sequence
LK.setTimeout(function () {
// Fade in text
tween(unlockText, {
alpha: 1
}, {
duration: 500
});
}, 200);
// Key moves toward lock
LK.setTimeout(function () {
tween(keyGraphic, {
x: 950,
// Move close to lock
rotation: Math.PI * 2 // Spinning effect
}, {
duration: 1000,
easing: tween.easeInOut
});
}, 500);
// Key enters lock with unlock effect
LK.setTimeout(function () {
// Key enters lock
tween(keyGraphic, {
x: 1024,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
// Lock opens with flash effect
tween(lockGraphic, {
tint: 0xFFFFFF,
scaleX: 4,
scaleY: 4,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut
});
// Key disappears with glow
tween(keyGraphic, {
tint: 0xFFFFFF,
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut
});
// Text grows and glows
tween(unlockText, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.easeOut
});
}
});
}, 1500);
// Clean up and continue
LK.setTimeout(function () {
tween(cinematicContainer, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
cinematicContainer.destroy();
if (callback) callback();
}
});
}, 3000);
}
function createGameControls() {
// Mobile Controls UI - Optimized for Android phones with larger touch targets
// Movement area background - larger for easier touch detection
var movementArea = LK.getAsset('platform', {
width: 350,
height: 320,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
tint: 0x333333
});
movementArea.x = 180;
movementArea.y = LK.gui.bottom.y - 160;
LK.gui.addChild(movementArea);
// Left movement button - larger for Android phones
leftButton = LK.getAsset('platform', {
width: 150,
height: 150,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
tint: 0x4444FF
});
leftButton.x = 120;
leftButton.y = LK.gui.bottom.y - 160;
leftButton.interactive = true;
leftButton.buttonMode = true;
LK.gui.addChild(leftButton);
var leftIcon = new Text2('<', {
size: 70,
fill: 0xFFFFFF
});
leftIcon.anchor.set(0.5, 0.5);
leftIcon.x = leftButton.x;
leftIcon.y = leftButton.y;
LK.gui.addChild(leftIcon);
// Right movement button - larger for Android phones
rightButton = LK.getAsset('platform', {
width: 150,
height: 150,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
tint: 0x4444FF
});
rightButton.x = 240;
rightButton.y = LK.gui.bottom.y - 160;
rightButton.interactive = true;
rightButton.buttonMode = true;
LK.gui.addChild(rightButton);
var rightIcon = new Text2('>', {
size: 70,
fill: 0xFFFFFF
});
rightIcon.anchor.set(0.5, 0.5);
rightIcon.x = rightButton.x;
rightIcon.y = rightButton.y;
LK.gui.addChild(rightIcon);
// Action buttons on the right side - optimized for Android thumb reach
// Jump button - largest and most accessible for Android phones
jumpButton = LK.getAsset('platform', {
width: 180,
height: 180,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
tint: 0x44FF44
});
jumpButton.x = LK.gui.right.x - 120;
jumpButton.y = LK.gui.bottom.y - 120;
jumpButton.interactive = true;
jumpButton.buttonMode = true;
LK.gui.addChild(jumpButton);
var jumpLabel = new Text2('JUMP', {
size: 32,
fill: 0xFFFFFF
});
jumpLabel.anchor.set(0.5, 0.5);
jumpLabel.x = jumpButton.x;
jumpLabel.y = jumpButton.y;
LK.gui.addChild(jumpLabel);
// Attack button - positioned for comfortable Android thumb reach
attackButton = LK.getAsset('platform', {
width: 160,
height: 160,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
tint: 0xFF4444
});
attackButton.x = LK.gui.right.x - 300;
attackButton.y = LK.gui.bottom.y - 120;
attackButton.interactive = true;
attackButton.buttonMode = true;
LK.gui.addChild(attackButton);
var attackLabel = new Text2('ATK', {
size: 30,
fill: 0xFFFFFF
});
attackLabel.anchor.set(0.5, 0.5);
attackLabel.x = attackButton.x;
attackLabel.y = attackButton.y;
LK.gui.addChild(attackLabel);
// Bow button - positioned higher for Android ergonomics
bowButton = LK.getAsset('platform', {
width: 140,
height: 140,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
tint: 0xFF8844
});
bowButton.x = LK.gui.right.x - 210;
bowButton.y = LK.gui.bottom.y - 280;
bowButton.interactive = true;
bowButton.buttonMode = true;
LK.gui.addChild(bowButton);
var bowLabel = new Text2('BOW', {
size: 26,
fill: 0xFFFFFF
});
bowLabel.anchor.set(0.5, 0.5);
bowLabel.x = bowButton.x;
bowLabel.y = bowButton.y;
LK.gui.addChild(bowLabel);
setupControlEvents();
}
// Global control variables
var leftButton, rightButton, jumpButton, attackButton, bowButton;
// Collision detection function
function checkCollisions(obj, platforms) {
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
if (obj.intersects(platform)) {
// Landing on top of platform
if (obj.velocityY > 0 && obj.y - 40 < platform.y) {
obj.y = platform.y - 40;
obj.velocityY = 0;
obj.onGround = true;
// Reset double jump when landing on platforms
if (obj === player) {
obj.canDoubleJump = true;
obj.hasUsedDoubleJump = false;
}
}
}
}
// Ground collision
if (obj.y > 1948) {
obj.y = 1948;
obj.velocityY = 0;
obj.onGround = true;
// Reset double jump when landing on ground
if (obj === player) {
obj.canDoubleJump = true;
obj.hasUsedDoubleJump = false;
}
}
}
function setupControlEvents() {
// Input handling - variables moved to global scope
game.down = function (x, y, obj) {
// Fallback touch handling for areas outside buttons
};
game.up = function (x, y, obj) {
// Fallback touch release handling
};
// Input state variables already declared in global scope
// Control event handlers - enhanced feedback for Android
leftButton.down = function (x, y, obj) {
leftPressed = true;
currentTouchId = 'left';
// Enhanced visual feedback for Android - simulates haptic feedback
tween(leftButton, {
alpha: 1.0,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 80
});
LK.getSound('button_click').play();
};
leftButton.up = function (x, y, obj) {
leftPressed = false;
if (currentTouchId === 'left') currentTouchId = null;
tween(leftButton, {
alpha: 0.8,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 120
});
};
rightButton.down = function (x, y, obj) {
rightPressed = true;
currentTouchId = 'right';
tween(rightButton, {
alpha: 0.9
}, {
duration: 100
});
LK.getSound('button_click').play();
};
rightButton.up = function (x, y, obj) {
rightPressed = false;
if (currentTouchId === 'right') currentTouchId = null;
tween(rightButton, {
alpha: 0.6
}, {
duration: 100
});
};
jumpButton.down = function (x, y, obj) {
player.jump();
tween(jumpButton, {
alpha: 0.9
}, {
duration: 100
});
};
jumpButton.up = function (x, y, obj) {
tween(jumpButton, {
alpha: 0.6
}, {
duration: 100
});
};
attackButton.down = function (x, y, obj) {
player.attack();
tween(attackButton, {
alpha: 0.9
}, {
duration: 100
});
};
attackButton.up = function (x, y, obj) {
tween(attackButton, {
alpha: 0.6
}, {
duration: 100
});
};
bowButton.down = function (x, y, obj) {
if (player.currentAmmo > 0) {
player.shootArrow();
tween(bowButton, {
alpha: 0.9
}, {
duration: 100
});
} else {
// Visual feedback for no ammo
tween(bowButton, {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(bowButton, {
tint: 0xFF8844
}, {
duration: 200
});
}
});
}
};
bowButton.up = function (x, y, obj) {
tween(bowButton, {
alpha: 0.6
}, {
duration: 100
});
};
}
// Initialize menu system
LK.playMusic('menu_music', {
volume: storage.musicVolume || 0.6
});
createMenu();
game.update = function () {
if (gameState !== 'playing' || gamePaused) {
return; // Don't update game logic if not playing or paused
}
// Handle input
if (leftPressed) {
player.moveLeft();
}
if (rightPressed) {
player.moveRight();
}
// Check collisions for player
checkCollisions(player, platforms);
// Check collisions for enemies
for (var i = 0; i < enemies.length; i++) {
checkCollisions(enemies[i], platforms);
}
// Update arrows and check enemy hits
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
// Check if arrow hits enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (arrow.intersects(enemy)) {
if (enemy.takeDamage(arrow.damage)) {
// Enemy destroyed
enemies.splice(j, 1);
LK.setScore(LK.getScore() + 150); // More points for ranged kills
storage.coins += 5; // Reward coins for defeating enemies
scoreText.setText('Score: ' + LK.getScore());
}
// Remove arrow
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
// Remove destroyed arrows
if (arrow.parent === null) {
arrows.splice(i, 1);
}
}
// Update projectiles and check enemy hits
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
// Check if projectile hits enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (projectile.intersects(enemy)) {
if (enemy.takeDamage(25)) {
// Enemy destroyed
enemies.splice(j, 1);
LK.setScore(LK.getScore() + 100);
storage.coins += 5; // Reward coins for defeating enemies
scoreText.setText('Score: ' + LK.getScore());
}
// Remove projectile
projectile.destroy();
projectiles.splice(i, 1);
break;
}
}
// Remove destroyed projectiles
if (projectile.parent === null) {
projectiles.splice(i, 1);
}
}
// Update enemy bullets and check player hits
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
// Check if bullet hits player
if (bullet.intersects(player)) {
player.takeDamage(10);
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Remove destroyed bullets
if (bullet.parent === null) {
enemyBullets.splice(i, 1);
}
}
// Check player-enemy collisions
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (player.intersects(enemy)) {
// Simple knockback and damage
var dx = player.x - enemy.x;
player.velocityX += dx > 0 ? 5 : -5;
player.takeDamage(1);
// Update health display
healthText.setText('Health: ' + Math.max(0, player.health));
healthText.tint = player.health < 30 ? 0xFF0000 : 0x00FF00;
// Update health bar
if (healthText.healthBarFill) {
var healthPercent = Math.max(0, player.health / player.maxHealth);
healthText.healthBarFill.width = 210 * healthPercent;
healthText.healthBarFill.tint = player.health < 30 ? 0xFF0000 : 0x00FF00;
}
}
}
// Win condition - defeat all enemies
if (enemies.length === 0) {
var returnToMainMenu = function returnToMainMenu() {
// Clean up current level more carefully
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
// Don't destroy menu containers
if (child !== menuContainer && child !== mapSelectContainer && child !== settingsContainer) {
child.destroy();
}
}
// Clear arrays
platforms = [];
enemies = [];
arrows = [];
projectiles = [];
enemyBullets = [];
// Reset camera position
game.x = 0;
game.y = 0;
// Change game state to main menu
gameState = 'menu';
gamePaused = false;
// Show main menu and play menu music
if (menuContainer) {
menuContainer.visible = true;
} else {
createMenu();
}
LK.playMusic('menu_music', {
volume: storage.musicVolume || 0.6
});
};
// Update high score
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
// Award bonus coins for level completion
var levelBonus = 10; // 10 coins for completing any level
storage.coins += levelBonus;
var completedLevel = currentLevel;
var shouldShowUnlock = false;
// Mark this level as completed
storage.lastCompletedLevel = completedLevel;
// Unlock the next level
var nextLevel = currentLevel + 1;
if (nextLevel > storage.unlockedLevels) {
storage.unlockedLevels = nextLevel;
shouldShowUnlock = true;
}
// Reset player health to full when leveling up
player.health = player.maxHealth;
healthText.setText('Health: ' + player.health);
healthText.tint = 0x00FF00;
// Reset current level to 1 for main menu (Play button will advance to next uncompleted level)
currentLevel = 1;
storage.currentLevel = currentLevel;
// Create completion screen overlay
var completionOverlay = LK.getAsset('platform', {
width: 2732,
height: 2048,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x000000,
alpha: 0.8
});
completionOverlay.x = 1366;
completionOverlay.y = 1024;
game.addChild(completionOverlay);
// Create completion message background
var completionBg = LK.getAsset('platform', {
width: 800,
height: 400,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x222222
});
completionBg.x = 1366;
completionBg.y = 1024;
game.addChild(completionBg);
// Show level complete message with enhanced styling
var levelComplete = new Text2('NIVEL ' + completedLevel + ' COMPLETADO', {
size: 60,
fill: 0xFFD700
});
levelComplete.anchor.set(0.5, 0.5);
levelComplete.x = 1366;
levelComplete.y = 950;
levelComplete.alpha = 0;
game.addChild(levelComplete);
// Success message
var successMessage = new Text2('¡Excelente trabajo!', {
size: 40,
fill: 0x00FF00
});
successMessage.anchor.set(0.5, 0.5);
successMessage.x = 1366;
successMessage.y = 1050;
successMessage.alpha = 0;
game.addChild(successMessage);
// Main menu message
var selectorText = new Text2('Regresando al menú principal...', {
size: 35,
fill: 0xFFFFFF
});
selectorText.anchor.set(0.5, 0.5);
selectorText.x = 1366;
selectorText.y = 1150;
selectorText.alpha = 0;
game.addChild(selectorText);
// Animate completion screen entrance
tween(levelComplete, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 600,
easing: tween.easeOut
});
// Animate success message
LK.setTimeout(function () {
tween(successMessage, {
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
}, 400);
// Animate selector text
LK.setTimeout(function () {
tween(selectorText, {
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
}, 800);
// Return to main menu after 3 seconds
LK.setTimeout(function () {
// Clean up completion screen elements first
if (completionOverlay && completionOverlay.parent) completionOverlay.destroy();
if (completionBg && completionBg.parent) completionBg.destroy();
if (levelComplete && levelComplete.parent) levelComplete.destroy();
if (successMessage && successMessage.parent) successMessage.destroy();
if (selectorText && selectorText.parent) selectorText.destroy();
if (shouldShowUnlock) {
// Show unlock cinematic for new level
showUnlockCinematic(nextLevel, function () {
// Return to main menu after cinematic
returnToMainMenu();
});
} else {
// Return to main menu immediately
returnToMainMenu();
}
}, 3000);
}
// Update stamina bar position and appearance - moved higher up
staminaBarBackground.x = player.x;
staminaBarBackground.y = player.y - 130;
staminaBar.x = player.x;
staminaBar.y = player.y - 130;
// Update stamina bar color based on double jump availability
if (player.canDoubleJump && !player.hasUsedDoubleJump) {
staminaBar.tint = 0x00FF00; // Bright green when available
} else {
staminaBar.tint = 0x444444; // Dark gray when used
}
// Update bow button appearance based on ammunition
if (bowButton) {
bowButton.alpha = player.currentAmmo > 0 ? 0.6 : 0.3;
bowButton.tint = player.currentAmmo > 0 ? 0xFF8844 : 0x666666;
}
// Horizontal camera follow player with smooth movement
var targetX = Math.max(-1024, Math.min(0, -(player.x - 1024)));
var targetY = Math.max(-500, Math.min(0, -(player.y - 1024)));
game.x += (targetX - game.x) * 0.08;
game.y += (targetY - game.y) * 0.06;
// Sound volume is now controlled through storage and applied during initialization
};
// Controls will be created when entering levels via startGame() ===================================================================
--- original.js
+++ change.js
@@ -3550,8 +3550,26 @@
name: 'Cyber Destroyer',
price: 450,
color: 0x00CED1,
description: 'Tech nightmare'
+ }, {
+ id: 'inferno',
+ name: 'Inferno Lord',
+ price: 500,
+ color: 0xFF4500,
+ description: 'Master of flames'
+ }, {
+ id: 'frost',
+ name: 'Frost Monarch',
+ price: 480,
+ color: 0x87CEEB,
+ description: 'Ruler of ice'
+ }, {
+ id: 'void',
+ name: 'Void Emperor',
+ price: 520,
+ color: 0x1C1C1C,
+ description: 'Lord of nothingness'
}],
flying: [{
id: 'default',
name: 'Brown Flyer',
@@ -3587,8 +3605,26 @@
name: 'Storm Wing',
price: 160,
color: 0x1E90FF,
description: 'Lightning fast'
+ }, {
+ id: 'toxic',
+ name: 'Toxic Wasp',
+ price: 140,
+ color: 0x9ACD32,
+ description: 'Poisonous hunter'
+ }, {
+ id: 'crystal',
+ name: 'Crystal Flyer',
+ price: 200,
+ color: 0xE6E6FA,
+ description: 'Crystalline predator'
+ }, {
+ id: 'shadow',
+ name: 'Shadow Bat',
+ price: 170,
+ color: 0x404040,
+ description: 'Darkness incarnate'
}],
voces: [{
id: 'default',
name: 'Classic Voice',
Crea un personaje estilo pixel art, un guerrero simple y fácil de animar. No debe tener manos ni piernas visibles, solo un cuerpo principal con forma redonda u ovalada. El diseño debe sugerir que es un guerrero usando colores llamativos, ojos brillantes o una armadura pixelada pegada al cuerpo. Puede tener una espada flotando al lado o en la espalda como decoración. Debe verse como un personaje de videojuego, pero sin partes que necesiten animación compleja.. In-Game asset. 2d. High contrast. No shadows
Diseña una espada estilo pixel art, sencilla pero llamativa. Debe ser fácil de animar o colocar junto a un personaje. El mango debe ser corto y simple, y la hoja puede ser recta o con un brillo digital. Usa colores llamativos como azul neón, rojo fuego o plateado con detalles pixelados. El diseño debe parecer futurista o mágico, como si fuera un arma especial de un guerrero digital.. In-Game asset. 2d. High contrast. No shadows