User prompt
Please fix the bug: 'TypeError: tween.create is not a function' in or related to this line: 'var bossExplodeTween = tween.create(secondBoss).to({' Line Number: 574
User prompt
Please fix the bug: 'TypeError: tween.to is not a function' in or related to this line: 'tween.to(secondBoss, {' Line Number: 573
User prompt
en son kötadamı yenince patlasın alttan dünya halkı gelip bizi kurtandın desinler ingilizce bir şekilde
User prompt
Please fix the bug: 'ReferenceError: inSecondBossPhase is not defined' in or related to this line: 'if (inSecondBossPhase && secondBoss) {' Line Number: 541
Initial prompt
Please fix the bug: 'ReferenceError: inSecondBossPhase is not defined' in or related to this line: 'if (inSecondBossPhase && secondBoss) {' Line Number: 541
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Boss Class var Boss = Container.expand(function () { var self = Container.call(this); // Attach enemy ball asset (boss face) var bossFace = self.attachAsset('enemyBall', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.2, scaleY: 2.2 }); self.bossFace = bossFace; self.speed = 8; self.targetX = 2048 / 2; self.targetY = 800; // Boss starts at top, then chases player self.x = 2048 / 2; self.y = -bossFace.height; self.update = function () { // Move boss into screen, then chase player if (self.y < self.targetY) { self.y += 18; if (self.y > self.targetY) self.y = self.targetY; } else { // Chase player var dx = playerBall.x - self.x; var dy = playerBall.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed * 0.7; } } }; return self; }); // Damage Ball Class var DamageBall = Container.expand(function () { var self = Container.call(this); // Attach a yellow ball asset var ball = self.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.1, scaleY: 1.1 }); ball.tint = 0xffe600; self.ball = ball; self.speed = 18; // Random X within game area var r = ball.width / 2; self.x = Math.random() * (2048 - 2 * r) + r; self.y = -r - 10; self.update = function () { self.y += self.speed; }; return self; }); // Start the boss chase phase // Enemy Ball Class var EnemyBall = Container.expand(function () { var self = Container.call(this); // Attach enemy ball asset (red circle) var ball = self.attachAsset('enemyBall', { anchorX: 0.5, anchorY: 0.5 }); // Assign ball asset to self.ball for external access self.ball = ball; // Speed will be set on creation self.speed = 6; // Update method: move down self.update = function () { self.y += self.speed; }; return self; }); // Player Ball Class var PlayerBall = Container.expand(function () { var self = Container.call(this); // Attach player ball asset (blue circle) var ball = self.attachAsset('playerBall', { anchorX: 0.5, anchorY: 0.5 }); // For possible future use (e.g., effects) self.ball = ball; // No update needed; position is controlled by drag return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 // Dark background }); /**** * Game Code ****/ // Start the boss chase phase // --- Game Variables --- // Tween plugin for future use (animations, etc.) // --- Asset Initialization (shapes) --- function startChasePhase() { inChasePhase = true; // Remove all enemies for (var i = 0; i < enemyBalls.length; i++) { enemyBalls[i].destroy(); } enemyBalls = []; // Spawn boss boss = new Boss(); game.addChild(boss); // Show boss health if (bossHealthTxt && bossHealthTxt.parent) bossHealthTxt.parent.removeChild(bossHealthTxt); bossHealth = 500; bossHealthTxt = new Text2("Boss: " + bossHealth, { size: 90, fill: 0xffe600, font: "Arial, Helvetica, sans-serif" }); // Place boss health bar below score, yellow color bossHealthTxt.anchor.set(0.5, 0); bossHealthTxt.x = 2048 / 2; bossHealthTxt.y = 120; // just below score LK.gui.top.addChild(bossHealthTxt); // Second boss phase variables (moved to global scope for access in update and reset) var secondBoss = null; var secondBossHealth = 1000; var secondBossHealthTxt = null; var inSecondBossPhase = false; var secondBossDamageBalls = []; var secondBossDamageBallSpawnTicks = 0; // Reset damage balls for (var j = 0; j < damageBalls.length; j++) { damageBalls[j].destroy(); } damageBalls = []; damageBallSpawnTicks = 0; } // Spawn a damage ball function spawnDamageBall() { var dball = new DamageBall(); damageBalls.push(dball); game.addChild(dball); } var playerBall; var enemyBalls = []; var lives = 3; var score = 0; var spawnInterval = 80; // Initial enemy spawn interval (ticks) var enemySpeed = 6; // Initial enemy speed var ticksSinceLastSpawn = 0; var dragNode = null; var lastGameOver = false; // --- Boss & Chase Phase --- var freezeEnemies = false; var freezeTicks = 0; var chasePhaseStarted = false; var boss = null; var bossHealth = 500; var bossHealthTxt = null; var damageBalls = []; var damageBallSpawnTicks = 0; var inChasePhase = false; // --- Tap to Start Overlay & Villain Dialog Control --- // (Removed tap-to-start overlay and tap logic. Game/dialog starts immediately.) var gameStarted = true; // Control for villain dialog per level (no tap-to-start) var waitingForTap = false; var pendingVillainText = null; var pendingVillainState = null; // Helper to show villain dialog (no tap-to-start overlay) function showVillainAndWait(text, nextState) { // Show villain dialog showVillainBubble(text); waitingForTap = false; pendingVillainText = text; pendingVillainState = nextState; gameStarted = true; } // --- UI Elements --- // Score Text var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Lives Text var livesTxt = new Text2('❤ 3', { size: 90, fill: 0xFF5252 }); livesTxt.anchor.set(1, 0); LK.gui.topRight.addChild(livesTxt); // --- Player Ball Setup --- playerBall = new PlayerBall(); game.addChild(playerBall); // Start at bottom center, above the bottom edge playerBall.x = 2048 / 2; playerBall.y = 2732 - 220; // --- Drag Handling --- function handleMove(x, y, obj) { if (dragNode) { // Clamp to game area (keep ball fully visible) var r = playerBall.ball.width / 2; var minX = r, maxX = 2048 - r; var minY = r, maxY = 2732 - r; dragNode.x = Math.max(minX, Math.min(maxX, x)); dragNode.y = Math.max(minY, Math.min(maxY, y)); } } game.move = handleMove; game.down = function (x, y, obj) { // Only start drag if touch/click is on the player ball var dx = x - playerBall.x; var dy = y - playerBall.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= playerBall.ball.width / 2) { dragNode = playerBall; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // --- Enemy Spawning --- function spawnEnemy() { var enemy = new EnemyBall(); // Random X within game area, keeping ball fully visible var r = enemy.ball.width / 2; enemy.x = Math.random() * (2048 - 2 * r) + r; enemy.y = -r - 10; // Start just above the screen enemy.speed = enemySpeed + Math.random() * 2; // Add a bit of speed variation enemyBalls.push(enemy); game.addChild(enemy); } // --- Story & Level State --- var villainState = 0; // 0: intro, 1: after 50, 2: after 100, 3: after 250 (win) var villainBubble = null; var villainTimeout = null; var villainShowTicks = 0; var villainShowDuration = 180; // 3 seconds at 60fps function showVillainBubble(text) { // Remove previous if exists if (villainBubble && villainBubble.parent) villainBubble.parent.removeChild(villainBubble); villainBubble = new Container(); // Villain face (red ball) var face = LK.getAsset('enemyBall', { anchorX: 0.5, anchorY: 1 }); face.x = 0; face.y = 0; villainBubble.addChild(face); // Speech bubble (white rounded box) var bubble = new Container(); var bubbleShape = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.2 }); bubbleShape.tint = 0xffffff; bubbleShape.alpha = 0.92; bubble.addChild(bubbleShape); // Text (English, white) var txt = new Text2(text, { size: 80, fill: 0xffffff, font: "Arial, Helvetica, sans-serif" }); txt.anchor.set(0.5, 0.5); bubble.addChild(txt); bubble.x = 0; bubble.y = -180; villainBubble.addChild(bubble); // Position villainBubble at top center, but lower for readability villainBubble.x = 2048 / 2; villainBubble.y = 520; // moved down from 320 to 520 game.addChild(villainBubble); villainShowTicks = 0; } function removeVillainBubble() { if (villainBubble && villainBubble.parent) villainBubble.parent.removeChild(villainBubble); villainBubble = null; villainShowTicks = 0; } // --- Game Update Loop --- game.update = function () { // --- Villain Dialog Logic --- if (villainBubble) { villainShowTicks++; if (villainShowTicks > villainShowDuration) { removeVillainBubble(); } } // --- Level/Story Progression --- // At each story point, show villain and advance automatically if (villainState === 0 && LK.ticks < 10) { showVillainAndWait("I will destroy the world!", 1); villainState = 0.25; } if (villainState === 0.25) { // Remove villain bubble after duration, then advance if (!villainBubble) { villainState = 1; } } if (villainState === 1 && score >= 50) { showVillainAndWait("You're finished now!", 2); // Speed up balls spawnInterval = Math.max(30, spawnInterval - 18); enemySpeed += 3.5; villainState = 1.25; } if (villainState === 1.25) { if (!villainBubble) { villainState = 2; } } if (villainState === 2 && score >= 100) { showVillainAndWait("Enough!", 3); // Balls get much faster spawnInterval = Math.max(18, spawnInterval - 10); enemySpeed += 4.5; villainState = 2.25; } if (villainState === 2.25) { if (!villainBubble) { villainState = 3; } } if (villainState === 3 && score >= 250) { showVillainAndWait("Stop right there!", 4); villainState = 3.25; // Freeze all enemies, but allow player to move for (var i = 0; i < enemyBalls.length; i++) { enemyBalls[i].frozen = true; } // Prevent new enemies from spawning freezeEnemies = true; freezeTicks = LK.ticks; chasePhaseStarted = false; } if (villainState === 3.25) { if (!villainBubble && !chasePhaseStarted) { // After villain dialog, wait 10 seconds, then start chase LK.setTimeout(function () { showVillainAndWait("Taste this!", 5); // After dialog, spawn boss and damage balls LK.setTimeout(function () { startChasePhase(); }, villainShowDuration * 16.7); // villainShowDuration is in ticks, convert to ms }, 10000); chasePhaseStarted = true; } // Do not advance villainState until chase phase is started } // --- Enemy Spawning --- if (!freezeEnemies && !inChasePhase) { ticksSinceLastSpawn++; if (ticksSinceLastSpawn >= spawnInterval) { spawnEnemy(); ticksSinceLastSpawn = 0; } } // --- Difficulty Scaling --- if (!freezeEnemies && !inChasePhase) { // Every 600 ticks (~10 seconds), increase difficulty if (LK.ticks % 600 === 0 && LK.ticks > 0) { if (spawnInterval > 30) spawnInterval -= 8; // Faster spawns if (enemySpeed < 22) enemySpeed += 1.2; // Faster enemies } } // --- Update Enemies --- for (var i = enemyBalls.length - 1; i >= 0; i--) { var enemy = enemyBalls[i]; if (!enemy.frozen) { enemy.update(); } // Check collision with player var collides = enemy.intersects(playerBall); if (collides && !enemy.frozen) { // Remove enemy enemy.destroy(); enemyBalls.splice(i, 1); // Lose a life lives--; livesTxt.setText('❤ ' + lives); // Flash player ball LK.effects.flashObject(playerBall, 0xff0000, 400); // Game over if no lives left if (lives <= 0 && !lastGameOver) { lastGameOver = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } continue; } // Remove if off screen if (enemy.y - enemy.ball.height / 2 > 2732 + 40) { enemy.destroy(); enemyBalls.splice(i, 1); // Score for dodging score++; scoreTxt.setText(score + ''); } } // --- Boss Chase Phase --- if (inChasePhase && boss) { boss.update(); // Check collision with player (lose instantly) if (boss.intersects(playerBall) && !lastGameOver) { lastGameOver = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } // Update damage balls for (var j = damageBalls.length - 1; j >= 0; j--) { var dball = damageBalls[j]; dball.update(); // If player collects damage ball if (dball.intersects(playerBall)) { // Remove damage ball dball.destroy(); damageBalls.splice(j, 1); // Deal 20 damage to boss bossHealth -= 20; if (bossHealth < 0) bossHealth = 0; if (bossHealthTxt) bossHealthTxt.setText("Boss: " + bossHealth); // Flash boss LK.effects.flashObject(boss, 0xffe600, 400); // If boss defeated, start second boss phase if (bossHealth <= 0 && !inSecondBossPhase) { // Remove first boss and health bar if (boss && boss.parent) boss.parent.removeChild(boss); boss = null; if (bossHealthTxt && bossHealthTxt.parent) bossHealthTxt.parent.removeChild(bossHealthTxt); bossHealthTxt = null; // Remove all damage balls for (var k = 0; k < damageBalls.length; k++) { damageBalls[k].destroy(); } damageBalls = []; inChasePhase = false; // Villain dialog: "How is this possible?!" showVillainAndWait("How is this possible?!", 100); villainState = 99.5; inSecondBossPhase = true; // After dialog, start second boss attack after 2 seconds LK.setTimeout(function () { showVillainAndWait("Now face me!", 101); LK.setTimeout(function () { // Spawn second boss secondBoss = new Boss(); secondBossHealth = 1000; secondBoss.speed = 13; // faster and angrier secondBoss.targetY = 700; secondBoss.bossFace.tint = 0xff2222; // visually more angry game.addChild(secondBoss); // Show second boss health bar if (secondBossHealthTxt && secondBossHealthTxt.parent) secondBossHealthTxt.parent.removeChild(secondBossHealthTxt); secondBossHealthTxt = new Text2("Villain: " + secondBossHealth, { size: 90, fill: 0xffe600, font: "Arial, Helvetica, sans-serif" }); secondBossHealthTxt.anchor.set(0.5, 0); secondBossHealthTxt.x = 2048 / 2; secondBossHealthTxt.y = 220; // below score and first boss bar LK.gui.top.addChild(secondBossHealthTxt); // Reset second boss damage balls for (var m = 0; m < secondBossDamageBalls.length; m++) { secondBossDamageBalls[m].destroy(); } secondBossDamageBalls = []; secondBossDamageBallSpawnTicks = 0; }, villainShowDuration * 16.7); }, 2000); return; } } // Remove if off screen if (dball.y > 2732 + 80) { dball.destroy(); damageBalls.splice(j, 1); } } // Spawn damage balls every 90 ticks damageBallSpawnTicks++; if (damageBallSpawnTicks >= 90) { spawnDamageBall(); damageBallSpawnTicks = 0; } } // --- Second Boss Phase --- if (inSecondBossPhase && secondBoss) { secondBoss.update(); // If villain collides with player, game over if (secondBoss.intersects(playerBall) && !lastGameOver) { lastGameOver = true; LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); return; } // Update second boss damage balls for (var n = secondBossDamageBalls.length - 1; n >= 0; n--) { var sdball = secondBossDamageBalls[n]; sdball.update(); // If player collects damage ball if (sdball.intersects(playerBall)) { sdball.destroy(); secondBossDamageBalls.splice(n, 1); // Deal 50 damage to second boss secondBossHealth -= 50; if (secondBossHealth < 0) secondBossHealth = 0; if (secondBossHealthTxt) secondBossHealthTxt.setText("Villain: " + secondBossHealth); LK.effects.flashObject(secondBoss, 0xffe600, 400); // If second boss defeated if (secondBossHealth <= 0 && !lastGameOver) { lastGameOver = true; if (secondBoss && secondBoss.parent) secondBoss.parent.removeChild(secondBoss); if (secondBossHealthTxt && secondBossHealthTxt.parent) secondBossHealthTxt.parent.removeChild(secondBossHealthTxt); // Remove all second boss damage balls for (var p = 0; p < secondBossDamageBalls.length; p++) { secondBossDamageBalls[p].destroy(); } secondBossDamageBalls = []; // Final villain dialog: "No! How could you?!" showVillainAndWait("No! How could you?!", 200); LK.setTimeout(function () { LK.effects.flashScreen(0x00ff00, 800); LK.showYouWin(); }, 1800); return; } } // Remove if off screen if (sdball.y > 2732 + 80) { sdball.destroy(); secondBossDamageBalls.splice(n, 1); } } // Spawn second boss damage balls every 60 ticks (faster) secondBossDamageBallSpawnTicks++; if (secondBossDamageBallSpawnTicks >= 60) { var sdb = new DamageBall(); sdb.ball.tint = 0xffe600; secondBossDamageBalls.push(sdb); game.addChild(sdb); secondBossDamageBallSpawnTicks = 0; } } }; // --- Reset Handler (for when game restarts) --- game.on('destroy', function () { // Clean up for (var i = 0; i < enemyBalls.length; i++) { enemyBalls[i].destroy(); } enemyBalls = []; // Remove boss if exists if (boss && boss.parent) boss.parent.removeChild(boss); boss = null; // Remove boss health text if (bossHealthTxt && bossHealthTxt.parent) bossHealthTxt.parent.removeChild(bossHealthTxt); bossHealthTxt = null; // Remove damage balls for (var j = 0; j < damageBalls.length; j++) { damageBalls[j].destroy(); } damageBalls = []; inChasePhase = false; freezeEnemies = false; chasePhaseStarted = false; bossHealth = 500; damageBallSpawnTicks = 0; // Remove second boss and its UI if (secondBoss && secondBoss.parent) secondBoss.parent.removeChild(secondBoss); secondBoss = null; if (secondBossHealthTxt && secondBossHealthTxt.parent) secondBossHealthTxt.parent.removeChild(secondBossHealthTxt); secondBossHealthTxt = null; for (var q = 0; q < secondBossDamageBalls.length; q++) { secondBossDamageBalls[q].destroy(); } secondBossDamageBalls = []; inSecondBossPhase = false; secondBossHealth = 1000; secondBossDamageBallSpawnTicks = 0; lives = 3; score = 0; spawnInterval = 80; enemySpeed = 6; ticksSinceLastSpawn = 0; lastGameOver = false; scoreTxt.setText('0'); livesTxt.setText('❤ 3'); // Reset player position playerBall.x = 2048 / 2; playerBall.y = 2732 - 220; // Reset gameStarted and villain state gameStarted = true; waitingForTap = false; pendingVillainText = null; pendingVillainState = null; villainState = 0; removeVillainBubble(); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Boss Class
var Boss = Container.expand(function () {
var self = Container.call(this);
// Attach enemy ball asset (boss face)
var bossFace = self.attachAsset('enemyBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2
});
self.bossFace = bossFace;
self.speed = 8;
self.targetX = 2048 / 2;
self.targetY = 800;
// Boss starts at top, then chases player
self.x = 2048 / 2;
self.y = -bossFace.height;
self.update = function () {
// Move boss into screen, then chase player
if (self.y < self.targetY) {
self.y += 18;
if (self.y > self.targetY) self.y = self.targetY;
} else {
// Chase player
var dx = playerBall.x - self.x;
var dy = playerBall.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed * 0.7;
}
}
};
return self;
});
// Damage Ball Class
var DamageBall = Container.expand(function () {
var self = Container.call(this);
// Attach a yellow ball asset
var ball = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1
});
ball.tint = 0xffe600;
self.ball = ball;
self.speed = 18;
// Random X within game area
var r = ball.width / 2;
self.x = Math.random() * (2048 - 2 * r) + r;
self.y = -r - 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Start the boss chase phase
// Enemy Ball Class
var EnemyBall = Container.expand(function () {
var self = Container.call(this);
// Attach enemy ball asset (red circle)
var ball = self.attachAsset('enemyBall', {
anchorX: 0.5,
anchorY: 0.5
});
// Assign ball asset to self.ball for external access
self.ball = ball;
// Speed will be set on creation
self.speed = 6;
// Update method: move down
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ball Class
var PlayerBall = Container.expand(function () {
var self = Container.call(this);
// Attach player ball asset (blue circle)
var ball = self.attachAsset('playerBall', {
anchorX: 0.5,
anchorY: 0.5
});
// For possible future use (e.g., effects)
self.ball = ball;
// No update needed; position is controlled by drag
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818 // Dark background
});
/****
* Game Code
****/
// Start the boss chase phase
// --- Game Variables ---
// Tween plugin for future use (animations, etc.)
// --- Asset Initialization (shapes) ---
function startChasePhase() {
inChasePhase = true;
// Remove all enemies
for (var i = 0; i < enemyBalls.length; i++) {
enemyBalls[i].destroy();
}
enemyBalls = [];
// Spawn boss
boss = new Boss();
game.addChild(boss);
// Show boss health
if (bossHealthTxt && bossHealthTxt.parent) bossHealthTxt.parent.removeChild(bossHealthTxt);
bossHealth = 500;
bossHealthTxt = new Text2("Boss: " + bossHealth, {
size: 90,
fill: 0xffe600,
font: "Arial, Helvetica, sans-serif"
});
// Place boss health bar below score, yellow color
bossHealthTxt.anchor.set(0.5, 0);
bossHealthTxt.x = 2048 / 2;
bossHealthTxt.y = 120; // just below score
LK.gui.top.addChild(bossHealthTxt);
// Second boss phase variables (moved to global scope for access in update and reset)
var secondBoss = null;
var secondBossHealth = 1000;
var secondBossHealthTxt = null;
var inSecondBossPhase = false;
var secondBossDamageBalls = [];
var secondBossDamageBallSpawnTicks = 0;
// Reset damage balls
for (var j = 0; j < damageBalls.length; j++) {
damageBalls[j].destroy();
}
damageBalls = [];
damageBallSpawnTicks = 0;
}
// Spawn a damage ball
function spawnDamageBall() {
var dball = new DamageBall();
damageBalls.push(dball);
game.addChild(dball);
}
var playerBall;
var enemyBalls = [];
var lives = 3;
var score = 0;
var spawnInterval = 80; // Initial enemy spawn interval (ticks)
var enemySpeed = 6; // Initial enemy speed
var ticksSinceLastSpawn = 0;
var dragNode = null;
var lastGameOver = false;
// --- Boss & Chase Phase ---
var freezeEnemies = false;
var freezeTicks = 0;
var chasePhaseStarted = false;
var boss = null;
var bossHealth = 500;
var bossHealthTxt = null;
var damageBalls = [];
var damageBallSpawnTicks = 0;
var inChasePhase = false;
// --- Tap to Start Overlay & Villain Dialog Control ---
// (Removed tap-to-start overlay and tap logic. Game/dialog starts immediately.)
var gameStarted = true;
// Control for villain dialog per level (no tap-to-start)
var waitingForTap = false;
var pendingVillainText = null;
var pendingVillainState = null;
// Helper to show villain dialog (no tap-to-start overlay)
function showVillainAndWait(text, nextState) {
// Show villain dialog
showVillainBubble(text);
waitingForTap = false;
pendingVillainText = text;
pendingVillainState = nextState;
gameStarted = true;
}
// --- UI Elements ---
// Score Text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Lives Text
var livesTxt = new Text2('❤ 3', {
size: 90,
fill: 0xFF5252
});
livesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(livesTxt);
// --- Player Ball Setup ---
playerBall = new PlayerBall();
game.addChild(playerBall);
// Start at bottom center, above the bottom edge
playerBall.x = 2048 / 2;
playerBall.y = 2732 - 220;
// --- Drag Handling ---
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to game area (keep ball fully visible)
var r = playerBall.ball.width / 2;
var minX = r,
maxX = 2048 - r;
var minY = r,
maxY = 2732 - r;
dragNode.x = Math.max(minX, Math.min(maxX, x));
dragNode.y = Math.max(minY, Math.min(maxY, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only start drag if touch/click is on the player ball
var dx = x - playerBall.x;
var dy = y - playerBall.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= playerBall.ball.width / 2) {
dragNode = playerBall;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Enemy Spawning ---
function spawnEnemy() {
var enemy = new EnemyBall();
// Random X within game area, keeping ball fully visible
var r = enemy.ball.width / 2;
enemy.x = Math.random() * (2048 - 2 * r) + r;
enemy.y = -r - 10; // Start just above the screen
enemy.speed = enemySpeed + Math.random() * 2; // Add a bit of speed variation
enemyBalls.push(enemy);
game.addChild(enemy);
}
// --- Story & Level State ---
var villainState = 0; // 0: intro, 1: after 50, 2: after 100, 3: after 250 (win)
var villainBubble = null;
var villainTimeout = null;
var villainShowTicks = 0;
var villainShowDuration = 180; // 3 seconds at 60fps
function showVillainBubble(text) {
// Remove previous if exists
if (villainBubble && villainBubble.parent) villainBubble.parent.removeChild(villainBubble);
villainBubble = new Container();
// Villain face (red ball)
var face = LK.getAsset('enemyBall', {
anchorX: 0.5,
anchorY: 1
});
face.x = 0;
face.y = 0;
villainBubble.addChild(face);
// Speech bubble (white rounded box)
var bubble = new Container();
var bubbleShape = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2
});
bubbleShape.tint = 0xffffff;
bubbleShape.alpha = 0.92;
bubble.addChild(bubbleShape);
// Text (English, white)
var txt = new Text2(text, {
size: 80,
fill: 0xffffff,
font: "Arial, Helvetica, sans-serif"
});
txt.anchor.set(0.5, 0.5);
bubble.addChild(txt);
bubble.x = 0;
bubble.y = -180;
villainBubble.addChild(bubble);
// Position villainBubble at top center, but lower for readability
villainBubble.x = 2048 / 2;
villainBubble.y = 520; // moved down from 320 to 520
game.addChild(villainBubble);
villainShowTicks = 0;
}
function removeVillainBubble() {
if (villainBubble && villainBubble.parent) villainBubble.parent.removeChild(villainBubble);
villainBubble = null;
villainShowTicks = 0;
}
// --- Game Update Loop ---
game.update = function () {
// --- Villain Dialog Logic ---
if (villainBubble) {
villainShowTicks++;
if (villainShowTicks > villainShowDuration) {
removeVillainBubble();
}
}
// --- Level/Story Progression ---
// At each story point, show villain and advance automatically
if (villainState === 0 && LK.ticks < 10) {
showVillainAndWait("I will destroy the world!", 1);
villainState = 0.25;
}
if (villainState === 0.25) {
// Remove villain bubble after duration, then advance
if (!villainBubble) {
villainState = 1;
}
}
if (villainState === 1 && score >= 50) {
showVillainAndWait("You're finished now!", 2);
// Speed up balls
spawnInterval = Math.max(30, spawnInterval - 18);
enemySpeed += 3.5;
villainState = 1.25;
}
if (villainState === 1.25) {
if (!villainBubble) {
villainState = 2;
}
}
if (villainState === 2 && score >= 100) {
showVillainAndWait("Enough!", 3);
// Balls get much faster
spawnInterval = Math.max(18, spawnInterval - 10);
enemySpeed += 4.5;
villainState = 2.25;
}
if (villainState === 2.25) {
if (!villainBubble) {
villainState = 3;
}
}
if (villainState === 3 && score >= 250) {
showVillainAndWait("Stop right there!", 4);
villainState = 3.25;
// Freeze all enemies, but allow player to move
for (var i = 0; i < enemyBalls.length; i++) {
enemyBalls[i].frozen = true;
}
// Prevent new enemies from spawning
freezeEnemies = true;
freezeTicks = LK.ticks;
chasePhaseStarted = false;
}
if (villainState === 3.25) {
if (!villainBubble && !chasePhaseStarted) {
// After villain dialog, wait 10 seconds, then start chase
LK.setTimeout(function () {
showVillainAndWait("Taste this!", 5);
// After dialog, spawn boss and damage balls
LK.setTimeout(function () {
startChasePhase();
}, villainShowDuration * 16.7); // villainShowDuration is in ticks, convert to ms
}, 10000);
chasePhaseStarted = true;
}
// Do not advance villainState until chase phase is started
}
// --- Enemy Spawning ---
if (!freezeEnemies && !inChasePhase) {
ticksSinceLastSpawn++;
if (ticksSinceLastSpawn >= spawnInterval) {
spawnEnemy();
ticksSinceLastSpawn = 0;
}
}
// --- Difficulty Scaling ---
if (!freezeEnemies && !inChasePhase) {
// Every 600 ticks (~10 seconds), increase difficulty
if (LK.ticks % 600 === 0 && LK.ticks > 0) {
if (spawnInterval > 30) spawnInterval -= 8; // Faster spawns
if (enemySpeed < 22) enemySpeed += 1.2; // Faster enemies
}
}
// --- Update Enemies ---
for (var i = enemyBalls.length - 1; i >= 0; i--) {
var enemy = enemyBalls[i];
if (!enemy.frozen) {
enemy.update();
}
// Check collision with player
var collides = enemy.intersects(playerBall);
if (collides && !enemy.frozen) {
// Remove enemy
enemy.destroy();
enemyBalls.splice(i, 1);
// Lose a life
lives--;
livesTxt.setText('❤ ' + lives);
// Flash player ball
LK.effects.flashObject(playerBall, 0xff0000, 400);
// Game over if no lives left
if (lives <= 0 && !lastGameOver) {
lastGameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
continue;
}
// Remove if off screen
if (enemy.y - enemy.ball.height / 2 > 2732 + 40) {
enemy.destroy();
enemyBalls.splice(i, 1);
// Score for dodging
score++;
scoreTxt.setText(score + '');
}
}
// --- Boss Chase Phase ---
if (inChasePhase && boss) {
boss.update();
// Check collision with player (lose instantly)
if (boss.intersects(playerBall) && !lastGameOver) {
lastGameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Update damage balls
for (var j = damageBalls.length - 1; j >= 0; j--) {
var dball = damageBalls[j];
dball.update();
// If player collects damage ball
if (dball.intersects(playerBall)) {
// Remove damage ball
dball.destroy();
damageBalls.splice(j, 1);
// Deal 20 damage to boss
bossHealth -= 20;
if (bossHealth < 0) bossHealth = 0;
if (bossHealthTxt) bossHealthTxt.setText("Boss: " + bossHealth);
// Flash boss
LK.effects.flashObject(boss, 0xffe600, 400);
// If boss defeated, start second boss phase
if (bossHealth <= 0 && !inSecondBossPhase) {
// Remove first boss and health bar
if (boss && boss.parent) boss.parent.removeChild(boss);
boss = null;
if (bossHealthTxt && bossHealthTxt.parent) bossHealthTxt.parent.removeChild(bossHealthTxt);
bossHealthTxt = null;
// Remove all damage balls
for (var k = 0; k < damageBalls.length; k++) {
damageBalls[k].destroy();
}
damageBalls = [];
inChasePhase = false;
// Villain dialog: "How is this possible?!"
showVillainAndWait("How is this possible?!", 100);
villainState = 99.5;
inSecondBossPhase = true;
// After dialog, start second boss attack after 2 seconds
LK.setTimeout(function () {
showVillainAndWait("Now face me!", 101);
LK.setTimeout(function () {
// Spawn second boss
secondBoss = new Boss();
secondBossHealth = 1000;
secondBoss.speed = 13; // faster and angrier
secondBoss.targetY = 700;
secondBoss.bossFace.tint = 0xff2222; // visually more angry
game.addChild(secondBoss);
// Show second boss health bar
if (secondBossHealthTxt && secondBossHealthTxt.parent) secondBossHealthTxt.parent.removeChild(secondBossHealthTxt);
secondBossHealthTxt = new Text2("Villain: " + secondBossHealth, {
size: 90,
fill: 0xffe600,
font: "Arial, Helvetica, sans-serif"
});
secondBossHealthTxt.anchor.set(0.5, 0);
secondBossHealthTxt.x = 2048 / 2;
secondBossHealthTxt.y = 220; // below score and first boss bar
LK.gui.top.addChild(secondBossHealthTxt);
// Reset second boss damage balls
for (var m = 0; m < secondBossDamageBalls.length; m++) {
secondBossDamageBalls[m].destroy();
}
secondBossDamageBalls = [];
secondBossDamageBallSpawnTicks = 0;
}, villainShowDuration * 16.7);
}, 2000);
return;
}
}
// Remove if off screen
if (dball.y > 2732 + 80) {
dball.destroy();
damageBalls.splice(j, 1);
}
}
// Spawn damage balls every 90 ticks
damageBallSpawnTicks++;
if (damageBallSpawnTicks >= 90) {
spawnDamageBall();
damageBallSpawnTicks = 0;
}
}
// --- Second Boss Phase ---
if (inSecondBossPhase && secondBoss) {
secondBoss.update();
// If villain collides with player, game over
if (secondBoss.intersects(playerBall) && !lastGameOver) {
lastGameOver = true;
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Update second boss damage balls
for (var n = secondBossDamageBalls.length - 1; n >= 0; n--) {
var sdball = secondBossDamageBalls[n];
sdball.update();
// If player collects damage ball
if (sdball.intersects(playerBall)) {
sdball.destroy();
secondBossDamageBalls.splice(n, 1);
// Deal 50 damage to second boss
secondBossHealth -= 50;
if (secondBossHealth < 0) secondBossHealth = 0;
if (secondBossHealthTxt) secondBossHealthTxt.setText("Villain: " + secondBossHealth);
LK.effects.flashObject(secondBoss, 0xffe600, 400);
// If second boss defeated
if (secondBossHealth <= 0 && !lastGameOver) {
lastGameOver = true;
if (secondBoss && secondBoss.parent) secondBoss.parent.removeChild(secondBoss);
if (secondBossHealthTxt && secondBossHealthTxt.parent) secondBossHealthTxt.parent.removeChild(secondBossHealthTxt);
// Remove all second boss damage balls
for (var p = 0; p < secondBossDamageBalls.length; p++) {
secondBossDamageBalls[p].destroy();
}
secondBossDamageBalls = [];
// Final villain dialog: "No! How could you?!"
showVillainAndWait("No! How could you?!", 200);
LK.setTimeout(function () {
LK.effects.flashScreen(0x00ff00, 800);
LK.showYouWin();
}, 1800);
return;
}
}
// Remove if off screen
if (sdball.y > 2732 + 80) {
sdball.destroy();
secondBossDamageBalls.splice(n, 1);
}
}
// Spawn second boss damage balls every 60 ticks (faster)
secondBossDamageBallSpawnTicks++;
if (secondBossDamageBallSpawnTicks >= 60) {
var sdb = new DamageBall();
sdb.ball.tint = 0xffe600;
secondBossDamageBalls.push(sdb);
game.addChild(sdb);
secondBossDamageBallSpawnTicks = 0;
}
}
};
// --- Reset Handler (for when game restarts) ---
game.on('destroy', function () {
// Clean up
for (var i = 0; i < enemyBalls.length; i++) {
enemyBalls[i].destroy();
}
enemyBalls = [];
// Remove boss if exists
if (boss && boss.parent) boss.parent.removeChild(boss);
boss = null;
// Remove boss health text
if (bossHealthTxt && bossHealthTxt.parent) bossHealthTxt.parent.removeChild(bossHealthTxt);
bossHealthTxt = null;
// Remove damage balls
for (var j = 0; j < damageBalls.length; j++) {
damageBalls[j].destroy();
}
damageBalls = [];
inChasePhase = false;
freezeEnemies = false;
chasePhaseStarted = false;
bossHealth = 500;
damageBallSpawnTicks = 0;
// Remove second boss and its UI
if (secondBoss && secondBoss.parent) secondBoss.parent.removeChild(secondBoss);
secondBoss = null;
if (secondBossHealthTxt && secondBossHealthTxt.parent) secondBossHealthTxt.parent.removeChild(secondBossHealthTxt);
secondBossHealthTxt = null;
for (var q = 0; q < secondBossDamageBalls.length; q++) {
secondBossDamageBalls[q].destroy();
}
secondBossDamageBalls = [];
inSecondBossPhase = false;
secondBossHealth = 1000;
secondBossDamageBallSpawnTicks = 0;
lives = 3;
score = 0;
spawnInterval = 80;
enemySpeed = 6;
ticksSinceLastSpawn = 0;
lastGameOver = false;
scoreTxt.setText('0');
livesTxt.setText('❤ 3');
// Reset player position
playerBall.x = 2048 / 2;
playerBall.y = 2732 - 220;
// Reset gameStarted and villain state
gameStarted = true;
waitingForTap = false;
pendingVillainText = null;
pendingVillainState = null;
villainState = 0;
removeVillainBubble();
});