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
User prompt
Please fix the bug: 'LK.getShape is not a function' in or related to this line: 'self.grillMenu = LK.getShape('grillMenu', {});' Line Number: 585
User prompt
Please fix the bug: 'LK.Text is not a constructor' in or related to this line: 'self.gameOverMessage = new LK.Text({' Line Number: 572
User prompt
Please fix the bug: 'LK.Text is not a constructor' in or related to this line: 'self.newBossPlusButtonText = new LK.Text({' Line Number: 557
User prompt
Please fix the bug: 'LK.getShape is not a function' in or related to this line: 'self.newBossPlusButton = LK.getShape('button_bg', {}); // Example shape button' Line Number: 550
User prompt
Please fix the bug: 'LK.Text is not a constructor' in or related to this line: 'self.startButtonText = new LK.Text({' Line Number: 535
User prompt
Please fix the bug: 'LK.getShape is not a function' in or related to this line: 'self.startButton = LK.getShape('button_bg', {}); // Example shape button' Line Number: 528
User prompt
Please fix the bug: 'DisplayObjectContainer is not defined' in or related to this line: 'self.playerHearts = new DisplayObjectContainer(); // Container for player hearts' Line Number: 522
User prompt
Please fix the bug: 'LK.getShape is not a function' in or related to this line: 'self.bossHpBar = LK.getShape('bossHpbar', {});' Line Number: 519
Code edit (1 edits merged)
Please save this source code
Code edit (6 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: self.startAttackPattern is not a function' in or related to this line: 'self.startAttackPattern();' Line Number: 470
Code edit (1 edits merged)
Please save this source code
Code edit (8 edits merged)
Please save this source code
User prompt
add assets bossAttack0 to bossattack3
User prompt
add assets bossAttack0 to bossattack3
Code edit (5 edits merged)
Please save this source code
User prompt
add asset fireball13 fireball14 fireball15 fireball16
Code edit (2 edits merged)
Please save this source code
User prompt
add assets fireball00 to fireball12
User prompt
Please fix the bug: 'TypeError: self.clearRollTimeouts is not a function' in or related to this line: 'self.clearRollTimeouts();' Line Number: 589
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: 'Uncaught ReferenceError: SpriteAnimation is not defined' in or related to this line: 'var rollAnimation = game.addChild(new SpriteAnimation({' Line Number: 448
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem) var Boss = Container.expand(function () { var self = Container.call(this); self.bossGraphics = self.attachAsset('bossIdle', { // [cite: 3] anchorX: 0.5, anchorY: 0.5 }); // Dodajemy funkcje animacji self.createBossAttackAnim = function () { var frames = [LK.getAsset('bossAttack0', {}), LK.getAsset('bossAttack1', {}), LK.getAsset('bossAttack2', {}), LK.getAsset('bossAttack3', {})]; // [cite: 3] var bossAttackAnim = new SpriteAnimation({ frames: frames, frameDuration: 100, loop: false, anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y }); return bossAttackAnim; }; self.playBossAttackAnim = function () { // Upewnij się, że poprzednia animacja jest całkowicie usunięta, zanim zaczniemy nową if (self.bossAttackAnim) { self.bossAttackAnim.stop(); // Zatrzymujemy animację // *** Usuń poprzednią animację z jej rodzica przed zniszczeniem *** if (self.bossAttackAnim.parent) { self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } self.bossAttackAnim.destroy(); // Usuwamy poprzednią animację [cite: 3] self.bossAttackAnim = null; // Ustawiamy na null po zniszczeniu } // *** WAŻNA POPRAWKA: Usuń obecną główną grafikę bossa (idle) przed rozpoczęciem animacji ataku *** if (self.bossGraphics && self.bossGraphics.parent) { self.bossGraphics.parent.removeChild(self.bossGraphics); } self.bossGraphics = null; // Wyczyść referencję // Tworzymy nową animację bossa self.bossAttackAnim = game.addChild(self.createBossAttackAnim()); // Nadal dodajemy do 'game' [cite: 4] // Ustawiamy pozycję animacji, żeby podążała za bossem self.bossAttackAnim.x = self.x; // [cite: 4] self.bossAttackAnim.y = self.y; // [cite: 4] // Metoda update dla TEJ NOWEJ animacji (tutaj definiujemy jej zachowanie) self.bossAttackAnim.update = function () { if (!self.bossAttackAnim.playing || self.bossAttackAnim.frames.length === 0) { return; } self.bossAttackAnim.frameTimer++; if (self.bossAttackAnim.frameTimer >= self.bossAttackAnim.frameDuration / (1000 / 60)) { // [cite: 6] self.bossAttackAnim.frameTimer = 0; self.bossAttackAnim.removeChildren(); // Usuń sprite klatki z kontenera animacji [cite: 6] self.bossAttackAnim.currentFrame++; if (self.bossAttackAnim.currentFrame >= self.bossAttackAnim.frames.length) { // Animacja skończona, wracamy do idle // *** Dodaj grafikę idle z powrotem do 'game' i ustaw self.bossGraphics *** self.bossGraphics = game.addChild(LK.getAsset('bossIdle', { // [cite: 7] anchorX: 0.5, anchorY: 0.5, x: self.x, // Użyj pozycji bossa [cite: 7] y: self.y // Użyj pozycji bossa [cite: 7] })); // *** Usuń obiekt animacji z jego rodzica i zniszcz go *** if (self.bossAttackAnim.parent) { self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } self.bossAttackAnim.destroy(); // [cite: 7] self.bossAttackAnim = null; // Wyczyść referencję } else { self.bossAttackAnim.addChild(self.bossAttackAnim.frames[self.bossAttackAnim.currentFrame]); // Dodaj sprite następnej klatki [cite: 8] } } }; }; // ... (pozostała część klasy Boss, aż do chargeAttack) ... self.chargeAttack = function () { LK.getSound('bossAttack').play(); // [cite: 34] // Upewnij się, że gracz istnieje przed próbą odczytu jego pozycji if (!player) { // [cite: 35] return; } // *** USUŃ PONIŻSZY BLOK KODU - Zarządzanie grafiką jest teraz w playBossAttackAnim i metodzie update animacji *** // if (self.bossGraphics) { // [cite: 35] // self.bossGraphics.destroy(); // Usuwamy poprzednią animację, jeśli była // } // self.bossGraphics = game.addChild(LK.getAsset('bossIdle', { // [cite: 36] // anchorX: 0.5, // anchorY: 0.5, // x: self.x, // y: self.y // })); // *** KONIEC USUWANIA *** // Oblicz kierunek do gracza var dx = player.x - self.x; // [cite: 36] var dy = player.y - self.y; // [cite: 37] var distance = Math.sqrt(dx * dx + dy * dy); // [cite: 37] if (distance > 0) { // [cite: 37] dx /= distance; dy /= distance; // [cite: 38] } // Zapisz oryginalną pozycję (z której zaczyna się szarża) var startX = self.x; // [cite: 39] var startY = self.y; // [cite: 39] var chargeDistance = isNewBossPlusMode ? 700 : 500; // Dłuższa szarża w New Boss+? [cite: 39] var chargeDuration = isNewBossPlusMode ? // [cite: 40] 600 : 800; // Szybsza szarża w New Boss+? var attacksOnPathCount = isNewBossPlusMode ? 8 : 5; // [cite: 41] // Więcej ataków na ścieżce szarży? var attackLifeTime = isNewBossPlusMode ? 1000 : 1500; // Krótszy czas życia? [cite: 42] // Animacja szarży (boss przesuwa się szybko w kierunku gracza) tween(self, { x: self.x + dx * chargeDistance, // Przesunięcie [cite: 42] y: self.y + dy * chargeDistance // [cite: 43] }, { duration: chargeDuration * self.attackSpeedMultiplier, // Czas trwania szarży, skalowane [cite: 43] easing: tween.easeIn, onFinish: function onFinish() { // Sprawdź, czy boss nadal żyje i gra jest w stanie gry if (self && !self.dead && gameState.currentState === "game") { // [cite: 43] // --- Start Repositioning Phase --- self.repositioning = true; // Ustaw flagę repozycji po zakończeniu szarży [cite: 43] // Czas na repozycję (np. 0.5 sekundy), skalowane LK.setTimeout(function () { // [cite: 43] if (self && !self.dead) { // [cite: 43] self.repositioning = false; // Zakończ stan repozycji po opóźnieniu [cite: 43] } }, 500 * self.attackSpeedMultiplier); // Opóźnienie skalowane przez mnożnik prędkości ataków [cite: 43] // --- End Repositioning Phase --- // Powrót do oryginalnej pozycji (startu szarży) po szarży tween(self, { // [cite: 44] x: startX, // [cite: 44] y: startY // [cite: 44] }, { duration: 1000 * self.attackSpeedMultiplier, // Czas powrotu, skalowane [cite: 44] easing: tween.easeOut }); // Utwórz ataki wzdłuż ścieżki szarży for (var i = 0; i < attacksOnPathCount; i++) { // [cite: 44] var t = i / (attacksOnPathCount - 1); // [cite: 45] var currentChargeX = self.x; // Get the end position of the charge [cite: 45] var currentChargeY = self.y; // [cite: 46] // Interpolujemy pomiędzy początkiem a końcem szarży var posX = startX + (currentChargeX - startX) * t; // [cite: 47] var posY = startY + (currentChargeY - startY) * t; // [cite: 47] // Utwórz atak w danej pozycji self.createAttack(posX, posY, attackLifeTime); // [cite: 48] // Czas życia skalowane w createAttack } } } }); }; self.createAttack = function (x, y, duration, framesList) { 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', {}), // 🔥 brakowało LK.getAsset('fireball14', {}) // 🔥 brakowało ]; var spriteAnim = game.addChild(new SpriteAnimation({ frames: frames, frameDuration: 140, // 🕒 tutaj też spowolniłem jak chciałeś loop: false, anchorX: 0.5, anchorY: 0.5, x: x, y: y })); spriteAnim.scaleX = 1.6; // 🔥 ręcznie ustawiamy spriteAnim.scaleY = 1.6; // 🔥 ręcznie ustawiamy var attack = { x: x, y: y, radius: 60, visual: spriteAnim, lifeTime: Math.floor(duration * (60 / 1000)), isActive: false }; spriteAnim.update = function () { if (!spriteAnim.playing || spriteAnim.frames.length === 0) { return; } spriteAnim.frameTimer++; if (spriteAnim.frameTimer >= spriteAnim.frameDuration / (1000 / 60)) { spriteAnim.frameTimer = 0; spriteAnim.removeChildren(); spriteAnim.currentFrame++; if (spriteAnim.currentFrame >= spriteAnim.frames.length) { spriteAnim.currentFrame = spriteAnim.frames.length - 1; spriteAnim.playing = false; } spriteAnim.addChild(spriteAnim.frames[spriteAnim.currentFrame]); if (spriteAnim.currentFrame === 7 || spriteAnim.currentFrame === 4) { attack.isActive = true; } } }; self.attacks.push(attack); }; self.circleAttack = function () { var numAttacks = isNewBossPlusMode ? 12 : 8; var radius = 300; for (var i = 0; i < numAttacks; i++) { var angle = i / numAttacks * Math.PI * 2; var x = self.x + Math.cos(angle) * radius; var y = self.y + Math.sin(angle) * radius; self.createAttack(x, y, 3000 * self.attackSpeedMultiplier, [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', {})]); } }; self.lineAttack = function () { var numAttacks = isNewBossPlusMode ? 8 : 5; var startX = self.x; var startY = self.y; var endX = player.x; var endY = player.y; var dx = (endX - startX) / numAttacks; var dy = (endY - startY) / numAttacks; for (var i = 0; i < numAttacks; i++) { var x = startX + dx * (i + 1); var y = startY + dy * (i + 1); self.createAttack(x, y, 2000 * self.attackSpeedMultiplier, [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', {})]); } }; self.takeDamage = function (amount) { if (self.dead || gameState.currentState !== "game") { // Boss otrzymuje obrażenia tylko w stanie gry return; } self.health -= amount; // Zmniejsz zdrowie // Upewnij się, że zdrowie nie spadnie poniżej zera (chyba że do śmierci) self.health = Math.max(0, self.health); // Wizualne sygnalizowanie otrzymania obrażeń LK.effects.flashObject(self, 0xFFFFFF, 200); // Przejście fazy bossa pri 50% zdrowia (może być wyłączone w New Boss+?) if (!isNewBossPlusMode) { // Tylko w standardowym trybie if (self.health <= self.maxHealth / 2 && self.phase === 1) { self.phase = 2; self.speed += 2; // Boss staje się szybszy (ruch) self.attackSpeedMultiplier *= 0.8; // Boss atakuje nieco szybciej (mnożnik < 1) // Wizualne przejście fazy (np. zmiana koloru) tween(self, { tint: 0xFF3300 // Pomarańczowy/czerwony odcień }, { duration: 1000, easing: tween.easeInOut }); } } // Sprawdzenie, czy boss został pokonany if (self.health <= 0) { self.die(); // Wywołaj funkcję śmierci } // Aktualizacja paska zdrowia jest w game.update -> ui.updateBossHealth }; self.die = function () { if (self.dead || gameState.currentState !== "game") { // Boss umiera tylko w stanie gry return; } self.dead = true; // Dźwięk zwycięstwa tylko dla STANDARDOWEGO trybu (jeśli nie New Boss+) if (!isNewBossPlusMode) { LK.getSound('victory').play(); // Dźwięk zwycięstwa w trybie standardowym } // 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 = []; // Animacja śmierci bossa (zanikanie i powiększanie) tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Sprawdź, czy boss nadal istnieje przed zniszczeniem if (self && self.destroy && !self.destroyed) { self.destroy(); // Zniszcz obiekt bossa } // Zwiększ licznik pokonanych bossów TYLKO W STANDARDOWYM TRYBIE LUB MOŻE OSOBNO DLA NEW BOSS+? // Na razie zwiększamy zawsze, ale można to zmienić storage.bossesDefeated = (storage.bossesDefeated || 0) + 1; // Zwiększ licznik pokonanych bossów // !!! Po pokonaniu bossa ZAWSZE przechodzimy do Grill Screena (niezależnie od trybu) !!! gameState.showGrillScreen(); // <--- Zmieniono z gameOver na showGrillScreen } }); // Ataki zostaną wyczyszczone na początku die() lub w gameState.showGrillScreen clean up }; // Initialize boss attacks array and properties self.attacks = []; self.maxHealth = 200; self.health = self.maxHealth; self.phase = 1; self.speed = 5; self.attackSpeedMultiplier = 1; self.dead = false; self.repositioning = false; self.attackCooldown = 90; // Method to start a random attack pattern self.startAttackPattern = function () { // Check if boss is dead or game state changed if (self.dead || gameState.currentState !== "game") { return; } // Choose a random attack pattern var attackChoice = Math.floor(Math.random() * 3); // Play attack animation first self.playBossAttackAnim(); // Execute attack based on choice switch (attackChoice) { case 0: self.chargeAttack(); break; case 1: self.circleAttack(); break; case 2: self.lineAttack(); break; default: self.chargeAttack(); } // Set cooldown for next attack self.attackCooldown = Math.floor(120 * self.attackSpeedMultiplier); }; self.update = function () { // Aktualizuj tylko w stanie gry if (gameState.currentState !== "game") { // Jeśli zmieniono stan gry i boss nie umiera, wyczyść pozostałe ataki if (!self.dead && self.attacks) { self.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); self.attacks = []; // Wyczyść tablicę ataków } return; } // Nie aktualizuj jeśli boss jest martwy i animacja śmierci działa (obiekt może być jeszcze widoczny) if (self.dead) { return; // Pozwól animacji śmierci działać } // --- START NEW MOVEMENT LOGIC --- // Poruszaj się w kierunku gracza tylko jeśli nie jesteś martwy, gra jest w stanie 'game', // nie jesteś w trakcie repozycji po szarży, cooldown ataku jest wystarczająco długi (nie przygotowujesz się do ataku), // i gracz istnieje i nie jest martwy. // Zakładamy, że 'chargeAttack' sam zarządza ruchem podczas szarży (przez tween). 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; // Użyj prędkości bossa // Poruszaj się tylko jeśli gracz jest dalej niż pewna odległość (np. 150 jednostek) // Zapobiega to "trzęsieniu się" bossa, gdy jest blisko gracza. if (distance > 150) { var moveX = dx / distance * moveSpeed; var moveY = dy / distance * moveSpeed; var nextX = self.x + moveX; var nextY = self.y + moveY; // Ograniczenia areny (ściany na 100px marginesie, mapa 2048x2732) // Uwzględnij rozmiar bossa (zakładając anchor 0.5, 0.5) var halfWidth = self.width * self.scaleX / 2; // Uwzględnij skalowanie 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)); } } // --- END NEW MOVEMENT LOGIC --- // --- START BOSS ATTACK HANDLING (Kolizje i Czas Życia) --- // Aktualizuj istniejące ataki bossa i sprawdzaj kolizje z graczem // Iteruj wstecz, aby bezpiecznie usuwać elementy z tablicy for (var i = self.attacks.length - 1; i >= 0; i--) { var attack = self.attacks[i]; // Zmniejsz czas życia ataku (w klatkach) if (attack.lifeTime > 0) { attack.lifeTime--; } // Sprawdź kolizję z graczem (jeśli gracz istnieje, żyje, nie jest nietykalny, a atak wizualnie istnieje) 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); // Użyj promieni do sprawdzenia kolizji var playerRadius_p = player.width / 2; // Załóżmy, że gracz ma właściwość width var attackRadius = attack.radius; // Promień ataku z obiektu 'attack' if (distance_p < playerRadius_p + attackRadius) { player.takeDamage(1); // Gracz otrzymuje obrażenia // Opcjonalnie: zniszcz wizualizację ataku natychmiast po trafieniu if (attack.visual && attack.visual.destroy) { attack.visual.destroy(); } self.attacks.splice(i, 1); // Usuń trafiony atak z listy // Można dodać 'break;', jeśli gracz ma otrzymać obrażenia tylko od jednego ataku na klatkę // break; } } // Usuń atak, jeśli skończył mu się czas życia LUB jego wizualizacja została zniszczona if (attack.lifeTime <= 0 || attack.visual && attack.visual.destroyed) { // Upewnij się, że wizualizacja jest zniszczona, jeśli jeszcze istnieje if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } self.attacks.splice(i, 1); // Usuń atak z tablicy } } // --- END BOSS ATTACK HANDLING --- // Zmniejszaj cooldown ataku w każdej klatce if (self.attackCooldown > 0) { self.attackCooldown--; } // Sprawdź, czy nadszedł czas na rozpoczęcie nowego wzorca ataku // Warunki: cooldown <= 0, boss żyje, stan gry to "game", boss nie jest w trakcie repozycji if (self.attackCooldown <= 0 && !self.repositioning) { // Wywołanie startAttackPattern sprawdzi resztę warunków (dead, state) wewnątrz siebie self.startAttackPattern(); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); // Zamiast var playerGraphics = ... self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Player properties self.health = 5; // Default health (will be overridden in startGame) self.speed = 8; // Movement speed (NIE UŻYWANE OBECNIE W UPDATE?) self.rolling = false; // Flag for roll state self.rollDirection = { x: 0, y: 0 }; // Direction of roll self.rollSpeed = 20; // Speed during roll self.rollDuration = 300; // Roll duration in ms self.rollCooldown = 0; // Cooldown between rolls (frames) self.invulnerable = false; // Invulnerability flag self.invulnerabilityFrames = 0; // Frames of invulnerability left self.dead = false; // Dead state self.rollTimeoutId = null; // Store timeout ID for roll duration self.invulnerabilityTimeoutId = null; // Store timeout ID for invulnerability (NIE UŻYWANE?) self.rollAnimationInterval = null; // Store interval ID for roll animation self.hasRolledThroughBossThisRoll = false; // Track if boss was hit during this roll // Clear all roll-related timeouts and intervals self.clearRollTimeouts = function () { if (self.rollTimeoutId) { LK.clearTimeout(self.rollTimeoutId); self.rollTimeoutId = null; } if (self.rollAnimationInterval) { LK.clearInterval(self.rollAnimationInterval); self.rollAnimationInterval = null; } }; // Roll mechanic self.roll = function (direction) { if (!self.rolling && self.rollCooldown <= 0 && !self.dead) { self.rolling = true; self.rollDirection = direction; self.rollCooldown = 45; self.invulnerable = true; self.invulnerabilityFrames = 30; self.hasRolledThroughBossThisRoll = false; // Ukryj normalną grafikę gracza if (self.playerGraphics && !self.playerGraphics.destroyed) { self.playerGraphics.visible = false; } // --- Roll Animation (turlanie) --- var rollFrames = ['roll', 'roll0', 'roll1', 'roll2']; var currentFrame = 0; var rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); var rollAnimationInterval = LK.setInterval(function () { if (rollAnimationSprite && rollAnimationSprite.destroy) { rollAnimationSprite.destroy(); } currentFrame = (currentFrame + 1) % rollFrames.length; rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); }, 70); // co 70ms zmienia klatkę podczas turlania if (self.rollTimeoutId) { LK.clearTimeout(self.rollTimeoutId); } self.rollTimeoutId = LK.setTimeout(function () { self.rolling = false; // --- Po rollu zakończ turlanie --- if (rollAnimationInterval) { LK.clearInterval(rollAnimationInterval); } if (rollAnimationSprite && rollAnimationSprite.destroy) { rollAnimationSprite.destroy(); } // --- Stand Up Animation (wstawanie + machnięcie mieczem) --- var standUpFrames = ['roll3', 'roll4']; var standUpFrame = 0; var standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); var standUpInterval = LK.setInterval(function () { if (standUpSprite && standUpSprite.destroy) { standUpSprite.destroy(); } standUpFrame++; if (standUpFrame < standUpFrames.length) { standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); } else { // Koniec animacji stand-up LK.clearInterval(standUpInterval); if (standUpSprite && standUpSprite.destroy) { standUpSprite.destroy(); } // Przywróć normalną grafikę gracza if (self.playerGraphics && !self.playerGraphics.destroyed) { self.playerGraphics.visible = true; } } }, 100); // 100ms między klatkami wstawania self.rollTimeoutId = null; }, self.rollDuration); } }; // Take damage method self.takeDamage = function (amount) { // Check if player can take damage (not invulnerable and not dead) if (!self.invulnerable && !self.dead) { self.health -= amount; // Flash effect when hit LK.effects.flashObject(self, 0xFF0000, 200); // Check if player is dead if (self.health <= 0) { self.health = 0; // Ensure health doesn't go below 0 visually self.die(); return; // Zakończ, jeśli gracz umarł } // Set brief invulnerability after hit (if not rolling) // Nietykalność po trafieniu jest teraz KRÓTSZA niż turlanie if (!self.rolling) { self.invulnerable = true; self.invulnerabilityFrames = 30; // Krótsza nietykalność po trafieniu (np. 0.5s) } } }; // Die method self.die = function () { if (self.dead) { // Zapobiegaj wielokrotnemu wywołaniu return; } self.dead = true; // Increment death counter storage.totalDeaths = (storage.totalDeaths || 0) + 1; // Increment max hearts after death (up to a maximum of 10) if (!storage.maxHearts || storage.maxHearts < 5) { // Upewnij się, że startujemy od min 5 storage.maxHearts = 5; } if (storage.maxHearts < 10) { storage.maxHearts = storage.maxHearts + 1; } self.clearRollTimeouts(); // Anuluj ewentualne trwające turlanie // Death animation tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Zniszcz obiekt gracza PO zakończeniu animacji if (self && self.destroy && !self.destroyed) { self.destroy(); } // Set death reason flag to true (player died) gameOverReasonIsDeath = true; // Show game over screen - gameState zarządza przejściem gameState.gameOver(true); // Przekaż true (śmierć gracza) } }); }; // Update method called every frame self.update = function () { // Aktualizuj tylko w stanie gry if (gameState.currentState !== "game") { // Wyczyść timery, jeśli stan gry się zmienił if (self.rolling) { self.rolling = false; } // Przerwij turlanie self.clearRollTimeouts(); return; } // Nie aktualizuj jeśli gracz jest martwy (ale pozwól animacji śmierci działać) if (self.dead) { return; } // Handle roll cooldown if (self.rollCooldown > 0) { self.rollCooldown--; } // Handle invulnerability frames (zarówno z turlania, jak i z otrzymania obrażeń) if (self.invulnerable && self.invulnerabilityFrames > 0) { self.invulnerabilityFrames--; // Blinking effect during invulnerability // Miga szybciej self.alpha = self.invulnerabilityFrames % 4 > 1 ? 0.3 : 1; if (self.invulnerabilityFrames <= 0) { self.invulnerable = false; self.alpha = 1; // Restore full opacity when invulnerability ends } } else if (self.invulnerable && self.invulnerabilityFrames <= 0) { // Upewnij się, że stan jest spójny jeśli klatki się skończyły self.invulnerable = false; self.alpha = 1; } // Handle movement during roll if (self.rolling) { var rollDx = self.rollDirection.x * self.rollSpeed; var rollDy = self.rollDirection.y * self.rollSpeed; var nextX = self.x + rollDx; var nextY = self.y + rollDy; // Ograniczenia areny (ściany na 100px marginesie, mapa 2048x2732) // Uwzględnij rozmiar gracza (zakładając anchor 0.5, 0.5) var halfWidth = self.width / 2; var halfHeight = self.height / 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)); // Sprawdź kolizję turlania z bossem // Sprawdź tylko jeśli boss istnieje, nie jest martwy i JESZCZE nie zadałeś obrażeń tym turlaniem if (boss && !boss.dead && !self.hasRolledThroughBossThisRoll) { // Uproszczone sprawdzenie kolizji na podstawie odległości między środkami var dx_b = self.x - boss.x; var dy_b = self.y - boss.y; var distance_b = Math.sqrt(dx_b * dx_b + dy_b * dy_b); // Uproszczony promień kolizji (połówka szerokości/wysokości) var playerCollisionRadius = self.width / 2; // Zakładamy, że kolizja gracza jest okrągła var bossCollisionRadius = boss.width / 2; // Zakładamy, że kolizja bossa jest okrągła // Jeśli odległość między środkami jest mniejsza niż suma promieni kolizji if (distance_b < playerCollisionRadius + bossCollisionRadius) { // Kolizja wykryta podczas turlania boss.takeDamage(10); // ZADAJ 10 OBRAŻEŃ BOSSOWI self.hasRolledThroughBossThisRoll = true; // Oznacz, że zadałeś obrażenia w tym turlaniu // Opcjonalny efekt wizualny/dźwiękowy trafienia bossa turlaniem LK.effects.flashObject(boss, 0x00FF00, 200); // Mignij bossa na zielono // LK.getSound('playerHitBossSound').play(); // Wymagałoby dodania nowego assetu dźwiękowego } } } else { // --- Podstawowy ruch gracza (jeśli nie turla się) --- // Ta część była nieobecna, dodajmy prosty ruch oparty na wejściu (jeśli LK go dostarcza) // Zakładając, że mamy dostęp do stanu klawiszy/joysticka np. przez LK.controls // To jest PRZYKŁAD - musisz dostosować do API LK /* var moveX = 0; var moveY = 0; if (LK.controls.left) moveX -= 1; if (LK.controls.right) moveX += 1; if (LK.controls.up) moveY -= 1; if (LK.controls.down) moveY += 1; if (moveX !== 0 || moveY !== 0) { // Normalizuj wektor ruchu, jeśli poruszasz się po przekątnej var moveMagnitude = Math.sqrt(moveX * moveX + moveY * moveY); var normalizedX = moveX / moveMagnitude; var normalizedY = moveY / moveMagnitude; var nextX = self.x + normalizedX * self.speed; // Użyj self.speed var nextY = self.y + normalizedY * self.speed; // Ograniczenia areny (jak w turlaniu) var halfWidth = self.width / 2; var halfHeight = self.height / 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)); } */ // Jeśli nie masz łatwego dostępu do stanu klawiszy, gracz będzie się ruszał tylko podczas turlania. } }; return self; }); // Shape class implementation for creating simple shapes var Shape = Container.expand(function (options) { var self = Container.call(this); options = options || {}; var width = options.width || 100; var height = options.height || 100; var color = options.color || 0xFFFFFF; var shape = options.shape || 'box'; // Create the shape as an asset var asset = self.attachAsset(shape, { anchorX: 0.5, anchorY: 0.5, width: width, height: height, tint: color }); // Set width and height for easier access self.width = width; self.height = height; // Add anchor property to Shape for positioning self.anchor = { set: function set(x, y) { // This mimics the behavior of the anchor.set method self.anchorX = x; self.anchorY = y; } }; return self; }); var SpriteAnimation = Container.expand(function (options) { var self = Container.call(this); // Initialize with default values options = options || {}; self.frames = options.frames || []; self.frameDuration = options.frameDuration || 100; // Default 100ms per frame self.loop = options.loop !== undefined ? options.loop : true; self.currentFrame = 0; self.frameTimer = 0; self.playing = true; // Set initial position and anchor if provided if (options.x !== undefined) { self.x = options.x; } if (options.y !== undefined) { self.y = options.y; } // Handle anchor options if (options.anchorX !== undefined || options.anchorY !== undefined) { var anchorX = options.anchorX !== undefined ? options.anchorX : 0; var anchorY = options.anchorY !== undefined ? options.anchorY : 0; // Apply anchor to all frames self.frames.forEach(function (frame) { frame.anchor.set(anchorX, anchorY); }); } // Add the first frame to display initially if (self.frames.length > 0) { self.addChild(self.frames[0]); } // Animation update method self.update = function () { if (!self.playing || self.frames.length === 0) { return; } self.frameTimer++; // Check if it's time to advance to the next frame if (self.frameTimer >= self.frameDuration / (1000 / 60)) { // Convert ms to frames at 60fps self.frameTimer = 0; // Remove current frame from display self.removeChildren(); // Advance to next frame self.currentFrame++; // Loop if needed if (self.currentFrame >= self.frames.length) { if (self.loop) { self.currentFrame = 0; } else { self.currentFrame = self.frames.length - 1; self.playing = false; } } // Add the new current frame self.addChild(self.frames[self.currentFrame]); } }; // Method to stop animation self.stop = function () { self.playing = false; }; // Method to start/resume animation self.play = function () { self.playing = true; }; // Method to set a specific frame self.gotoFrame = function (frameIndex) { if (frameIndex >= 0 && frameIndex < self.frames.length) { self.removeChildren(); self.currentFrame = frameIndex; self.addChild(self.frames[self.currentFrame]); } }; return self; }); var UI = Container.expand(function () { var self = Container.call(this); // Create heart containers (wizualizacja zdrowia) self.hearts = []; self.heartContainer = new Container(); // Kontener na ikony serc self.addChild(self.heartContainer); // Title and messages (teksty na ekranach) 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 (licznik śmierci) self.deathsText = new Text2("Deaths: 0", { size: 40, fill: 0xFFFFFF }); self.deathsText.anchor.set(1, 0); // Wyrównanie do prawego górnego rogu self.deathsText.x = 2048 - 50; // Pozycja X od prawej krawędzi self.deathsText.y = 50; // Pozycja Y od góry self.addChild(self.deathsText); // Tutorial text (teksty tutoriali) self.tutorialText = new Text2("", { size: 50, fill: 0xFFFFFF }); self.tutorialText.anchor.set(0.5, 0); // Wyrównanie do środka na górze self.tutorialText.x = 2048 / 2; // Pozycja X na środku self.tutorialText.y = 200; // Pozycja Y (może być różna w zależności od stanu) self.addChild(self.tutorialText); // --- Timer Text --- self.timerText = new Text2("2:00", { // Początkowe wyświetlanie czasu (2 minuty) size: 60, // Rozmiar czcionki fill: 0xFFFFFF // Biały kolor }); self.timerText.anchor.set(0, 0); // Punkt odniesienia w lewym górnym rogu self.timerText.x = 50; // Pozycja X od lewej krawędzi self.timerText.y = 50; // Pozycja Y od górnej krawędzi self.timerText.alpha = 0; // Domyślnie ukryty self.addChild(self.timerText); // --- Boss Health Bar (wizualizacja zdrowia bossa) --- self.bossHealthBarContainer = new Container(); // Kontener na pasek zdrowia bossa self.bossHealthBarContainer.x = 2048 / 2; // Wyśrodkuj poziomo self.bossHealthBarContainer.y = 150; // Pozycja Y (poniżej timera) self.bossHealthBarContainer.alpha = 0; // Domyślnie ukryty self.addChild(self.bossHealthBarContainer); var barWidth = 800; // Szerokość paska zdrowia (musi być taka sama jak szerokość assetu bossHpbar) var barHeight = 30; // Wysokość paska zdrowia (musi być taka sama jak wysokość assetu bossHpbar) // Tło paska zdrowia bossa (szare) - nadal używamy Shape dla tła self.bossHealthBarBg = new Shape({ width: barWidth, height: barHeight, color: 0x555555, // Szary kolor tła shape: 'box' }); self.bossHealthBarBg.anchor.set(0.5, 0.5); // Ustaw punkt odniesienia na środek self.bossHealthBarContainer.addChild(self.bossHealthBarBg); // Właściwy pasek zdrowia bossa (czerwony) - UŻYWAMY TERAZ NOWEGO ASSETU bossHpbar self.bossHealthBar = self.attachAsset('bossHpbar', { // Użyj assetu 'bossHpbar' anchorX: 0, // Ustaw punkt odniesienia na lewą krawędź (0), środek pionowo (0.5) anchorY: 0.5, x: -barWidth / 2 // Przesuń w lewo o połowę szerokości tła, żeby lewa krawędź paska zdrowia była na środku kontenera (czyli na środku ekranu) // Szerokość i wysokość są brane z assetu, nie ustawiamy ich tutaj jawnie (chyba że chcemy skalować) }); self.bossHealthBarContainer.addChild(self.bossHealthBar); // Aktualizuje wizualizację serc na UI self.updateHearts = function (current, max) { // Sprawdź poprawność danych wejściowych current = Math.max(0, current || 0); max = Math.max(1, max || 1); // Max musi być co najmniej 1 // Usuń istniejące serca tylko jeśli liczba się zmieniła lub nie ma serc if (self.hearts.length !== max) { while (self.hearts.length > 0) { var oldHeart = self.hearts.pop(); if (oldHeart && oldHeart.destroy && !oldHeart.destroyed) { oldHeart.destroy(); } } self.heartContainer.removeChildren(); // Usuń wizualne obiekty serc // Stwórz nowe ikony serc for (var i = 0; i < max; i++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, x: i * 50, // Pozycja w poziomie (rozstawione co 50 jednostek) y: 0, tint: i < current ? 0xFF0000 : 0x555555 // Czerwone jeśli zdrowie jest >=, szare jeśli < }); self.hearts.push(heart); self.heartContainer.addChild(heart); } // Wyśrodkuj kontener serc w poziomie i ustaw pozycję pionową self.heartContainer.x = (2048 - max * 50) / 2 + 25; // Dodano +25 aby wycentrować lepiej (bo anchorX = 0.5) self.heartContainer.y = 100; // Pozycja pionowa } else { // Zaktualizuj tylko kolory istniejących serc for (var j = 0; j < self.hearts.length; j++) { if (self.hearts[j]) { self.hearts[j].tint = j < current ? 0xFF0000 : 0x555555; } } } }; // Aktualizuje wizualizację paska zdrowia bossa self.updateBossHealth = function (current, max) { // Sprawdź poprawność danych current = Math.max(0, current || 0); max = Math.max(1, max || 1); // Max musi być co najmniej 1 // Oblicz szerokość paska zdrowia na podstawie aktualnego i maksymalnego zdrowia var barWidth = 800; // Całkowita szerokość paska (musi być taka sama jak szerokość assetu bossHpbar) var currentWidth = current / max * barWidth; // Upewnij się, że pasek zdrowia (obiekt sprite) istnieje przed aktualizacją if (self.bossHealthBar) { // Zmień szerokość sprite'a, aby odzwierciedlić aktualne zdrowie self.bossHealthBar.width = currentWidth; // Ustaw widoczność kontenera paska zdrowia bossa // Pokaż jeśli (stan gry lub game over) ORAZ (boss istnieje i żyje LUB jesteśmy w stanie game over z powodu czasu w Boss+) var shouldBeVisible = (gameState.currentState === "game" || gameState.currentState === "gameOver") && (boss && !boss.dead && current > 0 || gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode); self.bossHealthBarContainer.alpha = shouldBeVisible ? 1 : 0; } // Upewnij się, że pasek zdrowia tła też jest widoczny/ukryty tak samo jak kontener if (self.bossHealthBarBg) { self.bossHealthBarBg.alpha = self.bossHealthBarContainer.alpha; } }; // Wyświetla komunikat na środku ekranu self.showMessage = function (message, duration) { self.messageText.setText(message); self.messageText.alpha = 1; // Ustaw pełną przezroczystość // Wyczyść istniejący timer zanikania, jeśli istnieje if (self.messageTimeout) { LK.clearTimeout(self.messageTimeout); self.messageTimeout = null; } // Zaplanuj zanikanie po czasie, jeśli duration > 0 if (duration && duration > 0) { self.messageTimeout = LK.setTimeout(function () { tween(self.messageText, { alpha: 0 // Zaniknij do przezroczystości 0 }, { duration: 500, // Czas trwania zanikania onFinish: function onFinish() { self.messageTimeout = null; } // Reset ID }); }, duration); } else { // If duration is 0 or not provided, message stays visible self.messageText.alpha = 1; } }; // Wyświetla tekst tutorialu self.showTutorial = function (text) { self.tutorialText.setText(text); self.tutorialText.alpha = 1; // Ustaw pełną przezroczystość }; // Ukrywa tekst tutorialu (zanikając) self.hideTutorial = function () { tween(self.tutorialText, { alpha: 0 // Zaniknij }, { duration: 500 // Czas trwania }); }; // Aktualizuje licznik śmierci self.updateDeathsCounter = function () { // Upewnij się, że storage.totalDeaths jest liczbą var deaths = storage.totalDeaths || 0; self.deathsText.setText("Deaths: " + deaths); }; // Aktualizuje wyświetlanie czasu timera self.updateTimerDisplay = function (seconds) { // Upewnij się, że sekundy są liczbą >= 0 seconds = Math.max(0, seconds || 0); var minutes = Math.floor(seconds / 60); var remainingSeconds = seconds % 60; // Formatuj sekundy z wiodącym zerem jeśli < 10 var formattedSeconds = remainingSeconds < 10 ? '0' + remainingSeconds : remainingSeconds; self.timerText.setText(minutes + ':' + formattedSeconds); }; // Pozycjonuje elementy UI w zależności od stanu gry // Ta funkcja jest wywoływana przez metody w gameState self.positionElements = function (state) { // Pozycje stałe (niezależne od stanu) self.deathsText.x = 2048 - 50; self.deathsText.y = 50; self.timerText.x = 50; self.timerText.y = 50; self.heartContainer.y = 100; // Pozycja pionowa serc self.bossHealthBarContainer.x = 2048 / 2; self.bossHealthBarContainer.y = 150; // Pasek HP bossa poniżej timera/serc // Resetuj widoczność przed ustawieniem dla danego stanu self.titleText.alpha = 0; self.messageText.alpha = 0; self.tutorialText.alpha = 0; self.timerText.alpha = 0; self.heartContainer.alpha = 0; self.bossHealthBarContainer.alpha = 0; // Zostanie włączony przez updateBossHealth jeśli trzeba self.deathsText.alpha = 1; // Licznik śmierci zawsze widoczny switch (state) { case "title": // Pozycje dla ekranu tytułowego self.titleText.x = 2048 / 2; self.titleText.y = 800; self.titleText.alpha = 1; // Pokaż tytuł self.messageText.x = 2048 / 2; self.messageText.y = 1000; // "Tap to Start" (ustawiane przez showMessage) // self.messageText.alpha = 1; // Ustawiane przez showMessage self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; // "Swipe to Roll..." (ustawiane przez showTutorial) // self.tutorialText.alpha = 1; // Ustawiane przez showTutorial break; case "game": // Pozycje dla stanu gry (walka z bossem) // self.titleText.alpha = 0; // Ustawione domyślnie self.messageText.x = 2048 / 2; self.messageText.y = 1500; // Komunikaty w trakcie gry (np. przyspieszenie bossa) // self.messageText.alpha = 0; // Ustawiane przez showMessage self.tutorialText.x = 2048 / 2; self.tutorialText.y = 200; // Tutorial w trakcie gry ("Swipe to roll away!") // self.tutorialText.alpha = 1; // Ustawiane przez showTutorial/hideTutorial // Pokaż serca i timer podczas gry self.heartContainer.alpha = 1; self.timerText.alpha = 1; // Pasek zdrowia bossa jest zarządzany przez updateBossHealth break; case "grillMenu": // Nowy stan dla Grill Screena // self.titleText.alpha = 0; // Ustawione domyślnie self.messageText.x = 2048 / 2; self.messageText.y = 500; // Pozycja dla komunikatów "Rest in peace..." // self.messageText.alpha = 0; // Ustawiane przez showMessage // self.tutorialText.alpha = 0; // Ustawione domyślnie break; case "gameOver": // Pozycje dla ekranu Game Over self.titleText.x = 2048 / 2; self.titleText.y = 800; // Tytuł "YOU DIED" lub komunikat zwycięstwa Boss+ // self.titleText.alpha = 1; // Ustawiane w gameState.gameOver self.messageText.x = 2048 / 2; self.messageText.y = 1000; // Pusty lub inny komunikat // self.messageText.alpha = 0; // Ustawiane w gameState.gameOver przez showMessage self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; // Pusty // self.tutorialText.alpha = 0; // Ustawiane w gameState.gameOver przez showTutorial // Widoczność paska HP bossa zarządzana przez updateBossHealth break; case "intro": // Pozycje dla ekranów intro/tutoriali case "fakeTutorial": case "realTutorial": // Większość elementów ukryta (ustawione domyślnie wyżej) break; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 // Ciemne tło domyślne }); /**** * Game Code ****/ // Komentarze o assetach pominięte dla zwięzłości // Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia) var currentSceneElements = new Container(); // Zmienne gry var player; var boss; var ui; var walls = []; // Ściany areny bossa // Zmienna do przechowywania aktywnego tła sceny var currentBackground = null; // Flaga do śledzenia, czy jesteśmy w trybie New Boss+ var isNewBossPlusMode = false; // Flaga do śledzenia przyczyny Game Over (true = gracz zginął, false = czas minął) var gameOverReasonIsDeath = false; // Funkcja do czyszczenia elementów z kontenera currentSceneElements (nie usuwa UI ani tła) function clearScene() { while (currentSceneElements.children.length > 0) { var child = currentSceneElements.children[0]; if (child && child.destroy) { child.destroy(); // Użyj destroy jeśli dostępne } else if (child && child.parent) { child.parent.removeChild(child); // Fallback } else { // Jeśli obiekt nie ma ani destroy ani parent, usuń go z tablicy (choć to nie powinno się zdarzyć) currentSceneElements.children.shift(); } } // Usuń też dzieci bezpośrednio z 'game', które mogły zostać dodane poza 'currentSceneElements' w scenach, // ale UWAŻAJ, aby nie usunąć stałych elementów jak UI, walls, currentSceneElements sam w sobie. // Lepsze podejście: zawsze dodawaj elementy specyficzne dla sceny do currentSceneElements. } // Obiekt zarządzający stanami gry var gameState = { currentState: "title", // Początkowy stan gry gameDuration: 120, // Domyślny czas walki z bossem w sekundach (2 minuty) remainingTime: 0, // Pozostały czas gameTimerInterval: null, // ID interwału timera fakeTutorialTimerId: null, // ID timera przechodzącego do prawdziwego tutorialu bossSpeedIncreased: false, // Flaga przyspieszenia bossa touchStart: { x: 0, y: 0 }, // Początek gestu touchEnd: { x: 0, y: 0 }, // Koniec gestu // Inicjalizacja gry (wywoływana raz na początku) init: function init() { // Resetuj stan przy każdym uruchomieniu storage.totalDeaths = 0; storage.maxHearts = 5; // Zaczynaj zawsze z 5 sercami isNewBossPlusMode = false; gameOverReasonIsDeath = false; game.setBackgroundColor(0x111111); // Ustaw domyślny kolor tła this.createWalls(); // Stwórz ściany areny ui = game.addChild(new UI()); // Stwórz UI // Zainicjalizuj UI dla ekranu tytułowego ui.updateDeathsCounter(); ui.updateHearts(storage.maxHearts, storage.maxHearts); // Pokaż początkowe serca ui.positionElements("title"); // Pozycjonuj elementy UI dla tytułu game.addChild(currentSceneElements); // Dodaj kontener na elementy scen // Rozpocznij muzykę w tle LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); this.showTitleScreen(); // Rozpocznij od ekranu tytułowego }, // Tworzy/odświeża ściany areny bossa createWalls: function createWalls() { walls.forEach(function (wall) { if (wall && wall.destroy) { wall.destroy(); } }); walls = []; // Użyj getAsset zamiast new Shape dla ścian, jeśli 'wall' i 'floor' to assety var leftWall = game.addChild(LK.getAsset('wall', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); walls.push(leftWall); var rightWall = game.addChild(LK.getAsset('wall', { anchorX: 1, anchorY: 0, x: 2048, y: 0 })); // AnchorX=1 dla prawej ściany rightWall.x = 2048; // Poprawka pozycji prawej ściany walls.push(rightWall); var topWall = game.addChild(LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); walls.push(topWall); var bottomWall = game.addChild(LK.getAsset('floor', { anchorX: 0, anchorY: 1, x: 0, y: 2732 })); // AnchorY=1 dla dolnej ściany bottomWall.y = 2732; // Poprawka pozycji dolnej ściany walls.push(bottomWall); // Ustaw ściany na spodzie (niski zIndex) walls.forEach(function (wall) { game.setChildIndex(wall, 0); }); }, // ---------- Metody przejścia między stanami ---------- showTitleScreen: function showTitleScreen() { // Zatrzymaj timery poprzedniego stanu if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; clearScene(); // Wyczyść elementy poprzedniej sceny (Grill, GameOver, itp.) // Usuń stare tło, jeśli istnieje if (currentBackground) { tween.stop(currentBackground); // Zatrzymaj animacje tła currentBackground.destroy(); currentBackground = null; } this.currentState = "title"; game.setBackgroundColor(0x1a1a1a); // Ciemne tło bazowe dla tytułu // Dodaj tło ekranu tytułowego currentBackground = LK.getAsset('titleBg', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChildAt(currentBackground, 0); // Dodaj na spód // Ukryj gracza/bossa jeśli istnieją if (player && player.destroy) { player.destroy(); } player = null; if (boss && boss.destroy) { boss.destroy(); } boss = null; // Pokaż ściany (jako część tła menu) walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); // Ustaw UI dla tytułu ui.positionElements("title"); ui.titleText.setText("Welcome Unchosen"); ui.showMessage("Tap to Start", 0); ui.showTutorial("Swipe to Roll - Death is Progress"); // Resetuj stan gestu this.touchStart = { x: 0, y: 0 }; this.touchEnd = { x: 0, y: 0 }; // Przycisk testowy do Grill Menu (jeśli nadal potrzebny) var grillMenuTestButton = new Container(); grillMenuTestButton.interactive = true; grillMenuTestButton.x = 2048 / 2; grillMenuTestButton.y = 1600; currentSceneElements.addChild(grillMenuTestButton); var grillButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); grillMenuTestButton.addChild(grillButtonBg); var grillButtonText = new Text2('Go to Grill Menu', { size: 50, fill: 0xFFFFFF }); grillButtonText.anchor.set(0.5, 0.5); grillMenuTestButton.addChild(grillButtonText); grillMenuTestButton.down = function () { gameState.showGrillScreen(); }; // --- Tap anywhere on title screen to start the intro --- game.on('down', function () { if (gameState.currentState === 'title') { gameState.showIntro(); } }); }, showIntro: function showIntro() { if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; // Wyczyść timer clearScene(); // Wyczyść elementy tytułu if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); currentBackground = null; } // Usuń stare tło this.currentState = "intro"; game.setBackgroundColor(0x111111); // Dodaj tło intro i animację zoom currentBackground = LK.getAsset('introBg', { anchorX: 0.5, anchorY: 0.4, x: 1000, y: 1000, scaleX: 1, scaleY: 1 }); game.addChildAt(currentBackground, 0); tween(currentBackground, { scaleX: 1.4, scaleY: 1.4 }, { duration: 22000, easing: tween.linear, repeat: Infinity, yoyo: true }); // Ukryj ściany na czas intro if (player && player.destroy) { player.destroy(); } player = null; // Upewnij się, że nie ma gracza/bossa if (boss && boss.destroy) { boss.destroy(); } boss = null; walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ukryj ściany ui.positionElements("intro"); // Ustaw UI dla intro (głównie ukrywa elementy) // --- Teksty intro z timerami --- // Funkcja do wyświetlania tekstu z fade-in, fade-out i kontynuacją function showIntroText(text, delay, onComplete) { var introText = new Text2(text, { size: 90, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); introText.anchor.set(0.5, 0.5); introText.x = 2048 / 2; introText.y = 2232 / 2 - 400; introText.alpha = 0; currentSceneElements.addChild(introText); LK.setTimeout(function () { tween(introText, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut }); LK.setTimeout(function () { tween(introText, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut }); LK.setTimeout(function () { try { if (introText && introText.destroy) { introText.destroy(); } } catch (e) {} if (onComplete) { onComplete(); } }, 1000); // czas na fade-out }, 3000); // czas wyświetlania }, delay); } // --- Startujemy sekwencję intro --- showIntroText('From the creators of FromSoftware...', 2000, function () { showIntroText('...I have no idea. I don’t know them.', 0, function () { showIntroText('Silas GameStudio...', 0, function () { showIntroText('Still looking for funding.', 0, function () { showIntroText('Oh... and I don’t even exist.', 0, function () { // <-- NOWY TEKST // Po ostatnim tekście przechodzimy do howToScene LK.setTimeout(function () { if (typeof gameState.showFakeTutorial === 'function') { gameState.showFakeTutorial(); } else { console.error("Error: gameState.showFakeTutorial is not defined"); gameState.showTitleScreen(); // Fallback to title screen } }, 1000); }); }); }); }); }); }, showFakeTutorial: function showFakeTutorial() { clearScene(); // Wyczyść tekst intro if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; // Wyczyść stary timer this.currentState = "fakeTutorial"; // Zachowaj tło intro? Jeśli tak, usuń game.setBackgroundColor i nie niszcz currentBackground // game.setBackgroundColor(0x000000); // Czarne tło - usunięte, aby zachować tło intro ui.positionElements("fakeTutorial"); // Ustaw UI // Tekst fałszywego tutorialu var instructionText = new Text2('Press LPM to block.. or don’t press.', { size: 100, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800, dropShadow: true, dropShadowColor: 0x000000, // czarny cień dropShadowDistance: 3, // odległość cienia dropShadowBlur: 4, // lekkie rozmycie cienia dropShadowAngle: Math.PI / 4 // kąt cienia }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 2048 / 2; instructionText.y = 2732 / 2; currentSceneElements.addChild(instructionText); // Timer przechodzący do prawdziwego tutorialu this.fakeTutorialTimerId = LK.setTimeout(function () { try { if (instructionText && instructionText.destroy) { instructionText.destroy(); } } catch (e) {} gameState.fakeTutorialTimerId = null; // Zresetuj ID // *** POPRAWKA: Sprawdzenie przed wywołaniem *** if (gameState && typeof gameState.showRealTutorial === 'function') { gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu } else { console.error("Error: gameState.showRealTutorial is not a function in fakeTutorial timeout!"); gameState.showTitleScreen(); // Awaryjny powrót do tytułu } } /*.bind(this)*/, 6000); // 6 sekund }, // *** NOWA FUNKCJA: Prawdziwy Tutorial (oddzielona od startGame) *** showRealTutorial: function showRealTutorial() { if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; // Wyczyść timer fake tutorialu clearScene(); // Wyczyść elementy fake tutorialu/fake game over // Zatrzymaj animację tła intro i usuń je if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); currentBackground = null; } this.currentState = "realTutorial"; game.setBackgroundColor(0x111111); // Ciemne tło // Pokaż ściany areny walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); ui.positionElements("realTutorial"); // Ustaw UI (głównie ukrywa elementy gry) // Tekst prawdziwego tutorialu var tutorialTitle = new Text2('HOW TO PLAY', { size: 100, fill: 0xFFFFFF }); tutorialTitle.anchor.set(0.5, 0.5); tutorialTitle.x = 2048 / 2; tutorialTitle.y = 600; currentSceneElements.addChild(tutorialTitle); var tutorialDesc = new Text2('Swipe the screen to ROLL.\nRoll THROUGH the boss attacks to deal damage.\nSurvive!', { size: 60, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1600 }); tutorialDesc.anchor.set(0.5, 0.5); tutorialDesc.x = 2048 / 2; tutorialDesc.y = 1000; currentSceneElements.addChild(tutorialDesc); // Przycisk "Let's Roll!" do rozpoczęcia gry var startButton = new Container(); startButton.interactive = true; startButton.x = 2048 / 2; startButton.y = 1500; currentSceneElements.addChild(startButton); var startButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); startButton.addChild(startButtonBg); var startButtonText = new Text2("Let's Roll!", { size: 50, fill: 0xFFFFFF }); startButtonText.anchor.set(0.5, 0.5); startButton.addChild(startButtonText); startButton.down = function () { // clearScene(); // Niekonieczne, startGame to zrobi gameState.startGame(); // Rozpocznij grę }; }, // Obsługa inputu w stanie fakeTutorial (fałszywa śmierć) handleFakeTutorialInput: function handleFakeTutorialInput() { // Wyczyść timer, który miał prowadzić do prawdziwego tutorialu if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; // Zresetuj ID timera } clearScene(); // Wyczyść ekran fałszywego tutorialu // Ustaw stan tymczasowo (można by dodać dedykowany stan 'fakeGameOver') this.currentState = "fakeGameOver"; // Zmieniono z "gameOver" // Zatrzymaj animację tła intro, ale nie usuwaj go jeszcze if (currentBackground) { tween.stop(currentBackground); } ui.positionElements("gameOver"); // Użyj pozycjonowania gameOver dla tekstów "YOU DIED" // Wyświetl "YOU DIED" na czerwono var diedText = new Text2("YOU DIED", { size: 150, fill: 0xFF0000 }); // Czerwony kolor diedText.anchor.set(0.5, 0.5); diedText.x = 2048 / 2; diedText.y = 600; currentSceneElements.addChild(diedText); // Wyświetl wyjaśnienie var explanationText = new Text2("Did you check the title of the game?", { size: 70, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800, dropShadow: true, dropShadowColor: 0x000000, // cień czarny dropShadowDistance: 3, // odległość cienia dropShadowBlur: 4, // lekkie rozmycie cienia dropShadowAngle: Math.PI / 4 // kąt padania cienia (45 stopni) }); explanationText.anchor.set(0.5, 0.5); explanationText.x = 2048 / 2; explanationText.y = 800; currentSceneElements.addChild(explanationText); // Dodaj przycisk "How to play again" var howToPlayButtonContainer = new Container(); howToPlayButtonContainer.interactive = true; howToPlayButtonContainer.x = 2048 / 2; howToPlayButtonContainer.y = 1300; // Pozycja przycisku currentSceneElements.addChild(howToPlayButtonContainer); var buttonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); howToPlayButtonContainer.addChild(buttonBg); var buttonText = new Text2('How to play', { size: 50, fill: 0xFFFFFF }); // Zmieniono tekst i rozmiar buttonText.anchor.set(0.5, 0.5); howToPlayButtonContainer.addChild(buttonText); // Akcja przycisku: Przejdź do prawdziwego tutorialu howToPlayButtonContainer.down = function () { // *** POPRAWKA: Sprawdzenie przed wywołaniem *** if (gameState && typeof gameState.showRealTutorial === 'function') { gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu } else { console.error("Error: gameState.showRealTutorial is not a function in fake input handler!"); gameState.showTitleScreen(); // Awaryjny powrót do tytułu } }; }, // Przejście do stanu gry (walka z bossem) - wywoływane z Prawdziwego Tutorialu startGame: function startGame() { // Wyczyść timery z poprzednich stanów if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; clearScene(); // Wyczyść elementy tutorialu // Usuń tło tutorialu/intro jeśli istnieje if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); currentBackground = null; } this.currentState = "game"; // Ustaw stan na "game" game.setBackgroundColor(0x111111); // Stwórz gracza (jeśli nie istnieje) if (player && player.destroy) { player.destroy(); } // Zniszcz starego gracza player = game.addChild(new Player()); // Stwórz nowego gracza player.health = storage.maxHearts || 5; // Ustaw zdrowie gracza // Resetuj stan gracza player.rolling = false; player.invulnerable = false; player.invulnerabilityFrames = 0; player.rollCooldown = 0; if (player && typeof player.clearRollTimeouts === 'function') { player.clearRollTimeouts(); } player.hasRolledThroughBossThisRoll = false; player.x = 2048 / 2; player.y = 2732 / 2 + 400; // Pozycja startowa gracza player.alpha = 1; // Upewnij się, że jest widoczny // Stwórz bossa (jeśli nie istnieje) if (boss && boss.destroy) { boss.destroy(); } // Zniszcz starego bossa boss = game.addChild(new Boss()); // Stwórz nowego bossa boss.x = 2048 / 2; boss.y = 2732 / 2 - 400; // Pozycja startowa bossa boss.alpha = 1; // Upewnij się, że jest widoczny // Resetuj stan bossa boss.attackCooldown = 90; // Daj graczowi chwilę przed pierwszym atakiem (ok 1.5s) boss.attacks = []; boss.attackSpeedMultiplier = 1; boss.repositioning = false; boss.dead = false; // Upewnij się, że nie jest martwy boss.phase = 1; // Zacznij od fazy 1 boss.tint = 0xFFFFFF; // Resetuj kolor (na wypadek przejścia fazy w poprzedniej grze) boss.scale.set(1, 1); // Resetuj skalę (na wypadek animacji śmierci) // Ustaw parametry bossa i gry zależnie od trybu if (isNewBossPlusMode) { boss.maxHealth = 2000; boss.health = boss.maxHealth; this.gameDuration = 600; // 10 minut boss.attackSpeedMultiplier = 0.6; // Szybsze ataki od początku boss.speed = 7; // Szybszy ruch console.log("Starting NEW BOSS+ mode. HP:", boss.maxHealth, "Time:", this.gameDuration); } else { boss.maxHealth = 200; // Standardowy boss HP boss.health = boss.maxHealth; this.gameDuration = 120; // 2 minuty boss.speed = 5; // Standardowa prędkość boss.attackSpeedMultiplier = 1; // Standardowa prędkość ataków console.log("Starting STANDARD mode. HP:", boss.maxHealth, "Time:", this.gameDuration); } // Pokaż ściany areny walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); // Ustaw UI dla stanu gry ui.positionElements("game"); ui.updateHearts(player.health, storage.maxHearts); ui.showTutorial("Swipe to Roll!"); // Wyświetl krótki tutorial ui.updateBossHealth(boss.health, boss.maxHealth); // Pokaż pasek HP bossa // --- Timer walki z bossem --- this.remainingTime = this.gameDuration; // Ustaw początkowy czas ui.updateTimerDisplay(this.remainingTime); // Wyświetl czas // Rozpocznij interwał timera if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } // Wyczyść stary timer this.bossSpeedIncreased = false; // Resetuj flagę przyspieszenia bossa this.gameTimerInterval = LK.setInterval(function () { // Aktualizuj tylko w stanie gry if (gameState.currentState === "game") { gameState.remainingTime--; ui.updateTimerDisplay(gameState.remainingTime); // --- Przyspieszenie bossa - TYLKO W STANDARDOWYM TRYBIE --- if (!isNewBossPlusMode) { var accelerationThreshold = gameState.gameDuration - 60; // Przyspieszenie na 60s przed końcem if (gameState.remainingTime <= accelerationThreshold && !gameState.bossSpeedIncreased) { gameState.bossSpeedIncreased = true; if (boss && !boss.dead) { // Sprawdź czy boss nadal żyje boss.attackSpeedMultiplier *= 0.7; // Przyspiesz ataki boss.speed += 1; // Lekko przyspiesz ruch ui.showMessage("Boss attacks faster!", 2000); // Komunikat } } } // --- Koniec Przyspieszenia bossa --- // Sprawdź koniec gry (czas minął) if (gameState.remainingTime <= 0) { LK.clearInterval(gameState.gameTimerInterval); // Zatrzymaj timer gameState.gameTimerInterval = null; if (isNewBossPlusMode) { // Czas minął w Boss+ -> Zwycięstwo przez przetrwanie gameOverReasonIsDeath = false; // Przyczyna: czas gameState.gameOver(false); // Przejdź do ekranu końca gry (specjalny komunikat) } else { // Czas minął w standardowym trybie -> Przejdź do Grill Menu // Zakładamy, że przetrwanie 2 minut w standardowym trybie to też "zwycięstwo" gameState.showGrillScreen(); } } } else { // Jeśli stan gry się zmienił, zatrzymaj timer if (gameState.gameTimerInterval) { LK.clearInterval(gameState.gameTimerInterval); } gameState.gameTimerInterval = null; } } /*.bind(this)*/, 1000); // Interwał co 1 sekundę // Ukryj tutorial po chwili LK.setTimeout(function () { if (gameState.currentState === "game") { // Sprawdź czy nadal jesteśmy w grze ui.hideTutorial(); } // Rozpoczęcie ataków bossa jest teraz obsługiwane przez Boss.update i jego cooldown }, 3000); // 3 sekundy opóźnienia }, // victory() - nieużywane, logika w timerze i boss.die() showGrillScreen: function showGrillScreen() { // Wyczyść timery if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; this.bossSpeedIncreased = false; // Reset flagi clearScene(); // Wyczyść elementy gry (ataki, itp.) // Zniszcz gracza i bossa jeśli jeszcze istnieją if (player && player.destroy) { player.destroy(); } player = null; if (boss && boss.destroy) { boss.destroy(); } boss = null; // Boss powinien być już zniszczony przez die(), ale na wszelki wypadek this.currentState = "grillMenu"; game.setBackgroundColor(0x333333); // Tło grilla // Usuń tło gry if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); currentBackground = null; } // Dodaj tło dla ekranu Grilla currentBackground = LK.getAsset('grillMenu', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChildAt(currentBackground, 0); // Na spód // Ukryj ściany walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ustaw UI dla Grill Menu ui.positionElements("grillMenu"); ui.updateBossHealth(0, 1); // Ukryj pasek HP bossa ui.updateHearts(0, storage.maxHearts); // Ukryj serca gracza // --- Przyciski na ekranie Grilla --- var buttonYStart = 1000; var buttonYOffset = 150; // Przycisk "Rest" var restButton = new Container(); restButton.interactive = true; restButton.x = 2048 / 2; restButton.y = buttonYStart; currentSceneElements.addChild(restButton); var restButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); restButton.addChild(restButtonBg); var restButtonText = new Text2('Rest', { size: 50, fill: 0xFFFFFF }); restButtonText.anchor.set(0.5, 0.5); restButton.addChild(restButtonText); restButton.down = function () { ui.showMessage("Rest in peace...", 2000); LK.setTimeout(function () { ui.showMessage("Thank you for playing!", 3000); }, 2500); LK.setTimeout(function () { // clearScene(); // Niekonieczne, showTitleScreen to zrobi gameState.showTitleScreen(); // Wróć do tytułu }, 6000); // Opóźnienie }; // Przycisk "Upgrade Roll" (obecnie bez funkcji) var upgradeButton = new Container(); upgradeButton.interactive = true; upgradeButton.x = 2048 / 2; upgradeButton.y = buttonYStart + buttonYOffset; currentSceneElements.addChild(upgradeButton); var upgradeButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); upgradeButton.addChild(upgradeButtonBg); var upgradeButtonText = new Text2('Upgrade Roll', { size: 50, fill: 0xFFFFFF }); upgradeButtonText.anchor.set(0.5, 0.5); upgradeButton.addChild(upgradeButtonText); upgradeButton.down = function () { ui.showMessage("Not implemented yet!", 2000); }; // Zmieniono komunikat // Przycisk "New Boss+" var newBossButton = new Container(); newBossButton.interactive = true; newBossButton.x = 2048 / 2; newBossButton.y = buttonYStart + buttonYOffset * 2; currentSceneElements.addChild(newBossButton); var newBossButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); newBossButton.addChild(newBossButtonBg); var newBossButtonText = new Text2('New Boss+', { size: 50, fill: 0xFFFFFF }); newBossButtonText.anchor.set(0.5, 0.5); newBossButton.addChild(newBossButtonText); newBossButton.down = function () { // clearScene(); // Niekonieczne, startGame to zrobi isNewBossPlusMode = true; // Ustaw flagę gameState.startGame(); // Rozpocznij grę w trybie Boss+ }; }, // Przejście do stanu Game Over // isDeath: true (gracz zginął), false (czas minął w Boss+) gameOver: function gameOver(isDeath) { this.currentState = "gameOver"; // Ustaw stan gameOverReasonIsDeath = isDeath; // Zapisz przyczynę // Zatrzymaj timer gry if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; this.bossSpeedIncreased = false; // Reset flagi przyspieszenia // Zatrzymaj animację tła (jeśli istnieje, ale nie powinno w tym stanie) if (currentBackground) { tween.stop(currentBackground); } game.setBackgroundColor(0x000000); // Czarne tło // Ukryj/zniszcz gracza i bossa (powinny być już zniszczone przez swoje metody die) if (player && player.alpha !== 0) { player.alpha = 0; } // Ukryj, jeśli nie zniknął if (boss && boss.alpha !== 0) { boss.alpha = 0; } // Ukryj ściany walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ustaw UI dla ekranu game over ui.positionElements("gameOver"); // Logika komunikatu Game Over var gameOverMessage = "YOU DIED"; // Domyślny komunikat if (!isDeath) { // Jeśli czas minął (tylko w Boss+) gameOverMessage = "YOU SURVIVED... for now."; // Zmieniono komunikat zwycięstwa Boss+ } // Wyświetl komunikat i tytuł ui.titleText.setText(gameOverMessage); ui.titleText.alpha = 1; // Pokaż tytuł ui.showMessage("", 0); // Wyczyść ewentualny poprzedni komunikat ui.showTutorial(""); // Ukryj tutorial // Pasek zdrowia bossa jest zarządzany przez updateBossHealth w pętli gry // W tym stanie updateBossHealth pokaże pasek tylko jeśli !isDeath && isNewBossPlusMode // Zaplanuj restart gry po opóźnieniu LK.setTimeout(function () { // Wyczyść scenę przed restartem clearScene(); // Zniszcz gracza/bossa jeśli jakimś cudem przetrwali if (player && player.destroy) { player.destroy(); } player = null; if (boss && boss.destroy) { boss.destroy(); } boss = null; // Wyczyść ataki bossa (na wszelki wypadek) - teraz robione w Boss.die / Boss.update // if (boss && boss.attacks) boss.attacks = []; // Usuń tło game over (czarne) if (currentBackground) { currentBackground.destroy(); } currentBackground = null; // Resetuj UI do stanu "przed grą" (np. jak w tytule, ale bez tekstów) // Pozycjonowanie jest ok, ale trzeba wyczyścić teksty ui.titleText.setText(""); ui.titleText.alpha = 0; ui.messageText.setText(""); ui.messageText.alpha = 0; ui.tutorialText.setText(""); ui.tutorialText.alpha = 0; ui.updateDeathsCounter(); // Zaktualizuj licznik śmierci ui.updateBossHealth(0, 1); // Ukryj pasek HP ui.updateHearts(0, storage.maxHearts); // Ukryj serca // Flaga isNewBossPlusMode pozostaje niezmieniona (jeśli zginąłeś w Boss+, restartujesz w Boss+) // gameOverReasonIsDeath zostanie zresetowana przy następnym wywołaniu gameOver // Restartuj walkę (startGame użyje flagi isNewBossPlusMode) gameState.startGame(); }, 4000); // Zwiększono opóźnienie do 4 sekund przed restartem }, // Obsługa gestów dotykowych/myszy processTouchGesture: function processTouchGesture() { // --- Obsługa inputu w stanie fakeTutorial --- if (this.currentState === "fakeTutorial" && this.fakeTutorialTimerId) { this.handleFakeTutorialInput(); // Wywołaj fałszywą śmierć return; // Zakończ przetwarzanie gestu } var dx = this.touchEnd.x - this.touchStart.x; var dy = this.touchEnd.y - this.touchStart.y; var distance = Math.sqrt(dx * dx + dy * dy); // Minimalny dystans dla swipe (turlania) var swipeThreshold = 50; // --- Obsługa Tapnięcia --- if (distance < swipeThreshold) { // Sprawdź, czy tapnięcie było na interaktywnym obiekcie (przycisku) // Zakładamy, że LK.Game obsługuje to przez przekazanie 'obj' do game.down/up // Jeśli tapnięcie NIE było na przycisku: if (this.currentState === "title") { // Sprawdź czy tapnięcie nie było na przycisku Grill Menu // Prosty sposób: załóżmy, że jeśli obj nie jest zdefiniowany w game.up, to kliknięto tło // (Wymaga sprawdzenia, jak LK.Game przekazuje 'obj') // Na razie zakładamy, że tapnięcie w tło przechodzi do intro this.showIntro(); return; } // W innych stanach (gameOver, victory, grillMenu) tapnięcie w tło nic nie robi // Obsługa przycisków dzieje się przez ich własne handlery .down/.up return; } // --- Obsługa Swipe (Turlania) --- // Działa tylko w stanie gry i gdy gracz żyje if (this.currentState === "game" && player && !player.dead) { // Normalizuj kierunek gestu var direction = { x: 0, y: 0 }; if (distance > 0) { // Unikaj dzielenia przez zero direction.x = dx / distance; direction.y = dy / distance; } // Wykonaj turlanie gracza player.roll(direction); } // W innych stanach swipe jest ignorowany } }; // --- Obsługa inputu --- game.down = function (x, y, obj) { // Rejestruj początek dotyku/kliknięcia // Stany, w których śledzimy gesty lub kliknięcia przycisków: var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"]; if (trackStates.indexOf(gameState.currentState) !== -1) { gameState.touchStart.x = x; gameState.touchStart.y = y; gameState.touchEnd.x = x; // Reset end point gameState.touchEnd.y = y; } // Obsługa kliknięć przycisków jest robiona przez ich własne handlery .down }; game.up = function (x, y, obj) { // Rejestruj koniec dotyku/kliknięcia i przetwarzaj gest var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"]; if (trackStates.indexOf(gameState.currentState) !== -1) { gameState.touchEnd.x = x; gameState.touchEnd.y = y; // Przetwórz gest (sprawdzi czy to tap czy swipe i podejmie akcję) // Sprawdź, czy 'obj' istnieje - jeśli tak, to było kliknięcie na interaktywnym elemencie, // którego logikę obsługuje jego własny handler .down/.up, więc nie rób nic więcej. // Jeśli 'obj' nie istnieje, to było kliknięcie/swipe w tło. game.up = function (x, y, obj) { if (trackStates.indexOf(gameState.currentState) !== -1) { gameState.touchEnd.x = x; gameState.touchEnd.y = y; // WAŻNE: Wywołuj zawsze processTouchGesture(), bez if (!obj)! gameState.processTouchGesture(); } }; } // Obsługa puszczenia przycisków (.up) może być dodana tutaj, jeśli potrzebna }; game.move = function (x, y, obj) { // Śledź ruch palca/myszy dla swipe var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"]; if (trackStates.indexOf(gameState.currentState) !== -1) { gameState.touchEnd.x = x; // Aktualizuj końcową pozycję na bieżąco gameState.touchEnd.y = y; } }; // --- Główna pętla aktualizacji gry --- game.update = function () { // Aktualizuj UI niezależnie od stanu gry (ale tylko jeśli UI istnieje) if (ui) { ui.updateDeathsCounter(); // Aktualizuj pasek zdrowia bossa (jeśli boss istnieje i UI istnieje) if (boss) { // Upewnij się, że maxHealth jest sensowne var maxHp = boss.maxHealth > 0 ? boss.maxHealth : 1; ui.updateBossHealth(boss.health, maxHp); } 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) } // Sprawdzanie kolizji i zmiany stanów (gameOver, grillScreen) są teraz wewnątrz metod update/die gracza i bossa. } // Logika dla innych stanów (np. animacje w intro, obsługa przycisków) jest zarządzana przez ich własne funkcje i handlery. }; // --- Rozpoczęcie gry --- // Inicjalizuj obiekt gameState, który zajmie się resztą. gameState.init();
===================================================================
--- original.js
+++ change.js
@@ -10,14 +10,15 @@
// Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem)
var Boss = Container.expand(function () {
var self = Container.call(this);
self.bossGraphics = self.attachAsset('bossIdle', {
+ // [cite: 3]
anchorX: 0.5,
anchorY: 0.5
});
// Dodajemy funkcje animacji
self.createBossAttackAnim = function () {
- var frames = [LK.getAsset('bossAttack0', {}), LK.getAsset('bossAttack1', {}), LK.getAsset('bossAttack2', {}), LK.getAsset('bossAttack3', {})];
+ var frames = [LK.getAsset('bossAttack0', {}), LK.getAsset('bossAttack1', {}), LK.getAsset('bossAttack2', {}), LK.getAsset('bossAttack3', {})]; // [cite: 3]
var bossAttackAnim = new SpriteAnimation({
frames: frames,
frameDuration: 100,
loop: false,
@@ -28,193 +29,147 @@
});
return bossAttackAnim;
};
self.playBossAttackAnim = function () {
- if (self.bossGraphics) {
- // Podmieniamy animację w ramach tego samego obiektu, nie niszczymy go
- self.bossGraphics = game.addChild(self.createBossAttackAnim());
- } else {
- // Jeśli bossGraphics nie istnieje (np. nie ma jeszcze bossa), tworzysz animację od razu
- self.bossGraphics = game.addChild(self.createBossAttackAnim());
+ // Upewnij się, że poprzednia animacja jest całkowicie usunięta, zanim zaczniemy nową
+ if (self.bossAttackAnim) {
+ self.bossAttackAnim.stop(); // Zatrzymujemy animację
+ // *** Usuń poprzednią animację z jej rodzica przed zniszczeniem ***
+ if (self.bossAttackAnim.parent) {
+ self.bossAttackAnim.parent.removeChild(self.bossAttackAnim);
+ }
+ self.bossAttackAnim.destroy(); // Usuwamy poprzednią animację [cite: 3]
+ self.bossAttackAnim = null; // Ustawiamy na null po zniszczeniu
}
- self.bossGraphics.update = function () {
- if (!self.bossGraphics.playing || self.bossGraphics.frames.length === 0) {
+ // *** WAŻNA POPRAWKA: Usuń obecną główną grafikę bossa (idle) przed rozpoczęciem animacji ataku ***
+ if (self.bossGraphics && self.bossGraphics.parent) {
+ self.bossGraphics.parent.removeChild(self.bossGraphics);
+ }
+ self.bossGraphics = null; // Wyczyść referencję
+ // Tworzymy nową animację bossa
+ self.bossAttackAnim = game.addChild(self.createBossAttackAnim()); // Nadal dodajemy do 'game' [cite: 4]
+ // Ustawiamy pozycję animacji, żeby podążała za bossem
+ self.bossAttackAnim.x = self.x; // [cite: 4]
+ self.bossAttackAnim.y = self.y; // [cite: 4]
+ // Metoda update dla TEJ NOWEJ animacji (tutaj definiujemy jej zachowanie)
+ self.bossAttackAnim.update = function () {
+ if (!self.bossAttackAnim.playing || self.bossAttackAnim.frames.length === 0) {
return;
}
- self.bossGraphics.frameTimer++;
- if (self.bossGraphics.frameTimer >= self.bossGraphics.frameDuration / (1000 / 60)) {
- self.bossGraphics.frameTimer = 0;
- self.bossGraphics.removeChildren();
- self.bossGraphics.currentFrame++;
- if (self.bossGraphics.currentFrame >= self.bossGraphics.frames.length) {
+ self.bossAttackAnim.frameTimer++;
+ if (self.bossAttackAnim.frameTimer >= self.bossAttackAnim.frameDuration / (1000 / 60)) {
+ // [cite: 6]
+ self.bossAttackAnim.frameTimer = 0;
+ self.bossAttackAnim.removeChildren(); // Usuń sprite klatki z kontenera animacji [cite: 6]
+ self.bossAttackAnim.currentFrame++;
+ if (self.bossAttackAnim.currentFrame >= self.bossAttackAnim.frames.length) {
// Animacja skończona, wracamy do idle
- self.bossGraphics.destroy();
+ // *** Dodaj grafikę idle z powrotem do 'game' i ustaw self.bossGraphics ***
self.bossGraphics = game.addChild(LK.getAsset('bossIdle', {
+ // [cite: 7]
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
- y: self.y
+ // Użyj pozycji bossa [cite: 7]
+ y: self.y // Użyj pozycji bossa [cite: 7]
}));
+ // *** Usuń obiekt animacji z jego rodzica i zniszcz go ***
+ if (self.bossAttackAnim.parent) {
+ self.bossAttackAnim.parent.removeChild(self.bossAttackAnim);
+ }
+ self.bossAttackAnim.destroy(); // [cite: 7]
+ self.bossAttackAnim = null; // Wyczyść referencję
} else {
- self.bossGraphics.addChild(self.bossGraphics.frames[self.bossGraphics.currentFrame]);
+ self.bossAttackAnim.addChild(self.bossAttackAnim.frames[self.bossAttackAnim.currentFrame]); // Dodaj sprite następnej klatki [cite: 8]
}
}
};
};
- 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)
- 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
- 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óć
- }
- // 🔥 Odpalenie animacji ataku bossa
- self.playBossAttackAnim();
- // Jeśli dotarliśmy tutaj, oznacza to, że możemy rozpocząć nowy wzorzec ataku
- self.attackPattern = (self.attackPattern + 1) % 3; // Cykl wzorców ataków
- switch (self.attackPattern) {
- case 0:
- self.circleAttack();
- break;
- case 1:
- self.lineAttack();
- break;
- case 2:
- self.chargeAttack();
- 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
- };
- self.circleAttack = function () {
- LK.getSound('bossAttack').play();
- var center = {
- x: self.x,
- y: self.y
- };
- var count = isNewBossPlusMode ? 12 : 8; // W trybie New Boss+ więcej pocisków
- var radius = 300;
- var attackLifeTime = isNewBossPlusMode ? 2000 : 3000; // Krótszy czas życia w New Boss+?
- 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, attackLifeTime); // Utwórz atak w danej pozycji z czasem życia (skalowane w createAttack)
- }
- };
- self.lineAttack = function () {
- LK.getSound('bossAttack').play();
- // Tworzy linię ataków od bossa do gracza
- // Upewnij się, że gracz istnieje przed próbą odczytu jego pozycji
- if (!player) {
- return;
- }
- var targetX = player.x;
- var targetY = player.y;
- var count = isNewBossPlusMode ? 8 : 5; // W trybie New Boss+ więcej pocisków w linii
- var attackLifeTime = isNewBossPlusMode ? 1500 : 2000; // Krótszy czas życia?
- var delayBetweenAttacks = isNewBossPlusMode ? 100 : 200; // Mniejsze opóźnienie między atakami w linii
- for (var i = 0; i < count; i++) {
- var t = i / (count - 1); // Współczynnik interpolacji
- var posX = self.x + (targetX - self.x) * t;
- var posY = self.y + (targetY - self.y) * t;
- var delay = i * delayBetweenAttacks * self.attackSpeedMultiplier; // Opóźnienie dla kolejnych ataków w linii, skalowane
- LK.setTimeout(function (x, y) {
- return function () {
- // Sprawdź, czy boss nadal żyje i gra jest w stanie gry zanim stworzysz atak
- if (self && !self.dead && gameState.currentState === "game") {
- self.createAttack(x, y, attackLifeTime); // Utwórz atak po opóźnieniu (czas życia skalowane w createAttack)
- }
- };
- }(posX, posY), delay);
- }
- };
+ // ... (pozostała część klasy Boss, aż do chargeAttack) ...
self.chargeAttack = function () {
- LK.getSound('bossAttack').play();
+ LK.getSound('bossAttack').play(); // [cite: 34]
// Upewnij się, że gracz istnieje przed próbą odczytu jego pozycji
if (!player) {
+ // [cite: 35]
return;
}
- // Ustawienie statycznego wyglądu bossa
- if (self.bossGraphics) {
- self.bossGraphics.destroy(); // Usuwamy poprzednią animację, jeśli była
- }
- self.bossGraphics = game.addChild(LK.getAsset('bossIdle', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: self.x,
- y: self.y
- }));
+ // *** USUŃ PONIŻSZY BLOK KODU - Zarządzanie grafiką jest teraz w playBossAttackAnim i metodzie update animacji ***
+ // if (self.bossGraphics) { // [cite: 35]
+ // self.bossGraphics.destroy(); // Usuwamy poprzednią animację, jeśli była
+ // }
+ // self.bossGraphics = game.addChild(LK.getAsset('bossIdle', { // [cite: 36]
+ // anchorX: 0.5,
+ // anchorY: 0.5,
+ // x: self.x,
+ // y: self.y
+ // }));
+ // *** KONIEC USUWANIA ***
// Oblicz kierunek do gracza
- var dx = player.x - self.x;
- var dy = player.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
+ var dx = player.x - self.x; // [cite: 36]
+ var dy = player.y - self.y; // [cite: 37]
+ var distance = Math.sqrt(dx * dx + dy * dy); // [cite: 37]
if (distance > 0) {
+ // [cite: 37]
dx /= distance;
- dy /= distance;
+ dy /= distance; // [cite: 38]
}
// Zapisz oryginalną pozycję (z której zaczyna się szarża)
- var startX = self.x;
- var startY = self.y;
- var chargeDistance = isNewBossPlusMode ? 700 : 500; // Dłuższa szarża w New Boss+?
- var chargeDuration = isNewBossPlusMode ? 600 : 800; // Szybsza szarża w New Boss+?
- var attacksOnPathCount = isNewBossPlusMode ? 8 : 5; // Więcej ataków na ścieżce szarży?
- var attackLifeTime = isNewBossPlusMode ? 1000 : 1500; // Krótszy czas życia?
+ var startX = self.x; // [cite: 39]
+ var startY = self.y; // [cite: 39]
+ var chargeDistance = isNewBossPlusMode ? 700 : 500; // Dłuższa szarża w New Boss+? [cite: 39]
+ var chargeDuration = isNewBossPlusMode ?
+ // [cite: 40]
+ 600 : 800; // Szybsza szarża w New Boss+?
+ var attacksOnPathCount = isNewBossPlusMode ? 8 : 5; // [cite: 41]
+ // Więcej ataków na ścieżce szarży?
+ var attackLifeTime = isNewBossPlusMode ? 1000 : 1500; // Krótszy czas życia? [cite: 42]
// Animacja szarży (boss przesuwa się szybko w kierunku gracza)
tween(self, {
x: self.x + dx * chargeDistance,
- // Przesunięcie
- y: self.y + dy * chargeDistance
+ // Przesunięcie [cite: 42]
+ y: self.y + dy * chargeDistance // [cite: 43]
}, {
duration: chargeDuration * self.attackSpeedMultiplier,
- // Czas trwania szarży, skalowane
+ // Czas trwania szarży, skalowane [cite: 43]
easing: tween.easeIn,
onFinish: function onFinish() {
// Sprawdź, czy boss nadal żyje i gra jest w stanie gry
if (self && !self.dead && gameState.currentState === "game") {
+ // [cite: 43]
// --- Start Repositioning Phase ---
- self.repositioning = true; // Ustaw flagę repozycji po zakończeniu szarży
+ self.repositioning = true; // Ustaw flagę repozycji po zakończeniu szarży [cite: 43]
// Czas na repozycję (np. 0.5 sekundy), skalowane
LK.setTimeout(function () {
+ // [cite: 43]
if (self && !self.dead) {
- // Upewnij się, że boss nadal istnieje i żyje
- self.repositioning = false; // Zakończ stan repozycji po opóźnieniu
+ // [cite: 43]
+ self.repositioning = false; // Zakończ stan repozycji po opóźnieniu [cite: 43]
}
- }, 500 * self.attackSpeedMultiplier); // Opóźnienie skalowane przez mnożnik prędkości ataków
+ }, 500 * self.attackSpeedMultiplier); // Opóźnienie skalowane przez mnożnik prędkości ataków [cite: 43]
// --- End Repositioning Phase ---
// Powrót do oryginalnej pozycji (startu szarży) po szarży
tween(self, {
+ // [cite: 44]
x: startX,
- y: startY
+ // [cite: 44]
+ y: startY // [cite: 44]
}, {
duration: 1000 * self.attackSpeedMultiplier,
- // Czas powrotu, skalowane
+ // Czas powrotu, skalowane [cite: 44]
easing: tween.easeOut
});
// Utwórz ataki wzdłuż ścieżki szarży
for (var i = 0; i < attacksOnPathCount; i++) {
- var t = i / (attacksOnPathCount - 1);
- var currentChargeX = self.x; // Get the end position of the charge
- var currentChargeY = self.y;
+ // [cite: 44]
+ var t = i / (attacksOnPathCount - 1); // [cite: 45]
+ var currentChargeX = self.x; // Get the end position of the charge [cite: 45]
+ var currentChargeY = self.y; // [cite: 46]
// Interpolujemy pomiędzy początkiem a końcem szarży
- var posX = startX + (currentChargeX - startX) * t;
- var posY = startY + (currentChargeY - startY) * t;
+ var posX = startX + (currentChargeX - startX) * t; // [cite: 47]
+ var posY = startY + (currentChargeY - startY) * t; // [cite: 47]
// Utwórz atak w danej pozycji
- self.createAttack(posX, posY, attackLifeTime); // Czas życia skalowane w createAttack
+ self.createAttack(posX, posY, attackLifeTime); // [cite: 48]
+ // Czas życia skalowane w createAttack
}
}
}
});
@@ -359,8 +314,45 @@
}
});
// Ataki zostaną wyczyszczone na początku die() lub w gameState.showGrillScreen clean up
};
+ // Initialize boss attacks array and properties
+ self.attacks = [];
+ self.maxHealth = 200;
+ self.health = self.maxHealth;
+ self.phase = 1;
+ self.speed = 5;
+ self.attackSpeedMultiplier = 1;
+ self.dead = false;
+ self.repositioning = false;
+ self.attackCooldown = 90;
+ // Method to start a random attack pattern
+ self.startAttackPattern = function () {
+ // Check if boss is dead or game state changed
+ if (self.dead || gameState.currentState !== "game") {
+ return;
+ }
+ // Choose a random attack pattern
+ var attackChoice = Math.floor(Math.random() * 3);
+ // Play attack animation first
+ self.playBossAttackAnim();
+ // Execute attack based on choice
+ switch (attackChoice) {
+ case 0:
+ self.chargeAttack();
+ break;
+ case 1:
+ self.circleAttack();
+ break;
+ case 2:
+ self.lineAttack();
+ break;
+ default:
+ self.chargeAttack();
+ }
+ // Set cooldown for next attack
+ self.attackCooldown = Math.floor(120 * self.attackSpeedMultiplier);
+ };
self.update = function () {
// Aktualizuj tylko w stanie gry
if (gameState.currentState !== "game") {
// Jeśli zmieniono stan gry i boss nie umiera, wyczyść pozostałe ataki
@@ -1126,10 +1118,10 @@
/****
* Game Code
****/
-// Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia)
// Komentarze o assetach pominięte dla zwięzłości
+// Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia)
var currentSceneElements = new Container();
// Zmienne gry
var player;
var boss;