User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics);' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.bossGraphics = self.addChild(LK.getAsset('bossIdle', {' Line Number: 650
User prompt
Please fix the bug: 'self.attachAsset is not a function' in or related to this line: 'self.bossGraphics = self.attachAsset('bossIdle', {' Line Number: 650
/**** * Classes ****/ // End gameState /**** * Player class ****/ var Player = Container.expand(function () { var self = Container.call(this); // Attach player graphic (depends on asset) self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.health = 5; // Default player health (will be set by storage.maxHearts in gameState.startGame) self.speed = 8; // Player movement speed self.rollSpeed = 12; // Player roll speed self.rolling = false; self.rollDuration = 30; // Roll duration in frames (0.5 seconds at 60fps) self.rollTimer = 0; self.rollDirectionX = 0; self.rollDirectionY = 0; self.invulnerable = false; // Player invulnerability flag self.invulnerabilityDuration = 60; // Invulnerability duration after rolling/getting hit self.invulnerabilityTimer = 0; self.dead = false; // Create player roll animation (depends on assets) self.createRollAnim = function () { var frames = [LK.getAsset('roll0', {}), LK.getAsset('roll1', {}), LK.getAsset('roll2', {}), LK.getAsset('roll3', {}), LK.getAsset('roll4', {})]; var rollAnim = new SpriteAnimation({ frames: frames, frameDuration: 60, // Roll animation speed loop: true, // Roll animation loops anchorX: 0.5, anchorY: 0.5, x: 0, // Relative position y: 0 // Relative position }); return rollAnim; }; self.rollAnim = null; // Will hold the current roll animation object self.startRoll = function () { if (self.rolling || self.dead || gameState.currentState !== "game") { return; // Cannot roll if already rolling, dead, or not in game state } self.rolling = true; self.rollTimer = self.rollDuration; self.invulnerable = true; // Invulnerable during roll self.invulnerabilityTimer = self.invulnerabilityDuration; // Capture current movement direction for roll self.rollDirectionX = (LK.isDown("left") ? -1 : 0) + (LK.isDown("right") ? 1 : 0); self.rollDirectionY = (LK.isDown("up") ? -1 : 0) + (LK.isDown("down") ? 1 : 0); // Ensure there's a direction to roll, default if no input if (self.rollDirectionX === 0 && self.rollDirectionY === 0) { // Default roll direction (e.g., last movement direction, or down) // For simplicity, let's just prevent roll if no direction pressed. // Or set a default if needed: self.rollDirectionY = 1; // Example: roll down self.rolling = false; // Cancel roll if no direction input self.invulnerable = false; self.invulnerabilityTimer = 0; return; // Exit if no roll direction } // Normalize roll direction vector (optional, but good for consistent speed) var rollMagnitude = Math.sqrt(self.rollDirectionX * self.rollDirectionX + self.rollDirectionY * self.rollDirectionY); if (rollMagnitude > 0) { self.rollDirectionX /= rollMagnitude; self.rollDirectionY /= rollMagnitude; } // Remove idle graphic, add roll animation if (self.playerGraphics && self.playerGraphics.parent === self) { self.playerGraphics.parent.removeChild(self.playerGraphics); } else if (self.playerGraphics && self.playerGraphics.parent) { // Fallback self.playerGraphics.parent.removeChild(self.playerGraphics); } self.playerGraphics = null; self.rollAnim = self.addChild(self.createRollAnim()); // Add roll animation as child self.rollAnim.play(); // Start roll animation }; self.takeDamage = function (amount) { if (self.dead || self.invulnerable || gameState.currentState !== "game") { return; // Cannot take damage if dead, invulnerable, or not in game state } self.health -= amount; self.health = Math.max(0, self.health); // Health cannot go below zero LK.getSound('playerHit').play(); // Play player hit sound LK.effects.flashObject(self, 0xFF0000, 300); // Flash effect on hit self.invulnerable = true; // Become invulnerable after taking damage // Use a separate timer for hit invulnerability if needed, or just reuse roll timer logic // For simplicity, reuse invulnerabilityTimer: self.invulnerabilityTimer = self.invulnerabilityDuration; // Invulnerable for a short period // Update UI hearts immediately // ui.updateHearts(self.health, storage.maxHearts); // This is called in game.update -> ui.updateHearts if (self.health <= 0) { self.die(); // Player dies } }; self.die = function () { if (self.dead) { return; // Already dead } self.dead = true; // Stop all movement/actions self.rolling = false; self.invulnerable = false; // No longer invulnerable once dead // Stop/remove roll animation if active if (self.rollAnim) { self.rollAnim.stop(); if (self.rollAnim.parent === self) { self.rollAnim.parent.removeChild(self.rollAnim); } else if (self.rollAnim.parent) { // Fallback self.rollAnim.parent.removeChild(self.rollAnim); } self.rollAnim.destroy(); self.rollAnim = null; } // Add player death animation or graphic (if any) // For simplicity, just hide or remove the player graphic if (self.playerGraphics && self.playerGraphics.parent === self) { self.playerGraphics.parent.removeChild(self.playerGraphics); } else if (self.playerGraphics && self.playerGraphics.parent) { // Fallback self.playerGraphics.parent.removeChild(self.playerGraphics); } self.playerGraphics = null; // Clear reference // Trigger game over state after a short delay or death animation LK.setTimeout(function () { if (gameState.currentState === "game") { // Only transition if still in game state gameState.gameOver(true); // Call game over function with death reason } }, 1000); // Delay game over screen by 1 second }; self.update = function () { // Update only if in game state if (gameState.currentState !== "game") { // Stop rolling and remove animation if state changes if (self.rolling) { self.rolling = false; if (self.rollAnim) { self.rollAnim.stop(); if (self.rollAnim.parent === self) { self.rollAnim.parent.removeChild(self.rollAnim); } else if (self.rollAnim.parent) { // Fallback self.rollAnim.parent.removeChild(self.rollAnim); } self.rollAnim.destroy(); self.rollAnim = null; } // Add back idle graphic if it was removed if (!self.playerGraphics) { self.playerGraphics = self.addChild(self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 // Relative position })); } } return; // Exit update if not in game state } // Do not update if player is dead if (self.dead) { return; } // --- Rolling logic --- if (self.rolling) { self.x += self.rollDirectionX * self.rollSpeed; self.y += self.rollDirectionY * self.rollSpeed; self.rollTimer--; if (self.rollTimer <= 0) { self.rolling = false; // Stop and remove roll animation if (self.rollAnim) { self.rollAnim.stop(); if (self.rollAnim.parent === self) { self.rollAnim.parent.removeChild(self.rollAnim); } else if (self.rollAnim.parent) { // Fallback self.rollAnim.parent.removeChild(self.rollAnim); } self.rollAnim.destroy(); self.rollAnim = null; } // Add back idle graphic if (!self.playerGraphics) { self.playerGraphics = self.addChild(self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 // Relative position })); } } } else { // --- Movement logic (only when not rolling) --- var moveX = (LK.isDown("left") ? -1 : 0) + (LK.isDown("right") ? 1 : 0); var moveY = (LK.isDown("up") ? -1 : 0) + (LK.isDown("down") ? 1 : 0); var moveMagnitude = Math.sqrt(moveX * moveX + moveY * moveY); if (moveMagnitude > 0) { moveX /= moveMagnitude; moveY /= moveMagnitude; self.x += moveX * self.speed; self.y += moveY * self.speed; } // No specific idle animation in this example, uses static player graphic } // --- Invulnerability timer --- if (self.invulnerable) { self.invulnerabilityTimer--; if (self.invulnerabilityTimer <= 0) { self.invulnerable = false; } // Optional: add visual effect for invulnerability (e.g., alpha blinking) self.alpha = self.invulnerabilityTimer % 10 < 5 ? 0.5 : 1; // Simple blinking effect } else { self.alpha = 1; // Fully visible when not invulnerable } // --- Boundary checks (Arena borders) --- // Assuming arena borders are at 100px from edge, map 2048x2732 var halfWidth = self.width * self.scaleX / 2; var halfHeight = self.height * self.scaleY / 2; var minX = 100 + halfWidth; var maxX = 2048 - 100 - halfWidth; var minY = 100 + halfHeight; var maxY = 2732 - 100 - halfHeight; self.x = Math.max(minX, Math.min(self.x, maxX)); self.y = Math.max(minY, Math.min(self.y, maxY)); // --- Collision with Boss (only when not invulnerable) --- // Boss collision check is handled in player.update(), NOT boss.update() // It should only happen if player is NOT invulnerable // This collision check is handled in game.update -> player.update() // Let's add the check here if player.update is responsible for it. // Check collision with Boss if boss exists, is not dead, and player is not invulnerable if (boss && !boss.dead && !self.invulnerable) { var dx_b = boss.x - self.x; var dy_b = boss.y - self.y; var distance_b = Math.sqrt(dx_b * dx_b + dy_b * dy_b); var playerRadius_b = self.width / 2; var bossRadius_b = boss.width * boss.scaleX / 2; // Use scaled boss width if (distance_b < playerRadius_b + bossRadius_b) { // Collision detected with Boss body self.takeDamage(1); // Player takes 1 damage } } // Note: Collision with Boss *attacks* is handled in boss.update() }; return self; }); /**** * Initialize Game ****/ // End Player class /**** * UI object ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ /**** * Global variables ****/ var player = null; var boss = null; var ui = null; var storage = null; var isNewBossPlusMode = false; var game = null; var gameContainer = "gameContainer"; // ID elementu HTML, w którym będzie wyświetlana gra var gameOverReasonIsDeath = true; // true jeśli game over z powodu śmierci, false jeśli z powodu czasu w Boss+ /**** * Game state manager ****/ var gameState = function () { var self = {}; self.currentState = "loading"; // Initial state self.changeState = function (newState) { console.log("Changing state from", self.currentState, "to", newState); // Log state changes self.currentState = newState; // Manage visibility of game elements based on state // These position/visibility methods should be in gameState or UI, not individual element updates self.positionElements(); // Call positionElements on state change // Handle specific state transitions if (self.currentState === "title") { // Clear any game elements from previous attempts if necessary if (game) { game.removeChildren(); // Remove all children from the game container // Do not destroy game here, it's handled in createGame } // Ensure player and boss are null when returning to title player = null; boss = null; ui = null; // UI will be recreated in createGame if returning from game over // Ensure attacks are cleared if (boss && boss.attacks) { boss.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); boss.attacks = []; } // If boss object is destroyed, self.attacks check might fail, add a check for that // Display title screen elements // In this simple example, we just show the start button via positionElements } else if (self.currentState === "game") { // Start the game loop when entering game state // The gameLoop is already running based on LK.init settings (60fps) // Player and Boss are created just before changing to 'game' state // Reset player/boss health or properties if necessary (or do it in their constructors) if (player) { player.health = storage.maxHearts; // Reset player health // ui.updateHearts(player.health, storage.maxHearts); // Update UI immediately } if (boss) { boss.health = boss.maxHealth; // Reset boss health // ui.updateBossHealth(boss.health, boss.maxHealth); // Update UI immediately boss.attackCooldown = 60; // Initial delay before first boss attack (1 second at 60fps) boss.stunned = false; // Ensure boss is not stunned boss.repositioning = false; // Ensure boss is not repositioning boss.dead = false; // Ensure boss is not dead boss.phase = 1; // Reset boss phase boss.attackSpeedMultiplier = 1; // Reset attack speed multiplier boss.attacks = []; // Clear any lingering attacks // Reset boss graphic state if needed (e.g., tint) boss.tint = 0xFFFFFF; // Reset tint } } else if (self.currentState === "gameOver") { // Stop game loop? LK manages this, or we can stop updates manually in gameLoop // In this example, gameLoop checks gameState.currentState // Display game over screen elements (handled by positionElements) // Stop boss attacks explicitly if boss exists and has a method for it if (boss) { boss.attackCooldown = 9999; // Prevent new attacks // Ensure existing attacks are cleaned up in boss.update when state changes } // Stop player movement, etc. (handled by player.update checking gameState.currentState) // Clean up game elements from screen? gameState.positionElements might hide them. } else if (self.currentState === "grillScreen") { // Similar cleanup/display logic for grill screen if (boss) { boss.attackCooldown = 9999; // Prevent new attacks } // Ensure existing attacks are cleaned up if (boss && boss.attacks) { boss.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); boss.attacks = []; } // Hide player and boss (handled by positionElements) } }; self.positionElements = function () { // This function manages the visibility and potentially position of game elements based on the current state // It should be called whenever the state changes. var startButton = LK.getObjectById("startButton"); // Przykład pobierania przycisku var bossHpBar = LK.getObjectById("bossHpBar"); var playerHearts = LK.getObjectById("playerHearts"); // Zakładając, że UI ma kontener na serca var gameOverMessage = LK.getObjectById("gameOverMessage"); // Przykład wiadomości game over var grillMenu = LK.getObjectById("grillMenu"); // Przykład menu grill screen // Visibility logic if (startButton) { startButton.visible = self.currentState === "title"; } // Pasek HP bossa widoczny tylko w stanie "game" (lub game over po czasie w Boss+) if (bossHpBar) { bossHpBar.visible = self.currentState === "game" || self.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode; } // Serca gracza widoczne tylko w stanie "game" if (playerHearts) { playerHearts.visible = self.currentState === "game"; } if (gameOverMessage) { gameOverMessage.visible = self.currentState === "gameOver" && gameOverReasonIsDeath; } // Game over tylko ze śmierci if (grillMenu) { grillMenu.visible = self.currentState === "grillScreen" || self.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode; } // Menu grill screen lub game over po czasie // Show player and boss only in game state if (player) { player.visible = self.currentState === "game" || self.currentState === "gameOver" || self.currentState === "grillScreen"; } // Pokaż gracza w game, game over, grill screen if (boss) { boss.visible = self.currentState === "game" || self.currentState === "gameOver" || self.currentState === "grillScreen"; } // Pokaż bossa w game, game over, grill screen }; self.gameOver = function () { var reasonIsDeath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; gameOverReasonIsDeath = reasonIsDeath; // Zapisz przyczynę game over self.changeState("gameOver"); // Wyświetl wiadomość Game Over lub Grill Screen w zależności od przyczyny i trybu (zajmie się tym positionElements) // Tutaj można dodać logikę czyszczenia obiektów, zatrzymania muzyki itp. LK.getSound('playerHit').stop(); // Stop player hit sound if it's playing }; self.showGrillScreen = function () { self.changeState("grillScreen"); // Logic for showing the grill screen }; // Metody zmiany stanu dla przycisków self.startGame = function () { var newBossPlus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; isNewBossPlusMode = newBossPlus; // Set the mode storage.newBossPlusMode = isNewBossPlusMode; // Save mode to storage // Wyczyść poprzednią grę i stwórz nową instancję createGame(); // This function now contains a setTimeout delay // Change state to game is now inside the setTimeout in createGame() // self.changeState("game"); // MOVED INSIDE TIMEOUT IN createGame }; self.returnToTitle = function () { // Clean up game elements before returning to title if (game) { game.removeChildren(); // Remove all display objects from the game container // Destroy player, boss, ui objects explicitly to clear references and updates if (player && player.destroy && !player.destroyed) { player.destroy(); } if (boss && boss.destroy && !boss.destroyed) { boss.destroy(); } if (ui && ui.destroy && !ui.destroyed) { ui.destroy(); } player = null; boss = null; ui = null; // game.destroy(); // Do not destroy game container itself, it's reused } // Clean up any lingering attacks if Boss object was null if (boss && boss.attacks) { // This check is redundant if boss is null, but safe boss.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); boss.attacks = []; } self.changeState("title"); }; return self; }(); // End Player class /**** * UI object ****/ var UI = function () { var self = {}; // Create UI elements (depend on assets/shapes) self.bossHpBar = LK.getAsset('bossHpbar', {}); self.bossHpBar.x = 1024 - self.bossHpBar.width / 2; // Center the bar self.bossHpBar.y = 50; // Position near top self.bossHpBar.visible = false; // Hide initially self.playerHearts = new Container(); // Container for player hearts // Hearts will be added as children to this container in updateHearts self.playerHearts.x = 100; // Position the hearts container self.playerHearts.y = 50; self.playerHearts.visible = false; // Hide initially // Create start button (depends on asset/shape) self.startButton = LK.getAsset('button_bg', {}); // Example shape button self.startButton.x = 1024; // Center the button self.startButton.y = 1366; // Position it self.startButton.anchorX = 0.5; self.startButton.anchorY = 0.5; self.startButton.visible = false; // Hide initially // Add text to start button (depends on font/text rendering) self.startButtonText = new Text2("Start Game", { fill: 0x000000, size: 40, font: "Arial", anchorX: 0.5, anchorY: 0.5 }); self.startButton.addChild(self.startButtonText); // Add text as child of button // Add event listener to start button self.startButton.on("click", function () { if (gameState.currentState === "title") { gameState.startGame(); // Start game in standard mode } }); // Create Boss+ button (depends on asset/shape) self.newBossPlusButton = LK.getAsset('button_bg', {}); // Example shape button self.newBossPlusButton.x = 1024; // Center self.newBossPlusButton.y = 1500; // Position below start self.newBossPlusButton.anchorX = 0.5; self.newBossPlusButton.anchorY = 0.5; self.newBossPlusButton.visible = false; // Add text to Boss+ button self.newBossPlusButtonText = new Text2("NEW BOSS+", { fill: 0x000000, size: 40, font: "Arial", anchorX: 0.5, anchorY: 0.5 }); self.newBossPlusButton.addChild(self.newBossPlusButtonText); // Add event listener to Boss+ button self.newBossPlusButton.on("click", function () { if (gameState.currentState === "title") { gameState.startGame(true); // Start game in Boss+ mode } }); // Create Game Over message (depends on text rendering) self.gameOverMessage = new Text2("Game Over!", { fill: 0xFF0000, size: 80, font: "Arial", anchorX: 0.5, anchorY: 0.5, x: 1024, // Center X y: 1366, // Center Y visible: false // Hide initially }); // Create Grill Screen menu (depends on shape) self.grillMenu = LK.getAsset('grillMenu', { anchorX: 0.5, anchorY: 0.5 }); self.grillMenu.x = 1024; self.grillMenu.y = 1366; self.grillMenu.visible = false; // Hide initially // Add buttons/text to Grill Screen if needed // Add all UI elements to the game container (depth/layering handled by addChild order or setChildIndex) // Order matters for layering: background first, then game objects, then UI on top // Assuming UI should be on top of everything else in the 'game' container // Add UI elements AFTER adding player, boss, etc. in createGame // Or add them here and manage z-index // Let's add them here and assume later added elements are on top by default or managed by setChildIndex self.updateBossHealth = function (currentHp, maxHp) { // Ensure maxHp is at least 1 to prevent division by zero var safeMaxHp = Math.max(1, maxHp); // Calculate width based on health percentage var hpPercentage = currentHp / safeMaxHp; // Assuming original bossHpBar width was 800 self.bossHpBar.scaleX = hpPercentage; // Adjust position if anchor is not 0,0 to keep the bar filling from left // If anchorX is 0.5, the bar scales from the center. Need to adjust x. self.bossHpBar.x = 1024 - self.bossHpBar.width * self.bossHpBar.scaleX / 2; }; self.updateHearts = function (currentHearts, maxHearts) { // Clear existing heart graphics self.playerHearts.removeChildren(); // Add heart graphics based on currentHearts var heartSize = 40; // Match heart asset size var padding = 10; // Spacing between hearts for (var i = 0; i < maxHearts; i++) { var heartGraphic = LK.getShape('heart', {}); // Use heart shape asset // Position hearts horizontally within the container heartGraphic.x = i * (heartSize + padding); heartGraphic.y = 0; // All on the same vertical level // Tint heart based on current health if (i < currentHearts) { heartGraphic.tint = 0xFF0000; // Full heart color (Red) } else { heartGraphic.tint = 0x888888; // Empty heart color (Grayish) } self.playerHearts.addChild(heartGraphic); // Add heart to the container } // Position the container is done in gameState.positionElements or createGame }; // Add UI elements to game container in createGame after player/boss // This is handled in the modified createGame function self.update = function () { // UI elements are mostly passive and updated by boss/player health changes // No continuous update needed for UI elements themselves in this example, // but UI object could have update logic for animations, etc. }; return self; }(); // End UI object /**** * Boss class ****/ var Boss = Container.expand(function () { var self = Container.call(this); // Definicje list klatek dla fireballi - Zdefiniowane raz na początku klasy Boss var circleFrames = [LK.getAsset('fireball0', {}), LK.getAsset('fireball00', {}), LK.getAsset('fireball01', {}), LK.getAsset('fireball02', {}), LK.getAsset('fireball03', {}), LK.getAsset('fireball04', {}), LK.getAsset('fireball05', {}), LK.getAsset('fireball06', {}), LK.getAsset('fireball07', {}), LK.getAsset('fireball08', {}), LK.getAsset('fireball09', {}), LK.getAsset('fireball1', {}), LK.getAsset('fireball10', {}), LK.getAsset('fireball11', {}), LK.getAsset('fireball12', {}), LK.getAsset('fireball13', {}), LK.getAsset('fireball14', {})]; var lineFrames = [LK.getAsset('fireball2', {}), LK.getAsset('fireball3', {}), LK.getAsset('fireball4', {}), LK.getAsset('fireball5', {}), LK.getAsset('fireball6', {}), LK.getAsset('fireball7', {}), LK.getAsset('fireball8', {}), LK.getAsset('fireball9', {}), LK.getAsset('fireball15', {}), LK.getAsset('fireball16', {})]; // *** MODYFIKACJA: Dodaj grafikę bossIdle jako DZIECKO obiektu bossa (self) *** // Create and add the boss graphics var bossAsset = LK.getAsset('bossIdle', { anchorX: 0.5, anchorY: 0.5 // Pozycja relatywna na 0,0 jest domyślna i poprawna przy anchorX/Y 0.5 }); self.bossGraphics = bossAsset; // Add the bossGraphics as a child of self (correctly) self.addChild(self.bossGraphics); // Dodajemy funkcje animacji // Ta funkcja tworzy obiekt SpriteAnimation dla animacji ataku Bossa (nie fireballi) self.createBossAttackAnim = function () { var frames = [LK.getAsset('bossAttack0', {}), LK.getAsset('bossAttack1', {}), LK.getAsset('bossAttack2', {}), LK.getAsset('bossAttack3', {})]; var bossAttackAnim = new SpriteAnimation({ frames: frames, frameDuration: 100, // Czas trwania klatki animacji ataku Bossa (w ms) loop: false, anchorX: 0.5, anchorY: 0.5, // Pozycja animacji jako dziecka bossa - ustawiamy na 0,0 relatywnie do rodzica (self) x: 0, // MODIFIED y: 0 // MODIFIED }); return bossAttackAnim; }; // Zmieniamy sygnaturę funkcji, przyjmuje teraz typ ataku (do decydowania o grafice Bossa) // Ta funkcja zarządza grafiką Bossa (animacja ataku Bossa lub grafika idle) self.playBossAttackAnim = function (attackType) { // <--- DODANO PARAMETR // Upewnij się, że poprzednia animacja ataku Bossa jest całkowicie usunięta, zanim zaczniemy nową if (self.bossAttackAnim) { self.bossAttackAnim.stop(); // Zatrzymujemy animację // Usuń poprzednią animację z jej rodzica (Bossa) przed zniszczeniem // Sprawdź, czy rodzicem jest self przed próbą usunięcia if (self.bossAttackAnim.parent === self) { // MODIFIED check self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } else if (self.bossAttackAnim.parent) { // Fallback na wypadek, gdyby rodzic był inny self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } self.bossAttackAnim.destroy(); // Usuwamy poprzednią animację self.bossAttackAnim = null; // Ustawiamy na null po zniszczeniu } // *** Zmieniona logika: Zmiana grafiki bossa tylko jeśli to NIE jest atak szarży (attackType 2) *** if (attackType !== 2) { // Jeśli to atak koła (0) lub linii (1) // Usuń obecną główną grafikę bossa (idle) jako dziecko bossa // Sprawdź, czy rodzicem jest self przed próbą usunięcia if (self.bossGraphics && self.bossGraphics.parent === self) { // MODIFIED check self.bossGraphics.parent.removeChild(self.bossGraphics); } else if (self.bossGraphics && self.bossGraphics.parent) { // Fallback self.bossGraphics.parent.removeChild(self.bossGraphics); } self.bossGraphics = null; // Wyczyść referencję // Tworzymy nową animację bossa i dodajemy ją JAKO DZIECKO BOSSA self.bossAttackAnim = self.addChild(self.createBossAttackAnim()); // *** MODIFIED: DODANO DO self! *** // Pozycja animacji jako dziecka bossa jest już ustawiona na 0,0 w createBossAttackAnim // Metoda update dla TEJ NOWEJ animacji (definiowana tylko dla ataków 0 i 1) self.bossAttackAnim.update = function () { // Sprawdź, czy ten obiekt animacji jest nadal aktywny self.bossAttackAnim bossa // Użyj 'this' dla obiektu animacji, 'self' dla obiektu Boss if (self.bossAttackAnim !== this || !this.playing || this.frames.length === 0) { // Jeśli już nie jesteśmy aktualną animacją lub nie gramy, zakończ return; } this.frameTimer++; if (this.frameTimer >= this.frameDuration / (1000 / 60)) { // Przelicz ms na klatki gry (przy 60fps) this.frameTimer = 0; this.removeChildren(); // Usuń sprite klatki z kontenera animacji (z obiektu animacji) this.currentFrame++; if (this.currentFrame >= this.frames.length) { // Animacja skończona, wracamy do idle // *** Dodaj grafikę idle z powrotem JAKO DZIECKO BOSSA i ustaw self.bossGraphics *** self.bossGraphics = self.addChild(LK.getAsset('bossIdle', { // *** MODIFIED: DODANO DO self! *** anchorX: 0.5, anchorY: 0.5, x: 0, // Pozycja relatywna y: 0 // Pozycja relatywna })); // *** Usuń obiekt animacji z jego rodzica (Bossa) i zniszcz go *** // Sprawdź, czy rodzicem jest self przed próbą usunięcia if (this.parent === self) { // MODIFIED check this.parent.removeChild(this); // Użyj 'this' dla obiektu animacji } else if (this.parent) { // Fallback this.parent.removeChild(this); } this.destroy(); // Zniszcz obiekt animacji self.bossAttackAnim = null; // Wyczyść referencję w obiekcie bossa } else { this.addChild(this.frames[this.currentFrame]); // Dodaj sprite następnej klatki (do obiektu animacji) } } }; } // Else (attackType === 2, czyli chargeAttack): playBossAttackAnim nic nie robi z grafiką. // chargeAttack sam zadba o ustawienie grafiki 'idle' JAKO DZIECKO BOSSA. }; self.health = 100; // Domyślne zdrowie bossa (nadpisane w startGame) self.maxHealth = 100; // Domyślne max zdrowia bossa (nadpisane w startGame) self.speed = 5; // Prędkość ruchu bossa self.attackCooldown = 0; // Czas do następnego ataku (w klatkach gry) self.attackPattern = 0; self.attacks = []; // Aktywne ataki bossa (obiekty JS z pozycją, promieniem, itp.) self.stunned = false; self.stunDuration = 0; self.dead = false; self.phase = 1; self.attackSpeedMultiplier = 1; // Mnożnik prędkości ataków (1 = normalna, < 1 = szybsza) self.repositioning = false; // Flaga informująca czy boss się przemieszcza po szarży // startAttackPattern pozostaje jak w poprzedniej poprawionej wersji self.startAttackPattern = function () { // !!! Ustaw tymczasowy cooldown na początku, ZANIM sprawdzisz warunki powrotu !!! // To zapobiegnie natychmiastowemu ponownemu wywołaniu, jeśli metoda wróci wcześnie self.attackCooldown = 1; // Ustaw minimalny cooldown (np. 1 klatka) od razu // Sprawdź warunki wczesnego powrotu: boss jest martwy, gra nie jest w stanie "game" LUB boss jest w stanie "repositioning" if (self.dead || gameState.currentState !== "game" || self.repositioning) { // Dodano || self.repositioning return; // Jeśli warunek spełniony, wróć } // Decydujemy O WZORCU ATAKU przed zmianą grafiki bossa self.attackPattern = (self.attackPattern + 1) % 3; // Cykl wzorców ataków (0, 1, 2) // 🔥 Przekazujemy typ ataku do playBossAttackAnim - ta funkcja teraz decyduje CZY zmienić grafikę bossa self.playBossAttackAnim(self.attackPattern); // <--- PRZEKAZUJEMY TYP ATAKU // Następnie wywołujemy właściwą funkcję ataku (chargeAttack itp.) // Logika graficzna dla szarży jest TERAZ W chargeAttack switch (self.attackPattern) { case 0: self.circleAttack(); // Wywołaj atak koła break; case 1: self.lineAttack(); // Wywołaj atak linii break; case 2: self.chargeAttack(); // Wywołaj atak szarży break; } // Ustaw właściwy czas odnowienia dla następnego ataku self.attackCooldown = (90 + Math.floor(Math.random() * 60)) * self.attackSpeedMultiplier; // 1.5-2.5 seconds * multiplier }; // Zmodyfikowana funkcja createAttack - NIE nadpisuje lokalnie spriteAnim.update (jak w poprzedniej próbie) // Kolejność argumentów: x, y, duration (ms), activationFrame (index klatki), frameDurationMs (ms), framesList (opcjonalne) // Logika aktywacji kolizji (isActive) jest w Boss.update self.createAttack = function (x, y, duration, activationFrame, frameDurationMs, framesList) { // Użyj przekazanej framesList, domyślną oryginalną jeśli nie przekazana var frames = framesList || [LK.getAsset('fireball0', {}), LK.getAsset('fireball00', {}), LK.getAsset('fireball01', {}), LK.getAsset('fireball02', {}), LK.getAsset('fireball03', {}), LK.getAsset('fireball04', {}), LK.getAsset('fireball05', {}), LK.getAsset('fireball06', {}), LK.getAsset('fireball07', {}), LK.getAsset('fireball08', {}), LK.getAsset('fireball09', {}), LK.getAsset('fireball1', {}), LK.getAsset('fireball10', {}), LK.getAsset('fireball11', {}), LK.getAsset('fireball12', {}), LK.getAsset('fireball13', {}), LK.getAsset('fireball14', {})]; // console.log("CreateAttack called at:", x, y, " Frames array:", frames); // Debug log - można usunąć po naprawie // if (!frames || frames.length === 0 || frames.some(f => f === null || typeof f === 'undefined' || !f.texture)) { // Debug check - można usunąć po naprawie // console.error("Frames array is empty or contains invalid assets! Attack creation might fail."); // Debug log - można usunąć po naprawie // } var spriteAnim = game.addChild(new SpriteAnimation({ // Wizualizacja fireballa jest dodawana do kontenera 'game' frames: frames, frameDuration: frameDurationMs, // Użyj wartości przekazanej do konstruktora SpriteAnimation loop: false, // Animacja nie zapętla się anchorX: 0.5, anchorY: 0.5, x: x, y: y })); spriteAnim.scaleX = 1.6; // Ręczne skalowanie wizualizacji spriteAnim.scaleY = 1.6; var attack = { // Obiekt logiczny ataku x: x, y: y, radius: 60, // Promień kolizji visual: spriteAnim, // Referencja do wizualizacji (SpriteAnimation) lifeTime: Math.floor(duration * (60 / 1000)), // Czas życia w klatkach gry (przy 60fps) isActive: false, // Flaga aktywności kolizji, domyślnie false activationFrame: activationFrame // Przechowuj activationFrame w obiekcie ataku }; // USUNIĘTO: Lokalna definicja funkcji spriteAnim.update self.attacks.push(attack); // Dodaj obiekt ataku logicznego do tablicy bossa }; // Zmodyfikowana funkcja circleAttack - Z pętlą setTimeout delay (jak w poprzedniej próbie, która dała wiele pocisków) self.circleAttack = function () { LK.getSound('bossAttack').play(); // Odtwórz dźwięk ataku var center = { // Ustaw środek koła na pozycji bossa x: self.x, y: self.y }; var count = isNewBossPlusMode ? 12 : 8; // Liczba pocisków w ataku (więcej w New Boss+) var radius = 300; // Promień koła, na którym rozmieszczone są pociski var attackLifeTime = isNewBossPlusMode ? 2000 : 3000; // Czas życia pocisku w ms (krótszy w New Boss+?) // circleFrames array jest zdefiniowane raz na początku klasy Boss // Pętla tworząca wiele pocisków rozmieszczonych w kole Z MINIMALNYM OPÓŹNIENIEM DLA KAŻDEGO for (var i = 0; i < count; i++) { // Iteruj tyle razy, ile ma być pocisków (count) var angle = i / count * Math.PI * 2; // Oblicz kąt dla bieżącego pocisku (równo rozmieszczone na okręgu) var posX = center.x + Math.cos(angle) * radius; // Oblicz współrzędną X na okręgu var posY = center.y + Math.sin(angle) * radius; // Oblicz współrzędną Y na okręgu // Dodaj minimalne opóźnienie (np. 20ms) DO KAŻDEGO pocisku var delay = i * 20 + 20; // Zapewnij, że nawet pierwszy pocisk ma opóźnienie // Użyj setTimeout do stworzenia każdego pocisku po małym opóźnieniu LK.setTimeout(function (x, y) { // Używamy closure do "zamknięcia" wartości x, y (frames jest globalne/dostępne) return function () { // Sprawdź czy boss nadal istnieje i gra jest w stanie 'game' zanim stworzysz atak po opóźnieniu if (self && !self.dead && gameState.currentState === "game") { // Wywołaj funkcję createAttack dla tego pocisku z zapamiętaną pozycją i parametrami // Kolejność argumentów createAttack: x, y, duration(ms), activationFrame(index), frameDurationMs(ms), framesList // activationFrame = 8 (aktywacja od klatki fireball08) // frameDurationMs = 250ms (przykład prędkości) // framesList = circleFrames (używamy zmiennej zdefiniowanej poza funkcją) self.createAttack(x, y, attackLifeTime, 8, 250, circleFrames); // Używamy 250ms dla frameDurationMs } }; }(posX, posY), delay); // Przekaż aktualne posX, posY do closure } }; // *** START Zmodyfikowana funkcja lineAttack - Używa synchronicznej pętli i wywołuje nową createAttack *** self.lineAttack = function () { LK.getSound('bossAttack').play(); // Odtwórz dźwięk ataku // Tworzy linię ataków if (!player) { return; } var targetX = player.x; var targetY = player.y; var count = isNewBossPlusMode ? 8 : 5; // Liczba pocisków w linii var attackLifeTime = isNewBossPlusMode ? 1500 : 2000; // Czas życia pocisku w ms var delayBetweenAttacks = isNewBossPlusMode ? 100 : 200; // Opóźnienie między pociskami w ms // lineFrames array jest zdefiniowane raz na początku klasy // Pętla tworząca pociski w linii z opóźnieniem (jak w souls5.txt) for (var i = 0; i < count; i++) { var t = i / (count - 1); // Współczynnik interpolacji var posX = self.x + (targetX - self.x) * t; // Interpolowana pozycja X var posY = self.y + (targetY - self.y) * t; // Interpolowana pozycja Y var delay = i * delayBetweenAttacks * self.attackSpeedMultiplier; // Opóźnienie przed stworzeniem pocisku, skalowane LK.setTimeout(function (x, y) { // Używamy closure do przekazania pozycji return function () { // Sprawdź warunki przed stworzeniem ataku po opóźnieniu if (self && !self.dead && gameState.currentState === "game") { // Wywołaj funkcję createAttack // Kolejność argumentów createAttack: x, y, duration(ms), activationFrame(index), frameDurationMs(ms), framesList // activationFrame = 3 (indeks 'fireball5' na liście lineFrames) // frameDurationMs = 100ms (przykład prędkości) // framesList = lineFrames (używamy zmiennej zdefiniowanej poza funkcją) self.createAttack(x, y, attackLifeTime, 3, 100, lineFrames); // Przekaż parametry } }; }(posX, posY), delay); // Przekaż aktualne posX, posY do closure } }; // *** END Zmodyfikowana funkcja lineAttack *** // *** START Zmodyfikowana funkcja chargeAttack - tworzy pociski podczas szarży *** // Ta funkcja zarządza ruchem bossa (szarżą) i tworzy pociski wzdłuż ścieżki szarży self.chargeAttack = function () { LK.getSound('bossAttack').play(); // Odtwórz dźwięk szarży // Upewnij się, że gracz istnieje if (!player) { return; } // Obsługa grafiki bossa podczas szarży (pokazuje grafikę idle) if (self.bossAttackAnim) { self.bossAttackAnim.stop(); if (self.bossAttackAnim.parent === self) { self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } else if (self.bossAttackAnim.parent) { self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } self.bossAttackAnim.destroy(); self.bossAttackAnim = null; } if (self.bossGraphics && self.bossGraphics.parent === self) { self.bossGraphics.parent.removeChild(self.bossGraphics); } else if (self.bossGraphics && self.bossGraphics.parent) { self.bossGraphics.parent.removeChild(self.bossGraphics); } self.bossGraphics = self.addChild(LK.getAsset('bossIdle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); // Oblicz kierunek do gracza i parametry szarży 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; } var startX = self.x; // Pozycja początkowa szarży var startY = self.y; var chargeDistance = isNewBossPlusMode ? 700 : 500; // Dystans szarży var chargeDuration = isNewBossPlusMode ? // Czas trwania szarży 600 : 800; var attacksOnPathCount = isNewBossPlusMode ? 8 : 5; // Liczba pocisków wzdłuż ścieżki var attackLifeTime = isNewBossPlusMode ? 1000 : 1500; // Czas życia pocisku // Tween (animacja ruchu) bossa podczas szarży tween(self, { x: self.x + dx * chargeDistance, y: self.y + dy * chargeDistance }, { duration: chargeDuration * self.attackSpeedMultiplier, // Czas trwania, skalowany easing: tween.easeIn, // Krzywa animacji onFinish: function onFinish() { // Po zakończeniu szarży if (self && !self.dead && gameState.currentState === "game") { // Faza repozycji self.repositioning = true; LK.setTimeout(function () { if (self && !self.dead) { self.repositioning = false; } }, 500 * self.attackSpeedMultiplier); // Tween powrotu do pozycji startowej tween(self, { x: startX, y: startY }, { duration: 1000 * self.attackSpeedMultiplier, easing: tween.easeOut }); // Utworzenie ataków (fireballi) wzdłuż ścieżki szarży // Fireballe ze szarży używają domyślnej listy klatek z createAttack (circleFrames) for (var i = 0; i < attacksOnPathCount; i++) { var t = i / (attacksOnPathCount - 1); // Współczynnik interpolacji var currentChargeX = self.x; // Aktualna pozycja bossa (koniec szarży) var currentChargeY = self.y; var posX = startX + (currentChargeX - startX) * t; // Interpolowana pozycja X var posY = startY + (currentChargeY - startY) * t; // Interpolowana pozycja Y // Utwórz atak // Kolejność argumentów createAttack: x, y, duration(ms), activationFrame(index), frameDurationMs(ms), framesList // activationFrame = 4 (domyślna klatka aktywacji dla tych pocisków) // frameDurationMs = 140ms (domyślna prędkość) // framesList = circleFrames (używamy listy klatek ataku koła) self.createAttack(posX, posY, attackLifeTime, 4, 140, circleFrames); // Przekaż parametry } } } }); }; self.takeDamage = function (amount) { // Funkcja obsługująca otrzymywanie obrażeń przez bossa if (self.dead || gameState.currentState !== "game") { // Boss otrzymuje obrażenia tylko w stanie gry return; } self.health -= amount; // Zmniejsz zdrowie self.health = Math.max(0, self.health); // Upewnij się, że zdrowie nie spadnie poniżej zera LK.effects.flashObject(self, 0xFFFFFF, 200); // Efekt błysku if (!isNewBossPlusMode) { // Przejście fazy bossa przy 50% zdrowia tylko w standardowym trybie if (self.health <= self.maxHealth / 2 && self.phase === 1) { self.phase = 2; // Zmień fazę na 2 self.speed += 2; // Boss staje się szybszy (ruch) self.attackSpeedMultiplier *= 0.8; // Boss atakuje nieco szybciej tween(self, { tint: 0xFF3300 // Pomarańczowy/czerwony odcień }, { duration: 1000, easing: tween.easeInOut }); } } if (self.health <= 0) { self.die(); // Wywołaj funkcję śmierci } // ui.updateBossHealth jest w game.update }; self.die = function () { // Funkcja obsługująca śmierć bossa if (self.dead || gameState.currentState !== "game") { return; } self.dead = true; // Ustaw flagę śmierci if (!isNewBossPlusMode) { // Dźwięk zwycięstwa tylko dla STANDARDOWEGO trybu LK.getSound('victory').play(); } // Wyczyść pozostałe ataki przy śmierci bossa self.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); self.attacks = []; // Wyczyść tablicę ataków logicznych // Animacja śmierci bossa (zanikanie i powiększanie) tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Po zakończeniu animacji śmierci if (self && self.destroy && !self.destroyed) { self.destroy(); // Zniszcz obiekt bossa } storage.bossesDefeated = (storage.bossesDefeated || 0) + 1; // Zwiększ licznik pokonanych bossów // ZAWSZE przechodzimy do Grill Screena gameState.showGrillScreen(); } }); }; self.update = function () { // Główna metoda aktualizacji bossa // Aktualizuj tylko w stanie gry if (gameState.currentState !== "game") { if (!self.dead && self.attacks) { // Jeśli zmieniono stan i boss nie umiera, wyczyść ataki self.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); self.attacks = []; } return; } // Nie aktualizuj jeśli boss jest martwy (pozwól animacji śmierci działać) if (self.dead) { return; } // --- LOGIKA RUCHU BOSS --- // Poruszaj się w kierunku gracza jeśli spełnione warunki if (!self.repositioning && self.attackCooldown > 30 && player && !player.dead) { var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var moveSpeed = self.speed; if (distance > 150) { // Poruszaj się tylko jeśli gracz jest dalej niż 150 jednostek var moveX = dx / distance * moveSpeed; var moveY = dy / distance * moveSpeed; var nextX = self.x + moveX; var nextY = self.y + moveY; // Ograniczenia areny var halfWidth = self.width * self.scaleX / 2; var halfHeight = self.height * self.scaleY / 2; var minX = 100 + halfWidth; var maxX = 2048 - 100 - halfWidth; var minY = 100 + halfHeight; var maxY = 2732 - 100 - halfHeight; self.x = Math.max(minX, Math.min(nextX, maxX)); self.y = Math.max(minY, Math.min(nextY, maxY)); // Grafika jako dziecko podąża automatycznie. } } // --- KONIEC LOGIKA RUCHU BOSS --- // --- OBSŁUGA ATAKÓW BOSS (Kolizje i Czas Życia Pocisków) --- // Aktualizuj istniejące ataki bossa (pociski) i sprawdzaj kolizje z graczem for (var i = self.attacks.length - 1; i >= 0; i--) { var attack = self.attacks[i]; // Aktualizuj flagę isActive na podstawie aktualnej klatki visual i przechowywanego activationFrame // Sprawdź czy visual istnieje i ma currentFrame if (attack.visual && typeof attack.visual.currentFrame !== 'undefined') { if (attack.visual.currentFrame >= attack.activationFrame) { attack.isActive = true; // Aktywuj kolizję } else { attack.isActive = false; // Kolizja nieaktywna } } else { attack.isActive = false; } if (attack.lifeTime > 0) { // Zmniejszaj czas życia attack.lifeTime--; } // Sprawdź kolizję z graczem jeśli spełnione warunki if (attack.isActive && player && !player.dead && !player.invulnerable && attack.visual && !attack.visual.destroyed) { var dx_p = player.x - attack.x; var dy_p = player.y - attack.y; var distance_p = Math.sqrt(dx_p * dx_p + dy_p * dy_p); var playerRadius_p = player.width / 2; var attackRadius = attack.radius; if (distance_p < playerRadius_p + attackRadius) { player.takeDamage(1); // Gracz otrzymuje obrażenia if (attack.visual && attack.visual.destroy) { attack.visual.destroy(); } self.attacks.splice(i, 1); // Usuń trafiony atak // break; } } // Usuń atak, jeśli skończył mu się czas życia LUB wizualizacja zniszczona if (attack.lifeTime <= 0 || attack.visual && attack.visual.destroy || attack.visual && attack.visual.destroyed) { // Dodano trzeci warunek dla pewności if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } self.attacks.splice(i, 1); } } // --- KONIEC OBSŁUGA ATAKÓW BOSS --- if (self.attackCooldown > 0) { // Zmniejszaj cooldown self.attackCooldown--; } // Sprawdź, czy nadszedł czas na nowy atak if (self.attackCooldown <= 0 && !self.repositioning) { self.startAttackPattern(); } }; return self; // Zwróć instancję obiektu Boss })(); // End Boss class /**** * Game creation and loop ****/ // Global variables for game objects // Already defined at the beginning: var player, boss, ui, game, gameContainer, etc. var createGame = function createGame() { // Upewnij się, że poprzednia instancja gry jest posprzątana if (game) { game.destroy(); game = null; } game = new DisplayObjectContainer(); // Stwórz główny kontener gry LK.add(game); // Dodaj kontener gry do frameworka LK // Stwórz i dodaj podstawowe elementy tła od razu (nie zależą od assetów fireballi) gameState.createWalls(); // Tworzy i dodaje ściany gameState.createFloor(); // Tworzy i dodaje podłogę // *** MODYFIKACJA: Dodaj opóźnienie setTimeout przed tworzeniem elementów zależnych od assetów *** // Daje to assetom czas na załadowanie po odpaleniu onInit przez LK.init LK.setTimeout(function () { // *** Kod z oryginalnego createGame, który tworzył elementy zależne od assetów, PRZENIESIONY tutaj *** // Stwórz i dodaj gracza (zależy od assetów) player = new Player(); // Klasa Player prawdopodobnie używa assetów game.addChild(player); // Stwórz i dodaj bossa (mocno zależy od assetów dla grafiki i ataków) boss = new Boss(); // Klasa Boss używa wielu assetów game.addChild(boss); // Stwórz i dodaj elementy UI (niektóre elementy UI mogą używać assetów) ui = new UI(); // Klasa UI prawdopodobnie używa assetów (serca, przyciski, itp.) game.addChild(ui); // Add UI elements to the game container (order matters for layering) // Assuming UI should be on top of game elements game.addChild(ui.bossHpBar); game.addChild(ui.playerHearts); game.addChild(ui.startButton); // Start button is visible in title state game.addChild(ui.newBossPlusButton); // Boss+ button is visible in title state game.addChild(ui.gameOverMessage); // Game over message is hidden initially game.addChild(ui.grillMenu); // Grill menu is hidden initially // Start the game state (enables updates, attacks, etc.) // This was the last line in the original createGame gameState.changeState("title"); // Rozpocznij grę w stanie title console.log("Opóźnione kroki tworzenia gry wykonane."); // Opcjonalny log do potwierdzenia odpalenia timeoutu }, 500); // *** CZAS OPÓŹNIENIA (np. 500ms) - Dostosuj w razie potrzeby *** // To jest opóźnienie heurystyczne. Lepszym rozwiązaniem byłby właściwy mechanizm preładowania, jeśli dostępny. // Oryginalne linie kodu po ustawieniach createGame i przed gameState.changeState są teraz wewnątrz timeoutu // gameState.changeState("title"); // This line is moved inside the timeout }; var gameLoop = function gameLoop() { // Główne aktualizacje gry // LK automatycznie wywołuje update na obiektach dodanych do sceny (game) // Możemy dodać tutaj logikę globalną // Na przykład, aktualizacja UI, która nie jest automatycznie wywoływana na obiektach UI, chyba że UI jest DisplayObjectContainer i dodano je do sceny. // Aktualizuj UI niezależnie od stanu gry, bo jego widoczność i zawartość zależą od stanu/danych // Ale tylko jeśli UI zostało stworzone if (ui) { // UI update logic if needed (currently empty) // ui.update(); // Zakładając, że UI ma metodę update // Aktualizuj pasek zdrowia bossa (jeśli boss istnieje) if (boss) { // ui.updateBossHealth(boss.health, boss.maxHealth); // Ta linia była wcześniej, ale lepiej wywoływać ją po zmianie zdrowia bossa // Zaktualizuj UI HP Bossa w game.update, ale tylko gdy HP bossa > 0 (żeby nie migało przy śmierci) // UI HP Boss aktualizowane jest teraz w takeDamage i die Bossa. // Upewnijmy się, że jest aktualizowane też jeśli boss HP > 0. if (boss.health > 0 || gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode) { ui.updateBossHealth(boss.health, boss.maxHealth); } else { // Jeśli boss martwy i nie jest to game over po czasie, ukryj pasek if (!(gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode)) { ui.updateBossHealth(0, 1); // Ukryj pasek } } } else { // Jeśli boss nie istnieje, ukryj pasek (chyba że to wygrana Boss+ przez czas) if (!(gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode)) { // Domyślne maxHP do ukrycia paska to 1 (uniknięcie dzielenia przez 0) ui.updateBossHealth(0, 1); } } // Aktualizuj serca gracza (jeśli gracz istnieje i UI istnieje) if (player) { ui.updateHearts(player.health, storage.maxHearts); } else if (gameState.currentState !== "game" && gameState.currentState !== "gameOver") { // Ukryj serca poza grą/game over ui.updateHearts(0, storage.maxHearts); } // Pozycjonowanie/widoczność elementów UI jest zarządzana przez gameState.positionElements() w metodach zmiany stanu. } // Logika specyficzna dla stanu gry if (gameState.currentState === "game") { if (player) { player.update(); // Aktualizacja gracza (ruch turlania, nietykalność, kolizja turlania z bossem) } if (boss) { boss.update(); // Aktualizacja bossa (ruch, cooldown, tworzenie/usuwanie ataków, kolizje ataków z graczem) } } // Logika game over else if (gameState.currentState === "gameOver") { // Tutaj można dodać logikę game over, np. animację, możliwość restartu // Aktualizacja player i boss nadal jest wywoływana, ale ich metody update powinny sprawdzać stan gry if (player) { player.update(); } // Nadal aktualizuj gracza (np. animacja śmierci) if (boss) { boss.update(); } // Nadal aktualizuj bossa (np. animacja śmierci) // Można dodać logikę przycisków restartu, powrotu do menu itp. } // Logika grill screen else if (gameState.currentState === "grillScreen") { // Tutaj można dodać logikę grill screen, np. animację, możliwość przejścia dalej if (player) { player.update(); } // Nadal aktualizuj gracza (jeśli widoczny) if (boss) { boss.update(); } // Nadal aktualizuj bossa (jeśli widoczny) // Można dodać logikę przycisków przejścia dalej, powrotu do menu itp. } // Rysowanie sceny (wykonywane automatycznie przez LK po gameLoop) }; // Inicjalizacja gry LK.init(gameContainer, { onInit: function onInit() { storage = LK.localStorage; // Initialize storage here after LK.init // Load or set maxHearts from storage if needed, default to 5 storage.maxHearts = storage.maxHearts || 5; // Default max hearts // Determine game mode (Boss+ or Standard) from storage or default isNewBossPlusMode = storage.newBossPlusMode === true; // Default to false if not in storage createGame(); // Call the createGame function to set up the initial game state and objects // Game loop is set up by LK.init to call gameLoop function repeatedly (e.g., 60 times per second) } });
===================================================================
--- original.js
+++ change.js
@@ -596,10 +596,10 @@
anchorY: 0.5
// Pozycja relatywna na 0,0 jest domyślna i poprawna przy anchorX/Y 0.5
});
self.bossGraphics = bossAsset;
- // Add the bossGraphics as a child of self
- Container.prototype.addChild.call(self, self.bossGraphics);
+ // Add the bossGraphics as a child of self (correctly)
+ self.addChild(self.bossGraphics);
// Dodajemy funkcje animacji
// Ta funkcja tworzy obiekt SpriteAnimation dla animacji ataku Bossa (nie fireballi)
self.createBossAttackAnim = function () {
var frames = [LK.getAsset('bossAttack0', {}), LK.getAsset('bossAttack1', {}), LK.getAsset('bossAttack2', {}), LK.getAsset('bossAttack3', {})];