User prompt
Please fix the bug: 'Uncaught ReferenceError: Player is not defined' in or related to this line: 'player = game.addChild(new Player()); // Stwórz nowego gracza' Line Number: 1229
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: Player is not defined' in or related to this line: 'player = game.addChild(new Player()); // Stwórz nowego gracza' Line Number: 1228
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught RangeError: Maximum call stack size exceeded' in or related to this line: 'this.handleFakeTutorialInput(); // REMOVED self-recursive call that caused stack overflow' Line Number: 1199
User prompt
Please fix the bug: 'Uncaught ReferenceError: Player is not defined' in or related to this line: 'player = game.addChild(new Player()); // Stwórz nowego gracza' Line Number: 1196
Code edit (4 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught RangeError: Maximum call stack size exceeded' in or related to this line: 'this.handleFakeTutorialInput(); // REMOVED self-recursive call that caused stack overflow' Line Number: 1293
Code edit (1 edits merged)
Please save this source code
User prompt
add new asset grillMenu
Code edit (1 edits merged)
Please save this source code
User prompt
add asset bossHpbar
Code edit (5 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught RangeError: Maximum call stack size exceeded' in or related to this line: 'this.handleFakeTutorialInput(); // Wywołaj logikę fałszywej śmierci' Line Number: 1228
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'set')' in or related to this line: 'self.bossHealthBarBg.anchor.set(0.5, 0.5); // Ustaw punkt odniesienia na środek' Line Number: 705
User prompt
Please fix the bug: 'Shape is not defined' in or related to this line: 'self.bossHealthBarBg = new Shape({' Line Number: 677
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: playerRadius is not defined' in or related to this line: 'if (distance < attack.radius + playerRadius) {' Line Number: 347
Code edit (9 edits merged)
Please save this source code
User prompt
add asset introBg
User prompt
add asset titleBg
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Button2 is not defined' in or related to this line: 'var startButton = new Button2("Start Game", function () {' Line Number: 574
User prompt
Please fix the bug: 'clearScene is not defined' in or related to this line: 'clearScene();' Line Number: 555
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem) var Boss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.speed = 5; self.attackCooldown = 0; self.attackPattern = 0; self.attacks = []; // 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.startAttackPattern = function () { if (self.dead || gameState.currentState !== "game") { // Ataki tylko w stanie gry return; } self.attackPattern = (self.attackPattern + 1) % 3; // Cykl wzorców ataków switch (self.attackPattern) { case 0: self.circleAttack(); break; case 1: self.lineAttack(); break; case 2: self.chargeAttack(); break; } // Ustaw następny atak z losowym opóźnieniem, skalowane przez mnożnik prędkości self.attackCooldown = (90 + Math.floor(Math.random() * 60)) * self.attackSpeedMultiplier; // 1.5-2.5 seconds (90-150 frames) * multiplier }; self.circleAttack = function () { LK.getSound('bossAttack').play(); var center = { x: self.x, y: self.y }; var count = 8; // Liczba pocisków/ataków w kręgu var radius = 300; for (var i = 0; i < count; i++) { var angle = i / count * Math.PI * 2; var posX = center.x + Math.cos(angle) * radius; var posY = center.y + Math.sin(angle) * radius; self.createAttack(posX, posY, 3000); // Utwórz atak w danej pozycji z czasem życia (skalowane w createAttack) } }; self.lineAttack = function () { LK.getSound('bossAttack').play(); // Tworzy linię ataków od bossa do gracza var targetX = player.x; var targetY = player.y; var count = 5; for (var i = 0; i < count; i++) { var t = i / (count - 1); // Współczynnik interpolacji var posX = self.x + (targetX - self.x) * t; var posY = self.y + (targetY - self.y) * t; var delay = i * 200 * self.attackSpeedMultiplier; // Opóźnienie dla kolejnych ataków w linii, skalowane LK.setTimeout(function (x, y) { return function () { // Sprawdź, czy boss nadal żyje i gra jest w stanie gry zanim stworzysz atak if (self && !self.dead && gameState.currentState === "game") { self.createAttack(x, y, 2000); // Utwórz atak po opóźnieniu (czas życia skalowane w createAttack) } }; }(posX, posY), delay); } }; self.chargeAttack = function () { LK.getSound('bossAttack').play(); // Oblicz kierunek do gracza 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; } // Zapisz oryginalną pozycję var startX = self.x; var startY = self.y; // Animacja szarży (boss przesuwa się szybko w kierunku gracza) tween(self, { x: self.x + dx * 500, // Przesunięcie o 500 jednostek w kierunku gracza y: self.y + dy * 500 }, { duration: 800 * self.attackSpeedMultiplier, // Czas trwania szarży, skalowane easing: tween.easeIn, onFinish: function onFinish() { // Sprawdź, czy boss nadal żyje i gra jest w stanie gry if (self && !self.dead && gameState.currentState === "game") { // Powrót do oryginalnej pozycji po szarży tween(self, { x: startX, y: startY }, { duration: 1000 * self.attackSpeedMultiplier, // Czas powrotu, skalowane easing: tween.easeOut }); // Utwórz ataki wzdłuż ścieżki szarży for (var i = 0; i < 5; i++) { var t = i / 4; var posX = startX + (self.x - startX) * t; var posY = startY + (self.y - startY) * t; self.createAttack(posX, posY, 1500); // Czas życia skalowane w createAttack } } } }); }; self.createAttack = function (x, y, duration) { // Tworzy wizualne ostrzeżenie ataku (żółty okrąg) var warning = game.addChild(LK.getAsset('attack', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, alpha: 0.3, // Niska przezroczystość na początku tint: 0xFFFF00 // Żółty kolor ostrzeżenia })); // Animacja ostrzeżenia (miganie), skalowana przez mnożnik prędkości tween(warning, { alpha: 0.6 // Zwiększ przezroczystość }, { duration: 1000 * self.attackSpeedMultiplier, // Skalowany czas trwania easing: tween.easeInOut, onFinish: function onFinish() { // Sprawdź, czy warning nadal istnieje przed zmianą koloru if (warning && !warning.destroyed) { // Zmień kolor na czerwony, sygnalizując faktyczny atak warning.tint = 0xFF0000; // Animacja sygnalizująca gotowość ataku, skalowana przez mnożnik prędkości tween(warning, { alpha: 0.8 // Zwiększ przezroczystość bardziej }, { duration: 200 * self.attackSpeedMultiplier, // Skalowany czas trwania onFinish: function onFinish() { // Sprawdź, czy warning nadal istnieje if (warning && !warning.destroyed) { // Dodaj atak do listy aktywnych ataków bossa (jako obiekt danych) var attack = { x: warning.x, // Pozycja ataku (ze środka) y: warning.y, radius: warning.width / 2, // Promień kolizji (połówka szerokości) visual: warning, // Referencja do obiektu wizualnego lifeTime: duration * self.attackSpeedMultiplier * (60 / 1000) // Czas życia ataku w klatkach, skalowany. Przeliczamy ms na klatki (zakładając 60 FPS) }; self.attacks.push(attack); // !!! USUNIĘTO LK.setTimeout, USUWANIE BĘDZIE TYLKO W BOSS.update() !!! } // if warning exists } }); } // if warning exists } }); }; self.takeDamage = function (amount) { if (self.dead || gameState.currentState !== "game") { // Boss otrzymuje obrażenia tylko w stanie gry return; } self.health -= amount; // Wizualne sygnalizowanie otrzymania obrażeń LK.effects.flashObject(self, 0xFFFFFF, 200); // Przejście fazy bossa przy 50% zdrowia if (self.health <= self.maxHealth / 2 && self.phase === 1) { self.phase = 2; self.speed += 2; // Boss staje się szybszy (ruch) // Wizualne przejście fazy (np. zmiana koloru) tween(self, { tint: 0xFF3300 // Pomarańczowy/czerwony odcień }, { duration: 1000, easing: tween.easeInOut }); } // Sprawdzenie, czy boss został pokonany if (self.health <= 0) { self.die(); } }; self.die = function () { if (self.dead || gameState.currentState !== "game") { // Boss umiera tylko w stanie gry return; } self.dead = true; LK.getSound('victory').play(); // Dźwięk zwycięstwa // Animacja śmierci bossa (zanikanie i powiększanie) tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Sprawdź, czy boss nadal istnieje przed zniszczeniem if (self && self.destroy && !self.destroyed) { self.destroy(); // Zniszcz obiekt bossa } storage.bossesDefeated = (storage.bossesDefeated || 0) + 1; // Zwiększ licznik pokonanych bossów gameState.victory(); // Przejdź do stanu zwycięstwa (wywoła metodę w gameState) } }); // Zatrzymaj wszystkie timery związane z atakami bossa // Ataki zostaną wyczyszczone w gameState.victory/gameOver }; self.update = function () { // Aktualizuj tylko w stanie gry lub gdy boss umiera (do zakończenia animacji) if (gameState.currentState !== "game" && !self.dead) { // Jeśli zmieniono stan gry i boss nie umiera, wyczyść pozostałe ataki if (self.attacks) { self.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); self.attacks = []; // Wyczyść tablicę ataków } return; } // Nie aktualizuj jeśli boss jest martwy i animacja śmierci dobiegła końca (obiekt niewidoczny) if (self.dead && self.alpha === 0) { // Jeśli boss jest całkowicie niewidoczny po śmierci, wyczyść pozostałe ataki if (self.attacks) { self.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); self.attacks = []; // Wyczyść tablicę ataków } return; } // Logika ruchu tylko gdy boss żyje i gra jest w stanie gry if (!self.dead && gameState.currentState === "game") { // Aktualizacja cooldownu ataku if (self.attackCooldown > 0) { self.attackCooldown--; // !!! Zmieniono sprawdzanie cooldownu z === 0 na <= 0 !!! if (self.attackCooldown <= 0) { // Use <= 0 for robustness with floating point self.attackCooldown = 0; // Reset cooldown to exactly 0 after triggering attack self.startAttackPattern(); // This is where the next attack pattern starts } } // ... Movement logic ... } // Przetwarzaj aktywne ataki bossa (sprawdzaj kolizje z graczem i usuwaj po czasie życia) // Przetwarzaj ataki niezależnie od tego czy boss żyje, żeby pozostałe ataki po śmierci też znikały for (var i = self.attacks.length - 1; i >= 0; i--) { var attack = self.attacks[i]; // Upewnij się, że obiekt ataku i wizualizacja istnieją if (!attack || !attack.visual) { // Usuń z listy jeśli obiekt jest nieprawidłowy self.attacks.splice(i, 1); console.warn("Invalid attack object found in Boss.attacks:", attack); continue; } attack.lifeTime--; // Zmniejsz czas życia // Sprawdzenie kolizji z graczem i otrzymywanie obrażeń - tylko gdy gracz żyje, nie jest nietykalny i gra jest w stanie "game" if (player && !player.dead && !player.invulnerable && gameState.currentState === "game") { // Sprawdzenie kolizji z graczem (system kolizji z stworzylo zloto.txt - oparty na odległości między środkami) var dx = player.x - attack.x; var dy = player.y - attack.y; var distance = Math.sqrt(dx * dx + dy * dy); // Promień gracza (połówka szerokości) var playerRadius = player && player.width ? player.width / 2 : 0; if (distance < attack.radius + playerRadius) { // Kolizja wykryta // player.takeDamage(1) zwraca true jeśli gracz umrze po otrzymaniu obrażeń if (player.takeDamage(1)) { // Gracz umarł, obsługa game over nastąpi poprzez wywołanie gameState.gameOver() przez player.die() } // Wizualne sygnalizowanie trafienia atakiem (migotanie) // Sprawdź, czy wizualizacja nadal istnieje if (attack.visual && !attack.visual.destroyed) { LK.effects.flashObject(attack.visual, 0xFFFFFF, 200); } // Atak zazwyczaj trafia tylko raz. Możemy go usunąć po trafieniu. // Usuniemy go jednak po czasie życia, jak było w oryginalnym kodzie stworzylo_zloto. } } // Koniec sprawdzania kolizji tylko w stanie gry // Usuń ataki, których czas życia minął if (attack.lifeTime <= 0) { // Usuń z tablicy active attacks bossa self.attacks.splice(i, 1); // Zainicjuj animację zanikania i zniszczenie obiektu wizualnego // Check if the visual object exists and hasn't been destroyed if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { // Rozpocznij animację zanikania tween(attack.visual, { alpha: 0 }, { duration: 300 // Czas trwania zanikania w ms // No onFinish here, destruction is scheduled by timeout }); // Schedule destruction of the visual object after the fade out duration LK.setTimeout(function () { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }, 300); // Match the fade out duration } else { // If visual object is somehow missing or already destroyed, log a warning console.warn("Expired attack visual missing or already destroyed:", attack); } } } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Pobieramy szerokość i wysokość z grafiki gracza self.width = playerGraphics.width; self.height = playerGraphics.height; self.speed = 12; self.rolling = false; // Czy gracz się turla self.rollCooldown = 0; // Czas do następnego turlania (w klatkach) self.rollDuration = 0; // Pozostały czas turlania (w klatkach) self.rollDirection = { x: 0, y: 0 }; // Kierunek turlania self.rollDistance = 300; // Dystans turlania self.rollSpeed = 20; // Prędkość turlania self.invulnerable = false; // Czy gracz jest nietykalny self.invulnerabilityFrames = 0; // Czas nietykalności po trafieniu lub turlaniu (w klatkach) // Zdrowie gracza jest ustawiane w gameState.startGame() self.health = storage.maxHearts || 5; // Domyślnie 5 serc jeśli storage.maxHearts nie jest ustawione self.dead = false; // Czy gracz jest martwy // Funkcja turlania (wywoływana przez input) self.roll = function (direction) { // Nie można się turlać jeśli już się turlasz, jest cooldown, gracz jest martwy lub nie jesteśmy w stanie gry if (self.rolling || self.rollCooldown > 0 || self.dead || gameState.currentState !== "game") { return; } self.rolling = true; self.rollDuration = self.rollDistance / self.rollSpeed; // Czas trwania turlania obliczony z dystansu i prędkości self.rollDirection = direction; self.invulnerable = true; // Gracz jest nietykalny podczas turlania // Wizualny efekt turlania (niebieskie kółko) var rollEffect = game.addChild(LK.getAsset('roll', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 0.7 })); LK.getSound('roll').play(); // Dźwięk turlania // Zaplanuj koniec turlania i cooldown // Użyjemy self.rollEffectTimeoutId, żeby móc go wyczyścić przy restarcie lub śmierci self.rollEffectTimeoutId = LK.setTimeout(function () { // Sprawdź, czy gracz nadal istnieje i nie jest martwy zanim zakończysz turlanie if (self && !self.dead) { // Sprawdź, czy gracz nadal jest w stanie turlania ustawionym przez ten timeout if (self.rolling) { // Upewnij się, że flaga rolling nie została zmieniona przez nowe turlanie self.rolling = false; // Zakończ stan turlania self.rollCooldown = 30; // Ustaw cooldown // Mały bufor nietykalności po zakończeniu turlania // Użyjemy self.invulnerabilityTimeoutId self.invulnerabilityTimeoutId = LK.setTimeout(function () { // Sprawdź, czy gracz nadal żyje i nie zaczął nowego turlania if (!self.rolling && !self.dead) { self.invulnerable = false; // Zakończ nietykalność } }, 100); // 100ms dodatkowej nietykalności } else { // Jeśli flaga rolling została już zmieniona (np. rozpoczęto nowe turlanie), tylko ustaw cooldown if (!self.dead) { self.rollCooldown = 30; } } } // Animacja zanikania efektu turlania i zniszczenie obiektu wizualnego // Sprawdź, czy efekt turlania nadal istnieje i nie został zniszczony if (rollEffect && rollEffect.destroy && !rollEffect.destroyed) { tween(rollEffect, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (rollEffect && rollEffect.destroy && !rollEffect.destroyed) { rollEffect.destroy(); } } }); } self.rollEffectTimeoutId = null; // Wyczyść ID timeoutu po zakończeniu }, self.rollDuration * (1000 / 60)); // Czas trwania turlania w milisekundach }; // Metoda do czyszczenia timeoutów turlania/nietykalności (używane przy starcie gry/śmierci) self.clearRollTimeouts = function () { if (self.rollEffectTimeoutId) { LK.clearTimeout(self.rollEffectTimeoutId); self.rollEffectTimeoutId = null; } if (self.invulnerabilityTimeoutId) { LK.clearTimeout(self.invulnerabilityTimeoutId); self.invulnerabilityTimeoutId = null; } }; // Funkcja otrzymywania obrażeń self.takeDamage = function (amount) { // Nie można otrzymać obrażeń jeśli gracz jest nietykalny, martwy lub nie jesteśmy w stanie gry if (self.invulnerable || self.dead || gameState.currentState !== "game") { return false; // Nie otrzymano obrażeń } self.health -= amount; // Zmniejsz zdrowie LK.getSound('hit').play(); // Dźwięk trafienia // Sprawdzenie, czy gracz umarł if (self.health <= 0) { self.die(); // Wywołaj funkcję śmierci return true; // Gracz umarł } // Okres nietykalności po otrzymaniu obrażeń self.invulnerable = true; self.invulnerabilityFrames = 45; // 45 frames (ok. 0.75 sekundy) // Efekt migotania gracza LK.effects.flashObject(self, 0xFF0000, 500); return true; // Gracz otrzymał obrażenia (ale nie umarł) }; // Funkcja śmierci gracza self.die = function () { if (self.dead || gameState.currentState !== "game") { // Gracz umiera tylko w stanie gry return; } self.dead = true; LK.getSound('death').play(); // Dźwięk śmierci self.clearRollTimeouts(); // Wyczyść timery turlania/nietykalności przy śmierci // Wizualny efekt śmierci gracza (zanikanie i obrót) tween(self, { alpha: 0, rotation: Math.PI * 4 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Obiekt gracza zostanie zniszczony w gameState.gameOver clean up } }); // Aktualizacja licznika śmierci i sprawdzenie ulepszeń storage.totalDeaths = (storage.totalDeaths || 0) + 1; // Zwiększ licznik śmierci // Sprawdzenie, czy gracz powinien dostać dodatkowe serce (co 5 śmierci, max 10) storage.maxHearts = Math.min(10, 5 + Math.floor(storage.totalDeaths / 5)); // Ustaw max serc na podstawie śmierci, z limitem 10 // Zaktualizuj UI śmierci i serc if (ui) { ui.updateDeathsCounter(); // Zaktualizuj licznik śmierci ui.updateHearts(self.health, storage.maxHearts); // Zaktualizuj wyświetlanie serc (np. szarych przy śmierci) } // Przejdź do stanu game over po opóźnieniu (aby animacja śmierci mogła się zakończyć) LK.setTimeout(function () { gameState.gameOver(); // Wywołaj metodę game over w gameState }, 2000); // 2 sekundy opóźnienia }; self.update = function () { // Aktualizuj logikę gracza tylko w stanie gry if (gameState.currentState !== "game") { return; } // Nie aktualizuj jeśli gracz jest martwy (poza animacją śmierci) if (self.dead && self.alpha === 0) { // Gdy animacja śmierci dobiegła końca (obiekt niewidoczny) return; } // Jeśli gracz jest martwy, ale nadal widoczny, pozwól animacji zanikania działać (obsługiwane przez tween) if (self.dead) { return; // Nie wykonuj logiki ruchu i nietykalności jeśli gracz jest martwy } // Obsługa cooldownu turlania if (self.rollCooldown > 0) { self.rollCooldown--; } // Obsługa klatek nietykalności if (self.invulnerable && self.invulnerabilityFrames > 0) { self.invulnerabilityFrames--; // Efekt migotania (zmiana przezroczystości) self.alpha = self.invulnerabilityFrames % 6 > 2 ? 0.5 : 1; // Miga co kilka klatek if (self.invulnerabilityFrames <= 0) { self.invulnerable = false; self.alpha = 1; // Przywróć pełną przezroczystość gdy nietykalność minie } } // Obsługa ruchu podczas turlania - WYKONUJ TYLKO JEŚLI self.rolling JEST TRUE if (self.rolling) { // Jawne sprawdzenie flagi rolling self.x += self.rollDirection.x * self.rollSpeed; self.y += self.rollDirection.y * self.rollSpeed; } // Utrzymaj gracza w granicach ekranu (z uwzględnieniem ścian) // Ściany mają szerokość 100, więc gracz (środek) powinien być 100 jednostek od krawędzi mapy self.x = Math.max(100, Math.min(self.x, 2048 - 100)); self.y = Math.max(100, Math.min(self.y, 2732 - 100)); }; return self; }); var UI = Container.expand(function () { var self = Container.call(this); // Create heart containers (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); // Aktualizuje wizualizację serc na UI self.updateHearts = function (current, max) { // Usuń istniejące serca while (self.hearts.length > 0) { var heart = self.hearts.pop(); if (heart && heart.destroy) { heart.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; self.heartContainer.y = 100; // Pozycja pionowa }; // 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); } // Zaplanuj zanikanie po czasie, jeśli duration > 0 if (duration) { self.messageTimeout = LK.setTimeout(function () { tween(self.messageText, { alpha: 0 // Zaniknij do przezroczystości 0 }, { duration: 500 // Czas trwania zanikania }); }, duration); } }; // 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 () { self.deathsText.setText("Deaths: " + storage.totalDeaths); }; // Aktualizuje wyświetlanie czasu timera self.updateTimerDisplay = function (seconds) { 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) { switch (state) { case "title": // Pozycje dla ekranu tytułowego self.titleText.x = 2048 / 2; self.titleText.y = 800; self.messageText.x = 2048 / 2; self.messageText.y = 1000; // "Tap to Start" self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; // "Swipe to Roll - Death is Progress" // Licznik śmierci i timer są pozycjonowane na stałe w UI constructor, zmieniamy tylko ich alpha/widoczność // Ukryj serca i timer na ekranie tytułowym self.heartContainer.alpha = 0; self.timerText.alpha = 0; break; case "game": // Pozycje dla stanu gry (walka z bossem) self.titleText.alpha = 0; // Ukryj tytuł self.messageText.x = 2048 / 2; self.messageText.y = 1500; // Komunikaty w trakcie gry (np. "YOU DIED" - choć to też w victory/gameOver state) self.tutorialText.x = 2048 / 2; self.tutorialText.y = 200; // Tutorial w trakcie gry ("Swipe to roll away!") // Pokaż serca i timer podczas gry self.heartContainer.alpha = 1; self.timerText.alpha = 1; break; case "victory": // Pozycje dla ekranu zwycięstwa self.titleText.x = 2048 / 2; self.titleText.y = 800; // Tytuł "VICTORY ACHIEVED" self.messageText.x = 2048 / 2; self.messageText.y = 1000; // Komunikat o pokonanych bossach self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; // "Tap to Continue" // Ukryj serca i timer na ekranie zwycięstwa self.heartContainer.alpha = 0; self.timerText.alpha = 0; break; case "gameOver": // Pozycje dla ekranu Game Over (można użyć tych samych co Victory lub zdefiniować osobno) self.titleText.x = 2048 / 2; self.titleText.y = 800; // Tytuł "YOU DIED" self.messageText.x = 2048 / 2; self.messageText.y = 1000; // Pusty lub inny komunikat self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; // Pusty lub "Tap to Restart" // Ukryj serca i timer na ekranie Game Over self.heartContainer.alpha = 0; self.timerText.alpha = 0; break; case "intro": // Pozycje dla ekranów intro/tutoriali (głównie ukryte elementy UI walki) case "fakeTutorial": case "realTutorial": self.titleText.alpha = 0; // Ukryj tytuł self.messageText.alpha = 0; // Ukryj komunikat self.tutorialText.alpha = 0; // Ukryj tutorial // Licznik śmierci jest zawsze widoczny self.deathsText.x = 2048 - 50; // Upewnij się, że jest w prawym górnym rogu self.deathsText.y = 50; // Ukryj serca i timer self.heartContainer.alpha = 0; self.timerText.alpha = 0; break; } // Upewnij się, że licznik śmierci jest zawsze widoczny self.deathsText.alpha = 1; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 // Ciemne tło }); /**** * Game Code ****/ // Globalny kontener na elementy scen intro/tutoriali (do łatwego czyszczenia) // Dodano z souls4.txt dla przycisków intro // Używamy dźwięku zwycięstwa z stworzylo zloto.txt // Zachowujemy na wypadek użycia w animacjach gracza/UI var currentSceneElements = new Container(); // Dodano z souls4.txt // Zmienne gry (z stworzylo zloto.txt) var player; var boss; var ui; var walls = []; // Ściany areny bossa // Funkcja do czyszczenia elementów z kontenera currentSceneElements (dla scen intro/tutoriali) function clearScene() { // Dodano z souls4.txt // Usuń wszystkie dzieci z kontenera while (currentSceneElements.children.length > 0) { var child = currentSceneElements.children[0]; // Upewnij się, że obiekt istnieje i ma metodę destroy przed wywołaniem if (child && child.destroy) { child.destroy(); // Użyj destroy jeśli dostępne } else { currentSceneElements.removeChild(child); // Fallback } } // Nie resetujemy tutaj game.update ani input handlerów, bo są zarządzane przez gameState } // Obiekt zarządzający stanami gry (z stworzylo zloto.txt, rozbudowany o stany intro i timer) var gameState = { currentState: "title", // Początkowy stan gry // Zmienne dla timera walki z bossem gameDuration: 120, // Czas trwania walki z bossem w sekundach (2 minuty) remainingTime: 0, // Pozostały czas gameTimerInterval: null, // ID interwału timera // Zmienne dla fałszywego tutorialu fakeTutorialTimerId: null, // ID timera przechodzącego do prawdziwego tutorialu // Zmienne dla przyspieszania bossa bossSpeedIncreased: false, // Flaga informująca czy prędkość bossa została już zwiększona // Zmienne do śledzenia gestów dotykowych/myszy (dla turlania i tapowania) touchStart: { x: 0, y: 0 }, touchEnd: { x: 0, y: 0 }, // Inicjalizacja gry (wywoływana raz na początku aplikacji) init: function init() { // ZRESETUJ LICZNIK ŚMIERCI I USTAW DOMYŚLNĄ ILOŚĆ SERC NA START APLIKACJI // Resetuj licznik śmierci PRZY KAŻDYM URUCHOMIENIU GRY (załadowaniu strony) storage.totalDeaths = 0; // Ustaw początkową liczbę serc na 5 przy każdym uruchomieniu gry storage.maxHearts = 5; // Stwórz tło var bg = game.addChild(LK.getAsset('bg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 })); // Tło nie jest dodawane do currentSceneElements, bo ma być stałe // Stwórz ściany areny bossa (potrzebne w stanie gry, ale stworzone wcześniej) this.createWalls(); // Stwórz UI (ikony serc, teksty tytułów/komunikatów/tutoriali, licznik śmierci, timer) ui = game.addChild(new UI()); // Pozycjonuj UI początkowo dla ekranu tytułowego ui.positionElements("title"); ui.updateDeathsCounter(); // Zaktualizuj wyświetlanie licznika śmierci (powinien być 0) ui.updateHearts(storage.maxHearts, storage.maxHearts); // Zaktualizuj wyświetlanie serc (5 pełnych) // Dodaj kontener na elementy scen intro do wyświetlania (nad UI) game.addChild(currentSceneElements); // currentSceneElements powinien być dodany po UI, żeby być nad nim // Rozpocznij muzykę w tle LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); // Rozpocznij od ekranu tytułowego zarządzanego przez ten obiekt gameState this.showTitleScreen(); }, // Tworzy/odświeża ściany areny bossa createWalls: function createWalls() { // Usuń istniejące ściany, jeśli istnieją walls.forEach(function (wall) { if (wall && wall.destroy) { wall.destroy(); } }); walls = []; // Lewa ściana var leftWall = game.addChild(LK.getAsset('wall', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); walls.push(leftWall); // Prawa ściana var rightWall = game.addChild(LK.getAsset('wall', { anchorX: 0, anchorY: 0, x: 2048 - 100, y: 0 })); walls.push(rightWall); // Górna ściana var topWall = game.addChild(LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 0 })); walls.push(topWall); // Dolna ściana var bottomWall = game.addChild(LK.getAsset('floor', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - 100 })); walls.push(bottomWall); // Upewnij się, że ściany są na odpowiedniej warstwie (pod graczem i bosem) }, // ---------- Metody przejścia między stanami ---------- // Przejście do ekranu tytułowego showTitleScreen: function showTitleScreen() { clearScene(); // Wyczyść elementy poprzedniej sceny (np. z Victory lub Fake Death) // Upewnij się, że timer fake tutorialu jest wyczyszczony if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; } this.currentState = "title"; // Ustaw stan na tytuł // Ukryj obiekty gry (gracz, boss) jeśli istnieją if (player) { player.alpha = 0; } if (boss) { boss.alpha = 0; } // Pokaż ściany jako tło menu/intro (jeśli nie mają być widoczne, ustaw alpha na 0) walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); ui.positionElements("title"); // Pozycjonuj UI dla stanu tytułowego ui.titleText.setText("ROLL SOULS"); // Upewnij się, że tytuł jest poprawny ui.titleText.alpha = 1; // Pokaż tytuł ui.showMessage("Tap to Start", 0); // Wyświetl komunikat startowy ui.showTutorial("Swipe to Roll - Death is Progress"); // Wyświetl tekst tutorialu (można go ukryć i pokazać dopiero w prawdziwym tutorialu) // Resetuj stan touchGesture na wypadek kliknięcia w poprzednim stanie końcowym this.touchStart = { x: 0, y: 0 }; this.touchEnd = { x: 0, y: 0 }; }, // Przejście do pierwszej sceny intro showIntro: function showIntro() { clearScene(); // Wyczyść elementy poprzedniej sceny (Tytuł) // Upewnij się, że timer fake tutorialu jest wyczyszczony if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; } this.currentState = "intro"; // Ustaw stan na intro game.setBackgroundColor(0x111111); // Ciemne tło // Ukryj obiekty gry i ściany na czas intro/tutoriali if (player) { player.alpha = 0; } if (boss) { boss.alpha = 0; } walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ukryj ściany ui.positionElements("intro"); // Ukryj elementy UI walki (zostaw licznik śmierci) // Tekst pierwszej sceny intro (z souls4.txt) var introText1 = new Text2('From the creators of FromSoftware...', { size: 90, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); introText1.anchor.set(0.5, 0.5); introText1.x = 2048 / 2; introText1.y = 2732 / 2; currentSceneElements.addChild(introText1); // Dodaj do kontenera, który będzie czyszczony // Timer 1: Zmień tekst po 5 sekundach LK.setTimeout(function () { // Usuń pierwszy tekst if (introText1.parent) { introText1.destroy(); } // Tekst drugiej sceny intro (z souls4.txt) var introText2 = new Text2('...I have no idea. I don’t know them.', { size: 90, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); introText2.anchor.set(0.5, 0.5); introText2.x = 2048 / 2; introText2.y = 2732 / 2; currentSceneElements.addChild(introText2); // Dodaj do kontenera // Timer 2: Przejdź do fałszywego tutorialu po kolejnych 2 sekundach LK.setTimeout(function () { // clearScene() zostanie wywołane na początku następnej metody, aby usunąć ten tekst gameState.showFakeTutorial(); // Przejdź do fałszywego tutorialu }, 2000); // 2 sekundy opóźnienia po wyświetleniu drugiego tekstu }, 5000); // 5 sekund opóźnienia przed wyświetleniem drugiego tekstu }, // Przejście do sceny fałszywego tutorialu (Press LPM to block) showFakeTutorial: function showFakeTutorial() { clearScene(); // Wyczyść poprzednie elementy (tekst intro) this.currentState = "fakeTutorial"; // Ustaw stan na fałszywy tutorial game.setBackgroundColor(0x000000); // Czarne tło // Tekst fałszywego tutorialu - Zmieniony na "Press LPM to block" var instructionText = new Text2('Press LPM to block.. or don’t press.', { size: 100, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 2048 / 2; instructionText.y = 2732 / 2; currentSceneElements.addChild(instructionText); // Timer przechodzący do prawdziwego tutorialu - ZAPISZ JEGO ID this.fakeTutorialTimerId = LK.setTimeout(function () { // clearScene() zostanie wywołane na początku następnej metody // Zresetuj ID timera, bo się zakończył gameState.fakeTutorialTimerId = null; // Przejdź do prawdziwego tutorialu gameState.showRealTutorial(); }, 6000); // 6 sekund }, // Obsługa inputu w stanie fakeTutorial (fałszywa śmierć) handleFakeTutorialInput: function handleFakeTutorialInput() { // Tylko obsłuż input, jeśli jesteśmy w stanie fakeTutorial i timer jeszcze działa if (this.currentState !== "fakeTutorial" || !this.fakeTutorialTimerId) { return; } // Wyczyść timer, który miał prowadzić do prawdziwego tutorialu LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; // Zresetuj ID timera // Wyczyść ekran fałszywego tutorialu clearScene(); // Ustaw stan na game over (tymczasowo, żeby logika UI się zgadzała) this.currentState = "gameOver"; // Można użyć dedykowanego stanu 'fakeGameOver' jeśli chcemy inaczej obsłużyć UI // 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 = 800; currentSceneElements.addChild(diedText); // Wyświetl "Did you check the title of the game?" var explanationText = new Text2("Did you check the title of the game?", { size: 60, fill: 0xFFFFFF, // Biały kolor align: 'center', wordWrap: true, wordWrapWidth: 1800 }); explanationText.anchor.set(0.5, 0.5); explanationText.x = 2048 / 2; explanationText.y = 1000; 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 again', { size: 60, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); howToPlayButtonContainer.addChild(buttonText); // Akcja przycisku: Przejdź z powrotem do prawdziwego tutorialu howToPlayButtonContainer.down = function () { gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu }; }, // Przejście do sceny prawdziwego tutorialu showRealTutorial: function showRealTutorial() { clearScene(); // Wyczyść poprzednie elementy (fałszywa śmierć lub fałszywy tutorial) // Upewnij się, że timer fake tutorialu jest wyczyszczony if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; } this.currentState = "realTutorial"; // Ustaw stan na prawdziwy tutorial game.setBackgroundColor(0x1a1a1a); // Ciemne tło var yOffset = 2732 / 3; // Początkowa pozycja Y dla tekstu // Tekst tutorialu (zmodyfikowany do mechaniki stworzylo zloto.txt) var instructionText1 = new Text2('Swipe in a direction to ROLL.', { size: 80, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); instructionText1.anchor.set(0.5, 0.5); instructionText1.x = 2048 / 2; instructionText1.y = yOffset; currentSceneElements.addChild(instructionText1); var instructionText2 = new Text2('There is a short cooldown after each roll.', { size: 80, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); instructionText2.anchor.set(0.5, 0.5); instructionText2.x = 2048 / 2; instructionText2.y = yOffset + 150; currentSceneElements.addChild(instructionText2); var instructionText3 = new Text2('That\'s it. That\'s the whole game.', { size: 80, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); instructionText3.anchor.set(0.5, 0.5); instructionText3.x = 2048 / 2; instructionText3.y = yOffset + 300; currentSceneElements.addChild(instructionText3); // Przycisk "Let's Roll" (używamy button_bg) var rollButtonContainer = new Container(); rollButtonContainer.interactive = true; // Przycisk jest interaktywny rollButtonContainer.x = 2048 / 2; rollButtonContainer.y = yOffset + 600; currentSceneElements.addChild(rollButtonContainer); var buttonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); rollButtonContainer.addChild(buttonBg); var rollButtonText = new Text2('Let\'s Roll', { size: 80, fill: 0xFFFFFF }); rollButtonText.anchor.set(0.5, 0.5); rollButtonContainer.addChild(rollButtonText); // Akcja przycisku: Rozpocznij walkę z bossem (przejdź do stanu gry) rollButtonContainer.down = function () { // Wyczyść elementy intro przed rozpoczęciem gry clearScene(); gameState.startGame(); // Wywołaj metodę rozpoczynającą grę (walkę z bossem) }; }, // Przejście do stanu gry (walka z bossem) startGame: function startGame() { // Ta metoda rozpoczyna faktyczną walkę z bossem // Upewnij się, że timer fake tutorialu jest wyczyszczony if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; } this.currentState = "game"; // Ustaw stan na "game" // Pokaż obiekty gry (gracz, boss), ukryte w intro/tutorialu // Upewnij się, że istnieją przed ustawieniem alpha if (player) { player.alpha = 1; } if (boss) { boss.alpha = 1; } // Pokaż ściany areny walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); ui.positionElements("game"); // Pozycjonuj UI dla stanu gry (ikony serc, licznik śmierci, timer) // Stwórz gracza (jeśli nie istnieje lub zniszcz poprzedniego) if (player) { player.destroy(); } // Zniszcz poprzedniego gracza jeśli był player = game.addChild(new Player()); // Stwórz nowego gracza // Ustaw początkowe zdrowie gracza na podstawie storage.maxHearts (domyślnie 5 na start aplikacji, może wzrosnąć po śmierciach) player.health = storage.maxHearts || 5; // Jawnie zresetuj stan turlania i nietykalności gracza na początku walki player.rolling = false; player.invulnerable = false; player.invulnerabilityFrames = 0; player.rollCooldown = 0; player.clearRollTimeouts(); // Wyczyść timery turlania/nietykalności player.x = 2048 / 2; player.y = 2732 / 2 + 400; // Pozycja startowa gracza // Stwórz bossa (jeśli nie istnieje lub zniszcz poprzedniego) if (boss) { boss.destroy(); } // Zniszcz poprzedniego bossa jeśli był boss = game.addChild(new Boss()); // Stwórz nowego bossa boss.x = 2048 / 2; boss.y = 2732 / 2 - 400; // Pozycja startowa bossa // Upewnij się, że boss zaczyna w stanie gotowości do ataku z wyczyszczonymi poprzednimi atakami boss.attackCooldown = 0; // Boss zaatakuje po pierwszym opóźnieniu boss.attacks = []; // Wyczyść wszelkie pozostałości po poprzednich atakach boss.attackSpeedMultiplier = 1; // Ustaw początkowy mnożnik prędkości ataków na 1 (normalny) // Aktualizuj UI (wyświetl serca i początkowy komunikat) ui.updateHearts(player.health, storage.maxHearts); // Wyświetl aktualne serca (powinno być 5) ui.showTutorial("Swipe to roll away from attacks!"); // Wyświetl tutorial dla stanu gry // --- Timer walki z bossem --- this.remainingTime = this.gameDuration; // Ustaw początkowy czas (120 sekund) ui.updateTimerDisplay(this.remainingTime); // Wyświetl początkowy czas // Rozpocznij interwał timera // Wyczyść poprzedni interwał timera, jeśli istniał (przy restartach gry) if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } // Zresetuj flagę przyspieszenia bossa na początku nowej gry this.bossSpeedIncreased = false; this.gameTimerInterval = LK.setInterval(function () { // Tylko aktualizuj timer i sprawdzaj warunek zwycięstwa jeśli jesteśmy w stanie gry if (gameState.currentState === "game") { gameState.remainingTime--; // Zmniejsz pozostały czas ui.updateTimerDisplay(gameState.remainingTime); // Zaktualizuj wyświetlanie timera na UI // --- Przyspieszenie bossa po 1 minucie --- // Jeśli pozostały czas <= 60 sekund i boss jeszcze nie został przyspieszony if (gameState.remainingTime <= 60 && !gameState.bossSpeedIncreased) { gameState.bossSpeedIncreased = true; // Ustaw flagę if (boss && !boss.dead) { // Upewnij się, że boss istnieje i żyje // Zmniejsz mnożnik prędkości ataków bossa (np. o 30% -> mnożnik 0.7) boss.attackSpeedMultiplier = 0.7; // Opcjonalnie: wyświetl komunikat o przyspieszeniu ui.showMessage("Boss attacks faster!", 2000); // Komunikat na 2 sekundy } } // --- Koniec Przyspieszenia bossa --- // Sprawdź warunek zwycięstwa (czas minął) if (gameState.remainingTime <= 0) { LK.clearInterval(gameState.gameTimerInterval); // Zatrzymaj interwał timera // Obsłuż zwycięstwo przez przetrwanie // Boss cleanup jest obsługiwany w gameState.victory() gameState.victory(); // Przejdź do stanu zwycięstwa } } }, 1000); // Interwał co 1 sekundę (1000 ms) // --- Koniec Timer walki z bossem --- // Zaplanuj rozpoczęcie ataków bossa po krótkim opóźnieniu // Nie potrzebujemy jawnie czyścić timeoutu, bo startAttackPattern ma sprawdzenie stanu gry LK.setTimeout(function () { // Sprawdź, czy nadal jesteśmy w stanie gry gdy timeout minie i czy boss istnieje i żyje if (gameState.currentState === "game" && boss && !boss.dead) { ui.hideTutorial(); // Ukryj tekst tutorialu boss.startAttackPattern(); // Rozpocznij ataki bossa } }, 3000); // 3 sekundy opóźnienia przed pierwszym atakiem bossa }, // Przejście do stanu Game Over (wywoływane przez player.die()) gameOver: function gameOver() { this.currentState = "gameOver"; // Ustaw stan na game over // Zatrzymaj timer gry, jeśli nadal działa LK.clearInterval(this.gameTimerInterval); // Upewnij się, że interwał timera jest wyczyszczony // Zresetuj flagę przyspieszenia bossa this.bossSpeedIncreased = false; // Ukryj obiekty gry (są już niszczone, ale to dodatkowe zabezpieczenie) // Upewnij się, że istnieją przed ustawieniem alpha if (player) { player.alpha = 0; } if (boss) { boss.alpha = 0; } // Ukryj ściany walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ustaw pozycje UI dla ekranu game over ui.positionElements("gameOver"); // Wyświetl komunikat YOU DIED ui.showMessage("YOU DIED", 3000); // Komunikat zniknie po 3 sekundach ui.titleText.setText("YOU DIED"); // Ustaw tekst tytułu na "YOU DIED" ui.titleText.alpha = 1; // Pokaż tytuł "YOU DIED" ui.showTutorial(""); // Ukryj tutorial // Zaplanuj restart gry po opóźnieniu (powrót do stanu startGame) LK.setTimeout(function () { // Clean up player and boss (gdyby nie zostały zniszczone wcześniej - np. w player.die/boss.die) if (player) { player.destroy(); player = null; } if (boss) { boss.destroy(); boss = null; } // Wyczyść wszystkie aktywne ataki bossa i ich wizualizacje if (boss && boss.attacks) { // Sprawdź czy boss i jego ataki istniały przed zniszczeniem boss.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); boss.attacks = []; } // Resetuj UI do stanu tytułowego przed potencjalnym restartem ui.positionElements("title"); // Pozycjonuj UI jak dla tytułu ui.titleText.alpha = 0; // Ukryj tytuł ponownie ui.showMessage(""); // Wyczyść komunikat ui.showTutorial(""); // Wyczyść tutorial ui.updateDeathsCounter(); // Upewnij się, że licznik śmierci jest aktualny // Restartuj walkę z bossem bezpośrednio po Game Over gameState.startGame(); // Przejdź z powrotem do stanu gry (rozpocznij nową próbę) }, 3000); // 3 sekundy opóźnienia przed restartem }, // Przejście do stanu zwycięstwa (wywoływane przez boss.die() lub timer) victory: function victory() { this.currentState = "victory"; // Ustaw stan na zwycięstwo // Zatrzymaj timer gry, jeśli nadal działa (redundantne jeśli wygrana czasem, ale bezpieczniej) LK.clearInterval(this.gameTimerInterval); // Zresetuj flagę przyspieszenia bossa this.bossSpeedIncreased = false; // Clean up player (już niszczony w player.die) if (player) { player.destroy(); player = null; } // --- Boss Cleanup (jeśli wygrana była przez timer, a nie przez boss.die) --- // Jeśli obiekt bossa nadal istnieje i nie jest oznaczony jako martwy (czyli nie umarł od obrażeń), zniszcz go i jego ataki if (boss && !boss.dead) { // Wyczyść aktywne ataki bossa i ich wizualizacje if (boss.attacks) { boss.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); boss.attacks = []; } // Zniszcz obiekt bossa if (boss.destroy && !boss.destroyed) { boss.destroy(); } boss = null; // Wyczyść globalną referencję } // --- Koniec Boss Cleanup --- ui.positionElements("victory"); // Pozycjonuj UI dla stanu zwycięstwa ui.titleText.setText("VICTORY ACHIEVED"); // Ustaw tekst tytułu ui.titleText.alpha = 1; // Pokaż tytuł ui.showMessage("Bosses Defeated: " + storage.bossesDefeated, 0); // Pokaż komunikat o pokonanych bossach (zostaje na ekranie) ui.showTutorial("Tap to Continue"); // Pokaż komunikat "Tap to Continue" // Zaplanuj przejście do ekranu tytułowego po opóźnieniu LK.setTimeout(function () { ui.showMessage(""); // Wyczyść komunikat o pokonanych bossach ui.showTutorial(""); // Wyczyść tutorial gameState.showTitleScreen(); // Przejdź z powrotem do ekranu tytułowego }, 5000); // 5 sekund opóźnienia }, // Obsługa gestów dotykowych/myszy w zależności od stanu gry processTouchGesture: function processTouchGesture() { // --- Obsługa inputu w stanie fakeTutorial (fałszywa śmierć) --- // Jeśli jesteśmy w stanie fakeTutorial i timer do prawdziwego tutorialu jeszcze działa, // każdy input (tapnięcie lub swipe) wywoła fałszywą śmierć. if (this.currentState === "fakeTutorial" && this.fakeTutorialTimerId) { this.handleFakeTutorialInput(); // Wywołaj logikę fałszywej śmierci return; // Zakończ przetwarzanie gestu } // Oblicz kierunek i dystans gestu na podstawie zarejestrowanych pozycji startu i końca dotyku var dx = this.touchEnd.x - this.touchStart.x; var dy = this.touchEnd.y - this.touchStart.y; var distance = Math.sqrt(dx * dx + dy * dy); // --- Obsługa tapowania/gestów w stanach poza grą --- // Sprawdź, czy to było krótkie tapnięcie (dystans mniejszy niż próg turlania) if (distance < 50) { // Próg turlania to 50 jednostek // Jeśli jest to tapnięcie w stanie tytułowym, przejdź do intro if (this.currentState === "title") { this.showIntro(); // <--- Tap na ekranie tytułowym przechodzi do intro return; // Zakończ przetwarzanie gestu } // Jeśli jest to tapnięcie w stanie zwycięstwa, przejdź do ekranu tytułowego if (this.currentState === "victory") { this.showTitleScreen(); // <--- Tap na ekranie zwycięstwa wraca do tytułu return; // Zakończ przetwarzanie gestu } // Jeśli to tapnięcie w innych stanach (intro, real tutorial, game over), zignoruj lub dodaj logikę jeśli potrzebna // W real tutorialu tapnięcie nie wywoła nic poza przyciskiem "Let's Roll" // W game over restart jest timerem return; // Zakończ przetwarzanie gestu jeśli to tapnięcie i nie obsłużono stanu } // --- Obsługa turlania w stanie gry --- // Tylko przetwarzaj gesty turlania (swipe) w stanie "game" i gdy gracz nie jest martwy if (this.currentState !== "game" || !player || player.dead) { return; // Zignoruj gesty swipe w innych stanach } // W tym miejscu wiemy, że currentState === "game", gracz żyje, i dystans gestu jest >= 50 (to swipe) // Minimalny dystans swipe dla turlania (już sprawdzony powyżej) // Normalizuj kierunek gestu var direction = { x: dx / distance, y: dy / distance }; // Wykonaj turlanie gracza player.roll(direction); } }; // --- Obsługa inputu (mapowanie zdarzeń LK na metody gameState) --- game.down = function (x, y, obj) { // Zarejestruj początek dotyku/kliknięcia tylko w stanach, które tego wymagają if (gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "game" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial") { // Dodano stany tutoriali gameState.touchStart.x = x; gameState.touchStart.y = y; // Zresetuj end point na początku gestu gameState.touchEnd.x = x; gameState.touchEnd.y = y; } }; game.up = function (x, y, obj) { // Zarejestruj koniec dotyku/kliknięcia tylko w stanach, które tego wymagają if (gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "game" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial") { // Dodano stany tutoriali gameState.touchEnd.x = x; gameState.touchEnd.y = y; // Przetwórz zarejestrowany gest (tap lub swipe) gameState.processTouchGesture(); // Wywołaj metodę przetwarzającą gest } }; game.move = function (x, y, obj) { // Śledź aktualną pozycję dotyku/kursora, aby obliczyć dystans gestu na koniec if (gameState.currentState === "game" || gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial") { // Śledź ruch w stanach, gdzie gesty są istotne gameState.touchEnd.x = x; gameState.touchEnd.y = y; } }; // --- Główna pętla aktualizacji gry --- game.update = function () { // Aktualizuj UI (np. licznik śmierci) niezależnie od stanu gry if (ui) { ui.updateDeathsCounter(); // Pozycjonowanie elementów UI jest obsługiwane przez positionElements wywoływane w metodach stanów // Komunikaty i tytuły są obsługiwane przez showMessage/showTutorial // Timer jest aktualizowany w interwale timera i wyświetlany przez updateTimerDisplay } // Wykonaj logikę gry (aktualizacja gracza, bossa, ataków) tylko w stanie "game" if (gameState.currentState === "game") { if (player) { player.update(); // Aktualizacja gracza (ruch, turlanie, nietykalność) } if (boss) { boss.update(); // Aktualizacja bossa (ruch, ataki, kolizje ataków z graczem) } // Aktualizuj wizualizację serc na UI (ma sens tylko w stanie gry) if (player && ui) { ui.updateHearts(player.health, storage.maxHearts); } // Kolizje ataków bossa z graczem i przejścia do stanów game over/victory // są obsługiwane wewnątrz metod update klas Boss i Player, wywołując odpowiednie metody gameState. } // W innych stanach (title, intro, tutoriale, game over, victory) logika gry (ruch obiektów, kolizje) nie jest wykonywana. }; // --- Rozpoczęcie gry --- // Zamiast wywoływania konkretnej sceny, inicjalizujemy obiekt gameState // i wywołujemy jego metodę init(), która ustawia początkowy stan i rozpoczyna od ekranu tytułowego. gameState.init();
===================================================================
--- original.js
+++ change.js
@@ -23,8 +23,9 @@
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.startAttackPattern = function () {
if (self.dead || gameState.currentState !== "game") {
// Ataki tylko w stanie gry
return;
@@ -40,10 +41,10 @@
case 2:
self.chargeAttack();
break;
}
- // Ustaw następny atak z losowym opóźnieniem
- self.attackCooldown = 90 + Math.floor(Math.random() * 60); // 1.5-2.5 seconds (90-150 frames przy 60 FPS)
+ // Ustaw następny atak z losowym opóźnieniem, skalowane przez mnożnik prędkości
+ self.attackCooldown = (90 + Math.floor(Math.random() * 60)) * self.attackSpeedMultiplier; // 1.5-2.5 seconds (90-150 frames) * multiplier
};
self.circleAttack = function () {
LK.getSound('bossAttack').play();
var center = {
@@ -55,9 +56,9 @@
for (var i = 0; i < count; i++) {
var angle = i / count * Math.PI * 2;
var posX = center.x + Math.cos(angle) * radius;
var posY = center.y + Math.sin(angle) * radius;
- self.createAttack(posX, posY, 3000); // Utwórz atak w danej pozycji z czasem życia
+ self.createAttack(posX, posY, 3000); // Utwórz atak w danej pozycji z czasem życia (skalowane w createAttack)
}
};
self.lineAttack = function () {
LK.getSound('bossAttack').play();
@@ -68,14 +69,14 @@
for (var i = 0; i < count; i++) {
var t = i / (count - 1); // Współczynnik interpolacji
var posX = self.x + (targetX - self.x) * t;
var posY = self.y + (targetY - self.y) * t;
- var delay = i * 200; // Opóźnienie dla kolejnych ataków w linii
+ var delay = i * 200 * self.attackSpeedMultiplier; // Opóźnienie dla kolejnych ataków w linii, skalowane
LK.setTimeout(function (x, y) {
return function () {
// Sprawdź, czy boss nadal żyje i gra jest w stanie gry zanim stworzysz atak
if (self && !self.dead && gameState.currentState === "game") {
- self.createAttack(x, y, 2000); // Utwórz atak po opóźnieniu
+ self.createAttack(x, y, 2000); // Utwórz atak po opóźnieniu (czas życia skalowane w createAttack)
}
};
}(posX, posY), delay);
}
@@ -98,10 +99,10 @@
x: self.x + dx * 500,
// Przesunięcie o 500 jednostek w kierunku gracza
y: self.y + dy * 500
}, {
- duration: 800,
- // Czas trwania szarży
+ duration: 800 * self.attackSpeedMultiplier,
+ // Czas trwania szarży, skalowane
easing: tween.easeIn,
onFinish: function onFinish() {
// Sprawdź, czy boss nadal żyje i gra jest w stanie gry
if (self && !self.dead && gameState.currentState === "game") {
@@ -109,17 +110,18 @@
tween(self, {
x: startX,
y: startY
}, {
- duration: 1000,
+ duration: 1000 * self.attackSpeedMultiplier,
+ // Czas powrotu, skalowane
easing: tween.easeOut
});
// Utwórz ataki wzdłuż ścieżki szarży
for (var i = 0; i < 5; i++) {
var t = i / 4;
var posX = startX + (self.x - startX) * t;
var posY = startY + (self.y - startY) * t;
- self.createAttack(posX, posY, 1500);
+ self.createAttack(posX, posY, 1500); // Czas życia skalowane w createAttack
}
}
}
});
@@ -134,24 +136,26 @@
alpha: 0.3,
// Niska przezroczystość na początku
tint: 0xFFFF00 // Żółty kolor ostrzeżenia
}));
- // Animacja ostrzeżenia (miganie)
+ // Animacja ostrzeżenia (miganie), skalowana przez mnożnik prędkości
tween(warning, {
alpha: 0.6 // Zwiększ przezroczystość
}, {
- duration: 1000,
+ duration: 1000 * self.attackSpeedMultiplier,
+ // Skalowany czas trwania
easing: tween.easeInOut,
onFinish: function onFinish() {
// Sprawdź, czy warning nadal istnieje przed zmianą koloru
if (warning && !warning.destroyed) {
// Zmień kolor na czerwony, sygnalizując faktyczny atak
warning.tint = 0xFF0000;
- // Animacja sygnalizująca gotowość ataku
+ // Animacja sygnalizująca gotowość ataku, skalowana przez mnożnik prędkości
tween(warning, {
alpha: 0.8 // Zwiększ przezroczystość bardziej
}, {
- duration: 200,
+ duration: 200 * self.attackSpeedMultiplier,
+ // Skalowany czas trwania
onFinish: function onFinish() {
// Sprawdź, czy warning nadal istnieje
if (warning && !warning.destroyed) {
// Dodaj atak do listy aktywnych ataków bossa (jako obiekt danych)
@@ -162,23 +166,12 @@
radius: warning.width / 2,
// Promień kolizji (połówka szerokości)
visual: warning,
// Referencja do obiektu wizualnego
- lifeTime: duration // Czas życia ataku w klatkach
+ lifeTime: duration * self.attackSpeedMultiplier * (60 / 1000) // Czas życia ataku w klatkach, skalowany. Przeliczamy ms na klatki (zakładając 60 FPS)
};
self.attacks.push(attack);
- // Zaplanuj usunięcie ataku po jego czasie życia
- LK.setTimeout(function () {
- // Sprawdź, czy atak nadal istnieje w tablicy przed usunięciem
- var index = self.attacks.indexOf(attack);
- if (index !== -1) {
- self.attacks.splice(index, 1); // Usuń z listy ataków
- }
- // Sprawdź, czy wizualizacja ataku nadal istnieje przed zniszczeniem
- if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
- attack.visual.destroy(); // Zniszcz obiekt wizualny
- }
- }, duration);
+ // !!! USUNIĘTO LK.setTimeout, USUWANIE BĘDZIE TYLKO W BOSS.update() !!!
} // if warning exists
}
});
} // if warning exists
@@ -195,9 +188,9 @@
LK.effects.flashObject(self, 0xFFFFFF, 200);
// Przejście fazy bossa przy 50% zdrowia
if (self.health <= self.maxHealth / 2 && self.phase === 1) {
self.phase = 2;
- self.speed += 2; // Boss staje się szybszy
+ self.speed += 2; // Boss staje się szybszy (ruch)
// Wizualne przejście fazy (np. zmiana koloru)
tween(self, {
tint: 0xFF3300 // Pomarańczowy/czerwony odcień
}, {
@@ -234,86 +227,109 @@
gameState.victory(); // Przejdź do stanu zwycięstwa (wywoła metodę w gameState)
}
});
// Zatrzymaj wszystkie timery związane z atakami bossa
- // Nie ma bezpośredniej listy timerów, ale zatrzymanie głównej pętli gry i usunięcie ataków poniżej też pomaga
+ // Ataki zostaną wyczyszczone w gameState.victory/gameOver
};
self.update = function () {
- // Aktualizuj tylko w stanie gry
- if (gameState.currentState !== "game") {
+ // Aktualizuj tylko w stanie gry lub gdy boss umiera (do zakończenia animacji)
+ if (gameState.currentState !== "game" && !self.dead) {
+ // Jeśli zmieniono stan gry i boss nie umiera, wyczyść pozostałe ataki
+ if (self.attacks) {
+ self.attacks.forEach(function (attack) {
+ if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
+ attack.visual.destroy();
+ }
+ });
+ self.attacks = []; // Wyczyść tablicę ataków
+ }
return;
}
- // Nie aktualizuj jeśli boss jest martwy (poza animacją śmierci)
+ // Nie aktualizuj jeśli boss jest martwy i animacja śmierci dobiegła końca (obiekt niewidoczny)
if (self.dead && self.alpha === 0) {
- // Gdy animacja śmierci dobiegła końca (obiekt niewidoczny)
+ // Jeśli boss jest całkowicie niewidoczny po śmierci, wyczyść pozostałe ataki
+ if (self.attacks) {
+ self.attacks.forEach(function (attack) {
+ if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
+ attack.visual.destroy();
+ }
+ });
+ self.attacks = []; // Wyczyść tablicę ataków
+ }
return;
}
- // Jeśli boss jest martwy, ale nadal widoczny, pozwól animacji zanikania działać (obsługiwane przez tween)
- if (self.dead) {
- // Można by tu dodać inną logikę dla stanu śmierci jeśli potrzeba
- // Nadal przetwarzaj ataki, żeby zanikły po śmierci bossa
- }
- // Aktualizacja cooldownu ataku
- if (self.attackCooldown > 0) {
- self.attackCooldown--;
- if (self.attackCooldown === 0) {
- self.startAttackPattern(); // Rozpocznij nowy wzorzec ataku gdy cooldown minie
+ // Logika ruchu tylko gdy boss żyje i gra jest w stanie gry
+ if (!self.dead && gameState.currentState === "game") {
+ // Aktualizacja cooldownu ataku
+ if (self.attackCooldown > 0) {
+ self.attackCooldown--;
+ // !!! Zmieniono sprawdzanie cooldownu z === 0 na <= 0 !!!
+ if (self.attackCooldown <= 0) {
+ // Use <= 0 for robustness with floating point
+ self.attackCooldown = 0; // Reset cooldown to exactly 0 after triggering attack
+ self.startAttackPattern(); // This is where the next attack pattern starts
+ }
}
+ // ... Movement logic ...
}
- // Poruszaj się w kierunku gracza jeśli boss nie jest oszołomiony i żyje
- if (!self.stunned && !self.dead) {
- var dx = player.x - self.x;
- var dy = player.y - self.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance > 200) {
- // Utrzymuj pewien dystans od gracza
- self.x += dx / distance * self.speed;
- self.y += dy / distance * self.speed;
- }
- } else {
- // Aktualizacja czasu oszołomienia (stun)
- self.stunDuration--;
- if (self.stunDuration <= 0) {
- self.stunned = false;
- }
- }
- // Przetwarzaj aktywne ataki bossa (sprawdzaj kolizje z graczem)
+ // Przetwarzaj aktywne ataki bossa (sprawdzaj kolizje z graczem i usuwaj po czasie życia)
+ // Przetwarzaj ataki niezależnie od tego czy boss żyje, żeby pozostałe ataki po śmierci też znikały
for (var i = self.attacks.length - 1; i >= 0; i--) {
var attack = self.attacks[i];
- // Upewnij się, że obiekt ataku, wizualizacja i gracz istnieją, gracz żyje i nie jest nietykalny
- if (!attack || !attack.visual || !player || player.dead || player.invulnerable) {
- continue; // Pomiń przetwarzanie
+ // Upewnij się, że obiekt ataku i wizualizacja istnieją
+ if (!attack || !attack.visual) {
+ // Usuń z listy jeśli obiekt jest nieprawidłowy
+ self.attacks.splice(i, 1);
+ console.warn("Invalid attack object found in Boss.attacks:", attack);
+ continue;
}
- attack.lifeTime--;
- // Sprawdzenie kolizji z graczem (system kolizji z stworzylo zloto.txt - oparty na odległości między środkami)
- var dx = player.x - attack.x;
- var dy = player.y - attack.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- // Promień gracza (połówka szerokości, zakładamy gracza jako okrąg do kolizji z atakami bossa)
- var playerRadius = player.width / 2; // Używamy szerokości z obiektu gracza
- if (distance < attack.radius + playerRadius) {
- // Kolizja wykryta
- // player.takeDamage(1) zwraca true jeśli gracz umrze po otrzymaniu obrażeń
- if (player.takeDamage(1)) {
- // Gracz umarł, obsługa game over nastąpi poprzez wywołanie gameState.gameOver() przez player.die()
+ attack.lifeTime--; // Zmniejsz czas życia
+ // Sprawdzenie kolizji z graczem i otrzymywanie obrażeń - tylko gdy gracz żyje, nie jest nietykalny i gra jest w stanie "game"
+ if (player && !player.dead && !player.invulnerable && gameState.currentState === "game") {
+ // Sprawdzenie kolizji z graczem (system kolizji z stworzylo zloto.txt - oparty na odległości między środkami)
+ var dx = player.x - attack.x;
+ var dy = player.y - attack.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ // Promień gracza (połówka szerokości)
+ var playerRadius = player && player.width ? player.width / 2 : 0;
+ if (distance < attack.radius + playerRadius) {
+ // Kolizja wykryta
+ // player.takeDamage(1) zwraca true jeśli gracz umrze po otrzymaniu obrażeń
+ if (player.takeDamage(1)) {
+ // Gracz umarł, obsługa game over nastąpi poprzez wywołanie gameState.gameOver() przez player.die()
+ }
+ // Wizualne sygnalizowanie trafienia atakiem (migotanie)
+ // Sprawdź, czy wizualizacja nadal istnieje
+ if (attack.visual && !attack.visual.destroyed) {
+ LK.effects.flashObject(attack.visual, 0xFFFFFF, 200);
+ }
+ // Atak zazwyczaj trafia tylko raz. Możemy go usunąć po trafieniu.
+ // Usuniemy go jednak po czasie życia, jak było w oryginalnym kodzie stworzylo_zloto.
}
- // Wizualne sygnalizowanie trafienia atakiem (migotanie)
- // Sprawdź, czy wizualizacja nadal istnieje
- if (attack.visual && !attack.visual.destroyed) {
- LK.effects.flashObject(attack.visual, 0xFFFFFF, 200);
- }
- // Nie usuwamy ataku od razu, usuwany jest po czasie życia (lifeTime)
- }
+ } // Koniec sprawdzania kolizji tylko w stanie gry
// Usuń ataki, których czas życia minął
if (attack.lifeTime <= 0) {
- // Sprawdź, czy atak nadal istnieje w tablicy przed usunięciem
- var index = self.attacks.indexOf(attack);
- if (index !== -1) {
- self.attacks.splice(index, 1);
- }
- // Sprawdź, czy wizualizacja ataku nadal istnieje przed zniszczeniem
+ // Usuń z tablicy active attacks bossa
+ self.attacks.splice(i, 1);
+ // Zainicjuj animację zanikania i zniszczenie obiektu wizualnego
+ // Check if the visual object exists and hasn't been destroyed
if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
- attack.visual.destroy(); // Zniszcz obiekt wizualny
+ // Rozpocznij animację zanikania
+ tween(attack.visual, {
+ alpha: 0
+ }, {
+ duration: 300 // Czas trwania zanikania w ms
+ // No onFinish here, destruction is scheduled by timeout
+ });
+ // Schedule destruction of the visual object after the fade out duration
+ LK.setTimeout(function () {
+ if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
+ attack.visual.destroy();
+ }
+ }, 300); // Match the fade out duration
+ } else {
+ // If visual object is somehow missing or already destroyed, log a warning
+ console.warn("Expired attack visual missing or already destroyed:", attack);
}
}
}
};
@@ -324,9 +340,9 @@
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
- // Pobieramy szerokość i wysokość z grafiki gracza, tak jak w souls4.txt
+ // Pobieramy szerokość i wysokość z grafiki gracza
self.width = playerGraphics.width;
self.height = playerGraphics.height;
self.speed = 12;
self.rolling = false; // Czy gracz się turla
@@ -362,29 +378,34 @@
alpha: 0.7
}));
LK.getSound('roll').play(); // Dźwięk turlania
// Zaplanuj koniec turlania i cooldown
- LK.setTimeout(function () {
+ // Użyjemy self.rollEffectTimeoutId, żeby móc go wyczyścić przy restarcie lub śmierci
+ self.rollEffectTimeoutId = LK.setTimeout(function () {
// Sprawdź, czy gracz nadal istnieje i nie jest martwy zanim zakończysz turlanie
- if (self && !self.dead && !self.rolling) {
- // Sprawdź czy rolling nie zostało ponownie ustawione
- self.rolling = false;
- self.rollCooldown = 30; // 30 frames cooldown (ok. 0.5 sekundy przy 60 FPS)
- // Mały bufor nietykalności po zakończeniu turlania
- LK.setTimeout(function () {
- // Sprawdź, czy gracz nadal żyje i nie zaczął nowego turlania
- if (!self.rolling && !self.dead) {
- self.invulnerable = false;
+ if (self && !self.dead) {
+ // Sprawdź, czy gracz nadal jest w stanie turlania ustawionym przez ten timeout
+ if (self.rolling) {
+ // Upewnij się, że flaga rolling nie została zmieniona przez nowe turlanie
+ self.rolling = false; // Zakończ stan turlania
+ self.rollCooldown = 30; // Ustaw cooldown
+ // Mały bufor nietykalności po zakończeniu turlania
+ // Użyjemy self.invulnerabilityTimeoutId
+ self.invulnerabilityTimeoutId = LK.setTimeout(function () {
+ // Sprawdź, czy gracz nadal żyje i nie zaczął nowego turlania
+ if (!self.rolling && !self.dead) {
+ self.invulnerable = false; // Zakończ nietykalność
+ }
+ }, 100); // 100ms dodatkowej nietykalności
+ } else {
+ // Jeśli flaga rolling została już zmieniona (np. rozpoczęto nowe turlanie), tylko ustaw cooldown
+ if (!self.dead) {
+ self.rollCooldown = 30;
}
- }, 100); // 100ms dodatkowej nietykalności po rollCooldown
- } else {
- // Jeśli gracz już się turlął ponownie lub umarł, po prostu ustaw cooldown
- if (!self.dead) {
- self.rollCooldown = 30;
}
}
// Animacja zanikania efektu turlania i zniszczenie obiektu wizualnego
- // Sprawdź, czy efekt turlania nadal istnieje
+ // Sprawdź, czy efekt turlania nadal istnieje i nie został zniszczony
if (rollEffect && rollEffect.destroy && !rollEffect.destroyed) {
tween(rollEffect, {
alpha: 0
}, {
@@ -395,10 +416,22 @@
}
}
});
}
+ self.rollEffectTimeoutId = null; // Wyczyść ID timeoutu po zakończeniu
}, self.rollDuration * (1000 / 60)); // Czas trwania turlania w milisekundach
};
+ // Metoda do czyszczenia timeoutów turlania/nietykalności (używane przy starcie gry/śmierci)
+ self.clearRollTimeouts = function () {
+ if (self.rollEffectTimeoutId) {
+ LK.clearTimeout(self.rollEffectTimeoutId);
+ self.rollEffectTimeoutId = null;
+ }
+ if (self.invulnerabilityTimeoutId) {
+ LK.clearTimeout(self.invulnerabilityTimeoutId);
+ self.invulnerabilityTimeoutId = null;
+ }
+ };
// Funkcja otrzymywania obrażeń
self.takeDamage = function (amount) {
// Nie można otrzymać obrażeń jeśli gracz jest nietykalny, martwy lub nie jesteśmy w stanie gry
if (self.invulnerable || self.dead || gameState.currentState !== "game") {
@@ -425,8 +458,9 @@
return;
}
self.dead = true;
LK.getSound('death').play(); // Dźwięk śmierci
+ self.clearRollTimeouts(); // Wyczyść timery turlania/nietykalności przy śmierci
// Wizualny efekt śmierci gracza (zanikanie i obrót)
tween(self, {
alpha: 0,
rotation: Math.PI * 4
@@ -451,9 +485,9 @@
gameState.gameOver(); // Wywołaj metodę game over w gameState
}, 2000); // 2 sekundy opóźnienia
};
self.update = function () {
- // Aktualizuj tylko w stanie gry
+ // Aktualizuj logikę gracza tylko w stanie gry
if (gameState.currentState !== "game") {
return;
}
// Nie aktualizuj jeśli gracz jest martwy (poza animacją śmierci)
@@ -462,9 +496,8 @@
return;
}
// Jeśli gracz jest martwy, ale nadal widoczny, pozwól animacji zanikania działać (obsługiwane przez tween)
if (self.dead) {
- // Można by tu dodać inną logikę dla stanu śmierci jeśli potrzeba
return; // Nie wykonuj logiki ruchu i nietykalności jeśli gracz jest martwy
}
// Obsługa cooldownu turlania
if (self.rollCooldown > 0) {
@@ -479,10 +512,11 @@
self.invulnerable = false;
self.alpha = 1; // Przywróć pełną przezroczystość gdy nietykalność minie
}
}
- // Obsługa ruchu podczas turlania
+ // Obsługa ruchu podczas turlania - WYKONUJ TYLKO JEŚLI self.rolling JEST TRUE
if (self.rolling) {
+ // Jawne sprawdzenie flagi rolling
self.x += self.rollDirection.x * self.rollSpeed;
self.y += self.rollDirection.y * self.rollSpeed;
}
// Utrzymaj gracza w granicach ekranu (z uwzględnieniem ścian)
@@ -539,9 +573,9 @@
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); // Dodaj do kontenera UI
+ self.addChild(self.timerText);
// Aktualizuje wizualizację serc na UI
self.updateHearts = function (current, max) {
// Usuń istniejące serca
while (self.hearts.length > 0) {
@@ -670,15 +704,17 @@
self.titleText.alpha = 0; // Ukryj tytuł
self.messageText.alpha = 0; // Ukryj komunikat
self.tutorialText.alpha = 0; // Ukryj tutorial
// Licznik śmierci jest zawsze widoczny
+ self.deathsText.x = 2048 - 50; // Upewnij się, że jest w prawym górnym rogu
+ self.deathsText.y = 50;
// Ukryj serca i timer
self.heartContainer.alpha = 0;
self.timerText.alpha = 0;
break;
}
// Upewnij się, że licznik śmierci jest zawsze widoczny
- self.deathsText.alpha = 1; // Można ukryć w jakimś specyficznym stanie jeśli potrzeba
+ self.deathsText.alpha = 1;
};
return self;
});
@@ -691,23 +727,19 @@
/****
* Game Code
****/
-// Zachowujemy na wypadek użycia w animacjach gracza/UI
-// Używamy dźwięku zwycięstwa z stworzylo zloto.txt
-// Dodano z souls4.txt dla przycisków intro
// Globalny kontener na elementy scen intro/tutoriali (do łatwego czyszczenia)
+// Dodano z souls4.txt dla przycisków intro
+// Używamy dźwięku zwycięstwa z stworzylo zloto.txt
+// Zachowujemy na wypadek użycia w animacjach gracza/UI
var currentSceneElements = new Container(); // Dodano z souls4.txt
// Zmienne gry (z stworzylo zloto.txt)
var player;
var boss;
var ui;
var walls = []; // Ściany areny bossa
-// Dodatkowe zmienne dla timera gry (z souls4.txt, ale przeniesione do gameState)
-// var survivalTime = 120; // Przeniesione do gameState
-// var gameTimerInterval; // Przeniesione do gameState
-// var timerText; // Przeniesione do UI
-// Funkcja do czyszczenia elementów z kontenera currentSceneElements (dla scen intro)
+// Funkcja do czyszczenia elementów z kontenera currentSceneElements (dla scen intro/tutoriali)
function clearScene() {
// Dodano z souls4.txt
// Usuń wszystkie dzieci z kontenera
while (currentSceneElements.children.length > 0) {
@@ -731,8 +763,14 @@
remainingTime: 0,
// Pozostały czas
gameTimerInterval: null,
// ID interwału timera
+ // Zmienne dla fałszywego tutorialu
+ fakeTutorialTimerId: null,
+ // ID timera przechodzącego do prawdziwego tutorialu
+ // Zmienne dla przyspieszania bossa
+ bossSpeedIncreased: false,
+ // Flaga informująca czy prędkość bossa została już zwiększona
// Zmienne do śledzenia gestów dotykowych/myszy (dla turlania i tapowania)
touchStart: {
x: 0,
y: 0
@@ -743,10 +781,12 @@
},
// Inicjalizacja gry (wywoływana raz na początku aplikacji)
init: function init() {
// ZRESETUJ LICZNIK ŚMIERCI I USTAW DOMYŚLNĄ ILOŚĆ SERC NA START APLIKACJI
- storage.totalDeaths = 0; // Resetuj licznik śmierci przy każdym uruchomieniu gry (załadowaniu strony)
- storage.maxHearts = 5; // Ustaw początkową liczbę serc na 5
+ // Resetuj licznik śmierci PRZY KAŻDYM URUCHOMIENIU GRY (załadowaniu strony)
+ storage.totalDeaths = 0;
+ // Ustaw początkową liczbę serc na 5 przy każdym uruchomieniu gry
+ storage.maxHearts = 5;
// Stwórz tło
var bg = game.addChild(LK.getAsset('bg', {
anchorX: 0.5,
anchorY: 0.5,
@@ -816,14 +856,18 @@
y: 2732 - 100
}));
walls.push(bottomWall);
// Upewnij się, że ściany są na odpowiedniej warstwie (pod graczem i bosem)
- // Można ręcznie ustawić indeksy Z lub polegać na kolejności dodawania (UI i currentSceneElements dodane na końcu będą na górze)
},
// ---------- Metody przejścia między stanami ----------
// Przejście do ekranu tytułowego
showTitleScreen: function showTitleScreen() {
- clearScene(); // Wyczyść elementy poprzedniej sceny (np. z Victory)
+ clearScene(); // Wyczyść elementy poprzedniej sceny (np. z Victory lub Fake Death)
+ // Upewnij się, że timer fake tutorialu jest wyczyszczony
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ this.fakeTutorialTimerId = null;
+ }
this.currentState = "title"; // Ustaw stan na tytuł
// Ukryj obiekty gry (gracz, boss) jeśli istnieją
if (player) {
player.alpha = 0;
@@ -854,8 +898,13 @@
},
// Przejście do pierwszej sceny intro
showIntro: function showIntro() {
clearScene(); // Wyczyść elementy poprzedniej sceny (Tytuł)
+ // Upewnij się, że timer fake tutorialu jest wyczyszczony
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ this.fakeTutorialTimerId = null;
+ }
this.currentState = "intro"; // Ustaw stan na intro
game.setBackgroundColor(0x111111); // Ciemne tło
// Ukryj obiekty gry i ściany na czas intro/tutoriali
if (player) {
@@ -906,15 +955,15 @@
gameState.showFakeTutorial(); // Przejdź do fałszywego tutorialu
}, 2000); // 2 sekundy opóźnienia po wyświetleniu drugiego tekstu
}, 5000); // 5 sekund opóźnienia przed wyświetleniem drugiego tekstu
},
- // Przejście do sceny fałszywego tutorialu
+ // Przejście do sceny fałszywego tutorialu (Press LPM to block)
showFakeTutorial: function showFakeTutorial() {
clearScene(); // Wyczyść poprzednie elementy (tekst intro)
- this.currentState = "fakeTutorial";
+ this.currentState = "fakeTutorial"; // Ustaw stan na fałszywy tutorial
game.setBackgroundColor(0x000000); // Czarne tło
- // Tekst fałszywego tutorialu (z souls4.txt)
- var instructionText = new Text2('Press E to block. Or don’t press.', {
+ // Tekst fałszywego tutorialu - Zmieniony na "Press LPM to block"
+ var instructionText = new Text2('Press LPM to block.. or don’t press.', {
size: 100,
fill: 0xFFFFFF,
align: 'center',
wordWrap: true,
@@ -923,18 +972,83 @@
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 2048 / 2;
instructionText.y = 2732 / 2;
currentSceneElements.addChild(instructionText);
- // Timer do przejścia do prawdziwego tutorialu
- LK.setTimeout(function () {
+ // Timer przechodzący do prawdziwego tutorialu - ZAPISZ JEGO ID
+ this.fakeTutorialTimerId = LK.setTimeout(function () {
// clearScene() zostanie wywołane na początku następnej metody
- gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu
+ // Zresetuj ID timera, bo się zakończył
+ gameState.fakeTutorialTimerId = null;
+ // Przejdź do prawdziwego tutorialu
+ gameState.showRealTutorial();
}, 6000); // 6 sekund
},
+ // Obsługa inputu w stanie fakeTutorial (fałszywa śmierć)
+ handleFakeTutorialInput: function handleFakeTutorialInput() {
+ // Tylko obsłuż input, jeśli jesteśmy w stanie fakeTutorial i timer jeszcze działa
+ if (this.currentState !== "fakeTutorial" || !this.fakeTutorialTimerId) {
+ return;
+ }
+ // Wyczyść timer, który miał prowadzić do prawdziwego tutorialu
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ this.fakeTutorialTimerId = null; // Zresetuj ID timera
+ // Wyczyść ekran fałszywego tutorialu
+ clearScene();
+ // Ustaw stan na game over (tymczasowo, żeby logika UI się zgadzała)
+ this.currentState = "gameOver"; // Można użyć dedykowanego stanu 'fakeGameOver' jeśli chcemy inaczej obsłużyć UI
+ // 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 = 800;
+ currentSceneElements.addChild(diedText);
+ // Wyświetl "Did you check the title of the game?"
+ var explanationText = new Text2("Did you check the title of the game?", {
+ size: 60,
+ fill: 0xFFFFFF,
+ // Biały kolor
+ align: 'center',
+ wordWrap: true,
+ wordWrapWidth: 1800
+ });
+ explanationText.anchor.set(0.5, 0.5);
+ explanationText.x = 2048 / 2;
+ explanationText.y = 1000;
+ 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 again', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ buttonText.anchor.set(0.5, 0.5);
+ howToPlayButtonContainer.addChild(buttonText);
+ // Akcja przycisku: Przejdź z powrotem do prawdziwego tutorialu
+ howToPlayButtonContainer.down = function () {
+ gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu
+ };
+ },
// Przejście do sceny prawdziwego tutorialu
showRealTutorial: function showRealTutorial() {
- clearScene(); // Wyczyść poprzednie elementy (tekst fałszywego tutorialu)
- this.currentState = "realTutorial";
+ clearScene(); // Wyczyść poprzednie elementy (fałszywa śmierć lub fałszywy tutorial)
+ // Upewnij się, że timer fake tutorialu jest wyczyszczony
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ this.fakeTutorialTimerId = null;
+ }
+ this.currentState = "realTutorial"; // Ustaw stan na prawdziwy tutorial
game.setBackgroundColor(0x1a1a1a); // Ciemne tło
var yOffset = 2732 / 3; // Początkowa pozycja Y dla tekstu
// Tekst tutorialu (zmodyfikowany do mechaniki stworzylo zloto.txt)
var instructionText1 = new Text2('Swipe in a direction to ROLL.', {
@@ -996,10 +1110,16 @@
},
// Przejście do stanu gry (walka z bossem)
startGame: function startGame() {
// Ta metoda rozpoczyna faktyczną walkę z bossem
+ // Upewnij się, że timer fake tutorialu jest wyczyszczony
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ this.fakeTutorialTimerId = null;
+ }
this.currentState = "game"; // Ustaw stan na "game"
// Pokaż obiekty gry (gracz, boss), ukryte w intro/tutorialu
+ // Upewnij się, że istnieją przed ustawieniem alpha
if (player) {
player.alpha = 1;
}
if (boss) {
@@ -1010,75 +1130,95 @@
if (wall) {
wall.alpha = 1;
}
});
- ui.positionElements("game"); // Pozycjonuj UI dla stanu gry (ikony serc, licznik śmierci)
+ ui.positionElements("game"); // Pozycjonuj UI dla stanu gry (ikony serc, licznik śmierci, timer)
// Stwórz gracza (jeśli nie istnieje lub zniszcz poprzedniego)
if (player) {
player.destroy();
} // Zniszcz poprzedniego gracza jeśli był
player = game.addChild(new Player()); // Stwórz nowego gracza
// Ustaw początkowe zdrowie gracza na podstawie storage.maxHearts (domyślnie 5 na start aplikacji, może wzrosnąć po śmierciach)
player.health = storage.maxHearts || 5;
+ // Jawnie zresetuj stan turlania i nietykalności gracza na początku walki
+ player.rolling = false;
+ player.invulnerable = false;
+ player.invulnerabilityFrames = 0;
+ player.rollCooldown = 0;
+ player.clearRollTimeouts(); // Wyczyść timery turlania/nietykalności
player.x = 2048 / 2;
- player.y = 2732 / 2 + 400;
+ player.y = 2732 / 2 + 400; // Pozycja startowa gracza
// Stwórz bossa (jeśli nie istnieje lub zniszcz poprzedniego)
if (boss) {
boss.destroy();
} // Zniszcz poprzedniego bossa jeśli był
boss = game.addChild(new Boss()); // Stwórz nowego bossa
boss.x = 2048 / 2;
- boss.y = 2732 / 2 - 400;
+ boss.y = 2732 / 2 - 400; // Pozycja startowa bossa
+ // Upewnij się, że boss zaczyna w stanie gotowości do ataku z wyczyszczonymi poprzednimi atakami
+ boss.attackCooldown = 0; // Boss zaatakuje po pierwszym opóźnieniu
+ boss.attacks = []; // Wyczyść wszelkie pozostałości po poprzednich atakach
+ boss.attackSpeedMultiplier = 1; // Ustaw początkowy mnożnik prędkości ataków na 1 (normalny)
// Aktualizuj UI (wyświetl serca i początkowy komunikat)
ui.updateHearts(player.health, storage.maxHearts); // Wyświetl aktualne serca (powinno być 5)
ui.showTutorial("Swipe to roll away from attacks!"); // Wyświetl tutorial dla stanu gry
// --- Timer walki z bossem ---
this.remainingTime = this.gameDuration; // Ustaw początkowy czas (120 sekund)
ui.updateTimerDisplay(this.remainingTime); // Wyświetl początkowy czas
// Rozpocznij interwał timera
+ // Wyczyść poprzedni interwał timera, jeśli istniał (przy restartach gry)
+ if (this.gameTimerInterval) {
+ LK.clearInterval(this.gameTimerInterval);
+ }
+ // Zresetuj flagę przyspieszenia bossa na początku nowej gry
+ this.bossSpeedIncreased = false;
this.gameTimerInterval = LK.setInterval(function () {
- gameState.remainingTime--; // Zmniejsz pozostały czas
- ui.updateTimerDisplay(gameState.remainingTime); // Zaktualizuj wyświetlanie timera na UI
- // Sprawdź warunek zwycięstwa (czas minął)
- if (gameState.remainingTime <= 0) {
- LK.clearInterval(gameState.gameTimerInterval); // Zatrzymaj interwał timera
- // Obsłuż zwycięstwo przez przetrwanie
- // Upewnij się, że boss i jego ataki są zniszczone
- if (boss && !boss.dead) {
- // Jeśli boss nie umarł od obrażeń (czyli wygrana czasem)
- // Wyczyść aktywne ataki bossa i ich wizualizacje
- if (boss.attacks) {
- boss.attacks.forEach(function (attack) {
- if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
- attack.visual.destroy();
- }
- });
- boss.attacks = [];
+ // Tylko aktualizuj timer i sprawdzaj warunek zwycięstwa jeśli jesteśmy w stanie gry
+ if (gameState.currentState === "game") {
+ gameState.remainingTime--; // Zmniejsz pozostały czas
+ ui.updateTimerDisplay(gameState.remainingTime); // Zaktualizuj wyświetlanie timera na UI
+ // --- Przyspieszenie bossa po 1 minucie ---
+ // Jeśli pozostały czas <= 60 sekund i boss jeszcze nie został przyspieszony
+ if (gameState.remainingTime <= 60 && !gameState.bossSpeedIncreased) {
+ gameState.bossSpeedIncreased = true; // Ustaw flagę
+ if (boss && !boss.dead) {
+ // Upewnij się, że boss istnieje i żyje
+ // Zmniejsz mnożnik prędkości ataków bossa (np. o 30% -> mnożnik 0.7)
+ boss.attackSpeedMultiplier = 0.7;
+ // Opcjonalnie: wyświetl komunikat o przyspieszeniu
+ ui.showMessage("Boss attacks faster!", 2000); // Komunikat na 2 sekundy
}
- // Zniszcz obiekt bossa
- if (boss && boss.destroy && !boss.destroyed) {
- boss.destroy();
- }
- boss = null; // Wyczyść globalną referencję
}
- gameState.victory(); // Przejdź do stanu zwycięstwa
+ // --- Koniec Przyspieszenia bossa ---
+ // Sprawdź warunek zwycięstwa (czas minął)
+ if (gameState.remainingTime <= 0) {
+ LK.clearInterval(gameState.gameTimerInterval); // Zatrzymaj interwał timera
+ // Obsłuż zwycięstwo przez przetrwanie
+ // Boss cleanup jest obsługiwany w gameState.victory()
+ gameState.victory(); // Przejdź do stanu zwycięstwa
+ }
}
}, 1000); // Interwał co 1 sekundę (1000 ms)
// --- Koniec Timer walki z bossem ---
// Zaplanuj rozpoczęcie ataków bossa po krótkim opóźnieniu
+ // Nie potrzebujemy jawnie czyścić timeoutu, bo startAttackPattern ma sprawdzenie stanu gry
LK.setTimeout(function () {
- ui.hideTutorial(); // Ukryj tekst tutorialu
- if (boss && !boss.dead && gameState.currentState === "game") {
- boss.startAttackPattern();
- } // Rozpocznij ataki bossa jeśli boss żyje i gra jest w stanie gry
+ // Sprawdź, czy nadal jesteśmy w stanie gry gdy timeout minie i czy boss istnieje i żyje
+ if (gameState.currentState === "game" && boss && !boss.dead) {
+ ui.hideTutorial(); // Ukryj tekst tutorialu
+ boss.startAttackPattern(); // Rozpocznij ataki bossa
+ }
}, 3000); // 3 sekundy opóźnienia przed pierwszym atakiem bossa
},
// Przejście do stanu Game Over (wywoływane przez player.die())
gameOver: function gameOver() {
this.currentState = "gameOver"; // Ustaw stan na game over
// Zatrzymaj timer gry, jeśli nadal działa
LK.clearInterval(this.gameTimerInterval); // Upewnij się, że interwał timera jest wyczyszczony
+ // Zresetuj flagę przyspieszenia bossa
+ this.bossSpeedIncreased = false;
// Ukryj obiekty gry (są już niszczone, ale to dodatkowe zabezpieczenie)
+ // Upewnij się, że istnieją przed ustawieniem alpha
if (player) {
player.alpha = 0;
}
if (boss) {
@@ -1132,8 +1272,10 @@
victory: function victory() {
this.currentState = "victory"; // Ustaw stan na zwycięstwo
// Zatrzymaj timer gry, jeśli nadal działa (redundantne jeśli wygrana czasem, ale bezpieczniej)
LK.clearInterval(this.gameTimerInterval);
+ // Zresetuj flagę przyspieszenia bossa
+ this.bossSpeedIncreased = false;
// Clean up player (już niszczony w player.die)
if (player) {
player.destroy();
player = null;
@@ -1170,16 +1312,23 @@
}, 5000); // 5 sekund opóźnienia
},
// Obsługa gestów dotykowych/myszy w zależności od stanu gry
processTouchGesture: function processTouchGesture() {
+ // --- Obsługa inputu w stanie fakeTutorial (fałszywa śmierć) ---
+ // Jeśli jesteśmy w stanie fakeTutorial i timer do prawdziwego tutorialu jeszcze działa,
+ // każdy input (tapnięcie lub swipe) wywoła fałszywą śmierć.
+ if (this.currentState === "fakeTutorial" && this.fakeTutorialTimerId) {
+ this.handleFakeTutorialInput(); // Wywołaj logikę fałszywej śmierci
+ return; // Zakończ przetwarzanie gestu
+ }
// Oblicz kierunek i dystans gestu na podstawie zarejestrowanych pozycji startu i końca dotyku
var dx = this.touchEnd.x - this.touchStart.x;
var dy = this.touchEnd.y - this.touchStart.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// --- Obsługa tapowania/gestów w stanach poza grą ---
// Sprawdź, czy to było krótkie tapnięcie (dystans mniejszy niż próg turlania)
if (distance < 50) {
- // Próg turlania to 50 jednostek w stworzylo zloto.txt
+ // Próg turlania to 50 jednostek
// Jeśli jest to tapnięcie w stanie tytułowym, przejdź do intro
if (this.currentState === "title") {
this.showIntro(); // <--- Tap na ekranie tytułowym przechodzi do intro
return; // Zakończ przetwarzanie gestu
@@ -1188,10 +1337,11 @@
if (this.currentState === "victory") {
this.showTitleScreen(); // <--- Tap na ekranie zwycięstwa wraca do tytułu
return; // Zakończ przetwarzanie gestu
}
- // Jeśli to tapnięcie w innych stanach (intro, tutoriale, game over), zignoruj lub dodaj logikę jeśli potrzebna
- // Obecnie, tapnięcia w intro/tutorialach nic nie robią (poza przyciskiem "Let's Roll"), w game over restart jest timerem
+ // Jeśli to tapnięcie w innych stanach (intro, real tutorial, game over), zignoruj lub dodaj logikę jeśli potrzebna
+ // W real tutorialu tapnięcie nie wywoła nic poza przyciskiem "Let's Roll"
+ // W game over restart jest timerem
return; // Zakończ przetwarzanie gestu jeśli to tapnięcie i nie obsłużono stanu
}
// --- Obsługa turlania w stanie gry ---
// Tylko przetwarzaj gesty turlania (swipe) w stanie "game" i gdy gracz nie jest martwy
@@ -1211,9 +1361,10 @@
};
// --- Obsługa inputu (mapowanie zdarzeń LK na metody gameState) ---
game.down = function (x, y, obj) {
// Zarejestruj początek dotyku/kliknięcia tylko w stanach, które tego wymagają
- if (gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "game") {
+ if (gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "game" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial") {
+ // Dodano stany tutoriali
gameState.touchStart.x = x;
gameState.touchStart.y = y;
// Zresetuj end point na początku gestu
gameState.touchEnd.x = x;
@@ -1221,18 +1372,19 @@
}
};
game.up = function (x, y, obj) {
// Zarejestruj koniec dotyku/kliknięcia tylko w stanach, które tego wymagają
- if (gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "game") {
+ if (gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "game" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial") {
+ // Dodano stany tutoriali
gameState.touchEnd.x = x;
gameState.touchEnd.y = y;
// Przetwórz zarejestrowany gest (tap lub swipe)
gameState.processTouchGesture(); // Wywołaj metodę przetwarzającą gest
}
};
game.move = function (x, y, obj) {
// Śledź aktualną pozycję dotyku/kursora, aby obliczyć dystans gestu na koniec
- if (gameState.currentState === "game" || gameState.currentState === "title" || gameState.currentState === "victory") {
+ if (gameState.currentState === "game" || gameState.currentState === "title" || gameState.currentState === "victory" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial") {
// Śledź ruch w stanach, gdzie gesty są istotne
gameState.touchEnd.x = x;
gameState.touchEnd.y = y;
}