User prompt
Please fix the bug: 'Uncaught ReferenceError: Player is not defined' in or related to this line: 'player = game.addChild(new Player()); // Stwórz nowego gracza' Line Number: 1229
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: Player is not defined' in or related to this line: 'player = game.addChild(new Player()); // Stwórz nowego gracza' Line Number: 1228
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught RangeError: Maximum call stack size exceeded' in or related to this line: 'this.handleFakeTutorialInput(); // REMOVED self-recursive call that caused stack overflow' Line Number: 1199
User prompt
Please fix the bug: 'Uncaught ReferenceError: Player is not defined' in or related to this line: 'player = game.addChild(new Player()); // Stwórz nowego gracza' Line Number: 1196
Code edit (4 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught RangeError: Maximum call stack size exceeded' in or related to this line: 'this.handleFakeTutorialInput(); // REMOVED self-recursive call that caused stack overflow' Line Number: 1293
Code edit (1 edits merged)
Please save this source code
User prompt
add new asset grillMenu
Code edit (1 edits merged)
Please save this source code
User prompt
add asset bossHpbar
Code edit (5 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught RangeError: Maximum call stack size exceeded' in or related to this line: 'this.handleFakeTutorialInput(); // Wywołaj logikę fałszywej śmierci' Line Number: 1228
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'set')' in or related to this line: 'self.bossHealthBarBg.anchor.set(0.5, 0.5); // Ustaw punkt odniesienia na środek' Line Number: 705
User prompt
Please fix the bug: 'Shape is not defined' in or related to this line: 'self.bossHealthBarBg = new Shape({' Line Number: 677
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: playerRadius is not defined' in or related to this line: 'if (distance < attack.radius + playerRadius) {' Line Number: 347
Code edit (9 edits merged)
Please save this source code
User prompt
add asset introBg
User prompt
add asset titleBg
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Button2 is not defined' in or related to this line: 'var startButton = new Button2("Start Game", function () {' Line Number: 574
User prompt
Please fix the bug: 'clearScene is not defined' in or related to this line: 'clearScene();' Line Number: 555
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { totalDeaths: 0, maxHearts: 3, currentLevel: 0, bossesDefeated: 0 }); /**** * Classes ****/ var Boss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.speed = 5; self.attackCooldown = 0; self.attackPattern = 0; self.attacks = []; self.stunned = false; self.stunDuration = 0; self.dead = false; self.phase = 1; self.startAttackPattern = function () { if (self.dead) { return; } self.attackPattern = (self.attackPattern + 1) % 3; switch (self.attackPattern) { case 0: self.circleAttack(); break; case 1: self.lineAttack(); break; case 2: self.chargeAttack(); break; } // Schedule next attack self.attackCooldown = 90 + Math.floor(Math.random() * 60); // 1.5-2.5 seconds }; self.circleAttack = function () { LK.getSound('bossAttack').play(); var center = { x: self.x, y: self.y }; var count = 8; var radius = 300; for (var i = 0; i < count; i++) { var angle = i / count * Math.PI * 2; var posX = center.x + Math.cos(angle) * radius; var posY = center.y + Math.sin(angle) * radius; self.createAttack(posX, posY, 3000); } }; self.lineAttack = function () { LK.getSound('bossAttack').play(); // Create a line of attacks from boss to player var targetX = player.x; var targetY = player.y; var count = 5; for (var i = 0; i < count; i++) { var t = i / (count - 1); var posX = self.x + (targetX - self.x) * t; var posY = self.y + (targetY - self.y) * t; var delay = i * 200; LK.setTimeout(function (x, y) { return function () { self.createAttack(x, y, 2000); }; }(posX, posY), delay); } }; self.chargeAttack = function () { LK.getSound('bossAttack').play(); // Calculate direction to player var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { dx /= distance; dy /= distance; } // Save original position var startX = self.x; var startY = self.y; // Charge animation tween(self, { x: self.x + dx * 500, y: self.y + dy * 500 }, { duration: 800, easing: tween.easeIn, onFinish: function onFinish() { // Return to original position tween(self, { x: startX, y: startY }, { duration: 1000, easing: tween.easeOut }); // Create attacks along the charge path for (var i = 0; i < 5; i++) { var t = i / 4; var posX = startX + (self.x - startX) * t; var posY = startY + (self.y - startY) * t; self.createAttack(posX, posY, 1500); } } }); }; self.createAttack = function (x, y, duration) { // Create attack warning var warning = game.addChild(LK.getAsset('attack', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, alpha: 0.3, tint: 0xFFFF00 })); // Warning animation tween(warning, { alpha: 0.6 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Change to actual attack warning.tint = 0xFF0000; tween(warning, { alpha: 0.8 }, { duration: 200, onFinish: function onFinish() { // Add to active attacks var attack = { x: warning.x, y: warning.y, radius: warning.width / 2, visual: warning, lifeTime: duration }; self.attacks.push(attack); // Remove after duration LK.setTimeout(function () { var index = self.attacks.indexOf(attack); if (index !== -1) { self.attacks.splice(index, 1); } // Fade out and destroy tween(warning, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { warning.destroy(); } }); }, duration); } }); } }); }; self.takeDamage = function (amount) { if (self.dead) { return; } self.health -= amount; // Visual feedback LK.effects.flashObject(self, 0xFFFFFF, 200); // Phase transition at 50% health if (self.health <= self.maxHealth / 2 && self.phase === 1) { self.phase = 2; self.speed += 2; // Visual phase transition tween(self, { tint: 0xFF3300 }, { duration: 1000, easing: tween.easeInOut }); } // Check if boss is defeated if (self.health <= 0) { self.die(); } }; self.die = function () { if (self.dead) { return; } self.dead = true; LK.getSound('victory').play(); // Death animation tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); storage.bossesDefeated = (storage.bossesDefeated || 0) + 1; gameState.victory(); } }); }; self.update = function () { if (self.dead) { return; } // Update attack cooldown if (self.attackCooldown > 0) { self.attackCooldown--; if (self.attackCooldown === 0) { self.startAttackPattern(); } } // Move toward player if not stunned if (!self.stunned) { var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 200) { // Keep some distance self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } } else { // Update stun duration self.stunDuration--; if (self.stunDuration <= 0) { self.stunned = false; } } // Process active attacks for (var i = self.attacks.length - 1; i >= 0; i--) { var attack = self.attacks[i]; attack.lifeTime--; // Check for collision with player var dx = player.x - attack.x; var dy = player.y - attack.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < attack.radius + player.width / 2 && player.takeDamage(1)) { // Visual feedback LK.effects.flashObject(attack.visual, 0xFFFFFF, 200); } // Remove expired attacks if (attack.lifeTime <= 0) { self.attacks.splice(i, 1); attack.visual.destroy(); } } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 12; self.rolling = false; self.rollCooldown = 0; self.rollDuration = 0; self.rollDirection = { x: 0, y: 0 }; self.rollDistance = 300; self.rollSpeed = 20; self.invulnerable = false; self.invulnerabilityFrames = 0; self.health = storage.maxHearts || 3; self.dead = false; self.roll = function (direction) { if (self.rolling || self.rollCooldown > 0 || self.dead) { return; } self.rolling = true; self.rollDuration = self.rollDistance / self.rollSpeed; self.rollDirection = direction; self.invulnerable = true; // Visual effect for rolling var rollEffect = game.addChild(LK.getAsset('roll', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 0.7 })); // Start the roll tween LK.getSound('roll').play(); // Cleanup after roll LK.setTimeout(function () { self.rolling = false; self.rollCooldown = 30; // 30 frames cooldown (0.5 seconds) LK.setTimeout(function () { self.invulnerable = false; }, 100); // Small buffer of invulnerability after roll tween(rollEffect, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { rollEffect.destroy(); } }); }, self.rollDuration * (1000 / 60)); // Convert frames to ms }; self.takeDamage = function (amount) { if (self.invulnerable || self.dead) { return false; } self.health -= amount; LK.getSound('hit').play(); if (self.health <= 0) { self.die(); return true; } // Invulnerability period self.invulnerable = true; self.invulnerabilityFrames = 45; // 45 frames (0.75 seconds) // Flash effect LK.effects.flashObject(self, 0xFF0000, 500); return true; }; self.die = function () { if (self.dead) { return; } self.dead = true; LK.getSound('death').play(); // Death visual effect tween(self, { alpha: 0, rotation: Math.PI * 4 }, { duration: 1500, easing: tween.easeInOut }); // Update total deaths storage.totalDeaths = (storage.totalDeaths || 0) + 1; // Check if player should get an upgrade if (storage.totalDeaths % 3 === 0) { storage.maxHearts = (storage.maxHearts || 3) + 1; } // Restart the level after delay LK.setTimeout(function () { gameState.gameOver(); }, 2000); }; self.update = function () { // Handle roll cooldown if (self.rollCooldown > 0) { self.rollCooldown--; } // Handle invulnerability frames if (self.invulnerable && self.invulnerabilityFrames > 0) { self.invulnerabilityFrames--; // Blinking effect self.alpha = self.invulnerabilityFrames % 6 > 2 ? 0.5 : 1; if (self.invulnerabilityFrames <= 0) { self.invulnerable = false; self.alpha = 1; } } // Handle rolling movement if (self.rolling) { self.x += self.rollDirection.x * self.rollSpeed; self.y += self.rollDirection.y * self.rollSpeed; } // Keep player within bounds self.x = Math.max(100, Math.min(self.x, 2048 - 100)); self.y = Math.max(100, Math.min(self.y, 2732 - 100)); }; return self; }); var UI = Container.expand(function () { var self = Container.call(this); // Create heart containers self.hearts = []; self.heartContainer = new Container(); self.addChild(self.heartContainer); // Title and messages self.titleText = new Text2("ROLL SOULS", { size: 150, fill: 0xFFFFFF }); self.titleText.anchor.set(0.5, 0.5); self.addChild(self.titleText); self.messageText = new Text2("", { size: 60, fill: 0xFFFFFF }); self.messageText.anchor.set(0.5, 0.5); self.addChild(self.messageText); // Deaths counter self.deathsText = new Text2("Deaths: 0", { size: 40, fill: 0xFFFFFF }); self.deathsText.anchor.set(1, 0); self.addChild(self.deathsText); // Tutorial text self.tutorialText = new Text2("", { size: 50, fill: 0xFFFFFF }); self.tutorialText.anchor.set(0.5, 0); self.addChild(self.tutorialText); self.updateHearts = function (current, max) { // Clear existing hearts while (self.hearts.length > 0) { var heart = self.hearts.pop(); heart.destroy(); } self.heartContainer.removeChildren(); // Create new hearts for (var i = 0; i < max; i++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, x: i * 50, y: 0, tint: i < current ? 0xFF0000 : 0x555555 }); self.hearts.push(heart); self.heartContainer.addChild(heart); } // Center heart container self.heartContainer.x = (2048 - max * 50) / 2; self.heartContainer.y = 100; }; self.showMessage = function (message, duration) { self.messageText.setText(message); self.messageText.alpha = 1; // Clear any existing timeout if (self.messageTimeout) { LK.clearTimeout(self.messageTimeout); } // Fade out after duration if (duration) { self.messageTimeout = LK.setTimeout(function () { tween(self.messageText, { alpha: 0 }, { duration: 500 }); }, duration); } }; self.showTutorial = function (text) { self.tutorialText.setText(text); self.tutorialText.alpha = 1; }; self.hideTutorial = function () { tween(self.tutorialText, { alpha: 0 }, { duration: 500 }); }; self.updateDeathsCounter = function () { self.deathsText.setText("Deaths: " + storage.totalDeaths); }; self.positionElements = function (state) { // Position based on game state switch (state) { case "title": self.titleText.x = 2048 / 2; self.titleText.y = 800; self.messageText.x = 2048 / 2; self.messageText.y = 1000; self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; self.deathsText.x = 2048 - 50; self.deathsText.y = 50; break; case "game": self.titleText.alpha = 0; self.messageText.x = 2048 / 2; self.messageText.y = 1500; self.tutorialText.x = 2048 / 2; self.tutorialText.y = 200; self.deathsText.x = 2048 - 50; self.deathsText.y = 50; break; case "victory": self.titleText.x = 2048 / 2; self.titleText.y = 800; self.messageText.x = 2048 / 2; self.messageText.y = 1000; self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; self.deathsText.x = 2048 - 50; self.deathsText.y = 50; break; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 }); /**** * Game Code ****/ // Game variables var gameState = "menu"; var deathCount = 0; var maxHearts = 5; var currentHearts = 5; var gameTimer = 120; var timerText; var boss, player; // Helper function to clear the scene function clearScene() { // Remove all children except permanent elements var childrenToRemove = []; for (var i = 0; i < game.children.length; i++) { childrenToRemove.push(game.children[i]); } for (var i = 0; i < childrenToRemove.length; i++) { childrenToRemove[i].destroy(); } } function showMainMenu() { clearScene(); var title = new Text2("ROLL SOULS", { size: 120, fill: 0xffffff }); title.x = 1024; title.y = 400; game.addChild(title); var startButton = new Button2("Start Game", function () { showIntro(); }); startButton.x = 1024; startButton.y = 800; game.addChild(startButton); } function showIntro() { clearScene(); var introText = new Text2("Only those who roll shall survive...", { size: 60, fill: 0xffffff }); introText.x = 1024; introText.y = 600; game.addChild(introText); var continueButton = new Button2("Continue", function () { showFakeTutorial(); }); continueButton.x = 1024; continueButton.y = 1000; game.addChild(continueButton); } function showFakeTutorial() { clearScene(); var fakeText = new Text2("Press the button to roll!", { size: 60, fill: 0xff0000 }); fakeText.x = 1024; fakeText.y = 600; game.addChild(fakeText); var rollButton = new Button2("Roll", function () { showRealTutorial(); }); rollButton.x = 1024; rollButton.y = 1000; game.addChild(rollButton); } function showRealTutorial() { clearScene(); var realText = new Text2("Swipe to roll and survive!", { size: 60, fill: 0xffffff }); realText.x = 1024; realText.y = 600; game.addChild(realText); var startButton = new Button2("Start Boss Fight", function () { startGame(); }); startButton.x = 1024; startButton.y = 1000; game.addChild(startButton); } function startGame() { clearScene(); initGame(); timerText = new Text2("Time Left: 120", { size: 50, fill: 0xffffff }); timerText.x = 50; timerText.y = 50; game.addChild(timerText); LK.setInterval(function () { if (gameState === "bossFight") { gameTimer--; timerText.setText("Time Left: " + gameTimer); if (gameTimer <= 0) { victory(); } } }, 1000); } function initGame() { gameState = "bossFight"; player = new Player(); player.x = 1024; player.y = 2048; game.addChild(player); boss = new Boss(); boss.x = 1024; boss.y = 400; game.addChild(boss); currentHearts = 5 + Math.min(Math.floor(deathCount / 5), 5); updateHeartUI(); } function gameOver() { deathCount++; showMainMenu(); } function victory() { clearScene(); var victoryText = new Text2("Victory!", { size: 100, fill: 0x00ff00 }); victoryText.x = 1024; victoryText.y = 700; game.addChild(victoryText); var restartButton = new Button2("Restart", function () { showMainMenu(); }); restartButton.x = 1024; restartButton.y = 1000; game.addChild(restartButton); } function updateHeartUI() { // Update heart display if needed } showMainMenu(); var player; var boss; var ui; var walls = []; var gameState = { currentState: "title", touchStart: { x: 0, y: 0 }, touchEnd: { x: 0, y: 0 }, init: function init() { // Create background var bg = game.addChild(LK.getAsset('bg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 })); // Create walls this.createWalls(); // Create UI ui = game.addChild(new UI()); ui.positionElements("title"); ui.updateDeathsCounter(); // Show title screen this.showTitleScreen(); // Start background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); }, createWalls: function createWalls() { // Left wall var leftWall = game.addChild(LK.getAsset('wall', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); walls.push(leftWall); // Right wall var rightWall = game.addChild(LK.getAsset('wall', { anchorX: 0, anchorY: 0, x: 2048 - 100, y: 0 })); walls.push(rightWall); // Top wall var topWall = game.addChild(LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); walls.push(topWall); // Bottom wall var bottomWall = game.addChild(LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - 100 })); walls.push(bottomWall); }, showTitleScreen: function showTitleScreen() { this.currentState = "title"; ui.positionElements("title"); ui.titleText.alpha = 1; ui.showMessage("Tap to Start", 0); ui.showTutorial("Swipe to Roll - Death is Progress"); }, startGame: function startGame() { this.currentState = "game"; ui.positionElements("game"); // Create player player = game.addChild(new Player()); player.x = 2048 / 2; player.y = 2732 / 2 + 400; player.health = storage.maxHearts || 3; // Create boss boss = game.addChild(new Boss()); boss.x = 2048 / 2; boss.y = 2732 / 2 - 400; // Update UI ui.updateHearts(player.health, storage.maxHearts); ui.showTutorial("Swipe to roll away from attacks!"); LK.setTimeout(function () { ui.hideTutorial(); boss.startAttackPattern(); }, 3000); }, gameOver: function gameOver() { // Show game over message ui.showMessage("YOU DIED", 3000); // Restart after delay LK.setTimeout(function () { // Clean up if (player) { player.destroy(); } if (boss) { boss.destroy(); } // Restart gameState.startGame(); }, 3000); }, victory: function victory() { this.currentState = "victory"; ui.positionElements("victory"); ui.titleText.setText("VICTORY ACHIEVED"); ui.titleText.alpha = 1; ui.showMessage("Bosses Defeated: " + storage.bossesDefeated, 0); ui.showTutorial("Tap to Continue"); // Clean up player if (player) { player.destroy(); } LK.setTimeout(function () { // Return to title after delay LK.setTimeout(function () { gameState.showTitleScreen(); }, 5000); }, 3000); }, processTouchGesture: function processTouchGesture() { if (this.currentState === "title") { this.startGame(); return; } if (this.currentState === "victory") { this.showTitleScreen(); return; } // Only process roll gestures during gameplay if (this.currentState !== "game" || !player || player.dead) { return; } // Calculate swipe direction and distance var dx = this.touchEnd.x - this.touchStart.x; var dy = this.touchEnd.y - this.touchStart.y; var distance = Math.sqrt(dx * dx + dy * dy); // Minimum swipe distance if (distance < 50) { return; } // Normalize direction var direction = { x: dx / distance, y: dy / distance }; // Execute roll player.roll(direction); } }; // Event handlers game.down = function (x, y, obj) { gameState.touchStart.x = x; gameState.touchStart.y = y; }; game.up = function (x, y, obj) { gameState.touchEnd.x = x; gameState.touchEnd.y = y; gameState.processTouchGesture(); }; game.move = function (x, y, obj) { // Only used for tracking the current touch position gameState.touchEnd.x = x; gameState.touchEnd.y = y; }; // Main update loop game.update = function () { // Only update game objects during gameplay if (gameState.currentState === "game") { if (player) { player.update(); } if (boss) { boss.update(); } // Update hearts UI if (player && ui) { ui.updateHearts(player.health, storage.maxHearts); } } // Always update deaths counter if (ui) { ui.updateDeathsCounter(); } }; // Initialize the game gameState.init();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
totalDeaths: 0,
maxHearts: 3,
currentLevel: 0,
bossesDefeated: 0
});
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 5;
self.attackCooldown = 0;
self.attackPattern = 0;
self.attacks = [];
self.stunned = false;
self.stunDuration = 0;
self.dead = false;
self.phase = 1;
self.startAttackPattern = function () {
if (self.dead) {
return;
}
self.attackPattern = (self.attackPattern + 1) % 3;
switch (self.attackPattern) {
case 0:
self.circleAttack();
break;
case 1:
self.lineAttack();
break;
case 2:
self.chargeAttack();
break;
}
// Schedule next attack
self.attackCooldown = 90 + Math.floor(Math.random() * 60); // 1.5-2.5 seconds
};
self.circleAttack = function () {
LK.getSound('bossAttack').play();
var center = {
x: self.x,
y: self.y
};
var count = 8;
var radius = 300;
for (var i = 0; i < count; i++) {
var angle = i / count * Math.PI * 2;
var posX = center.x + Math.cos(angle) * radius;
var posY = center.y + Math.sin(angle) * radius;
self.createAttack(posX, posY, 3000);
}
};
self.lineAttack = function () {
LK.getSound('bossAttack').play();
// Create a line of attacks from boss to player
var targetX = player.x;
var targetY = player.y;
var count = 5;
for (var i = 0; i < count; i++) {
var t = i / (count - 1);
var posX = self.x + (targetX - self.x) * t;
var posY = self.y + (targetY - self.y) * t;
var delay = i * 200;
LK.setTimeout(function (x, y) {
return function () {
self.createAttack(x, y, 2000);
};
}(posX, posY), delay);
}
};
self.chargeAttack = function () {
LK.getSound('bossAttack').play();
// Calculate direction to player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
dx /= distance;
dy /= distance;
}
// Save original position
var startX = self.x;
var startY = self.y;
// Charge animation
tween(self, {
x: self.x + dx * 500,
y: self.y + dy * 500
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
// Return to original position
tween(self, {
x: startX,
y: startY
}, {
duration: 1000,
easing: tween.easeOut
});
// Create attacks along the charge path
for (var i = 0; i < 5; i++) {
var t = i / 4;
var posX = startX + (self.x - startX) * t;
var posY = startY + (self.y - startY) * t;
self.createAttack(posX, posY, 1500);
}
}
});
};
self.createAttack = function (x, y, duration) {
// Create attack warning
var warning = game.addChild(LK.getAsset('attack', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 0.3,
tint: 0xFFFF00
}));
// Warning animation
tween(warning, {
alpha: 0.6
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Change to actual attack
warning.tint = 0xFF0000;
tween(warning, {
alpha: 0.8
}, {
duration: 200,
onFinish: function onFinish() {
// Add to active attacks
var attack = {
x: warning.x,
y: warning.y,
radius: warning.width / 2,
visual: warning,
lifeTime: duration
};
self.attacks.push(attack);
// Remove after duration
LK.setTimeout(function () {
var index = self.attacks.indexOf(attack);
if (index !== -1) {
self.attacks.splice(index, 1);
}
// Fade out and destroy
tween(warning, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
warning.destroy();
}
});
}, duration);
}
});
}
});
};
self.takeDamage = function (amount) {
if (self.dead) {
return;
}
self.health -= amount;
// Visual feedback
LK.effects.flashObject(self, 0xFFFFFF, 200);
// Phase transition at 50% health
if (self.health <= self.maxHealth / 2 && self.phase === 1) {
self.phase = 2;
self.speed += 2;
// Visual phase transition
tween(self, {
tint: 0xFF3300
}, {
duration: 1000,
easing: tween.easeInOut
});
}
// Check if boss is defeated
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
if (self.dead) {
return;
}
self.dead = true;
LK.getSound('victory').play();
// Death animation
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
storage.bossesDefeated = (storage.bossesDefeated || 0) + 1;
gameState.victory();
}
});
};
self.update = function () {
if (self.dead) {
return;
}
// Update attack cooldown
if (self.attackCooldown > 0) {
self.attackCooldown--;
if (self.attackCooldown === 0) {
self.startAttackPattern();
}
}
// Move toward player if not stunned
if (!self.stunned) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 200) {
// Keep some distance
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else {
// Update stun duration
self.stunDuration--;
if (self.stunDuration <= 0) {
self.stunned = false;
}
}
// Process active attacks
for (var i = self.attacks.length - 1; i >= 0; i--) {
var attack = self.attacks[i];
attack.lifeTime--;
// Check for collision with player
var dx = player.x - attack.x;
var dy = player.y - attack.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < attack.radius + player.width / 2 && player.takeDamage(1)) {
// Visual feedback
LK.effects.flashObject(attack.visual, 0xFFFFFF, 200);
}
// Remove expired attacks
if (attack.lifeTime <= 0) {
self.attacks.splice(i, 1);
attack.visual.destroy();
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.rolling = false;
self.rollCooldown = 0;
self.rollDuration = 0;
self.rollDirection = {
x: 0,
y: 0
};
self.rollDistance = 300;
self.rollSpeed = 20;
self.invulnerable = false;
self.invulnerabilityFrames = 0;
self.health = storage.maxHearts || 3;
self.dead = false;
self.roll = function (direction) {
if (self.rolling || self.rollCooldown > 0 || self.dead) {
return;
}
self.rolling = true;
self.rollDuration = self.rollDistance / self.rollSpeed;
self.rollDirection = direction;
self.invulnerable = true;
// Visual effect for rolling
var rollEffect = game.addChild(LK.getAsset('roll', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 0.7
}));
// Start the roll tween
LK.getSound('roll').play();
// Cleanup after roll
LK.setTimeout(function () {
self.rolling = false;
self.rollCooldown = 30; // 30 frames cooldown (0.5 seconds)
LK.setTimeout(function () {
self.invulnerable = false;
}, 100); // Small buffer of invulnerability after roll
tween(rollEffect, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
rollEffect.destroy();
}
});
}, self.rollDuration * (1000 / 60)); // Convert frames to ms
};
self.takeDamage = function (amount) {
if (self.invulnerable || self.dead) {
return false;
}
self.health -= amount;
LK.getSound('hit').play();
if (self.health <= 0) {
self.die();
return true;
}
// Invulnerability period
self.invulnerable = true;
self.invulnerabilityFrames = 45; // 45 frames (0.75 seconds)
// Flash effect
LK.effects.flashObject(self, 0xFF0000, 500);
return true;
};
self.die = function () {
if (self.dead) {
return;
}
self.dead = true;
LK.getSound('death').play();
// Death visual effect
tween(self, {
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 1500,
easing: tween.easeInOut
});
// Update total deaths
storage.totalDeaths = (storage.totalDeaths || 0) + 1;
// Check if player should get an upgrade
if (storage.totalDeaths % 3 === 0) {
storage.maxHearts = (storage.maxHearts || 3) + 1;
}
// Restart the level after delay
LK.setTimeout(function () {
gameState.gameOver();
}, 2000);
};
self.update = function () {
// Handle roll cooldown
if (self.rollCooldown > 0) {
self.rollCooldown--;
}
// Handle invulnerability frames
if (self.invulnerable && self.invulnerabilityFrames > 0) {
self.invulnerabilityFrames--;
// Blinking effect
self.alpha = self.invulnerabilityFrames % 6 > 2 ? 0.5 : 1;
if (self.invulnerabilityFrames <= 0) {
self.invulnerable = false;
self.alpha = 1;
}
}
// Handle rolling movement
if (self.rolling) {
self.x += self.rollDirection.x * self.rollSpeed;
self.y += self.rollDirection.y * self.rollSpeed;
}
// Keep player within bounds
self.x = Math.max(100, Math.min(self.x, 2048 - 100));
self.y = Math.max(100, Math.min(self.y, 2732 - 100));
};
return self;
});
var UI = Container.expand(function () {
var self = Container.call(this);
// Create heart containers
self.hearts = [];
self.heartContainer = new Container();
self.addChild(self.heartContainer);
// Title and messages
self.titleText = new Text2("ROLL SOULS", {
size: 150,
fill: 0xFFFFFF
});
self.titleText.anchor.set(0.5, 0.5);
self.addChild(self.titleText);
self.messageText = new Text2("", {
size: 60,
fill: 0xFFFFFF
});
self.messageText.anchor.set(0.5, 0.5);
self.addChild(self.messageText);
// Deaths counter
self.deathsText = new Text2("Deaths: 0", {
size: 40,
fill: 0xFFFFFF
});
self.deathsText.anchor.set(1, 0);
self.addChild(self.deathsText);
// Tutorial text
self.tutorialText = new Text2("", {
size: 50,
fill: 0xFFFFFF
});
self.tutorialText.anchor.set(0.5, 0);
self.addChild(self.tutorialText);
self.updateHearts = function (current, max) {
// Clear existing hearts
while (self.hearts.length > 0) {
var heart = self.hearts.pop();
heart.destroy();
}
self.heartContainer.removeChildren();
// Create new hearts
for (var i = 0; i < max; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 50,
y: 0,
tint: i < current ? 0xFF0000 : 0x555555
});
self.hearts.push(heart);
self.heartContainer.addChild(heart);
}
// Center heart container
self.heartContainer.x = (2048 - max * 50) / 2;
self.heartContainer.y = 100;
};
self.showMessage = function (message, duration) {
self.messageText.setText(message);
self.messageText.alpha = 1;
// Clear any existing timeout
if (self.messageTimeout) {
LK.clearTimeout(self.messageTimeout);
}
// Fade out after duration
if (duration) {
self.messageTimeout = LK.setTimeout(function () {
tween(self.messageText, {
alpha: 0
}, {
duration: 500
});
}, duration);
}
};
self.showTutorial = function (text) {
self.tutorialText.setText(text);
self.tutorialText.alpha = 1;
};
self.hideTutorial = function () {
tween(self.tutorialText, {
alpha: 0
}, {
duration: 500
});
};
self.updateDeathsCounter = function () {
self.deathsText.setText("Deaths: " + storage.totalDeaths);
};
self.positionElements = function (state) {
// Position based on game state
switch (state) {
case "title":
self.titleText.x = 2048 / 2;
self.titleText.y = 800;
self.messageText.x = 2048 / 2;
self.messageText.y = 1000;
self.tutorialText.x = 2048 / 2;
self.tutorialText.y = 1200;
self.deathsText.x = 2048 - 50;
self.deathsText.y = 50;
break;
case "game":
self.titleText.alpha = 0;
self.messageText.x = 2048 / 2;
self.messageText.y = 1500;
self.tutorialText.x = 2048 / 2;
self.tutorialText.y = 200;
self.deathsText.x = 2048 - 50;
self.deathsText.y = 50;
break;
case "victory":
self.titleText.x = 2048 / 2;
self.titleText.y = 800;
self.messageText.x = 2048 / 2;
self.messageText.y = 1000;
self.tutorialText.x = 2048 / 2;
self.tutorialText.y = 1200;
self.deathsText.x = 2048 - 50;
self.deathsText.y = 50;
break;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// Game variables
var gameState = "menu";
var deathCount = 0;
var maxHearts = 5;
var currentHearts = 5;
var gameTimer = 120;
var timerText;
var boss, player;
// Helper function to clear the scene
function clearScene() {
// Remove all children except permanent elements
var childrenToRemove = [];
for (var i = 0; i < game.children.length; i++) {
childrenToRemove.push(game.children[i]);
}
for (var i = 0; i < childrenToRemove.length; i++) {
childrenToRemove[i].destroy();
}
}
function showMainMenu() {
clearScene();
var title = new Text2("ROLL SOULS", {
size: 120,
fill: 0xffffff
});
title.x = 1024;
title.y = 400;
game.addChild(title);
var startButton = new Button2("Start Game", function () {
showIntro();
});
startButton.x = 1024;
startButton.y = 800;
game.addChild(startButton);
}
function showIntro() {
clearScene();
var introText = new Text2("Only those who roll shall survive...", {
size: 60,
fill: 0xffffff
});
introText.x = 1024;
introText.y = 600;
game.addChild(introText);
var continueButton = new Button2("Continue", function () {
showFakeTutorial();
});
continueButton.x = 1024;
continueButton.y = 1000;
game.addChild(continueButton);
}
function showFakeTutorial() {
clearScene();
var fakeText = new Text2("Press the button to roll!", {
size: 60,
fill: 0xff0000
});
fakeText.x = 1024;
fakeText.y = 600;
game.addChild(fakeText);
var rollButton = new Button2("Roll", function () {
showRealTutorial();
});
rollButton.x = 1024;
rollButton.y = 1000;
game.addChild(rollButton);
}
function showRealTutorial() {
clearScene();
var realText = new Text2("Swipe to roll and survive!", {
size: 60,
fill: 0xffffff
});
realText.x = 1024;
realText.y = 600;
game.addChild(realText);
var startButton = new Button2("Start Boss Fight", function () {
startGame();
});
startButton.x = 1024;
startButton.y = 1000;
game.addChild(startButton);
}
function startGame() {
clearScene();
initGame();
timerText = new Text2("Time Left: 120", {
size: 50,
fill: 0xffffff
});
timerText.x = 50;
timerText.y = 50;
game.addChild(timerText);
LK.setInterval(function () {
if (gameState === "bossFight") {
gameTimer--;
timerText.setText("Time Left: " + gameTimer);
if (gameTimer <= 0) {
victory();
}
}
}, 1000);
}
function initGame() {
gameState = "bossFight";
player = new Player();
player.x = 1024;
player.y = 2048;
game.addChild(player);
boss = new Boss();
boss.x = 1024;
boss.y = 400;
game.addChild(boss);
currentHearts = 5 + Math.min(Math.floor(deathCount / 5), 5);
updateHeartUI();
}
function gameOver() {
deathCount++;
showMainMenu();
}
function victory() {
clearScene();
var victoryText = new Text2("Victory!", {
size: 100,
fill: 0x00ff00
});
victoryText.x = 1024;
victoryText.y = 700;
game.addChild(victoryText);
var restartButton = new Button2("Restart", function () {
showMainMenu();
});
restartButton.x = 1024;
restartButton.y = 1000;
game.addChild(restartButton);
}
function updateHeartUI() {
// Update heart display if needed
}
showMainMenu();
var player;
var boss;
var ui;
var walls = [];
var gameState = {
currentState: "title",
touchStart: {
x: 0,
y: 0
},
touchEnd: {
x: 0,
y: 0
},
init: function init() {
// Create background
var bg = game.addChild(LK.getAsset('bg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
}));
// Create walls
this.createWalls();
// Create UI
ui = game.addChild(new UI());
ui.positionElements("title");
ui.updateDeathsCounter();
// Show title screen
this.showTitleScreen();
// Start background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
},
createWalls: function createWalls() {
// Left wall
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
walls.push(leftWall);
// Right wall
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 2048 - 100,
y: 0
}));
walls.push(rightWall);
// Top wall
var topWall = game.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
walls.push(topWall);
// Bottom wall
var bottomWall = game.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - 100
}));
walls.push(bottomWall);
},
showTitleScreen: function showTitleScreen() {
this.currentState = "title";
ui.positionElements("title");
ui.titleText.alpha = 1;
ui.showMessage("Tap to Start", 0);
ui.showTutorial("Swipe to Roll - Death is Progress");
},
startGame: function startGame() {
this.currentState = "game";
ui.positionElements("game");
// Create player
player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2 + 400;
player.health = storage.maxHearts || 3;
// Create boss
boss = game.addChild(new Boss());
boss.x = 2048 / 2;
boss.y = 2732 / 2 - 400;
// Update UI
ui.updateHearts(player.health, storage.maxHearts);
ui.showTutorial("Swipe to roll away from attacks!");
LK.setTimeout(function () {
ui.hideTutorial();
boss.startAttackPattern();
}, 3000);
},
gameOver: function gameOver() {
// Show game over message
ui.showMessage("YOU DIED", 3000);
// Restart after delay
LK.setTimeout(function () {
// Clean up
if (player) {
player.destroy();
}
if (boss) {
boss.destroy();
}
// Restart
gameState.startGame();
}, 3000);
},
victory: function victory() {
this.currentState = "victory";
ui.positionElements("victory");
ui.titleText.setText("VICTORY ACHIEVED");
ui.titleText.alpha = 1;
ui.showMessage("Bosses Defeated: " + storage.bossesDefeated, 0);
ui.showTutorial("Tap to Continue");
// Clean up player
if (player) {
player.destroy();
}
LK.setTimeout(function () {
// Return to title after delay
LK.setTimeout(function () {
gameState.showTitleScreen();
}, 5000);
}, 3000);
},
processTouchGesture: function processTouchGesture() {
if (this.currentState === "title") {
this.startGame();
return;
}
if (this.currentState === "victory") {
this.showTitleScreen();
return;
}
// Only process roll gestures during gameplay
if (this.currentState !== "game" || !player || player.dead) {
return;
}
// Calculate swipe direction and distance
var dx = this.touchEnd.x - this.touchStart.x;
var dy = this.touchEnd.y - this.touchStart.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Minimum swipe distance
if (distance < 50) {
return;
}
// Normalize direction
var direction = {
x: dx / distance,
y: dy / distance
};
// Execute roll
player.roll(direction);
}
};
// Event handlers
game.down = function (x, y, obj) {
gameState.touchStart.x = x;
gameState.touchStart.y = y;
};
game.up = function (x, y, obj) {
gameState.touchEnd.x = x;
gameState.touchEnd.y = y;
gameState.processTouchGesture();
};
game.move = function (x, y, obj) {
// Only used for tracking the current touch position
gameState.touchEnd.x = x;
gameState.touchEnd.y = y;
};
// Main update loop
game.update = function () {
// Only update game objects during gameplay
if (gameState.currentState === "game") {
if (player) {
player.update();
}
if (boss) {
boss.update();
}
// Update hearts UI
if (player && ui) {
ui.updateHearts(player.health, storage.maxHearts);
}
}
// Always update deaths counter
if (ui) {
ui.updateDeathsCounter();
}
};
// Initialize the game
gameState.init();