Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'self.attachAsset is not a function' in or related to this line: 'self.playerGraphics = self.attachAsset('player', {' Line Number: 259
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' 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); // Add the bossGraphics as a child' Line Number: 657
/**** * 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) // Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem) 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) *** // Używamy attachAsset, który już dodaje jako dziecko i ustawia pozycję relatywną na podstawie anchor. self.bossGraphics = self.attachAsset('bossIdle', { // Dziecko self anchorX: 0.5, anchorY: 0.5 // Pozycja relatywna na 0,0 jest domyślna i poprawna przy anchorX/Y 0.5 }); // 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', {})]; 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() { // Funkcja 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) { 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 }); 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.removeChildren(); // zabezpieczenie self.addChild(self.frames[self.currentFrame]); } // Animation update method self.update = function () { if (!self.playing || self.frames.length === 0) { return; } self.frameTimer++; if (self.frameTimer >= self.frameDuration / (1000 / 60)) { self.frameTimer = 0; if (self.children.length > 0) { self.removeChild(self.children[0]); // Usuwamy tylko 1 aktualną klatkę } self.currentFrame++; if (self.currentFrame >= self.frames.length) { if (self.loop) { self.currentFrame = 0; } else { self.currentFrame = self.frames.length - 1; self.playing = false; } } self.addChild(self.frames[self.currentFrame]); // Dodajemy nową klatkę } }; // 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 ****/ // Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia) // Komentarze o assetach pominięte dla zwięzłości 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
@@ -1,594 +1,23 @@
/****
-* Initialize Game
+* Plugins
****/
-var game = new LK.Game({
- backgroundColor: 0x000000
-});
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1");
/****
-* Game Code
+* Classes
****/
-/****
-* 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 gameState
-/****
-* Player class
-****/
-var Player = Container.expand(function () {
- var self = Container.call(this);
- // Attach player graphic (depends on asset)
- self.playerGraphics = LK.getAsset('player', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.addChild(self.playerGraphics);
- 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()); // User reported 'self.addChild is not a function' here, but it should work on a Container instance.
- 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', {
- // self.addChild should work here.
- 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', {
- // self.addChild should work here.
- 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;
-})(); // End Player class
-/****
-* UI object
-****/
-var UI = function () {
- var self = {};
- // Create UI elements (depend on assets/shapes)
- self.bossHpBar = LK.getAsset('bossHpbar', {}); // Should use LK.getAsset for shapes in this code.
- 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 DisplayObjectContainer(); // 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', {}); // Should use LK.getAsset for shapes in this code.
- 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 LK.Text({
- text: "Start Game",
- color: 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', {}); // Should use LK.getAsset for shapes in this code.
- 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 LK.Text({
- text: "NEW BOSS+",
- color: 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 LK.Text({
- text: "Game Over!",
- color: 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', {}); // User reported 'LK.getShape is not a function' here, but LK.getAsset should work in this code.
- self.grillMenu.x = 1024;
- self.grillMenu.y = 1366;
- self.grillMenu.anchorX = 0.5;
- self.grillMenu.anchorY = 0.5;
- 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 currentHealth
- var heartSize = 40; // Match heart asset size
- var padding = 10; // Spacing between hearts
- for (var i = 0; i < maxHearts; i++) {
- var heartGraphic = LK.getAsset('heart', {}); // Should use LK.getAsset for shapes in this code.
- // 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
-****/
+// Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem)
+// Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem)
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) ***
// Używamy attachAsset, który już dodaje jako dziecko i ustawia pozycję relatywną na podstawie anchor.
self.bossGraphics = self.attachAsset('bossIdle', {
- // User reported 'self.attachAsset is not a function' here, but it should work on a Container instance.
// Dziecko self
anchorX: 0.5,
anchorY: 0.5
// Pozycja relatywna na 0,0 jest domyślna i poprawna przy anchorX/Y 0.5
@@ -643,9 +72,9 @@
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()); // self.addChild should work here.
+ 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
@@ -663,9 +92,9 @@
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', {
- // self.addChild should work here.
+ // *** MODIFIED: DODANO DO self! ***
anchorX: 0.5,
anchorY: 0.5,
x: 0,
// Pozycja relatywna
@@ -739,12 +168,8 @@
// 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,
@@ -872,9 +297,8 @@
} else if (self.bossGraphics && self.bossGraphics.parent) {
self.bossGraphics.parent.removeChild(self.bossGraphics);
}
self.bossGraphics = self.addChild(LK.getAsset('bossIdle', {
- // self.addChild should work here.
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
@@ -904,9 +328,9 @@
// Czas trwania, skalowany
easing: tween.easeIn,
// Krzywa animacji
onFinish: function onFinish() {
- // Po zakończeniu szarży
+ // Funkcja po zakończeniu szarży
if (self && !self.dead && gameState.currentState === "game") {
// Faza repozycji
self.repositioning = true;
LK.setTimeout(function () {
@@ -1086,9 +510,8 @@
}
}
// 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);
@@ -1104,79 +527,1618 @@
self.startAttackPattern();
}
};
return self; // Zwróć instancję obiektu Boss
-})(); // End Boss class
+});
+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.removeChildren(); // zabezpieczenie
+ self.addChild(self.frames[self.currentFrame]);
+ }
+ // Animation update method
+ self.update = function () {
+ if (!self.playing || self.frames.length === 0) {
+ return;
+ }
+ self.frameTimer++;
+ if (self.frameTimer >= self.frameDuration / (1000 / 60)) {
+ self.frameTimer = 0;
+ if (self.children.length > 0) {
+ self.removeChild(self.children[0]); // Usuwamy tylko 1 aktualną klatkę
+ }
+ self.currentFrame++;
+ if (self.currentFrame >= self.frames.length) {
+ if (self.loop) {
+ self.currentFrame = 0;
+ } else {
+ self.currentFrame = self.frames.length - 1;
+ self.playing = false;
+ }
+ }
+ self.addChild(self.frames[self.currentFrame]); // Dodajemy nową klatkę
+ }
+ };
+ // 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;
+});
+
/****
-* Game creation and loop
+* Initialize Game
****/
-// 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;
+var game = new LK.Game({
+ backgroundColor: 0x111111 // Ciemne tło domyślne
+});
+
+/****
+* 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
+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();
+ }
}
- 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); // game.addChild should work here.
- // 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); // game.addChild should work here.
- // 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); // game.addChild should work here.
- // 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 should work here.
- game.addChild(ui.playerHearts); // game.addChild should work here.
- game.addChild(ui.startButton); // Start button is visible in title state // game.addChild should work here.
- game.addChild(ui.newBossPlusButton); // Boss+ button is visible in title state // game.addChild should work here.
- game.addChild(ui.gameOverMessage); // Game over message is hidden initially // game.addChild should work here.
- game.addChild(ui.grillMenu); // Grill menu is hidden initially // game.addChild should work here.
- // 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);
+ // 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 {
- // 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
+ 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)
@@ -1199,42 +2161,11 @@
}
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 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)
+ // Logika dla innych stanów (np. animacje w intro, obsługa przycisków) jest zarządzana przez ich własne funkcje i handlery.
};
-// 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)
- }
-});
\ No newline at end of file
+// --- Rozpoczęcie gry ---
+// Inicjalizuj obiekt gameState, który zajmie się resztą.
+gameState.init();
\ No newline at end of file