Code edit (8 edits merged)
Please save this source code
User prompt
add assets bossAttack0 to bossattack3
User prompt
add assets bossAttack0 to bossattack3
Code edit (5 edits merged)
Please save this source code
User prompt
add asset fireball13 fireball14 fireball15 fireball16
Code edit (2 edits merged)
Please save this source code
User prompt
add assets fireball00 to fireball12
User prompt
Please fix the bug: 'TypeError: self.clearRollTimeouts is not a function' in or related to this line: 'self.clearRollTimeouts();' Line Number: 589
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: SpriteAnimation is not defined' in or related to this line: 'var rollAnimation = game.addChild(new SpriteAnimation({' Line Number: 448
Code edit (18 edits merged)
Please save this source code
User prompt
Add 6 assets roll0, roll1, roll2, roll3, roll4, roll5.
Code edit (1 edits merged)
Please save this source code
Code edit (8 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Timeout.tick error: howToScene is not defined' in or related to this line: 'howToScene();' Line Number: 1263
Code edit (3 edits merged)
Please save this source code
User prompt
fix problem with click "tap to start" it's not working
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: 1103
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'alpha')' in or related to this line: 'self.deathsText.alpha = 1;' Line Number: 366
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
/**** * 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; // Domyślne zdrowie bossa (nadpisane w startGame) self.maxHealth = 100; // Domyślne max zdrowia bossa (nadpisane w startGame) 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.repositioning = false; // Flaga informująca czy boss się przemieszcza po szarży self.startAttackPattern = function () { // !!! Ustaw tymczasowy cooldown na początku, ZANIM sprawdzisz warunki powrotu !!! // To zapobiegnie natychmiastowemu ponownemu wywołaniu, jeśli metoda wróci wcześnie self.attackCooldown = 1; // Ustaw minimalny cooldown (np. 1 klatka) od razu // Sprawdź warunki wczesnego powrotu: boss jest martwy, gra nie jest w stanie "game" LUB boss jest w stanie "repositioning" if (self.dead || gameState.currentState !== "game" || self.repositioning) { // Dodano || self.repositioning return; // Jeśli warunek spełniony, wróć } // Jeśli dotarliśmy tutaj, oznacza to, że możemy rozpocząć nowy wzorzec ataku self.attackPattern = (self.attackPattern + 1) % 3; // Cykl wzorców ataków switch (self.attackPattern) { case 0: self.circleAttack(); break; case 1: self.lineAttack(); break; case 2: self.chargeAttack(); break; } // Ustaw WŁAŚCIWY czas odnowienia dla następnego ataku, PO zainicjowaniu obecnego // Mnożnik prędkości ataków bossa jest już ustawiony w gameState.startGame 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 = isNewBossPlusMode ? 12 : 8; // W trybie New Boss+ więcej pocisków var radius = 300; var attackLifeTime = isNewBossPlusMode ? 2000 : 3000; // Krótszy czas życia w New Boss+? for (var i = 0; i < count; i++) { var angle = i / count * Math.PI * 2; var posX = center.x + Math.cos(angle) * radius; var posY = center.y + Math.sin(angle) * radius; self.createAttack(posX, posY, attackLifeTime); // Utwórz atak w danej pozycji z czasem życia (skalowane w createAttack) } }; self.lineAttack = function () { LK.getSound('bossAttack').play(); // Tworzy linię ataków od bossa do gracza var targetX = player.x; var targetY = player.y; var count = isNewBossPlusMode ? 8 : 5; // W trybie New Boss+ więcej pocisków w linii var attackLifeTime = isNewBossPlusMode ? 1500 : 2000; // Krótszy czas życia? var delayBetweenAttacks = isNewBossPlusMode ? 100 : 200; // Mniejsze opóźnienie między atakami w linii for (var i = 0; i < count; i++) { var t = i / (count - 1); // Współczynnik interpolacji var posX = self.x + (targetX - self.x) * t; var posY = self.y + (targetY - self.y) * t; var delay = i * delayBetweenAttacks * self.attackSpeedMultiplier; // Opóźnienie dla kolejnych ataków w linii, skalowane LK.setTimeout(function (x, y) { return function () { // Sprawdź, czy boss nadal żyje i gra jest w stanie gry zanim stworzysz atak if (self && !self.dead && gameState.currentState === "game") { self.createAttack(x, y, attackLifeTime); // Utwórz atak po opóźnieniu (czas życia skalowane w createAttack) } }; }(posX, posY), delay); } }; 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ę (z której zaczyna się szarża) var startX = self.x; var startY = self.y; var chargeDistance = isNewBossPlusMode ? 700 : 500; // Dłuższa szarża w New Boss+? var chargeDuration = isNewBossPlusMode ? 600 : 800; // Szybsza szarża w New Boss+? var attacksOnPathCount = isNewBossPlusMode ? 8 : 5; // Więcej ataków na ścieżce szarży? var attackLifeTime = isNewBossPlusMode ? 1000 : 1500; // Krótszy czas życia? // Animacja szarży (boss przesuwa się szybko w kierunku gracza) tween(self, { x: self.x + dx * chargeDistance, // Przesunięcie y: self.y + dy * chargeDistance }, { duration: chargeDuration * 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") { // --- Start Repositioning Phase --- self.repositioning = true; // Ustaw flagę repozycji po zakończeniu szarży // Czas na repozycję (np. 0.5 sekundy), skalowane LK.setTimeout(function () { if (self && !self.dead) { // Upewnij się, że boss nadal istnieje i żyje self.repositioning = false; // Zakończ stan repozycji po opóźnieniu } }, 500 * self.attackSpeedMultiplier); // Opóźnienie skalowane przez mnożnik prędkości ataków // --- End Repositioning Phase --- // Powrót do oryginalnej pozycji (startu szarży) po szarży tween(self, { x: startX, y: startY }, { duration: 1000 * self.attackSpeedMultiplier, // Czas powrotu, skalowane easing: tween.easeOut // Nie potrzebujemy tutaj już onFinish do repozycji, bo obsłużono to powyżej }); // Utwórz ataki wzdłuż ścieżki szarży for (var i = 0; i < attacksOnPathCount; i++) { var t = i / (attacksOnPathCount - 1); var posX = startX + (self.x - startX) * t; var posY = startY + (self.y - startY) * t; self.createAttack(posX, posY, attackLifeTime); // 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 var warningDuration = isNewBossPlusMode ? 800 : 1000; // Krótsze ostrzeżenie w New Boss+? tween(warning, { alpha: 0.6 // Zwiększ przezroczystość }, { duration: warningDuration * 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; // Zmniejsz zdrowie // Upewnij się, że zdrowie nie spadnie poniżej zera (chyba że do śmierci) self.health = Math.max(0, self.health); // Wizualne sygnalizowanie otrzymania obrażeń LK.effects.flashObject(self, 0xFFFFFF, 200); // Przejście fazy bossa pri 50% zdrowia (może być wyłączone w New Boss+?) if (!isNewBossPlusMode) { // Tylko w standardowym trybie if (self.health <= self.maxHealth / 2 && self.phase === 1) { self.phase = 2; self.speed += 2; // Boss staje się szybszy (ruch) // Wizualne przejście fazy (np. zmiana koloru) tween(self, { tint: 0xFF3300 // Pomarańczowy/czerwony odcień }, { duration: 1000, easing: tween.easeInOut }); } } // Sprawdzenie, czy boss został pokonany if (self.health <= 0) { self.die(); // Wywołaj funkcję śmierci } // Opcjonalnie: Aktualizuj wizualne wskazanie zdrowia bossa (jeśli istnieje) // ui.updateBossHealth(self.health, self.maxHealth); // Wymagałoby to dodania do klasy UI }; self.die = function () { if (self.dead || gameState.currentState !== "game") { // Boss umiera tylko w stanie gry return; } self.dead = true; // Dźwięk zwycięstwa tylko dla STANDARDOWEGO trybu (jeśli nie New Boss+) if (!isNewBossPlusMode) { LK.getSound('victory').play(); // Dźwięk zwycięstwa w trybie standardowym } // Animacja śmierci bossa (zanikanie i powiększanie) tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Sprawdź, czy boss nadal istnieje przed zniszczeniem if (self && self.destroy && !self.destroyed) { self.destroy(); // Zniszcz obiekt bossa } // Zwiększ licznik pokonanych bossów TYLKO W STANDARDOWYM TRYBIE LUB MOŻE OSOBNO DLA NEW BOSS+? // Na razie zwiększamy zawsze, ale można to zmienić storage.bossesDefeated = (storage.bossesDefeated || 0) + 1; // Zwiększ licznik pokonanych bossów // !!! Po pokonaniu bossa ZAWSZE przechodzimy do Grill Screena (niezależnie od trybu) !!! gameState.showGrillScreen(); } }); // Zatrzymaj wszystkie timery związane z atakami bossa // Ataki zostaną wyczyszczone w gameState.showGrillScreen clean up }; self.update = function () { var playerRadius; // Declare playerRadius here // Aktualizuj tylko w stanie gry if (gameState.currentState !== "game") { // Jeśli zmieniono stan gry i boss nie umiera, wyczyść pozostałe ataki if (!self.dead && self.attacks) { self.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); self.attacks = []; // Wyczyść tablicę ataków } return; } // Nie aktualizuj jeśli boss jest martwy i animacja śmierci dobiegła końca (obiekt niewidoczny) 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; // !!! NOWA LOGIKA: Sprawdzenie kolizji z bossem podczas turlania i zadawanie obrażeń !!! // 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 = self.x - boss.x; var dy = self.y - boss.y; var distance = Math.sqrt(dx * dx + dy * dy); // 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 < playerCollisionRadius + bossCollisionRadius) { // Kolizja wykryta podczas turlania boss.takeDamage(10); // ZADJ 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 } } } // 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; }); // 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 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) { // 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 }; // Aktualizuje wizualizację paska zdrowia bossa self.updateBossHealth = function (current, max) { // 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 (jeśli boss istnieje i żyje i jesteśmy w grze) self.bossHealthBarContainer.alpha = (gameState.currentState === "game" || gameState.currentState === "gameOver") && boss && !boss.dead && current > 0 ? 1 : 0; // Pokaż pasek w stanie gry ORAZ game over (przed zresetowaniem UI) tylko jeśli boss żyje i ma >0 HP. } // Upewnij się, że pasek zdrowia tła też jest widoczny, gdy kontener jest widoczny 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); } // 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; // Ukryj pasek zdrowia bossa self.bossHealthBarContainer.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; // Pokaż pasek zdrowia bossa (jeśli boss żyje) // Pasek będzie widoczny tylko w stanie "game" dzięki updateBossHealth i update w game.update self.bossHealthBarContainer.alpha = 1; break; case "grillMenu": // Nowy stan dla Grill Screena self.titleText.alpha = 0; // Ukryj tytuł na Grill Screenie (lub ustaw na "Grill Menu" jeśli chcesz) self.messageText.x = 2048 / 2; // Pozycjonowanie komunikatów/tekstów przycisków na środku self.messageText.y = 500; // Możesz dostosować pozycję self.messageText.setText(""); // Wyczyść domyślny messageText self.tutorialText.alpha = 0; // Ukryj tutorial // Ukryj serca i timer self.heartContainer.alpha = 0; self.timerText.alpha = 0; // Ukryj pasek zdrowia bossa self.bossHealthBarContainer.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" lub komunikat zwycięstwa 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" (choć restart jest timerem) // Ukryj serca i timer na ekranie Game Over self.heartContainer.alpha = 0; self.timerText.alpha = 0; // Pokaż pasek zdrowia bossa TYLKO JEŚLI TO ZWYCIĘSTWO PRZEZ CZAS W BOSS+ (gameover reason is not death) self.bossHealthBarContainer.alpha = gameState.gameOverReasonIsDeath === false && isNewBossPlusMode ? 1 : 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; // Ukryj pasek zdrowia bossa self.bossHealthBarContainer.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 domyślne }); /**** * Game Code ****/ // Zachowujemy na wypadek użycia w animacjach gracza/UI // Używamy dźwięku zwycięstwa z stworzylo zloto.txt // Przykład koloru, użyje grafiki z upit // Przykład koloru, użyje grafiki z upit // Asset dla tła Grill Screena (dostosuj wymiary jeśli Twoja grafika ma inne) // Zmieniono wymiary przycisku // Twój nowy asset dla paska zdrowia bossa (800x30) // Zachowujemy jako domyślne tło lub na wypadek potrzeby // Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia) var currentSceneElements = new Container(); // Dodano z souls4.txt i rozbudowano dla Grill Screena // Zmienne gry (z stworzylo zloto.txt) var player; var boss; var ui; var walls = []; // Ściany areny bossa // Dodano zmienną do przechowywania aktywnego tła sceny var currentBackground = null; // Dodana flaga do śledzenia, czy jesteśmy w trybie New Boss+ var isNewBossPlusMode = false; // Dodana flaga do śledzenia przyczyny Game Over (true = gracz zginął, false = czas minął) var gameOverReasonIsDeath = false; // Funkcja do czyszczenia elementów z kontenera currentSceneElements (dla scen intro/tutoriali i Grill Screena) // Nie usuwa tła sceny (currentBackground) ani elementów UI. function clearScene() { // Dodano z souls4.txt i rozbudowano dla Grill Screena // 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, timer i Grill Screen) 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) - Domyślne 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 przyspieszenia 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; // Zresetuj flagę trybu New Boss+ na początku gry isNewBossPlusMode = false; // Zresetuj flagę przyczyny Game Over gameOverReasonIsDeath = false; // Nie dodajemy tutaj domyślnego tła 'bg'. Tła będą dodawane w metodach scen. game.setBackgroundColor(0x111111); // Ustaw domyślny kolor tła (widoczny np. podczas ładowania assetów) // 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, pasek zdrowia bossa) 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 i Grill Screena 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) // Domyślnie obiekty są dodawane w kolejności, więc ściany dodane przed graczem i bosem będą pod nimi. // UI i currentSceneElements dodane na końcu w init są 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 Grill Screena, Victory lub Fake Death) // Upewnij się, że timer fake tutorialu jest wyczyszczony if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; } // Zatrzymaj wszelkie trwające tweeny tła if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); // Usuń poprzednie tło currentBackground = null; } this.currentState = "title"; // Ustaw stan na tytuł game.setBackgroundColor(0x1a1a1a); // Ciemne tło bazowe dla tytułu // Dodaj tło ekranu tytułowego (titleBg) currentBackground = game.addChildAt(LK.getAsset('titleBg', { // Dodaj na spód (index 0) anchorX: 0, anchorY: 0, x: 0, y: 0 }), 0); // Dodaj jako pierwsze dziecko gry (na samym spodzie) // 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 }; // --- Dodaj testowy przycisk do Grill Menu --- var grillMenuTestButton = new Container(); grillMenuTestButton.interactive = true; // Spraw, żeby przycisk był interaktywny grillMenuTestButton.x = 2048 / 2; // Wyśrodkuj poziomo grillMenuTestButton.y = 1600; // Ustaw pozycję pionową (np. poniżej innych tekstów) currentSceneElements.addChild(grillMenuTestButton); // Dodaj do kontenera sceny tytułowej var grillButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); grillMenuTestButton.addChild(grillButtonBg); // Dodaj tło przycisku var grillButtonText = new Text2('Go to Grill Menu', { size: 50, fill: 0xFFFFFF }); // Tekst przycisku grillButtonText.anchor.set(0.5, 0.5); grillMenuTestButton.addChild(grillButtonText); // Dodaj tekst do przycisku // Logika przycisku: po kliknięciu przejdź do Grill Menu grillMenuTestButton.down = function () { // Opcjonalnie: wyczyść elementy sceny tytułowej przed przejściem clearScene(); // Przejdź do ekranu Grill Menu gameState.showGrillScreen(); }; // --- Koniec dodawania przycisku testowego --- }, // 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; } // Zatrzymaj wszelkie trwające tweeny tła if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); // Usuń poprzednie tło currentBackground = null; } this.currentState = "intro"; // Ustaw stan na intro game.setBackgroundColor(0x111111); // Ciemne tło bazowe dla intro // Dodaj tło scen intro (introBg) currentBackground = game.addChildAt(LK.getAsset('introBg', { // Dodaj na spód (index 0) anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 1, scaleY: 1 // Upewnij się, że zaczyna w normalnym rozmiarze }), 0); // Zastosuj powolny zoom do tła intro // Zoom IN do 1.1x przez 30 sekund, potem zoom OUT, powtarzaj tween(currentBackground, { scaleX: 1.1, scaleY: 1.1 }, { duration: 30000, // 30 sekund na jeden kierunek zoomu easing: tween.linear, repeat: Infinity, // Powtarzaj w nieskończoność yoyo: true // Zoomuj w tam i z powrotem }); // 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 this.fakeTutorialTimerId = LK.setTimeout(function () { // ZAPISZ ID TIMERA // clearScene() zostanie wywołane na początku następnej metody // Zresetuj ID timera, bo się zakończył gameState.fakeTutorialTimerId = null; // Przejdź do fałszywego tutorialu gameState.showFakeTutorial(); }.bind(this), 2000); // 2 sekundy opóźnienia po wyświetleniu drugiego tekstu. Użyj .bind(this) dla dostępu do this.fakeTutorialTimerId }.bind(this), 5000); // 5 sekund opóźnienia przed wyświetleniem drugiego tekstu. Użyj .bind(this) dla dostępu do this.fakeTutorialTimerId }, // Przejście do sceny fałszywego tutorialu (Press LPM to block) showFakeTutorial: function showFakeTutorial() { clearScene(); // Wyczyść poprzednie elementy (tekst intro) // Tło introBg i jego zoom powinny pozostać, bo nie są w currentSceneElements // Upewnij się, że timer fake tutorialu jest wyczyszczony (na wypadek gdybyśmy tu dotarli inaczej niż przez timer) if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; } this.currentState = "fakeTutorial"; // Ustaw stan na fałszywy tutorial game.setBackgroundColor(0x000000); // Czarne tło - Może chcesz, żeby tło introBg było widoczne? Zmieniam na czarne zgodnie z oryginalnym souls4.txt, ale możesz to zmienić. // Ukryj obiekty gry i ściany if (player) { player.alpha = 0; } if (boss) { boss.alpha = 0; } walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // 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() { // Wyczyść timer, który miał prowadzić do prawdziwego tutorialu if (this.fakeTutorialTimerId) { 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 // Zatrzymaj wszelkie trwające tweeny tła (np. zoom intro) if (currentBackground) { tween.stop(currentBackground); // Nie usuwamy tła od razu, może chcemy, żeby było widać je za ekranem "YOU DIED" // currentBackground.destroy(); currentBackground = null; } game.setBackgroundColor(0x000000); // Czarne tło bazowe za fake game over // 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 }); // Zmniejszono rozmiar czcionki, żeby pasowała do przycisku 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 stanu gry (walka z bossem) startGame: function startGame() { // Upewnij się, że timer fake tutorialu jest wyczyszczony if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; } // Zatrzymaj wszelkie trwające tweeny tła if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); // Usuń tło intro/tytułu currentBackground = null; } this.currentState = "game"; // Ustaw stan na "game" game.setBackgroundColor(0x111111); // Ciemne tło bazowe dla gry // 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 wzrośnie 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.hasRolledThroughBossThisRoll = false; // Zresetuj flagę obrażeń od turlania dla nowego gracza 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) boss.repositioning = false; // Zresetuj flagę repozycji bossa // !!! Ustaw zdrowie bossa i czas gry W ZALEŻNOŚCI OD TRYBU !!! if (isNewBossPlusMode) { boss.maxHealth = 2000; // New Boss+ ma 2000 HP boss.health = boss.maxHealth; this.gameDuration = 600; // New Boss+ ma 10 minut (600 sekund) // W trybie New Boss+ ataki są szybsze OD POCZĄTKU i NIE MA dodadkowego przyspieszenia po minucie boss.attackSpeedMultiplier = 0.6; // Przykładowy, niższy mnożnik (szybsze ataki) OD POCZĄTKU // Można tu dodać inne modyfikatory dla New Boss+, np. wyższą prędkość ruchu bossa boss.speed = 7; // Przykładowe szybsze poruszanie się bossa } else { // Standardowy boss boss.maxHealth = 200; boss.health = boss.maxHealth; this.gameDuration = 120; // Standardowy czas 2 minuty boss.speed = 5; // Standardowa prędkość poruszania się bossa boss.attackSpeedMultiplier = 1; // Standardowa prędkość ataków } // Aktualizuj UI (wyświetl serca i początkowy komunikat) ui.updateHearts(player.health, storage.maxHearts); // Wyświetl aktualne serca (powinno być 5 lub więcej) ui.showTutorial("Swipe to roll away from attacks!"); // Wyświetl tutorial dla stanu gry ui.updateBossHealth(boss.health, boss.maxHealth); // Zaktualizuj pasek zdrowia bossa z poprawnym HP // --- Timer walki z bossem --- this.remainingTime = this.gameDuration; // Ustaw początkowy czas ZALEŻNIE OD TRYBU 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 po 1 minucie (dotyczy obu trybów) this.bossSpeedIncreased = false; this.gameTimerInterval = LK.setInterval(function () { // Tylko aktualizuj timer i sprawdzaj warunek zwycięstwa/game over 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 - TYLKO W STANDARDOWYM TRYBIE --- if (!isNewBossPlusMode) { // Przyspieszenie tylko w standardowym trybie var accelerationThreshold = gameState.gameDuration - 60; // Przyspieszenie na 60 sekund przed końcem if (gameState.remainingTime <= accelerationThreshold && !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 kolejne 30% -> mnożnik 0.7 * 1 = 0.7) boss.attackSpeedMultiplier *= 0.7; // Przykładowe dalsze przyspieszenie ataków // Opcjonalnie: wyświetl komunikat o przyspieszeniu ui.showMessage("Boss attacks faster!", 2000); // Komunikat } } } // --- Koniec Przyspieszenia bossa --- // Sprawdź warunek końca gry (czas minął) if (gameState.remainingTime <= 0) { LK.clearInterval(gameState.gameTimerInterval); // Stop the timer if (isNewBossPlusMode) { // Czas minął w trybie Boss+ (zwycięstwo przez przetrwanie) gameOverReasonIsDeath = false; // Czas minął gameState.gameOver(false); // Przejdź do stanu Game Over z komunikatem zwycięstwa Boss+ } else { // Czas minął w trybie standardowym (zwycięstwo przez przetrwanie) // Przejdź bezpośrednio do Grill Menu gameState.showGrillScreen(); } } } }, 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 }, // Ta metoda zwycięstwa (przez czas w trybie standardowym) przechodzi teraz do Grill Screena victory: function victory() { // Ta metoda nie jest już używana do zarządzania stanami gry po zwycięstwie przez czas. // Logika przejścia do Grill Screena dla standardowego zwycięstwa przez czas jest teraz bezpośrednio w gameTimerInterval. console.log("Old victory state - transition is handled by gameTimerInterval now."); }, // Przejście do ekranu Grilla po zwycięstwie (pokonaniu bossa) lub przetrwaniu czasu showGrillScreen: function showGrillScreen() { clearScene(); // Wyczyść elementy poprzedniej sceny (gra, ataki, itp.) // Zatrzymaj timer gry, jeśli nadal działa (jeśli wygrana była przez zbicie HP bossa, a nie czasem) LK.clearInterval(this.gameTimerInterval); // Zresetuj flagę przyspieszenia bossa this.bossSpeedIncreased = false; this.currentState = "grillMenu"; // Ustaw nowy stan gry game.setBackgroundColor(0x333333); // Możesz ustawić kolor tła lub pozwolić assetowi to zrobić // Zatrzymaj wszelkie trwające tweeny tła gry i usuń tło gry, jeśli istnieje if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); currentBackground = null; } // Dodaj tło dla ekranu Grilla currentBackground = game.addChildAt(LK.getAsset('grillMenu', { // Użyj assetu 'grillMenu' anchorX: 0.5, anchorY: 0.5, // Ustaw środek jako punkt odniesienia x: 2048 / 2, y: 2732 / 2 // Wyśrodkuj na ekranie }), 0); // Dodaj na spód // Ukryj obiekty gry (gracz, boss) jeśli istnieją (choć powinny być zniszczone) if (player) { player.alpha = 0; } if (boss) { boss.alpha = 0; } // Ukryj ściany areny walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Pozycjonuj elementy UI dla stanu Grill Screena ui.positionElements("grillMenu"); // --- Dodaj przyciski na ekranie Grilla --- var buttonYStart = 1000; // Początkowa pozycja Y dla pierwszego przycisku var buttonYOffset = 150; // Odstęp pionowy między przyciskami // Przycisk "Rest" var restButton = new Container(); restButton.interactive = true; restButton.x = 2048 / 2; restButton.y = buttonYStart; currentSceneElements.addChild(restButton); // Dodaj do kontenera sceny 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); // Logika przycisku "Rest" restButton.down = function () { // Wyświetl komunikat "Rest in peace" i podziękowanie ui.showMessage("Rest in peace...", 2000); // Zniknie po 2 sekundach LK.setTimeout(function () { ui.showMessage("Thank you for playing!", 3000); // Zniknie po 3 sekundach }, 2500); // Pokaż podziękowanie po chwili // Zaplanuj powrót do ekranu tytułowego po wyświetleniu podziękowań LK.setTimeout(function () { // Upewnij się, że elementy Grill Screena zostały wyczyszczone przed powrotem do tytułu clearScene(); gameState.showTitleScreen(); // Wróć do ekranu tytułowego }, 6000); // Opóźnienie przed powrotem do tytułu }; // Przycisk "Upgrade Roll" var upgradeButton = new Container(); upgradeButton.interactive = true; upgradeButton.x = 2048 / 2; upgradeButton.y = buttonYStart + buttonYOffset; currentSceneElements.addChild(upgradeButton); // Dodaj do kontenera sceny 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); // Logika przycisku "Upgrade Roll" upgradeButton.down = function () { ui.showMessage("Rolling?", 2000); // Wyświetl śmieszny napis // W przyszłości można tu dodać rzeczywistą logikę ulepszenia turlania }; // Przycisk "New Boss+" var newBossButton = new Container(); newBossButton.interactive = true; newBossButton.x = 2048 / 2; newBossButton.y = buttonYStart + buttonYOffset * 2; currentSceneElements.addChild(newBossButton); // Dodaj do kontenera sceny 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); // Logika przycisku "New Boss+" newBossButton.down = function () { clearScene(); // Wyczyść przyciski i tło Grilla // Ustaw flagę trybu New Boss+ isNewBossPlusMode = true; // Rozpocznij grę z nowymi parametrami (obsłużone w startGame) gameState.startGame(); }; // --- Koniec dodawania przycisków --- // Ukryj pasek zdrowia bossa na ekranie Grilla ui.updateBossHealth(0, boss ? boss.maxHealth : 200); // Ustaw 0 zdrowia, ukryje pasek }, // Przejście do stanu Game Over (wywoływane przez player.die() lub przez timer) // Przyjmuje argument isDeath: true jeśli gracz zginął, false jeśli czas minął gameOver: function gameOver(isDeath) { this.currentState = "gameOver"; // Ustaw stan na game over // Zapisz przyczynę Game Over, aby UI mogło jej użyć do pozycjonowania/widoczności elementów gameOverReasonIsDeath = isDeath; // 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; // Zachowaj reset flagi przyspieszenia // Zatrzymaj wszelkie trwające tweeny tła if (currentBackground) { tween.stop(currentBackground); // Nie usuwamy tła od razu, może chcemy, żeby było widać je za ekranem game over // currentBackground.destroy(); currentBackground = null; } game.setBackgroundColor(0x000000); // Czarne tło bazowe za game over // Ukryj obiekty gry (są już zniszczone, 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"); // Logika komunikatu Game Over w zależności od trybu i przyczyny var gameOverMessage = "YOU DIED"; // Domyślny komunikat dla śmierci gracza if (!isDeath) { // Jeśli Game Over nie nastąpiło przez śmierć gracza (czyli czas minął) if (isNewBossPlusMode) { // Czas minął w trybie Boss+ (zwycięstwo przez przetrwanie) gameOverMessage = "I haven't prepared a scene for this touch the grass"; // Komunikat zwycięstwa Boss+ } // else: Czas minął w trybie standardowym - ten przypadek teraz przechodzi bezpośrednio do Grill Menu i nie wywołuje gameover() } // Jeśli isDeath jest true, gameOverMessage pozostaje "YOU DIED" (domyślne) // Wyświetl komunikat Game Over/Zwycięstwa ui.showMessage(gameOverMessage, 0); // Wyświetl na stałe do momentu restartu ui.titleText.setText(gameOverMessage); // Ustaw tekst tytułu ui.titleText.alpha = 1; // Pokaż tytuł ui.showTutorial(""); // Ukryj tutorial // Pasek zdrowia bossa jest teraz zarządzany przez ui.positionElements("gameOver") i ui.updateBossHealth() // 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 = []; // Wyczyść tablicę ataków } // Usuń tło game over if (currentBackground) { currentBackground.destroy(); currentBackground = null; } // Resetuj UI do stanu tytułowego przed potencjalnym restartem ui.positionElements("title"); // Pozycjonuj UI jak dla tytułu (ukryje tytuł gameover/zwycięstwa, komunikaty, paski itp.) ui.titleText.alpha = 0; // Ukryj tytuł ponownie ui.showMessage(""); // Wyczyść komunikat ui.showTutorial(""); // Wyczyść tutorial ui.updateDeathsCounter(); // Upewnij się, że licznik śmierci jest aktualny ui.updateBossHealth(0, 200); // Zresetuj i ukryj pasek zdrowia bossa // Flaga isNewBossPlusMode zachowa swoją wartość sprzed wywołania gameOver, // dzięki czemu restart w przypadku śmierci w Boss+ wróci do Boss+ // Resetuj flagę przyczyny Game Over gameOverReasonIsDeath = false; // Reset po użyciu // Restartuj walkę z bossem (jeśli zginął w Boss+, startGame ustawi Boss+; jeśli zginął w standard, ustawi standard) gameState.startGame(); // Przejdź z powrotem do stanu gry }, 3000); // 3 sekundy opóźnienia przed restartem }, // 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ą (z wyjątkiem Grill Menu) --- // 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") { // Sprawdź, czy kliknięto w przycisk testowy - obsługa w game.down/up interaktywnych obiektów // Jeśli nie kliknięto w przycisk, tapnięcie przechodzi do intro if (this.touchStart.x === this.touchEnd.x && this.touchStart.y === this.touchEnd.y) { // Upewnij się, że to faktycznie tapnięcie (start i koniec w tym samym miejscu) // Dodatkowo można sprawdzić, czy pod klikniętym punktem NIE BYŁO interaktywnego obiektu (np. przycisku) // Ale prostsze jest poleganie na tym, że interaktywne obiekty mają własne handlery (game.down/up z obj) // Jeśli dotarliśmy tutaj, to prawdopodobnie tapnięto w tło this.showIntro(); // <--- Tap na ekranie tytułowym przechodzi do intro return; // Zakończ przetwarzanie gestu } } // Jeśli to tapnięcie w stanach game over lub victory (które teraz też prowadzą do Grill Screena lub restartu) - zignoruj tapnięcia // Restart/przejście do Grill Screena dzieje się automatycznie po Game Over/Victory timerem if (this.currentState === "gameOver") { // Zignoruj tapnięcie na ekranie Game Over, restart jest timerem return; } if (this.currentState === "victory") { // Ta metoda nie jest już wywoływana bezpośrednio, ale zabezpieczenie na wszelki wypadek return; } // Jeśli to tapnięcie w innych stanach (intro, real tutorial), zignoruj lub dodaj logikę jeśli potrzebna // W real tutorialu tapnięcie nie wywoła nic poza przyciskiem "Let's Roll" 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 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 === "game" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial" || gameState.currentState === "grillMenu" || gameState.currentState === "gameOver") { // Dodano stan game over do śledzenia potencjalnych tapnięć na ekranie końcowym 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 === "game" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial" || gameState.currentState === "grillMenu" || gameState.currentState === "gameOver") { // Dodano stan game over 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 === "fakeTutorial" || gameState.currentState === "realTutorial" || gameState.currentState === "grillMenu" || gameState.currentState === "gameOver") { // Śledź ruch w stanach, gdzie gesty są istotne. Dodano stan game over. 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(); // Aktualizuj pasek zdrowia bossa w każdej klatce (jeśli boss istnieje) if (boss) { ui.updateBossHealth(boss.health, boss.maxHealth); } else { // Jeśli boss nie istnieje, ukryj pasek zdrowia bossa // Użyj poprawnego maxHP dla ukrycia/resetu w zależności od trybu // Sprawdź, czy nie jesteśmy w stanie game over po zwycięstwie Boss+, gdzie pasek ma pozostać widoczny if (!(gameState.currentState === "gameOver" && gameState.gameOverReasonIsDeath === false && isNewBossPlusMode)) { ui.updateBossHealth(0, isNewBossPlusMode ? 2000 : 200); } } // 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ść, kolizja turlania z bossem) } 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. } // Logika w innych stanach (title, intro, tutoriale, game over, grillMenu) jest obsługiwana przez ich własne metody/przyciski. }; // --- 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
@@ -339,174 +339,8 @@
self.y = Math.max(100, Math.min(self.y, 2732 - 100));
};
return self;
});
-// Player class implementation for the Roll Souls game
-var Player = Container.expand(function () {
- var self = Container.call(this);
- // Create and attach player graphic
- var playerGraphics = self.attachAsset('player', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- // Player properties
- self.health = 5; // Default health, will be set from storage.maxHearts in gameState.startGame
- self.maxHealth = 5;
- self.speed = 10;
- self.rolling = false; // Flag to track if player is currently rolling
- self.rollSpeed = 30; // Speed during roll
- self.rollDirection = {
- x: 0,
- y: 0
- }; // Direction of roll
- self.rollDuration = 300; // Duration of roll in ms
- self.rollCooldown = 0; // Cooldown frames before player can roll again
- self.rollCooldownTotal = 30; // Total frames for cooldown (half second at 60fps)
- self.rollTimeouts = []; // Array to track roll-related timeouts
- self.invulnerable = false; // Flag for invulnerability frames
- self.invulnerabilityFrames = 0; // Counter for invulnerability
- self.dead = false; // Flag to track if player is dead
- self.hasRolledThroughBossThisRoll = false; // Flag to track if already damaged boss during current roll
- // Clear all roll-related timeouts
- self.clearRollTimeouts = function () {
- while (self.rollTimeouts.length > 0) {
- var timeout = self.rollTimeouts.pop();
- LK.clearTimeout(timeout);
- }
- };
- // Perform a roll in the given direction
- self.roll = function (direction) {
- // Check if roll is on cooldown or player is already rolling or dead
- if (self.rollCooldown > 0 || self.rolling || self.dead) {
- return;
- }
- // Reset flag for boss damage during roll
- self.hasRolledThroughBossThisRoll = false;
- // Set rolling state and direction
- self.rolling = true;
- self.rollDirection = direction;
- // Make player invulnerable during roll
- self.invulnerable = true;
- self.invulnerabilityFrames = self.rollDuration / (1000 / 60); // Convert ms to frames assuming 60fps
- // Create roll effect
- var rollEffect = game.addChild(LK.getAsset('roll', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: self.x,
- y: self.y,
- alpha: 0.7
- }));
- // Schedule the end of roll and cleanup
- var rollEndTimeout = LK.setTimeout(function () {
- self.rolling = false;
- self.rollCooldown = self.rollCooldownTotal;
- // Remove roll effect with fade out
- tween(rollEffect, {
- alpha: 0,
- scaleX: 0.5,
- scaleY: 0.5
- }, {
- duration: 200,
- onFinish: function onFinish() {
- if (rollEffect && rollEffect.destroy) {
- rollEffect.destroy();
- }
- }
- });
- }, self.rollDuration);
- self.rollTimeouts.push(rollEndTimeout);
- };
- // Handle player taking damage
- self.takeDamage = function (amount) {
- // Only take damage if player is alive, not invulnerable, and in game state
- if (self.dead || self.invulnerable || gameState.currentState !== "game") {
- return;
- }
- // Reduce health
- self.health -= amount;
- // Flash player to show damage
- LK.effects.flashObject(self, 0xFF0000, 300);
- // Check if player died
- if (self.health <= 0) {
- self.die();
- return;
- }
- // Make player temporarily invulnerable after taking damage
- self.invulnerable = true;
- self.invulnerabilityFrames = 90; // 1.5 seconds at 60fps
- };
- // Handle player death
- self.die = function () {
- if (self.dead) {
- return;
- }
- self.dead = true;
- self.clearRollTimeouts();
- // Increment death counter
- storage.totalDeaths = (storage.totalDeaths || 0) + 1;
- // Increase max hearts after death (as death is progress in this game)
- storage.maxHearts = (storage.maxHearts || 5) + 1;
- // Death animation
- tween(self, {
- alpha: 0,
- scaleX: 0.5,
- scaleY: 0.5
- }, {
- duration: 1000,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- if (self && !self.destroyed) {
- self.destroy();
- }
- // Set death as reason for game over
- gameOverReasonIsDeath = true;
- // Transition to game over state
- gameState.gameOver(true);
- }
- });
- };
- // Update method called every frame
- self.update = function () {
- // Don't update if player is dead
- if (self.dead) {
- return;
- }
- // Handle roll cooldown
- if (self.rollCooldown > 0) {
- self.rollCooldown--;
- }
- // Handle invulnerability frames
- if (self.invulnerable && self.invulnerabilityFrames > 0) {
- self.invulnerabilityFrames--;
- // Flashing effect during invulnerability
- self.alpha = self.invulnerabilityFrames % 6 > 2 ? 0.5 : 1;
- if (self.invulnerabilityFrames <= 0) {
- self.invulnerable = false;
- self.alpha = 1; // Restore full opacity when invulnerability ends
- }
- }
- // Check collision with boss attacks
- if (boss && !self.invulnerable && !self.dead && gameState.currentState === "game") {
- // Iterate through all boss attacks
- for (var i = 0; i < boss.attacks.length; i++) {
- var attack = boss.attacks[i];
- // Calculate distance between player and attack
- var dx = self.x - attack.x;
- var dy = self.y - attack.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- // Check if player is hit by attack
- if (distance < attack.radius + self.width / 2) {
- self.takeDamage(1);
- break; // Only take damage once per frame
- }
- }
- }
- // Keep player within bounds (with wall collision)
- self.x = Math.max(100, Math.min(self.x, 2048 - 100));
- self.y = Math.max(100, Math.min(self.y, 2732 - 100));
- };
- return self;
-});
// Shape class implementation for creating simple shapes
var Shape = Container.expand(function (options) {
var self = Container.call(this);
options = options || {};
@@ -775,8 +609,10 @@
// Ukryj pasek zdrowia bossa
self.bossHealthBarContainer.alpha = 0;
break;
}
+ // Upewnij się, że licznik śmierci jest zawsze widoczny
+ self.deathsText.alpha = 1;
};
return self;
});
@@ -798,42 +634,8 @@
// Zmieniono wymiary przycisku
// Twój nowy asset dla paska zdrowia bossa (800x30)
// Zachowujemy jako domyślne tło lub na wypadek potrzeby
// Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia)
-function _typeof(o) {
- "@babel/helpers - typeof";
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
- return typeof o;
- } : function (o) {
- return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
- }, _typeof(o);
-}
-function _defineProperty(e, r, t) {
- return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
- value: t,
- enumerable: !0,
- configurable: !0,
- writable: !0
- }) : e[r] = t, e;
-}
-function _toPropertyKey(t) {
- var i = _toPrimitive(t, "string");
- return "symbol" == _typeof(i) ? i : i + "";
-}
-function _toPrimitive(t, r) {
- if ("object" != _typeof(t) || !t) {
- return t;
- }
- var e = t[Symbol.toPrimitive];
- if (void 0 !== e) {
- var i = e.call(t, r || "default");
- if ("object" != _typeof(i)) {
- return i;
- }
- throw new TypeError("@@toPrimitive must return a primitive value.");
- }
- return ("string" === r ? String : Number)(t);
-}
var currentSceneElements = new Container(); // Dodano z souls4.txt i rozbudowano dla Grill Screena
// Zmienne gry (z stworzylo zloto.txt)
var player;
var boss;
@@ -861,9 +663,9 @@
}
// 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, timer i Grill Screen)
-var gameState = _defineProperty(_defineProperty({
+var gameState = {
currentState: "title",
// Początkowy stan gry
// Zmienne dla timera walki z bossem
gameDuration: 120,
@@ -1243,99 +1045,8 @@
howToPlayButtonContainer.down = function () {
gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu
};
},
- // Przejście do sceny prawdziwego tutorialu
- // Przejście do sceny prawdziwego tutorialu
- showRealTutorial: function showRealTutorial() {
- clearScene(); // Wyczyść poprzednie elementy (fałszywa śmierć lub fałszywy tutorial)
- // Tło introBg i jego zoom powinny pozostać
- // 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 bazowe - Może chcesz, żeby tło introBg było widoczne?
- // Ukryj obiekty gry i ściany
- if (player) {
- player.alpha = 0;
- }
- if (boss) {
- boss.alpha = 0;
- }
- walls.forEach(function (wall) {
- if (wall) {
- wall.alpha = 0;
- }
- });
- 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: 60,
- fill: 0xFFFFFF
- }); // Zmniejszono rozmiar czcionki, żeby pasowała do przycisku
- 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();
- // Zatrzymaj tween zoomu tła intro i usuń tło
- if (currentBackground) {
- tween.stop(currentBackground);
- currentBackground.destroy();
- currentBackground = null;
- }
- // Upewnij się, że tryb New Boss+ jest WYŁĄCZONY przy standardowym starcie z tutorialu
- isNewBossPlusMode = false;
- gameState.startGame(); // Wywołaj metodę rozpoczynającą grę (walkę z bossem)
- };
- },
// Przejście do stanu gry (walka z bossem)
startGame: function startGame() {
// Upewnij się, że timer fake tutorialu jest wyczyszczony
if (this.fakeTutorialTimerId) {
@@ -1692,71 +1403,69 @@
gameOverReasonIsDeath = false; // Reset po użyciu
// Restartuj walkę z bossem (jeśli zginął w Boss+, startGame ustawi Boss+; jeśli zginął w standard, ustawi standard)
gameState.startGame(); // Przejdź z powrotem do stanu gry
}, 3000); // 3 sekundy opóźnienia przed restartem
- }
-}, "victory", function victory() {
- // Ta metoda nie jest już używana do zarządzania stanami gry po zwycięstwie przez czas.
- // Logika przejścia do Grill Screena dla standardowego zwycięstwa przez czas jest teraz bezpośrednio w gameTimerInterval.
- console.log("Old victory state - transition is handled by gameTimerInterval now.");
-}), "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ą (z wyjątkiem Grill Menu) ---
- // 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") {
- // Sprawdź, czy kliknięto w przycisk testowy - obsługa w game.down/up interaktywnych obiektów
- // Jeśli nie kliknięto w przycisk, tapnięcie przechodzi do intro
- if (this.touchStart.x === this.touchEnd.x && this.touchStart.y === this.touchEnd.y) {
- // Upewnij się, że to faktycznie tapnięcie (start i koniec w tym samym miejscu)
- // Dodatkowo można sprawdzić, czy pod klikniętym punktem NIE BYŁO interaktywnego obiektu (np. przycisku)
- // Ale prostsze jest poleganie na tym, że interaktywne obiekty mają własne handlery (game.down/up z obj)
- // Jeśli dotarliśmy tutaj, to prawdopodobnie tapnięto w tło
- this.showIntro(); // <--- Tap na ekranie tytułowym przechodzi do intro
- return; // Zakończ przetwarzanie gestu
+ },
+ // 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ą (z wyjątkiem Grill Menu) ---
+ // 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") {
+ // Sprawdź, czy kliknięto w przycisk testowy - obsługa w game.down/up interaktywnych obiektów
+ // Jeśli nie kliknięto w przycisk, tapnięcie przechodzi do intro
+ if (this.touchStart.x === this.touchEnd.x && this.touchStart.y === this.touchEnd.y) {
+ // Upewnij się, że to faktycznie tapnięcie (start i koniec w tym samym miejscu)
+ // Dodatkowo można sprawdzić, czy pod klikniętym punktem NIE BYŁO interaktywnego obiektu (np. przycisku)
+ // Ale prostsze jest poleganie na tym, że interaktywne obiekty mają własne handlery (game.down/up z obj)
+ // Jeśli dotarliśmy tutaj, to prawdopodobnie tapnięto w tło
+ this.showIntro(); // <--- Tap na ekranie tytułowym przechodzi do intro
+ return; // Zakończ przetwarzanie gestu
+ }
}
+ // Jeśli to tapnięcie w stanach game over lub victory (które teraz też prowadzą do Grill Screena lub restartu) - zignoruj tapnięcia
+ // Restart/przejście do Grill Screena dzieje się automatycznie po Game Over/Victory timerem
+ if (this.currentState === "gameOver") {
+ // Zignoruj tapnięcie na ekranie Game Over, restart jest timerem
+ return;
+ }
+ if (this.currentState === "victory") {
+ // Ta metoda nie jest już wywoływana bezpośrednio, ale zabezpieczenie na wszelki wypadek
+ return;
+ }
+ // Jeśli to tapnięcie w innych stanach (intro, real tutorial), zignoruj lub dodaj logikę jeśli potrzebna
+ // W real tutorialu tapnięcie nie wywoła nic poza przyciskiem "Let's Roll"
+ return; // Zakończ przetwarzanie gestu jeśli to tapnięcie i nie obsłużono stanu
}
- // Jeśli to tapnięcie w stanach game over lub victory (które teraz też prowadzą do Grill Screena lub restartu) - zignoruj tapnięcia
- // Restart/przejście do Grill Screena dzieje się automatycznie po Game Over/Victory timerem
- if (this.currentState === "gameOver") {
- // Zignoruj tapnięcie na ekranie Game Over, restart jest timerem
- return;
+ // --- Obsługa turlania w stanie gry ---
+ // Tylko przetwarzaj gesty turlania (swipe) w stanie "game" i gdy gracz jest martwy
+ if (this.currentState !== "game" || !player || player.dead) {
+ return; // Zignoruj gesty swipe w innych stanach
}
- if (this.currentState === "victory") {
- // Ta metoda nie jest już wywoływana bezpośrednio, ale zabezpieczenie na wszelki wypadek
- return;
- }
- // Jeśli to tapnięcie w innych stanach (intro, real tutorial), zignoruj lub dodaj logikę jeśli potrzebna
- // W real tutorialu tapnięcie nie wywoła nic poza przyciskiem "Let's Roll"
- return; // Zakończ przetwarzanie gestu jeśli to tapnięcie i nie obsłużono stanu
+ // 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 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 === "game" || gameState.currentState === "fakeTutorial" || gameState.currentState === "realTutorial" || gameState.currentState === "grillMenu" || gameState.currentState === "gameOver") {