/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Enemy = Container.expand(function () { var self = Container.call(this); // Enemy attributes self.health = 100; self.maxHealth = 100; self.attackDamage = 10; self.attackRange = 150; self.attackCooldown = 0; self.maxAttackCooldown = 60; // frames self.moveSpeed = 3; self.isActive = true; // Visual representation var enemySprite = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); // Take damage method self.takeDamage = function (amount) { self.health -= amount; // Visual effect LK.effects.flashObject(self, 0xFF0000, 300); // Play hit sound LK.getSound('hit').play(); // Check if enemy is dead if (self.health <= 0) { self.isActive = false; LK.getSound('enemy_die').play(); // Death animation tween(self, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Remove from game self.destroy(); // Increase score LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); // Find index and remove from array var index = enemies.indexOf(self); if (index !== -1) { enemies.splice(index, 1); } } }); } }; // Update method - called automatically every tick self.update = function () { if (!self.isActive) { return; } // Move towards player var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Move if not in attack range if (distance > self.attackRange) { var moveX = dx / distance * self.moveSpeed; self.x += moveX; // Keep on ground self.y = groundLevel - self.height / 2; } else { // Try to attack player if in range if (self.attackCooldown <= 0) { player.takeDamage(self.attackDamage); self.attackCooldown = self.maxAttackCooldown; // Attack animation tween(enemySprite, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(enemySprite, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); } } // Decrease attack cooldown if (self.attackCooldown > 0) { self.attackCooldown--; } }; return self; }); var Platform = Container.expand(function () { var self = Container.call(this); // Platform attributes self.isActive = true; // Visual representation var platformSprite = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); // Check if player is on this platform self.checkCollision = function (playerObj) { if (!self.isActive) { return false; } // Basic platform collision detection var playerBottom = playerObj.y + playerObj.height / 2; var playerTop = playerObj.y - playerObj.height / 2; var playerLeft = playerObj.x - playerObj.width / 2; var playerRight = playerObj.x + playerObj.width / 2; var platformTop = self.y - self.height / 2; var platformBottom = self.y + self.height / 2; var platformLeft = self.x - self.width / 2; var platformRight = self.x + self.width / 2; // Check if player is falling onto platform if (playerObj.velocityY > 0 && playerBottom >= platformTop && playerTop < platformTop && playerRight > platformLeft && playerLeft < platformRight) { // Position player on top of platform playerObj.y = platformTop - playerObj.height / 2; playerObj.isJumping = false; playerObj.velocityY = 0; return true; } return false; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); // Player attributes self.health = 100; self.maxHealth = 100; self.stance = 'swan'; // Default stance self.isAttacking = false; self.isJumping = false; self.velocityY = 0; self.gravity = 0.5; self.jumpPower = -15; self.moveSpeed = 10; // Stats per stance self.stanceData = { swan: { color: 0xFFFFFF, attackDamage: 20, attackRange: 150, attackSpeed: 1.0, moveSpeedMod: 1.2 }, crow: { color: 0x222222, attackDamage: 30, attackRange: 100, attackSpeed: 0.8, moveSpeedMod: 1.0 }, phoenix: { color: 0xFF3300, attackDamage: 40, attackRange: 80, attackSpeed: 0.6, moveSpeedMod: 0.8 } }; // Create all stance sprites but only show the current stance self.swanSprite = self.attachAsset('playerSwan', { anchorX: 0.5, anchorY: 0.5, visible: true }); self.crowSprite = self.attachAsset('playerCrow', { anchorX: 0.5, anchorY: 0.5, visible: false }); self.phoenixSprite = self.attachAsset('playerPhoenix', { anchorX: 0.5, anchorY: 0.5, visible: false }); // Crack overlay for health visualization self.crackOverlay = self.attachAsset('crackOverlay', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); // Set hit area for touch detection self.width = 150; self.height = 200; // Movement control self.moveTo = function (x, y) { self.targetX = x; }; // Jump method self.jump = function () { if (!self.isJumping) { self.isJumping = true; self.velocityY = self.jumpPower; } }; // Attack method self.attack = function () { if (!self.isAttacking) { self.isAttacking = true; // Different attack animation based on stance var stanceData = self.stanceData[self.stance]; var attackDuration = 500 * (1 / stanceData.attackSpeed); // Get the current visible sprite var currentSprite; if (self.stance === 'swan') { currentSprite = self.swanSprite; } else if (self.stance === 'crow') { currentSprite = self.crowSprite; } else { currentSprite = self.phoenixSprite; } // Attack animation tween(currentSprite, { scaleX: 1.3, scaleY: 1.3 }, { duration: attackDuration / 2, easing: tween.easeOut, onFinish: function onFinish() { tween(currentSprite, { scaleX: 1, scaleY: 1 }, { duration: attackDuration / 2, easing: tween.easeIn, onFinish: function onFinish() { self.isAttacking = false; } }); } }); // Create projectile based on stance var projectile = new Projectile(self.stance, stanceData.attackDamage); projectile.x = self.x; projectile.y = self.y; game.addChild(projectile); projectiles.push(projectile); // Play attack sound LK.getSound('attack').play(); // Check for enemies in range 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 < stanceData.attackRange) { enemy.takeDamage(stanceData.attackDamage); } } } }; // Switch stance method self.switchStance = function (newStance) { if (self.stance !== newStance) { self.stance = newStance; // Update visibility of sprites self.swanSprite.visible = newStance === 'swan'; self.crowSprite.visible = newStance === 'crow'; self.phoenixSprite.visible = newStance === 'phoenix'; // Play stance change sound LK.getSound('stance_change').play(); // Visual effect for stance change LK.effects.flashObject(self, self.stanceData[newStance].color, 500); } }; // Take damage method self.takeDamage = function (amount) { self.health -= amount; if (self.health < 0) { self.health = 0; } // Update crack overlay based on health var crackAlpha = 1 - self.health / self.maxHealth; self.crackOverlay.alpha = crackAlpha; // Visual effect LK.effects.flashObject(self, 0xFF0000, 300); // Play hit sound LK.getSound('player_hit').play(); // Check if player is dead if (self.health <= 0) { LK.showGameOver(); } }; // Update method - called automatically every tick self.update = function () { // Apply gravity if (self.isJumping) { self.velocityY += self.gravity; self.y += self.velocityY; // Check if landed on ground if (self.y >= groundLevel - self.height / 2) { self.y = groundLevel - self.height / 2; self.isJumping = false; self.velocityY = 0; } } // Move towards target if exists if (self.targetX !== undefined) { var moveSpeed = self.moveSpeed * self.stanceData[self.stance].moveSpeedMod; var dx = self.targetX - self.x; // If close enough to target, stop moving if (Math.abs(dx) < moveSpeed) { self.x = self.targetX; self.targetX = undefined; } else { // Move towards target self.x += dx > 0 ? moveSpeed : -moveSpeed; } } // Ensure player stays on screen if (self.x < self.width / 2) { self.x = self.width / 2; } else if (self.x > 2048 - self.width / 2) { self.x = 2048 - self.width / 2; } }; // Touch event handlers self.down = function (x, y, obj) { // Check if the player should attack or jump if (y < self.y - self.height / 4) { self.jump(); } else { self.attack(); } }; return self; }); var Projectile = Container.expand(function (stanceType, damage) { var self = Container.call(this); // Projectile attributes self.type = stanceType || 'swan'; self.damage = damage || 10; self.speed = 15; self.range = 500; // Max travel distance self.distanceTraveled = 0; self.isActive = true; // Color based on stance var colors = { swan: 0xFFFFFF, crow: 0x222222, phoenix: 0xFF3300 }; // Visual representation var projectileSprite = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5, tint: colors[self.type] || 0xFFFFFF }); // Update method - called automatically every tick self.update = function () { if (!self.isActive) { return; } // Move forward self.x += self.speed; self.distanceTraveled += self.speed; // Check if out of range if (self.distanceTraveled >= self.range || self.x < 0 || self.x > 2048) { self.destroy(); var index = projectiles.indexOf(self); if (index !== -1) { projectiles.splice(index, 1); } return; } // Check for collision with enemies for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (self.intersects(enemy) && enemy.isActive) { // Apply damage based on stance enemy.takeDamage(self.damage); // Destroy projectile self.destroy(); var index = projectiles.indexOf(self); if (index !== -1) { projectiles.splice(index, 1); } break; } } }; return self; }); var StanceButton = Container.expand(function (stanceType) { var self = Container.call(this); // Button attributes self.type = stanceType; // Colors based on stance var colors = { swan: 0xFFFFFF, crow: 0x222222, phoenix: 0xFF3300 }; // Visual representation var buttonSprite = self.attachAsset(stanceType === 'swan' ? 'playerSwan' : stanceType === 'crow' ? 'playerCrow' : 'playerPhoenix', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); // Highlight when selected self.setSelected = function (isSelected) { if (isSelected) { tween(buttonSprite, { scaleX: 0.9, scaleY: 0.9 }, { duration: 300, easing: tween.easeOut }); } else { tween(buttonSprite, { scaleX: 0.7, scaleY: 0.7 }, { duration: 300, easing: tween.easeOut }); } }; // Touch event handler self.down = function (x, y, obj) { // Switch player stance player.switchStance(self.type); // Update UI buttons updateStanceButtons(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111827 }); /**** * Game Code ****/ // Game variables var player; var enemies = []; var platforms = []; var projectiles = []; var stanceButtons = {}; var groundLevel = 2500; // Y position of ground var enemySpawnTimer = 0; var maxEnemies = 5; var gameStarted = false; var difficultyLevel = 1; var difficultyTimer = 0; var maxDifficultyLevel = 10; // Background var background = game.addChild(LK.getAsset('background', { x: 0, y: 0, anchorX: 0, anchorY: 0 })); // Initialize score display var scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Initialize health display var healthTxt = new Text2('Health: 100%', { size: 80, fill: 0xFFFFFF }); healthTxt.anchor.set(0, 0); LK.gui.topRight.addChild(healthTxt); // Create and position stance buttons function createStanceButtons() { var stanceTypes = ['swan', 'crow', 'phoenix']; var buttonSpacing = 180; var startX = 2048 / 2 - (stanceTypes.length - 1) * buttonSpacing / 2; for (var i = 0; i < stanceTypes.length; i++) { var button = new StanceButton(stanceTypes[i]); button.x = startX + i * buttonSpacing; button.y = 2732 - 150; game.addChild(button); stanceButtons[stanceTypes[i]] = button; } // Set initial selection updateStanceButtons(); } // Update stance button visuals function updateStanceButtons() { for (var type in stanceButtons) { stanceButtons[type].setSelected(type === player.stance); } } // Create player function createPlayer() { player = new Player(); player.x = 2048 / 2; player.y = groundLevel - player.height / 2; game.addChild(player); } // Create enemy function createEnemy() { var enemy = new Enemy(); // Position enemy on edge of screen enemy.x = Math.random() < 0.5 ? 100 : 2048 - 100; enemy.y = groundLevel - enemy.height / 2; // Scale difficulty enemy.health = 50 + difficultyLevel * 10; enemy.maxHealth = enemy.health; enemy.attackDamage = 5 + difficultyLevel * 2; enemy.moveSpeed = 2 + difficultyLevel * 0.3; game.addChild(enemy); enemies.push(enemy); } // Create platforms function createPlatforms() { var numPlatforms = 5; var platformWidth = 300; for (var i = 0; i < numPlatforms; i++) { var platform = new Platform(); platform.x = 300 + i * 350; platform.y = groundLevel - 300 - i * 100; platform.width = platformWidth; platform.height = 50; game.addChild(platform); platforms.push(platform); } } // Initialize game function initGame() { // Reset game state LK.setScore(0); enemies = []; platforms = []; projectiles = []; difficultyLevel = 1; difficultyTimer = 0; // Create player createPlayer(); // Create platforms createPlatforms(); // Create stance buttons createStanceButtons(); // Start music LK.playMusic('ballet_theme'); gameStarted = true; } // Event handlers game.down = function (x, y, obj) { // Start game if not started if (!gameStarted) { initGame(); return; } // Move player when clicking on game area (not on player or buttons) var isButtonClick = false; for (var type in stanceButtons) { if (stanceButtons[type].intersects({ x: x, y: y })) { isButtonClick = true; break; } } if (!isButtonClick && !player.intersects({ x: x, y: y })) { player.moveTo(x); } }; // Game update loop game.update = function () { if (!gameStarted) { // Show title screen if game not started if (!titleShown) { var titleText = new Text2('Ballet of Shadows', { size: 150, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 2 - 200; game.addChild(titleText); var startText = new Text2('Tap to Begin', { size: 100, fill: 0xAAAAAA }); startText.anchor.set(0.5, 0.5); startText.x = 2048 / 2; startText.y = 2732 / 2 + 200; game.addChild(startText); titleShown = true; } return; } // Update health display healthTxt.setText('Health: ' + player.health + '%'); // Spawn enemies on timer enemySpawnTimer++; if (enemySpawnTimer >= 120 && enemies.length < maxEnemies) { // Every 2 seconds (120 frames at 60fps) createEnemy(); enemySpawnTimer = 0; } // Increase difficulty over time difficultyTimer++; if (difficultyTimer >= 1800 && difficultyLevel < maxDifficultyLevel) { // Every 30 seconds difficultyLevel++; difficultyTimer = 0; // Increase max enemies based on difficulty maxEnemies = 5 + Math.floor(difficultyLevel / 2); } // Platform collision detection if (player.isJumping) { for (var i = 0; i < platforms.length; i++) { platforms[i].checkCollision(player); } } // Check for game win condition (example: score 1000) if (LK.getScore() >= 1000) { LK.showYouWin(); } }; // Start with title screen var titleShown = false; // Play background music LK.playMusic('ballet_theme', { fade: { start: 0, end: 0.8, duration: 1000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Enemy attributes
self.health = 100;
self.maxHealth = 100;
self.attackDamage = 10;
self.attackRange = 150;
self.attackCooldown = 0;
self.maxAttackCooldown = 60; // frames
self.moveSpeed = 3;
self.isActive = true;
// Visual representation
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Take damage method
self.takeDamage = function (amount) {
self.health -= amount;
// Visual effect
LK.effects.flashObject(self, 0xFF0000, 300);
// Play hit sound
LK.getSound('hit').play();
// Check if enemy is dead
if (self.health <= 0) {
self.isActive = false;
LK.getSound('enemy_die').play();
// Death animation
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove from game
self.destroy();
// Increase score
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Find index and remove from array
var index = enemies.indexOf(self);
if (index !== -1) {
enemies.splice(index, 1);
}
}
});
}
};
// Update method - called automatically every tick
self.update = function () {
if (!self.isActive) {
return;
}
// Move towards player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Move if not in attack range
if (distance > self.attackRange) {
var moveX = dx / distance * self.moveSpeed;
self.x += moveX;
// Keep on ground
self.y = groundLevel - self.height / 2;
} else {
// Try to attack player if in range
if (self.attackCooldown <= 0) {
player.takeDamage(self.attackDamage);
self.attackCooldown = self.maxAttackCooldown;
// Attack animation
tween(enemySprite, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemySprite, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
}
// Decrease attack cooldown
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
// Platform attributes
self.isActive = true;
// Visual representation
var platformSprite = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
// Check if player is on this platform
self.checkCollision = function (playerObj) {
if (!self.isActive) {
return false;
}
// Basic platform collision detection
var playerBottom = playerObj.y + playerObj.height / 2;
var playerTop = playerObj.y - playerObj.height / 2;
var playerLeft = playerObj.x - playerObj.width / 2;
var playerRight = playerObj.x + playerObj.width / 2;
var platformTop = self.y - self.height / 2;
var platformBottom = self.y + self.height / 2;
var platformLeft = self.x - self.width / 2;
var platformRight = self.x + self.width / 2;
// Check if player is falling onto platform
if (playerObj.velocityY > 0 && playerBottom >= platformTop && playerTop < platformTop && playerRight > platformLeft && playerLeft < platformRight) {
// Position player on top of platform
playerObj.y = platformTop - playerObj.height / 2;
playerObj.isJumping = false;
playerObj.velocityY = 0;
return true;
}
return false;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Player attributes
self.health = 100;
self.maxHealth = 100;
self.stance = 'swan'; // Default stance
self.isAttacking = false;
self.isJumping = false;
self.velocityY = 0;
self.gravity = 0.5;
self.jumpPower = -15;
self.moveSpeed = 10;
// Stats per stance
self.stanceData = {
swan: {
color: 0xFFFFFF,
attackDamage: 20,
attackRange: 150,
attackSpeed: 1.0,
moveSpeedMod: 1.2
},
crow: {
color: 0x222222,
attackDamage: 30,
attackRange: 100,
attackSpeed: 0.8,
moveSpeedMod: 1.0
},
phoenix: {
color: 0xFF3300,
attackDamage: 40,
attackRange: 80,
attackSpeed: 0.6,
moveSpeedMod: 0.8
}
};
// Create all stance sprites but only show the current stance
self.swanSprite = self.attachAsset('playerSwan', {
anchorX: 0.5,
anchorY: 0.5,
visible: true
});
self.crowSprite = self.attachAsset('playerCrow', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
self.phoenixSprite = self.attachAsset('playerPhoenix', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
// Crack overlay for health visualization
self.crackOverlay = self.attachAsset('crackOverlay', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Set hit area for touch detection
self.width = 150;
self.height = 200;
// Movement control
self.moveTo = function (x, y) {
self.targetX = x;
};
// Jump method
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.velocityY = self.jumpPower;
}
};
// Attack method
self.attack = function () {
if (!self.isAttacking) {
self.isAttacking = true;
// Different attack animation based on stance
var stanceData = self.stanceData[self.stance];
var attackDuration = 500 * (1 / stanceData.attackSpeed);
// Get the current visible sprite
var currentSprite;
if (self.stance === 'swan') {
currentSprite = self.swanSprite;
} else if (self.stance === 'crow') {
currentSprite = self.crowSprite;
} else {
currentSprite = self.phoenixSprite;
}
// Attack animation
tween(currentSprite, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: attackDuration / 2,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(currentSprite, {
scaleX: 1,
scaleY: 1
}, {
duration: attackDuration / 2,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isAttacking = false;
}
});
}
});
// Create projectile based on stance
var projectile = new Projectile(self.stance, stanceData.attackDamage);
projectile.x = self.x;
projectile.y = self.y;
game.addChild(projectile);
projectiles.push(projectile);
// Play attack sound
LK.getSound('attack').play();
// Check for enemies in range
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 < stanceData.attackRange) {
enemy.takeDamage(stanceData.attackDamage);
}
}
}
};
// Switch stance method
self.switchStance = function (newStance) {
if (self.stance !== newStance) {
self.stance = newStance;
// Update visibility of sprites
self.swanSprite.visible = newStance === 'swan';
self.crowSprite.visible = newStance === 'crow';
self.phoenixSprite.visible = newStance === 'phoenix';
// Play stance change sound
LK.getSound('stance_change').play();
// Visual effect for stance change
LK.effects.flashObject(self, self.stanceData[newStance].color, 500);
}
};
// Take damage method
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health < 0) {
self.health = 0;
}
// Update crack overlay based on health
var crackAlpha = 1 - self.health / self.maxHealth;
self.crackOverlay.alpha = crackAlpha;
// Visual effect
LK.effects.flashObject(self, 0xFF0000, 300);
// Play hit sound
LK.getSound('player_hit').play();
// Check if player is dead
if (self.health <= 0) {
LK.showGameOver();
}
};
// Update method - called automatically every tick
self.update = function () {
// Apply gravity
if (self.isJumping) {
self.velocityY += self.gravity;
self.y += self.velocityY;
// Check if landed on ground
if (self.y >= groundLevel - self.height / 2) {
self.y = groundLevel - self.height / 2;
self.isJumping = false;
self.velocityY = 0;
}
}
// Move towards target if exists
if (self.targetX !== undefined) {
var moveSpeed = self.moveSpeed * self.stanceData[self.stance].moveSpeedMod;
var dx = self.targetX - self.x;
// If close enough to target, stop moving
if (Math.abs(dx) < moveSpeed) {
self.x = self.targetX;
self.targetX = undefined;
} else {
// Move towards target
self.x += dx > 0 ? moveSpeed : -moveSpeed;
}
}
// Ensure player stays on screen
if (self.x < self.width / 2) {
self.x = self.width / 2;
} else if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
}
};
// Touch event handlers
self.down = function (x, y, obj) {
// Check if the player should attack or jump
if (y < self.y - self.height / 4) {
self.jump();
} else {
self.attack();
}
};
return self;
});
var Projectile = Container.expand(function (stanceType, damage) {
var self = Container.call(this);
// Projectile attributes
self.type = stanceType || 'swan';
self.damage = damage || 10;
self.speed = 15;
self.range = 500; // Max travel distance
self.distanceTraveled = 0;
self.isActive = true;
// Color based on stance
var colors = {
swan: 0xFFFFFF,
crow: 0x222222,
phoenix: 0xFF3300
};
// Visual representation
var projectileSprite = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5,
tint: colors[self.type] || 0xFFFFFF
});
// Update method - called automatically every tick
self.update = function () {
if (!self.isActive) {
return;
}
// Move forward
self.x += self.speed;
self.distanceTraveled += self.speed;
// Check if out of range
if (self.distanceTraveled >= self.range || self.x < 0 || self.x > 2048) {
self.destroy();
var index = projectiles.indexOf(self);
if (index !== -1) {
projectiles.splice(index, 1);
}
return;
}
// Check for collision with enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (self.intersects(enemy) && enemy.isActive) {
// Apply damage based on stance
enemy.takeDamage(self.damage);
// Destroy projectile
self.destroy();
var index = projectiles.indexOf(self);
if (index !== -1) {
projectiles.splice(index, 1);
}
break;
}
}
};
return self;
});
var StanceButton = Container.expand(function (stanceType) {
var self = Container.call(this);
// Button attributes
self.type = stanceType;
// Colors based on stance
var colors = {
swan: 0xFFFFFF,
crow: 0x222222,
phoenix: 0xFF3300
};
// Visual representation
var buttonSprite = self.attachAsset(stanceType === 'swan' ? 'playerSwan' : stanceType === 'crow' ? 'playerCrow' : 'playerPhoenix', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
// Highlight when selected
self.setSelected = function (isSelected) {
if (isSelected) {
tween(buttonSprite, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 300,
easing: tween.easeOut
});
} else {
tween(buttonSprite, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 300,
easing: tween.easeOut
});
}
};
// Touch event handler
self.down = function (x, y, obj) {
// Switch player stance
player.switchStance(self.type);
// Update UI buttons
updateStanceButtons();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111827
});
/****
* Game Code
****/
// Game variables
var player;
var enemies = [];
var platforms = [];
var projectiles = [];
var stanceButtons = {};
var groundLevel = 2500; // Y position of ground
var enemySpawnTimer = 0;
var maxEnemies = 5;
var gameStarted = false;
var difficultyLevel = 1;
var difficultyTimer = 0;
var maxDifficultyLevel = 10;
// Background
var background = game.addChild(LK.getAsset('background', {
x: 0,
y: 0,
anchorX: 0,
anchorY: 0
}));
// Initialize score display
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Initialize health display
var healthTxt = new Text2('Health: 100%', {
size: 80,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(healthTxt);
// Create and position stance buttons
function createStanceButtons() {
var stanceTypes = ['swan', 'crow', 'phoenix'];
var buttonSpacing = 180;
var startX = 2048 / 2 - (stanceTypes.length - 1) * buttonSpacing / 2;
for (var i = 0; i < stanceTypes.length; i++) {
var button = new StanceButton(stanceTypes[i]);
button.x = startX + i * buttonSpacing;
button.y = 2732 - 150;
game.addChild(button);
stanceButtons[stanceTypes[i]] = button;
}
// Set initial selection
updateStanceButtons();
}
// Update stance button visuals
function updateStanceButtons() {
for (var type in stanceButtons) {
stanceButtons[type].setSelected(type === player.stance);
}
}
// Create player
function createPlayer() {
player = new Player();
player.x = 2048 / 2;
player.y = groundLevel - player.height / 2;
game.addChild(player);
}
// Create enemy
function createEnemy() {
var enemy = new Enemy();
// Position enemy on edge of screen
enemy.x = Math.random() < 0.5 ? 100 : 2048 - 100;
enemy.y = groundLevel - enemy.height / 2;
// Scale difficulty
enemy.health = 50 + difficultyLevel * 10;
enemy.maxHealth = enemy.health;
enemy.attackDamage = 5 + difficultyLevel * 2;
enemy.moveSpeed = 2 + difficultyLevel * 0.3;
game.addChild(enemy);
enemies.push(enemy);
}
// Create platforms
function createPlatforms() {
var numPlatforms = 5;
var platformWidth = 300;
for (var i = 0; i < numPlatforms; i++) {
var platform = new Platform();
platform.x = 300 + i * 350;
platform.y = groundLevel - 300 - i * 100;
platform.width = platformWidth;
platform.height = 50;
game.addChild(platform);
platforms.push(platform);
}
}
// Initialize game
function initGame() {
// Reset game state
LK.setScore(0);
enemies = [];
platforms = [];
projectiles = [];
difficultyLevel = 1;
difficultyTimer = 0;
// Create player
createPlayer();
// Create platforms
createPlatforms();
// Create stance buttons
createStanceButtons();
// Start music
LK.playMusic('ballet_theme');
gameStarted = true;
}
// Event handlers
game.down = function (x, y, obj) {
// Start game if not started
if (!gameStarted) {
initGame();
return;
}
// Move player when clicking on game area (not on player or buttons)
var isButtonClick = false;
for (var type in stanceButtons) {
if (stanceButtons[type].intersects({
x: x,
y: y
})) {
isButtonClick = true;
break;
}
}
if (!isButtonClick && !player.intersects({
x: x,
y: y
})) {
player.moveTo(x);
}
};
// Game update loop
game.update = function () {
if (!gameStarted) {
// Show title screen if game not started
if (!titleShown) {
var titleText = new Text2('Ballet of Shadows', {
size: 150,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 2 - 200;
game.addChild(titleText);
var startText = new Text2('Tap to Begin', {
size: 100,
fill: 0xAAAAAA
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 / 2 + 200;
game.addChild(startText);
titleShown = true;
}
return;
}
// Update health display
healthTxt.setText('Health: ' + player.health + '%');
// Spawn enemies on timer
enemySpawnTimer++;
if (enemySpawnTimer >= 120 && enemies.length < maxEnemies) {
// Every 2 seconds (120 frames at 60fps)
createEnemy();
enemySpawnTimer = 0;
}
// Increase difficulty over time
difficultyTimer++;
if (difficultyTimer >= 1800 && difficultyLevel < maxDifficultyLevel) {
// Every 30 seconds
difficultyLevel++;
difficultyTimer = 0;
// Increase max enemies based on difficulty
maxEnemies = 5 + Math.floor(difficultyLevel / 2);
}
// Platform collision detection
if (player.isJumping) {
for (var i = 0; i < platforms.length; i++) {
platforms[i].checkCollision(player);
}
}
// Check for game win condition (example: score 1000)
if (LK.getScore() >= 1000) {
LK.showYouWin();
}
};
// Start with title screen
var titleShown = false;
// Play background music
LK.playMusic('ballet_theme', {
fade: {
start: 0,
end: 0.8,
duration: 1000
}
});