User prompt
add new assets rmattack2_explode_0 rmattack2_explode_1 rmattack2_explode_2 rmattack2_explode_3 rmattack2_explode_4 rmattack2_explode_5 rmattack2_explode_6
Code edit (11 edits merged)
Please save this source code
User prompt
add new assets rmattack1_0 rmattack1_1 rmattack1_2 rmattack1_3
Code edit (1 edits merged)
Please save this source code
Code edit (14 edits merged)
Please save this source code
User prompt
add new assets rmattack1 rmattack2 rmattack3 rmattack4
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (21 edits merged)
Please save this source code
User prompt
add asset rollMasterBg
Code edit (1 edits merged)
Please save this source code
Code edit (3 edits merged)
Please save this source code
User prompt
Change the font of the "Welcome Unchosen" title on the start screen of my game Roll Souls to "Georgia"
Code edit (12 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'fontFamily')' in or related to this line: 'ui.titleText.style.fontFamily = "Georgia";' Line Number: 1725
Code edit (1 edits merged)
Please save this source code
Code edit (4 edits merged)
Please save this source code
User prompt
Please fix the bug: 'spawnBackgroundParticle is not defined' in or related to this line: 'var particleIntervalId = LK.setInterval(spawnBackgroundParticle, 400);' Line Number: 1749
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'setInterval is not a function' in or related to this line: 'var particleIntervalId = setInterval(spawnBackgroundParticle, 400);' Line Number: 1734
User prompt
Please fix the bug: 'Timeout.tick error: tween.to is not a function' in or related to this line: 'tween.to(particle, {' Line Number: 1724 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'setInterval is not a function' in or related to this line: 'var particleIntervalId = setInterval(spawnBackgroundParticle, 400);' Line Number: 1734
Code edit (1 edits merged)
Please save this source code
User prompt
add asset sparticlebg
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem) // Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem) var Boss = Container.expand(function () { var self = Container.call(this); // Definicje list klatek dla fireballi - NIE używane w starej metodzie createAttack, ale mogą być potrzebne dla grafiki bossa lub jeśli zdecydujesz się wrócić do SpriteAnimation // Zachowujemy definicje na wypadek przyszłych zmian var circleFrames = [LK.getAsset('fireball0', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball00', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball01', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball02', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball03', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball04', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball05', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball06', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball07', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball08', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball09', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball1', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball10', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball11', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball12', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball13', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball14', { anchorX: 0.5, anchorY: 0.5 })]; var lineFrames = [LK.getAsset('fireball2', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball3', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball4', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball5', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball6', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball7', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball8', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball9', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball15', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball16', { anchorX: 0.5, anchorY: 0.5 })]; // Używamy attachAsset, który już dodaje jako dziecko i ustawia pozycję relatywną na podstawie anchor. self.bossGraphics = self.addChild(LK.getAsset('bossIdle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); self.bossGraphics.scaleX = 2; self.bossGraphics.scaleY = 2; // Dodajemy funkcje animacji bossa (NIE ataków fireballi) // Ta funkcja tworzy obiekt SpriteAnimation dla animacji ataku Bossa (nie fireballi) self.createBossAttackAnim = function () { var frames = [LK.getAsset('bossAttack0', {}), LK.getAsset('bossAttack1', {}), LK.getAsset('bossAttack2', {}), LK.getAsset('bossAttack3', {})]; var bossAttackAnim = new SpriteAnimation({ frames: frames, frameDuration: 100, loop: false, anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); // <<< DODAJ TO NATYCHMIAST PO STWORZENIU animacji bossAttackAnim.scaleX = 2; bossAttackAnim.scaleY = 2; return bossAttackAnim; }; // Ta funkcja zarządza grafiką Bossa (animacja ataku Bossa lub grafika idle) self.playBossAttackAnim = function (attackType) { // Upewnij się, że poprzednia animacja ataku Bossa jest całkowicie usunięta, zanim zaczniemy nową if (self.bossAttackAnim) { self.bossAttackAnim.stop(); // Zatrzymujemy animację // Usuń poprzednią animację z jej rodzica (Bossa) przed zniszczeniem if (self.bossAttackAnim.parent === self) { self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } else if (self.bossAttackAnim.parent) { // Fallback na wypadek, gdyby rodzic był inny self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } self.bossAttackAnim.destroy(); // Usuwamy poprzednią animację self.bossAttackAnim = null; // Ustawiamy na null po zniszczeniu } // *** Zmieniona logika: Zmiana grafiki bossa tylko jeśli to NIE jest atak szarży (attackType 2) *** if (attackType !== 2) { // Jeśli to atak koła (0) lub linii (1) // Usuń obecną główną grafikę bossa (idle) jako dziecko bossa if (self.bossGraphics && self.bossGraphics.parent === self) { self.bossGraphics.parent.removeChild(self.bossGraphics); } else if (self.bossGraphics && self.bossGraphics.parent) { // Fallback self.bossGraphics.parent.removeChild(self.bossGraphics); } self.bossGraphics = null; // Wyczyść referencję // Tworzymy nową animację bossa i dodajemy ją JAKO DZIECKO BOSSA self.bossAttackAnim = self.addChild(self.createBossAttackAnim()); // Pozycja animacji jako dziecka bossa jest już ustawiona na 0,0 w createBossAttackAnim // Metoda update dla TEJ NOWEJ animacji (definiowana tylko dla ataków 0 i 1) self.bossAttackAnim.update = function () { // Sprawdź, czy ten obiekt animacji jest nadal aktywny self.bossAttackAnim bossa // Użyj 'this' dla obiektu animacji, 'self' dla obiektu Boss if (self.bossAttackAnim !== this || !this.playing || this.frames.length === 0) { // Jeśli już nie jesteśmy aktualną animacją lub nie gramy, zakończ return; } this.frameTimer++; if (this.frameTimer >= this.frameDuration / (1000 / 60)) { // Przelicz ms na klatki gry (przy 60fps) this.frameTimer = 0; this.removeChildren(); // Usuń sprite klatki z kontenera animacji (z obiektu animacji) this.currentFrame++; if (this.currentFrame >= this.frames.length) { // Animacja skończona, wracamy do idle // *** Dodaj grafikę idle z powrotem JAKO DZIECKO BOSSA i ustaw self.bossGraphics *** self.bossGraphics = self.addChild(LK.getAsset('bossIdle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); self.bossGraphics.scaleX = 2; self.bossGraphics.scaleY = 2; // *** Usuń obiekt animacji z jego rodzica (Bossa) i zniszcz go *** if (this.parent === self) { this.parent.removeChild(this); // Użyj 'this' dla obiektu animacji } else if (this.parent) { // Fallback this.parent.removeChild(this); } this.destroy(); // Zniszcz obiekt animacji self.bossAttackAnim = null; // Wyczyść referencję w obiekcie bossa } else { this.addChild(this.frames[this.currentFrame]); // Dodaj sprite następnej klatki (do obiektu animacji) } } }; } // Else (attackType === 2, czyli chargeAttack): playBossAttackAnim nic nie robi z grafiką. // chargeAttack sam zadba o ustawienie grafiki 'idle' JAKO DZIECKO BOSSA. }; // *** STAROŚĆ WRACA! // Stara funkcja createAttack (przyjmuje x, y, duration) *** self.createAttack = function (x, y, duration, type) { var frames = []; if (type === 'circle') { frames = [LK.getAsset('fireball0', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball00', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball01', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball02', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball03', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball04', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball05', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball06', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball07', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball08', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball09', { anchorX: 0.5, anchorY: 0.5 })]; } else if (type === 'line') { frames = [LK.getAsset('fireball2', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball3', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball4', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball5', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball6', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball7', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball8', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball9', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball15', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('fireball16', { anchorX: 0.5, anchorY: 0.5 })]; } var spriteAnim = game.addChild(new SpriteAnimation({ frames: frames, frameDuration: 120, // czas w ms jednej klatki loop: true, anchorX: 0.5, anchorY: 0.5, x: x, y: y })); spriteAnim.scaleX = 1.6; spriteAnim.scaleY = 1.6; var attackData = { x: x, y: y, radius: 60, visual: spriteAnim, lifeTime: Math.floor(duration / (1000 / 60)), isActive: true }; self.attacks.push(attackData); LK.setTimeout(function () { var index = self.attacks.indexOf(attackData); if (index !== -1) { self.attacks.splice(index, 1); } if (spriteAnim && !spriteAnim.destroyed) { spriteAnim.destroy(); } }, duration); }; // *** STAROŚĆ WRACA! Stara funkcja circleAttack *** self.circleAttack = function () { LK.getSound('bossAttack').play(); var center = { x: self.x, y: self.y }; var count = isNewBossPlusMode ? 12 : 8; var radius = 300; var attackLifeTime = isNewBossPlusMode ? 2000 : 3000; for (var i = 0; i < count; i++) { var angle = i / count * Math.PI * 2; var x = center.x + Math.cos(angle) * radius; var y = center.y + Math.sin(angle) * radius; self.createAttack(x, y, attackLifeTime, 'circle'); // DODANE 'circle' } }; // Zmodyfikowana funkcja lineAttack, teraz używa starej sygnatury createAttack self.lineAttack = function () { LK.getSound('bossAttack').play(); if (!player) { return; } var targetX = player.x; var targetY = player.y; var count = isNewBossPlusMode ? 8 : 5; var attackLifeTime = isNewBossPlusMode ? 1500 : 2000; var delayBetweenAttacks = isNewBossPlusMode ? 100 : 200; for (var i = 0; i < count; i++) { var t = i / (count - 1); var posX = self.x + (targetX - self.x) * t; var posY = self.y + (targetY - self.y) * t; var delay = i * delayBetweenAttacks * self.attackSpeedMultiplier; LK.setTimeout(function (x, y) { return function () { if (self && !self.dead && gameState.currentState === "game") { self.createAttack(x, y, attackLifeTime, 'line'); // DODANE 'line' } }; }(posX, posY), delay); } }; // Zmodyfikowana funkcja chargeAttack, teraz używa starej sygnatury createAttack self.chargeAttack = function () { LK.getSound('bossAttack').play(); // Odtwórz dźwięk szarży // Upewnij się, że gracz istnieje if (!player) { return; } // Obsługa grafiki bossa podczas szarży (pokazuje grafikę idle) - to pozostaje z nowszego kodu, jest ok. if (self.bossAttackAnim) { self.bossAttackAnim.stop(); if (self.bossAttackAnim.parent === self) { self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } else if (self.bossAttackAnim.parent) { // Fallback na wypadek, gdyby rodzic był inny self.bossAttackAnim.parent.removeChild(self.bossAttackAnim); } self.bossAttackAnim.destroy(); self.bossAttackAnim = null; } if (self.bossGraphics && self.bossGraphics.parent === self) { self.bossGraphics.parent.removeChild(self.bossGraphics); } else if (self.bossGraphics && self.bossGraphics.parent) { // Fallback self.bossGraphics.parent.removeChild(self.bossGraphics); } self.bossGraphics = self.addChild(LK.getAsset('bossIdle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 })); self.bossGraphics.scaleX = 2; self.bossGraphics.scaleY = 2; // Oblicz kierunek do gracza i parametry szarży var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { dx /= distance; dy /= distance; } // Zapisz pozycję startową szarży PRZED tweenem var startX = self.x; var startY = self.y; var chargeDistance = isNewBossPlusMode ? 700 : 500; // Dystans szarży var chargeDuration = isNewBossPlusMode ? // Czas trwania szarży 600 : 800; var attacksOnPathCount = isNewBossPlusMode ? // Liczba pocisków wzdłuż ścieżki 8 : 5; var attackLifeTime = isNewBossPlusMode ? 1000 : 1500; // Czas życia pocisku // Tween (animacja ruchu) bossa podczas szarży tween(self, { x: self.x + dx * chargeDistance, y: self.y + dy * chargeDistance }, { duration: chargeDuration * self.attackSpeedMultiplier, // Czas trwania, skalowany easing: tween.easeIn, // Krzywa animacji onFinish: function onFinish() { // Funkcja po zakończeniu szarży if (self && !self.dead && gameState.currentState === "game") { // *** USUNIĘTO LOGIKĘ POWROTU NA POZYCJĘ STARTOWĄ *** // Usunięto: self.repositioning = true; // Usunięto: LK.setTimeout(...); // Usunięto: tween(self, { x: startX, y: startY }, { duration: 1000 * self.attackSpeedMultiplier, easing: tween.easeOut }); // Utworzenie ataków (fireballi) wzdłuż ścieżki szarży (pozostawiamy, bo to część ataku) // Pociski pojawiają się wzdłuż ścieżki od startX/startY do miejsca, gdzie boss zakończył szarżę (aktualne self.x/self.y) for (var i = 0; i < attacksOnPathCount; i++) { var t = i / (attacksOnPathCount - 1); // Współczynnik interpolacji var currentChargeX = self.x; // Aktualna pozycja bossa (koniec szarży) var currentChargeY = self.y; var posX = startX + (currentChargeX - startX) * t; // Interpolowana pozycja X var posY = startY + (currentChargeY - startY) * t; // Interpolowana pozycja Y // Utwórz atak // Wywołaj funkcję createAttack z 3 argumentami (stara sygnatura) self.createAttack(posX, posY, attackLifeTime, 'line'); // Używamy typu 'line' dla pocisków szarży } } } }); }; self.takeDamage = function (amount) { // Funkcja obsługująca otrzymywanie obrażeń przez bossa if (self.dead || gameState.currentState !== "game") { // Boss otrzymuje obrażenia tylko w stanie gry return; } self.health -= amount; // Zmniejsz zdrowie self.health = Math.max(0, self.health); // Upewnij się, że zdrowie nie spadnie poniżej zera LK.effects.flashObject(self, 0xFFFFFF, 200); // Efekt błysku if (!isNewBossPlusMode) { // Przejście fazy bossa przy 50% zdrowia tylko w standardowym trybie if (self.health <= self.maxHealth / 2 && self.phase === 1) { self.phase = 2; // Zmień fazę na 2 self.speed += 2; // Boss staje się szybszy (ruch) self.attackSpeedMultiplier *= 0.8; // Boss atakuje nieco szybciej tween(self, { tint: 0xFF3300 // Pomarańczowy/czerwony odcień }, { duration: 1000, easing: tween.easeInOut }); } } if (self.health <= 0) { self.die(); // Wywołaj funkcję śmierci } // ui.updateBossHealth jest w game.update }; self.die = function () { // Funkcja obsługująca śmierć bossa if (self.dead || gameState.currentState !== "game") { return; } self.dead = true; // Ustaw flagę śmierci stopSparkles(); if (!isNewBossPlusMode) { // Dźwięk zwycięstwa tylko dla STANDARDOWEGO trybu LK.getSound('victory').play(); } // Wyczyść pozostałe ataki przy śmierci bossa self.attacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { attack.visual.destroy(); } }); self.attacks = []; // Wyczyść tablicę ataków logicznych // Animacja śmierci bossa (zanikanie i powiększanie) tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Po zakończeniu animacji śmierci if (self && self.destroy && !self.destroyed) { self.destroy(); // Zniszcz obiekt bossa } storage.bossesDefeated = (storage.bossesDefeated || 0) + 1; // Zwiększ licznik pokonanych bossów // ZAWSZE przechodzimy do Grill Screena gameState.showGrillScreen(); } }); }; // Update method called every frame // Update method called every frame self.update = function () { // Główna metoda aktualizacji bossa // Aktualizuj tylko w stanie gry if (gameState.currentState !== "game" && gameState.currentState !== "rollMaster") { if (self.rolling) { self.rolling = false; if (self.idleAnimationSprite && !self.idleAnimationSprite.destroyed) { self.idleAnimationSprite.visible = true; self.idleAnimationSprite.play(); } } self.clearRollTimeouts(); return; } // Nie aktualizuj jeśli boss jest martwy (pozwól animacji śmierci działać) if (self.dead) { return; } // --- LOGIKA RUCHU BOSS --- // Poruszaj się w kierunku gracza jeśli spełnione warunki // Usunięto warunek !self.repositioning, ponieważ boss nie wchodzi już w ten stan po szarży if (self.attackCooldown > 30 && player && !player.dead) { var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var moveSpeed = self.speed; if (distance > 150) { // Poruszaj się tylko jeśli gracz jest dalej niż 150 jednostek var moveX = dx / distance * moveSpeed; var moveY = dy / distance * moveSpeed; var nextX = self.x + moveX; var nextY = self.y + moveY; // Ograniczenia areny var halfWidth = self.width * self.scaleX / 2; var halfHeight = self.height * self.scaleY / 2; var minX = 100 + halfWidth; var maxX = 2048 - 100 - halfWidth; var minY = 300 + halfHeight; var maxY = 2732 - 100 - halfHeight; self.x = Math.max(minX, Math.min(nextX, maxX)); self.y = Math.max(minY, Math.min(nextY, maxY)); // Grafika jako dziecko podąża automatycznie. } } // --- KONIEC LOGIKA RUCHU BOSS --- // --- OBSŁUGA ATAKÓW BOSS (Kolizje i Czas Życia Pocisków) --- // W starej metodzie wizualizacja ataku jest zarządzana przez tween i setTimeout w createAttack. // Obiekt logiczny ataku w self.attacks zawiera flagę isActive i jest usuwany przez setTimeout lub poniżej. // Nie aktualizujemy tutaj attack.visual.update ani isActive na podstawie klatek. // Aktualizuj istniejące ataki bossa (pociski) i sprawdzaj kolizje z graczem for (var i = self.attacks.length - 1; i >= 0; i--) { var attack = self.attacks[i]; // Czas życia w klatkach był zarządzany w starej metodzie w createAttack timeout. // Tutaj można dodać opcjonalne zmniejszanie lifeTime dla pewności, jeśli attackData.lifeTime jest używane gdzie indziej, // ale główny mechanizm usuwania jest w setTimeout w createAttack. // If (attack.lifeTime > 0) { attack.lifeTime--; } // Sprawdź kolizję z graczem jeśli spełnione warunki // Flaga isActive powinna być już ustawiona na true w createAttack (stara metoda) if (attack.isActive && player && !player.dead && !player.invulnerable && attack.visual && !attack.visual.destroyed) { var dx_p = player.x - attack.x; var dy_p = player.y - attack.y; var distance_p = Math.sqrt(dx_p * dx_p + dy_p * dy_p); var playerRadius_p = player.width / 2; // Zakładamy promień kolizji gracza var attackRadius = attack.radius; // Promień kolizji ataku z attackData if (distance_p < playerRadius_p + attackRadius) { player.takeDamage(1); // Gracz otrzymuje obrażenia // Usuń wizualizację i obiekt logiczny natychmiast po trafieniu if (attack.visual && attack.visual.destroy) { attack.visual.destroy(); } self.attacks.splice(i, 1); // Usuń trafiony atak logiczny // break; // Opcjonalnie: zatrzymaj sprawdzanie kolizji dla tej klatki po trafieniu gracza } } // Usuń atak z listy, jeśli jego wizualizacja została zniszczona (np. przez timeout w createAttack lub powyżej) // Obiekt logiczny powinien też zostać usunięty przez timeout w createAttack, ale to dodatkowe sprawdzenie nie zaszkodzi. if (attack.visual && attack.visual.destroyed) { self.attacks.splice(i, 1); } } // --- KONIEC OBSŁUGA ATAKÓW BOSS --- if (self.attackCooldown > 0) { // Zmniejszaj cooldown self.attackCooldown--; } // Sprawdź, czy nadszedł czas na nowy atak // Usunięto warunek !self.repositioning if (self.attackCooldown <= 0) { self.startAttackPattern(); } // startAttackPattern jest teraz definiowane wyżej, nie tutaj w update }; self.startAttackPattern = function () { // Resetuj cooldown ataku self.attackCooldown = isNewBossPlusMode ? 150 : 200; // Wybierz losowy typ ataku var attackType = Math.floor(Math.random() * 3); // 0, 1 lub 2 // Odtwórz animację ataku bossa self.playBossAttackAnim(attackType); // Wykonaj atak odpowiedniego typu if (attackType === 0) { // Atak kołowy self.circleAttack(); } else if (attackType === 1) { // Atak liniowy self.lineAttack(); } else { // Atak szarży self.chargeAttack(); } }; return self; // Zwróć instancję obiektu Boss }); // Zamykająca klamra dla Container.expand klasy Boss var Player = Container.expand(function () { var self = Container.call(this); // REMOVED: The old playerGraphics line is removed, // as we will now use idleAnimationSprite instead of a single graphic. // self.playerGraphics = self.attachAsset('player', { // anchorX: 0.5, // anchorY: 0.5 // }); // --- NEW: Create and add the Idle Animation --- // Define the frames for the idle animation. // Note: Ensure 'player', 'player1', and 'player2' are distinct assets in your project. var idleFrames = [LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('player1', { anchorX: 0.5, anchorY: 0.5 }), LK.getAsset('player2', { anchorX: 0.5, anchorY: 0.5 })]; // Create the SpriteAnimation object for idle. self.idleAnimationSprite = new SpriteAnimation({ frames: idleFrames, frameDuration: 200, // Set a duration for each frame (e.g., 200ms = 5 frames per second). Adjust as needed. loop: true, // Make the animation loop indefinitely. anchorX: 0.5, // Set anchor to center. anchorY: 0.5, x: 0, // Position relative to the player container center. y: 0 // Position relative to the player container center. }); // Add the idle animation to the player container and start it. self.addChild(self.idleAnimationSprite); self.idleAnimationSprite.play(); // Start the animation loop. // Player properties (kept the rest as in your original code) self.health = 5; // Default health (will be overridden in startGame) self.speed = 8; // Movement speed (NIE UŻYWANE OBECNIE W UPDATE?) self.rolling = false; // Flag for roll state self.rollDirection = { x: 0, y: 0 }; // Direction of roll self.rollSpeed = 20; // Speed during roll self.rollDuration = 300; // Roll duration in ms self.rollCooldown = 0; // Cooldown between rolls (frames) self.invulnerable = false; // Invulnerability flag self.invulnerabilityFrames = 0; // Frames of invulnerability left self.postHitInvulnerabilityTimer = null; // Dodana właściwość do przechowywania ID timera po trafieniu self.dead = false; // Dead state self.rollTimeoutId = null; // Store timeout ID for roll duration self.invulnerabilityTimeoutId = null; // Store timeout ID for invulnerability (NIE UŻYWANE?) self.rollAnimationInterval = null; // Store interval ID for roll animation self.hasRolledThroughBossThisRoll = false; // Track if boss was hit during this roll // Clear all roll-related timeouts and intervals self.clearRollTimeouts = function () { if (self.rollTimeoutId) { LK.clearTimeout(self.rollTimeoutId); self.rollTimeoutId = null; } if (self.rollAnimationInterval) { LK.clearInterval(self.rollAnimationInterval); self.rollAnimationInterval = null; } // Added: clear post-hit invulnerability timer if (self.postHitInvulnerabilityTimer) { LK.clearTimeout(self.postHitInvulnerabilityTimer); self.postHitInvulnerabilityTimer = null; } }; // Roll mechanic // Roll mechanic self.roll = function (direction) { if (!self.rolling && self.rollCooldown <= 0 && !self.dead) { // --- START MODYFIKACJI --- var targetScaleX = direction.x >= 0 || direction.x === 0 ? 1 : -1; // Ustal kierunek (domyślnie prawo, jeśli ruch pionowy) // --- KONIEC MODYFIKACJI --- self.rolling = true; self.rollDirection = direction; self.rollCooldown = 45; // self.invulnerable = true; // Invulnerability is now handled in update while rolling self.invulnerabilityFrames = 30; // Frames of invulnerability left during roll self.hasRolledThroughBossThisRoll = false; // --- MODIFIED: Hide idle animation and stop its playback before starting roll --- if (self.idleAnimationSprite && !self.idleAnimationSprite.destroyed) { self.idleAnimationSprite.stop(); // Stop the idle animation loop self.idleAnimationSprite.visible = false; // Hide the idle sprite immediately for a smooth transition } // Ukryj normalną grafikę gracza - This line is now redundant as we removed playerGraphics // if (self.playerGraphics && !self.playerGraphics.destroyed) { // self.playerGraphics.visible = false; // } // --- Roll Animation (turlanie) --- (Keep this logic, ensures temporary sprites are used for roll) var rollFrames = ['roll', 'roll0', 'roll1', 'roll2']; var currentFrame = 0; // Clear any existing roll animation interval before setting a new one if (self.rollAnimationInterval) { LK.clearInterval(self.rollAnimationInterval); } // Add the first frame of the roll animation var rollAnimationSprite = null; if (self && !self.destroyed) { // Check player exists before adding child rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: targetScaleX // <--- ZMIANA 1 })); } // Set up interval for the rest of the roll animation frames self.rollAnimationInterval = LK.setInterval(function () { // Check if player object still exists before continuing animation if (!self || self.destroyed) { LK.clearInterval(self.rollAnimationInterval); // Clear if player destroyed self.rollAnimationInterval = null; return; } if (rollAnimationSprite && rollAnimationSprite.destroy) { rollAnimationSprite.destroy(); rollAnimationSprite = null; // Clear reference } currentFrame = (currentFrame + 1) % rollFrames.length; // Ensure the player object still exists before adding children if (self && !self.destroyed) { rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: targetScaleX // <--- ZMIANA 2 })); } }, 70); // co 70ms zmienia klatkę podczas turlania // Clear any existing roll duration timeout before setting a new one if (self.rollTimeoutId) { LK.clearTimeout(self.rollTimeoutId); } // Set timeout for the end of the roll duration self.rollTimeoutId = LK.setTimeout(function () { // Ensure player object still exists before finishing roll sequence if (!self || self.destroyed) { return; } self.rolling = false; // --- After roll, finish roll animation and start stand-up --- if (self.rollAnimationInterval) { // Use self.rollAnimationInterval LK.clearInterval(self.rollAnimationInterval); self.rollAnimationInterval = null; // Clear reference } if (rollAnimationSprite && rollAnimationSprite.destroy) { rollAnimationSprite.destroy(); rollAnimationSprite = null; // Clear reference } // --- Stand Up Animation (wstawanie + machnięcie mieczem) --- (Keep this logic) var standUpFrames = ['roll3', 'roll4']; var standUpFrame = 0; var standUpSprite = null; if (self && !self.destroyed) { // Check player exists before adding child standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: targetScaleX // <--- ZMIANA 3 })); } var standUpInterval = LK.setInterval(function () { // Check if player object still exists if (!self || self.destroyed) { LK.clearInterval(standUpInterval); return; } if (standUpSprite && standUpSprite.destroy) { standUpSprite.destroy(); standUpSprite = null; // Clear reference } standUpFrame++; if (standUpFrame < standUpFrames.length) { // Ensure player object still exists before adding children if (self && !self.destroyed) { standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: targetScaleX // <--- ZMIANA 4 })); } } else { // Koniec animacji stand-up LK.clearInterval(standUpInterval); standUpInterval = null; // Clear reference if (standUpSprite && standUpSprite.destroy) { standUpSprite.destroy(); standUpSprite = null; // Clear reference } // Restore normal player graphic - MODIFIED: Show idle animation again // if (self.playerGraphics && !self.playerGraphics.destroyed) { // self.playerGraphics.visible = true; // } // --- NEW: Show idle animation and start playback after stand-up --- if (self.idleAnimationSprite && !self.idleAnimationSprite.destroyed) { // --- MODYFIKACJA: Zastosuj kierunek rowniez do idle po zakonczeniu roll --- self.idleAnimationSprite.scaleX = targetScaleX; // Ustaw kierunek idle taki jak ostatni kierunek roll self.idleAnimationSprite.visible = true; // Show the idle sprite self.idleAnimationSprite.play(); // Start the animation loop again } } }, 100); // 100ms między klatkami wstawania self.rollTimeoutId = null; // Clear reference after the whole roll sequence finishes }, self.rollDuration); } }; // <--- Corrected comma! // Take damage method (kept as in your original code) self.takeDamage = function (amount) { // Check if player can take damage (not invulnerable and not dead) if (!self.invulnerable && !self.dead) { self.health -= amount; // Zmniejsz zdrowie // Flash effect when hit LK.effects.flashObject(self, 0xFF0000, 200); // Check if player is dead if (self.health <= 0) { self.health = 0; self.die(); return; // Exit if player died } // *** NEW POST-HIT INVULNERABILITY LOGIC *** self.invulnerable = true; // Set invulnerability flag // Clear previous post-hit invulnerability timer if it exists if (self.postHitInvulnerabilityTimer) { LK.clearTimeout(self.postHitInvulnerabilityTimer); self.postHitInvulnerabilityTimer = null; // Reset timer ID } // Set a new timer for 2 seconds (2000 ms) self.postHitInvulnerabilityTimer = LK.setTimeout(function () { // Check if player still exists if (!self || self.destroyed) { return; } self.invulnerable = false; // End invulnerability // Ensure current visual sprite alpha is reset to 1 var currentVisualSprite = null; if (self.rolling && self.children.length > 0) { currentVisualSprite = self.children[0]; } else if (self.idleAnimationSprite && self.idleAnimationSprite.visible && self.idleAnimationSprite.children.length > 0) { currentVisualSprite = self.idleAnimationSprite.children[0]; } if (currentVisualSprite) { currentVisualSprite.alpha = 1; // Ensure full visibility } self.postHitInvulnerabilityTimer = null; // Clear timer reference }, 2000); // 2000 ms = 2 seconds // Removed old invulnerability logic based on invulnerabilityFrames. // *** END OF NEW LOGIC *** } }; // <--- Corrected comma! // Die method (kept as in your original code) self.die = function () { if (self.dead) { // Prevent multiple calls return; } self.dead = true; // Set death flag // Increment death counter storage.totalDeaths = (storage.totalDeaths || 0) + 1; // Increment max hearts after death (up to a maximum of 10) if (!storage.maxHearts || storage.maxHearts < 5) { // Ensure we start with at least 5 storage.maxHearts = 5; } if (storage.maxHearts < 10) { storage.maxHearts = storage.maxHearts + 1; } self.clearRollTimeouts(); // Cancel any ongoing roll // Death animation (fade out and scale up) tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Destroy the player object AFTER the animation finishes if (self && self.destroy && !self.destroyed) { self.destroy(); } // Set death reason flag to true (player died) gameOverReasonIsDeath = true; // Show game over screen - gameState manages the transition gameState.gameOver(true); // Pass true (player death) } }); }; // <--- Corrected comma! // Update method called every frame // Update method called every frame self.update = function () { // <--- Początek funkcji update // Update only in game state if (gameState.currentState !== "game" && gameState.currentState !== "rollMaster") { // Clear timers if game state changed if (self.rolling) { self.rolling = false; // Interrupt roll // Make sure idle animation is shown if game state changes while rolling if (self.idleAnimationSprite && !self.idleAnimationSprite.destroyed) { self.idleAnimationSprite.visible = true; self.idleAnimationSprite.play(); } } self.clearRollTimeouts(); return; } // Do not update if player is dead (but allow death animation to play) if (self.dead) { return; } // Handle roll cooldown if (self.rollCooldown > 0) { self.rollCooldown--; } // *** MODIFIED: Blink and Invulnerability Logic *** // (tutaj wklej całą logikę mrugania, którą miałeś) var currentVisualSprite = null; if (self.rolling && self.children.length > 0) { currentVisualSprite = self.children[0]; } else if (self.idleAnimationSprite && self.idleAnimationSprite.visible && self.idleAnimationSprite.children.length > 0) { currentVisualSprite = self.idleAnimationSprite.children[0]; } if (self.rolling) { if (self.invulnerabilityFrames > 0) { self.invulnerabilityFrames--; if (currentVisualSprite) { currentVisualSprite.alpha = self.invulnerabilityFrames % 4 > 1 ? 0.3 : 1; } } else { if (currentVisualSprite) { currentVisualSprite.alpha = 1; } } } else if (self.invulnerable && !self.rolling) { if (currentVisualSprite) { currentVisualSprite.alpha = Math.floor(Date.now() / 100) % 4 > 1 ? 0.3 : 1; } } else { if (currentVisualSprite && currentVisualSprite.alpha !== 1) { currentVisualSprite.alpha = 1; } } // *** END OF MODIFIED BLINKING AND INVULNERABILITY LOGIC *** // Handle movement during roll (kept as in your original code) if (self.rolling) { var rollDx = self.rollDirection.x * self.rollSpeed; var rollDy = self.rollDirection.y * self.rollSpeed; var nextX = self.x + rollDx; var nextY = self.y + rollDy; // Arena boundaries... (cała logika granic i kolizji z bossem podczas roll) var halfWidth = self.width / 2; var halfHeight = self.height / 2; if (currentVisualSprite) { halfWidth = currentVisualSprite.width / 2 * currentVisualSprite.scaleX; halfHeight = currentVisualSprite.height / 2 * currentVisualSprite.scaleY; } var minX = 100 + halfWidth; var maxX = 2048 - 100 - halfWidth; var minY = 300 + halfHeight; var maxY = 2732 - 100 - halfHeight; self.x = Math.max(minX, Math.min(nextX, maxX)); self.y = Math.max(minY, Math.min(nextY, maxY)); if (boss && !boss.dead && !self.hasRolledThroughBossThisRoll) { var dx_b = self.x - boss.x; var dy_b = self.y - boss.y; var distance_b = Math.sqrt(dx_b * dx_b + dy_b * dy_b); var playerCollisionRadius = self.width / 2; if (currentVisualSprite) { playerCollisionRadius = Math.max(currentVisualSprite.width, currentVisualSprite.height) / 2 * currentVisualSprite.scaleX; } var bossCollisionRadius = boss.width * boss.scaleX / 2; if (distance_b < playerCollisionRadius + bossCollisionRadius) { boss.takeDamage(10); self.hasRolledThroughBossThisRoll = true; LK.effects.flashObject(boss, 0x00FF00, 200); } } } else { // <--- Ten blok zastąpiłeś moim kodem // --- START MODYFIKACJI --- var targetScaleX = self.idleAnimationSprite.scaleX; // Domyślnie zachowaj obecny kierunek // --- KONIEC MODYFIKACJI --- if (gameState.isInputActive && gameState.currentState === "game" && player && !player.dead) { var targetX = gameState.currentInputPos.x; var targetY = gameState.currentInputPos.y; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { var normalizedX = dx / distance; var normalizedY = dy / distance; var moveSpeed = 3; // Możesz dostosować prędkość self.x += normalizedX * moveSpeed; self.y += normalizedY * moveSpeed; // --- START MODYFIKACJI: Ustaw kierunek na podstawie ruchu --- if (normalizedX > 0.1) { // Porusza się w prawo (z małym marginesem) targetScaleX = 1; } else if (normalizedX < -0.1) { // Porusza się w lewo (z małym marginesem) targetScaleX = -1; } // Jeśli normalizedX jest bliskie zeru, kierunek pozostaje niezmieniony // --- KONIEC MODYFIKACJI --- } } // --- START MODYFIKACJI: Zastosuj zmianę kierunku do animacji idle --- if (self.idleAnimationSprite && !self.idleAnimationSprite.destroyed) { self.idleAnimationSprite.scaleX = targetScaleX; } // --- KONIEC MODYFIKACJI --- } // <--- Koniec bloku else (normalny ruch) }; // <--- !!! WAŻNE: Ten nawias klamrowy i średnik zamykają funkcję self.update !!! return self; // <- TO ZWRACA OBIEKT GRACZA }); // <--- To zamyka Container.expand dla gracza var Shape = Container.expand(function (options) { var self = Container.call(this); // <--- Tutaj zaczyna się Shape // ... options = options || {}; var width = options.width || 100; var height = options.height || 100; var color = options.color || 0xFFFFFF; var shape = options.shape || 'box'; // Create the shape as an asset var asset = self.attachAsset(shape, { anchorX: 0.5, anchorY: 0.5, width: width, height: height, tint: color }); // Set width and height for easier access self.width = width; self.height = height; // Add anchor property to Shape for positioning self.anchor = { set: function set(x, y) { // This mimics the behavior of the anchor.set method self.anchorX = x; self.anchorY = y; } }; return self; }); var SpriteAnimation = Container.expand(function (options) { var self = Container.call(this); // Initialize with default values options = options || {}; self.frames = options.frames || []; self.frameDuration = options.frameDuration || 120; // Default 100ms per frame self.loop = options.loop !== undefined ? options.loop : true; self.currentFrame = 0; self.frameTimer = 0; self.playing = true; // Set initial position and anchor if provided if (options.x !== undefined) { self.x = options.x; } if (options.y !== undefined) { self.y = options.y; } // Handle anchor options if (options.anchorX !== undefined || options.anchorY !== undefined) { var anchorX = options.anchorX !== undefined ? options.anchorX : 0; var anchorY = options.anchorY !== undefined ? options.anchorY : 0; // Apply anchor to all frames self.frames.forEach(function (frame) { if (frame && frame.anchor) { frame.anchor.set(anchorX, anchorY); } }); } // Add the first frame to display initially if (self.frames.length > 0) { self.removeChildren(); var firstFrame = self.frames[self.currentFrame]; if (firstFrame && firstFrame.anchor) { firstFrame.anchor.set(options.anchorX || 0, options.anchorY || 0); } self.addChild(firstFrame); } // Animation update method self.update = function () { if (!self.playing || self.frames.length === 0) { return; } self.frameTimer++; if (self.frameTimer >= self.frameDuration / (1000 / 60)) { self.frameTimer = 0; if (self.children.length > 0) { self.removeChild(self.children[0]); // Usuwamy tylko 1 aktualną klatkę } self.currentFrame++; if (self.currentFrame >= self.frames.length) { if (self.loop) { self.currentFrame = 0; } else { self.currentFrame = self.frames.length - 1; self.playing = false; } } self.addChild(self.frames[self.currentFrame]); // Dodajemy nową klatkę } }; // Method to stop animation self.stop = function () { self.playing = false; }; // Method to start/resume animation self.play = function () { self.playing = true; }; // Method to set a specific frame self.gotoFrame = function (frameIndex) { if (frameIndex >= 0 && frameIndex < self.frames.length) { self.removeChildren(); self.currentFrame = frameIndex; self.addChild(self.frames[self.currentFrame]); } }; return self; }); var UI = Container.expand(function () { var self = Container.call(this); // Create heart containers (wizualizacja zdrowia) self.hearts = []; self.heartContainer = new Container(); // Kontener na ikony serc self.addChild(self.heartContainer); // Title and messages (teksty na ekranach) self.titleText = new Text2("ROLL SOULS", { size: 150, fill: 0xFFFFFF }); self.titleText.anchor.set(0.5, 0.5); self.addChild(self.titleText); self.messageText = new Text2("", { size: 60, fill: 0xFFFFFF }); self.messageText.anchor.set(0.5, 0.5); self.addChild(self.messageText); // Deaths counter (licznik śmierci) self.deathsText = new Text2("Deaths: 0", { size: 40, fill: 0xFFFFFF }); self.deathsText.anchor.set(1, 0); // Wyrównanie do prawego górnego rogu self.deathsText.x = 2048 - 50; // Pozycja X od prawej krawędzi self.deathsText.y = 50; // Pozycja Y od góry self.addChild(self.deathsText); // Tutorial text (teksty tutoriali) self.tutorialText = new Text2("", { size: 50, fill: 0xFFFFFF }); self.tutorialText.anchor.set(0.5, 0); // Wyrównanie do środka na górze self.tutorialText.x = 2048 / 2; // Pozycja X na środku self.tutorialText.y = 200; // Pozycja Y (może być różna w zależności od stanu) self.addChild(self.tutorialText); // --- Timer Text --- self.timerText = new Text2("2:00", { // Początkowe wyświetlanie czasu (2 minuty) size: 60, // Rozmiar czcionki fill: 0xFFFFFF // Biały kolor }); self.timerText.anchor.set(0, 0); // Punkt odniesienia w lewym górnym rogu self.timerText.x = 50; // Pozycja X od lewej krawędzi self.timerText.y = 50; // Pozycja Y od górnej krawędzi self.timerText.alpha = 0; // Domyślnie ukryty self.addChild(self.timerText); // --- Boss Health Bar (wizualizacja zdrowia bossa) --- self.bossHealthBarContainer = new Container(); // Kontener na pasek zdrowia bossa self.bossHealthBarContainer.x = 2048 / 2; // Wyśrodkuj poziomo self.bossHealthBarContainer.y = 150; // Pozycja Y (poniżej timera) self.bossHealthBarContainer.alpha = 0; // Domyślnie ukryty self.addChild(self.bossHealthBarContainer); var barWidth = 800; // Szerokość paska zdrowia (musi być taka sama jak szerokość assetu bossHpbar) var barHeight = 30; // Wysokość paska zdrowia (musi być taka sama jak wysokość assetu bossHpbar) // Tło paska zdrowia bossa (szare) - nadal używamy Shape dla tła // self.bossHealthBarBg = new Shape({ // width: barWidth, // height: barHeight, // color: 0x555555, // shape: 'box' // }); // self.bossHealthBarBg.anchor.set(0.5, 0.5); // self.bossHealthBarContainer.addChild(self.bossHealthBarBg); // Właściwy pasek zdrowia bossa (czerwony) - UŻYWAMY TERAZ NOWEGO ASSETU bossHpbar self.bossHealthBar = self.attachAsset('bossHpbar', { // Użyj assetu 'bossHpbar' anchorX: 0, // Ustaw punkt odniesienia na lewą krawędź (0), środek pionowo (0.5) anchorY: 0.5, x: -barWidth / 2 // Przesuń w lewo o połowę szerokości tła, żeby lewa krawędź paska zdrowia była na środku kontenera (czyli na środku ekranu) // Szerokość i wysokość są brane z assetu, nie ustawiamy ich tutaj jawnie (chyba że chcemy skalować) }); self.bossHealthBarContainer.addChild(self.bossHealthBar); // Aktualizuje wizualizację serc na UI self.updateHearts = function (current, max) { // Sprawdź poprawność danych wejściowych current = Math.max(0, current || 0); max = Math.max(1, max || 1); // Max musi być co najmniej 1 // Usuń istniejące serca tylko jeśli liczba się zmieniła lub nie ma serc if (self.hearts.length !== max) { while (self.hearts.length > 0) { var oldHeart = self.hearts.pop(); if (oldHeart && oldHeart.destroy && !oldHeart.destroyed) { oldHeart.destroy(); } } self.heartContainer.removeChildren(); // Usuń wizualne obiekty serc // Stwórz nowe ikony serc for (var i = 0; i < max; i++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, x: i * 50, // Pozycja w poziomie (rozstawione co 50 jednostek) y: 0, tint: i < current ? 0xFF0000 : 0x555555 // Czerwone jeśli zdrowie jest >=, szare jeśli < }); self.hearts.push(heart); self.heartContainer.addChild(heart); } // Wyśrodkuj kontener serc w poziomie i ustaw pozycję pionową self.heartContainer.x = (2048 - max * 50) / 2 + 25; // Dodano +25 aby wycentrować lepiej (bo anchorX = 0.5) self.heartContainer.y = 100; // Pozycja pionowa } else { // Zaktualizuj tylko kolory istniejących serc for (var j = 0; j < self.hearts.length; j++) { if (self.hearts[j]) { self.hearts[j].tint = j < current ? 0xFF0000 : 0x555555; } } } }; // Aktualizuje wizualizację paska zdrowia bossa self.updateBossHealth = function (current, max) { // Sprawdź poprawność danych current = Math.max(0, current || 0); max = Math.max(1, max || 1); // Max musi być co najmniej 1 // Oblicz szerokość paska zdrowia na podstawie aktualnego i maksymalnego zdrowia var barWidth = 800; // Całkowita szerokość paska (musi być taka sama jak szerokość assetu bossHpbar) var currentWidth = current / max * barWidth; // Upewnij się, że pasek zdrowia (obiekt sprite) istnieje przed aktualizacją if (self.bossHealthBar) { // Zmień szerokość sprite'a, aby odzwierciedlić aktualne zdrowie self.bossHealthBar.width = currentWidth; // Ustaw widoczność kontenera paska zdrowia bossa // Pokaż jeśli (stan gry lub game over) ORAZ (boss istnieje i żyje LUB jesteśmy w stanie game over z powodu czasu w Boss+) var shouldBeVisible = (gameState.currentState === "game" || gameState.currentState === "gameOver") && (boss && !boss.dead && current > 0 || gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode); self.bossHealthBarContainer.alpha = shouldBeVisible ? 1 : 0; } // Upewnij się, że pasek zdrowia tła też jest widoczny/ukryty tak samo jak kontener if (self.bossHealthBarBg) { self.bossHealthBarBg.alpha = self.bossHealthBarContainer.alpha; } }; // Wyświetla komunikat na środku ekranu self.showMessage = function (message, duration) { self.messageText.setText(message); self.messageText.alpha = 1; // Ustaw pełną przezroczystość // Wyczyść istniejący timer zanikania, jeśli istnieje if (self.messageTimeout) { LK.clearTimeout(self.messageTimeout); self.messageTimeout = null; } // Zaplanuj zanikanie po czasie, jeśli duration > 0 if (duration && duration > 0) { self.messageTimeout = LK.setTimeout(function () { tween(self.messageText, { alpha: 0 // Zaniknij do przezroczystości 0 }, { duration: 500, // Czas trwania zanikania onFinish: function onFinish() { self.messageTimeout = null; } // Reset ID }); }, duration); } else { // If duration is 0 or not provided, message stays visible self.messageText.alpha = 1; } }; // Wyświetla tekst tutorialu self.showTutorial = function (text) { self.tutorialText.setText(text); self.tutorialText.alpha = 1; // Ustaw pełną przezroczystość }; // Ukrywa tekst tutorialu (zanikając) self.hideTutorial = function () { tween(self.tutorialText, { alpha: 0 // Zaniknij }, { duration: 500 // Czas trwania }); }; // Aktualizuje licznik śmierci self.updateDeathsCounter = function () { var deaths = storage.totalDeaths || 0; self.deathsText.setText("Deaths: " + deaths); // Licznik zgonów widoczny tylko w walce self.deathsText.visible = gameState.currentState === "game"; }; // Aktualizuje wyświetlanie czasu timera self.updateTimerDisplay = function (seconds) { // Upewnij się, że sekundy są liczbą >= 0 seconds = Math.max(0, seconds || 0); var minutes = Math.floor(seconds / 60); var remainingSeconds = seconds % 60; // Formatuj sekundy z wiodącym zerem jeśli < 10 var formattedSeconds = remainingSeconds < 10 ? '0' + remainingSeconds : remainingSeconds; self.timerText.setText(minutes + ':' + formattedSeconds); }; // Pozycjonuje elementy UI w zależności od stanu gry // Ta funkcja jest wywoływana przez metody w gameState self.positionElements = function (state) { // Pozycje stałe (niezależne od stanu) self.deathsText.x = 2048 - 150; self.deathsText.y = 100; self.timerText.x = 350; self.timerText.y = 100; self.heartContainer.y = 80; // Pozycja pionowa serc self.bossHealthBarContainer.x = 2048 / 2; self.bossHealthBarContainer.y = 160; // Pasek HP bossa poniżej timera/serc // Resetuj widoczność przed ustawieniem dla danego stanu self.titleText.alpha = 0; self.messageText.alpha = 0; self.tutorialText.alpha = 0; self.timerText.alpha = 0; self.heartContainer.alpha = 0; self.bossHealthBarContainer.alpha = 0; // Zostanie włączony przez updateBossHealth jeśli trzeba self.deathsText.alpha = 1; // Licznik śmierci zawsze widoczny switch (state) { case "title": // Pozycje dla ekranu tytułowego self.titleText.x = 2048 / 2; self.titleText.y = 800; self.titleText.alpha = 1; // Pokaż tytuł self.messageText.x = 2048 / 2; self.messageText.y = 1000; // "Tap to Start" (ustawiane przez showMessage) // self.messageText.alpha = 1; // Ustawiane przez showMessage self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; // "Swipe to Roll..." (ustawiane przez showTutorial) // self.tutorialText.alpha = 1; // Ustawiane przez showTutorial break; case "game": // Pozycje dla stanu gry (walka z bossem) // self.titleText.alpha = 0; // Ustawione domyślnie self.messageText.x = 2048 / 2; self.messageText.y = 1500; // Komunikaty w trakcie gry (np. przyspieszenie bossa) // self.messageText.alpha = 0; // Ustawiane przez showMessage self.tutorialText.x = 2048 / 2; self.tutorialText.y = 200; // Tutorial w trakcie gry ("Swipe to roll away!") // self.tutorialText.alpha = 1; // Ustawiane przez showTutorial/hideTutorial // Pokaż serca i timer podczas gry self.heartContainer.alpha = 1; self.timerText.alpha = 1; // Pasek zdrowia bossa jest zarządzany przez updateBossHealth break; case "grillMenu": // Nowy stan dla Grill Screena // self.titleText.alpha = 0; // Ustawione domyślnie self.messageText.x = 2048 / 2; self.messageText.y = 500; // Pozycja dla komunikatów "Rest in peace..." // self.messageText.alpha = 0; // Ustawiane przez showMessage // self.tutorialText.alpha = 0; // Ustawione domyślnie break; case "gameOver": // Pozycje dla ekranu Game Over self.titleText.x = 2048 / 2; self.titleText.y = 800; // Tytuł "YOU DIED" lub komunikat zwycięstwa Boss+ // self.titleText.alpha = 1; // Ustawiane w gameState.gameOver self.messageText.x = 2048 / 2; self.messageText.y = 1000; // Pusty lub inny komunikat // self.messageText.alpha = 0; // Ustawiane w gameState.gameOver przez showMessage self.tutorialText.x = 2048 / 2; self.tutorialText.y = 1200; // Pusty // self.tutorialText.alpha = 0; // Ustawiane w gameState.gameOver przez showTutorial // Widoczność paska HP bossa zarządzana przez updateBossHealth break; case "intro": // Pozycje dla ekranów intro/tutoriali case "fakeTutorial": case "realTutorial": // Większość elementów ukryta (ustawione domyślnie wyżej) break; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 // Ciemne tło domyślne }); /**** * Game Code ****/ // Komentarze o assetach pominięte dla zwięzłości // Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia) var currentSceneElements = new Container(); // Zmienne gry var player; var boss; var ui; var walls = []; // Ściany areny bossa // Zmienna do przechowywania aktywnego tła sceny var currentBackground = null; // Flaga do śledzenia, czy jesteśmy w trybie New Boss+ var isNewBossPlusMode = false; // Flaga do śledzenia przyczyny Game Over (true = gracz zginął, false = czas minął) var gameOverReasonIsDeath = false; // Funkcja do czyszczenia elementów z kontenera currentSceneElements (nie usuwa UI ani tła) function clearScene() { while (currentSceneElements.children.length > 0) { var child = currentSceneElements.children[0]; if (child && child.destroy) { child.destroy(); // Użyj destroy jeśli dostępne } else if (child && child.parent) { child.parent.removeChild(child); // Fallback } else { // Jeśli obiekt nie ma ani destroy ani parent, usuń go z tablicy (choć to nie powinno się zdarzyć) currentSceneElements.children.shift(); } } // Usuń też dzieci bezpośrednio z 'game', które mogły zostać dodane poza 'currentSceneElements' w scenach, // ale UWAŻAJ, aby nie usunąć stałych elementów jak UI, walls, currentSceneElements sam w sobie. // Lepsze podejście: zawsze dodawaj elementy specyficzne dla sceny do currentSceneElements. } // --- SPARKLE SYSTEM NA ARENIE --- var sparkleIntervalId = null; function startSparkles() { if (sparkleIntervalId !== null) { return; } sparkleIntervalId = LK.setInterval(function () { if (!game || game.destroyed) { return; } var colors = ['sparkred', 'sparkgreen', 'sparkblue']; var randomColor = colors[Math.floor(Math.random() * colors.length)]; var x = Math.random() * (2048 - 200) + 100; var y = Math.random() * (2732 - 700) + 400; var sparkle = game.addChild(LK.getAsset(randomColor, { anchorX: 0.5, anchorY: 0.5, x: x, y: y, alpha: 0 })); tween(sparkle, { alpha: 1, y: sparkle.y - 30 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { tween(sparkle, { alpha: 0 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { if (sparkle && sparkle.destroy && !sparkle.destroyed) { sparkle.destroy(); } } }); } }); }, 800); } function stopSparkles() { if (sparkleIntervalId !== null) { LK.clearInterval(sparkleIntervalId); sparkleIntervalId = null; } } // Obiekt zarządzający stanami gry var gameState = { currentState: "title", gameDuration: 120, remainingTime: 0, gameTimerInterval: null, fakeTutorialTimerId: null, bossSpeedIncreased: false, rollMasterTime: 0, rollMasterDifficulty: 1, rollMasterTimerInterval: null, rollMasterAttacks: [], attackSpawnTimer: 0, attackSpawnInterval: 120, explosionSpawnTimer: 0, // <<< NOWE: Timer dla eksplozji explosionSpawnInterval: 240, // <<< NOWE: Interwał dla eksplozji (np. 4s) rollMasterHighScore: 0, isInputActive: false, currentInputPos: { x: 0, y: 0 }, touchStart: { x: 0, y: 0 }, touchEnd: { x: 0, y: 0 }, grillMenuEffects: [], init: function init() { // Resetuj stan przy każdym uruchomieniu storage.totalDeaths = 0; storage.maxHearts = 5; // Zaczynaj zawsze z 5 sercami isNewBossPlusMode = false; gameOverReasonIsDeath = false; this.rollMasterHighScore = storage.rollMasterHighScore || 0; // Odczytaj zapisany high score game.setBackgroundColor(0x111111); // Ustaw domyślny kolor tła ui = game.addChild(new UI()); // Stwórz UI // Zainicjalizuj UI dla ekranu tytułowego ui.updateDeathsCounter(); ui.updateHearts(storage.maxHearts, storage.maxHearts); // Pokaż początkowe serca ui.positionElements("title"); // Pozycjonuj elementy UI dla tytułu game.addChild(currentSceneElements); // Dodaj kontener na elementy scen // Rozpocznij muzykę w tle LK.playMusic('introMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); this.showTitleScreen(); // Rozpocznij od ekranu tytułowego }, // ---------- Metody przejścia między stanami ---------- showTitleScreen: function showTitleScreen() { // Zatrzymaj timery poprzedniego stanu if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; clearScene(); // Wyczyść elementy poprzedniej sceny (Grill, GameOver, itp.) // Usuń stare tło, jeśli istnieje if (currentBackground) { tween.stop(currentBackground); // Zatrzymaj animacje tła currentBackground.destroy(); currentBackground = null; } this.currentState = "title"; game.setBackgroundColor(0x1a1a1a); // Ciemne tło bazowe dla tytułu // Dodaj tło ekranu tytułowego currentBackground = LK.getAsset('titleBg', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChildAt(currentBackground, 0); // Dodaj na spód // START SCREEN PARTICLES var startScreenParticles = []; function spawnBackgroundParticle() { var particle = LK.getAsset('sparticlebg', { x: Math.random() * 2048, y: -50, scale: 0.3 + Math.random() * 0.3, anchorX: 0.5, anchorY: 0.5, alpha: 0.7 + Math.random() * 0.3 }); game.addChildAt(particle, 0); // Dodaj na spód startScreenParticles.push(particle); // Animacja cząsteczki tween(particle, { y: 1300, alpha: 0 }, { duration: (2 + Math.random() * 2) * 1000, easing: tween.linear, onFinish: function onFinish() { game.removeChild(particle); var index = startScreenParticles.indexOf(particle); if (index !== -1) { startScreenParticles.splice(index, 1); } } }); } var particleIntervalId = LK.setInterval(spawnBackgroundParticle, 400); // Ukryj gracza/bossa jeśli istnieją if (player && player.destroy) { player.destroy(); } player = null; if (boss && boss.destroy) { boss.destroy(); } boss = null; // Pokaż ściany (jako część tła menu) walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); // Ustaw UI dla tytułu ui.positionElements("title"); ui.titleText.setText("Welcome Unchosen"); ui.showMessage("Tap to Start", 0); ui.showTutorial("Swipe to Roll - Death is Progress"); ui.showMessage("Tap to Start", 0); ui.showTutorial("Swipe to Roll - Death is Progress"); var testGoToGrillButton = new Container(); testGoToGrillButton.interactive = true; testGoToGrillButton.cursor = "pointer"; // --- ZMIANA: Ustawienie pozycji przycisku NA ŚRODKU EKRANU --- testGoToGrillButton.x = 2048 / 2; // Pozycja X na środku testGoToGrillButton.y = 2732 / 2; // Pozycja Y na środku // (Jeśli Twoja gra ma inne wymiary, dostosuj 2048 i 2732) // --- Koniec zmiany pozycji --- game.addChild(testGoToGrillButton); // Tło przycisku (niebieskawe, żeby było widoczne) var testButtonBg = new Shape({ width: 350, height: 80, color: 0x4444DD, shape: 'box' }); // Ważne: Ustawienie punktu zakotwiczenia na środek (0.5, 0.5) powoduje, // że przycisk będzie wycentrowany dokładnie w punkcie (x, y) podanym wyżej. testButtonBg.anchor.set(0.5, 0.5); testGoToGrillButton.addChild(testButtonBg); // Tekst na przycisku var testButtonText = new Text2("TEST: Idź do Grilla", { size: 30, fill: 0xFFFFFF }); // Wyśrodkowanie tekstu wewnątrz przycisku testButtonText.anchor.set(0.5, 0.5); testGoToGrillButton.addChild(testButtonText); // Akcja po kliknięciu (ta sama co poprzednio) testGoToGrillButton.down = function () { console.log("TEST: Przejście do Grill Menu"); // Zatrzymujemy muzykę intro / efekty ... try { var introMusic = LK.getMusic('introMusic'); if (introMusic && introMusic.playing) { introMusic.stop(); } if (typeof particleIntervalId !== 'undefined' && particleIntervalId !== null) { LK.clearInterval(particleIntervalId); particleIntervalId = null; } if (typeof startScreenParticles !== 'undefined' && startScreenParticles.length > 0) { startScreenParticles.forEach(function (p) { if (p && p.destroy) { p.destroy(); } }); startScreenParticles = []; } } catch (e) { console.warn("Problem przy zatrzymywaniu elementów z ekranu tytułowego."); } // Bezpośrednie wywołanie funkcji przejścia do Grill Menu if (gameState && typeof gameState.showGrillScreen === 'function') { gameState.showGrillScreen(); } else { console.error("Nie można wywołać gameState.showGrillScreen()!"); } }; // <-- Koniec definicji akcji dla kliknięcia // Resetuj stan gestu this.touchStart = { x: 0, y: 0 }; this.touchEnd = { x: 0, y: 0 }; // --- Tap anywhere on title screen to start the intro --- game.on('down', function () { if (gameState.currentState === 'title') { gameState.showIntro(); } }); }, showIntro: function showIntro() { if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; // 🔥 STOP particles from title screen if (typeof particleIntervalId !== 'undefined') { LK.clearInterval(particleIntervalId); particleIntervalId = null; } if (typeof startScreenParticles !== 'undefined') { startScreenParticles.forEach(function (p) { if (p && p.destroy) { p.destroy(); } }); startScreenParticles = []; } clearScene(); if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); currentBackground = null; } // Usuń stare tło this.currentState = "intro"; game.setBackgroundColor(0x111111); // Dodaj tło intro i animację zoom currentBackground = LK.getAsset('introBg', { anchorX: 0.5, anchorY: 0.4, x: 1000, y: 1000, scaleX: 1, scaleY: 1 }); game.addChildAt(currentBackground, 0); tween(currentBackground, { scaleX: 1.5, scaleY: 1.5 }, { duration: 38000, easing: tween.linear, repeat: Infinity, yoyo: true }); // Ukryj ściany na czas intro if (player && player.destroy) { player.destroy(); } player = null; // Upewnij się, że nie ma gracza/bossa if (boss && boss.destroy) { boss.destroy(); } boss = null; walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ukryj ściany ui.positionElements("intro"); // Ustaw UI dla intro (głównie ukrywa elementy) // --- Teksty intro z timerami --- // Funkcja do wyświetlania tekstu z fade-in, fade-out i kontynuacją function showIntroText(text, delay, onComplete) { var introText = new Text2(text, { size: 90, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800 }); introText.anchor.set(0.5, 0.5); introText.x = 2048 / 2; introText.y = 2232 / 2 - 400; introText.alpha = 0; currentSceneElements.addChild(introText); LK.setTimeout(function () { tween(introText, { alpha: 1 }, { duration: 2000, // Fade in = 1s easing: tween.easeInOut }); LK.setTimeout(function () { tween(introText, { alpha: 0 }, { duration: 2000, // Fade out = 1s easing: tween.easeInOut }); LK.setTimeout(function () { try { if (introText && introText.destroy) { introText.destroy(); } } catch (e) {} if (onComplete) { onComplete(); } }, 3000); // 1s fade out + 2s przerwy 1; // Wygląda na pozostałość, prawdopodobnie nie robi nic }, 3000); // 3 sekundy pełnej widoczności }, delay); } // --- Startujemy sekwencję intro --- // --- Startujemy sekwencję intro --- showIntroText('From the authors of Dark Souls...', 2000, function () { showIntroText('I have no information. I don’t know them.', 0, function () { showIntroText('Silas GameStudio...', 0, function () { showIntroText('Still looking for funding.', 0, function () { showIntroText('Oh, and it doesn’t even exist.', 0, function () { // <-- NOWY TEKST // Po ostatnim tekście przechodzimy do howToScene LK.setTimeout(function () { if (typeof gameState.showFakeTutorial === 'function') { gameState.showFakeTutorial(); } else { console.error("Error: gameState.showFakeTutorial is not defined"); gameState.showTitleScreen(); // Fallback to title screen } }, 1000); }); }); }); }); }); }, // Koniec funkcji showIntro // --- KONIEC SEKWENCJI INTRO --- // <--- Koniec funkcji showIntro showFakeTutorial: function showFakeTutorial() { clearScene(); // Wyczyść tekst intro if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; // Wyczyść stary timer // --- DODAJ TO TUTAJ --- var introMusic = LK.music; if (introMusic) { LK.tween(introMusic, { volume: 0 }, { duration: 3000, onFinish: function onFinish() { introMusic.stop(); } }); } LK.playMusic('RollSouls', { loop: true, fade: { start: 0, end: 0.7, duration: 3000 } }); // --- KONIEC DODANEGO --- this.currentState = "fakeTutorial"; // Zachowaj tło intro? Jeśli tak, usuń game.setBackgroundColor i nie niszcz currentBackground // game.setBackgroundColor(0x000000); // Czarne tło - usunięte, aby zachować tło intro ui.positionElements("fakeTutorial"); // Ustaw UI // Tekst fałszywego tutorialu var instructionText = new Text2('Press LPM to block.. or don’t press.', { size: 100, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800, dropShadow: true, dropShadowColor: 0x000000, // czarny cień dropShadowDistance: 3, // odległość cienia dropShadowBlur: 4, // lekkie rozmycie cienia dropShadowAngle: Math.PI / 4 // kąt cienia }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 2048 / 2; instructionText.y = 2732 / 2; currentSceneElements.addChild(instructionText); // Timer przechodzący do prawdziwego tutorialu this.fakeTutorialTimerId = LK.setTimeout(function () { try { if (instructionText && instructionText.destroy) { instructionText.destroy(); } } catch (e) {} gameState.fakeTutorialTimerId = null; // Zresetuj ID // *** POPRAWKA: Sprawdzenie przed wywołaniem *** if (gameState && typeof gameState.showRealTutorial === 'function') { gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu } else { console.error("Error: gameState.showRealTutorial is not a function in fakeTutorial timeout!"); gameState.showTitleScreen(); // Awaryjny powrót do tytułu } } /*.bind(this)*/, 6000); // 6 sekund }, // *** NOWA FUNKCJA: Prawdziwy Tutorial (oddzielona od startGame) *** showRealTutorial: function showRealTutorial() { if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; // Wyczyść timer fake tutorialu clearScene(); // Wyczyść elementy fake tutorialu/fake game over // Zatrzymaj animację tła intro i usuń je if (currentBackground) { tween.stop(currentBackground); currentBackground.destroy(); currentBackground = null; } this.currentState = "realTutorial"; // Ustaw tło tutoriala if (currentBackground && currentBackground.destroy) { currentBackground.destroy(); } currentBackground = LK.getAsset('realtutorialbg', {}); game.addChildAt(currentBackground, 0); // Pokaż ściany areny walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); ui.positionElements("realTutorial"); // Ustaw UI (głównie ukrywa elementy gry) // --- NIE MA tutorialTitle ani tutorialDesc (tekst jest na grafice) --- // Przycisk "Let's Roll!" do rozpoczęcia gry var startButton = new Container(); startButton.interactive = true; startButton.x = 2048 / 2; startButton.y = 1250; // Zmniejszona pozycja Y, był 1500 currentSceneElements.addChild(startButton); // Powiększone tło przycisku var startButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); startButtonBg.scale.set(1.3); // Skala tła przycisku zwiększona startButton.addChild(startButtonBg); // Powiększony i wyraźniejszy tekst przycisku var startButtonText = new Text2("Let's Roll!", { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 6 }); startButtonText.anchor.set(0.5, 0.5); startButton.addChild(startButtonText); // Funkcja kliknięcia przycisku startButton.down = function () { gameState.startGame(); // Rozpocznij grę }; }, // Obsługa inputu w stanie fakeTutorial (fałszywa śmierć) handleFakeTutorialInput: function handleFakeTutorialInput() { // Wyczyść timer, który miał prowadzić do prawdziwego tutorialu if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); this.fakeTutorialTimerId = null; // Zresetuj ID timera } clearScene(); // Wyczyść ekran fałszywego tutorialu // Ustaw stan tymczasowo (można by dodać dedykowany stan 'fakeGameOver') this.currentState = "fakeGameOver"; // Zmieniono z "gameOver" // Zatrzymaj animację tła intro, ale nie usuwaj go jeszcze if (currentBackground) { tween.stop(currentBackground); } ui.positionElements("gameOver"); // Użyj pozycjonowania gameOver dla tekstów "YOU DIED" // Wyświetl "YOU DIED" na czerwono var diedText = new Text2("YOU DIED", { size: 150, fill: 0xFF0000 }); // Czerwony kolor diedText.anchor.set(0.5, 0.5); diedText.x = 2048 / 2; diedText.y = 600; currentSceneElements.addChild(diedText); // Wyświetl wyjaśnienie var explanationText = new Text2("Did you check the title of the game?", { size: 70, fill: 0xFFFFFF, align: 'center', wordWrap: true, wordWrapWidth: 1800, dropShadow: true, dropShadowColor: 0x000000, // cień czarny dropShadowDistance: 3, // odległość cienia dropShadowBlur: 4, // lekkie rozmycie cienia dropShadowAngle: Math.PI / 4 // kąt padania cienia (45 stopni) }); explanationText.anchor.set(0.5, 0.5); explanationText.x = 2048 / 2; explanationText.y = 800; currentSceneElements.addChild(explanationText); // Dodaj przycisk "How to play again" var howToPlayButtonContainer = new Container(); howToPlayButtonContainer.interactive = true; howToPlayButtonContainer.x = 2048 / 2; howToPlayButtonContainer.y = 1300; // Pozycja przycisku currentSceneElements.addChild(howToPlayButtonContainer); var buttonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); howToPlayButtonContainer.addChild(buttonBg); var buttonText = new Text2('How to play', { size: 50, fill: 0xFFFFFF }); // Zmieniono tekst i rozmiar buttonText.anchor.set(0.5, 0.5); howToPlayButtonContainer.addChild(buttonText); // Akcja przycisku: Przejdź do prawdziwego tutorialu howToPlayButtonContainer.down = function () { // *** POPRAWKA: Sprawdzenie przed wywołaniem *** if (gameState && typeof gameState.showRealTutorial === 'function') { gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu } else { console.error("Error: gameState.showRealTutorial is not a function in fake input handler!"); gameState.showTitleScreen(); // Awaryjny powrót do tytułu } }; }, // Przejście do stanu gry (walka z bossem) - wywoływane z Prawdziwego Tutorialu startGame: function startGame() { var _this = this; // Wyczyść timery z poprzednich stanów if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; // Wyczyść elementy poprzedniej sceny (te w currentSceneElements) clearScene(); // Usuń tło tutorialu/intro jeśli istnieje if (currentBackground) { tween.stop(currentBackground); // Zatrzymaj ewentualne tweens na tle currentBackground.destroy(); // Zniszcz obiekt tła currentBackground = null; // Wyczyść referencję } this.currentState = "game"; // Ustaw stan na "game" // --- DODANY KOD: Usuń iskry i gwiazdki z Grill Menu przechowywane w tablicy --- if (gameState.grillMenuEffects && gameState.grillMenuEffects.length > 0) { gameState.grillMenuEffects.forEach(function (effect) { // Upewnij się, że obiekt istnieje i ma rodzica (powinien nim być obiekt game) if (effect && effect.parent) { tween.stop(effect); // Zatrzymaj ewentualne tweens (chociaż powinny już być zatrzymane przez sprawdzenie stanu w animacji) effect.parent.removeChild(effect); // *** Najpierw usuń obiekt z jego rodzica (usuwa ze sceny) *** // Opcjonalnie spróbuj zniszczyć obiekt, jeśli metoda destroy istnieje i nie był już zniszczony if (effect.destroy && !effect.destroyed) { effect.destroy(); // Zniszcz obiekt (zwalnia zasoby) } } else if (effect && effect.destroy && !effect.destroyed) { // Jeśli z jakiegoś powodu obiekt nie ma rodzica (dziwne), ale ma metodę destroy, spróbuj ją wywołać console.warn("Sparkle/Star object found without parent but has destroy method:", effect); // Log pomocniczy effect.destroy(); } else { // Jeśli obiekt istnieje, ale nie ma ani parenta, ani destroy, zaloguj, bo to nietypowa sytuacja console.warn("Sparkle/Star object found without parent or destroy method:", effect); // Log pomocniczy } }); gameState.grillMenuEffects = []; // Wyczyść tablicę po próbie zniszczenia/usunięcia obiektów } // --- KONIEC DODANEGO KODU --- // Najpierw ustaw tło kolorem (żeby nie zamalować areny później) game.setBackgroundColor(0x111111); // Dodaj nowe tło areny podczas walki var arenaBg = LK.getAsset('arena', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChildAt(arenaBg, 0); // Dodaj arenę na spód (zIndex 0) // Rozpocznij animacje iskier na arenie (te, które mają być widoczne) startSparkles(); // Stwórz gracza (jeśli nie istnieje) if (player && player.destroy) { player.destroy(); // Zniszcz starego gracza } player = game.addChild(new Player()); // Stwórz nowego gracza player.health = storage.maxHearts || 5; // Ustaw zdrowie gracza // Resetuj stan gracza player.rolling = false; player.invulnerable = false; player.invulnerabilityFrames = 0; player.rollCooldown = 0; if (player && typeof player.clearRollTimeouts === 'function') { player.clearRollTimeouts(); } player.hasRolledThroughBossThisRoll = false; player.x = 2048 / 2; player.y = 2732 / 2 + 400; // Pozycja startowa gracza player.alpha = 1; // Upewnij się, że jest widoczny // Stwórz bossa (jeśli nie istnieje) if (boss && boss.destroy) { boss.destroy(); // Zniszcz starego bossa } boss = game.addChild(new Boss()); // Stwórz nowego bossa boss.x = 2048 / 2; boss.y = 2732 / 2 - 400; // Pozycja startowa bossa boss.alpha = 1; // Upewnij się, że jest widoczny // Resetuj stan bossa boss.attackCooldown = 90; // Daj graczowi chwilę przed pierwszym atakiem (ok 1.5s) boss.attacks = []; boss.attackSpeedMultiplier = 1; boss.repositioning = false; boss.dead = false; // Upewnij się, że nie jest martwy boss.phase = 1; // Zacznij od fazy 1 boss.tint = 0xFFFFFF; // Resetuj kolor (na wypadek przejścia fazy w poprzedniej grze) boss.scale.set(1, 1); // Resetuj skalę (na wypadek animacji śmierci) // Ustaw parametry bossa i gry zależnie od trybu if (isNewBossPlusMode) { boss.maxHealth = 1000; boss.health = boss.maxHealth; this.gameDuration = 600; // 10 minut boss.attackSpeedMultiplier = 0.6; // Szybsze ataki od początku boss.speed = 7; // Szybszy ruch console.log("Starting NEW BOSS+ mode. HP:", boss.maxHealth, "Time:", this.gameDuration); } else { boss.maxHealth = 200; // Standardowy boss HP boss.health = boss.maxHealth; this.gameDuration = 120; // 2 minuty boss.speed = 5; // Standardowa prędkość boss.attackSpeedMultiplier = 1; // Standardowa prędkość ataków console.log("Starting STANDARD mode. HP:", boss.maxHealth, "Time:", this.gameDuration); } // Pokaż ściany areny (jeśli są częścią areny) walls.forEach(function (wall) { if (wall) { wall.alpha = 1; } }); // Ustaw UI dla stanu gry ui.positionElements("game"); ui.updateHearts(player.health, storage.maxHearts); ui.showTutorial("Swipe to Roll!"); // Wyświetl krótki tutorial ui.updateBossHealth(boss.health, boss.maxHealth); // Pokaż pasek HP bossa // --- Timer walki z bossem --- this.remainingTime = this.gameDuration; // Ustaw początkowy czas ui.updateTimerDisplay(this.remainingTime); // Wyświetl czas // Rozpocznij interwał timera if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } // Wyczyść stary timer this.bossSpeedIncreased = false; // Resetuj flagę przyspieszenia bossa // Użyj funkcji strzałkowej, aby this wewnątrz setInterval odnosiło się do gameState this.gameTimerInterval = LK.setInterval(function () { // Użyto funkcji strzałkowej dla zachowania kontekstu 'this' // Aktualizuj tylko w stanie gry if (_this.currentState === "game") { // Użyj this.currentState _this.remainingTime--; // Użyj this.remainingTime ui.updateTimerDisplay(_this.remainingTime); // Użyj this.remainingTime // --- Przyspieszenie bossa - TYLKO W STANDARDOWYM TRYBIE --- if (!isNewBossPlusMode) { var accelerationThreshold = _this.gameDuration - 60; // Użyj this.gameDuration if (_this.remainingTime <= accelerationThreshold && !_this.bossSpeedIncreased) { // Użyj this.remainingTime, this.bossSpeedIncreased _this.bossSpeedIncreased = true; // Użyj this.bossSpeedIncreased if (boss && !boss.dead) { // Sprawdź czy boss nadal żyje boss.attackSpeedMultiplier *= 0.7; // Przyspiesz ataki boss.speed += 1; // Lekko przyspiesz ruch ui.showMessage("Boss attacks faster!", 2000); // Komunikat } } } // --- Koniec Przyspieszenia bossa --- // Sprawdź koniec gry (czas minął) if (_this.remainingTime <= 0) { // Użyj this.remainingTime LK.clearInterval(_this.gameTimerInterval); // Użyj this.gameTimerInterval _this.gameTimerInterval = null; // Użyj this.gameTimerInterval if (isNewBossPlusMode) { // Czas minął w Boss+ -> Zwycięstwo przez przetrwanie gameOverReasonIsDeath = false; // Przyczyna: czas gameState.gameOver(false); // Przejdź do ekranu końca gry (specjalny komunikat) } else { // Czas minął w standardowym trybie -> Przejdź do Grill Menu // Zakładamy, że przetrwanie 2 minut w standardowym trybie to też "zwycięstwo" gameState.showGrillScreen(); } } } else { // Jeśli stan gry się zmienił, zatrzymaj timer if (_this.gameTimerInterval) { // Użyj this.gameTimerInterval LK.clearInterval(_this.gameTimerInterval); } _this.gameTimerInterval = null; // Użyj this.gameTimerInterval } }, 1000); // Interwał co 1 sekundę // Ukryj tutorial po chwili // Użyj funkcji strzałkowej, aby this wewnątrz setTimeout odnosiło się do gameState LK.setTimeout(function () { // Użyto funkcji strzałkowej dla zachowania kontekstu 'this' if (_this.currentState === "game") { // Użyj this.currentState ui.hideTutorial(); } // Rozpoczęcie ataków bossa jest teraz obsługiwane przez Boss.update i jego cooldown }, 3000); // 3 sekundy opóźnienia }, // victory() - nieużywane, logika w timerze i boss.die() showGrillScreen: function showGrillScreen() { // Wyczyść timery (pozostaw ten fragment) if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; if (this.fakeTutorialTimerId) { LK.clearTimeout(this.fakeTutorialTimerId); } this.fakeTutorialTimerId = null; this.bossSpeedIncreased = false; // Reset flagi // --- DODANY KOD: Tablica do przechowywania efektów z menu grilla --- gameState.grillMenuEffects = []; // Tworzymy nową tablicę przy wejściu do menu // --- KONIEC DODANEGO KODU --- // --- Rozpoczęcie agresywnego czyszczenia sceny --- // Stwórz listę obiektów (kontenerów, UI, ścian), które powinny pozostać w 'game'. // Upewnij się, że 'ui', 'currentSceneElements' i wszystkie obiekty w tablicy 'walls' są tutaj. var childrenToKeep = [ui, currentSceneElements].concat(walls); // Przejdź przez wszystkie dzieci głównego obiektu 'game' od końca do początku for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; // Sprawdź, czy obecne dziecko NIE jest na liście obiektów do zachowania if (childrenToKeep.indexOf(child) === -1) { // console.log("Usuwam obiekt:", child); // Możesz tymczasowo odkomentować do debugowania // Usuń i zniszcz obiekt if (child && child.destroy) { child.destroy(); // Użyj destroy jeśli dostępne (zalecane w LK) } else if (child && child.parent) { child.parent.removeChild(child); // Fallback - usuń z rodzica } } } // Po agresywnym czyszczeniu, upewnij się, że globalne zmienne gracza, bossa i tła są null // Te obiekty powinny zostać usunięte przez powyższą pętlę, ale warto zresetować zmienne. currentBackground = null; player = null; boss = null; // --- Koniec agresywnego czyszczenia sceny --- this.currentState = "grillMenu"; game.setBackgroundColor(0x333333); // Tło grilla // Teraz dodaj nowe tło dla ekranu Grilla (ten fragment zostaje taki, jak go poprawiliśmy ostatnio - JEDNO dodanie grillMenu) currentBackground = LK.getAsset('grillMenu', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); // Dodaj nowe tło na spód if (currentBackground) { // Sprawdź czy asset został poprawnie załadowany game.addChildAt(currentBackground, 0); } // ✨ DODAJEMY 5 SPARKLINGÓW ✨ // Sparkle 1 var sparkle1 = game.addChild(LK.getAsset('sparkle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 460, y: 2732 / 2 + 690 })); gameState.grillMenuEffects.push(sparkle1); // <--- DODANA LINIJA // Sparkle 2 var sparkle2 = game.addChild(LK.getAsset('sparkle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 520, y: 2732 / 2 + 580 })); gameState.grillMenuEffects.push(sparkle2); // <--- DODANA LINIJA // Sparkle 3 var sparkle3 = game.addChild(LK.getAsset('sparkle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 660, y: 2732 / 2 + 550 })); gameState.grillMenuEffects.push(sparkle3); // <--- DODANA LINIJA // Sparkle 4 var sparkle4 = game.addChild(LK.getAsset('sparkle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 500, y: 2732 / 2 + 680 })); gameState.grillMenuEffects.push(sparkle4); // <--- DODANA LINIJA // Sparkle 5 var sparkle5 = game.addChild(LK.getAsset('sparkle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 620, y: 2732 / 2 + 720 })); gameState.grillMenuEffects.push(sparkle5); // <--- DODANA LINIJA // Funkcja animacji sparkle function animateSparkle(s) { // --- DODANE SPRAWDZENIE STANU --- if (gameState.currentState !== "grillMenu" || !s || s.destroyed) { // Jeśli nie jesteśmy w menu grilla lub obiekt już nie istnieje, zakończ return; } // --- KONIEC DODANEGO SPRAWDZENIA --- s.alpha = 0; var startY = s.y; tween(s, { alpha: 1, y: startY - 40 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // --- DODANE SPRAWDZENIE STANU W CALLBACKU --- if (gameState.currentState !== "grillMenu" || !s || s.destroyed) { return; } // --- KONIEC DODANEGO SPRAWDZENIA --- tween(s, { alpha: 0 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { // --- DODANE SPRAWDZENIE STANU W WEWNĘTRZNYM CALLBACKU --- if (gameState.currentState !== "grillMenu" || !s || s.destroyed) { return; } // --- KONIEC DODANEGO SPRAWDZENIA --- LK.setTimeout(function () { animateSparkle(s); }, Math.random() * 500 + 300); } }); } }); } // Start animacji sparklingów // Upewnij się, że animacje startują z pewnym opóźnieniem po dodaniu do tablicy LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateSparkle(sparkle1); } }, Math.random() * 500); LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateSparkle(sparkle2); } }, Math.random() * 500 + 500); LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateSparkle(sparkle3); } }, Math.random() * 500 + 1000); LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateSparkle(sparkle4); } }, Math.random() * 500 + 1500); LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateSparkle(sparkle5); } }, Math.random() * 500 + 2000); // 🌟 DODAJEMY 3 GWIAZDKI NA NIEBIE 🌟 // Star 1 var star1 = game.addChild(LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 800, y: 2732 / 2 - 1000 })); gameState.grillMenuEffects.push(star1); // <--- DODANA LINIJA // Star 2 var star2 = game.addChild(LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 300, y: 2732 / 2 - 1150 })); gameState.grillMenuEffects.push(star2); // <--- DODANA LINIJA // Star 3 var star3 = game.addChild(LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 200, y: 2732 / 2 - 900 })); gameState.grillMenuEffects.push(star3); // <--- DODANA LINIJA // Funkcja animacji gwiazdek function animateStar(star) { // --- DODANE SPRAWDZENIE STANU --- if (gameState.currentState !== "grillMenu" || !star || star.destroyed) { return; } // --- KONIEC DODANEGO SPRAWDZENIA --- star.alpha = 0; tween(star, { alpha: 1 }, { duration: 2000, easing: tween.easeIn, onFinish: function onFinish() { // --- DODANE SPRAWDZENIE STANU W CALLBACKU --- if (gameState.currentState !== "grillMenu" || !star || star.destroyed) { return; } // --- KONIEC DODANEGO SPRAWDZENIA --- tween(star, { alpha: 0 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // --- DODANE SPRAWDZENIE STANU W WEWNĘTRZNYM CALLBACKU --- if (gameState.currentState !== "grillMenu" || !star || star.destroyed) { return; } // --- KONIEC DODANEGO SPRAWDZENIA --- // Po całym cyklu czekamy losowo 2-5 sekund przed kolejnym pojawieniem się LK.setTimeout(function () { animateStar(star); }, Math.random() * 3000 + 2000); } }); } }); } // Start animacji gwiazdek // Upewnij się, że animacje startują z pewnym opóźnieniem po dodaniu do tablicy LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateStar(star1); } }, Math.random() * 1000); LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateStar(star2); } }, Math.random() * 1000 + 500); LK.setTimeout(function () { if (gameState.currentState === "grillMenu") { animateStar(star3); } }, Math.random() * 1000 + 1000); // --- KONIEC gwiazdek --- // --- KONIEC sparklingów --- // Ukryj ściany (jeśli są widoczne w menu) walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ustaw UI dla Grill Menu ui.positionElements("grillMenu"); ui.updateBossHealth(0, 1); // Ukryj pasek HP bossa ui.updateHearts(0, storage.maxHearts); // Ukryj serca gracza // --- Przyciski na ekranie Grilla --- var buttonYStart = 1000; var buttonYOffset = 150; // Przycisk "Rest" var restButton = new Container(); restButton.interactive = true; restButton.cursor = "pointer"; restButton.x = 600; // bardziej w lewo nad grilla restButton.y = 650; // wysokość currentSceneElements.addChild(restButton); // Dodaj do kontenera sceny var restButtonBg = LK.getAsset('buttonRest', { anchorX: 0.5, anchorY: 0.5 }); restButton.addChild(restButtonBg); restButton.down = function () { ui.showMessage("Rest in peace...", 2000); LK.setTimeout(function () { ui.showMessage("Thank you for playing!", 3000); }, 2500); LK.setTimeout(function () { gameState.showTitleScreen(); }, 6000); }; // Przycisk "Upgrade Roll" var upgradeButton = new Container(); upgradeButton.interactive = true; upgradeButton.x = 600; // to samo X co reszta upgradeButton.y = 850; // +200 niżej currentSceneElements.addChild(upgradeButton); // Dodaj do kontenera sceny var upgradeButtonBg = LK.getAsset('buttonUpgrade', { anchorX: 0.5, anchorY: 0.5 }); upgradeButton.addChild(upgradeButtonBg); // Akcja po kliknięciu w Upgrade upgradeButton.down = function () { // ... (istniejący kod animacji tła przy przejściu do Upgrade) ... if (currentBackground) { // Zanikajace stare tlo tween(currentBackground, { alpha: 0 }, { duration: 600, easing: tween.easeIn, onFinish: function onFinish() { currentBackground.destroy(); // Usuwamy stare tlo currentBackground = LK.getAsset('upgradebg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 // zaczynamy nowe tło od niewidoczności }); game.addChildAt(currentBackground, 0); // Pokazujemy nowe tlo z fade in tween(currentBackground, { alpha: 1 }, { duration: 1200, easing: tween.easeOut }); } }); } }; // Przycisk "New Boss+" var newBossButton = new Container(); newBossButton.interactive = true; newBossButton.cursor = "pointer"; newBossButton.x = 600; newBossButton.y = 1050; // jeszcze niżej currentSceneElements.addChild(newBossButton); // Dodaj do kontenera sceny var newBossButtonBg = LK.getAsset('buttonBoss', { anchorX: 0.5, anchorY: 0.5 }); newBossButton.addChild(newBossButtonBg); newBossButton.down = function () { isNewBossPlusMode = true; // Poprzedni kod usuwający sparkle tutaj jest już niepotrzebny, // ponieważ obsługa usuwania jest teraz w startGame z wykorzystaniem gameState.grillMenuEffects gameState.startGame(); }; // Przycisk "Roll Master" var rollMasterButton = new Container(); rollMasterButton.interactive = true; rollMasterButton.cursor = "pointer"; rollMasterButton.x = 600; rollMasterButton.y = 1250; currentSceneElements.addChild(rollMasterButton); var rollMasterButtonBg = LK.getAsset('button_bg', { anchorX: 0.5, anchorY: 0.5 }); rollMasterButton.addChild(rollMasterButtonBg); var rollMasterButtonText = new Text2("Roll Master", { size: 50, fill: 0xFFFFFF }); rollMasterButtonText.anchor.set(0.5, 0.5); rollMasterButton.addChild(rollMasterButtonText); // --- ZMIANA TUTAJ: Akcja przycisku Roll Master --- rollMasterButton.down = function () { gameState.startRollMasterMode(); // To jest poprawna akcja dla tego przycisku }; }, // Koniec funkcji showGrillScreen (przecinek oddziela ją od następnej metody gameState) // --- NOWA FUNKCJA: Start przejścia do trybu Roll Master --- startRollMasterMode: function startRollMasterMode() { console.log("Rozpoczynanie trybu Roll Master..."); this.currentState = "rollMaster"; // Ustawienie nowego stanu gry // Wyczyszczenie elementów z poprzedniej sceny (np. Grill Menu) // Upewnij się, że 'currentSceneElements' to poprawny kontener dla elementów Grill Menu // lub użyj innej metody czyszczenia specyficznej dla Twojej konfiguracji. // Poniżej przykład czyszczenia globalnego kontenera, jeśli go używasz: if (typeof clearScene === 'function') { // Sprawdź, czy funkcja clearScene istnieje clearScene(); // Użyj globalnej funkcji clearScene, jeśli jest dostępna } else if (currentSceneElements && typeof currentSceneElements.removeChildren === 'function') { // Alternatywnie, jeśli currentSceneElements zawiera tylko elementy menu: currentSceneElements.removeChildren(); } else { console.warn("Nie można automatycznie wyczyścić sceny przed Roll Master!"); } // Dodatkowo, usuń specyficzne efekty z Grill Menu, jeśli istnieją if (this.grillMenuEffects && this.grillMenuEffects.length > 0) { this.grillMenuEffects.forEach(function (effect) { if (effect && effect.destroy) { effect.destroy(); } }); this.grillMenuEffects = []; } // Usuń tło Grill Menu if (currentBackground && currentBackground.destroy) { tween.stop(currentBackground); // Zatrzymaj animacje, jeśli były currentBackground.destroy(); currentBackground = null; } // Wywołanie funkcji konfigurującej scenę tego trybu this.setupRollMasterScene(); }, // <-- WAŻNE: Przecinek tutaj, bo następuje kolejna funkcja gameState // --- NOWA FUNKCJA: Konfiguracja sceny Roll Master --- setupRollMasterScene: function setupRollMasterScene() { var _this2 = this; console.log("Konfiguracja sceny Roll Master..."); try { currentBackground = LK.getAsset('rollMasterBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChildAt(currentBackground, 0); } catch (e) { console.warn("Nie znaleziono assetu 'rollMasterBg', używam koloru tła."); game.setBackgroundColor(0x222233); } if (typeof Player !== 'undefined') { player = game.addChild(new Player()); player.x = 2048 / 2; player.y = 2732 / 2 + 400; player.health = 1; } else { console.error("Klasa Player nie jest zdefiniowana!"); } this.rollMasterTime = 0; if (ui && ui.timerText) { ui.timerText.style = { size: 80, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 5 }; ui.timerText.x = 850; ui.timerText.y = 200; ui.timerText.anchor.set(0, 0.5); ui.timerText.alpha = 1; ui.timerText.setText("Time: 00:00"); } else { console.error("Nie można znaleźć ui.timerText do skonfigurowania!"); } if (ui && ui.updateHearts) { ui.updateHearts(1, 1); } if (ui && ui.updateBossHealth) { ui.updateBossHealth(0, 1); } this.rollMasterDifficulty = 1; this.rollMasterAttacks = []; this.attackSpawnTimer = 0; this.attackSpawnInterval = 120; this.explosionSpawnTimer = 0; this.explosionSpawnInterval = 240; if (this.rollMasterTimerInterval) { LK.clearInterval(this.rollMasterTimerInterval); } var self = this; this.rollMasterTimerInterval = LK.setInterval(function () { if (self.currentState !== "rollMaster") { LK.clearInterval(self.rollMasterTimerInterval); self.rollMasterTimerInterval = null; return; } self.rollMasterTime++; var minutes = Math.floor(self.rollMasterTime / 60); var seconds = self.rollMasterTime % 60; var formattedTime = String(minutes).padStart(2, '0') + ":" + String(seconds).padStart(2, '0'); if (ui && ui.timerText) { ui.timerText.setText("Time: " + formattedTime); } if (self.rollMasterTime > 0 && self.rollMasterTime % 60 === 0) { self.increaseRollMasterDifficulty(); } }, 1000); console.log("Scena Roll Master skonfigurowana."); }, // <-- WAŻNE: Przecinek tutaj // --- NOWA FUNKCJA: Zwiększanie trudności w Roll Master --- increaseRollMasterDifficulty: function increaseRollMasterDifficulty() { this.rollMasterDifficulty++; console.log("Trudność Roll Master zwiększona do:", this.rollMasterDifficulty); var decreaseAmountProjectile = 10; var minIntervalProjectile = 30; this.attackSpawnInterval = Math.max(minIntervalProjectile, this.attackSpawnInterval - decreaseAmountProjectile); console.log("Nowy interwał projectile:", this.attackSpawnInterval); var decreaseAmountExplosion = 15; var minIntervalExplosion = 60; this.explosionSpawnInterval = Math.max(minIntervalExplosion, this.explosionSpawnInterval - decreaseAmountExplosion); console.log("Nowy interwał explosion:", this.explosionSpawnInterval); // --- NOWA LOGIKA DLA LASERA --- var decreaseAmountLaser = 20; // Jak szybko ma skracać się interwał lasera var minIntervalLaser = 120; // Minimalny interwał lasera (np. 2 sekundy) this.laserSpawnInterval = Math.max(minIntervalLaser, this.laserSpawnInterval - decreaseAmountLaser); console.log("Nowy interwał laser:", this.laserSpawnInterval); // --- NOWA LOGIKA DLA SPREADERA --- var decreaseAmountSpreader = 30; // Jak szybko ma skracać się interwał spreadera var minIntervalSpreader = 180; // Minimalny interwał spreadera (np. 3 sekundy) this.spreaderSpawnInterval = Math.max(minIntervalSpreader, this.spreaderSpawnInterval - decreaseAmountSpreader); console.log("Nowy interwał spreader:", this.spreaderSpawnInterval); if (ui && ui.showMessage) { ui.showMessage("Difficulty Increased! (" + this.rollMasterDifficulty + ")", 1500); } }, // Przejście do stanu Game Over // isDeath: true (gracz zginął), false (czas minął w Boss+) gameOver: function gameOver(isDeath) { this.currentState = "gameOver"; // Ustaw stan gameOverReasonIsDeath = isDeath; // Zapisz przyczynę // Zatrzymaj timer gry if (this.gameTimerInterval) { LK.clearInterval(this.gameTimerInterval); } this.gameTimerInterval = null; this.bossSpeedIncreased = false; // Reset flagi przyspieszenia // Zatrzymaj animację tła (jeśli istnieje, ale nie powinno w tym stanie) if (currentBackground) { tween.stop(currentBackground); } game.setBackgroundColor(0x000000); // Czarne tło // Ukryj/zniszcz gracza i bossa (powinny być już zniszczone przez swoje metody die) if (player && player.alpha !== 0) { player.alpha = 0; } // Ukryj, jeśli nie zniknął if (boss && boss.alpha !== 0) { boss.alpha = 0; } // Ukryj ściany walls.forEach(function (wall) { if (wall) { wall.alpha = 0; } }); // Ustaw UI dla ekranu game over ui.positionElements("gameOver"); // Logika komunikatu Game Over var gameOverMessage = "YOU DIED"; // Domyślny komunikat if (!isDeath) { // Jeśli czas minął (tylko w Boss+) gameOverMessage = "You just lost 10 minutes of your life. For what?"; // Zmieniono komunikat zwycięstwa Boss+ } // Wyświetl komunikat i tytuł ui.titleText.setText(gameOverMessage); ui.titleText.alpha = 1; // Pokaż tytuł ui.showMessage("", 0); // Wyczyść ewentualny poprzedni komunikat ui.showTutorial(""); // Ukryj tutorial // Pasek zdrowia bossa jest zarządzany przez updateBossHealth w pętli gry // W tym stanie updateBossHealth pokaże pasek tylko jeśli !isDeath && isNewBossPlusMode // Zaplanuj restart gry po opóźnieniu LK.setTimeout(function () { // Wyczyść scenę przed restartem clearScene(); // Zniszcz gracza/bossa jeśli jakimś cudem przetrwali if (player && player.destroy) { player.destroy(); } player = null; if (boss && boss.destroy) { boss.destroy(); } boss = null; // Wyczyść ataki bossa (na wszelki wypadek) - teraz robione w Boss.die / Boss.update // if (boss && boss.attacks) boss.attacks = []; // Usuń tło game over (czarne) if (currentBackground) { currentBackground.destroy(); } currentBackground = null; // Resetuj UI do stanu "przed grą" (np. jak w tytule, ale bez tekstów) // Pozycjonowanie jest ok, ale trzeba wyczyścić teksty ui.titleText.setText(""); ui.titleText.alpha = 0; ui.messageText.setText(""); ui.messageText.alpha = 0; ui.tutorialText.setText(""); ui.tutorialText.alpha = 0; ui.updateDeathsCounter(); // Zaktualizuj licznik śmierci ui.updateBossHealth(0, 1); // Ukryj pasek HP ui.updateHearts(0, storage.maxHearts); // Ukryj serca // Flaga isNewBossPlusMode pozostaje niezmieniona (jeśli zginąłeś w Boss+, restartujesz w Boss+) // gameOverReasonIsDeath zostanie zresetowana przy następnym wywołaniu gameOver // Restartuj walkę (startGame użyje flagi isNewBossPlusMode) gameState.startGame(); }, 4000); // Zwiększono opóźnienie do 4 sekund przed restartem }, // --- NOWA FUNKCJA: Zakończenie trybu Roll Master --- endRollMasterMode: function endRollMasterMode(finalTime) { var _this3 = this; console.log("Zakończono tryb Roll Master. Czas:", finalTime); this.currentState = "rollMasterGameOver"; if (this.rollMasterTimerInterval) { LK.clearInterval(this.rollMasterTimerInterval); this.rollMasterTimerInterval = null; } if (this.rollMasterAttacks && this.rollMasterAttacks.length > 0) { this.rollMasterAttacks.forEach(function (attack) { if (attack.visual && attack.visual.destroy) { attack.visual.destroy(); } }); this.rollMasterAttacks = []; } if (player && player.clearRollTimeouts) { player.clearRollTimeouts(); } if (player) { player.alpha = 0; } var oldHighScore = this.rollMasterHighScore; if (finalTime > oldHighScore) { this.rollMasterHighScore = finalTime; storage.rollMasterHighScore = this.rollMasterHighScore; console.log("Nowy rekord Roll Master!", this.rollMasterHighScore); } var minutes = Math.floor(finalTime / 60); var seconds = finalTime % 60; var formattedTime = String(minutes).padStart(2, '0') + ":" + String(seconds).padStart(2, '0'); var scoreMessage = "Your Time: " + formattedTime; var highScoreMessage = "Best: " + String(Math.floor(oldHighScore / 60)).padStart(2, '0') + ":" + String(oldHighScore % 60).padStart(2, '0'); if (ui) { ui.showMessage(scoreMessage, 0); ui.showTutorial(highScoreMessage); } LK.setTimeout(function () { if (ui) { ui.showMessage("", 0); ui.showTutorial(""); } if (player && player.destroy) { player.destroy(); } player = null; _this3.showGrillScreen(); }, 5000); }, // Koniec funkcji endRo // Obsługa gestów dotykowych/myszy processTouchGesture: function processTouchGesture() { // --- Obsługa inputu w stanie fakeTutorial --- if (this.currentState === "fakeTutorial" && this.fakeTutorialTimerId) { this.handleFakeTutorialInput(); // Wywołaj fałszywą śmierć return; // Zakończ przetwarzanie gestu } var dx = this.touchEnd.x - this.touchStart.x; var dy = this.touchEnd.y - this.touchStart.y; var distance = Math.sqrt(dx * dx + dy * dy); // Minimalny dystans dla swipe (turlania) var swipeThreshold = 50; // --- Obsługa Tapnięcia --- if (distance < swipeThreshold) { // Sprawdź, czy tapnięcie było na interaktywnym obiekcie (przycisku) // Zakładamy, że LK.Game obsługuje to przez przekazanie 'obj' do game.down/up // Jeśli tapnięcie NIE było na przycisku: if (this.currentState === "title") { // Sprawdź czy tapnięcie nie było na przycisku Grill Menu // Prosty sposób: załóżmy, że jeśli obj nie jest zdefiniowany w game.up, to kliknięto tło // (Wymaga sprawdzenia, jak LK.Game przekazuje 'obj') // Na razie zakładamy, że tapnięcie w tło przechodzi do intro this.showIntro(); return; } // W innych stanach (gameOver, victory, grillMenu) tapnięcie w tło nic nie robi // Obsługa przycisków dzieje się przez ich własne handlery .down/.up return; } // --- Obsługa Swipe (Turlania) --- // Działa tylko w stanie gry i gdy gracz żyje if ((this.currentState === "game" || this.currentState === "rollMaster") && player && !player.dead) { // ... reszta kodu bez zmian (obliczanie direction, player.roll(direction)) ... // Normalizuj kierunek gestu var direction = { x: 0, y: 0 }; if (distance > 0) { // Unikaj dzielenia przez zero direction.x = dx / distance; direction.y = dy / distance; } // Wykonaj turlanie gracza player.roll(direction); } // W innych stanach swipe jest ignorowany } }; // --- Obsługa inputu --- game.down = function (x, y, obj) { // Rejestruj początek dotyku/kliknięcia // Stany, w których śledzimy gesty lub kliknięcia przycisków: var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver", "rollMaster"]; if (trackStates.indexOf(gameState.currentState) !== -1) { gameState.touchStart.x = x; gameState.touchStart.y = y; gameState.touchEnd.x = x; // Reset end point for gesture gameState.touchEnd.y = y; // --- NEW: Set input active flag and initial current position --- gameState.isInputActive = true; gameState.currentInputPos.x = x; gameState.currentInputPos.y = y; // ------------------------------------------------------------------ } // Obsługa kliknięć przycisków jest robiona przez ich własne handlery .down // Note: If you have complex button logic that consumes clicks, // you might need to check if 'obj' is null here before setting isInputActive. // For simple cases, setting it always on 'down' might be fine, // but make sure your player movement check in update respects the game state. }; game.up = function (x, y, obj) { // Rejestruj koniec dotyku/kliknięcia i przetwarzaj gest var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver", "rollMaster"]; if (trackStates.indexOf(gameState.currentState) !== -1) { gameState.touchEnd.x = x; gameState.touchEnd.y = y; // --- NEW: Reset input active flag --- gameState.isInputActive = false; gameState.processTouchGesture(); } // Obsługa puszczenia przycisków (.up) może być dodana tutaj, jeśli potrzebna }; game.move = function (x, y, obj) { // Śledź ruch palca/myszy dla swipe i dla ciągłego śledzenia pozycji var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver", "rollMaster"]; // <-- DODAJ "rollMaster" TUTAJ! if (trackStates.indexOf(gameState.currentState) !== -1) { gameState.touchEnd.x = x; // Update end point for gesture on the fly gameState.touchEnd.y = y; // --- NEW: Update current input position ONLY if input is active --- if (gameState.isInputActive) { gameState.currentInputPos.x = x; gameState.currentInputPos.y = y; } // ------------------------------------------------------------------ } }; // --- Główna pętla aktualizacji gry --- // --- Główna pętla aktualizacji gry --- game.update = function () { if (ui) { ui.updateDeathsCounter(); if (boss) { var maxHp = boss.maxHealth > 0 ? boss.maxHealth : 1; ui.updateBossHealth(boss.health, maxHp); } else { if (!(gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode)) { ui.updateBossHealth(0, 1); } } if (player) { if (gameState.currentState === "rollMaster") { ui.updateHearts(player.health, 1); } else { ui.updateHearts(player.health, storage.maxHearts); } } else if (gameState.currentState !== "game" && gameState.currentState !== "gameOver") { ui.updateHearts(0, storage.maxHearts); } } if (gameState.currentState === "game") { if (player) { player.update(); } if (boss) { boss.update(); } } else if (gameState.currentState === "rollMaster") { if (player) { player.update(); } // Spawner Pocisków (rmattack1) // Spawner Pocisków (rmattack1) - TERAZ WYŁĄCZA SIĘ PO 75 SEKUNDACH if (gameState.spreaderSpawnInterval > 0) { gameState.attackSpawnTimer++; if (gameState.attackSpawnTimer >= gameState.attackSpawnInterval) { gameState.attackSpawnTimer = 0; var projectileVisual = LK.getAsset('rmattack1', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.6, scaleY: 1.6 }); var projectileRadius = 45; var edge = Math.floor(Math.random() * 4); var startX, startY; var targetX = 2048 / 2; var targetY = 2732 / 2; if (edge === 0) { startX = Math.random() * 2048; startY = -projectileRadius; } else if (edge === 1) { startX = 2048 + projectileRadius; startY = Math.random() * 2732; } else if (edge === 2) { startX = Math.random() * 2048; startY = 2732 + projectileRadius; } else { startX = -projectileRadius; startY = Math.random() * 2732; } projectileVisual.x = startX; projectileVisual.y = startY; var dx = targetX - startX; var dy = targetY - startY; var dist = Math.sqrt(dx * dx + dy * dy); var speed = 5 + gameState.rollMasterDifficulty; var vx = dist > 0 ? dx / dist * speed : 0; var vy = dist > 0 ? dy / dist * speed : 0; var projectileObject = game.addChild(projectileVisual); gameState.rollMasterAttacks.push({ type: 'projectile', visual: projectileObject, vx: vx, vy: vy, radius: projectileRadius }); } } // Spawner Eksplozji (rmattack2) if (gameState.spreaderSpawnInterval > 0) { if (gameState.explosionSpawnTimer >= gameState.explosionSpawnInterval) { gameState.explosionSpawnTimer = 0; var explosionWarningTime = 120; var explosionActiveTime = 10; var explosionRadius = 100; var explosionVisual = LK.getAsset('rmattack2', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); explosionVisual.width = 200; explosionVisual.height = 200; var margin = 150; var spawnX = margin + Math.random() * (2048 - 2 * margin); var spawnY = margin + Math.random() * (2732 - 2 * margin); explosionVisual.x = spawnX; explosionVisual.y = spawnY; tween(explosionVisual, { alpha: 1.0 }, { duration: 500, easing: tween.easeInOut, yoyo: true, repeat: 1 }); var explosionObject = game.addChild(explosionVisual); gameState.rollMasterAttacks.push({ type: 'explosion', visual: explosionObject, radius: explosionRadius, state: 'warning', timer: explosionWarningTime, activeTimer: explosionActiveTime }); } } // --- NOWY Spawner Promienia Lasera (rmattack3) --- if (gameState.spreaderSpawnInterval > 0) { gameState.laserSpawnTimer++; if (gameState.laserSpawnTimer >= gameState.laserSpawnInterval) { gameState.laserSpawnTimer = 0; console.log("Spawnuję Laser (rmattack3)!"); var laserAngle = Math.random() * Math.PI * 2; var laserWidth = 2200; var laserVisualHeight = 120; // Zakładana wysokość assetu rmattack3 (dostosuj jeśli inna) var laserHitboxHeight = 30; // Faktyczna wysokość hitboxa (cieńsza niż grafika) var laserRadius = laserHitboxHeight / 2; // Dla uproszczonej kolizji // Użyj 'rmattack3' var laserWarningVisual = LK.getAsset('rmattack3', { anchorX: 0.5, anchorY: 0.5, width: laserWidth, // Rozciągnij na szerokość ekranu height: laserVisualHeight, // Użyj wysokości assetu rotation: laserAngle, alpha: 0.1, // Niska alfa dla ostrzeżenia tint: 0xFF8888 // Lekko czerwony odcień dla ostrzeżenia }); laserWarningVisual.x = 2048 / 2; laserWarningVisual.y = 2732 / 2; // Animacja ostrzeżenia (pulsowanie alpha) // Oblicz liczbę powtórzeń dla pulsowania przez cały czas ostrzeżenia var repeatCountWarning = Math.max(0, Math.floor(gameState.laserWarningTime / (300 * 2 / (1000 / 60))) - 1); tween(laserWarningVisual, { alpha: 0.5 }, { duration: 300, yoyo: true, repeat: repeatCountWarning }); var laserObject = game.addChild(laserWarningVisual); gameState.rollMasterAttacks.push({ type: 'laser', // Typ ataku visual: laserObject, // Obiekt wizualny radius: laserRadius, // Uproszczony promień kolizji state: 'warning', // Stan początkowy timer: gameState.laserWarningTime, // Czas ostrzeżenia angle: laserAngle, // Kąt lasera width: laserWidth, // Szerokość (dla kolizji) activeHeight: laserHitboxHeight // Faktyczna wysokość hitboxa }); } } // --- NOWY Spawner Wzoru Rozchodzącego Się (rmattack4 -> rmattack1) --- if (gameState.spreaderSpawnInterval > 0) { // ZACZYNA SIĘ PO 75 SEKUNDACH gameState.spreaderSpawnTimer++; if (gameState.spreaderSpawnTimer >= gameState.spreaderSpawnInterval) { gameState.spreaderSpawnTimer = 0; console.log("Spawnuję Spreader Parent (rmattack4)!"); // Użyj 'rmattack4' jako rodzica var spreaderParentVisual = LK.getAsset('rmattack4', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, // Ewentualnie dostosuj skalę scaleY: 1.2 }); // Zakładając, że rmattack4 ma wymiary 120x120 jak inne ataki var spreaderParentRadius = 120 / 2 * 1.2; // Promień kolizji rodzica var margin = 200; var startX = margin + Math.random() * (2048 - 2 * margin); var startY = margin + Math.random() * (2732 - 2 * margin); spreaderParentVisual.x = startX; spreaderParentVisual.y = startY; var spreaderObject = game.addChild(spreaderParentVisual); gameState.rollMasterAttacks.push({ type: 'spreader_parent', // Typ rodzica visual: spreaderObject, // Obiekt wizualny radius: spreaderParentRadius, // Promień kolizji (czy ma kolidować?) timer: gameState.spreaderSplitTime // Czas do podziału }); } } // Aktualizacja, Kolizje i Usuwanie WSZYSTKICH Ataków // Aktualizacja, Kolizje i Usuwanie WSZYSTKICH Ataków if (player && !player.dead) { for (var i = gameState.rollMasterAttacks.length - 1; i >= 0; i--) { var attack = gameState.rollMasterAttacks[i]; var destroyAttack = false; // --- Logika aktualizacji i kolizji dla każdego typu ataku --- if (attack.type === 'projectile') { // Dla rmattack1 attack.visual.x += attack.vx; attack.visual.y += attack.vy; // Sprawdzenie wyjścia poza ekran if (attack.visual.x < -200 || attack.visual.x > 2048 + 200 || attack.visual.y < -200 || attack.visual.y > 2732 + 200) { destroyAttack = true; } // Sprawdzenie kolizji dla projectile if (!player.invulnerable) { var dx_coll = player.x - attack.visual.x; var dy_coll = player.y - attack.visual.y; var dist_coll = Math.sqrt(dx_coll * dx_coll + dy_coll * dy_coll); var playerRadius = player.width / 2 * 0.8; if (dist_coll < playerRadius + attack.radius) { console.log("Kolizja! Typ:", attack.type, "Koniec gry Roll Master."); gameState.endRollMasterMode(gameState.rollMasterTime); LK.effects.flashObject(game, 0xFF0000, 300); return; // Zakończ update po śmierci } } } else if (attack.type === 'explosion') { // Dla rmattack2 if (attack.state === 'warning') { attack.timer--; if (attack.timer <= 0) { attack.state = 'active'; attack.timer = attack.activeTimer; tween.stop(attack.visual); attack.visual.alpha = 1; tween(attack.visual, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: attack.timer * (1000 / 60), easing: tween.easeOut }); } } else if (attack.state === 'active') { attack.timer--; // Sprawdzenie kolizji dla explosion (TYLKO w stanie 'active') if (!player.invulnerable) { var dx_coll_e = player.x - attack.visual.x; var dy_coll_e = player.y - attack.visual.y; var dist_coll_e = Math.sqrt(dx_coll_e * dx_coll_e + dy_coll_e * dy_coll_e); var playerRadius_e = player.width / 2 * 0.8; if (dist_coll_e < playerRadius_e + attack.radius) { console.log("Kolizja! Typ:", attack.type, "Koniec gry Roll Master."); gameState.endRollMasterMode(gameState.rollMasterTime); LK.effects.flashObject(game, 0xFF0000, 300); return; // Zakończ update po śmierci } } if (attack.timer <= 0) { attack.state = 'finished'; destroyAttack = true; } } else { // finished or unknown destroyAttack = true; } } // --- NOWA Obsługa Lasera (rmattack3) --- else if (attack.type === 'laser') { if (attack.state === 'warning') { attack.timer--; if (attack.timer <= 0) { // Zmiana na stan aktywny attack.state = 'active'; attack.timer = gameState.laserActiveTime; // Ustaw czas aktywności // Zatrzymaj animację ostrzeżenia i zmień wygląd tween.stop(attack.visual); attack.visual.alpha = 1; // Pełna alfa attack.visual.tint = 0xFFFFFF; // Normalny kolor (usuń odcień ostrzegawczy) LK.effects.flashObject(attack.visual, 0xFFFFFF, 100); // Krótki błysk przy aktywacji } } else if (attack.state === 'active') { attack.timer--; // SPRAWDZENIE KOLIZJI Z LASEREM (Uproszczone) if (player && !player.dead && !player.invulnerable) { // Sprawdzenie czy gracz istnieje i żyje var playerRadius = player.width / 2 * 0.8; var dx_p = player.x - attack.visual.x; var dy_p = player.y - attack.visual.y; var rotated_dy = -dx_p * Math.sin(-attack.angle) + dy_p * Math.cos(-attack.angle); var rotated_dx = dx_p * Math.cos(-attack.angle) + dy_p * Math.sin(-attack.angle); if (Math.abs(rotated_dy) < playerRadius + attack.activeHeight / 2 && Math.abs(rotated_dx) < attack.width / 2) { console.log("Kolizja! Typ: laser (rmattack3)"); gameState.endRollMasterMode(gameState.rollMasterTime); LK.effects.flashObject(game, 0xFF0000, 300); return; // Zakończ update po śmierci } } if (attack.timer <= 0) { // Koniec aktywności - zacznij znikać attack.state = 'fading'; // Zapisz referencję do ataku, bo 'attack' może się zmienić w onFinish var currentAttack = attack; tween(attack.visual, { alpha: 0 }, { duration: 150, onFinish: function onFinish() { // Upewnij się, że stan jest ustawiony na finished dopiero po zniknięciu // i że to wciąż ten sam atak (nie został usunięty w międzyczasie) if (currentAttack && currentAttack.state === 'fading') { currentAttack.state = 'finished'; } } }); } } else if (attack.state === 'fading') { // Czekaj aż animacja znikania się skończy (stan zmieni się na 'finished' w onFinish) } else { // finished or unknown state destroyAttack = true; } } // --- NOWA Obsługa Wzoru Rozchodzącego Się (rmattack4 -> rmattack1) --- else if (attack.type === 'spreader_parent') { // Rodzic (rmattack4) attack.timer--; if (attack.timer <= 0) { // Czas na podział! console.log("Spreader Parent (rmattack4) dzieli się!"); var parentX = attack.visual.x; var parentY = attack.visual.y; // Stwórz pociski potomne for (var j = 0; j < gameState.spreaderChildCount; j++) { var angle = j / gameState.spreaderChildCount * Math.PI * 2; // Kąt dla każdego dziecka var vxChild = Math.cos(angle) * gameState.spreaderChildSpeed; var vyChild = Math.sin(angle) * gameState.spreaderChildSpeed; // Użyj 'rmattack1' jako pocisku-dziecka var childVisual = LK.getAsset('rmattack1', { anchorX: 0.5, anchorY: 0.5, x: parentX, // Startuj z pozycji rodzica y: parentY, scaleX: 1.2, // Dostosuj skalę jeśli rmattack1 jest mniejszy/większy scaleY: 1.2 }); var childRadius = 45 * 1.2; // Dostosuj promień jeśli skalujesz rmattack1 var childObject = game.addChild(childVisual); // Dodaj dziecko do tablicy ataków gameState.rollMasterAttacks.push({ type: 'spreader_child', // Typ dziecka visual: childObject, vx: vxChild, vy: vyChild, radius: childRadius }); } // Zniszcz rodzica destroyAttack = true; } // Opcjonalna kolizja z rodzicem (rmattack4) - można dodać tutaj jeśli potrzebne /* if (player && !player.dead && !player.invulnerable) { ... } */ } else if (attack.type === 'spreader_child') { // Dzieci (rmattack1) // Zachowuje się jak zwykły pocisk attack.visual.x += attack.vx; attack.visual.y += attack.vy; // Sprawdzenie, czy wyszedł poza ekran if (attack.visual.x < -100 || attack.visual.x > 2048 + 100 || attack.visual.y < -100 || attack.visual.y > 2732 + 100) { destroyAttack = true; } // Sprawdzenie kolizji if (player && !player.dead && !player.invulnerable) { var dx_coll_c = player.x - attack.visual.x; var dy_coll_c = player.y - attack.visual.y; var dist_coll_c = Math.sqrt(dx_coll_c * dx_coll_c + dy_coll_c * dy_coll_c); var playerRadius_c = player.width / 2 * 0.8; if (dist_coll_c < playerRadius_c + attack.radius) { console.log("Kolizja! Typ: spreader_child (rmattack1)"); gameState.endRollMasterMode(gameState.rollMasterTime); LK.effects.flashObject(game, 0xFF0000, 300); return; // Zakończ update po śmierci } } } // --- Koniec logiki aktualizacji i kolizji --- // --- Usuwanie ataku (wspólne dla wszystkich) --- if (destroyAttack || attack.visual && attack.visual.destroyed) { if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) { tween.stop(attack.visual); // Zatrzymaj tweens przed zniszczeniem attack.visual.destroy(); } // Sprawdzenie czy atak nadal istnieje w tablicy przed usunięciem (na wszelki wypadek) var currentIndex = gameState.rollMasterAttacks.indexOf(attack); if (currentIndex === i) { // Upewnij się, że usuwasz właściwy element gameState.rollMasterAttacks.splice(i, 1); } else { // Jeśli indeks się nie zgadza, poszukaj i usuń (bardziej bezpieczne, ale wolniejsze) var correctIndex = gameState.rollMasterAttacks.indexOf(attack); if (correctIndex !== -1) { gameState.rollMasterAttacks.splice(correctIndex, 1); } // Zmniejsz i, aby skompensować przesunięcie po splice w nietypowej sytuacji i--; } } // --- Koniec usuwania --- } // Koniec pętli for } // Koniec if (player && !player.dead) } }; // --- Rozpoczęcie gry --- // Inicjalizuj obiekt gameState, który zajmie się resztą. gameState.init();
===================================================================
--- original.js
+++ change.js
@@ -1431,10 +1431,10 @@
/****
* Game Code
****/
-// Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia)
// Komentarze o assetach pominięte dla zwięzłości
+// Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia)
var currentSceneElements = new Container();
// Zmienne gry
var player;
var boss;
@@ -1514,50 +1514,38 @@
}
// Obiekt zarządzający stanami gry
var gameState = {
currentState: "title",
- // ... (pozostałe zmienne gameState)
gameDuration: 120,
- // Domyślny czas walki z bossem w sekundach (2 minuty)
remainingTime: 0,
- // Pozostały czas
gameTimerInterval: null,
- // ID interwału timera
fakeTutorialTimerId: null,
- // ID timera przechodzącego do prawdziwego tutorialu
bossSpeedIncreased: false,
- // Flaga przyspieszenia bossa
- // --- Zmienne dla trybu Roll Master ---
rollMasterTime: 0,
rollMasterDifficulty: 1,
rollMasterTimerInterval: null,
rollMasterAttacks: [],
- // Tablica na aktywne ataki w tym trybie
attackSpawnTimer: 0,
- // Licznik klatek do następnego spawnu
attackSpawnInterval: 120,
- // Początkowy interwał spawnu (klatki, np. 120 = 2s @60fps)
+ explosionSpawnTimer: 0,
+ // <<< NOWE: Timer dla eksplozji
+ explosionSpawnInterval: 240,
+ // <<< NOWE: Interwał dla eksplozji (np. 4s)
rollMasterHighScore: 0,
- // Najlepszy wynik w Roll Master
- // --- NEW: Variables for tracking continuous input ---
isInputActive: false,
- // Flag to check if touch/mouse is currently pressed
currentInputPos: {
x: 0,
y: 0
},
- // Current touch/mouse position
touchStart: {
x: 0,
y: 0
},
- // Początek gestu
touchEnd: {
x: 0,
y: 0
},
- // Koniec gestu
- // Inicjalizacja gry (wywoływana raz na początku)
+ grillMenuEffects: [],
init: function init() {
// Resetuj stan przy każdym uruchomieniu
storage.totalDeaths = 0;
storage.maxHearts = 5; // Zaczynaj zawsze z 5 sercami
@@ -2648,15 +2636,9 @@
// --- NOWA FUNKCJA: Konfiguracja sceny Roll Master ---
setupRollMasterScene: function setupRollMasterScene() {
var _this2 = this;
console.log("Konfiguracja sceny Roll Master...");
- // game.setBackgroundColor(0x222233); // Możesz zostawić lub użyć tła obrazkowego
- // --- Implementacja TODO ---
- // 1. Tło dla trybu Roll Master
- // Załóżmy, że masz asset o nazwie 'rollMasterBg'
- // Jeśli nie, zmień nazwę lub użyj koloru tła
try {
- // Dodajemy try-catch na wypadek braku assetu
currentBackground = LK.getAsset('rollMasterBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
@@ -2666,101 +2648,90 @@
} catch (e) {
console.warn("Nie znaleziono assetu 'rollMasterBg', używam koloru tła.");
game.setBackgroundColor(0x222233);
}
- // 2. Gracz
- // Zakładamy, że klasa Player istnieje
if (typeof Player !== 'undefined') {
player = game.addChild(new Player());
- player.x = 2048 / 2; // Ustaw pozycję startową X
- player.y = 2732 / 2 + 400; // Ustaw pozycję startową Y (np. jak w startGame)
- player.health = 1; // Gracz ma tylko 1 "życie" w tym trybie
- // TODO (Opcjonalnie): Wyłącz możliwość ataku gracza w tym trybie, jeśli chcesz
- // np. player.canAttack = false; (jeśli masz taką flagę)
+ player.x = 2048 / 2;
+ player.y = 2732 / 2 + 400;
+ player.health = 1;
} else {
console.error("Klasa Player nie jest zdefiniowana!");
}
- this.rollMasterTime = 0; // Inicjalizacja czasu (zostaje)
- // --- UŻYWAMY ISTNIEJĄCEGO ui.timerText ---
+ this.rollMasterTime = 0;
if (ui && ui.timerText) {
- // Sprawdź, czy obiekt ui i ui.timerText istnieją
- // Ustaw styl dla istniejącego timera
ui.timerText.style = {
size: 80,
- // Rozmiar
fill: 0xFFFFFF,
- // Kolor
stroke: 0x000000,
- // Obrys
- strokeThickness: 5 // Grubość obrysu
- // Możesz dodać inne opcje stylu, jeśli są potrzebne
+ strokeThickness: 5
};
- // Ustaw pozycję i anchor dla istniejącego timera
- ui.timerText.x = 850; // <<< Spróbuj teraz zmienić tę wartość X
- ui.timerText.y = 200; // <<< Spróbuj teraz zmienić tę wartość Y
- ui.timerText.anchor.set(0, 0.5); // Ustaw anchor (np. lewo-środek)
- // Upewnij się, że jest widoczny
+ ui.timerText.x = 850;
+ ui.timerText.y = 200;
+ ui.timerText.anchor.set(0, 0.5);
ui.timerText.alpha = 1;
- // Zaktualizuj tekst początkowy (na wszelki wypadek)
ui.timerText.setText("Time: 00:00");
} else {
console.error("Nie można znaleźć ui.timerText do skonfigurowania!");
}
- // --- Koniec konfiguracji istniejącego ui.timerText ---
- // Aktualizacja pozostałych elementów UI (serca, pasek bossa - bez zmian)
if (ui && ui.updateHearts) {
ui.updateHearts(1, 1);
}
if (ui && ui.updateBossHealth) {
ui.updateBossHealth(0, 1);
}
- // 4. Zmienna trudności (bez zmian)
this.rollMasterDifficulty = 1;
- // 5. Uruchom Timer (aktualizacja co sekundę - zmiana celu aktualizacji tekstu)
+ this.rollMasterAttacks = [];
+ this.attackSpawnTimer = 0;
+ this.attackSpawnInterval = 120;
+ this.explosionSpawnTimer = 0;
+ this.explosionSpawnInterval = 240;
if (this.rollMasterTimerInterval) {
LK.clearInterval(this.rollMasterTimerInterval);
}
- // Zapisz 'this' (gameState) dla użycia w callbacku
var self = this;
this.rollMasterTimerInterval = LK.setInterval(function () {
if (self.currentState !== "rollMaster") {
- // Używamy 'self' zamiast '_this2'
LK.clearInterval(self.rollMasterTimerInterval);
self.rollMasterTimerInterval = null;
return;
}
self.rollMasterTime++;
var minutes = Math.floor(self.rollMasterTime / 60);
var seconds = self.rollMasterTime % 60;
var formattedTime = String(minutes).padStart(2, '0') + ":" + String(seconds).padStart(2, '0');
- // --- ZMIANA TUTAJ: Aktualizuj ui.timerText ---
if (ui && ui.timerText) {
- // Sprawdź czy ui.timerText istnieje
- ui.timerText.setText("Time: " + formattedTime); // Aktualizuj tekst istniejącego timera
+ ui.timerText.setText("Time: " + formattedTime);
}
- // --- KONIEC ZMIANY ---
if (self.rollMasterTime > 0 && self.rollMasterTime % 60 === 0) {
self.increaseRollMasterDifficulty();
}
}, 1000);
- // 6. Spawner Ataków (na razie tylko komunikat)
- // TODO: Uruchom Spawner Ataków
- console.log("TODO: Uruchom spawner ataków z trudnością:", this.rollMasterDifficulty);
- // np. this.attackSpawner = new AttackSpawner(this.rollMasterDifficulty);
- // 7. Muzyka (opcjonalnie)
- // TODO: Odtwórz muzykę dla trybu
- // LK.playMusic('rollMasterTheme', { loop: true });
console.log("Scena Roll Master skonfigurowana.");
},
// <-- WAŻNE: Przecinek tutaj
// --- NOWA FUNKCJA: Zwiększanie trudności w Roll Master ---
- iincreaseRollMasterDifficulty: function increaseRollMasterDifficulty() {
+ increaseRollMasterDifficulty: function increaseRollMasterDifficulty() {
this.rollMasterDifficulty++;
console.log("Trudność Roll Master zwiększona do:", this.rollMasterDifficulty);
- var decreaseAmount = 10;
- var minInterval = 30;
- this.attackSpawnInterval = Math.max(minInterval, this.attackSpawnInterval - decreaseAmount);
- console.log("Nowy interwał spawnu ataków:", this.attackSpawnInterval);
+ var decreaseAmountProjectile = 10;
+ var minIntervalProjectile = 30;
+ this.attackSpawnInterval = Math.max(minIntervalProjectile, this.attackSpawnInterval - decreaseAmountProjectile);
+ console.log("Nowy interwał projectile:", this.attackSpawnInterval);
+ var decreaseAmountExplosion = 15;
+ var minIntervalExplosion = 60;
+ this.explosionSpawnInterval = Math.max(minIntervalExplosion, this.explosionSpawnInterval - decreaseAmountExplosion);
+ console.log("Nowy interwał explosion:", this.explosionSpawnInterval);
+ // --- NOWA LOGIKA DLA LASERA ---
+ var decreaseAmountLaser = 20; // Jak szybko ma skracać się interwał lasera
+ var minIntervalLaser = 120; // Minimalny interwał lasera (np. 2 sekundy)
+ this.laserSpawnInterval = Math.max(minIntervalLaser, this.laserSpawnInterval - decreaseAmountLaser);
+ console.log("Nowy interwał laser:", this.laserSpawnInterval);
+ // --- NOWA LOGIKA DLA SPREADERA ---
+ var decreaseAmountSpreader = 30; // Jak szybko ma skracać się interwał spreadera
+ var minIntervalSpreader = 180; // Minimalny interwał spreadera (np. 3 sekundy)
+ this.spreaderSpawnInterval = Math.max(minIntervalSpreader, this.spreaderSpawnInterval - decreaseAmountSpreader);
+ console.log("Nowy interwał spreader:", this.spreaderSpawnInterval);
if (ui && ui.showMessage) {
ui.showMessage("Difficulty Increased! (" + this.rollMasterDifficulty + ")", 1500);
}
},
@@ -3026,78 +2997,402 @@
} else if (gameState.currentState === "rollMaster") {
if (player) {
player.update();
}
- if (gameState.attackSpawnInterval > 0) {
+ // Spawner Pocisków (rmattack1)
+ // Spawner Pocisków (rmattack1) - TERAZ WYŁĄCZA SIĘ PO 75 SEKUNDACH
+ if (gameState.spreaderSpawnInterval > 0) {
gameState.attackSpawnTimer++;
if (gameState.attackSpawnTimer >= gameState.attackSpawnInterval) {
gameState.attackSpawnTimer = 0;
- var attackVisual = LK.getAsset('rmattack1', {
+ var projectileVisual = LK.getAsset('rmattack1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.6,
scaleY: 1.6
});
- var attackRadius = 45;
+ var projectileRadius = 45;
var edge = Math.floor(Math.random() * 4);
var startX, startY;
var targetX = 2048 / 2;
var targetY = 2732 / 2;
if (edge === 0) {
startX = Math.random() * 2048;
- startY = -attackRadius;
+ startY = -projectileRadius;
} else if (edge === 1) {
- startX = 2048 + attackRadius;
+ startX = 2048 + projectileRadius;
startY = Math.random() * 2732;
} else if (edge === 2) {
startX = Math.random() * 2048;
- startY = 2732 + attackRadius;
+ startY = 2732 + projectileRadius;
} else {
- startX = -attackRadius;
+ startX = -projectileRadius;
startY = Math.random() * 2732;
}
- attackVisual.x = startX;
- attackVisual.y = startY;
+ projectileVisual.x = startX;
+ projectileVisual.y = startY;
var dx = targetX - startX;
var dy = targetY - startY;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 5 + gameState.rollMasterDifficulty;
- var vx = dx / dist * speed;
- var vy = dy / dist * speed;
- var attackObject = game.addChild(attackVisual);
+ var vx = dist > 0 ? dx / dist * speed : 0;
+ var vy = dist > 0 ? dy / dist * speed : 0;
+ var projectileObject = game.addChild(projectileVisual);
gameState.rollMasterAttacks.push({
- visual: attackObject,
+ type: 'projectile',
+ visual: projectileObject,
vx: vx,
vy: vy,
- radius: attackRadius
+ radius: projectileRadius
});
}
}
+ // Spawner Eksplozji (rmattack2)
+ if (gameState.spreaderSpawnInterval > 0) {
+ if (gameState.explosionSpawnTimer >= gameState.explosionSpawnInterval) {
+ gameState.explosionSpawnTimer = 0;
+ var explosionWarningTime = 120;
+ var explosionActiveTime = 10;
+ var explosionRadius = 100;
+ var explosionVisual = LK.getAsset('rmattack2', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.5
+ });
+ explosionVisual.width = 200;
+ explosionVisual.height = 200;
+ var margin = 150;
+ var spawnX = margin + Math.random() * (2048 - 2 * margin);
+ var spawnY = margin + Math.random() * (2732 - 2 * margin);
+ explosionVisual.x = spawnX;
+ explosionVisual.y = spawnY;
+ tween(explosionVisual, {
+ alpha: 1.0
+ }, {
+ duration: 500,
+ easing: tween.easeInOut,
+ yoyo: true,
+ repeat: 1
+ });
+ var explosionObject = game.addChild(explosionVisual);
+ gameState.rollMasterAttacks.push({
+ type: 'explosion',
+ visual: explosionObject,
+ radius: explosionRadius,
+ state: 'warning',
+ timer: explosionWarningTime,
+ activeTimer: explosionActiveTime
+ });
+ }
+ }
+ // --- NOWY Spawner Promienia Lasera (rmattack3) ---
+ if (gameState.spreaderSpawnInterval > 0) {
+ gameState.laserSpawnTimer++;
+ if (gameState.laserSpawnTimer >= gameState.laserSpawnInterval) {
+ gameState.laserSpawnTimer = 0;
+ console.log("Spawnuję Laser (rmattack3)!");
+ var laserAngle = Math.random() * Math.PI * 2;
+ var laserWidth = 2200;
+ var laserVisualHeight = 120; // Zakładana wysokość assetu rmattack3 (dostosuj jeśli inna)
+ var laserHitboxHeight = 30; // Faktyczna wysokość hitboxa (cieńsza niż grafika)
+ var laserRadius = laserHitboxHeight / 2; // Dla uproszczonej kolizji
+ // Użyj 'rmattack3'
+ var laserWarningVisual = LK.getAsset('rmattack3', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: laserWidth,
+ // Rozciągnij na szerokość ekranu
+ height: laserVisualHeight,
+ // Użyj wysokości assetu
+ rotation: laserAngle,
+ alpha: 0.1,
+ // Niska alfa dla ostrzeżenia
+ tint: 0xFF8888 // Lekko czerwony odcień dla ostrzeżenia
+ });
+ laserWarningVisual.x = 2048 / 2;
+ laserWarningVisual.y = 2732 / 2;
+ // Animacja ostrzeżenia (pulsowanie alpha)
+ // Oblicz liczbę powtórzeń dla pulsowania przez cały czas ostrzeżenia
+ var repeatCountWarning = Math.max(0, Math.floor(gameState.laserWarningTime / (300 * 2 / (1000 / 60))) - 1);
+ tween(laserWarningVisual, {
+ alpha: 0.5
+ }, {
+ duration: 300,
+ yoyo: true,
+ repeat: repeatCountWarning
+ });
+ var laserObject = game.addChild(laserWarningVisual);
+ gameState.rollMasterAttacks.push({
+ type: 'laser',
+ // Typ ataku
+ visual: laserObject,
+ // Obiekt wizualny
+ radius: laserRadius,
+ // Uproszczony promień kolizji
+ state: 'warning',
+ // Stan początkowy
+ timer: gameState.laserWarningTime,
+ // Czas ostrzeżenia
+ angle: laserAngle,
+ // Kąt lasera
+ width: laserWidth,
+ // Szerokość (dla kolizji)
+ activeHeight: laserHitboxHeight // Faktyczna wysokość hitboxa
+ });
+ }
+ }
+ // --- NOWY Spawner Wzoru Rozchodzącego Się (rmattack4 -> rmattack1) ---
+ if (gameState.spreaderSpawnInterval > 0) {
+ // ZACZYNA SIĘ PO 75 SEKUNDACH
+ gameState.spreaderSpawnTimer++;
+ if (gameState.spreaderSpawnTimer >= gameState.spreaderSpawnInterval) {
+ gameState.spreaderSpawnTimer = 0;
+ console.log("Spawnuję Spreader Parent (rmattack4)!");
+ // Użyj 'rmattack4' jako rodzica
+ var spreaderParentVisual = LK.getAsset('rmattack4', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 1.2,
+ // Ewentualnie dostosuj skalę
+ scaleY: 1.2
+ });
+ // Zakładając, że rmattack4 ma wymiary 120x120 jak inne ataki
+ var spreaderParentRadius = 120 / 2 * 1.2; // Promień kolizji rodzica
+ var margin = 200;
+ var startX = margin + Math.random() * (2048 - 2 * margin);
+ var startY = margin + Math.random() * (2732 - 2 * margin);
+ spreaderParentVisual.x = startX;
+ spreaderParentVisual.y = startY;
+ var spreaderObject = game.addChild(spreaderParentVisual);
+ gameState.rollMasterAttacks.push({
+ type: 'spreader_parent',
+ // Typ rodzica
+ visual: spreaderObject,
+ // Obiekt wizualny
+ radius: spreaderParentRadius,
+ // Promień kolizji (czy ma kolidować?)
+ timer: gameState.spreaderSplitTime // Czas do podziału
+ });
+ }
+ }
+ // Aktualizacja, Kolizje i Usuwanie WSZYSTKICH Ataków
+ // Aktualizacja, Kolizje i Usuwanie WSZYSTKICH Ataków
if (player && !player.dead) {
for (var i = gameState.rollMasterAttacks.length - 1; i >= 0; i--) {
var attack = gameState.rollMasterAttacks[i];
- attack.visual.x += attack.vx;
- attack.visual.y += attack.vy;
- if (!player.invulnerable) {
- var dx_coll = player.x - attack.visual.x;
- var dy_coll = player.y - attack.visual.y;
- var dist_coll = Math.sqrt(dx_coll * dx_coll + dy_coll * dy_coll);
- var playerRadius = player.width / 2 * 0.8;
- if (dist_coll < playerRadius + attack.radius) {
- console.log("Kolizja! Koniec gry Roll Master.");
- gameState.endRollMasterMode(gameState.rollMasterTime);
- LK.effects.flashObject(game, 0xFF0000, 300);
- break;
+ var destroyAttack = false;
+ // --- Logika aktualizacji i kolizji dla każdego typu ataku ---
+ if (attack.type === 'projectile') {
+ // Dla rmattack1
+ attack.visual.x += attack.vx;
+ attack.visual.y += attack.vy;
+ // Sprawdzenie wyjścia poza ekran
+ if (attack.visual.x < -200 || attack.visual.x > 2048 + 200 || attack.visual.y < -200 || attack.visual.y > 2732 + 200) {
+ destroyAttack = true;
}
+ // Sprawdzenie kolizji dla projectile
+ if (!player.invulnerable) {
+ var dx_coll = player.x - attack.visual.x;
+ var dy_coll = player.y - attack.visual.y;
+ var dist_coll = Math.sqrt(dx_coll * dx_coll + dy_coll * dy_coll);
+ var playerRadius = player.width / 2 * 0.8;
+ if (dist_coll < playerRadius + attack.radius) {
+ console.log("Kolizja! Typ:", attack.type, "Koniec gry Roll Master.");
+ gameState.endRollMasterMode(gameState.rollMasterTime);
+ LK.effects.flashObject(game, 0xFF0000, 300);
+ return; // Zakończ update po śmierci
+ }
+ }
+ } else if (attack.type === 'explosion') {
+ // Dla rmattack2
+ if (attack.state === 'warning') {
+ attack.timer--;
+ if (attack.timer <= 0) {
+ attack.state = 'active';
+ attack.timer = attack.activeTimer;
+ tween.stop(attack.visual);
+ attack.visual.alpha = 1;
+ tween(attack.visual, {
+ scaleX: 1.2,
+ scaleY: 1.2,
+ alpha: 0
+ }, {
+ duration: attack.timer * (1000 / 60),
+ easing: tween.easeOut
+ });
+ }
+ } else if (attack.state === 'active') {
+ attack.timer--;
+ // Sprawdzenie kolizji dla explosion (TYLKO w stanie 'active')
+ if (!player.invulnerable) {
+ var dx_coll_e = player.x - attack.visual.x;
+ var dy_coll_e = player.y - attack.visual.y;
+ var dist_coll_e = Math.sqrt(dx_coll_e * dx_coll_e + dy_coll_e * dy_coll_e);
+ var playerRadius_e = player.width / 2 * 0.8;
+ if (dist_coll_e < playerRadius_e + attack.radius) {
+ console.log("Kolizja! Typ:", attack.type, "Koniec gry Roll Master.");
+ gameState.endRollMasterMode(gameState.rollMasterTime);
+ LK.effects.flashObject(game, 0xFF0000, 300);
+ return; // Zakończ update po śmierci
+ }
+ }
+ if (attack.timer <= 0) {
+ attack.state = 'finished';
+ destroyAttack = true;
+ }
+ } else {
+ // finished or unknown
+ destroyAttack = true;
+ }
}
- if (attack.visual.x < -200 || attack.visual.x > 2048 + 200 || attack.visual.y < -200 || attack.visual.y > 2732 + 200) {
- if (attack.visual && attack.visual.destroy) {
+ // --- NOWA Obsługa Lasera (rmattack3) ---
+ else if (attack.type === 'laser') {
+ if (attack.state === 'warning') {
+ attack.timer--;
+ if (attack.timer <= 0) {
+ // Zmiana na stan aktywny
+ attack.state = 'active';
+ attack.timer = gameState.laserActiveTime; // Ustaw czas aktywności
+ // Zatrzymaj animację ostrzeżenia i zmień wygląd
+ tween.stop(attack.visual);
+ attack.visual.alpha = 1; // Pełna alfa
+ attack.visual.tint = 0xFFFFFF; // Normalny kolor (usuń odcień ostrzegawczy)
+ LK.effects.flashObject(attack.visual, 0xFFFFFF, 100); // Krótki błysk przy aktywacji
+ }
+ } else if (attack.state === 'active') {
+ attack.timer--;
+ // SPRAWDZENIE KOLIZJI Z LASEREM (Uproszczone)
+ if (player && !player.dead && !player.invulnerable) {
+ // Sprawdzenie czy gracz istnieje i żyje
+ var playerRadius = player.width / 2 * 0.8;
+ var dx_p = player.x - attack.visual.x;
+ var dy_p = player.y - attack.visual.y;
+ var rotated_dy = -dx_p * Math.sin(-attack.angle) + dy_p * Math.cos(-attack.angle);
+ var rotated_dx = dx_p * Math.cos(-attack.angle) + dy_p * Math.sin(-attack.angle);
+ if (Math.abs(rotated_dy) < playerRadius + attack.activeHeight / 2 && Math.abs(rotated_dx) < attack.width / 2) {
+ console.log("Kolizja! Typ: laser (rmattack3)");
+ gameState.endRollMasterMode(gameState.rollMasterTime);
+ LK.effects.flashObject(game, 0xFF0000, 300);
+ return; // Zakończ update po śmierci
+ }
+ }
+ if (attack.timer <= 0) {
+ // Koniec aktywności - zacznij znikać
+ attack.state = 'fading';
+ // Zapisz referencję do ataku, bo 'attack' może się zmienić w onFinish
+ var currentAttack = attack;
+ tween(attack.visual, {
+ alpha: 0
+ }, {
+ duration: 150,
+ onFinish: function onFinish() {
+ // Upewnij się, że stan jest ustawiony na finished dopiero po zniknięciu
+ // i że to wciąż ten sam atak (nie został usunięty w międzyczasie)
+ if (currentAttack && currentAttack.state === 'fading') {
+ currentAttack.state = 'finished';
+ }
+ }
+ });
+ }
+ } else if (attack.state === 'fading') {
+ // Czekaj aż animacja znikania się skończy (stan zmieni się na 'finished' w onFinish)
+ } else {
+ // finished or unknown state
+ destroyAttack = true;
+ }
+ }
+ // --- NOWA Obsługa Wzoru Rozchodzącego Się (rmattack4 -> rmattack1) ---
+ else if (attack.type === 'spreader_parent') {
+ // Rodzic (rmattack4)
+ attack.timer--;
+ if (attack.timer <= 0) {
+ // Czas na podział!
+ console.log("Spreader Parent (rmattack4) dzieli się!");
+ var parentX = attack.visual.x;
+ var parentY = attack.visual.y;
+ // Stwórz pociski potomne
+ for (var j = 0; j < gameState.spreaderChildCount; j++) {
+ var angle = j / gameState.spreaderChildCount * Math.PI * 2; // Kąt dla każdego dziecka
+ var vxChild = Math.cos(angle) * gameState.spreaderChildSpeed;
+ var vyChild = Math.sin(angle) * gameState.spreaderChildSpeed;
+ // Użyj 'rmattack1' jako pocisku-dziecka
+ var childVisual = LK.getAsset('rmattack1', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: parentX,
+ // Startuj z pozycji rodzica
+ y: parentY,
+ scaleX: 1.2,
+ // Dostosuj skalę jeśli rmattack1 jest mniejszy/większy
+ scaleY: 1.2
+ });
+ var childRadius = 45 * 1.2; // Dostosuj promień jeśli skalujesz rmattack1
+ var childObject = game.addChild(childVisual);
+ // Dodaj dziecko do tablicy ataków
+ gameState.rollMasterAttacks.push({
+ type: 'spreader_child',
+ // Typ dziecka
+ visual: childObject,
+ vx: vxChild,
+ vy: vyChild,
+ radius: childRadius
+ });
+ }
+ // Zniszcz rodzica
+ destroyAttack = true;
+ }
+ // Opcjonalna kolizja z rodzicem (rmattack4) - można dodać tutaj jeśli potrzebne
+ /*
+ if (player && !player.dead && !player.invulnerable) { ... }
+ */
+ } else if (attack.type === 'spreader_child') {
+ // Dzieci (rmattack1)
+ // Zachowuje się jak zwykły pocisk
+ attack.visual.x += attack.vx;
+ attack.visual.y += attack.vy;
+ // Sprawdzenie, czy wyszedł poza ekran
+ if (attack.visual.x < -100 || attack.visual.x > 2048 + 100 || attack.visual.y < -100 || attack.visual.y > 2732 + 100) {
+ destroyAttack = true;
+ }
+ // Sprawdzenie kolizji
+ if (player && !player.dead && !player.invulnerable) {
+ var dx_coll_c = player.x - attack.visual.x;
+ var dy_coll_c = player.y - attack.visual.y;
+ var dist_coll_c = Math.sqrt(dx_coll_c * dx_coll_c + dy_coll_c * dy_coll_c);
+ var playerRadius_c = player.width / 2 * 0.8;
+ if (dist_coll_c < playerRadius_c + attack.radius) {
+ console.log("Kolizja! Typ: spreader_child (rmattack1)");
+ gameState.endRollMasterMode(gameState.rollMasterTime);
+ LK.effects.flashObject(game, 0xFF0000, 300);
+ return; // Zakończ update po śmierci
+ }
+ }
+ }
+ // --- Koniec logiki aktualizacji i kolizji ---
+ // --- Usuwanie ataku (wspólne dla wszystkich) ---
+ if (destroyAttack || attack.visual && attack.visual.destroyed) {
+ if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
+ tween.stop(attack.visual); // Zatrzymaj tweens przed zniszczeniem
attack.visual.destroy();
}
- gameState.rollMasterAttacks.splice(i, 1);
+ // Sprawdzenie czy atak nadal istnieje w tablicy przed usunięciem (na wszelki wypadek)
+ var currentIndex = gameState.rollMasterAttacks.indexOf(attack);
+ if (currentIndex === i) {
+ // Upewnij się, że usuwasz właściwy element
+ gameState.rollMasterAttacks.splice(i, 1);
+ } else {
+ // Jeśli indeks się nie zgadza, poszukaj i usuń (bardziej bezpieczne, ale wolniejsze)
+ var correctIndex = gameState.rollMasterAttacks.indexOf(attack);
+ if (correctIndex !== -1) {
+ gameState.rollMasterAttacks.splice(correctIndex, 1);
+ }
+ // Zmniejsz i, aby skompensować przesunięcie po splice w nietypowej sytuacji
+ i--;
+ }
}
- }
- }
+ // --- Koniec usuwania ---
+ } // Koniec pętli for
+ } // Koniec if (player && !player.dead)
}
};
// --- Rozpoczęcie gry ---
// Inicjalizuj obiekt gameState, który zajmie się resztą.