Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.playerGraphics);' Line Number: 262
User prompt
Please fix the bug: 'self.attachAsset is not a function' in or related to this line: 'self.playerGraphics = self.attachAsset('player', {' Line Number: 259
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'self.addChild is not a function' in or related to this line: 'self.addChild(self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
User prompt
Please fix the bug: 'this.addChildAt is not a function' in or related to this line: 'Container.prototype.addChild.call(self, self.bossGraphics); // Add the bossGraphics as a child' Line Number: 657
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem)
// Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem)
var Boss = Container.expand(function () {
var self = Container.call(this);
// Definicje list klatek dla fireballi - Zdefiniowane raz na początku klasy Boss
var circleFrames = [LK.getAsset('fireball0', {}), LK.getAsset('fireball00', {}), LK.getAsset('fireball01', {}), LK.getAsset('fireball02', {}), LK.getAsset('fireball03', {}), LK.getAsset('fireball04', {}), LK.getAsset('fireball05', {}), LK.getAsset('fireball06', {}), LK.getAsset('fireball07', {}), LK.getAsset('fireball08', {}), LK.getAsset('fireball09', {}), LK.getAsset('fireball1', {}), LK.getAsset('fireball10', {}), LK.getAsset('fireball11', {}), LK.getAsset('fireball12', {}), LK.getAsset('fireball13', {}), LK.getAsset('fireball14', {})];
var lineFrames = [LK.getAsset('fireball2', {}), LK.getAsset('fireball3', {}), LK.getAsset('fireball4', {}), LK.getAsset('fireball5', {}), LK.getAsset('fireball6', {}), LK.getAsset('fireball7', {}), LK.getAsset('fireball8', {}), LK.getAsset('fireball9', {}), LK.getAsset('fireball15', {}), LK.getAsset('fireball16', {})];
// *** MODYFIKACJA: Dodaj grafikę bossIdle jako DZIECKO obiektu bossa (self) ***
// Używamy attachAsset, który już dodaje jako dziecko i ustawia pozycję relatywną na podstawie anchor.
self.bossGraphics = self.attachAsset('bossIdle', {
// Dziecko self
anchorX: 0.5,
anchorY: 0.5
// Pozycja relatywna na 0,0 jest domyślna i poprawna przy anchorX/Y 0.5
});
// Dodajemy funkcje animacji
// Ta funkcja tworzy obiekt SpriteAnimation dla animacji ataku Bossa (nie fireballi)
self.createBossAttackAnim = function () {
var frames = [LK.getAsset('bossAttack0', {}), LK.getAsset('bossAttack1', {}), LK.getAsset('bossAttack2', {}), LK.getAsset('bossAttack3', {})];
var bossAttackAnim = new SpriteAnimation({
frames: frames,
frameDuration: 100,
// Czas trwania klatki animacji ataku Bossa (w ms)
loop: false,
anchorX: 0.5,
anchorY: 0.5,
// Pozycja animacji jako dziecka bossa - ustawiamy na 0,0 relatywnie do rodzica (self)
x: 0,
// MODIFIED
y: 0 // MODIFIED
});
return bossAttackAnim;
};
// Zmieniamy sygnaturę funkcji, przyjmuje teraz typ ataku (do decydowania o grafice Bossa)
// Ta funkcja zarządza grafiką Bossa (animacja ataku Bossa lub grafika idle)
self.playBossAttackAnim = function (attackType) {
// <--- DODANO PARAMETR
// Upewnij się, że poprzednia animacja ataku Bossa jest całkowicie usunięta, zanim zaczniemy nową
if (self.bossAttackAnim) {
self.bossAttackAnim.stop(); // Zatrzymujemy animację
// Usuń poprzednią animację z jej rodzica (Bossa) przed zniszczeniem
// Sprawdź, czy rodzicem jest self przed próbą usunięcia
if (self.bossAttackAnim.parent === self) {
// MODIFIED check
self.bossAttackAnim.parent.removeChild(self.bossAttackAnim);
} else if (self.bossAttackAnim.parent) {
// Fallback na wypadek, gdyby rodzic był inny
self.bossAttackAnim.parent.removeChild(self.bossAttackAnim);
}
self.bossAttackAnim.destroy(); // Usuwamy poprzednią animację
self.bossAttackAnim = null; // Ustawiamy na null po zniszczeniu
}
// *** Zmieniona logika: Zmiana grafiki bossa tylko jeśli to NIE jest atak szarży (attackType 2) ***
if (attackType !== 2) {
// Jeśli to atak koła (0) lub linii (1)
// Usuń obecną główną grafikę bossa (idle) jako dziecko bossa
// Sprawdź, czy rodzicem jest self przed próbą usunięcia
if (self.bossGraphics && self.bossGraphics.parent === self) {
// MODIFIED check
self.bossGraphics.parent.removeChild(self.bossGraphics);
} else if (self.bossGraphics && self.bossGraphics.parent) {
// Fallback
self.bossGraphics.parent.removeChild(self.bossGraphics);
}
self.bossGraphics = null; // Wyczyść referencję
// Tworzymy nową animację bossa i dodajemy ją JAKO DZIECKO BOSSA
self.bossAttackAnim = self.addChild(self.createBossAttackAnim()); // *** MODIFIED: DODANO DO self! ***
// Pozycja animacji jako dziecka bossa jest już ustawiona na 0,0 w createBossAttackAnim
// Metoda update dla TEJ NOWEJ animacji (definiowana tylko dla ataków 0 i 1)
self.bossAttackAnim.update = function () {
// Sprawdź, czy ten obiekt animacji jest nadal aktywny self.bossAttackAnim bossa
// Użyj 'this' dla obiektu animacji, 'self' dla obiektu Boss
if (self.bossAttackAnim !== this || !this.playing || this.frames.length === 0) {
// Jeśli już nie jesteśmy aktualną animacją lub nie gramy, zakończ
return;
}
this.frameTimer++;
if (this.frameTimer >= this.frameDuration / (1000 / 60)) {
// Przelicz ms na klatki gry (przy 60fps)
this.frameTimer = 0;
this.removeChildren(); // Usuń sprite klatki z kontenera animacji (z obiektu animacji)
this.currentFrame++;
if (this.currentFrame >= this.frames.length) {
// Animacja skończona, wracamy do idle
// *** Dodaj grafikę idle z powrotem JAKO DZIECKO BOSSA i ustaw self.bossGraphics ***
self.bossGraphics = self.addChild(LK.getAsset('bossIdle', {
// *** MODIFIED: DODANO DO self! ***
anchorX: 0.5,
anchorY: 0.5,
x: 0,
// Pozycja relatywna
y: 0 // Pozycja relatywna
}));
// *** Usuń obiekt animacji z jego rodzica (Bossa) i zniszcz go ***
// Sprawdź, czy rodzicem jest self przed próbą usunięcia
if (this.parent === self) {
// MODIFIED check
this.parent.removeChild(this); // Użyj 'this' dla obiektu animacji
} else if (this.parent) {
// Fallback
this.parent.removeChild(this);
}
this.destroy(); // Zniszcz obiekt animacji
self.bossAttackAnim = null; // Wyczyść referencję w obiekcie bossa
} else {
this.addChild(this.frames[this.currentFrame]); // Dodaj sprite następnej klatki (do obiektu animacji)
}
}
};
}
// Else (attackType === 2, czyli chargeAttack): playBossAttackAnim nic nie robi z grafiką.
// chargeAttack sam zadba o ustawienie grafiki 'idle' JAKO DZIECKO BOSSA.
};
self.health = 100; // Domyślne zdrowie bossa (nadpisane w startGame)
self.maxHealth = 100; // Domyślne max zdrowia bossa (nadpisane w startGame)
self.speed = 5; // Prędkość ruchu bossa
self.attackCooldown = 0; // Czas do następnego ataku (w klatkach gry)
self.attackPattern = 0;
self.attacks = []; // Aktywne ataki bossa (obiekty JS z pozycją, promieniem, itp.)
self.stunned = false;
self.stunDuration = 0;
self.dead = false;
self.phase = 1;
self.attackSpeedMultiplier = 1; // Mnożnik prędkości ataków (1 = normalna, < 1 = szybsza)
self.repositioning = false; // Flaga informująca czy boss się przemieszcza po szarży
// startAttackPattern pozostaje jak w poprzedniej poprawionej wersji
self.startAttackPattern = function () {
// !!! Ustaw tymczasowy cooldown na początku, ZANIM sprawdzisz warunki powrotu !!!
// To zapobiegnie natychmiastowemu ponownemu wywołaniu, jeśli metoda wróci wcześnie
self.attackCooldown = 1;
// Ustaw minimalny cooldown (np. 1 klatka) od razu
// Sprawdź warunki wczesnego powrotu: boss jest martwy, gra nie jest w stanie "game" LUB boss jest w stanie "repositioning"
if (self.dead || gameState.currentState !== "game" || self.repositioning) {
// Dodano || self.repositioning
return; // Jeśli warunek spełniony, wróć
}
// Decydujemy O WZORCU ATAKU przed zmianą grafiki bossa
self.attackPattern = (self.attackPattern + 1) % 3; // Cykl wzorców ataków (0, 1, 2)
// 🔥 Przekazujemy typ ataku do playBossAttackAnim - ta funkcja teraz decyduje CZY zmienić grafikę bossa
self.playBossAttackAnim(self.attackPattern); // <--- PRZEKAZUJEMY TYP ATAKU
// Następnie wywołujemy właściwą funkcję ataku (chargeAttack itp.)
// Logika graficzna dla szarży jest TERAZ W chargeAttack
switch (self.attackPattern) {
case 0:
self.circleAttack(); // Wywołaj atak koła
break;
case 1:
self.lineAttack(); // Wywołaj atak linii
break;
case 2:
self.chargeAttack(); // Wywołaj atak szarży
break;
}
// Ustaw właściwy czas odnowienia dla następnego ataku
self.attackCooldown = (90 + Math.floor(Math.random() * 60)) * self.attackSpeedMultiplier; // 1.5-2.5 seconds * multiplier
};
// Zmodyfikowana funkcja createAttack - NIE nadpisuje lokalnie spriteAnim.update (jak w poprzedniej próbie)
// Kolejność argumentów: x, y, duration (ms), activationFrame (index klatki), frameDurationMs (ms), framesList (opcjonalne)
// Logika aktywacji kolizji (isActive) jest w Boss.update
self.createAttack = function (x, y, duration, activationFrame, frameDurationMs, framesList) {
// Użyj przekazanej framesList, domyślną oryginalną jeśli nie przekazana
var frames = framesList || [LK.getAsset('fireball0', {}), LK.getAsset('fireball00', {}), LK.getAsset('fireball01', {}), LK.getAsset('fireball02', {}), LK.getAsset('fireball03', {}), LK.getAsset('fireball04', {}), LK.getAsset('fireball05', {}), LK.getAsset('fireball06', {}), LK.getAsset('fireball07', {}), LK.getAsset('fireball08', {}), LK.getAsset('fireball09', {}), LK.getAsset('fireball1', {}), LK.getAsset('fireball10', {}), LK.getAsset('fireball11', {}), LK.getAsset('fireball12', {}), LK.getAsset('fireball13', {}), LK.getAsset('fireball14', {})];
var spriteAnim = game.addChild(new SpriteAnimation({
// Wizualizacja fireballa jest dodawana do kontenera 'game'
frames: frames,
frameDuration: frameDurationMs,
// Użyj wartości przekazanej do konstruktora SpriteAnimation
loop: false,
// Animacja nie zapętla się
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y
}));
spriteAnim.scaleX = 1.6; // Ręczne skalowanie wizualizacji
spriteAnim.scaleY = 1.6;
var attack = {
// Obiekt logiczny ataku
x: x,
y: y,
radius: 60,
// Promień kolizji
visual: spriteAnim,
// Referencja do wizualizacji (SpriteAnimation)
lifeTime: Math.floor(duration * (60 / 1000)),
// Czas życia w klatkach gry (przy 60fps)
isActive: false,
// Flaga aktywności kolizji, domyślnie false
activationFrame: activationFrame // Przechowuj activationFrame w obiekcie ataku
};
// USUNIĘTO: Lokalna definicja funkcji spriteAnim.update
self.attacks.push(attack); // Dodaj obiekt ataku logicznego do tablicy bossa
};
// Zmodyfikowana funkcja circleAttack - Z pętlą setTimeout delay (jak w poprzedniej próbie, która dała wiele pocisków)
self.circleAttack = function () {
LK.getSound('bossAttack').play(); // Odtwórz dźwięk ataku
var center = {
// Ustaw środek koła na pozycji bossa
x: self.x,
y: self.y
};
var count = isNewBossPlusMode ? 12 : 8; // Liczba pocisków w ataku (więcej w New Boss+)
var radius = 300; // Promień koła, na którym rozmieszczone są pociski
var attackLifeTime = isNewBossPlusMode ? 2000 : 3000; // Czas życia pocisku w ms (krótszy w New Boss+?)
// circleFrames array jest zdefiniowane raz na początku klasy Boss
// Pętla tworząca wiele pocisków rozmieszczonych w kole Z MINIMALNYM OPÓŹNIENIEM DLA KAŻDEGO
for (var i = 0; i < count; i++) {
// Iteruj tyle razy, ile ma być pocisków (count)
var angle = i / count * Math.PI * 2; // Oblicz kąt dla bieżącego pocisku (równo rozmieszczone na okręgu)
var posX = center.x + Math.cos(angle) * radius; // Oblicz współrzędną X na okręgu
var posY = center.y + Math.sin(angle) * radius; // Oblicz współrzędną Y na okręgu
// Dodaj minimalne opóźnienie (np. 20ms) DO KAŻDEGO pocisku
var delay = i * 20 + 20; // Zapewnij, że nawet pierwszy pocisk ma opóźnienie
// Użyj setTimeout do stworzenia każdego pocisku po małym opóźnieniu
LK.setTimeout(function (x, y) {
// Używamy closure do "zamknięcia" wartości x, y (frames jest globalne/dostępne)
return function () {
// Sprawdź czy boss nadal istnieje i gra jest w stanie 'game' zanim stworzysz atak po opóźnieniu
if (self && !self.dead && gameState.currentState === "game") {
// Wywołaj funkcję createAttack dla tego pocisku z zapamiętaną pozycją i parametrami
// Kolejność argumentów createAttack: x, y, duration(ms), activationFrame(index), frameDurationMs(ms), framesList
// activationFrame = 8 (aktywacja od klatki fireball08)
// frameDurationMs = 250ms (przykład prędkości)
// framesList = circleFrames (używamy zmiennej zdefiniowanej poza funkcją)
self.createAttack(x, y, attackLifeTime, 8, 250, circleFrames); // Używamy 250ms dla frameDurationMs
}
};
}(posX, posY), delay); // Przekaż aktualne posX, posY do closure
}
};
// *** START Zmodyfikowana funkcja lineAttack - Używa synchronicznej pętli i wywołuje nową createAttack ***
self.lineAttack = function () {
LK.getSound('bossAttack').play(); // Odtwórz dźwięk ataku
// Tworzy linię ataków
if (!player) {
return;
}
var targetX = player.x;
var targetY = player.y;
var count = isNewBossPlusMode ? 8 : 5; // Liczba pocisków w linii
var attackLifeTime = isNewBossPlusMode ? 1500 : 2000; // Czas życia pocisku w ms
var delayBetweenAttacks = isNewBossPlusMode ? 100 : 200; // Opóźnienie między pociskami w ms
// lineFrames array jest zdefiniowane raz na początku klasy
// Pętla tworząca pociski w linii z opóźnieniem (jak w souls5.txt)
for (var i = 0; i < count; i++) {
var t = i / (count - 1); // Współczynnik interpolacji
var posX = self.x + (targetX - self.x) * t; // Interpolowana pozycja X
var posY = self.y + (targetY - self.y) * t; // Interpolowana pozycja Y
var delay = i * delayBetweenAttacks * self.attackSpeedMultiplier; // Opóźnienie przed stworzeniem pocisku, skalowane
LK.setTimeout(function (x, y) {
// Używamy closure do przekazania pozycji
return function () {
// Sprawdź warunki przed stworzeniem ataku po opóźnieniu
if (self && !self.dead && gameState.currentState === "game") {
// Wywołaj funkcję createAttack
// Kolejność argumentów createAttack: x, y, duration(ms), activationFrame(index), frameDurationMs(ms), framesList
// activationFrame = 3 (indeks 'fireball5' na liście lineFrames)
// frameDurationMs = 100ms (przykład prędkości)
// framesList = lineFrames (używamy zmiennej zdefiniowanej poza funkcją)
self.createAttack(x, y, attackLifeTime, 3, 100, lineFrames); // Przekaż parametry
}
};
}(posX, posY), delay); // Przekaż aktualne posX, posY do closure
}
};
// *** END Zmodyfikowana funkcja lineAttack ***
// *** START Zmodyfikowana funkcja chargeAttack - tworzy pociski podczas szarży ***
// Ta funkcja zarządza ruchem bossa (szarżą) i tworzy pociski wzdłuż ścieżki szarży
self.chargeAttack = function () {
LK.getSound('bossAttack').play(); // Odtwórz dźwięk szarży
// Upewnij się, że gracz istnieje
if (!player) {
return;
}
// Obsługa grafiki bossa podczas szarży (pokazuje grafikę idle)
if (self.bossAttackAnim) {
self.bossAttackAnim.stop();
if (self.bossAttackAnim.parent === self) {
self.bossAttackAnim.parent.removeChild(self.bossAttackAnim);
} else if (self.bossAttackAnim.parent) {
self.bossAttackAnim.parent.removeChild(self.bossAttackAnim);
}
self.bossAttackAnim.destroy();
self.bossAttackAnim = null;
}
if (self.bossGraphics && self.bossGraphics.parent === self) {
self.bossGraphics.parent.removeChild(self.bossGraphics);
} else if (self.bossGraphics && self.bossGraphics.parent) {
self.bossGraphics.parent.removeChild(self.bossGraphics);
}
self.bossGraphics = self.addChild(LK.getAsset('bossIdle', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
}));
// Oblicz kierunek do gracza i parametry szarży
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
dx /= distance;
dy /= distance;
}
var startX = self.x; // Pozycja początkowa szarży
var startY = self.y;
var chargeDistance = isNewBossPlusMode ? 700 : 500; // Dystans szarży
var chargeDuration = isNewBossPlusMode ?
// Czas trwania szarży
600 : 800;
var attacksOnPathCount = isNewBossPlusMode ? 8 : 5; // Liczba pocisków wzdłuż ścieżki
var attackLifeTime = isNewBossPlusMode ? 1000 : 1500; // Czas życia pocisku
// Tween (animacja ruchu) bossa podczas szarży
tween(self, {
x: self.x + dx * chargeDistance,
y: self.y + dy * chargeDistance
}, {
duration: chargeDuration * self.attackSpeedMultiplier,
// Czas trwania, skalowany
easing: tween.easeIn,
// Krzywa animacji
onFinish: function onFinish() {
// Funkcja po zakończeniu szarży
if (self && !self.dead && gameState.currentState === "game") {
// Faza repozycji
self.repositioning = true;
LK.setTimeout(function () {
if (self && !self.dead) {
self.repositioning = false;
}
}, 500 * self.attackSpeedMultiplier);
// Tween powrotu do pozycji startowej
tween(self, {
x: startX,
y: startY
}, {
duration: 1000 * self.attackSpeedMultiplier,
easing: tween.easeOut
});
// Utworzenie ataków (fireballi) wzdłuż ścieżki szarży
// Fireballe ze szarży używają domyślnej listy klatek z createAttack (circleFrames)
for (var i = 0; i < attacksOnPathCount; i++) {
var t = i / (attacksOnPathCount - 1); // Współczynnik interpolacji
var currentChargeX = self.x; // Aktualna pozycja bossa (koniec szarży)
var currentChargeY = self.y;
var posX = startX + (currentChargeX - startX) * t; // Interpolowana pozycja X
var posY = startY + (currentChargeY - startY) * t; // Interpolowana pozycja Y
// Utwórz atak
// Kolejność argumentów createAttack: x, y, duration(ms), activationFrame(index), frameDurationMs(ms), framesList
// activationFrame = 4 (domyślna klatka aktywacji dla tych pocisków)
// frameDurationMs = 140ms (domyślna prędkość)
// framesList = circleFrames (używamy listy klatek ataku koła)
self.createAttack(posX, posY, attackLifeTime, 4, 140, circleFrames); // Przekaż parametry
}
}
}
});
};
self.takeDamage = function (amount) {
// Funkcja obsługująca otrzymywanie obrażeń przez bossa
if (self.dead || gameState.currentState !== "game") {
// Boss otrzymuje obrażenia tylko w stanie gry
return;
}
self.health -= amount; // Zmniejsz zdrowie
self.health = Math.max(0, self.health); // Upewnij się, że zdrowie nie spadnie poniżej zera
LK.effects.flashObject(self, 0xFFFFFF, 200); // Efekt błysku
if (!isNewBossPlusMode) {
// Przejście fazy bossa przy 50% zdrowia tylko w standardowym trybie
if (self.health <= self.maxHealth / 2 && self.phase === 1) {
self.phase = 2; // Zmień fazę na 2
self.speed += 2; // Boss staje się szybszy (ruch)
self.attackSpeedMultiplier *= 0.8; // Boss atakuje nieco szybciej
tween(self, {
tint: 0xFF3300 // Pomarańczowy/czerwony odcień
}, {
duration: 1000,
easing: tween.easeInOut
});
}
}
if (self.health <= 0) {
self.die(); // Wywołaj funkcję śmierci
}
// ui.updateBossHealth jest w game.update
};
self.die = function () {
// Funkcja obsługująca śmierć bossa
if (self.dead || gameState.currentState !== "game") {
return;
}
self.dead = true; // Ustaw flagę śmierci
if (!isNewBossPlusMode) {
// Dźwięk zwycięstwa tylko dla STANDARDOWEGO trybu
LK.getSound('victory').play();
}
// Wyczyść pozostałe ataki przy śmierci bossa
self.attacks.forEach(function (attack) {
if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
attack.visual.destroy();
}
});
self.attacks = []; // Wyczyść tablicę ataków logicznych
// Animacja śmierci bossa (zanikanie i powiększanie)
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Po zakończeniu animacji śmierci
if (self && self.destroy && !self.destroyed) {
self.destroy(); // Zniszcz obiekt bossa
}
storage.bossesDefeated = (storage.bossesDefeated || 0) + 1; // Zwiększ licznik pokonanych bossów
// ZAWSZE przechodzimy do Grill Screena
gameState.showGrillScreen();
}
});
};
self.update = function () {
// Główna metoda aktualizacji bossa
// Aktualizuj tylko w stanie gry
if (gameState.currentState !== "game") {
if (!self.dead && self.attacks) {
// Jeśli zmieniono stan i boss nie umiera, wyczyść ataki
self.attacks.forEach(function (attack) {
if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
attack.visual.destroy();
}
});
self.attacks = [];
}
return;
}
// Nie aktualizuj jeśli boss jest martwy (pozwól animacji śmierci działać)
if (self.dead) {
return;
}
// --- LOGIKA RUCHU BOSS ---
// Poruszaj się w kierunku gracza jeśli spełnione warunki
if (!self.repositioning && self.attackCooldown > 30 && player && !player.dead) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var moveSpeed = self.speed;
if (distance > 150) {
// Poruszaj się tylko jeśli gracz jest dalej niż 150 jednostek
var moveX = dx / distance * moveSpeed;
var moveY = dy / distance * moveSpeed;
var nextX = self.x + moveX;
var nextY = self.y + moveY;
// Ograniczenia areny
var halfWidth = self.width * self.scaleX / 2;
var halfHeight = self.height * self.scaleY / 2;
var minX = 100 + halfWidth;
var maxX = 2048 - 100 - halfWidth;
var minY = 100 + halfHeight;
var maxY = 2732 - 100 - halfHeight;
self.x = Math.max(minX, Math.min(nextX, maxX));
self.y = Math.max(minY, Math.min(nextY, maxY));
// Grafika jako dziecko podąża automatycznie.
}
}
// --- KONIEC LOGIKA RUCHU BOSS ---
// --- OBSŁUGA ATAKÓW BOSS (Kolizje i Czas Życia Pocisków) ---
// Aktualizuj istniejące ataki bossa (pociski) i sprawdzaj kolizje z graczem
for (var i = self.attacks.length - 1; i >= 0; i--) {
var attack = self.attacks[i];
// Aktualizuj flagę isActive na podstawie aktualnej klatki visual i przechowywanego activationFrame
// Sprawdź czy visual istnieje i ma currentFrame
if (attack.visual && typeof attack.visual.currentFrame !== 'undefined') {
if (attack.visual.currentFrame >= attack.activationFrame) {
attack.isActive = true; // Aktywuj kolizję
} else {
attack.isActive = false; // Kolizja nieaktywna
}
} else {
attack.isActive = false;
}
if (attack.lifeTime > 0) {
// Zmniejszaj czas życia
attack.lifeTime--;
}
// Sprawdź kolizję z graczem jeśli spełnione warunki
if (attack.isActive && player && !player.dead && !player.invulnerable && attack.visual && !attack.visual.destroyed) {
var dx_p = player.x - attack.x;
var dy_p = player.y - attack.y;
var distance_p = Math.sqrt(dx_p * dx_p + dy_p * dy_p);
var playerRadius_p = player.width / 2;
var attackRadius = attack.radius;
if (distance_p < playerRadius_p + attackRadius) {
player.takeDamage(1); // Gracz otrzymuje obrażenia
if (attack.visual && attack.visual.destroy) {
attack.visual.destroy();
}
self.attacks.splice(i, 1); // Usuń trafiony atak
// break;
}
}
// Usuń atak, jeśli skończył mu się czas życia LUB wizualizacja zniszczona
if (attack.lifeTime <= 0 || attack.visual && attack.visual.destroy || attack.visual && attack.visual.destroyed) {
if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
attack.visual.destroy();
}
self.attacks.splice(i, 1);
}
}
// --- KONIEC OBSŁUGA ATAKÓW BOSS ---
if (self.attackCooldown > 0) {
// Zmniejszaj cooldown
self.attackCooldown--;
}
// Sprawdź, czy nadszedł czas na nowy atak
if (self.attackCooldown <= 0 && !self.repositioning) {
self.startAttackPattern();
}
};
return self; // Zwróć instancję obiektu Boss
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Zamiast var playerGraphics = ...
self.playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Player properties
self.health = 5; // Default health (will be overridden in startGame)
self.speed = 8; // Movement speed (NIE UŻYWANE OBECNIE W UPDATE?)
self.rolling = false; // Flag for roll state
self.rollDirection = {
x: 0,
y: 0
}; // Direction of roll
self.rollSpeed = 20; // Speed during roll
self.rollDuration = 300; // Roll duration in ms
self.rollCooldown = 0; // Cooldown between rolls (frames)
self.invulnerable = false; // Invulnerability flag
self.invulnerabilityFrames = 0; // Frames of invulnerability left
self.dead = false; // Dead state
self.rollTimeoutId = null; // Store timeout ID for roll duration
self.invulnerabilityTimeoutId = null; // Store timeout ID for invulnerability (NIE UŻYWANE?)
self.rollAnimationInterval = null; // Store interval ID for roll animation
self.hasRolledThroughBossThisRoll = false; // Track if boss was hit during this roll
// Clear all roll-related timeouts and intervals
self.clearRollTimeouts = function () {
if (self.rollTimeoutId) {
LK.clearTimeout(self.rollTimeoutId);
self.rollTimeoutId = null;
}
if (self.rollAnimationInterval) {
LK.clearInterval(self.rollAnimationInterval);
self.rollAnimationInterval = null;
}
};
// Roll mechanic
self.roll = function (direction) {
if (!self.rolling && self.rollCooldown <= 0 && !self.dead) {
self.rolling = true;
self.rollDirection = direction;
self.rollCooldown = 45;
self.invulnerable = true;
self.invulnerabilityFrames = 30;
self.hasRolledThroughBossThisRoll = false;
// Ukryj normalną grafikę gracza
if (self.playerGraphics && !self.playerGraphics.destroyed) {
self.playerGraphics.visible = false;
}
// --- Roll Animation (turlanie) ---
var rollFrames = ['roll', 'roll0', 'roll1', 'roll2'];
var currentFrame = 0;
var rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
}));
var rollAnimationInterval = LK.setInterval(function () {
if (rollAnimationSprite && rollAnimationSprite.destroy) {
rollAnimationSprite.destroy();
}
currentFrame = (currentFrame + 1) % rollFrames.length;
rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
}));
}, 70); // co 70ms zmienia klatkę podczas turlania
if (self.rollTimeoutId) {
LK.clearTimeout(self.rollTimeoutId);
}
self.rollTimeoutId = LK.setTimeout(function () {
self.rolling = false;
// --- Po rollu zakończ turlanie ---
if (rollAnimationInterval) {
LK.clearInterval(rollAnimationInterval);
}
if (rollAnimationSprite && rollAnimationSprite.destroy) {
rollAnimationSprite.destroy();
}
// --- Stand Up Animation (wstawanie + machnięcie mieczem) ---
var standUpFrames = ['roll3', 'roll4'];
var standUpFrame = 0;
var standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
}));
var standUpInterval = LK.setInterval(function () {
if (standUpSprite && standUpSprite.destroy) {
standUpSprite.destroy();
}
standUpFrame++;
if (standUpFrame < standUpFrames.length) {
standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
}));
} else {
// Koniec animacji stand-up
LK.clearInterval(standUpInterval);
if (standUpSprite && standUpSprite.destroy) {
standUpSprite.destroy();
}
// Przywróć normalną grafikę gracza
if (self.playerGraphics && !self.playerGraphics.destroyed) {
self.playerGraphics.visible = true;
}
}
}, 100); // 100ms między klatkami wstawania
self.rollTimeoutId = null;
}, self.rollDuration);
}
};
// Take damage method
self.takeDamage = function (amount) {
// Check if player can take damage (not invulnerable and not dead)
if (!self.invulnerable && !self.dead) {
self.health -= amount;
// Flash effect when hit
LK.effects.flashObject(self, 0xFF0000, 200);
// Check if player is dead
if (self.health <= 0) {
self.health = 0; // Ensure health doesn't go below 0 visually
self.die();
return; // Zakończ, jeśli gracz umarł
}
// Set brief invulnerability after hit (if not rolling)
// Nietykalność po trafieniu jest teraz KRÓTSZA niż turlanie
if (!self.rolling) {
self.invulnerable = true;
self.invulnerabilityFrames = 30; // Krótsza nietykalność po trafieniu (np. 0.5s)
}
}
};
// Die method
self.die = function () {
if (self.dead) {
// Zapobiegaj wielokrotnemu wywołaniu
return;
}
self.dead = true;
// Increment death counter
storage.totalDeaths = (storage.totalDeaths || 0) + 1;
// Increment max hearts after death (up to a maximum of 10)
if (!storage.maxHearts || storage.maxHearts < 5) {
// Upewnij się, że startujemy od min 5
storage.maxHearts = 5;
}
if (storage.maxHearts < 10) {
storage.maxHearts = storage.maxHearts + 1;
}
self.clearRollTimeouts(); // Anuluj ewentualne trwające turlanie
// Death animation
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Zniszcz obiekt gracza PO zakończeniu animacji
if (self && self.destroy && !self.destroyed) {
self.destroy();
}
// Set death reason flag to true (player died)
gameOverReasonIsDeath = true;
// Show game over screen - gameState zarządza przejściem
gameState.gameOver(true); // Przekaż true (śmierć gracza)
}
});
};
// Update method called every frame
self.update = function () {
// Aktualizuj tylko w stanie gry
if (gameState.currentState !== "game") {
// Wyczyść timery, jeśli stan gry się zmienił
if (self.rolling) {
self.rolling = false;
} // Przerwij turlanie
self.clearRollTimeouts();
return;
}
// Nie aktualizuj jeśli gracz jest martwy (ale pozwól animacji śmierci działać)
if (self.dead) {
return;
}
// Handle roll cooldown
if (self.rollCooldown > 0) {
self.rollCooldown--;
}
// Handle invulnerability frames (zarówno z turlania, jak i z otrzymania obrażeń)
if (self.invulnerable && self.invulnerabilityFrames > 0) {
self.invulnerabilityFrames--;
// Blinking effect during invulnerability
// Miga szybciej
self.alpha = self.invulnerabilityFrames % 4 > 1 ? 0.3 : 1;
if (self.invulnerabilityFrames <= 0) {
self.invulnerable = false;
self.alpha = 1; // Restore full opacity when invulnerability ends
}
} else if (self.invulnerable && self.invulnerabilityFrames <= 0) {
// Upewnij się, że stan jest spójny jeśli klatki się skończyły
self.invulnerable = false;
self.alpha = 1;
}
// Handle movement during roll
if (self.rolling) {
var rollDx = self.rollDirection.x * self.rollSpeed;
var rollDy = self.rollDirection.y * self.rollSpeed;
var nextX = self.x + rollDx;
var nextY = self.y + rollDy;
// Ograniczenia areny (ściany na 100px marginesie, mapa 2048x2732)
// Uwzględnij rozmiar gracza (zakładając anchor 0.5, 0.5)
var halfWidth = self.width / 2;
var halfHeight = self.height / 2;
var minX = 100 + halfWidth;
var maxX = 2048 - 100 - halfWidth;
var minY = 100 + halfHeight;
var maxY = 2732 - 100 - halfHeight;
self.x = Math.max(minX, Math.min(nextX, maxX));
self.y = Math.max(minY, Math.min(nextY, maxY));
// Sprawdź kolizję turlania z bossem
// Sprawdź tylko jeśli boss istnieje, nie jest martwy i JESZCZE nie zadałeś obrażeń tym turlaniem
if (boss && !boss.dead && !self.hasRolledThroughBossThisRoll) {
// Uproszczone sprawdzenie kolizji na podstawie odległości między środkami
var dx_b = self.x - boss.x;
var dy_b = self.y - boss.y;
var distance_b = Math.sqrt(dx_b * dx_b + dy_b * dy_b);
// Uproszczony promień kolizji (połówka szerokości/wysokości)
var playerCollisionRadius = self.width / 2; // Zakładamy, że kolizja gracza jest okrągła
var bossCollisionRadius = boss.width / 2; // Zakładamy, że kolizja bossa jest okrągła
// Jeśli odległość między środkami jest mniejsza niż suma promieni kolizji
if (distance_b < playerCollisionRadius + bossCollisionRadius) {
// Kolizja wykryta podczas turlania
boss.takeDamage(10); // ZADAJ 10 OBRAŻEŃ BOSSOWI
self.hasRolledThroughBossThisRoll = true; // Oznacz, że zadałeś obrażenia w tym turlaniu
// Opcjonalny efekt wizualny/dźwiękowy trafienia bossa turlaniem
LK.effects.flashObject(boss, 0x00FF00, 200); // Mignij bossa na zielono
// LK.getSound('playerHitBossSound').play(); // Wymagałoby dodania nowego assetu dźwiękowego
}
}
} else {
// --- Podstawowy ruch gracza (jeśli nie turla się) ---
// Ta część była nieobecna, dodajmy prosty ruch oparty na wejściu (jeśli LK go dostarcza)
// Zakładając, że mamy dostęp do stanu klawiszy/joysticka np. przez LK.controls
// To jest PRZYKŁAD - musisz dostosować do API LK
/*
var moveX = 0;
var moveY = 0;
if (LK.controls.left) moveX -= 1;
if (LK.controls.right) moveX += 1;
if (LK.controls.up) moveY -= 1;
if (LK.controls.down) moveY += 1;
if (moveX !== 0 || moveY !== 0) {
// Normalizuj wektor ruchu, jeśli poruszasz się po przekątnej
var moveMagnitude = Math.sqrt(moveX * moveX + moveY * moveY);
var normalizedX = moveX / moveMagnitude;
var normalizedY = moveY / moveMagnitude;
var nextX = self.x + normalizedX * self.speed; // Użyj self.speed
var nextY = self.y + normalizedY * self.speed;
// Ograniczenia areny (jak w turlaniu)
var halfWidth = self.width / 2;
var halfHeight = self.height / 2;
var minX = 100 + halfWidth;
var maxX = 2048 - 100 - halfWidth;
var minY = 100 + halfHeight;
var maxY = 2732 - 100 - halfHeight;
self.x = Math.max(minX, Math.min(nextX, maxX));
self.y = Math.max(minY, Math.min(nextY, maxY));
}
*/
// Jeśli nie masz łatwego dostępu do stanu klawiszy, gracz będzie się ruszał tylko podczas turlania.
}
};
return self;
});
// Shape class implementation for creating simple shapes
var Shape = Container.expand(function (options) {
var self = Container.call(this);
options = options || {};
var width = options.width || 100;
var height = options.height || 100;
var color = options.color || 0xFFFFFF;
var shape = options.shape || 'box';
// Create the shape as an asset
var asset = self.attachAsset(shape, {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: height,
tint: color
});
// Set width and height for easier access
self.width = width;
self.height = height;
// Add anchor property to Shape for positioning
self.anchor = {
set: function set(x, y) {
// This mimics the behavior of the anchor.set method
self.anchorX = x;
self.anchorY = y;
}
};
return self;
});
var SpriteAnimation = Container.expand(function (options) {
var self = Container.call(this);
// Initialize with default values
options = options || {};
self.frames = options.frames || [];
self.frameDuration = options.frameDuration || 100; // Default 100ms per frame
self.loop = options.loop !== undefined ? options.loop : true;
self.currentFrame = 0;
self.frameTimer = 0;
self.playing = true;
// Set initial position and anchor if provided
if (options.x !== undefined) {
self.x = options.x;
}
if (options.y !== undefined) {
self.y = options.y;
}
// Handle anchor options
if (options.anchorX !== undefined || options.anchorY !== undefined) {
var anchorX = options.anchorX !== undefined ? options.anchorX : 0;
var anchorY = options.anchorY !== undefined ? options.anchorY : 0;
// Apply anchor to all frames
self.frames.forEach(function (frame) {
frame.anchor.set(anchorX, anchorY);
});
}
// Add the first frame to display initially
if (self.frames.length > 0) {
self.removeChildren(); // zabezpieczenie
self.addChild(self.frames[self.currentFrame]);
}
// Animation update method
self.update = function () {
if (!self.playing || self.frames.length === 0) {
return;
}
self.frameTimer++;
if (self.frameTimer >= self.frameDuration / (1000 / 60)) {
self.frameTimer = 0;
if (self.children.length > 0) {
self.removeChild(self.children[0]); // Usuwamy tylko 1 aktualną klatkę
}
self.currentFrame++;
if (self.currentFrame >= self.frames.length) {
if (self.loop) {
self.currentFrame = 0;
} else {
self.currentFrame = self.frames.length - 1;
self.playing = false;
}
}
self.addChild(self.frames[self.currentFrame]); // Dodajemy nową klatkę
}
};
// Method to stop animation
self.stop = function () {
self.playing = false;
};
// Method to start/resume animation
self.play = function () {
self.playing = true;
};
// Method to set a specific frame
self.gotoFrame = function (frameIndex) {
if (frameIndex >= 0 && frameIndex < self.frames.length) {
self.removeChildren();
self.currentFrame = frameIndex;
self.addChild(self.frames[self.currentFrame]);
}
};
return self;
});
var UI = Container.expand(function () {
var self = Container.call(this);
// Create heart containers (wizualizacja zdrowia)
self.hearts = [];
self.heartContainer = new Container(); // Kontener na ikony serc
self.addChild(self.heartContainer);
// Title and messages (teksty na ekranach)
self.titleText = new Text2("ROLL SOULS", {
size: 150,
fill: 0xFFFFFF
});
self.titleText.anchor.set(0.5, 0.5);
self.addChild(self.titleText);
self.messageText = new Text2("", {
size: 60,
fill: 0xFFFFFF
});
self.messageText.anchor.set(0.5, 0.5);
self.addChild(self.messageText);
// Deaths counter (licznik śmierci)
self.deathsText = new Text2("Deaths: 0", {
size: 40,
fill: 0xFFFFFF
});
self.deathsText.anchor.set(1, 0); // Wyrównanie do prawego górnego rogu
self.deathsText.x = 2048 - 50; // Pozycja X od prawej krawędzi
self.deathsText.y = 50; // Pozycja Y od góry
self.addChild(self.deathsText);
// Tutorial text (teksty tutoriali)
self.tutorialText = new Text2("", {
size: 50,
fill: 0xFFFFFF
});
self.tutorialText.anchor.set(0.5, 0); // Wyrównanie do środka na górze
self.tutorialText.x = 2048 / 2; // Pozycja X na środku
self.tutorialText.y = 200; // Pozycja Y (może być różna w zależności od stanu)
self.addChild(self.tutorialText);
// --- Timer Text ---
self.timerText = new Text2("2:00", {
// Początkowe wyświetlanie czasu (2 minuty)
size: 60,
// Rozmiar czcionki
fill: 0xFFFFFF // Biały kolor
});
self.timerText.anchor.set(0, 0); // Punkt odniesienia w lewym górnym rogu
self.timerText.x = 50; // Pozycja X od lewej krawędzi
self.timerText.y = 50; // Pozycja Y od górnej krawędzi
self.timerText.alpha = 0; // Domyślnie ukryty
self.addChild(self.timerText);
// --- Boss Health Bar (wizualizacja zdrowia bossa) ---
self.bossHealthBarContainer = new Container(); // Kontener na pasek zdrowia bossa
self.bossHealthBarContainer.x = 2048 / 2; // Wyśrodkuj poziomo
self.bossHealthBarContainer.y = 150; // Pozycja Y (poniżej timera)
self.bossHealthBarContainer.alpha = 0; // Domyślnie ukryty
self.addChild(self.bossHealthBarContainer);
var barWidth = 800; // Szerokość paska zdrowia (musi być taka sama jak szerokość assetu bossHpbar)
var barHeight = 30; // Wysokość paska zdrowia (musi być taka sama jak wysokość assetu bossHpbar)
// Tło paska zdrowia bossa (szare) - nadal używamy Shape dla tła
self.bossHealthBarBg = new Shape({
width: barWidth,
height: barHeight,
color: 0x555555,
// Szary kolor tła
shape: 'box'
});
self.bossHealthBarBg.anchor.set(0.5, 0.5); // Ustaw punkt odniesienia na środek
self.bossHealthBarContainer.addChild(self.bossHealthBarBg);
// Właściwy pasek zdrowia bossa (czerwony) - UŻYWAMY TERAZ NOWEGO ASSETU bossHpbar
self.bossHealthBar = self.attachAsset('bossHpbar', {
// Użyj assetu 'bossHpbar'
anchorX: 0,
// Ustaw punkt odniesienia na lewą krawędź (0), środek pionowo (0.5)
anchorY: 0.5,
x: -barWidth / 2 // Przesuń w lewo o połowę szerokości tła, żeby lewa krawędź paska zdrowia była na środku kontenera (czyli na środku ekranu)
// Szerokość i wysokość są brane z assetu, nie ustawiamy ich tutaj jawnie (chyba że chcemy skalować)
});
self.bossHealthBarContainer.addChild(self.bossHealthBar);
// Aktualizuje wizualizację serc na UI
self.updateHearts = function (current, max) {
// Sprawdź poprawność danych wejściowych
current = Math.max(0, current || 0);
max = Math.max(1, max || 1); // Max musi być co najmniej 1
// Usuń istniejące serca tylko jeśli liczba się zmieniła lub nie ma serc
if (self.hearts.length !== max) {
while (self.hearts.length > 0) {
var oldHeart = self.hearts.pop();
if (oldHeart && oldHeart.destroy && !oldHeart.destroyed) {
oldHeart.destroy();
}
}
self.heartContainer.removeChildren(); // Usuń wizualne obiekty serc
// Stwórz nowe ikony serc
for (var i = 0; i < max; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 50,
// Pozycja w poziomie (rozstawione co 50 jednostek)
y: 0,
tint: i < current ? 0xFF0000 : 0x555555 // Czerwone jeśli zdrowie jest >=, szare jeśli <
});
self.hearts.push(heart);
self.heartContainer.addChild(heart);
}
// Wyśrodkuj kontener serc w poziomie i ustaw pozycję pionową
self.heartContainer.x = (2048 - max * 50) / 2 + 25; // Dodano +25 aby wycentrować lepiej (bo anchorX = 0.5)
self.heartContainer.y = 100; // Pozycja pionowa
} else {
// Zaktualizuj tylko kolory istniejących serc
for (var j = 0; j < self.hearts.length; j++) {
if (self.hearts[j]) {
self.hearts[j].tint = j < current ? 0xFF0000 : 0x555555;
}
}
}
};
// Aktualizuje wizualizację paska zdrowia bossa
self.updateBossHealth = function (current, max) {
// Sprawdź poprawność danych
current = Math.max(0, current || 0);
max = Math.max(1, max || 1); // Max musi być co najmniej 1
// Oblicz szerokość paska zdrowia na podstawie aktualnego i maksymalnego zdrowia
var barWidth = 800; // Całkowita szerokość paska (musi być taka sama jak szerokość assetu bossHpbar)
var currentWidth = current / max * barWidth;
// Upewnij się, że pasek zdrowia (obiekt sprite) istnieje przed aktualizacją
if (self.bossHealthBar) {
// Zmień szerokość sprite'a, aby odzwierciedlić aktualne zdrowie
self.bossHealthBar.width = currentWidth;
// Ustaw widoczność kontenera paska zdrowia bossa
// Pokaż jeśli (stan gry lub game over) ORAZ (boss istnieje i żyje LUB jesteśmy w stanie game over z powodu czasu w Boss+)
var shouldBeVisible = (gameState.currentState === "game" || gameState.currentState === "gameOver") && (boss && !boss.dead && current > 0 || gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode);
self.bossHealthBarContainer.alpha = shouldBeVisible ? 1 : 0;
}
// Upewnij się, że pasek zdrowia tła też jest widoczny/ukryty tak samo jak kontener
if (self.bossHealthBarBg) {
self.bossHealthBarBg.alpha = self.bossHealthBarContainer.alpha;
}
};
// Wyświetla komunikat na środku ekranu
self.showMessage = function (message, duration) {
self.messageText.setText(message);
self.messageText.alpha = 1; // Ustaw pełną przezroczystość
// Wyczyść istniejący timer zanikania, jeśli istnieje
if (self.messageTimeout) {
LK.clearTimeout(self.messageTimeout);
self.messageTimeout = null;
}
// Zaplanuj zanikanie po czasie, jeśli duration > 0
if (duration && duration > 0) {
self.messageTimeout = LK.setTimeout(function () {
tween(self.messageText, {
alpha: 0 // Zaniknij do przezroczystości 0
}, {
duration: 500,
// Czas trwania zanikania
onFinish: function onFinish() {
self.messageTimeout = null;
} // Reset ID
});
}, duration);
} else {
// If duration is 0 or not provided, message stays visible
self.messageText.alpha = 1;
}
};
// Wyświetla tekst tutorialu
self.showTutorial = function (text) {
self.tutorialText.setText(text);
self.tutorialText.alpha = 1; // Ustaw pełną przezroczystość
};
// Ukrywa tekst tutorialu (zanikając)
self.hideTutorial = function () {
tween(self.tutorialText, {
alpha: 0 // Zaniknij
}, {
duration: 500 // Czas trwania
});
};
// Aktualizuje licznik śmierci
self.updateDeathsCounter = function () {
// Upewnij się, że storage.totalDeaths jest liczbą
var deaths = storage.totalDeaths || 0;
self.deathsText.setText("Deaths: " + deaths);
};
// Aktualizuje wyświetlanie czasu timera
self.updateTimerDisplay = function (seconds) {
// Upewnij się, że sekundy są liczbą >= 0
seconds = Math.max(0, seconds || 0);
var minutes = Math.floor(seconds / 60);
var remainingSeconds = seconds % 60;
// Formatuj sekundy z wiodącym zerem jeśli < 10
var formattedSeconds = remainingSeconds < 10 ? '0' + remainingSeconds : remainingSeconds;
self.timerText.setText(minutes + ':' + formattedSeconds);
};
// Pozycjonuje elementy UI w zależności od stanu gry
// Ta funkcja jest wywoływana przez metody w gameState
self.positionElements = function (state) {
// Pozycje stałe (niezależne od stanu)
self.deathsText.x = 2048 - 50;
self.deathsText.y = 50;
self.timerText.x = 50;
self.timerText.y = 50;
self.heartContainer.y = 100; // Pozycja pionowa serc
self.bossHealthBarContainer.x = 2048 / 2;
self.bossHealthBarContainer.y = 150; // Pasek HP bossa poniżej timera/serc
// Resetuj widoczność przed ustawieniem dla danego stanu
self.titleText.alpha = 0;
self.messageText.alpha = 0;
self.tutorialText.alpha = 0;
self.timerText.alpha = 0;
self.heartContainer.alpha = 0;
self.bossHealthBarContainer.alpha = 0; // Zostanie włączony przez updateBossHealth jeśli trzeba
self.deathsText.alpha = 1; // Licznik śmierci zawsze widoczny
switch (state) {
case "title":
// Pozycje dla ekranu tytułowego
self.titleText.x = 2048 / 2;
self.titleText.y = 800;
self.titleText.alpha = 1; // Pokaż tytuł
self.messageText.x = 2048 / 2;
self.messageText.y = 1000; // "Tap to Start" (ustawiane przez showMessage)
// self.messageText.alpha = 1; // Ustawiane przez showMessage
self.tutorialText.x = 2048 / 2;
self.tutorialText.y = 1200; // "Swipe to Roll..." (ustawiane przez showTutorial)
// self.tutorialText.alpha = 1; // Ustawiane przez showTutorial
break;
case "game":
// Pozycje dla stanu gry (walka z bossem)
// self.titleText.alpha = 0; // Ustawione domyślnie
self.messageText.x = 2048 / 2;
self.messageText.y = 1500; // Komunikaty w trakcie gry (np. przyspieszenie bossa)
// self.messageText.alpha = 0; // Ustawiane przez showMessage
self.tutorialText.x = 2048 / 2;
self.tutorialText.y = 200; // Tutorial w trakcie gry ("Swipe to roll away!")
// self.tutorialText.alpha = 1; // Ustawiane przez showTutorial/hideTutorial
// Pokaż serca i timer podczas gry
self.heartContainer.alpha = 1;
self.timerText.alpha = 1;
// Pasek zdrowia bossa jest zarządzany przez updateBossHealth
break;
case "grillMenu":
// Nowy stan dla Grill Screena
// self.titleText.alpha = 0; // Ustawione domyślnie
self.messageText.x = 2048 / 2;
self.messageText.y = 500; // Pozycja dla komunikatów "Rest in peace..."
// self.messageText.alpha = 0; // Ustawiane przez showMessage
// self.tutorialText.alpha = 0; // Ustawione domyślnie
break;
case "gameOver":
// Pozycje dla ekranu Game Over
self.titleText.x = 2048 / 2;
self.titleText.y = 800; // Tytuł "YOU DIED" lub komunikat zwycięstwa Boss+
// self.titleText.alpha = 1; // Ustawiane w gameState.gameOver
self.messageText.x = 2048 / 2;
self.messageText.y = 1000; // Pusty lub inny komunikat
// self.messageText.alpha = 0; // Ustawiane w gameState.gameOver przez showMessage
self.tutorialText.x = 2048 / 2;
self.tutorialText.y = 1200; // Pusty
// self.tutorialText.alpha = 0; // Ustawiane w gameState.gameOver przez showTutorial
// Widoczność paska HP bossa zarządzana przez updateBossHealth
break;
case "intro": // Pozycje dla ekranów intro/tutoriali
case "fakeTutorial":
case "realTutorial":
// Większość elementów ukryta (ustawione domyślnie wyżej)
break;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111 // Ciemne tło domyślne
});
/****
* Game Code
****/
// Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia)
// Komentarze o assetach pominięte dla zwięzłości
var currentSceneElements = new Container();
// Zmienne gry
var player;
var boss;
var ui;
var walls = []; // Ściany areny bossa
// Zmienna do przechowywania aktywnego tła sceny
var currentBackground = null;
// Flaga do śledzenia, czy jesteśmy w trybie New Boss+
var isNewBossPlusMode = false;
// Flaga do śledzenia przyczyny Game Over (true = gracz zginął, false = czas minął)
var gameOverReasonIsDeath = false;
// Funkcja do czyszczenia elementów z kontenera currentSceneElements (nie usuwa UI ani tła)
function clearScene() {
while (currentSceneElements.children.length > 0) {
var child = currentSceneElements.children[0];
if (child && child.destroy) {
child.destroy(); // Użyj destroy jeśli dostępne
} else if (child && child.parent) {
child.parent.removeChild(child); // Fallback
} else {
// Jeśli obiekt nie ma ani destroy ani parent, usuń go z tablicy (choć to nie powinno się zdarzyć)
currentSceneElements.children.shift();
}
}
// Usuń też dzieci bezpośrednio z 'game', które mogły zostać dodane poza 'currentSceneElements' w scenach,
// ale UWAŻAJ, aby nie usunąć stałych elementów jak UI, walls, currentSceneElements sam w sobie.
// Lepsze podejście: zawsze dodawaj elementy specyficzne dla sceny do currentSceneElements.
}
// Obiekt zarządzający stanami gry
var gameState = {
currentState: "title",
// Początkowy stan gry
gameDuration: 120,
// Domyślny czas walki z bossem w sekundach (2 minuty)
remainingTime: 0,
// Pozostały czas
gameTimerInterval: null,
// ID interwału timera
fakeTutorialTimerId: null,
// ID timera przechodzącego do prawdziwego tutorialu
bossSpeedIncreased: false,
// Flaga przyspieszenia bossa
touchStart: {
x: 0,
y: 0
},
// Początek gestu
touchEnd: {
x: 0,
y: 0
},
// Koniec gestu
// Inicjalizacja gry (wywoływana raz na początku)
init: function init() {
// Resetuj stan przy każdym uruchomieniu
storage.totalDeaths = 0;
storage.maxHearts = 5; // Zaczynaj zawsze z 5 sercami
isNewBossPlusMode = false;
gameOverReasonIsDeath = false;
game.setBackgroundColor(0x111111); // Ustaw domyślny kolor tła
this.createWalls(); // Stwórz ściany areny
ui = game.addChild(new UI()); // Stwórz UI
// Zainicjalizuj UI dla ekranu tytułowego
ui.updateDeathsCounter();
ui.updateHearts(storage.maxHearts, storage.maxHearts); // Pokaż początkowe serca
ui.positionElements("title"); // Pozycjonuj elementy UI dla tytułu
game.addChild(currentSceneElements); // Dodaj kontener na elementy scen
// Rozpocznij muzykę w tle
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
this.showTitleScreen(); // Rozpocznij od ekranu tytułowego
},
// Tworzy/odświeża ściany areny bossa
createWalls: function createWalls() {
walls.forEach(function (wall) {
if (wall && wall.destroy) {
wall.destroy();
}
});
walls = [];
// Użyj getAsset zamiast new Shape dla ścian, jeśli 'wall' i 'floor' to assety
var leftWall = game.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
walls.push(leftWall);
var rightWall = game.addChild(LK.getAsset('wall', {
anchorX: 1,
anchorY: 0,
x: 2048,
y: 0
})); // AnchorX=1 dla prawej ściany
rightWall.x = 2048; // Poprawka pozycji prawej ściany
walls.push(rightWall);
var topWall = game.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
}));
walls.push(topWall);
var bottomWall = game.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 1,
x: 0,
y: 2732
})); // AnchorY=1 dla dolnej ściany
bottomWall.y = 2732; // Poprawka pozycji dolnej ściany
walls.push(bottomWall);
// Ustaw ściany na spodzie (niski zIndex)
walls.forEach(function (wall) {
game.setChildIndex(wall, 0);
});
},
// ---------- Metody przejścia między stanami ----------
showTitleScreen: function showTitleScreen() {
// Zatrzymaj timery poprzedniego stanu
if (this.fakeTutorialTimerId) {
LK.clearTimeout(this.fakeTutorialTimerId);
}
this.fakeTutorialTimerId = null;
if (this.gameTimerInterval) {
LK.clearInterval(this.gameTimerInterval);
}
this.gameTimerInterval = null;
clearScene(); // Wyczyść elementy poprzedniej sceny (Grill, GameOver, itp.)
// Usuń stare tło, jeśli istnieje
if (currentBackground) {
tween.stop(currentBackground); // Zatrzymaj animacje tła
currentBackground.destroy();
currentBackground = null;
}
this.currentState = "title";
game.setBackgroundColor(0x1a1a1a); // Ciemne tło bazowe dla tytułu
// Dodaj tło ekranu tytułowego
currentBackground = LK.getAsset('titleBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChildAt(currentBackground, 0); // Dodaj na spód
// Ukryj gracza/bossa jeśli istnieją
if (player && player.destroy) {
player.destroy();
}
player = null;
if (boss && boss.destroy) {
boss.destroy();
}
boss = null;
// Pokaż ściany (jako część tła menu)
walls.forEach(function (wall) {
if (wall) {
wall.alpha = 1;
}
});
// Ustaw UI dla tytułu
ui.positionElements("title");
ui.titleText.setText("Welcome Unchosen");
ui.showMessage("Tap to Start", 0);
ui.showTutorial("Swipe to Roll - Death is Progress");
// Resetuj stan gestu
this.touchStart = {
x: 0,
y: 0
};
this.touchEnd = {
x: 0,
y: 0
};
// Przycisk testowy do Grill Menu (jeśli nadal potrzebny)
var grillMenuTestButton = new Container();
grillMenuTestButton.interactive = true;
grillMenuTestButton.x = 2048 / 2;
grillMenuTestButton.y = 1600;
currentSceneElements.addChild(grillMenuTestButton);
var grillButtonBg = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
grillMenuTestButton.addChild(grillButtonBg);
var grillButtonText = new Text2('Go to Grill Menu', {
size: 50,
fill: 0xFFFFFF
});
grillButtonText.anchor.set(0.5, 0.5);
grillMenuTestButton.addChild(grillButtonText);
grillMenuTestButton.down = function () {
gameState.showGrillScreen();
};
// --- Tap anywhere on title screen to start the intro ---
game.on('down', function () {
if (gameState.currentState === 'title') {
gameState.showIntro();
}
});
},
showIntro: function showIntro() {
if (this.fakeTutorialTimerId) {
LK.clearTimeout(this.fakeTutorialTimerId);
}
this.fakeTutorialTimerId = null; // Wyczyść timer
clearScene(); // Wyczyść elementy tytułu
if (currentBackground) {
tween.stop(currentBackground);
currentBackground.destroy();
currentBackground = null;
} // Usuń stare tło
this.currentState = "intro";
game.setBackgroundColor(0x111111);
// Dodaj tło intro i animację zoom
currentBackground = LK.getAsset('introBg', {
anchorX: 0.5,
anchorY: 0.4,
x: 1000,
y: 1000,
scaleX: 1,
scaleY: 1
});
game.addChildAt(currentBackground, 0);
tween(currentBackground, {
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 22000,
easing: tween.linear,
repeat: Infinity,
yoyo: true
});
// Ukryj ściany na czas intro
if (player && player.destroy) {
player.destroy();
}
player = null; // Upewnij się, że nie ma gracza/bossa
if (boss && boss.destroy) {
boss.destroy();
}
boss = null;
walls.forEach(function (wall) {
if (wall) {
wall.alpha = 0;
}
}); // Ukryj ściany
ui.positionElements("intro"); // Ustaw UI dla intro (głównie ukrywa elementy)
// --- Teksty intro z timerami ---
// Funkcja do wyświetlania tekstu z fade-in, fade-out i kontynuacją
function showIntroText(text, delay, onComplete) {
var introText = new Text2(text, {
size: 90,
fill: 0xFFFFFF,
align: 'center',
wordWrap: true,
wordWrapWidth: 1800
});
introText.anchor.set(0.5, 0.5);
introText.x = 2048 / 2;
introText.y = 2232 / 2 - 400;
introText.alpha = 0;
currentSceneElements.addChild(introText);
LK.setTimeout(function () {
tween(introText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeInOut
});
LK.setTimeout(function () {
tween(introText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut
});
LK.setTimeout(function () {
try {
if (introText && introText.destroy) {
introText.destroy();
}
} catch (e) {}
if (onComplete) {
onComplete();
}
}, 1000); // czas na fade-out
}, 3000); // czas wyświetlania
}, delay);
}
// --- Startujemy sekwencję intro ---
showIntroText('From the creators of FromSoftware...', 2000, function () {
showIntroText('...I have no idea. I don’t know them.', 0, function () {
showIntroText('Silas GameStudio...', 0, function () {
showIntroText('Still looking for funding.', 0, function () {
showIntroText('Oh... and I don’t even exist.', 0, function () {
// <-- NOWY TEKST
// Po ostatnim tekście przechodzimy do howToScene
LK.setTimeout(function () {
if (typeof gameState.showFakeTutorial === 'function') {
gameState.showFakeTutorial();
} else {
console.error("Error: gameState.showFakeTutorial is not defined");
gameState.showTitleScreen(); // Fallback to title screen
}
}, 1000);
});
});
});
});
});
},
showFakeTutorial: function showFakeTutorial() {
clearScene(); // Wyczyść tekst intro
if (this.fakeTutorialTimerId) {
LK.clearTimeout(this.fakeTutorialTimerId);
}
this.fakeTutorialTimerId = null; // Wyczyść stary timer
this.currentState = "fakeTutorial";
// Zachowaj tło intro? Jeśli tak, usuń game.setBackgroundColor i nie niszcz currentBackground
// game.setBackgroundColor(0x000000); // Czarne tło - usunięte, aby zachować tło intro
ui.positionElements("fakeTutorial"); // Ustaw UI
// Tekst fałszywego tutorialu
var instructionText = new Text2('Press LPM to block.. or don’t press.', {
size: 100,
fill: 0xFFFFFF,
align: 'center',
wordWrap: true,
wordWrapWidth: 1800,
dropShadow: true,
dropShadowColor: 0x000000,
// czarny cień
dropShadowDistance: 3,
// odległość cienia
dropShadowBlur: 4,
// lekkie rozmycie cienia
dropShadowAngle: Math.PI / 4 // kąt cienia
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 2048 / 2;
instructionText.y = 2732 / 2;
currentSceneElements.addChild(instructionText);
// Timer przechodzący do prawdziwego tutorialu
this.fakeTutorialTimerId = LK.setTimeout(function () {
try {
if (instructionText && instructionText.destroy) {
instructionText.destroy();
}
} catch (e) {}
gameState.fakeTutorialTimerId = null; // Zresetuj ID
// *** POPRAWKA: Sprawdzenie przed wywołaniem ***
if (gameState && typeof gameState.showRealTutorial === 'function') {
gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu
} else {
console.error("Error: gameState.showRealTutorial is not a function in fakeTutorial timeout!");
gameState.showTitleScreen(); // Awaryjny powrót do tytułu
}
} /*.bind(this)*/, 6000); // 6 sekund
},
// *** NOWA FUNKCJA: Prawdziwy Tutorial (oddzielona od startGame) ***
showRealTutorial: function showRealTutorial() {
if (this.fakeTutorialTimerId) {
LK.clearTimeout(this.fakeTutorialTimerId);
}
this.fakeTutorialTimerId = null; // Wyczyść timer fake tutorialu
clearScene(); // Wyczyść elementy fake tutorialu/fake game over
// Zatrzymaj animację tła intro i usuń je
if (currentBackground) {
tween.stop(currentBackground);
currentBackground.destroy();
currentBackground = null;
}
this.currentState = "realTutorial";
game.setBackgroundColor(0x111111); // Ciemne tło
// Pokaż ściany areny
walls.forEach(function (wall) {
if (wall) {
wall.alpha = 1;
}
});
ui.positionElements("realTutorial"); // Ustaw UI (głównie ukrywa elementy gry)
// Tekst prawdziwego tutorialu
var tutorialTitle = new Text2('HOW TO PLAY', {
size: 100,
fill: 0xFFFFFF
});
tutorialTitle.anchor.set(0.5, 0.5);
tutorialTitle.x = 2048 / 2;
tutorialTitle.y = 600;
currentSceneElements.addChild(tutorialTitle);
var tutorialDesc = new Text2('Swipe the screen to ROLL.\nRoll THROUGH the boss attacks to deal damage.\nSurvive!', {
size: 60,
fill: 0xFFFFFF,
align: 'center',
wordWrap: true,
wordWrapWidth: 1600
});
tutorialDesc.anchor.set(0.5, 0.5);
tutorialDesc.x = 2048 / 2;
tutorialDesc.y = 1000;
currentSceneElements.addChild(tutorialDesc);
// Przycisk "Let's Roll!" do rozpoczęcia gry
var startButton = new Container();
startButton.interactive = true;
startButton.x = 2048 / 2;
startButton.y = 1500;
currentSceneElements.addChild(startButton);
var startButtonBg = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
startButton.addChild(startButtonBg);
var startButtonText = new Text2("Let's Roll!", {
size: 50,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
startButton.addChild(startButtonText);
startButton.down = function () {
// clearScene(); // Niekonieczne, startGame to zrobi
gameState.startGame(); // Rozpocznij grę
};
},
// Obsługa inputu w stanie fakeTutorial (fałszywa śmierć)
handleFakeTutorialInput: function handleFakeTutorialInput() {
// Wyczyść timer, który miał prowadzić do prawdziwego tutorialu
if (this.fakeTutorialTimerId) {
LK.clearTimeout(this.fakeTutorialTimerId);
this.fakeTutorialTimerId = null; // Zresetuj ID timera
}
clearScene(); // Wyczyść ekran fałszywego tutorialu
// Ustaw stan tymczasowo (można by dodać dedykowany stan 'fakeGameOver')
this.currentState = "fakeGameOver"; // Zmieniono z "gameOver"
// Zatrzymaj animację tła intro, ale nie usuwaj go jeszcze
if (currentBackground) {
tween.stop(currentBackground);
}
ui.positionElements("gameOver"); // Użyj pozycjonowania gameOver dla tekstów "YOU DIED"
// Wyświetl "YOU DIED" na czerwono
var diedText = new Text2("YOU DIED", {
size: 150,
fill: 0xFF0000
}); // Czerwony kolor
diedText.anchor.set(0.5, 0.5);
diedText.x = 2048 / 2;
diedText.y = 600;
currentSceneElements.addChild(diedText);
// Wyświetl wyjaśnienie
var explanationText = new Text2("Did you check the title of the game?", {
size: 70,
fill: 0xFFFFFF,
align: 'center',
wordWrap: true,
wordWrapWidth: 1800,
dropShadow: true,
dropShadowColor: 0x000000,
// cień czarny
dropShadowDistance: 3,
// odległość cienia
dropShadowBlur: 4,
// lekkie rozmycie cienia
dropShadowAngle: Math.PI / 4 // kąt padania cienia (45 stopni)
});
explanationText.anchor.set(0.5, 0.5);
explanationText.x = 2048 / 2;
explanationText.y = 800;
currentSceneElements.addChild(explanationText);
// Dodaj przycisk "How to play again"
var howToPlayButtonContainer = new Container();
howToPlayButtonContainer.interactive = true;
howToPlayButtonContainer.x = 2048 / 2;
howToPlayButtonContainer.y = 1300; // Pozycja przycisku
currentSceneElements.addChild(howToPlayButtonContainer);
var buttonBg = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
howToPlayButtonContainer.addChild(buttonBg);
var buttonText = new Text2('How to play', {
size: 50,
fill: 0xFFFFFF
}); // Zmieniono tekst i rozmiar
buttonText.anchor.set(0.5, 0.5);
howToPlayButtonContainer.addChild(buttonText);
// Akcja przycisku: Przejdź do prawdziwego tutorialu
howToPlayButtonContainer.down = function () {
// *** POPRAWKA: Sprawdzenie przed wywołaniem ***
if (gameState && typeof gameState.showRealTutorial === 'function') {
gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu
} else {
console.error("Error: gameState.showRealTutorial is not a function in fake input handler!");
gameState.showTitleScreen(); // Awaryjny powrót do tytułu
}
};
},
// Przejście do stanu gry (walka z bossem) - wywoływane z Prawdziwego Tutorialu
startGame: function startGame() {
// Wyczyść timery z poprzednich stanów
if (this.fakeTutorialTimerId) {
LK.clearTimeout(this.fakeTutorialTimerId);
}
this.fakeTutorialTimerId = null;
if (this.gameTimerInterval) {
LK.clearInterval(this.gameTimerInterval);
}
this.gameTimerInterval = null;
clearScene(); // Wyczyść elementy tutorialu
// Usuń tło tutorialu/intro jeśli istnieje
if (currentBackground) {
tween.stop(currentBackground);
currentBackground.destroy();
currentBackground = null;
}
this.currentState = "game"; // Ustaw stan na "game"
game.setBackgroundColor(0x111111);
// Stwórz gracza (jeśli nie istnieje)
if (player && player.destroy) {
player.destroy();
} // Zniszcz starego gracza
player = game.addChild(new Player()); // Stwórz nowego gracza
player.health = storage.maxHearts || 5; // Ustaw zdrowie gracza
// Resetuj stan gracza
player.rolling = false;
player.invulnerable = false;
player.invulnerabilityFrames = 0;
player.rollCooldown = 0;
if (player && typeof player.clearRollTimeouts === 'function') {
player.clearRollTimeouts();
}
player.hasRolledThroughBossThisRoll = false;
player.x = 2048 / 2;
player.y = 2732 / 2 + 400; // Pozycja startowa gracza
player.alpha = 1; // Upewnij się, że jest widoczny
// Stwórz bossa (jeśli nie istnieje)
if (boss && boss.destroy) {
boss.destroy();
} // Zniszcz starego bossa
boss = game.addChild(new Boss()); // Stwórz nowego bossa
boss.x = 2048 / 2;
boss.y = 2732 / 2 - 400; // Pozycja startowa bossa
boss.alpha = 1; // Upewnij się, że jest widoczny
// Resetuj stan bossa
boss.attackCooldown = 90; // Daj graczowi chwilę przed pierwszym atakiem (ok 1.5s)
boss.attacks = [];
boss.attackSpeedMultiplier = 1;
boss.repositioning = false;
boss.dead = false; // Upewnij się, że nie jest martwy
boss.phase = 1; // Zacznij od fazy 1
boss.tint = 0xFFFFFF; // Resetuj kolor (na wypadek przejścia fazy w poprzedniej grze)
boss.scale.set(1, 1); // Resetuj skalę (na wypadek animacji śmierci)
// Ustaw parametry bossa i gry zależnie od trybu
if (isNewBossPlusMode) {
boss.maxHealth = 2000;
boss.health = boss.maxHealth;
this.gameDuration = 600; // 10 minut
boss.attackSpeedMultiplier = 0.6; // Szybsze ataki od początku
boss.speed = 7; // Szybszy ruch
console.log("Starting NEW BOSS+ mode. HP:", boss.maxHealth, "Time:", this.gameDuration);
} else {
boss.maxHealth = 200; // Standardowy boss HP
boss.health = boss.maxHealth;
this.gameDuration = 120; // 2 minuty
boss.speed = 5; // Standardowa prędkość
boss.attackSpeedMultiplier = 1; // Standardowa prędkość ataków
console.log("Starting STANDARD mode. HP:", boss.maxHealth, "Time:", this.gameDuration);
}
// Pokaż ściany areny
walls.forEach(function (wall) {
if (wall) {
wall.alpha = 1;
}
});
// Ustaw UI dla stanu gry
ui.positionElements("game");
ui.updateHearts(player.health, storage.maxHearts);
ui.showTutorial("Swipe to Roll!"); // Wyświetl krótki tutorial
ui.updateBossHealth(boss.health, boss.maxHealth); // Pokaż pasek HP bossa
// --- Timer walki z bossem ---
this.remainingTime = this.gameDuration; // Ustaw początkowy czas
ui.updateTimerDisplay(this.remainingTime); // Wyświetl czas
// Rozpocznij interwał timera
if (this.gameTimerInterval) {
LK.clearInterval(this.gameTimerInterval);
} // Wyczyść stary timer
this.bossSpeedIncreased = false; // Resetuj flagę przyspieszenia bossa
this.gameTimerInterval = LK.setInterval(function () {
// Aktualizuj tylko w stanie gry
if (gameState.currentState === "game") {
gameState.remainingTime--;
ui.updateTimerDisplay(gameState.remainingTime);
// --- Przyspieszenie bossa - TYLKO W STANDARDOWYM TRYBIE ---
if (!isNewBossPlusMode) {
var accelerationThreshold = gameState.gameDuration - 60; // Przyspieszenie na 60s przed końcem
if (gameState.remainingTime <= accelerationThreshold && !gameState.bossSpeedIncreased) {
gameState.bossSpeedIncreased = true;
if (boss && !boss.dead) {
// Sprawdź czy boss nadal żyje
boss.attackSpeedMultiplier *= 0.7; // Przyspiesz ataki
boss.speed += 1; // Lekko przyspiesz ruch
ui.showMessage("Boss attacks faster!", 2000); // Komunikat
}
}
}
// --- Koniec Przyspieszenia bossa ---
// Sprawdź koniec gry (czas minął)
if (gameState.remainingTime <= 0) {
LK.clearInterval(gameState.gameTimerInterval); // Zatrzymaj timer
gameState.gameTimerInterval = null;
if (isNewBossPlusMode) {
// Czas minął w Boss+ -> Zwycięstwo przez przetrwanie
gameOverReasonIsDeath = false; // Przyczyna: czas
gameState.gameOver(false); // Przejdź do ekranu końca gry (specjalny komunikat)
} else {
// Czas minął w standardowym trybie -> Przejdź do Grill Menu
// Zakładamy, że przetrwanie 2 minut w standardowym trybie to też "zwycięstwo"
gameState.showGrillScreen();
}
}
} else {
// Jeśli stan gry się zmienił, zatrzymaj timer
if (gameState.gameTimerInterval) {
LK.clearInterval(gameState.gameTimerInterval);
}
gameState.gameTimerInterval = null;
}
} /*.bind(this)*/, 1000); // Interwał co 1 sekundę
// Ukryj tutorial po chwili
LK.setTimeout(function () {
if (gameState.currentState === "game") {
// Sprawdź czy nadal jesteśmy w grze
ui.hideTutorial();
}
// Rozpoczęcie ataków bossa jest teraz obsługiwane przez Boss.update i jego cooldown
}, 3000); // 3 sekundy opóźnienia
},
// victory() - nieużywane, logika w timerze i boss.die()
showGrillScreen: function showGrillScreen() {
// Wyczyść timery
if (this.gameTimerInterval) {
LK.clearInterval(this.gameTimerInterval);
}
this.gameTimerInterval = null;
if (this.fakeTutorialTimerId) {
LK.clearTimeout(this.fakeTutorialTimerId);
}
this.fakeTutorialTimerId = null;
this.bossSpeedIncreased = false; // Reset flagi
clearScene(); // Wyczyść elementy gry (ataki, itp.)
// Zniszcz gracza i bossa jeśli jeszcze istnieją
if (player && player.destroy) {
player.destroy();
}
player = null;
if (boss && boss.destroy) {
boss.destroy();
}
boss = null; // Boss powinien być już zniszczony przez die(), ale na wszelki wypadek
this.currentState = "grillMenu";
game.setBackgroundColor(0x333333); // Tło grilla
// Usuń tło gry
if (currentBackground) {
tween.stop(currentBackground);
currentBackground.destroy();
currentBackground = null;
}
// Dodaj tło dla ekranu Grilla
currentBackground = LK.getAsset('grillMenu', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChildAt(currentBackground, 0); // Na spód
// Ukryj ściany
walls.forEach(function (wall) {
if (wall) {
wall.alpha = 0;
}
});
// Ustaw UI dla Grill Menu
ui.positionElements("grillMenu");
ui.updateBossHealth(0, 1); // Ukryj pasek HP bossa
ui.updateHearts(0, storage.maxHearts); // Ukryj serca gracza
// --- Przyciski na ekranie Grilla ---
var buttonYStart = 1000;
var buttonYOffset = 150;
// Przycisk "Rest"
var restButton = new Container();
restButton.interactive = true;
restButton.x = 2048 / 2;
restButton.y = buttonYStart;
currentSceneElements.addChild(restButton);
var restButtonBg = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
restButton.addChild(restButtonBg);
var restButtonText = new Text2('Rest', {
size: 50,
fill: 0xFFFFFF
});
restButtonText.anchor.set(0.5, 0.5);
restButton.addChild(restButtonText);
restButton.down = function () {
ui.showMessage("Rest in peace...", 2000);
LK.setTimeout(function () {
ui.showMessage("Thank you for playing!", 3000);
}, 2500);
LK.setTimeout(function () {
// clearScene(); // Niekonieczne, showTitleScreen to zrobi
gameState.showTitleScreen(); // Wróć do tytułu
}, 6000); // Opóźnienie
};
// Przycisk "Upgrade Roll" (obecnie bez funkcji)
var upgradeButton = new Container();
upgradeButton.interactive = true;
upgradeButton.x = 2048 / 2;
upgradeButton.y = buttonYStart + buttonYOffset;
currentSceneElements.addChild(upgradeButton);
var upgradeButtonBg = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
upgradeButton.addChild(upgradeButtonBg);
var upgradeButtonText = new Text2('Upgrade Roll', {
size: 50,
fill: 0xFFFFFF
});
upgradeButtonText.anchor.set(0.5, 0.5);
upgradeButton.addChild(upgradeButtonText);
upgradeButton.down = function () {
ui.showMessage("Not implemented yet!", 2000);
}; // Zmieniono komunikat
// Przycisk "New Boss+"
var newBossButton = new Container();
newBossButton.interactive = true;
newBossButton.x = 2048 / 2;
newBossButton.y = buttonYStart + buttonYOffset * 2;
currentSceneElements.addChild(newBossButton);
var newBossButtonBg = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
newBossButton.addChild(newBossButtonBg);
var newBossButtonText = new Text2('New Boss+', {
size: 50,
fill: 0xFFFFFF
});
newBossButtonText.anchor.set(0.5, 0.5);
newBossButton.addChild(newBossButtonText);
newBossButton.down = function () {
// clearScene(); // Niekonieczne, startGame to zrobi
isNewBossPlusMode = true; // Ustaw flagę
gameState.startGame(); // Rozpocznij grę w trybie Boss+
};
},
// Przejście do stanu Game Over
// isDeath: true (gracz zginął), false (czas minął w Boss+)
gameOver: function gameOver(isDeath) {
this.currentState = "gameOver"; // Ustaw stan
gameOverReasonIsDeath = isDeath; // Zapisz przyczynę
// Zatrzymaj timer gry
if (this.gameTimerInterval) {
LK.clearInterval(this.gameTimerInterval);
}
this.gameTimerInterval = null;
this.bossSpeedIncreased = false; // Reset flagi przyspieszenia
// Zatrzymaj animację tła (jeśli istnieje, ale nie powinno w tym stanie)
if (currentBackground) {
tween.stop(currentBackground);
}
game.setBackgroundColor(0x000000); // Czarne tło
// Ukryj/zniszcz gracza i bossa (powinny być już zniszczone przez swoje metody die)
if (player && player.alpha !== 0) {
player.alpha = 0;
} // Ukryj, jeśli nie zniknął
if (boss && boss.alpha !== 0) {
boss.alpha = 0;
}
// Ukryj ściany
walls.forEach(function (wall) {
if (wall) {
wall.alpha = 0;
}
});
// Ustaw UI dla ekranu game over
ui.positionElements("gameOver");
// Logika komunikatu Game Over
var gameOverMessage = "YOU DIED"; // Domyślny komunikat
if (!isDeath) {
// Jeśli czas minął (tylko w Boss+)
gameOverMessage = "YOU SURVIVED... for now."; // Zmieniono komunikat zwycięstwa Boss+
}
// Wyświetl komunikat i tytuł
ui.titleText.setText(gameOverMessage);
ui.titleText.alpha = 1; // Pokaż tytuł
ui.showMessage("", 0); // Wyczyść ewentualny poprzedni komunikat
ui.showTutorial(""); // Ukryj tutorial
// Pasek zdrowia bossa jest zarządzany przez updateBossHealth w pętli gry
// W tym stanie updateBossHealth pokaże pasek tylko jeśli !isDeath && isNewBossPlusMode
// Zaplanuj restart gry po opóźnieniu
LK.setTimeout(function () {
// Wyczyść scenę przed restartem
clearScene();
// Zniszcz gracza/bossa jeśli jakimś cudem przetrwali
if (player && player.destroy) {
player.destroy();
}
player = null;
if (boss && boss.destroy) {
boss.destroy();
}
boss = null;
// Wyczyść ataki bossa (na wszelki wypadek) - teraz robione w Boss.die / Boss.update
// if (boss && boss.attacks) boss.attacks = [];
// Usuń tło game over (czarne)
if (currentBackground) {
currentBackground.destroy();
}
currentBackground = null;
// Resetuj UI do stanu "przed grą" (np. jak w tytule, ale bez tekstów)
// Pozycjonowanie jest ok, ale trzeba wyczyścić teksty
ui.titleText.setText("");
ui.titleText.alpha = 0;
ui.messageText.setText("");
ui.messageText.alpha = 0;
ui.tutorialText.setText("");
ui.tutorialText.alpha = 0;
ui.updateDeathsCounter(); // Zaktualizuj licznik śmierci
ui.updateBossHealth(0, 1); // Ukryj pasek HP
ui.updateHearts(0, storage.maxHearts); // Ukryj serca
// Flaga isNewBossPlusMode pozostaje niezmieniona (jeśli zginąłeś w Boss+, restartujesz w Boss+)
// gameOverReasonIsDeath zostanie zresetowana przy następnym wywołaniu gameOver
// Restartuj walkę (startGame użyje flagi isNewBossPlusMode)
gameState.startGame();
}, 4000); // Zwiększono opóźnienie do 4 sekund przed restartem
},
// Obsługa gestów dotykowych/myszy
processTouchGesture: function processTouchGesture() {
// --- Obsługa inputu w stanie fakeTutorial ---
if (this.currentState === "fakeTutorial" && this.fakeTutorialTimerId) {
this.handleFakeTutorialInput(); // Wywołaj fałszywą śmierć
return; // Zakończ przetwarzanie gestu
}
var dx = this.touchEnd.x - this.touchStart.x;
var dy = this.touchEnd.y - this.touchStart.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Minimalny dystans dla swipe (turlania)
var swipeThreshold = 50;
// --- Obsługa Tapnięcia ---
if (distance < swipeThreshold) {
// Sprawdź, czy tapnięcie było na interaktywnym obiekcie (przycisku)
// Zakładamy, że LK.Game obsługuje to przez przekazanie 'obj' do game.down/up
// Jeśli tapnięcie NIE było na przycisku:
if (this.currentState === "title") {
// Sprawdź czy tapnięcie nie było na przycisku Grill Menu
// Prosty sposób: załóżmy, że jeśli obj nie jest zdefiniowany w game.up, to kliknięto tło
// (Wymaga sprawdzenia, jak LK.Game przekazuje 'obj')
// Na razie zakładamy, że tapnięcie w tło przechodzi do intro
this.showIntro();
return;
}
// W innych stanach (gameOver, victory, grillMenu) tapnięcie w tło nic nie robi
// Obsługa przycisków dzieje się przez ich własne handlery .down/.up
return;
}
// --- Obsługa Swipe (Turlania) ---
// Działa tylko w stanie gry i gdy gracz żyje
if (this.currentState === "game" && player && !player.dead) {
// Normalizuj kierunek gestu
var direction = {
x: 0,
y: 0
};
if (distance > 0) {
// Unikaj dzielenia przez zero
direction.x = dx / distance;
direction.y = dy / distance;
}
// Wykonaj turlanie gracza
player.roll(direction);
}
// W innych stanach swipe jest ignorowany
}
};
// --- Obsługa inputu ---
game.down = function (x, y, obj) {
// Rejestruj początek dotyku/kliknięcia
// Stany, w których śledzimy gesty lub kliknięcia przycisków:
var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"];
if (trackStates.indexOf(gameState.currentState) !== -1) {
gameState.touchStart.x = x;
gameState.touchStart.y = y;
gameState.touchEnd.x = x; // Reset end point
gameState.touchEnd.y = y;
}
// Obsługa kliknięć przycisków jest robiona przez ich własne handlery .down
};
game.up = function (x, y, obj) {
// Rejestruj koniec dotyku/kliknięcia i przetwarzaj gest
var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"];
if (trackStates.indexOf(gameState.currentState) !== -1) {
gameState.touchEnd.x = x;
gameState.touchEnd.y = y;
// Przetwórz gest (sprawdzi czy to tap czy swipe i podejmie akcję)
// Sprawdź, czy 'obj' istnieje - jeśli tak, to było kliknięcie na interaktywnym elemencie,
// którego logikę obsługuje jego własny handler .down/.up, więc nie rób nic więcej.
// Jeśli 'obj' nie istnieje, to było kliknięcie/swipe w tło.
game.up = function (x, y, obj) {
if (trackStates.indexOf(gameState.currentState) !== -1) {
gameState.touchEnd.x = x;
gameState.touchEnd.y = y;
// WAŻNE: Wywołuj zawsze processTouchGesture(), bez if (!obj)!
gameState.processTouchGesture();
}
};
}
// Obsługa puszczenia przycisków (.up) może być dodana tutaj, jeśli potrzebna
};
game.move = function (x, y, obj) {
// Śledź ruch palca/myszy dla swipe
var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"];
if (trackStates.indexOf(gameState.currentState) !== -1) {
gameState.touchEnd.x = x; // Aktualizuj końcową pozycję na bieżąco
gameState.touchEnd.y = y;
}
};
// --- Główna pętla aktualizacji gry ---
game.update = function () {
// Aktualizuj UI niezależnie od stanu gry (ale tylko jeśli UI istnieje)
if (ui) {
ui.updateDeathsCounter();
// Aktualizuj pasek zdrowia bossa (jeśli boss istnieje i UI istnieje)
if (boss) {
// Upewnij się, że maxHealth jest sensowne
var maxHp = boss.maxHealth > 0 ? boss.maxHealth : 1;
ui.updateBossHealth(boss.health, maxHp);
} else {
// Jeśli boss nie istnieje, ukryj pasek (chyba że to wygrana Boss+ przez czas)
if (!(gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode)) {
// Domyślne maxHP do ukrycia paska to 1 (uniknięcie dzielenia przez 0)
ui.updateBossHealth(0, 1);
}
}
// Aktualizuj serca gracza (jeśli gracz istnieje i UI istnieje)
if (player) {
ui.updateHearts(player.health, storage.maxHearts);
} else if (gameState.currentState !== "game" && gameState.currentState !== "gameOver") {
// Ukryj serca poza grą/game over
ui.updateHearts(0, storage.maxHearts);
}
// Pozycjonowanie/widoczność elementów UI jest zarządzana przez gameState.positionElements() w metodach zmiany stanu.
}
// Logika specyficzna dla stanu gry
if (gameState.currentState === "game") {
if (player) {
player.update(); // Aktualizacja gracza (ruch turlania, nietykalność, kolizja turlania z bossem)
}
if (boss) {
boss.update(); // Aktualizacja bossa (ruch, cooldown, tworzenie/usuwanie ataków, kolizje ataków z graczem)
}
// Sprawdzanie kolizji i zmiany stanów (gameOver, grillScreen) są teraz wewnątrz metod update/die gracza i bossa.
}
// Logika dla innych stanów (np. animacje w intro, obsługa przycisków) jest zarządzana przez ich własne funkcje i handlery.
};
// --- Rozpoczęcie gry ---
// Inicjalizuj obiekt gameState, który zajmie się resztą.
gameState.init(); ===================================================================
--- original.js
+++ change.js
@@ -1,594 +1,23 @@
/****
-* Initialize Game
+* Plugins
****/
-var game = new LK.Game({
- backgroundColor: 0x000000
-});
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1");
/****
-* Game Code
+* Classes
****/
-/****
-* Global variables
-****/
-var player = null;
-var boss = null;
-var ui = null;
-var storage = null;
-var isNewBossPlusMode = false;
-var game = null;
-var gameContainer = "gameContainer"; // ID elementu HTML, w którym będzie wyświetlana gra
-var gameOverReasonIsDeath = true; // true jeśli game over z powodu śmierci, false jeśli z powodu czasu w Boss+
-/****
-* Game state manager
-****/
-var gameState = function () {
- var self = {};
- self.currentState = "loading"; // Initial state
- self.changeState = function (newState) {
- console.log("Changing state from", self.currentState, "to", newState); // Log state changes
- self.currentState = newState;
- // Manage visibility of game elements based on state
- // These position/visibility methods should be in gameState or UI, not individual element updates
- self.positionElements(); // Call positionElements on state change
- // Handle specific state transitions
- if (self.currentState === "title") {
- // Clear any game elements from previous attempts if necessary
- if (game) {
- game.removeChildren(); // Remove all children from the game container
- // Do not destroy game here, it's handled in createGame
- }
- // Ensure player and boss are null when returning to title
- player = null;
- boss = null;
- ui = null; // UI will be recreated in createGame if returning from game over
- // Ensure attacks are cleared
- if (boss && boss.attacks) {
- boss.attacks.forEach(function (attack) {
- if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
- attack.visual.destroy();
- }
- });
- boss.attacks = [];
- }
- // If boss object is destroyed, self.attacks check might fail, add a check for that
- // Display title screen elements
- // In this simple example, we just show the start button via positionElements
- } else if (self.currentState === "game") {
- // Start the game loop when entering game state
- // The gameLoop is already running based on LK.init settings (60fps)
- // Player and Boss are created just before changing to 'game' state
- // Reset player/boss health or properties if necessary (or do it in their constructors)
- if (player) {
- player.health = storage.maxHearts; // Reset player health
- // ui.updateHearts(player.health, storage.maxHearts); // Update UI immediately
- }
- if (boss) {
- boss.health = boss.maxHealth; // Reset boss health
- // ui.updateBossHealth(boss.health, boss.maxHealth); // Update UI immediately
- boss.attackCooldown = 60; // Initial delay before first boss attack (1 second at 60fps)
- boss.stunned = false; // Ensure boss is not stunned
- boss.repositioning = false; // Ensure boss is not repositioning
- boss.dead = false; // Ensure boss is not dead
- boss.phase = 1; // Reset boss phase
- boss.attackSpeedMultiplier = 1; // Reset attack speed multiplier
- boss.attacks = []; // Clear any lingering attacks
- // Reset boss graphic state if needed (e.g., tint)
- boss.tint = 0xFFFFFF; // Reset tint
- }
- } else if (self.currentState === "gameOver") {
- // Stop game loop? LK manages this, or we can stop updates manually in gameLoop
- // In this example, gameLoop checks gameState.currentState
- // Display game over screen elements (handled by positionElements)
- // Stop boss attacks explicitly if boss exists and has a method for it
- if (boss) {
- boss.attackCooldown = 9999; // Prevent new attacks
- // Ensure existing attacks are cleaned up in boss.update when state changes
- }
- // Stop player movement, etc. (handled by player.update checking gameState.currentState)
- // Clean up game elements from screen? gameState.positionElements might hide them.
- } else if (self.currentState === "grillScreen") {
- // Similar cleanup/display logic for grill screen
- if (boss) {
- boss.attackCooldown = 9999; // Prevent new attacks
- }
- // Ensure existing attacks are cleaned up
- if (boss && boss.attacks) {
- boss.attacks.forEach(function (attack) {
- if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
- attack.visual.destroy();
- }
- });
- boss.attacks = [];
- }
- // Hide player and boss (handled by positionElements)
- }
- };
- self.positionElements = function () {
- // This function manages the visibility and potentially position of game elements based on the current state
- // It should be called whenever the state changes.
- var startButton = LK.getObjectById("startButton"); // Przykład pobierania przycisku
- var bossHpBar = LK.getObjectById("bossHpBar");
- var playerHearts = LK.getObjectById("playerHearts"); // Zakładając, że UI ma kontener na serca
- var gameOverMessage = LK.getObjectById("gameOverMessage"); // Przykład wiadomości game over
- var grillMenu = LK.getObjectById("grillMenu"); // Przykład menu grill screen
- // Visibility logic
- if (startButton) {
- startButton.visible = self.currentState === "title";
- }
- // Pasek HP bossa widoczny tylko w stanie "game" (lub game over po czasie w Boss+)
- if (bossHpBar) {
- bossHpBar.visible = self.currentState === "game" || self.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode;
- }
- // Serca gracza widoczne tylko w stanie "game"
- if (playerHearts) {
- playerHearts.visible = self.currentState === "game";
- }
- if (gameOverMessage) {
- gameOverMessage.visible = self.currentState === "gameOver" && gameOverReasonIsDeath;
- } // Game over tylko ze śmierci
- if (grillMenu) {
- grillMenu.visible = self.currentState === "grillScreen" || self.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode;
- } // Menu grill screen lub game over po czasie
- // Show player and boss only in game state
- if (player) {
- player.visible = self.currentState === "game" || self.currentState === "gameOver" || self.currentState === "grillScreen";
- } // Pokaż gracza w game, game over, grill screen
- if (boss) {
- boss.visible = self.currentState === "game" || self.currentState === "gameOver" || self.currentState === "grillScreen";
- } // Pokaż bossa w game, game over, grill screen
- };
- self.gameOver = function () {
- var reasonIsDeath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
- gameOverReasonIsDeath = reasonIsDeath; // Zapisz przyczynę game over
- self.changeState("gameOver");
- // Wyświetl wiadomość Game Over lub Grill Screen w zależności od przyczyny i trybu (zajmie się tym positionElements)
- // Tutaj można dodać logikę czyszczenia obiektów, zatrzymania muzyki itp.
- LK.getSound('playerHit').stop(); // Stop player hit sound if it's playing
- };
- self.showGrillScreen = function () {
- self.changeState("grillScreen");
- // Logic for showing the grill screen
- };
- // Metody zmiany stanu dla przycisków
- self.startGame = function () {
- var newBossPlus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
- isNewBossPlusMode = newBossPlus; // Set the mode
- storage.newBossPlusMode = isNewBossPlusMode; // Save mode to storage
- // Wyczyść poprzednią grę i stwórz nową instancję
- createGame(); // This function now contains a setTimeout delay
- // Change state to game is now inside the setTimeout in createGame()
- // self.changeState("game"); // MOVED INSIDE TIMEOUT IN createGame
- };
- self.returnToTitle = function () {
- // Clean up game elements before returning to title
- if (game) {
- game.removeChildren(); // Remove all display objects from the game container
- // Destroy player, boss, ui objects explicitly to clear references and updates
- if (player && player.destroy && !player.destroyed) {
- player.destroy();
- }
- if (boss && boss.destroy && !boss.destroyed) {
- boss.destroy();
- }
- if (ui && ui.destroy && !ui.destroyed) {
- ui.destroy();
- }
- player = null;
- boss = null;
- ui = null;
- // game.destroy(); // Do not destroy game container itself, it's reused
- }
- // Clean up any lingering attacks if Boss object was null
- if (boss && boss.attacks) {
- // This check is redundant if boss is null, but safe
- boss.attacks.forEach(function (attack) {
- if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
- attack.visual.destroy();
- }
- });
- boss.attacks = [];
- }
- self.changeState("title");
- };
- return self;
-}(); // End gameState
-/****
-* Player class
-****/
-var Player = Container.expand(function () {
- var self = Container.call(this);
- // Attach player graphic (depends on asset)
- self.playerGraphics = LK.getAsset('player', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.addChild(self.playerGraphics);
- self.health = 5; // Default player health (will be set by storage.maxHearts in gameState.startGame)
- self.speed = 8; // Player movement speed
- self.rollSpeed = 12; // Player roll speed
- self.rolling = false;
- self.rollDuration = 30; // Roll duration in frames (0.5 seconds at 60fps)
- self.rollTimer = 0;
- self.rollDirectionX = 0;
- self.rollDirectionY = 0;
- self.invulnerable = false; // Player invulnerability flag
- self.invulnerabilityDuration = 60; // Invulnerability duration after rolling/getting hit
- self.invulnerabilityTimer = 0;
- self.dead = false;
- // Create player roll animation (depends on assets)
- self.createRollAnim = function () {
- var frames = [LK.getAsset('roll0', {}), LK.getAsset('roll1', {}), LK.getAsset('roll2', {}), LK.getAsset('roll3', {}), LK.getAsset('roll4', {})];
- var rollAnim = new SpriteAnimation({
- frames: frames,
- frameDuration: 60,
- // Roll animation speed
- loop: true,
- // Roll animation loops
- anchorX: 0.5,
- anchorY: 0.5,
- x: 0,
- // Relative position
- y: 0 // Relative position
- });
- return rollAnim;
- };
- self.rollAnim = null; // Will hold the current roll animation object
- self.startRoll = function () {
- if (self.rolling || self.dead || gameState.currentState !== "game") {
- return; // Cannot roll if already rolling, dead, or not in game state
- }
- self.rolling = true;
- self.rollTimer = self.rollDuration;
- self.invulnerable = true; // Invulnerable during roll
- self.invulnerabilityTimer = self.invulnerabilityDuration;
- // Capture current movement direction for roll
- self.rollDirectionX = (LK.isDown("left") ? -1 : 0) + (LK.isDown("right") ? 1 : 0);
- self.rollDirectionY = (LK.isDown("up") ? -1 : 0) + (LK.isDown("down") ? 1 : 0);
- // Ensure there's a direction to roll, default if no input
- if (self.rollDirectionX === 0 && self.rollDirectionY === 0) {
- // Default roll direction (e.g., last movement direction, or down)
- // For simplicity, let's just prevent roll if no direction pressed.
- // Or set a default if needed: self.rollDirectionY = 1; // Example: roll down
- self.rolling = false; // Cancel roll if no direction input
- self.invulnerable = false;
- self.invulnerabilityTimer = 0;
- return; // Exit if no roll direction
- }
- // Normalize roll direction vector (optional, but good for consistent speed)
- var rollMagnitude = Math.sqrt(self.rollDirectionX * self.rollDirectionX + self.rollDirectionY * self.rollDirectionY);
- if (rollMagnitude > 0) {
- self.rollDirectionX /= rollMagnitude;
- self.rollDirectionY /= rollMagnitude;
- }
- // Remove idle graphic, add roll animation
- if (self.playerGraphics && self.playerGraphics.parent === self) {
- self.playerGraphics.parent.removeChild(self.playerGraphics);
- } else if (self.playerGraphics && self.playerGraphics.parent) {
- // Fallback
- self.playerGraphics.parent.removeChild(self.playerGraphics);
- }
- self.playerGraphics = null;
- self.rollAnim = self.addChild(self.createRollAnim()); // User reported 'self.addChild is not a function' here, but it should work on a Container instance.
- self.rollAnim.play(); // Start roll animation
- };
- self.takeDamage = function (amount) {
- if (self.dead || self.invulnerable || gameState.currentState !== "game") {
- return; // Cannot take damage if dead, invulnerable, or not in game state
- }
- self.health -= amount;
- self.health = Math.max(0, self.health); // Health cannot go below zero
- LK.getSound('playerHit').play(); // Play player hit sound
- LK.effects.flashObject(self, 0xFF0000, 300); // Flash effect on hit
- self.invulnerable = true; // Become invulnerable after taking damage
- // Use a separate timer for hit invulnerability if needed, or just reuse roll timer logic
- // For simplicity, reuse invulnerabilityTimer:
- self.invulnerabilityTimer = self.invulnerabilityDuration; // Invulnerable for a short period
- // Update UI hearts immediately
- // ui.updateHearts(self.health, storage.maxHearts); // This is called in game.update -> ui.updateHearts
- if (self.health <= 0) {
- self.die(); // Player dies
- }
- };
- self.die = function () {
- if (self.dead) {
- return; // Already dead
- }
- self.dead = true;
- // Stop all movement/actions
- self.rolling = false;
- self.invulnerable = false; // No longer invulnerable once dead
- // Stop/remove roll animation if active
- if (self.rollAnim) {
- self.rollAnim.stop();
- if (self.rollAnim.parent === self) {
- self.rollAnim.parent.removeChild(self.rollAnim);
- } else if (self.rollAnim.parent) {
- // Fallback
- self.rollAnim.parent.removeChild(self.rollAnim);
- }
- self.rollAnim.destroy();
- self.rollAnim = null;
- }
- // Add player death animation or graphic (if any)
- // For simplicity, just hide or remove the player graphic
- if (self.playerGraphics && self.playerGraphics.parent === self) {
- self.playerGraphics.parent.removeChild(self.playerGraphics);
- } else if (self.playerGraphics && self.playerGraphics.parent) {
- // Fallback
- self.playerGraphics.parent.removeChild(self.playerGraphics);
- }
- self.playerGraphics = null; // Clear reference
- // Trigger game over state after a short delay or death animation
- LK.setTimeout(function () {
- if (gameState.currentState === "game") {
- // Only transition if still in game state
- gameState.gameOver(true); // Call game over function with death reason
- }
- }, 1000); // Delay game over screen by 1 second
- };
- self.update = function () {
- // Update only if in game state
- if (gameState.currentState !== "game") {
- // Stop rolling and remove animation if state changes
- if (self.rolling) {
- self.rolling = false;
- if (self.rollAnim) {
- self.rollAnim.stop();
- if (self.rollAnim.parent === self) {
- self.rollAnim.parent.removeChild(self.rollAnim);
- } else if (self.rollAnim.parent) {
- // Fallback
- self.rollAnim.parent.removeChild(self.rollAnim);
- }
- self.rollAnim.destroy();
- self.rollAnim = null;
- }
- // Add back idle graphic if it was removed
- if (!self.playerGraphics) {
- self.playerGraphics = self.addChild(self.attachAsset('player', {
- // self.addChild should work here.
- anchorX: 0.5,
- anchorY: 0.5,
- x: 0,
- y: 0 // Relative position
- }));
- }
- }
- return; // Exit update if not in game state
- }
- // Do not update if player is dead
- if (self.dead) {
- return;
- }
- // --- Rolling logic ---
- if (self.rolling) {
- self.x += self.rollDirectionX * self.rollSpeed;
- self.y += self.rollDirectionY * self.rollSpeed;
- self.rollTimer--;
- if (self.rollTimer <= 0) {
- self.rolling = false;
- // Stop and remove roll animation
- if (self.rollAnim) {
- self.rollAnim.stop();
- if (self.rollAnim.parent === self) {
- self.rollAnim.parent.removeChild(self.rollAnim);
- } else if (self.rollAnim.parent) {
- // Fallback
- self.rollAnim.parent.removeChild(self.rollAnim);
- }
- self.rollAnim.destroy();
- self.rollAnim = null;
- }
- // Add back idle graphic
- if (!self.playerGraphics) {
- self.playerGraphics = self.addChild(self.attachAsset('player', {
- // self.addChild should work here.
- anchorX: 0.5,
- anchorY: 0.5,
- x: 0,
- y: 0 // Relative position
- }));
- }
- }
- } else {
- // --- Movement logic (only when not rolling) ---
- var moveX = (LK.isDown("left") ? -1 : 0) + (LK.isDown("right") ? 1 : 0);
- var moveY = (LK.isDown("up") ? -1 : 0) + (LK.isDown("down") ? 1 : 0);
- var moveMagnitude = Math.sqrt(moveX * moveX + moveY * moveY);
- if (moveMagnitude > 0) {
- moveX /= moveMagnitude;
- moveY /= moveMagnitude;
- self.x += moveX * self.speed;
- self.y += moveY * self.speed;
- }
- // No specific idle animation in this example, uses static player graphic
- }
- // --- Invulnerability timer ---
- if (self.invulnerable) {
- self.invulnerabilityTimer--;
- if (self.invulnerabilityTimer <= 0) {
- self.invulnerable = false;
- }
- // Optional: add visual effect for invulnerability (e.g., alpha blinking)
- self.alpha = self.invulnerabilityTimer % 10 < 5 ? 0.5 : 1; // Simple blinking effect
- } else {
- self.alpha = 1; // Fully visible when not invulnerable
- }
- // --- Boundary checks (Arena borders) ---
- // Assuming arena borders are at 100px from edge, map 2048x2732
- var halfWidth = self.width * self.scaleX / 2;
- var halfHeight = self.height * self.scaleY / 2;
- var minX = 100 + halfWidth;
- var maxX = 2048 - 100 - halfWidth;
- var minY = 100 + halfHeight;
- var maxY = 2732 - 100 - halfHeight;
- self.x = Math.max(minX, Math.min(self.x, maxX));
- self.y = Math.max(minY, Math.min(self.y, maxY));
- // --- Collision with Boss (only when not invulnerable) ---
- // Boss collision check is handled in player.update(), NOT boss.update()
- // It should only happen if player is NOT invulnerable
- // This collision check is handled in game.update -> player.update()
- // Let's add the check here if player.update is responsible for it.
- // Check collision with Boss if boss exists, is not dead, and player is not invulnerable
- if (boss && !boss.dead && !self.invulnerable) {
- var dx_b = boss.x - self.x;
- var dy_b = boss.y - self.y;
- var distance_b = Math.sqrt(dx_b * dx_b + dy_b * dy_b);
- var playerRadius_b = self.width / 2;
- var bossRadius_b = boss.width * boss.scaleX / 2; // Use scaled boss width
- if (distance_b < playerRadius_b + bossRadius_b) {
- // Collision detected with Boss body
- self.takeDamage(1); // Player takes 1 damage
- }
- }
- // Note: Collision with Boss *attacks* is handled in boss.update()
- };
- return self;
-})(); // End Player class
-/****
-* UI object
-****/
-var UI = function () {
- var self = {};
- // Create UI elements (depend on assets/shapes)
- self.bossHpBar = LK.getAsset('bossHpbar', {}); // Should use LK.getAsset for shapes in this code.
- self.bossHpBar.x = 1024 - self.bossHpBar.width / 2; // Center the bar
- self.bossHpBar.y = 50; // Position near top
- self.bossHpBar.visible = false; // Hide initially
- self.playerHearts = new DisplayObjectContainer(); // Container for player hearts
- // Hearts will be added as children to this container in updateHearts
- self.playerHearts.x = 100; // Position the hearts container
- self.playerHearts.y = 50;
- self.playerHearts.visible = false; // Hide initially
- // Create start button (depends on asset/shape)
- self.startButton = LK.getAsset('button_bg', {}); // Should use LK.getAsset for shapes in this code.
- self.startButton.x = 1024; // Center the button
- self.startButton.y = 1366; // Position it
- self.startButton.anchorX = 0.5;
- self.startButton.anchorY = 0.5;
- self.startButton.visible = false; // Hide initially
- // Add text to start button (depends on font/text rendering)
- self.startButtonText = new LK.Text({
- text: "Start Game",
- color: 0x000000,
- size: 40,
- font: "Arial",
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.startButton.addChild(self.startButtonText); // Add text as child of button
- // Add event listener to start button
- self.startButton.on("click", function () {
- if (gameState.currentState === "title") {
- gameState.startGame(); // Start game in standard mode
- }
- });
- // Create Boss+ button (depends on asset/shape)
- self.newBossPlusButton = LK.getAsset('button_bg', {}); // Should use LK.getAsset for shapes in this code.
- self.newBossPlusButton.x = 1024; // Center
- self.newBossPlusButton.y = 1500; // Position below start
- self.newBossPlusButton.anchorX = 0.5;
- self.newBossPlusButton.anchorY = 0.5;
- self.newBossPlusButton.visible = false;
- // Add text to Boss+ button
- self.newBossPlusButtonText = new LK.Text({
- text: "NEW BOSS+",
- color: 0x000000,
- size: 40,
- font: "Arial",
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.newBossPlusButton.addChild(self.newBossPlusButtonText);
- // Add event listener to Boss+ button
- self.newBossPlusButton.on("click", function () {
- if (gameState.currentState === "title") {
- gameState.startGame(true); // Start game in Boss+ mode
- }
- });
- // Create Game Over message (depends on text rendering)
- self.gameOverMessage = new LK.Text({
- text: "Game Over!",
- color: 0xFF0000,
- size: 80,
- font: "Arial",
- anchorX: 0.5,
- anchorY: 0.5,
- x: 1024,
- // Center X
- y: 1366,
- // Center Y
- visible: false // Hide initially
- });
- // Create Grill Screen menu (depends on shape)
- self.grillMenu = LK.getAsset('grillMenu', {}); // User reported 'LK.getShape is not a function' here, but LK.getAsset should work in this code.
- self.grillMenu.x = 1024;
- self.grillMenu.y = 1366;
- self.grillMenu.anchorX = 0.5;
- self.grillMenu.anchorY = 0.5;
- self.grillMenu.visible = false; // Hide initially
- // Add buttons/text to Grill Screen if needed
- // Add all UI elements to the game container (depth/layering handled by addChild order or setChildIndex)
- // Order matters for layering: background first, then game objects, then UI on top
- // Assuming UI should be on top of everything else in the 'game' container
- // Add UI elements AFTER adding player, boss, etc. in createGame
- // Or add them here and manage z-index
- // Let's add them here and assume later added elements are on top by default or managed by setChildIndex
- self.updateBossHealth = function (currentHp, maxHp) {
- // Ensure maxHp is at least 1 to prevent division by zero
- var safeMaxHp = Math.max(1, maxHp);
- // Calculate width based on health percentage
- var hpPercentage = currentHp / safeMaxHp;
- // Assuming original bossHpBar width was 800
- self.bossHpBar.scaleX = hpPercentage;
- // Adjust position if anchor is not 0,0 to keep the bar filling from left
- // If anchorX is 0.5, the bar scales from the center. Need to adjust x.
- self.bossHpBar.x = 1024 - self.bossHpBar.width * self.bossHpBar.scaleX / 2;
- };
- self.updateHearts = function (currentHearts, maxHearts) {
- // Clear existing heart graphics
- self.playerHearts.removeChildren();
- // Add heart graphics based on currentHealth
- var heartSize = 40; // Match heart asset size
- var padding = 10; // Spacing between hearts
- for (var i = 0; i < maxHearts; i++) {
- var heartGraphic = LK.getAsset('heart', {}); // Should use LK.getAsset for shapes in this code.
- // Position hearts horizontally within the container
- heartGraphic.x = i * (heartSize + padding);
- heartGraphic.y = 0; // All on the same vertical level
- // Tint heart based on current health
- if (i < currentHearts) {
- heartGraphic.tint = 0xFF0000; // Full heart color (Red)
- } else {
- heartGraphic.tint = 0x888888; // Empty heart color (Grayish)
- }
- self.playerHearts.addChild(heartGraphic); // Add heart to the container
- }
- // Position the container is done in gameState.positionElements or createGame
- };
- // Add UI elements to game container in createGame after player/boss
- // This is handled in the modified createGame function
- self.update = function () {
- // UI elements are mostly passive and updated by boss/player health changes
- // No continuous update needed for UI elements themselves in this example,
- // but UI object could have update logic for animations, etc.
- };
- return self;
-}(); // End UI object
-/****
-* Boss class
-****/
+// Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem)
+// Klasy z stworzylo zloto.txt (bardziej zaawansowana walka z bossem)
var Boss = Container.expand(function () {
var self = Container.call(this);
// Definicje list klatek dla fireballi - Zdefiniowane raz na początku klasy Boss
var circleFrames = [LK.getAsset('fireball0', {}), LK.getAsset('fireball00', {}), LK.getAsset('fireball01', {}), LK.getAsset('fireball02', {}), LK.getAsset('fireball03', {}), LK.getAsset('fireball04', {}), LK.getAsset('fireball05', {}), LK.getAsset('fireball06', {}), LK.getAsset('fireball07', {}), LK.getAsset('fireball08', {}), LK.getAsset('fireball09', {}), LK.getAsset('fireball1', {}), LK.getAsset('fireball10', {}), LK.getAsset('fireball11', {}), LK.getAsset('fireball12', {}), LK.getAsset('fireball13', {}), LK.getAsset('fireball14', {})];
var lineFrames = [LK.getAsset('fireball2', {}), LK.getAsset('fireball3', {}), LK.getAsset('fireball4', {}), LK.getAsset('fireball5', {}), LK.getAsset('fireball6', {}), LK.getAsset('fireball7', {}), LK.getAsset('fireball8', {}), LK.getAsset('fireball9', {}), LK.getAsset('fireball15', {}), LK.getAsset('fireball16', {})];
// *** MODYFIKACJA: Dodaj grafikę bossIdle jako DZIECKO obiektu bossa (self) ***
// Używamy attachAsset, który już dodaje jako dziecko i ustawia pozycję relatywną na podstawie anchor.
self.bossGraphics = self.attachAsset('bossIdle', {
- // User reported 'self.attachAsset is not a function' here, but it should work on a Container instance.
// Dziecko self
anchorX: 0.5,
anchorY: 0.5
// Pozycja relatywna na 0,0 jest domyślna i poprawna przy anchorX/Y 0.5
@@ -643,9 +72,9 @@
self.bossGraphics.parent.removeChild(self.bossGraphics);
}
self.bossGraphics = null; // Wyczyść referencję
// Tworzymy nową animację bossa i dodajemy ją JAKO DZIECKO BOSSA
- self.bossAttackAnim = self.addChild(self.createBossAttackAnim()); // self.addChild should work here.
+ self.bossAttackAnim = self.addChild(self.createBossAttackAnim()); // *** MODIFIED: DODANO DO self! ***
// Pozycja animacji jako dziecka bossa jest już ustawiona na 0,0 w createBossAttackAnim
// Metoda update dla TEJ NOWEJ animacji (definiowana tylko dla ataków 0 i 1)
self.bossAttackAnim.update = function () {
// Sprawdź, czy ten obiekt animacji jest nadal aktywny self.bossAttackAnim bossa
@@ -663,9 +92,9 @@
if (this.currentFrame >= this.frames.length) {
// Animacja skończona, wracamy do idle
// *** Dodaj grafikę idle z powrotem JAKO DZIECKO BOSSA i ustaw self.bossGraphics ***
self.bossGraphics = self.addChild(LK.getAsset('bossIdle', {
- // self.addChild should work here.
+ // *** MODIFIED: DODANO DO self! ***
anchorX: 0.5,
anchorY: 0.5,
x: 0,
// Pozycja relatywna
@@ -739,12 +168,8 @@
// Logika aktywacji kolizji (isActive) jest w Boss.update
self.createAttack = function (x, y, duration, activationFrame, frameDurationMs, framesList) {
// Użyj przekazanej framesList, domyślną oryginalną jeśli nie przekazana
var frames = framesList || [LK.getAsset('fireball0', {}), LK.getAsset('fireball00', {}), LK.getAsset('fireball01', {}), LK.getAsset('fireball02', {}), LK.getAsset('fireball03', {}), LK.getAsset('fireball04', {}), LK.getAsset('fireball05', {}), LK.getAsset('fireball06', {}), LK.getAsset('fireball07', {}), LK.getAsset('fireball08', {}), LK.getAsset('fireball09', {}), LK.getAsset('fireball1', {}), LK.getAsset('fireball10', {}), LK.getAsset('fireball11', {}), LK.getAsset('fireball12', {}), LK.getAsset('fireball13', {}), LK.getAsset('fireball14', {})];
- // console.log("CreateAttack called at:", x, y, " Frames array:", frames); // Debug log - można usunąć po naprawie
- // if (!frames || frames.length === 0 || frames.some(f => f === null || typeof f === 'undefined' || !f.texture)) { // Debug check - można usunąć po naprawie
- // console.error("Frames array is empty or contains invalid assets! Attack creation might fail."); // Debug log - można usunąć po naprawie
- // }
var spriteAnim = game.addChild(new SpriteAnimation({
// Wizualizacja fireballa jest dodawana do kontenera 'game'
frames: frames,
frameDuration: frameDurationMs,
@@ -872,9 +297,8 @@
} else if (self.bossGraphics && self.bossGraphics.parent) {
self.bossGraphics.parent.removeChild(self.bossGraphics);
}
self.bossGraphics = self.addChild(LK.getAsset('bossIdle', {
- // self.addChild should work here.
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
@@ -904,9 +328,9 @@
// Czas trwania, skalowany
easing: tween.easeIn,
// Krzywa animacji
onFinish: function onFinish() {
- // Po zakończeniu szarży
+ // Funkcja po zakończeniu szarży
if (self && !self.dead && gameState.currentState === "game") {
// Faza repozycji
self.repositioning = true;
LK.setTimeout(function () {
@@ -1086,9 +510,8 @@
}
}
// Usuń atak, jeśli skończył mu się czas życia LUB wizualizacja zniszczona
if (attack.lifeTime <= 0 || attack.visual && attack.visual.destroy || attack.visual && attack.visual.destroyed) {
- // Dodano trzeci warunek dla pewności
if (attack.visual && attack.visual.destroy && !attack.visual.destroyed) {
attack.visual.destroy();
}
self.attacks.splice(i, 1);
@@ -1104,79 +527,1618 @@
self.startAttackPattern();
}
};
return self; // Zwróć instancję obiektu Boss
-})(); // End Boss class
+});
+var Player = Container.expand(function () {
+ var self = Container.call(this);
+ // Zamiast var playerGraphics = ...
+ self.playerGraphics = self.attachAsset('player', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Player properties
+ self.health = 5; // Default health (will be overridden in startGame)
+ self.speed = 8; // Movement speed (NIE UŻYWANE OBECNIE W UPDATE?)
+ self.rolling = false; // Flag for roll state
+ self.rollDirection = {
+ x: 0,
+ y: 0
+ }; // Direction of roll
+ self.rollSpeed = 20; // Speed during roll
+ self.rollDuration = 300; // Roll duration in ms
+ self.rollCooldown = 0; // Cooldown between rolls (frames)
+ self.invulnerable = false; // Invulnerability flag
+ self.invulnerabilityFrames = 0; // Frames of invulnerability left
+ self.dead = false; // Dead state
+ self.rollTimeoutId = null; // Store timeout ID for roll duration
+ self.invulnerabilityTimeoutId = null; // Store timeout ID for invulnerability (NIE UŻYWANE?)
+ self.rollAnimationInterval = null; // Store interval ID for roll animation
+ self.hasRolledThroughBossThisRoll = false; // Track if boss was hit during this roll
+ // Clear all roll-related timeouts and intervals
+ self.clearRollTimeouts = function () {
+ if (self.rollTimeoutId) {
+ LK.clearTimeout(self.rollTimeoutId);
+ self.rollTimeoutId = null;
+ }
+ if (self.rollAnimationInterval) {
+ LK.clearInterval(self.rollAnimationInterval);
+ self.rollAnimationInterval = null;
+ }
+ };
+ // Roll mechanic
+ self.roll = function (direction) {
+ if (!self.rolling && self.rollCooldown <= 0 && !self.dead) {
+ self.rolling = true;
+ self.rollDirection = direction;
+ self.rollCooldown = 45;
+ self.invulnerable = true;
+ self.invulnerabilityFrames = 30;
+ self.hasRolledThroughBossThisRoll = false;
+ // Ukryj normalną grafikę gracza
+ if (self.playerGraphics && !self.playerGraphics.destroyed) {
+ self.playerGraphics.visible = false;
+ }
+ // --- Roll Animation (turlanie) ---
+ var rollFrames = ['roll', 'roll0', 'roll1', 'roll2'];
+ var currentFrame = 0;
+ var rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 0,
+ y: 0
+ }));
+ var rollAnimationInterval = LK.setInterval(function () {
+ if (rollAnimationSprite && rollAnimationSprite.destroy) {
+ rollAnimationSprite.destroy();
+ }
+ currentFrame = (currentFrame + 1) % rollFrames.length;
+ rollAnimationSprite = self.addChild(LK.getAsset(rollFrames[currentFrame], {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 0,
+ y: 0
+ }));
+ }, 70); // co 70ms zmienia klatkę podczas turlania
+ if (self.rollTimeoutId) {
+ LK.clearTimeout(self.rollTimeoutId);
+ }
+ self.rollTimeoutId = LK.setTimeout(function () {
+ self.rolling = false;
+ // --- Po rollu zakończ turlanie ---
+ if (rollAnimationInterval) {
+ LK.clearInterval(rollAnimationInterval);
+ }
+ if (rollAnimationSprite && rollAnimationSprite.destroy) {
+ rollAnimationSprite.destroy();
+ }
+ // --- Stand Up Animation (wstawanie + machnięcie mieczem) ---
+ var standUpFrames = ['roll3', 'roll4'];
+ var standUpFrame = 0;
+ var standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 0,
+ y: 0
+ }));
+ var standUpInterval = LK.setInterval(function () {
+ if (standUpSprite && standUpSprite.destroy) {
+ standUpSprite.destroy();
+ }
+ standUpFrame++;
+ if (standUpFrame < standUpFrames.length) {
+ standUpSprite = self.addChild(LK.getAsset(standUpFrames[standUpFrame], {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 0,
+ y: 0
+ }));
+ } else {
+ // Koniec animacji stand-up
+ LK.clearInterval(standUpInterval);
+ if (standUpSprite && standUpSprite.destroy) {
+ standUpSprite.destroy();
+ }
+ // Przywróć normalną grafikę gracza
+ if (self.playerGraphics && !self.playerGraphics.destroyed) {
+ self.playerGraphics.visible = true;
+ }
+ }
+ }, 100); // 100ms między klatkami wstawania
+ self.rollTimeoutId = null;
+ }, self.rollDuration);
+ }
+ };
+ // Take damage method
+ self.takeDamage = function (amount) {
+ // Check if player can take damage (not invulnerable and not dead)
+ if (!self.invulnerable && !self.dead) {
+ self.health -= amount;
+ // Flash effect when hit
+ LK.effects.flashObject(self, 0xFF0000, 200);
+ // Check if player is dead
+ if (self.health <= 0) {
+ self.health = 0; // Ensure health doesn't go below 0 visually
+ self.die();
+ return; // Zakończ, jeśli gracz umarł
+ }
+ // Set brief invulnerability after hit (if not rolling)
+ // Nietykalność po trafieniu jest teraz KRÓTSZA niż turlanie
+ if (!self.rolling) {
+ self.invulnerable = true;
+ self.invulnerabilityFrames = 30; // Krótsza nietykalność po trafieniu (np. 0.5s)
+ }
+ }
+ };
+ // Die method
+ self.die = function () {
+ if (self.dead) {
+ // Zapobiegaj wielokrotnemu wywołaniu
+ return;
+ }
+ self.dead = true;
+ // Increment death counter
+ storage.totalDeaths = (storage.totalDeaths || 0) + 1;
+ // Increment max hearts after death (up to a maximum of 10)
+ if (!storage.maxHearts || storage.maxHearts < 5) {
+ // Upewnij się, że startujemy od min 5
+ storage.maxHearts = 5;
+ }
+ if (storage.maxHearts < 10) {
+ storage.maxHearts = storage.maxHearts + 1;
+ }
+ self.clearRollTimeouts(); // Anuluj ewentualne trwające turlanie
+ // Death animation
+ tween(self, {
+ alpha: 0,
+ scaleX: 2,
+ scaleY: 2
+ }, {
+ duration: 1000,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ // Zniszcz obiekt gracza PO zakończeniu animacji
+ if (self && self.destroy && !self.destroyed) {
+ self.destroy();
+ }
+ // Set death reason flag to true (player died)
+ gameOverReasonIsDeath = true;
+ // Show game over screen - gameState zarządza przejściem
+ gameState.gameOver(true); // Przekaż true (śmierć gracza)
+ }
+ });
+ };
+ // Update method called every frame
+ self.update = function () {
+ // Aktualizuj tylko w stanie gry
+ if (gameState.currentState !== "game") {
+ // Wyczyść timery, jeśli stan gry się zmienił
+ if (self.rolling) {
+ self.rolling = false;
+ } // Przerwij turlanie
+ self.clearRollTimeouts();
+ return;
+ }
+ // Nie aktualizuj jeśli gracz jest martwy (ale pozwól animacji śmierci działać)
+ if (self.dead) {
+ return;
+ }
+ // Handle roll cooldown
+ if (self.rollCooldown > 0) {
+ self.rollCooldown--;
+ }
+ // Handle invulnerability frames (zarówno z turlania, jak i z otrzymania obrażeń)
+ if (self.invulnerable && self.invulnerabilityFrames > 0) {
+ self.invulnerabilityFrames--;
+ // Blinking effect during invulnerability
+ // Miga szybciej
+ self.alpha = self.invulnerabilityFrames % 4 > 1 ? 0.3 : 1;
+ if (self.invulnerabilityFrames <= 0) {
+ self.invulnerable = false;
+ self.alpha = 1; // Restore full opacity when invulnerability ends
+ }
+ } else if (self.invulnerable && self.invulnerabilityFrames <= 0) {
+ // Upewnij się, że stan jest spójny jeśli klatki się skończyły
+ self.invulnerable = false;
+ self.alpha = 1;
+ }
+ // Handle movement during roll
+ if (self.rolling) {
+ var rollDx = self.rollDirection.x * self.rollSpeed;
+ var rollDy = self.rollDirection.y * self.rollSpeed;
+ var nextX = self.x + rollDx;
+ var nextY = self.y + rollDy;
+ // Ograniczenia areny (ściany na 100px marginesie, mapa 2048x2732)
+ // Uwzględnij rozmiar gracza (zakładając anchor 0.5, 0.5)
+ var halfWidth = self.width / 2;
+ var halfHeight = self.height / 2;
+ var minX = 100 + halfWidth;
+ var maxX = 2048 - 100 - halfWidth;
+ var minY = 100 + halfHeight;
+ var maxY = 2732 - 100 - halfHeight;
+ self.x = Math.max(minX, Math.min(nextX, maxX));
+ self.y = Math.max(minY, Math.min(nextY, maxY));
+ // Sprawdź kolizję turlania z bossem
+ // Sprawdź tylko jeśli boss istnieje, nie jest martwy i JESZCZE nie zadałeś obrażeń tym turlaniem
+ if (boss && !boss.dead && !self.hasRolledThroughBossThisRoll) {
+ // Uproszczone sprawdzenie kolizji na podstawie odległości między środkami
+ var dx_b = self.x - boss.x;
+ var dy_b = self.y - boss.y;
+ var distance_b = Math.sqrt(dx_b * dx_b + dy_b * dy_b);
+ // Uproszczony promień kolizji (połówka szerokości/wysokości)
+ var playerCollisionRadius = self.width / 2; // Zakładamy, że kolizja gracza jest okrągła
+ var bossCollisionRadius = boss.width / 2; // Zakładamy, że kolizja bossa jest okrągła
+ // Jeśli odległość między środkami jest mniejsza niż suma promieni kolizji
+ if (distance_b < playerCollisionRadius + bossCollisionRadius) {
+ // Kolizja wykryta podczas turlania
+ boss.takeDamage(10); // ZADAJ 10 OBRAŻEŃ BOSSOWI
+ self.hasRolledThroughBossThisRoll = true; // Oznacz, że zadałeś obrażenia w tym turlaniu
+ // Opcjonalny efekt wizualny/dźwiękowy trafienia bossa turlaniem
+ LK.effects.flashObject(boss, 0x00FF00, 200); // Mignij bossa na zielono
+ // LK.getSound('playerHitBossSound').play(); // Wymagałoby dodania nowego assetu dźwiękowego
+ }
+ }
+ } else {
+ // --- Podstawowy ruch gracza (jeśli nie turla się) ---
+ // Ta część była nieobecna, dodajmy prosty ruch oparty na wejściu (jeśli LK go dostarcza)
+ // Zakładając, że mamy dostęp do stanu klawiszy/joysticka np. przez LK.controls
+ // To jest PRZYKŁAD - musisz dostosować do API LK
+ /*
+ var moveX = 0;
+ var moveY = 0;
+ if (LK.controls.left) moveX -= 1;
+ if (LK.controls.right) moveX += 1;
+ if (LK.controls.up) moveY -= 1;
+ if (LK.controls.down) moveY += 1;
+ if (moveX !== 0 || moveY !== 0) {
+ // Normalizuj wektor ruchu, jeśli poruszasz się po przekątnej
+ var moveMagnitude = Math.sqrt(moveX * moveX + moveY * moveY);
+ var normalizedX = moveX / moveMagnitude;
+ var normalizedY = moveY / moveMagnitude;
+ var nextX = self.x + normalizedX * self.speed; // Użyj self.speed
+ var nextY = self.y + normalizedY * self.speed;
+ // Ograniczenia areny (jak w turlaniu)
+ var halfWidth = self.width / 2;
+ var halfHeight = self.height / 2;
+ var minX = 100 + halfWidth;
+ var maxX = 2048 - 100 - halfWidth;
+ var minY = 100 + halfHeight;
+ var maxY = 2732 - 100 - halfHeight;
+ self.x = Math.max(minX, Math.min(nextX, maxX));
+ self.y = Math.max(minY, Math.min(nextY, maxY));
+ }
+ */
+ // Jeśli nie masz łatwego dostępu do stanu klawiszy, gracz będzie się ruszał tylko podczas turlania.
+ }
+ };
+ return self;
+});
+// Shape class implementation for creating simple shapes
+var Shape = Container.expand(function (options) {
+ var self = Container.call(this);
+ options = options || {};
+ var width = options.width || 100;
+ var height = options.height || 100;
+ var color = options.color || 0xFFFFFF;
+ var shape = options.shape || 'box';
+ // Create the shape as an asset
+ var asset = self.attachAsset(shape, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: width,
+ height: height,
+ tint: color
+ });
+ // Set width and height for easier access
+ self.width = width;
+ self.height = height;
+ // Add anchor property to Shape for positioning
+ self.anchor = {
+ set: function set(x, y) {
+ // This mimics the behavior of the anchor.set method
+ self.anchorX = x;
+ self.anchorY = y;
+ }
+ };
+ return self;
+});
+var SpriteAnimation = Container.expand(function (options) {
+ var self = Container.call(this);
+ // Initialize with default values
+ options = options || {};
+ self.frames = options.frames || [];
+ self.frameDuration = options.frameDuration || 100; // Default 100ms per frame
+ self.loop = options.loop !== undefined ? options.loop : true;
+ self.currentFrame = 0;
+ self.frameTimer = 0;
+ self.playing = true;
+ // Set initial position and anchor if provided
+ if (options.x !== undefined) {
+ self.x = options.x;
+ }
+ if (options.y !== undefined) {
+ self.y = options.y;
+ }
+ // Handle anchor options
+ if (options.anchorX !== undefined || options.anchorY !== undefined) {
+ var anchorX = options.anchorX !== undefined ? options.anchorX : 0;
+ var anchorY = options.anchorY !== undefined ? options.anchorY : 0;
+ // Apply anchor to all frames
+ self.frames.forEach(function (frame) {
+ frame.anchor.set(anchorX, anchorY);
+ });
+ }
+ // Add the first frame to display initially
+ if (self.frames.length > 0) {
+ self.removeChildren(); // zabezpieczenie
+ self.addChild(self.frames[self.currentFrame]);
+ }
+ // Animation update method
+ self.update = function () {
+ if (!self.playing || self.frames.length === 0) {
+ return;
+ }
+ self.frameTimer++;
+ if (self.frameTimer >= self.frameDuration / (1000 / 60)) {
+ self.frameTimer = 0;
+ if (self.children.length > 0) {
+ self.removeChild(self.children[0]); // Usuwamy tylko 1 aktualną klatkę
+ }
+ self.currentFrame++;
+ if (self.currentFrame >= self.frames.length) {
+ if (self.loop) {
+ self.currentFrame = 0;
+ } else {
+ self.currentFrame = self.frames.length - 1;
+ self.playing = false;
+ }
+ }
+ self.addChild(self.frames[self.currentFrame]); // Dodajemy nową klatkę
+ }
+ };
+ // Method to stop animation
+ self.stop = function () {
+ self.playing = false;
+ };
+ // Method to start/resume animation
+ self.play = function () {
+ self.playing = true;
+ };
+ // Method to set a specific frame
+ self.gotoFrame = function (frameIndex) {
+ if (frameIndex >= 0 && frameIndex < self.frames.length) {
+ self.removeChildren();
+ self.currentFrame = frameIndex;
+ self.addChild(self.frames[self.currentFrame]);
+ }
+ };
+ return self;
+});
+var UI = Container.expand(function () {
+ var self = Container.call(this);
+ // Create heart containers (wizualizacja zdrowia)
+ self.hearts = [];
+ self.heartContainer = new Container(); // Kontener na ikony serc
+ self.addChild(self.heartContainer);
+ // Title and messages (teksty na ekranach)
+ self.titleText = new Text2("ROLL SOULS", {
+ size: 150,
+ fill: 0xFFFFFF
+ });
+ self.titleText.anchor.set(0.5, 0.5);
+ self.addChild(self.titleText);
+ self.messageText = new Text2("", {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ self.messageText.anchor.set(0.5, 0.5);
+ self.addChild(self.messageText);
+ // Deaths counter (licznik śmierci)
+ self.deathsText = new Text2("Deaths: 0", {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ self.deathsText.anchor.set(1, 0); // Wyrównanie do prawego górnego rogu
+ self.deathsText.x = 2048 - 50; // Pozycja X od prawej krawędzi
+ self.deathsText.y = 50; // Pozycja Y od góry
+ self.addChild(self.deathsText);
+ // Tutorial text (teksty tutoriali)
+ self.tutorialText = new Text2("", {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ self.tutorialText.anchor.set(0.5, 0); // Wyrównanie do środka na górze
+ self.tutorialText.x = 2048 / 2; // Pozycja X na środku
+ self.tutorialText.y = 200; // Pozycja Y (może być różna w zależności od stanu)
+ self.addChild(self.tutorialText);
+ // --- Timer Text ---
+ self.timerText = new Text2("2:00", {
+ // Początkowe wyświetlanie czasu (2 minuty)
+ size: 60,
+ // Rozmiar czcionki
+ fill: 0xFFFFFF // Biały kolor
+ });
+ self.timerText.anchor.set(0, 0); // Punkt odniesienia w lewym górnym rogu
+ self.timerText.x = 50; // Pozycja X od lewej krawędzi
+ self.timerText.y = 50; // Pozycja Y od górnej krawędzi
+ self.timerText.alpha = 0; // Domyślnie ukryty
+ self.addChild(self.timerText);
+ // --- Boss Health Bar (wizualizacja zdrowia bossa) ---
+ self.bossHealthBarContainer = new Container(); // Kontener na pasek zdrowia bossa
+ self.bossHealthBarContainer.x = 2048 / 2; // Wyśrodkuj poziomo
+ self.bossHealthBarContainer.y = 150; // Pozycja Y (poniżej timera)
+ self.bossHealthBarContainer.alpha = 0; // Domyślnie ukryty
+ self.addChild(self.bossHealthBarContainer);
+ var barWidth = 800; // Szerokość paska zdrowia (musi być taka sama jak szerokość assetu bossHpbar)
+ var barHeight = 30; // Wysokość paska zdrowia (musi być taka sama jak wysokość assetu bossHpbar)
+ // Tło paska zdrowia bossa (szare) - nadal używamy Shape dla tła
+ self.bossHealthBarBg = new Shape({
+ width: barWidth,
+ height: barHeight,
+ color: 0x555555,
+ // Szary kolor tła
+ shape: 'box'
+ });
+ self.bossHealthBarBg.anchor.set(0.5, 0.5); // Ustaw punkt odniesienia na środek
+ self.bossHealthBarContainer.addChild(self.bossHealthBarBg);
+ // Właściwy pasek zdrowia bossa (czerwony) - UŻYWAMY TERAZ NOWEGO ASSETU bossHpbar
+ self.bossHealthBar = self.attachAsset('bossHpbar', {
+ // Użyj assetu 'bossHpbar'
+ anchorX: 0,
+ // Ustaw punkt odniesienia na lewą krawędź (0), środek pionowo (0.5)
+ anchorY: 0.5,
+ x: -barWidth / 2 // Przesuń w lewo o połowę szerokości tła, żeby lewa krawędź paska zdrowia była na środku kontenera (czyli na środku ekranu)
+ // Szerokość i wysokość są brane z assetu, nie ustawiamy ich tutaj jawnie (chyba że chcemy skalować)
+ });
+ self.bossHealthBarContainer.addChild(self.bossHealthBar);
+ // Aktualizuje wizualizację serc na UI
+ self.updateHearts = function (current, max) {
+ // Sprawdź poprawność danych wejściowych
+ current = Math.max(0, current || 0);
+ max = Math.max(1, max || 1); // Max musi być co najmniej 1
+ // Usuń istniejące serca tylko jeśli liczba się zmieniła lub nie ma serc
+ if (self.hearts.length !== max) {
+ while (self.hearts.length > 0) {
+ var oldHeart = self.hearts.pop();
+ if (oldHeart && oldHeart.destroy && !oldHeart.destroyed) {
+ oldHeart.destroy();
+ }
+ }
+ self.heartContainer.removeChildren(); // Usuń wizualne obiekty serc
+ // Stwórz nowe ikony serc
+ for (var i = 0; i < max; i++) {
+ var heart = LK.getAsset('heart', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: i * 50,
+ // Pozycja w poziomie (rozstawione co 50 jednostek)
+ y: 0,
+ tint: i < current ? 0xFF0000 : 0x555555 // Czerwone jeśli zdrowie jest >=, szare jeśli <
+ });
+ self.hearts.push(heart);
+ self.heartContainer.addChild(heart);
+ }
+ // Wyśrodkuj kontener serc w poziomie i ustaw pozycję pionową
+ self.heartContainer.x = (2048 - max * 50) / 2 + 25; // Dodano +25 aby wycentrować lepiej (bo anchorX = 0.5)
+ self.heartContainer.y = 100; // Pozycja pionowa
+ } else {
+ // Zaktualizuj tylko kolory istniejących serc
+ for (var j = 0; j < self.hearts.length; j++) {
+ if (self.hearts[j]) {
+ self.hearts[j].tint = j < current ? 0xFF0000 : 0x555555;
+ }
+ }
+ }
+ };
+ // Aktualizuje wizualizację paska zdrowia bossa
+ self.updateBossHealth = function (current, max) {
+ // Sprawdź poprawność danych
+ current = Math.max(0, current || 0);
+ max = Math.max(1, max || 1); // Max musi być co najmniej 1
+ // Oblicz szerokość paska zdrowia na podstawie aktualnego i maksymalnego zdrowia
+ var barWidth = 800; // Całkowita szerokość paska (musi być taka sama jak szerokość assetu bossHpbar)
+ var currentWidth = current / max * barWidth;
+ // Upewnij się, że pasek zdrowia (obiekt sprite) istnieje przed aktualizacją
+ if (self.bossHealthBar) {
+ // Zmień szerokość sprite'a, aby odzwierciedlić aktualne zdrowie
+ self.bossHealthBar.width = currentWidth;
+ // Ustaw widoczność kontenera paska zdrowia bossa
+ // Pokaż jeśli (stan gry lub game over) ORAZ (boss istnieje i żyje LUB jesteśmy w stanie game over z powodu czasu w Boss+)
+ var shouldBeVisible = (gameState.currentState === "game" || gameState.currentState === "gameOver") && (boss && !boss.dead && current > 0 || gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode);
+ self.bossHealthBarContainer.alpha = shouldBeVisible ? 1 : 0;
+ }
+ // Upewnij się, że pasek zdrowia tła też jest widoczny/ukryty tak samo jak kontener
+ if (self.bossHealthBarBg) {
+ self.bossHealthBarBg.alpha = self.bossHealthBarContainer.alpha;
+ }
+ };
+ // Wyświetla komunikat na środku ekranu
+ self.showMessage = function (message, duration) {
+ self.messageText.setText(message);
+ self.messageText.alpha = 1; // Ustaw pełną przezroczystość
+ // Wyczyść istniejący timer zanikania, jeśli istnieje
+ if (self.messageTimeout) {
+ LK.clearTimeout(self.messageTimeout);
+ self.messageTimeout = null;
+ }
+ // Zaplanuj zanikanie po czasie, jeśli duration > 0
+ if (duration && duration > 0) {
+ self.messageTimeout = LK.setTimeout(function () {
+ tween(self.messageText, {
+ alpha: 0 // Zaniknij do przezroczystości 0
+ }, {
+ duration: 500,
+ // Czas trwania zanikania
+ onFinish: function onFinish() {
+ self.messageTimeout = null;
+ } // Reset ID
+ });
+ }, duration);
+ } else {
+ // If duration is 0 or not provided, message stays visible
+ self.messageText.alpha = 1;
+ }
+ };
+ // Wyświetla tekst tutorialu
+ self.showTutorial = function (text) {
+ self.tutorialText.setText(text);
+ self.tutorialText.alpha = 1; // Ustaw pełną przezroczystość
+ };
+ // Ukrywa tekst tutorialu (zanikając)
+ self.hideTutorial = function () {
+ tween(self.tutorialText, {
+ alpha: 0 // Zaniknij
+ }, {
+ duration: 500 // Czas trwania
+ });
+ };
+ // Aktualizuje licznik śmierci
+ self.updateDeathsCounter = function () {
+ // Upewnij się, że storage.totalDeaths jest liczbą
+ var deaths = storage.totalDeaths || 0;
+ self.deathsText.setText("Deaths: " + deaths);
+ };
+ // Aktualizuje wyświetlanie czasu timera
+ self.updateTimerDisplay = function (seconds) {
+ // Upewnij się, że sekundy są liczbą >= 0
+ seconds = Math.max(0, seconds || 0);
+ var minutes = Math.floor(seconds / 60);
+ var remainingSeconds = seconds % 60;
+ // Formatuj sekundy z wiodącym zerem jeśli < 10
+ var formattedSeconds = remainingSeconds < 10 ? '0' + remainingSeconds : remainingSeconds;
+ self.timerText.setText(minutes + ':' + formattedSeconds);
+ };
+ // Pozycjonuje elementy UI w zależności od stanu gry
+ // Ta funkcja jest wywoływana przez metody w gameState
+ self.positionElements = function (state) {
+ // Pozycje stałe (niezależne od stanu)
+ self.deathsText.x = 2048 - 50;
+ self.deathsText.y = 50;
+ self.timerText.x = 50;
+ self.timerText.y = 50;
+ self.heartContainer.y = 100; // Pozycja pionowa serc
+ self.bossHealthBarContainer.x = 2048 / 2;
+ self.bossHealthBarContainer.y = 150; // Pasek HP bossa poniżej timera/serc
+ // Resetuj widoczność przed ustawieniem dla danego stanu
+ self.titleText.alpha = 0;
+ self.messageText.alpha = 0;
+ self.tutorialText.alpha = 0;
+ self.timerText.alpha = 0;
+ self.heartContainer.alpha = 0;
+ self.bossHealthBarContainer.alpha = 0; // Zostanie włączony przez updateBossHealth jeśli trzeba
+ self.deathsText.alpha = 1; // Licznik śmierci zawsze widoczny
+ switch (state) {
+ case "title":
+ // Pozycje dla ekranu tytułowego
+ self.titleText.x = 2048 / 2;
+ self.titleText.y = 800;
+ self.titleText.alpha = 1; // Pokaż tytuł
+ self.messageText.x = 2048 / 2;
+ self.messageText.y = 1000; // "Tap to Start" (ustawiane przez showMessage)
+ // self.messageText.alpha = 1; // Ustawiane przez showMessage
+ self.tutorialText.x = 2048 / 2;
+ self.tutorialText.y = 1200; // "Swipe to Roll..." (ustawiane przez showTutorial)
+ // self.tutorialText.alpha = 1; // Ustawiane przez showTutorial
+ break;
+ case "game":
+ // Pozycje dla stanu gry (walka z bossem)
+ // self.titleText.alpha = 0; // Ustawione domyślnie
+ self.messageText.x = 2048 / 2;
+ self.messageText.y = 1500; // Komunikaty w trakcie gry (np. przyspieszenie bossa)
+ // self.messageText.alpha = 0; // Ustawiane przez showMessage
+ self.tutorialText.x = 2048 / 2;
+ self.tutorialText.y = 200; // Tutorial w trakcie gry ("Swipe to roll away!")
+ // self.tutorialText.alpha = 1; // Ustawiane przez showTutorial/hideTutorial
+ // Pokaż serca i timer podczas gry
+ self.heartContainer.alpha = 1;
+ self.timerText.alpha = 1;
+ // Pasek zdrowia bossa jest zarządzany przez updateBossHealth
+ break;
+ case "grillMenu":
+ // Nowy stan dla Grill Screena
+ // self.titleText.alpha = 0; // Ustawione domyślnie
+ self.messageText.x = 2048 / 2;
+ self.messageText.y = 500; // Pozycja dla komunikatów "Rest in peace..."
+ // self.messageText.alpha = 0; // Ustawiane przez showMessage
+ // self.tutorialText.alpha = 0; // Ustawione domyślnie
+ break;
+ case "gameOver":
+ // Pozycje dla ekranu Game Over
+ self.titleText.x = 2048 / 2;
+ self.titleText.y = 800; // Tytuł "YOU DIED" lub komunikat zwycięstwa Boss+
+ // self.titleText.alpha = 1; // Ustawiane w gameState.gameOver
+ self.messageText.x = 2048 / 2;
+ self.messageText.y = 1000; // Pusty lub inny komunikat
+ // self.messageText.alpha = 0; // Ustawiane w gameState.gameOver przez showMessage
+ self.tutorialText.x = 2048 / 2;
+ self.tutorialText.y = 1200; // Pusty
+ // self.tutorialText.alpha = 0; // Ustawiane w gameState.gameOver przez showTutorial
+ // Widoczność paska HP bossa zarządzana przez updateBossHealth
+ break;
+ case "intro": // Pozycje dla ekranów intro/tutoriali
+ case "fakeTutorial":
+ case "realTutorial":
+ // Większość elementów ukryta (ustawione domyślnie wyżej)
+ break;
+ }
+ };
+ return self;
+});
+
/****
-* Game creation and loop
+* Initialize Game
****/
-// Global variables for game objects
-// Already defined at the beginning: var player, boss, ui, game, gameContainer, etc.
-var createGame = function createGame() {
- // Upewnij się, że poprzednia instancja gry jest posprzątana
- if (game) {
- game.destroy();
- game = null;
+var game = new LK.Game({
+ backgroundColor: 0x111111 // Ciemne tło domyślne
+});
+
+/****
+* Game Code
+****/
+// Globalny kontener na elementy scen intro/tutoriali i Grill Screena (do łatwego czyszczenia)
+// Komentarze o assetach pominięte dla zwięzłości
+var currentSceneElements = new Container();
+// Zmienne gry
+var player;
+var boss;
+var ui;
+var walls = []; // Ściany areny bossa
+// Zmienna do przechowywania aktywnego tła sceny
+var currentBackground = null;
+// Flaga do śledzenia, czy jesteśmy w trybie New Boss+
+var isNewBossPlusMode = false;
+// Flaga do śledzenia przyczyny Game Over (true = gracz zginął, false = czas minął)
+var gameOverReasonIsDeath = false;
+// Funkcja do czyszczenia elementów z kontenera currentSceneElements (nie usuwa UI ani tła)
+function clearScene() {
+ while (currentSceneElements.children.length > 0) {
+ var child = currentSceneElements.children[0];
+ if (child && child.destroy) {
+ child.destroy(); // Użyj destroy jeśli dostępne
+ } else if (child && child.parent) {
+ child.parent.removeChild(child); // Fallback
+ } else {
+ // Jeśli obiekt nie ma ani destroy ani parent, usuń go z tablicy (choć to nie powinno się zdarzyć)
+ currentSceneElements.children.shift();
+ }
}
- game = new DisplayObjectContainer(); // Stwórz główny kontener gry
- LK.add(game); // Dodaj kontener gry do frameworka LK
- // Stwórz i dodaj podstawowe elementy tła od razu (nie zależą od assetów fireballi)
- gameState.createWalls(); // Tworzy i dodaje ściany
- gameState.createFloor(); // Tworzy i dodaje podłogę
- // *** MODYFIKACJA: Dodaj opóźnienie setTimeout przed tworzeniem elementów zależnych od assetów ***
- // Daje to assetom czas na załadowanie po odpaleniu onInit przez LK.init
- LK.setTimeout(function () {
- // *** Kod z oryginalnego createGame, który tworzył elementy zależne od assetów, PRZENIESIONY tutaj ***
- // Stwórz i dodaj gracza (zależy od assetów)
- player = new Player(); // Klasa Player prawdopodobnie używa assetów
- game.addChild(player); // game.addChild should work here.
- // Stwórz i dodaj bossa (mocno zależy od assetów dla grafiki i ataków)
- boss = new Boss(); // Klasa Boss używa wielu assetów
- game.addChild(boss); // game.addChild should work here.
- // Stwórz i dodaj elementy UI (niektóre elementy UI mogą używać assetów)
- ui = new UI(); // Klasa UI prawdopodobnie używa assetów (serca, przyciski, itp.)
- game.addChild(ui); // game.addChild should work here.
- // Add UI elements to the game container (order matters for layering)
- // Assuming UI should be on top of game elements
- game.addChild(ui.bossHpBar); // game.addChild should work here.
- game.addChild(ui.playerHearts); // game.addChild should work here.
- game.addChild(ui.startButton); // Start button is visible in title state // game.addChild should work here.
- game.addChild(ui.newBossPlusButton); // Boss+ button is visible in title state // game.addChild should work here.
- game.addChild(ui.gameOverMessage); // Game over message is hidden initially // game.addChild should work here.
- game.addChild(ui.grillMenu); // Grill menu is hidden initially // game.addChild should work here.
- // Start the game state (enables updates, attacks, etc.)
- // This was the last line in the original createGame
- gameState.changeState("title"); // Rozpocznij grę w stanie title
- console.log("Opóźnione kroki tworzenia gry wykonane."); // Opcjonalny log do potwierdzenia odpalenia timeoutu
- }, 500); // *** CZAS OPÓŹNIENIA (np. 500ms) - Dostosuj w razie potrzeby ***
- // To jest opóźnienie heurystyczne. Lepszym rozwiązaniem byłby właściwy mechanizm preładowania, jeśli dostępny.
- // Oryginalne linie kodu po ustawieniach createGame i przed gameState.changeState są teraz wewnątrz timeoutu
- // gameState.changeState("title"); // This line is moved inside the timeout
-};
-var gameLoop = function gameLoop() {
- // Główne aktualizacje gry
- // LK automatycznie wywołuje update na obiektach dodanych do sceny (game)
- // Możemy dodać tutaj logikę globalną
- // Na przykład, aktualizacja UI, która nie jest automatycznie wywoływana na obiektach UI, chyba że UI jest DisplayObjectContainer i dodano je do sceny.
- // Aktualizuj UI niezależnie od stanu gry, bo jego widoczność i zawartość zależą od stanu/danych
- // Ale tylko jeśli UI zostało stworzone
- if (ui) {
- // UI update logic if needed (currently empty)
- // ui.update(); // Zakładając, że UI ma metodę update
- // Aktualizuj pasek zdrowia bossa (jeśli boss istnieje)
- if (boss) {
- // ui.updateBossHealth(boss.health, boss.maxHealth); // Ta linia była wcześniej, ale lepiej wywoływać ją po zmianie zdrowia bossa
- // Zaktualizuj UI HP Bossa w game.update, ale tylko gdy HP bossa > 0 (żeby nie migało przy śmierci)
- // UI HP Boss aktualizowane jest teraz w takeDamage i die Bossa.
- // Upewnijmy się, że jest aktualizowane też jeśli boss HP > 0.
- if (boss.health > 0 || gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode) {
- ui.updateBossHealth(boss.health, boss.maxHealth);
+ // Usuń też dzieci bezpośrednio z 'game', które mogły zostać dodane poza 'currentSceneElements' w scenach,
+ // ale UWAŻAJ, aby nie usunąć stałych elementów jak UI, walls, currentSceneElements sam w sobie.
+ // Lepsze podejście: zawsze dodawaj elementy specyficzne dla sceny do currentSceneElements.
+}
+// Obiekt zarządzający stanami gry
+var gameState = {
+ currentState: "title",
+ // Początkowy stan gry
+ gameDuration: 120,
+ // Domyślny czas walki z bossem w sekundach (2 minuty)
+ remainingTime: 0,
+ // Pozostały czas
+ gameTimerInterval: null,
+ // ID interwału timera
+ fakeTutorialTimerId: null,
+ // ID timera przechodzącego do prawdziwego tutorialu
+ bossSpeedIncreased: false,
+ // Flaga przyspieszenia bossa
+ touchStart: {
+ x: 0,
+ y: 0
+ },
+ // Początek gestu
+ touchEnd: {
+ x: 0,
+ y: 0
+ },
+ // Koniec gestu
+ // Inicjalizacja gry (wywoływana raz na początku)
+ init: function init() {
+ // Resetuj stan przy każdym uruchomieniu
+ storage.totalDeaths = 0;
+ storage.maxHearts = 5; // Zaczynaj zawsze z 5 sercami
+ isNewBossPlusMode = false;
+ gameOverReasonIsDeath = false;
+ game.setBackgroundColor(0x111111); // Ustaw domyślny kolor tła
+ this.createWalls(); // Stwórz ściany areny
+ ui = game.addChild(new UI()); // Stwórz UI
+ // Zainicjalizuj UI dla ekranu tytułowego
+ ui.updateDeathsCounter();
+ ui.updateHearts(storage.maxHearts, storage.maxHearts); // Pokaż początkowe serca
+ ui.positionElements("title"); // Pozycjonuj elementy UI dla tytułu
+ game.addChild(currentSceneElements); // Dodaj kontener na elementy scen
+ // Rozpocznij muzykę w tle
+ LK.playMusic('bgMusic', {
+ fade: {
+ start: 0,
+ end: 0.3,
+ duration: 1000
+ }
+ });
+ this.showTitleScreen(); // Rozpocznij od ekranu tytułowego
+ },
+ // Tworzy/odświeża ściany areny bossa
+ createWalls: function createWalls() {
+ walls.forEach(function (wall) {
+ if (wall && wall.destroy) {
+ wall.destroy();
+ }
+ });
+ walls = [];
+ // Użyj getAsset zamiast new Shape dla ścian, jeśli 'wall' i 'floor' to assety
+ var leftWall = game.addChild(LK.getAsset('wall', {
+ anchorX: 0,
+ anchorY: 0,
+ x: 0,
+ y: 0
+ }));
+ walls.push(leftWall);
+ var rightWall = game.addChild(LK.getAsset('wall', {
+ anchorX: 1,
+ anchorY: 0,
+ x: 2048,
+ y: 0
+ })); // AnchorX=1 dla prawej ściany
+ rightWall.x = 2048; // Poprawka pozycji prawej ściany
+ walls.push(rightWall);
+ var topWall = game.addChild(LK.getAsset('floor', {
+ anchorX: 0,
+ anchorY: 0,
+ x: 0,
+ y: 0
+ }));
+ walls.push(topWall);
+ var bottomWall = game.addChild(LK.getAsset('floor', {
+ anchorX: 0,
+ anchorY: 1,
+ x: 0,
+ y: 2732
+ })); // AnchorY=1 dla dolnej ściany
+ bottomWall.y = 2732; // Poprawka pozycji dolnej ściany
+ walls.push(bottomWall);
+ // Ustaw ściany na spodzie (niski zIndex)
+ walls.forEach(function (wall) {
+ game.setChildIndex(wall, 0);
+ });
+ },
+ // ---------- Metody przejścia między stanami ----------
+ showTitleScreen: function showTitleScreen() {
+ // Zatrzymaj timery poprzedniego stanu
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ }
+ this.fakeTutorialTimerId = null;
+ if (this.gameTimerInterval) {
+ LK.clearInterval(this.gameTimerInterval);
+ }
+ this.gameTimerInterval = null;
+ clearScene(); // Wyczyść elementy poprzedniej sceny (Grill, GameOver, itp.)
+ // Usuń stare tło, jeśli istnieje
+ if (currentBackground) {
+ tween.stop(currentBackground); // Zatrzymaj animacje tła
+ currentBackground.destroy();
+ currentBackground = null;
+ }
+ this.currentState = "title";
+ game.setBackgroundColor(0x1a1a1a); // Ciemne tło bazowe dla tytułu
+ // Dodaj tło ekranu tytułowego
+ currentBackground = LK.getAsset('titleBg', {
+ anchorX: 0,
+ anchorY: 0,
+ x: 0,
+ y: 0
+ });
+ game.addChildAt(currentBackground, 0); // Dodaj na spód
+ // Ukryj gracza/bossa jeśli istnieją
+ if (player && player.destroy) {
+ player.destroy();
+ }
+ player = null;
+ if (boss && boss.destroy) {
+ boss.destroy();
+ }
+ boss = null;
+ // Pokaż ściany (jako część tła menu)
+ walls.forEach(function (wall) {
+ if (wall) {
+ wall.alpha = 1;
+ }
+ });
+ // Ustaw UI dla tytułu
+ ui.positionElements("title");
+ ui.titleText.setText("Welcome Unchosen");
+ ui.showMessage("Tap to Start", 0);
+ ui.showTutorial("Swipe to Roll - Death is Progress");
+ // Resetuj stan gestu
+ this.touchStart = {
+ x: 0,
+ y: 0
+ };
+ this.touchEnd = {
+ x: 0,
+ y: 0
+ };
+ // Przycisk testowy do Grill Menu (jeśli nadal potrzebny)
+ var grillMenuTestButton = new Container();
+ grillMenuTestButton.interactive = true;
+ grillMenuTestButton.x = 2048 / 2;
+ grillMenuTestButton.y = 1600;
+ currentSceneElements.addChild(grillMenuTestButton);
+ var grillButtonBg = LK.getAsset('button_bg', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ grillMenuTestButton.addChild(grillButtonBg);
+ var grillButtonText = new Text2('Go to Grill Menu', {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ grillButtonText.anchor.set(0.5, 0.5);
+ grillMenuTestButton.addChild(grillButtonText);
+ grillMenuTestButton.down = function () {
+ gameState.showGrillScreen();
+ };
+ // --- Tap anywhere on title screen to start the intro ---
+ game.on('down', function () {
+ if (gameState.currentState === 'title') {
+ gameState.showIntro();
+ }
+ });
+ },
+ showIntro: function showIntro() {
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ }
+ this.fakeTutorialTimerId = null; // Wyczyść timer
+ clearScene(); // Wyczyść elementy tytułu
+ if (currentBackground) {
+ tween.stop(currentBackground);
+ currentBackground.destroy();
+ currentBackground = null;
+ } // Usuń stare tło
+ this.currentState = "intro";
+ game.setBackgroundColor(0x111111);
+ // Dodaj tło intro i animację zoom
+ currentBackground = LK.getAsset('introBg', {
+ anchorX: 0.5,
+ anchorY: 0.4,
+ x: 1000,
+ y: 1000,
+ scaleX: 1,
+ scaleY: 1
+ });
+ game.addChildAt(currentBackground, 0);
+ tween(currentBackground, {
+ scaleX: 1.4,
+ scaleY: 1.4
+ }, {
+ duration: 22000,
+ easing: tween.linear,
+ repeat: Infinity,
+ yoyo: true
+ });
+ // Ukryj ściany na czas intro
+ if (player && player.destroy) {
+ player.destroy();
+ }
+ player = null; // Upewnij się, że nie ma gracza/bossa
+ if (boss && boss.destroy) {
+ boss.destroy();
+ }
+ boss = null;
+ walls.forEach(function (wall) {
+ if (wall) {
+ wall.alpha = 0;
+ }
+ }); // Ukryj ściany
+ ui.positionElements("intro"); // Ustaw UI dla intro (głównie ukrywa elementy)
+ // --- Teksty intro z timerami ---
+ // Funkcja do wyświetlania tekstu z fade-in, fade-out i kontynuacją
+ function showIntroText(text, delay, onComplete) {
+ var introText = new Text2(text, {
+ size: 90,
+ fill: 0xFFFFFF,
+ align: 'center',
+ wordWrap: true,
+ wordWrapWidth: 1800
+ });
+ introText.anchor.set(0.5, 0.5);
+ introText.x = 2048 / 2;
+ introText.y = 2232 / 2 - 400;
+ introText.alpha = 0;
+ currentSceneElements.addChild(introText);
+ LK.setTimeout(function () {
+ tween(introText, {
+ alpha: 1
+ }, {
+ duration: 1000,
+ easing: tween.easeInOut
+ });
+ LK.setTimeout(function () {
+ tween(introText, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ easing: tween.easeInOut
+ });
+ LK.setTimeout(function () {
+ try {
+ if (introText && introText.destroy) {
+ introText.destroy();
+ }
+ } catch (e) {}
+ if (onComplete) {
+ onComplete();
+ }
+ }, 1000); // czas na fade-out
+ }, 3000); // czas wyświetlania
+ }, delay);
+ }
+ // --- Startujemy sekwencję intro ---
+ showIntroText('From the creators of FromSoftware...', 2000, function () {
+ showIntroText('...I have no idea. I don’t know them.', 0, function () {
+ showIntroText('Silas GameStudio...', 0, function () {
+ showIntroText('Still looking for funding.', 0, function () {
+ showIntroText('Oh... and I don’t even exist.', 0, function () {
+ // <-- NOWY TEKST
+ // Po ostatnim tekście przechodzimy do howToScene
+ LK.setTimeout(function () {
+ if (typeof gameState.showFakeTutorial === 'function') {
+ gameState.showFakeTutorial();
+ } else {
+ console.error("Error: gameState.showFakeTutorial is not defined");
+ gameState.showTitleScreen(); // Fallback to title screen
+ }
+ }, 1000);
+ });
+ });
+ });
+ });
+ });
+ },
+ showFakeTutorial: function showFakeTutorial() {
+ clearScene(); // Wyczyść tekst intro
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ }
+ this.fakeTutorialTimerId = null; // Wyczyść stary timer
+ this.currentState = "fakeTutorial";
+ // Zachowaj tło intro? Jeśli tak, usuń game.setBackgroundColor i nie niszcz currentBackground
+ // game.setBackgroundColor(0x000000); // Czarne tło - usunięte, aby zachować tło intro
+ ui.positionElements("fakeTutorial"); // Ustaw UI
+ // Tekst fałszywego tutorialu
+ var instructionText = new Text2('Press LPM to block.. or don’t press.', {
+ size: 100,
+ fill: 0xFFFFFF,
+ align: 'center',
+ wordWrap: true,
+ wordWrapWidth: 1800,
+ dropShadow: true,
+ dropShadowColor: 0x000000,
+ // czarny cień
+ dropShadowDistance: 3,
+ // odległość cienia
+ dropShadowBlur: 4,
+ // lekkie rozmycie cienia
+ dropShadowAngle: Math.PI / 4 // kąt cienia
+ });
+ instructionText.anchor.set(0.5, 0.5);
+ instructionText.x = 2048 / 2;
+ instructionText.y = 2732 / 2;
+ currentSceneElements.addChild(instructionText);
+ // Timer przechodzący do prawdziwego tutorialu
+ this.fakeTutorialTimerId = LK.setTimeout(function () {
+ try {
+ if (instructionText && instructionText.destroy) {
+ instructionText.destroy();
+ }
+ } catch (e) {}
+ gameState.fakeTutorialTimerId = null; // Zresetuj ID
+ // *** POPRAWKA: Sprawdzenie przed wywołaniem ***
+ if (gameState && typeof gameState.showRealTutorial === 'function') {
+ gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu
} else {
- // Jeśli boss martwy i nie jest to game over po czasie, ukryj pasek
- if (!(gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode)) {
- ui.updateBossHealth(0, 1); // Ukryj pasek
+ console.error("Error: gameState.showRealTutorial is not a function in fakeTutorial timeout!");
+ gameState.showTitleScreen(); // Awaryjny powrót do tytułu
+ }
+ } /*.bind(this)*/, 6000); // 6 sekund
+ },
+ // *** NOWA FUNKCJA: Prawdziwy Tutorial (oddzielona od startGame) ***
+ showRealTutorial: function showRealTutorial() {
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ }
+ this.fakeTutorialTimerId = null; // Wyczyść timer fake tutorialu
+ clearScene(); // Wyczyść elementy fake tutorialu/fake game over
+ // Zatrzymaj animację tła intro i usuń je
+ if (currentBackground) {
+ tween.stop(currentBackground);
+ currentBackground.destroy();
+ currentBackground = null;
+ }
+ this.currentState = "realTutorial";
+ game.setBackgroundColor(0x111111); // Ciemne tło
+ // Pokaż ściany areny
+ walls.forEach(function (wall) {
+ if (wall) {
+ wall.alpha = 1;
+ }
+ });
+ ui.positionElements("realTutorial"); // Ustaw UI (głównie ukrywa elementy gry)
+ // Tekst prawdziwego tutorialu
+ var tutorialTitle = new Text2('HOW TO PLAY', {
+ size: 100,
+ fill: 0xFFFFFF
+ });
+ tutorialTitle.anchor.set(0.5, 0.5);
+ tutorialTitle.x = 2048 / 2;
+ tutorialTitle.y = 600;
+ currentSceneElements.addChild(tutorialTitle);
+ var tutorialDesc = new Text2('Swipe the screen to ROLL.\nRoll THROUGH the boss attacks to deal damage.\nSurvive!', {
+ size: 60,
+ fill: 0xFFFFFF,
+ align: 'center',
+ wordWrap: true,
+ wordWrapWidth: 1600
+ });
+ tutorialDesc.anchor.set(0.5, 0.5);
+ tutorialDesc.x = 2048 / 2;
+ tutorialDesc.y = 1000;
+ currentSceneElements.addChild(tutorialDesc);
+ // Przycisk "Let's Roll!" do rozpoczęcia gry
+ var startButton = new Container();
+ startButton.interactive = true;
+ startButton.x = 2048 / 2;
+ startButton.y = 1500;
+ currentSceneElements.addChild(startButton);
+ var startButtonBg = LK.getAsset('button_bg', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ startButton.addChild(startButtonBg);
+ var startButtonText = new Text2("Let's Roll!", {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ startButtonText.anchor.set(0.5, 0.5);
+ startButton.addChild(startButtonText);
+ startButton.down = function () {
+ // clearScene(); // Niekonieczne, startGame to zrobi
+ gameState.startGame(); // Rozpocznij grę
+ };
+ },
+ // Obsługa inputu w stanie fakeTutorial (fałszywa śmierć)
+ handleFakeTutorialInput: function handleFakeTutorialInput() {
+ // Wyczyść timer, który miał prowadzić do prawdziwego tutorialu
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ this.fakeTutorialTimerId = null; // Zresetuj ID timera
+ }
+ clearScene(); // Wyczyść ekran fałszywego tutorialu
+ // Ustaw stan tymczasowo (można by dodać dedykowany stan 'fakeGameOver')
+ this.currentState = "fakeGameOver"; // Zmieniono z "gameOver"
+ // Zatrzymaj animację tła intro, ale nie usuwaj go jeszcze
+ if (currentBackground) {
+ tween.stop(currentBackground);
+ }
+ ui.positionElements("gameOver"); // Użyj pozycjonowania gameOver dla tekstów "YOU DIED"
+ // Wyświetl "YOU DIED" na czerwono
+ var diedText = new Text2("YOU DIED", {
+ size: 150,
+ fill: 0xFF0000
+ }); // Czerwony kolor
+ diedText.anchor.set(0.5, 0.5);
+ diedText.x = 2048 / 2;
+ diedText.y = 600;
+ currentSceneElements.addChild(diedText);
+ // Wyświetl wyjaśnienie
+ var explanationText = new Text2("Did you check the title of the game?", {
+ size: 70,
+ fill: 0xFFFFFF,
+ align: 'center',
+ wordWrap: true,
+ wordWrapWidth: 1800,
+ dropShadow: true,
+ dropShadowColor: 0x000000,
+ // cień czarny
+ dropShadowDistance: 3,
+ // odległość cienia
+ dropShadowBlur: 4,
+ // lekkie rozmycie cienia
+ dropShadowAngle: Math.PI / 4 // kąt padania cienia (45 stopni)
+ });
+ explanationText.anchor.set(0.5, 0.5);
+ explanationText.x = 2048 / 2;
+ explanationText.y = 800;
+ currentSceneElements.addChild(explanationText);
+ // Dodaj przycisk "How to play again"
+ var howToPlayButtonContainer = new Container();
+ howToPlayButtonContainer.interactive = true;
+ howToPlayButtonContainer.x = 2048 / 2;
+ howToPlayButtonContainer.y = 1300; // Pozycja przycisku
+ currentSceneElements.addChild(howToPlayButtonContainer);
+ var buttonBg = LK.getAsset('button_bg', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ howToPlayButtonContainer.addChild(buttonBg);
+ var buttonText = new Text2('How to play', {
+ size: 50,
+ fill: 0xFFFFFF
+ }); // Zmieniono tekst i rozmiar
+ buttonText.anchor.set(0.5, 0.5);
+ howToPlayButtonContainer.addChild(buttonText);
+ // Akcja przycisku: Przejdź do prawdziwego tutorialu
+ howToPlayButtonContainer.down = function () {
+ // *** POPRAWKA: Sprawdzenie przed wywołaniem ***
+ if (gameState && typeof gameState.showRealTutorial === 'function') {
+ gameState.showRealTutorial(); // Przejdź do prawdziwego tutorialu
+ } else {
+ console.error("Error: gameState.showRealTutorial is not a function in fake input handler!");
+ gameState.showTitleScreen(); // Awaryjny powrót do tytułu
+ }
+ };
+ },
+ // Przejście do stanu gry (walka z bossem) - wywoływane z Prawdziwego Tutorialu
+ startGame: function startGame() {
+ // Wyczyść timery z poprzednich stanów
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ }
+ this.fakeTutorialTimerId = null;
+ if (this.gameTimerInterval) {
+ LK.clearInterval(this.gameTimerInterval);
+ }
+ this.gameTimerInterval = null;
+ clearScene(); // Wyczyść elementy tutorialu
+ // Usuń tło tutorialu/intro jeśli istnieje
+ if (currentBackground) {
+ tween.stop(currentBackground);
+ currentBackground.destroy();
+ currentBackground = null;
+ }
+ this.currentState = "game"; // Ustaw stan na "game"
+ game.setBackgroundColor(0x111111);
+ // Stwórz gracza (jeśli nie istnieje)
+ if (player && player.destroy) {
+ player.destroy();
+ } // Zniszcz starego gracza
+ player = game.addChild(new Player()); // Stwórz nowego gracza
+ player.health = storage.maxHearts || 5; // Ustaw zdrowie gracza
+ // Resetuj stan gracza
+ player.rolling = false;
+ player.invulnerable = false;
+ player.invulnerabilityFrames = 0;
+ player.rollCooldown = 0;
+ if (player && typeof player.clearRollTimeouts === 'function') {
+ player.clearRollTimeouts();
+ }
+ player.hasRolledThroughBossThisRoll = false;
+ player.x = 2048 / 2;
+ player.y = 2732 / 2 + 400; // Pozycja startowa gracza
+ player.alpha = 1; // Upewnij się, że jest widoczny
+ // Stwórz bossa (jeśli nie istnieje)
+ if (boss && boss.destroy) {
+ boss.destroy();
+ } // Zniszcz starego bossa
+ boss = game.addChild(new Boss()); // Stwórz nowego bossa
+ boss.x = 2048 / 2;
+ boss.y = 2732 / 2 - 400; // Pozycja startowa bossa
+ boss.alpha = 1; // Upewnij się, że jest widoczny
+ // Resetuj stan bossa
+ boss.attackCooldown = 90; // Daj graczowi chwilę przed pierwszym atakiem (ok 1.5s)
+ boss.attacks = [];
+ boss.attackSpeedMultiplier = 1;
+ boss.repositioning = false;
+ boss.dead = false; // Upewnij się, że nie jest martwy
+ boss.phase = 1; // Zacznij od fazy 1
+ boss.tint = 0xFFFFFF; // Resetuj kolor (na wypadek przejścia fazy w poprzedniej grze)
+ boss.scale.set(1, 1); // Resetuj skalę (na wypadek animacji śmierci)
+ // Ustaw parametry bossa i gry zależnie od trybu
+ if (isNewBossPlusMode) {
+ boss.maxHealth = 2000;
+ boss.health = boss.maxHealth;
+ this.gameDuration = 600; // 10 minut
+ boss.attackSpeedMultiplier = 0.6; // Szybsze ataki od początku
+ boss.speed = 7; // Szybszy ruch
+ console.log("Starting NEW BOSS+ mode. HP:", boss.maxHealth, "Time:", this.gameDuration);
+ } else {
+ boss.maxHealth = 200; // Standardowy boss HP
+ boss.health = boss.maxHealth;
+ this.gameDuration = 120; // 2 minuty
+ boss.speed = 5; // Standardowa prędkość
+ boss.attackSpeedMultiplier = 1; // Standardowa prędkość ataków
+ console.log("Starting STANDARD mode. HP:", boss.maxHealth, "Time:", this.gameDuration);
+ }
+ // Pokaż ściany areny
+ walls.forEach(function (wall) {
+ if (wall) {
+ wall.alpha = 1;
+ }
+ });
+ // Ustaw UI dla stanu gry
+ ui.positionElements("game");
+ ui.updateHearts(player.health, storage.maxHearts);
+ ui.showTutorial("Swipe to Roll!"); // Wyświetl krótki tutorial
+ ui.updateBossHealth(boss.health, boss.maxHealth); // Pokaż pasek HP bossa
+ // --- Timer walki z bossem ---
+ this.remainingTime = this.gameDuration; // Ustaw początkowy czas
+ ui.updateTimerDisplay(this.remainingTime); // Wyświetl czas
+ // Rozpocznij interwał timera
+ if (this.gameTimerInterval) {
+ LK.clearInterval(this.gameTimerInterval);
+ } // Wyczyść stary timer
+ this.bossSpeedIncreased = false; // Resetuj flagę przyspieszenia bossa
+ this.gameTimerInterval = LK.setInterval(function () {
+ // Aktualizuj tylko w stanie gry
+ if (gameState.currentState === "game") {
+ gameState.remainingTime--;
+ ui.updateTimerDisplay(gameState.remainingTime);
+ // --- Przyspieszenie bossa - TYLKO W STANDARDOWYM TRYBIE ---
+ if (!isNewBossPlusMode) {
+ var accelerationThreshold = gameState.gameDuration - 60; // Przyspieszenie na 60s przed końcem
+ if (gameState.remainingTime <= accelerationThreshold && !gameState.bossSpeedIncreased) {
+ gameState.bossSpeedIncreased = true;
+ if (boss && !boss.dead) {
+ // Sprawdź czy boss nadal żyje
+ boss.attackSpeedMultiplier *= 0.7; // Przyspiesz ataki
+ boss.speed += 1; // Lekko przyspiesz ruch
+ ui.showMessage("Boss attacks faster!", 2000); // Komunikat
+ }
+ }
}
+ // --- Koniec Przyspieszenia bossa ---
+ // Sprawdź koniec gry (czas minął)
+ if (gameState.remainingTime <= 0) {
+ LK.clearInterval(gameState.gameTimerInterval); // Zatrzymaj timer
+ gameState.gameTimerInterval = null;
+ if (isNewBossPlusMode) {
+ // Czas minął w Boss+ -> Zwycięstwo przez przetrwanie
+ gameOverReasonIsDeath = false; // Przyczyna: czas
+ gameState.gameOver(false); // Przejdź do ekranu końca gry (specjalny komunikat)
+ } else {
+ // Czas minął w standardowym trybie -> Przejdź do Grill Menu
+ // Zakładamy, że przetrwanie 2 minut w standardowym trybie to też "zwycięstwo"
+ gameState.showGrillScreen();
+ }
+ }
+ } else {
+ // Jeśli stan gry się zmienił, zatrzymaj timer
+ if (gameState.gameTimerInterval) {
+ LK.clearInterval(gameState.gameTimerInterval);
+ }
+ gameState.gameTimerInterval = null;
}
+ } /*.bind(this)*/, 1000); // Interwał co 1 sekundę
+ // Ukryj tutorial po chwili
+ LK.setTimeout(function () {
+ if (gameState.currentState === "game") {
+ // Sprawdź czy nadal jesteśmy w grze
+ ui.hideTutorial();
+ }
+ // Rozpoczęcie ataków bossa jest teraz obsługiwane przez Boss.update i jego cooldown
+ }, 3000); // 3 sekundy opóźnienia
+ },
+ // victory() - nieużywane, logika w timerze i boss.die()
+ showGrillScreen: function showGrillScreen() {
+ // Wyczyść timery
+ if (this.gameTimerInterval) {
+ LK.clearInterval(this.gameTimerInterval);
+ }
+ this.gameTimerInterval = null;
+ if (this.fakeTutorialTimerId) {
+ LK.clearTimeout(this.fakeTutorialTimerId);
+ }
+ this.fakeTutorialTimerId = null;
+ this.bossSpeedIncreased = false; // Reset flagi
+ clearScene(); // Wyczyść elementy gry (ataki, itp.)
+ // Zniszcz gracza i bossa jeśli jeszcze istnieją
+ if (player && player.destroy) {
+ player.destroy();
+ }
+ player = null;
+ if (boss && boss.destroy) {
+ boss.destroy();
+ }
+ boss = null; // Boss powinien być już zniszczony przez die(), ale na wszelki wypadek
+ this.currentState = "grillMenu";
+ game.setBackgroundColor(0x333333); // Tło grilla
+ // Usuń tło gry
+ if (currentBackground) {
+ tween.stop(currentBackground);
+ currentBackground.destroy();
+ currentBackground = null;
+ }
+ // Dodaj tło dla ekranu Grilla
+ currentBackground = LK.getAsset('grillMenu', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 2732 / 2
+ });
+ game.addChildAt(currentBackground, 0); // Na spód
+ // Ukryj ściany
+ walls.forEach(function (wall) {
+ if (wall) {
+ wall.alpha = 0;
+ }
+ });
+ // Ustaw UI dla Grill Menu
+ ui.positionElements("grillMenu");
+ ui.updateBossHealth(0, 1); // Ukryj pasek HP bossa
+ ui.updateHearts(0, storage.maxHearts); // Ukryj serca gracza
+ // --- Przyciski na ekranie Grilla ---
+ var buttonYStart = 1000;
+ var buttonYOffset = 150;
+ // Przycisk "Rest"
+ var restButton = new Container();
+ restButton.interactive = true;
+ restButton.x = 2048 / 2;
+ restButton.y = buttonYStart;
+ currentSceneElements.addChild(restButton);
+ var restButtonBg = LK.getAsset('button_bg', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ restButton.addChild(restButtonBg);
+ var restButtonText = new Text2('Rest', {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ restButtonText.anchor.set(0.5, 0.5);
+ restButton.addChild(restButtonText);
+ restButton.down = function () {
+ ui.showMessage("Rest in peace...", 2000);
+ LK.setTimeout(function () {
+ ui.showMessage("Thank you for playing!", 3000);
+ }, 2500);
+ LK.setTimeout(function () {
+ // clearScene(); // Niekonieczne, showTitleScreen to zrobi
+ gameState.showTitleScreen(); // Wróć do tytułu
+ }, 6000); // Opóźnienie
+ };
+ // Przycisk "Upgrade Roll" (obecnie bez funkcji)
+ var upgradeButton = new Container();
+ upgradeButton.interactive = true;
+ upgradeButton.x = 2048 / 2;
+ upgradeButton.y = buttonYStart + buttonYOffset;
+ currentSceneElements.addChild(upgradeButton);
+ var upgradeButtonBg = LK.getAsset('button_bg', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ upgradeButton.addChild(upgradeButtonBg);
+ var upgradeButtonText = new Text2('Upgrade Roll', {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ upgradeButtonText.anchor.set(0.5, 0.5);
+ upgradeButton.addChild(upgradeButtonText);
+ upgradeButton.down = function () {
+ ui.showMessage("Not implemented yet!", 2000);
+ }; // Zmieniono komunikat
+ // Przycisk "New Boss+"
+ var newBossButton = new Container();
+ newBossButton.interactive = true;
+ newBossButton.x = 2048 / 2;
+ newBossButton.y = buttonYStart + buttonYOffset * 2;
+ currentSceneElements.addChild(newBossButton);
+ var newBossButtonBg = LK.getAsset('button_bg', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ newBossButton.addChild(newBossButtonBg);
+ var newBossButtonText = new Text2('New Boss+', {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ newBossButtonText.anchor.set(0.5, 0.5);
+ newBossButton.addChild(newBossButtonText);
+ newBossButton.down = function () {
+ // clearScene(); // Niekonieczne, startGame to zrobi
+ isNewBossPlusMode = true; // Ustaw flagę
+ gameState.startGame(); // Rozpocznij grę w trybie Boss+
+ };
+ },
+ // Przejście do stanu Game Over
+ // isDeath: true (gracz zginął), false (czas minął w Boss+)
+ gameOver: function gameOver(isDeath) {
+ this.currentState = "gameOver"; // Ustaw stan
+ gameOverReasonIsDeath = isDeath; // Zapisz przyczynę
+ // Zatrzymaj timer gry
+ if (this.gameTimerInterval) {
+ LK.clearInterval(this.gameTimerInterval);
+ }
+ this.gameTimerInterval = null;
+ this.bossSpeedIncreased = false; // Reset flagi przyspieszenia
+ // Zatrzymaj animację tła (jeśli istnieje, ale nie powinno w tym stanie)
+ if (currentBackground) {
+ tween.stop(currentBackground);
+ }
+ game.setBackgroundColor(0x000000); // Czarne tło
+ // Ukryj/zniszcz gracza i bossa (powinny być już zniszczone przez swoje metody die)
+ if (player && player.alpha !== 0) {
+ player.alpha = 0;
+ } // Ukryj, jeśli nie zniknął
+ if (boss && boss.alpha !== 0) {
+ boss.alpha = 0;
+ }
+ // Ukryj ściany
+ walls.forEach(function (wall) {
+ if (wall) {
+ wall.alpha = 0;
+ }
+ });
+ // Ustaw UI dla ekranu game over
+ ui.positionElements("gameOver");
+ // Logika komunikatu Game Over
+ var gameOverMessage = "YOU DIED"; // Domyślny komunikat
+ if (!isDeath) {
+ // Jeśli czas minął (tylko w Boss+)
+ gameOverMessage = "YOU SURVIVED... for now."; // Zmieniono komunikat zwycięstwa Boss+
+ }
+ // Wyświetl komunikat i tytuł
+ ui.titleText.setText(gameOverMessage);
+ ui.titleText.alpha = 1; // Pokaż tytuł
+ ui.showMessage("", 0); // Wyczyść ewentualny poprzedni komunikat
+ ui.showTutorial(""); // Ukryj tutorial
+ // Pasek zdrowia bossa jest zarządzany przez updateBossHealth w pętli gry
+ // W tym stanie updateBossHealth pokaże pasek tylko jeśli !isDeath && isNewBossPlusMode
+ // Zaplanuj restart gry po opóźnieniu
+ LK.setTimeout(function () {
+ // Wyczyść scenę przed restartem
+ clearScene();
+ // Zniszcz gracza/bossa jeśli jakimś cudem przetrwali
+ if (player && player.destroy) {
+ player.destroy();
+ }
+ player = null;
+ if (boss && boss.destroy) {
+ boss.destroy();
+ }
+ boss = null;
+ // Wyczyść ataki bossa (na wszelki wypadek) - teraz robione w Boss.die / Boss.update
+ // if (boss && boss.attacks) boss.attacks = [];
+ // Usuń tło game over (czarne)
+ if (currentBackground) {
+ currentBackground.destroy();
+ }
+ currentBackground = null;
+ // Resetuj UI do stanu "przed grą" (np. jak w tytule, ale bez tekstów)
+ // Pozycjonowanie jest ok, ale trzeba wyczyścić teksty
+ ui.titleText.setText("");
+ ui.titleText.alpha = 0;
+ ui.messageText.setText("");
+ ui.messageText.alpha = 0;
+ ui.tutorialText.setText("");
+ ui.tutorialText.alpha = 0;
+ ui.updateDeathsCounter(); // Zaktualizuj licznik śmierci
+ ui.updateBossHealth(0, 1); // Ukryj pasek HP
+ ui.updateHearts(0, storage.maxHearts); // Ukryj serca
+ // Flaga isNewBossPlusMode pozostaje niezmieniona (jeśli zginąłeś w Boss+, restartujesz w Boss+)
+ // gameOverReasonIsDeath zostanie zresetowana przy następnym wywołaniu gameOver
+ // Restartuj walkę (startGame użyje flagi isNewBossPlusMode)
+ gameState.startGame();
+ }, 4000); // Zwiększono opóźnienie do 4 sekund przed restartem
+ },
+ // Obsługa gestów dotykowych/myszy
+ processTouchGesture: function processTouchGesture() {
+ // --- Obsługa inputu w stanie fakeTutorial ---
+ if (this.currentState === "fakeTutorial" && this.fakeTutorialTimerId) {
+ this.handleFakeTutorialInput(); // Wywołaj fałszywą śmierć
+ return; // Zakończ przetwarzanie gestu
+ }
+ var dx = this.touchEnd.x - this.touchStart.x;
+ var dy = this.touchEnd.y - this.touchStart.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ // Minimalny dystans dla swipe (turlania)
+ var swipeThreshold = 50;
+ // --- Obsługa Tapnięcia ---
+ if (distance < swipeThreshold) {
+ // Sprawdź, czy tapnięcie było na interaktywnym obiekcie (przycisku)
+ // Zakładamy, że LK.Game obsługuje to przez przekazanie 'obj' do game.down/up
+ // Jeśli tapnięcie NIE było na przycisku:
+ if (this.currentState === "title") {
+ // Sprawdź czy tapnięcie nie było na przycisku Grill Menu
+ // Prosty sposób: załóżmy, że jeśli obj nie jest zdefiniowany w game.up, to kliknięto tło
+ // (Wymaga sprawdzenia, jak LK.Game przekazuje 'obj')
+ // Na razie zakładamy, że tapnięcie w tło przechodzi do intro
+ this.showIntro();
+ return;
+ }
+ // W innych stanach (gameOver, victory, grillMenu) tapnięcie w tło nic nie robi
+ // Obsługa przycisków dzieje się przez ich własne handlery .down/.up
+ return;
+ }
+ // --- Obsługa Swipe (Turlania) ---
+ // Działa tylko w stanie gry i gdy gracz żyje
+ if (this.currentState === "game" && player && !player.dead) {
+ // Normalizuj kierunek gestu
+ var direction = {
+ x: 0,
+ y: 0
+ };
+ if (distance > 0) {
+ // Unikaj dzielenia przez zero
+ direction.x = dx / distance;
+ direction.y = dy / distance;
+ }
+ // Wykonaj turlanie gracza
+ player.roll(direction);
+ }
+ // W innych stanach swipe jest ignorowany
+ }
+};
+// --- Obsługa inputu ---
+game.down = function (x, y, obj) {
+ // Rejestruj początek dotyku/kliknięcia
+ // Stany, w których śledzimy gesty lub kliknięcia przycisków:
+ var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"];
+ if (trackStates.indexOf(gameState.currentState) !== -1) {
+ gameState.touchStart.x = x;
+ gameState.touchStart.y = y;
+ gameState.touchEnd.x = x; // Reset end point
+ gameState.touchEnd.y = y;
+ }
+ // Obsługa kliknięć przycisków jest robiona przez ich własne handlery .down
+};
+game.up = function (x, y, obj) {
+ // Rejestruj koniec dotyku/kliknięcia i przetwarzaj gest
+ var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"];
+ if (trackStates.indexOf(gameState.currentState) !== -1) {
+ gameState.touchEnd.x = x;
+ gameState.touchEnd.y = y;
+ // Przetwórz gest (sprawdzi czy to tap czy swipe i podejmie akcję)
+ // Sprawdź, czy 'obj' istnieje - jeśli tak, to było kliknięcie na interaktywnym elemencie,
+ // którego logikę obsługuje jego własny handler .down/.up, więc nie rób nic więcej.
+ // Jeśli 'obj' nie istnieje, to było kliknięcie/swipe w tło.
+ game.up = function (x, y, obj) {
+ if (trackStates.indexOf(gameState.currentState) !== -1) {
+ gameState.touchEnd.x = x;
+ gameState.touchEnd.y = y;
+ // WAŻNE: Wywołuj zawsze processTouchGesture(), bez if (!obj)!
+ gameState.processTouchGesture();
+ }
+ };
+ }
+ // Obsługa puszczenia przycisków (.up) może być dodana tutaj, jeśli potrzebna
+};
+game.move = function (x, y, obj) {
+ // Śledź ruch palca/myszy dla swipe
+ var trackStates = ["title", "game", "fakeTutorial", "fakeGameOver", "realTutorial", "grillMenu", "gameOver"];
+ if (trackStates.indexOf(gameState.currentState) !== -1) {
+ gameState.touchEnd.x = x; // Aktualizuj końcową pozycję na bieżąco
+ gameState.touchEnd.y = y;
+ }
+};
+// --- Główna pętla aktualizacji gry ---
+game.update = function () {
+ // Aktualizuj UI niezależnie od stanu gry (ale tylko jeśli UI istnieje)
+ if (ui) {
+ ui.updateDeathsCounter();
+ // Aktualizuj pasek zdrowia bossa (jeśli boss istnieje i UI istnieje)
+ if (boss) {
+ // Upewnij się, że maxHealth jest sensowne
+ var maxHp = boss.maxHealth > 0 ? boss.maxHealth : 1;
+ ui.updateBossHealth(boss.health, maxHp);
} else {
// Jeśli boss nie istnieje, ukryj pasek (chyba że to wygrana Boss+ przez czas)
if (!(gameState.currentState === "gameOver" && !gameOverReasonIsDeath && isNewBossPlusMode)) {
// Domyślne maxHP do ukrycia paska to 1 (uniknięcie dzielenia przez 0)
@@ -1199,42 +2161,11 @@
}
if (boss) {
boss.update(); // Aktualizacja bossa (ruch, cooldown, tworzenie/usuwanie ataków, kolizje ataków z graczem)
}
+ // Sprawdzanie kolizji i zmiany stanów (gameOver, grillScreen) są teraz wewnątrz metod update/die gracza i bossa.
}
- // Logika game over
- else if (gameState.currentState === "gameOver") {
- // Tutaj można dodać logikę game over, np. animację, możliwość restartu
- // Aktualizacja player i boss nadal jest wywoływana, ale ich metody update powinny sprawdzać stan gry
- if (player) {
- player.update();
- } // Nadal aktualizuj gracza (np. animacja śmierci)
- if (boss) {
- boss.update();
- } // Nadal aktualizuj bossa (np. animacja śmierci)
- // Można dodać logikę przycisków restartu, powrotu do menu itp.
- }
- // Logika grill screen
- else if (gameState.currentState === "grillScreen") {
- // Tutaj można dodać logikę grill screen, np. animację, możliwość przejścia dalej
- if (player) {
- player.update();
- } // Nadal aktualizuj gracza (jeśli widoczny)
- if (boss) {
- boss.update();
- } // Nadal aktualizuj bossa (jeśli widoczny)
- // Można dodać logikę przycisków przejścia dalej, powrotu do menu itp.
- }
- // Rysowanie sceny (wykonywane automatycznie przez LK po gameLoop)
+ // Logika dla innych stanów (np. animacje w intro, obsługa przycisków) jest zarządzana przez ich własne funkcje i handlery.
};
-// Inicjalizacja gry
-LK.init(gameContainer, {
- onInit: function onInit() {
- storage = LK.localStorage; // Initialize storage here after LK.init
- // Load or set maxHearts from storage if needed, default to 5
- storage.maxHearts = storage.maxHearts || 5; // Default max hearts
- // Determine game mode (Boss+ or Standard) from storage or default
- isNewBossPlusMode = storage.newBossPlusMode === true; // Default to false if not in storage
- createGame(); // Call the createGame function to set up the initial game state and objects
- // Game loop is set up by LK.init to call gameLoop function repeatedly (e.g., 60 times per second)
- }
-});
\ No newline at end of file
+// --- Rozpoczęcie gry ---
+// Inicjalizuj obiekt gameState, który zajmie się resztą.
+gameState.init();
\ No newline at end of file