User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '0')' in or related to this line: 'return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;' Line Number: 359
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (18 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ggame is not defined' in or related to this line: 'ggame.update = function () {' Line Number: 1468
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (4 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'height')' in or related to this line: 'LK.gui.top.add(new Text2('SONG ENDED', {' Line Number: 920
Code edit (11 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'height')' in or related to this line: 'playerHpBarContainer.y = LK.data.height - 80; // Pozycja Y na dole (LK.data.height to wysokość ekranu)' Line Number: 698
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'stringify')' in or related to this line: 'console.log("Swipe BBox:", JSON.stringify(swipeBoundingBox));' Line Number: 497
Code edit (6 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'stringify')' in or related to this line: 'console.log("Swipe BBox:", JSON.stringify(swipeBoundingBox));' Line Number: 497
Code edit (1 edits merged)
Please save this source code
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Graphics is not a constructor' in or related to this line: 'var hitZoneLine = new Graphics();' Line Number: 242
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: LK.getTime is not a function' in or related to this line: 'var now = LK.getTime();' Line Number: 535
User prompt
Please fix the bug: 'LK.getTime is not a function' in or related to this line: 'gameStartTime = LK.getTime();' Line Number: 549
Code edit (1 edits merged)
Please save this source code
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Note = Container.expand(function (noteType, swipeDir, targetHitTimeFull, centerXVal) {
var self = Container.call(this);
self.noteType = noteType || 'tap';
self.swipeDir = swipeDir || null;
self.targetHitTime = targetHitTimeFull;
self.visualSpawnTime = self.targetHitTime - noteTravelTime;
self.hit = false;
self.judged = false;
self.scaleStart = 0.3;
self.scaleEnd = 1.2;
self.centerX = centerXVal;
self.centerY = 1800;
self.startY = 600;
self.noteAsset = null;
self.isWiderSwipePart = false; // Flag for wider swipe rendering if needed later
// Store original columnIndex if available, for potential debugging or advanced logic
if (this.mapData && this.mapData.originalColumnHint !== undefined) {
self.originalColumnHint = this.mapData.originalColumnHint;
}
if (self.noteType === 'tap') {
self.noteAsset = self.attachAsset('tapNote', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.noteType === 'swipe') {
self.noteAsset = self.attachAsset('swipeNote', {
anchorX: 0.5,
anchorY: 0.5
});
if (self.swipeDir) {
var arrow = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
arrow.anchor.set(0.5, 0.5);
if (self.swipeDir === 'left') {
arrow.setText('←');
} else if (self.swipeDir === 'right') {
arrow.setText('→');
} else if (self.swipeDir === 'up') {
arrow.setText('↑');
} else if (self.swipeDir === 'down') {
arrow.setText('↓');
}
self.addChild(arrow);
self.arrow = arrow;
}
} else if (self.noteType === 'trap') {
self.noteAsset = self.attachAsset('trapNote', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.alpha = 0;
self.showHitFeedback = function (result) {
// Existing circular feedback
var feedbackCircle = LK.getAsset('hitFeedback', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
// Relative to the note's center
y: 0,
// Relative to the note's center
scaleX: 0.7,
scaleY: 0.7,
alpha: 0.7
});
var feedbackTextContent = "";
var feedbackTextColor = 0xFFFFFF; // Default white
if (result === 'perfect') {
feedbackCircle.tint = 0xffff00; // Yellow
feedbackTextContent = "Perfect!"; // Or "+100"
feedbackTextColor = 0xffff00;
} else if (result === 'good') {
feedbackCircle.tint = 0x00ff00; // Green
feedbackTextContent = "Good!"; // Or "+50"
feedbackTextColor = 0x00ff00;
} else {
// 'miss'
feedbackCircle.tint = 0xff0000; // Red
feedbackTextContent = "Miss";
feedbackTextColor = 0xff0000;
}
self.addChild(feedbackCircle); // Add circle to the note container
tween(feedbackCircle, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
if (feedbackCircle.parent) {
feedbackCircle.destroy();
}
}
});
// New text feedback
if (feedbackTextContent) {
var scorePopup = new Text2(feedbackTextContent, {
size: 60,
// Możemy dostosować rozmiar
fill: feedbackTextColor,
stroke: 0x000000,
// Czarny kontur dla lepszej czytelności
strokeThickness: 3
});
scorePopup.anchor.set(0.5, 0.5);
scorePopup.x = 0; // W centrum notatki (tam gdzie feedbackCircle)
scorePopup.y = -SWIPE_NOTE_WIDTH / 2 - 30; // Trochę nad notatką (zakładając, że notatka ma środek w 0,0)
// Możemy potrzebować dostosować Y, jeśli anchor notatki jest inny
// Add to the main game layer (or LK.gui.top) so it's not affected by note's alpha/scale changes later
// If added to self (note), it would disappear if note is quickly destroyed.
// Let's add it to the note's parent (which should be 'game')
if (self.parent) {
// Ensure note has a parent
// Position needs to be absolute if added to game layer
scorePopup.x = self.x + 0; // (self.x (Note's absolute X) + relative X (0))
scorePopup.y = self.y - SWIPE_NOTE_WIDTH * self.scale.y / 2 - 30; // (self.y (Note's absolute Y) + relative Y)
self.parent.addChild(scorePopup); // Add to the same layer as the note
}
tween(scorePopup, {
y: scorePopup.y - 80,
// Animacja w górę
alpha: 0
}, {
duration: 700,
// Dłuższy czas trwania animacji
easing: tween.easeOut,
onFinish: function onFinish() {
if (scorePopup.parent) {
scorePopup.destroy();
}
}
});
}
};
self.update = function () {
var now = Date.now();
if (self.judged) {
// If judged (hit or missed), it might still be moving for a bit
if (now > self.targetHitTime + 500) {// Time after hit/miss to be fully removed by removeOldNotes
// This note is done, removeOldNotes will handle it.
// If it was hit, its alpha might be 0 from feedback animation.
// If it was missed and had flash, it's visible.
// We can explicitly hide it here if we want it to disappear sooner than removeOldNotes.
// self.alpha = 0; // Opcjonalnie, aby szybciej zniknęła po ocenie
}
// Allow it to continue moving based on original trajectory if needed for visual effect
// For simplicity, if judged, we mostly let removeOldNotes handle it.
// However, if we want it to continue moving PAST the hit line visibly:
if (this.alpha > 0) {
// Only update position if visible
var elapsedTimeSinceSpawn = now - self.visualSpawnTime;
var currentProgress = elapsedTimeSinceSpawn / noteTravelTime;
// No clamping progress to 1 here, so it can go beyond
self.x = self.centerX;
self.y = self.startY + (self.centerY - self.startY) * currentProgress;
// Keep scale at max after passing hit line
var currentScale = currentProgress >= 1 ? self.scaleEnd : self.scaleStart + (self.scaleEnd - self.scaleStart) * currentProgress;
self.scale.x = Math.max(0.01, currentScale);
self.scale.y = Math.max(0.01, currentScale);
}
return;
}
if (now < self.visualSpawnTime) {
self.alpha = 0;
return;
}
if (self.alpha === 0 && !self.judged) {
self.alpha = 1;
}
var elapsedTimeSinceSpawn = now - self.visualSpawnTime;
var progress = elapsedTimeSinceSpawn / noteTravelTime;
// Notatka leci dalej, więc progress może być > 1
// Nie ograniczamy już progress do 1 tutaj dla pozycji i skali
// Skala osiąga self.scaleEnd przy progress = 1 i tak pozostaje
var scale;
if (progress >= 1) {
scale = self.scaleEnd;
} else if (progress < 0) {
// Powinno być obsłużone przez visualSpawnTime check, ale dla pewności
scale = self.scaleStart;
progress = 0; // Clamp progress for position if it was somehow negative
} else {
scale = self.scaleStart + (self.scaleEnd - self.scaleStart) * progress;
}
self.scale.x = Math.max(0.01, scale);
self.scale.y = Math.max(0.01, scale);
self.x = self.centerX;
self.y = self.startY + (self.centerY - self.startY) * progress; // Pozwalamy Y iść dalej
// Oznacz jako miss, jeśli nie została oceniona i minął czas + okno tolerancji
if (!self.judged && now > self.targetHitTime + hitWindowGood) {
self.judged = true; // Oznaczona jako oceniona (w tym przypadku jako miss)
if (self.noteType !== 'trap') {
game.onNoteMiss(self); // Wywołaj logikę miss
}
// Notatka będzie dalej widoczna i usuwana przez removeOldNotes
}
};
self.isInHitWindow = function () {
var now = Date.now();
var dt = Math.abs(now - self.targetHitTime);
return dt <= hitWindowGood;
};
self.getHitAccuracy = function () {
var now = Date.now();
var dt = Math.abs(now - self.targetHitTime);
if (dt <= hitWindowPerfect) {
return 'perfect';
}
if (dt <= hitWindowGood) {
return 'good';
}
return 'miss';
};
return self;
});
var PowerUpItem = Container.expand(function (powerUpType, centerXVal) {
var self = Container.call(this);
self.itemType = powerUpType;
self.centerX = centerXVal;
self.startY = 600;
self.centerY = 1800; // Linia uderzenia/zbierania
self.visualAsset = null;
self.collected = false;
self.spawnTime = Date.now();
self.isAtDestinationAndPotentiallyMissed = false; // Nowa flaga
if (self.itemType === 'potion') {
self.visualAsset = self.attachAsset('hpPotionAsset', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.itemType === 'shield') {
self.visualAsset = self.attachAsset('shieldAsset', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.itemType === 'swipeToTap') {
self.visualAsset = self.attachAsset('swipeToTapBuffAsset', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
console.log("Error: Nieznany typ PowerUpa przy tworzeniu assetu: " + self.itemType);
self.visualAsset = self.attachAsset('hpPotionAsset', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.alpha = 1;
self.scale.set(1);
self.x = self.centerX; // Ustawienie początkowej pozycji X
self.y = self.startY; // Ustawienie początkowej pozycji Y
self.update = function () {
if (self.collected) {
// Jeśli zebrany, nic więcej nie rób (zostanie usunięty przez managePowerUpItems)
return;
}
// Jeśli w poprzedniej klatce dotarł do celu i nie został zebrany, teraz go usuwamy
if (self.isAtDestinationAndPotentiallyMissed) {
if (!self.collected) {
// Podwójne sprawdzenie, na wszelki wypadek
console.log("PowerUp " + self.itemType + " ostatecznie minięty i niszczony");
if (self.parent) {
self.destroy();
}
}
return; // Kończymy update dla tego obiektu
}
var now = Date.now();
var progress = (now - self.spawnTime) / noteTravelTime;
if (progress >= 1) {
self.y = self.centerY; // Ustawiamy dokładnie na linii uderzenia
// Dotarł do linii. Ustawiamy flagę, ale nie niszczymy od razu.
// Zniszczenie (jeśli nie zebrany) nastąpi w następnej klatce update().
// Daje to szansę na zarejestrowanie kliknięcia w tej klatce.
self.isAtDestinationAndPotentiallyMissed = true;
// Na tym etapie jeszcze nie logujemy "minięty", bo może zostać kliknięty w tej klatce.
} else {
// Nadal w ruchu
self.y = self.startY + (self.centerY - self.startY) * progress;
}
};
self.collect = function () {
if (self.collected) {
return;
} // Już zebrany, nic nie rób
self.collected = true; // Oznacz jako zebrany
console.log("PowerUp " + self.itemType + " zebrany!");
if (collectedPowerUps.hasOwnProperty(self.itemType)) {
collectedPowerUps[self.itemType]++;
console.log("Masz teraz " + collectedPowerUps[self.itemType] + " x " + self.itemType);
}
// Natychmiast usuń wizualnie po zebraniu
if (self.parent) {
self.destroy();
}
// Zostanie usunięty z tablicy activePowerUpItems przez managePowerUpItems,
// ponieważ self.collected jest teraz true (lub !self.parent)
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181828
});
/****
* Game Code
****/
// Żółta elipsa dla buffa zmiany nut
// Niebieski kwadrat dla tarczy
// Zielona elipsa dla mikstury HP
// Game Screen Dimensions & Playfield Configuration
/**** * Game Code
****/
var gameScreenWidth = 2048;
var gameScreenHeight = Math.round(gameScreenWidth * (2732 / 2048)); // iPad-like proportions
var playfieldWidth = 1408;
var playfieldStartX = (gameScreenWidth - playfieldWidth) / 2;
var NUM_COLUMNS = 4;
var columnWidth = playfieldWidth / NUM_COLUMNS;
var columnCenterXs = [];
for (var i = 0; i < NUM_COLUMNS; i++) {
columnCenterXs.push(playfieldStartX + i * columnWidth + columnWidth / 2);
}
var SWIPE_NOTE_WIDTH = 160;
// HP System Variables
var playerMaxHP = 10;
var playerCurrentHP = 10;
var bossMaxHP = 30;
var bossCurrentHP = 30;
var gameOverFlag = false;
// HP Bar UI Elements Configuration (actual elements created in setupHpBars)
var hpBarWidth = 400;
var hpBarHeight = 30;
// UI Container
var gameUIContainer; // Declare gameUIContainer
// HP Bar Containers (will be initialized in setupHpBars)
var playerHpBarContainer;
var playerHpBarFill;
var bossHpBarContainer;
var bossHpBarFill;
var POWERUP_SPAWN_INTERVAL = 6000; // Co 6 sekund
var lastPowerUpSpawnAttemptTime = 0;
var POWERUP_SPAWN_CHANCE = 0.75; // 15% szansy
var activePowerUpItems = [];
var powerUpDisplayContainer; // Główny kontener na UI power-upów
var hpPotionIcon, shieldIcon, swipeToTapIcon; // Referencje do ikon
var hpPotionCountText, shieldCountText, swipeToTapCountText; // Referencje do tekstów z ilością
var collectedPowerUps = {
potion: 0,
shield: 0,
swipeToTap: 0 // Klucz 'swipeToTap' dla spójności
};
// Game State & Rhythm Logic Variables
var allSongData = {
"defaultTestTrack": {
musicAsset: null,
config: {
playerMaxHP: 10,
bossMaxHP: 20
},
rawRhythmMap: [
// Istniejące nuty
{
time: 1000,
type: 'tap',
columnIndex: 0
}, {
time: 1800,
type: 'tap',
columnIndex: 1
}, {
time: 2600,
type: 'swipe',
swipeDir: 'left',
columnIndex: 2
}, {
time: 3400,
type: 'tap',
columnIndex: 3
}, {
time: 4200,
type: 'swipe',
swipeDir: 'right',
columnIndex: 0
}, {
time: 5000,
type: 'trap',
columnIndex: 1
}, {
time: 5800,
type: 'tap',
columnIndex: 2
}, {
time: 6600,
type: 'swipe',
swipeDir: 'right',
columnIndex: 1
}, {
time: 6600,
type: 'swipe',
swipeDir: 'right',
columnIndex: 2
}, {
time: 7400,
type: 'tap',
columnIndex: 0
},
// Dodatkowe nuty dla przedłużenia mapy
{
time: 9000,
type: 'tap',
columnIndex: 1
}, {
time: 10000,
type: 'swipe',
swipeDir: 'left',
columnIndex: 2
}, {
time: 11000,
type: 'tap',
columnIndex: 3
}, {
time: 12000,
type: 'swipe',
swipeDir: 'right',
columnIndex: 0
}, {
time: 13000,
type: 'tap',
columnIndex: 1
}, {
time: 14000,
type: 'trap',
columnIndex: 2
}, {
time: 15000,
type: 'tap',
columnIndex: 3
}, {
time: 16000,
type: 'swipe',
swipeDir: 'up',
columnIndex: 0
}, {
time: 17000,
type: 'swipe',
swipeDir: 'down',
columnIndex: 1
}, {
time: 18000,
type: 'tap',
columnIndex: 2
}, {
time: 19000,
type: 'swipe',
swipeDir: 'left',
columnIndex: 3
}, {
time: 20000,
type: 'tap',
columnIndex: 0
}, {
time: 21000,
type: 'tap',
columnIndex: 1
}, {
time: 22000,
type: 'swipe',
swipeDir: 'right',
columnIndex: 2
}, {
time: 23000,
type: 'tap',
columnIndex: 3
}, {
time: 24000,
type: 'swipe',
swipeDir: 'up',
columnIndex: 1
}, {
time: 25000,
type: 'swipe',
swipeDir: 'down',
columnIndex: 2
}, {
time: 26000,
type: 'tap',
columnIndex: 0
}, {
time: 27000,
type: 'tap',
columnIndex: 3
}]
},
"test3": {
// Pozostaje bez zmian, chyba że też chcesz go modyfikować
musicAsset: "test3",
config: {
playerMaxHP: 10,
bossMaxHP: 30
}
}
};
var currentActiveRhythmMap = null;
var currentMusic = null;
var noteTravelTime = 2200;
var hitWindowPerfect = 190;
var hitWindowGood = 260;
var MIN_SWIPE_DISTANCE = 60;
var notes = [];
var nextNoteIdx = 0;
var gameStartTime = 0;
var score = 0;
var combo = 0;
var maxCombo = 0;
var swipeStart = null;
var inputLocked = false;
var hpBarsInitialized = false;
// Initialize and add gameUIContainer to the game scene
gameUIContainer = new Container();
game.addChild(gameUIContainer);
// Function to process raw rhythm map data (remains unchanged, ensure it's here)
function processRawRhythmMap(rawMapData, songKeyForLogging) {
console.log("Processing raw map for: " + songKeyForLogging + " with " + rawMapData.length + " initial notes.");
var processedMap = [];
var tempMap = [];
for (var k = 0; k < rawMapData.length; k++) {
var originalNote = rawMapData[k];
var copiedNote = {};
for (var key in originalNote) {
if (originalNote.hasOwnProperty(key)) {
copiedNote[key] = originalNote[key];
}
}
tempMap.push(copiedNote);
}
for (var i = 0; i < tempMap.length; i++) {
var note = tempMap[i];
if (note.type === 'swipe' && note.swipeDir) {
var dir = note.swipeDir.toLowerCase();
if (dir.includes('right')) {
note.swipeDir = 'right';
} else if (dir.includes('left')) {
note.swipeDir = 'left';
} else if (dir.includes('up')) {
note.swipeDir = 'up';
} else if (dir.includes('down')) {
note.swipeDir = 'down';
}
}
}
var timeGroupedNotes = {};
tempMap.forEach(function (note) {
if (!timeGroupedNotes[note.time]) {
timeGroupedNotes[note.time] = [];
}
timeGroupedNotes[note.time].push(note);
});
var finalMapNotes = [];
var sortedTimes = Object.keys(timeGroupedNotes).map(Number).sort(function (a, b) {
return a - b;
});
for (var tIdx = 0; tIdx < sortedTimes.length; tIdx++) {
var time = sortedTimes[tIdx];
var notesAtThisTime = timeGroupedNotes[time];
var notesToKeepAtThisTime = [];
var processedForWiderSwipeConversion = [];
var colsWithVerticalSwipes = [null, null, null, null];
notesAtThisTime.forEach(function (note) {
if (note.type === 'swipe' && (note.swipeDir === 'up' || note.swipeDir === 'down')) {
if (note.columnIndex >= 0 && note.columnIndex < NUM_COLUMNS) {
var alreadyConverted = false;
for (var convIdx = 0; convIdx < processedForWiderSwipeConversion.length; convIdx++) {
if (processedForWiderSwipeConversion[convIdx].originalTime === note.time && processedForWiderSwipeConversion[convIdx].originalColumn === note.columnIndex) {
alreadyConverted = true;
break;
}
}
if (!alreadyConverted) {
colsWithVerticalSwipes[note.columnIndex] = note.swipeDir;
}
}
}
});
for (var c = 0; c < NUM_COLUMNS - 1; c++) {
if (colsWithVerticalSwipes[c] && colsWithVerticalSwipes[c + 1] && colsWithVerticalSwipes[c] === colsWithVerticalSwipes[c + 1]) {
var randomHorizontalDir = Math.random() < 0.5 ? 'left' : 'right';
var pairCenterX = (columnCenterXs[c] + columnCenterXs[c + 1]) / 2;
notesToKeepAtThisTime.push({
time: time,
type: 'swipe',
swipeDir: randomHorizontalDir,
partOfWiderSwipe: 'leftHalf',
widerSwipePairCenterX: pairCenterX,
originalColumnHint: c
});
notesToKeepAtThisTime.push({
time: time,
type: 'swipe',
swipeDir: randomHorizontalDir,
partOfWiderSwipe: 'rightHalf',
widerSwipePairCenterX: pairCenterX,
originalColumnHint: c + 1
});
processedForWiderSwipeConversion.push({
originalTime: time,
originalColumn: c
});
processedForWiderSwipeConversion.push({
originalTime: time,
originalColumn: c + 1
});
colsWithVerticalSwipes[c] = null;
colsWithVerticalSwipes[c + 1] = null;
c++;
}
}
notesAtThisTime.forEach(function (note) {
var wasConverted = false;
for (var convIdx = 0; convIdx < processedForWiderSwipeConversion.length; convIdx++) {
if (processedForWiderSwipeConversion[convIdx].originalTime === note.time && processedForWiderSwipeConversion[convIdx].originalColumn === note.columnIndex && (note.swipeDir === 'up' || note.swipeDir === 'down')) {
wasConverted = true;
break;
}
}
if (!wasConverted) {
notesToKeepAtThisTime.push(note);
}
});
var horizontalWiderSwipePairs = {};
var notesForFinalProcessing = [];
var uniqueNotesAtThisTime = [];
var seenNotesKeysAtThisTime = {};
notesToKeepAtThisTime.forEach(function (note) {
var key = "" + note.type + "_" + (note.columnIndex !== undefined ? note.columnIndex : note.partOfWiderSwipe ? note.widerSwipePairCenterX + note.partOfWiderSwipe : '') + "_" + (note.swipeDir || '');
if (!seenNotesKeysAtThisTime[key]) {
uniqueNotesAtThisTime.push(note);
seenNotesKeysAtThisTime[key] = true;
}
});
uniqueNotesAtThisTime.sort(function (a, b) {
var valA = a.originalColumnHint !== undefined ? a.originalColumnHint : a.columnIndex !== undefined ? a.columnIndex : a.widerSwipePairCenterX || 0;
var valB = b.originalColumnHint !== undefined ? b.originalColumnHint : b.columnIndex !== undefined ? b.columnIndex : b.widerSwipePairCenterX || 0;
return valA - valB;
});
for (var nIdx = 0; nIdx < uniqueNotesAtThisTime.length; nIdx++) {
var note = uniqueNotesAtThisTime[nIdx];
if (note.partOfWiderSwipe) {
notesForFinalProcessing.push(note);
continue;
}
var potentialPartner = null;
if (nIdx + 1 < uniqueNotesAtThisTime.length) {
potentialPartner = uniqueNotesAtThisTime[nIdx + 1];
}
if (note.type === 'swipe' && (note.swipeDir === 'left' || note.swipeDir === 'right') && potentialPartner && potentialPartner.type === 'swipe' && potentialPartner.time === note.time && potentialPartner.swipeDir === note.swipeDir && potentialPartner.columnIndex === note.columnIndex + 1) {
var pairCenterX = (columnCenterXs[note.columnIndex] + columnCenterXs[potentialPartner.columnIndex]) / 2;
notesForFinalProcessing.push({
time: note.time,
type: 'swipe',
swipeDir: note.swipeDir,
partOfWiderSwipe: 'leftHalf',
widerSwipePairCenterX: pairCenterX,
originalColumnHint: note.columnIndex
});
notesForFinalProcessing.push({
time: potentialPartner.time,
type: 'swipe',
swipeDir: potentialPartner.swipeDir,
partOfWiderSwipe: 'rightHalf',
widerSwipePairCenterX: pairCenterX,
originalColumnHint: potentialPartner.columnIndex
});
nIdx++;
} else {
notesForFinalProcessing.push(note);
}
}
finalMapNotes.push.apply(finalMapNotes, notesForFinalProcessing);
}
var uniqueNotesOverall = [];
var seenNotesOverall = {};
finalMapNotes.sort(function (a, b) {
return a.time - b.time;
});
finalMapNotes.forEach(function (note) {
var cX;
var keyPartForColumn;
if (note.partOfWiderSwipe) {
cX = note.widerSwipePairCenterX + (note.partOfWiderSwipe === 'leftHalf' ? -(SWIPE_NOTE_WIDTH / 2) : SWIPE_NOTE_WIDTH / 2);
keyPartForColumn = "pC" + note.widerSwipePairCenterX + "h" + (note.partOfWiderSwipe === 'leftHalf' ? 'L' : 'R');
} else if (note.columnIndex !== undefined) {
cX = columnCenterXs[note.columnIndex];
keyPartForColumn = "c" + note.columnIndex;
} else {
cX = gameScreenWidth / 2;
keyPartForColumn = "cX" + cX;
}
var key = "" + note.time + "_" + note.type + "_" + keyPartForColumn + "_" + (note.swipeDir || '');
if (!seenNotesOverall[key]) {
uniqueNotesOverall.push(note);
seenNotesOverall[key] = true;
} else {
// console.log("Filtered final duplicate note: " + key);
}
});
console.log("Processed map for: " + songKeyForLogging + " FINALLY contains " + uniqueNotesOverall.length + " notes.");
return uniqueNotesOverall;
}
var hpBarsInitialized = false;
var scoreTxt = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF,
alpha: 1
});
scoreTxt.anchor.set(1, 0); // Anchor center-top
scoreTxt.x = gameScreenWidth / 1;
scoreTxt.y = 20; // Position from the top of its container (gameUIContainer)
gameUIContainer.addChild(scoreTxt);
console.log("Restored scoreTxt: X=" + scoreTxt.x + " Y=" + scoreTxt.y + " Visible:" + scoreTxt.visible + " Parent: gameUIContainer");
var comboTxt = new Text2('Combo: 0', {
size: 60,
fill: 0xFFFF00,
alpha: 1
});
comboTxt.anchor.set(0.5, 0.5); // Anchor center-center
comboTxt.x = gameScreenWidth / 2; // Centered horizontally
comboTxt.y = scoreTxt.y + scoreTxt.height / 2 + 60; // Positioned below scoreTxt
gameUIContainer.addChild(comboTxt);
console.log("Restored comboTxt: X=" + comboTxt.x + " Y=" + comboTxt.y + " Visible:" + comboTxt.visible + " Parent: gameUIContainer");
comboTxt.anchor.set(0.5, 0.5); // Kotwiczenie na środku obiektu Tekstowego
comboTxt.x = gameScreenWidth / 2;
comboTxt.y = scoreTxt.y + scoreTxt.height / 2 + 60; // Pozycjonowanie poniżej scoreTxt, uwzględniając jego wysokość
LK.gui.top.addChild(comboTxt);
console.log("Restored comboTxt: X=" + comboTxt.x + " Y=" + comboTxt.y + " Visible:" + comboTxt.visible);
var hitZoneY = 1800;
var hitZoneVisualWidth = playfieldWidth;
var hitZoneLine = LK.getAsset('lineAsset', {
anchorX: 0.5,
anchorY: 0.5,
x: gameScreenWidth / 2,
y: hitZoneY,
width: hitZoneVisualWidth,
height: 4,
alpha: 0.6
});
game.addChild(hitZoneLine);
function rectsIntersect(r1, r2) {
return !(r2.x > r1.x + r1.width || r2.x + r2.width < r1.x || r2.y > r1.y + r1.height || r2.y + r2.height < r1.y);
}
function resetGameState() {
notes.forEach(function (n) {
if (n && n.parent) {
n.destroy();
}
});
notes = [];
nextNoteIdx = 0;
score = 0;
combo = 0;
maxCombo = 0;
swipeStart = null;
inputLocked = false;
scoreTxt.setText('Score: 0');
comboTxt.setText('Combo: 0');
// Reset HP System
gameOverFlag = false;
// playerMaxHP i bossMaxHP są teraz ustawiane w loadSong z konfiguracji piosenki
playerCurrentHP = playerMaxHP;
bossCurrentHP = bossMaxHP;
// Upewnij się, że paski HP są gotowe przed aktualizacją
if (playerHpBarFill && bossHpBarFill) {
updatePlayerHpDisplay();
updateBossHpDisplay();
}
}
function loadSong(songKey) {
var songData = allSongData[songKey];
if (!songData) {
console.log("Error: Song data not found for key: " + songKey);
if (allSongData["defaultTestTrack"]) {
songData = allSongData["defaultTestTrack"];
console.log("Fallback to defaultTestTrack");
} else {
currentActiveRhythmMap = [];
console.log("No fallback song data found.");
return;
}
}
if (songData.config) {
playerMaxHP = songData.config.playerMaxHP || 10;
bossMaxHP = songData.config.bossMaxHP || 50;
} else {
playerMaxHP = 10;
bossMaxHP = 50;
}
if (currentMusic && typeof currentMusic.stop === 'function') {
currentMusic.stop();
}
currentMusic = null;
hpBarsInitialized = false;
resetGameState();
nextNoteIdx = 0;
if (songData.rawRhythmMap) {
currentActiveRhythmMap = processRawRhythmMap(songData.rawRhythmMap, songKey);
} else {
currentActiveRhythmMap = [];
console.log("Warning: No rawRhythmMap for song: " + songKey + ". Game will have no notes.");
}
gameStartTime = Date.now();
if (songData.musicAsset) {
currentMusic = LK.getSound(songData.musicAsset);
if (currentMusic && typeof currentMusic.play === 'function') {
currentMusic.play();
console.log("Music asset for " + songKey + " PLAYING. gameStartTime: " + gameStartTime);
} else {
console.log("Warning: Music asset '" + songData.musicAsset + "' not found or not playable.");
}
} else {
console.log("No musicAsset defined for " + songKey + ". Map will run on system time only.");
}
console.log("Loaded and processed song: " + songKey + " PlayerHP: " + playerMaxHP + " BossHP: " + bossMaxHP);
}
function setupHpBars() {
if (bossHpBarContainer) {
bossHpBarContainer.destroy();
}
bossHpBarContainer = new Container();
bossHpBarContainer.x = gameScreenWidth / 2;
bossHpBarContainer.y = 100; // Adjusted Y slightly lower than scoreTxt for clarity
gameUIContainer.addChild(bossHpBarContainer); // Add to gameUIContainer
var bossBg = LK.getAsset('hpBarBackground', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0.5,
anchorY: 0.5
});
bossHpBarContainer.addChild(bossBg);
bossHpBarFill = LK.getAsset('bossHpFill', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0,
anchorY: 0.5,
x: -hpBarWidth / 2
});
bossHpBarContainer.addChild(bossHpBarFill);
if (playerHpBarContainer) {
playerHpBarContainer.destroy();
}
playerHpBarContainer = new Container();
playerHpBarContainer.x = gameScreenWidth / 2;
playerHpBarContainer.y = gameScreenHeight - 80;
gameUIContainer.addChild(playerHpBarContainer); // Add to gameUIContainer
var playerBg = LK.getAsset('hpBarBackground', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0.5,
anchorY: 0.5
});
playerHpBarContainer.addChild(playerBg);
playerHpBarFill = LK.getAsset('playerHpFill', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0,
anchorY: 0.5,
x: -hpBarWidth / 2
});
playerHpBarContainer.addChild(playerHpBarFill);
updateBossHpDisplay();
updatePlayerHpDisplay();
console.log("HP Bars setup and added to gameUIContainer. PlayerHP Y: " + playerHpBarContainer.y + " BossHP Y: " + bossHpBarContainer.y);
}
function updatePlayerHpDisplay() {
if (playerHpBarFill) {
var healthPercent = playerCurrentHP / playerMaxHP;
playerHpBarFill.width = hpBarWidth * Math.max(0, healthPercent);
}
}
function updateBossHpDisplay() {
if (bossHpBarFill) {
var healthPercent = bossCurrentHP / bossMaxHP;
bossHpBarFill.width = hpBarWidth * Math.max(0, healthPercent);
}
}
function spawnNotes() {
var now = Date.now();
if (!currentActiveRhythmMap) {
return;
}
while (nextNoteIdx < currentActiveRhythmMap.length) {
var noteData = currentActiveRhythmMap[nextNoteIdx]; // Use processed map
var noteTargetHitTime = gameStartTime + noteData.time;
if (noteTargetHitTime - noteTravelTime <= now) {
var targetCenterX;
if (noteData.partOfWiderSwipe && noteData.widerSwipePairCenterX !== undefined) {
if (noteData.partOfWiderSwipe === 'leftHalf') {
targetCenterX = noteData.widerSwipePairCenterX - SWIPE_NOTE_WIDTH / 2;
} else if (noteData.partOfWiderSwipe === 'rightHalf') {
targetCenterX = noteData.widerSwipePairCenterX + SWIPE_NOTE_WIDTH / 2;
} else {
// Fallback if partOfWiderSwipe has unexpected value, use original column if available
targetCenterX = columnCenterXs[noteData.originalColumnHint !== undefined ? noteData.originalColumnHint : Math.floor(NUM_COLUMNS / 2)];
}
} else if (noteData.columnIndex !== undefined && noteData.columnIndex >= 0 && noteData.columnIndex < NUM_COLUMNS) {
targetCenterX = columnCenterXs[noteData.columnIndex];
} else {
targetCenterX = playfieldStartX + playfieldWidth / 2;
}
// Pass original noteData to Note constructor for potential debugging/reference if needed
var n = new Note(noteData.type, noteData.swipeDir, noteTargetHitTime, targetCenterX);
n.mapData = noteData; // Attach original map data to the note instance
if (noteData.partOfWiderSwipe) {
n.isWiderSwipePart = true;
}
n.alpha = 0;
notes.push(n);
game.addChild(n);
nextNoteIdx++;
} else {
break;
}
}
}
function removeOldNotes() {
var now = Date.now();
for (var i = notes.length - 1; i >= 0; i--) {
var n = notes[i];
var timeToRemoveAfterJudged = 700; // ms po targetHitTime dla ocenionych notatek
var timeToRemoveIfNotJudged = noteTravelTime / 2 + hitWindowGood + 500; // Dłuższy czas, jeśli nieoceniona, liczony od targetHitTime
if (n.judged && now > n.targetHitTime + timeToRemoveAfterJudged) {
if (n.parent) {
n.destroy();
}
notes.splice(i, 1);
} else if (!n.judged && now > n.targetHitTime + timeToRemoveIfNotJudged) {
if (n.noteType !== 'trap') {}
if (n.parent) {
n.destroy();
}
notes.splice(i, 1);
}
}
}
function findNoteAt(x, y, typeToFind) {
var now = Date.now();
var bestNote = null;
var smallestTimeDiff = hitWindowGood + 1;
for (var i = 0; i < notes.length; i++) {
var n = notes[i];
if (n.judged || n.noteType !== typeToFind) {
continue;
}
var timeDiff = Math.abs(now - n.targetHitTime);
if (timeDiff > hitWindowGood) {
continue;
}
var currentNoteAsset = n.noteAsset;
if (!currentNoteAsset) {
continue;
}
var currentNoteWidth = currentNoteAsset.width * n.scale.x;
var currentNoteHeight = currentNoteAsset.height * n.scale.y;
if (n.noteType === 'swipe') {
currentNoteWidth = SWIPE_NOTE_WIDTH * n.scale.x;
currentNoteHeight = SWIPE_NOTE_WIDTH * n.scale.y;
}
var dx = x - n.x;
var dy = y - n.y;
if (Math.abs(dx) <= currentNoteWidth / 2 && Math.abs(dy) <= currentNoteHeight / 2) {
if (timeDiff < smallestTimeDiff) {
bestNote = n;
smallestTimeDiff = timeDiff;
}
}
}
return bestNote;
}
function addScore(result) {
if (result === 'perfect') {
score += 100;
} else if (result === 'good') {
score += 50;
}
scoreTxt.setText('Score: ' + score);
}
function addCombo() {
combo += 1;
if (combo > maxCombo) {
maxCombo = combo;
}
comboTxt.setText('Combo: ' + combo);
// Animacja dla comboTxt
if (combo > 1) {
// Mała animacja tylko jeśli combo jest większe niż 1
var originalScale = comboTxt.scale.x; // Zakładamy, że skala X i Y jest taka sama
tween(comboTxt.scale, {
x: originalScale * 1.3,
y: originalScale * 1.3
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(comboTxt.scale, {
x: originalScale,
y: originalScale
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
}
function resetCombo() {
combo = 0;
comboTxt.setText('Combo: 0');
}
function checkGameEnd() {
if (gameOverFlag) {
return;
}
if (playerCurrentHP <= 0) {
gameOverFlag = true;
console.log("GAME OVER - Player HP depleted");
if (currentMusic && typeof currentMusic.stop === 'function') {
currentMusic.stop();
}
var gameOverText = new Text2('GAME OVER', {
x: gameScreenWidth / 2,
y: gameScreenHeight / 2,
anchorX: 0.5,
anchorY: 0.5,
// Ensure vertical centering
size: 100,
fill: 0xff0000
});
gameUIContainer.addChild(gameOverText); // Add to gameUIContainer
return;
}
if (bossCurrentHP <= 0) {
gameOverFlag = true;
console.log("YOU WIN - Boss HP depleted");
if (currentMusic && typeof currentMusic.stop === 'function') {
currentMusic.stop();
}
// LK.showYouWin(); // This is an engine specific function, we'll use a text display for now
var winText = new Text2('YOU WIN!', {
x: gameScreenWidth / 2,
y: gameScreenHeight / 2,
anchorX: 0.5,
anchorY: 0.5,
// Ensure vertical centering
size: 100,
fill: 0x00ff00
});
gameUIContainer.addChild(winText); // Add to gameUIContainer
return;
}
if (currentActiveRhythmMap && nextNoteIdx >= currentActiveRhythmMap.length && notes.length === 0) {
if (!gameOverFlag) {
// Check gameOverFlag again to prevent double messages if HP ran out simultaneously
gameOverFlag = true;
console.log("SONG ENDED - Boss might still be alive");
if (currentMusic && typeof currentMusic.stop === 'function') {
currentMusic.stop();
}
var songEndedText = new Text2('SONG ENDED', {
x: gameScreenWidth / 2,
y: gameScreenHeight / 2,
anchorX: 0.5,
anchorY: 0.5,
// Ensure vertical centering
size: 100,
fill: 0xffff00
});
gameUIContainer.addChild(songEndedText); // Add to gameUIContainer
}
}
}
function trySpawnRandomPowerUp() {
var now = Date.now();
if (now > lastPowerUpSpawnAttemptTime + POWERUP_SPAWN_INTERVAL) {
lastPowerUpSpawnAttemptTime = now;
if (Math.random() < POWERUP_SPAWN_CHANCE) {
var powerUpTypes = ['potion', 'shield', 'swipeToTap'];
var randomType = powerUpTypes[Math.floor(Math.random() * powerUpTypes.length)];
var randomColumnIndex = Math.floor(Math.random() * NUM_COLUMNS);
var spawnX = columnCenterXs[randomColumnIndex];
var newPowerUp = new PowerUpItem(randomType, spawnX);
game.addChild(newPowerUp);
activePowerUpItems.push(newPowerUp);
console.log("Spawnowano PowerUp: " + randomType + " w kolumnie " + (randomColumnIndex + 1));
}
}
}
function managePowerUpItems() {
for (var i = activePowerUpItems.length - 1; i >= 0; i--) {
var pItem = activePowerUpItems[i];
// Sprawdzamy, czy obiekt w ogóle istnieje i czy ma rodzica (czy jest na scenie)
// Jeśli został zebrany (collected=true) LUB nie ma rodzica (został zniszczony), to usuwamy z tablicy
if (!pItem || pItem.collected || !pItem.parent) {
activePowerUpItems.splice(i, 1);
} else {
pItem.update(); // Aktualizuj tylko, jeśli jest na scenie i niezebrany
}
}
}
game.onNoteMiss = function (note) {
if (!note) {
// Sprawdzamy tylko, czy nuta istnieje; warunek 'note.judged' został usunięty
return;
}
// Flaga 'note.judged' jest już ustawiona na 'true' przez funkcję Note.update,
// zanim ta funkcja (game.onNoteMiss) została wywołana.
note.showHitFeedback('miss');
resetCombo();
// Logika utraty HP przez gracza
if (!gameOverFlag) {
// Tylko jeśli gra się jeszcze nie skończyła
playerCurrentHP = Math.max(0, playerCurrentHP - 1);
updatePlayerHpDisplay();
console.log("Player HP after miss: " + playerCurrentHP);
// Warunek przegranej zostanie sprawdzony w checkGameEnd(), które jest regularnie wywoływane w game.update()
}
if (note.parent) {
// Efekt wizualny dla chybionej nuty
LK.effects.flashObject(note, 0xff0000, 300);
}
};
game.down = function (x, y, obj) {
// Logika zbierania PowerUpów
for (var i = activePowerUpItems.length - 1; i >= 0; i--) {
var pItem = activePowerUpItems[i];
if (pItem && !pItem.collected && pItem.visualAsset && pItem.parent) {
// Upewniamy się, że pItem istnieje i jest na scenie
var pWidth = pItem.visualAsset.width * pItem.scale.x;
var pHeight = pItem.visualAsset.height * pItem.scale.y;
// Proste sprawdzenie kolizji AABB (axis-aligned bounding box)
if (x >= pItem.x - pWidth / 2 && x <= pItem.x + pWidth / 2 && y >= pItem.y - pHeight / 2 && y <= pItem.y + pHeight / 2) {
// Dodatkowe sprawdzenie, czy jest blisko linii uderzenia (opcjonalne, ale zalecane)
// Możesz dostosować tolerancję (pHeight)
if (Math.abs(pItem.y - hitZoneY) < pHeight * 1.5) {
// Zwiększona tolerancja dla łatwiejszego trafienia
pItem.collect();
// swipeStart = null; // Możemy zresetować swipeStart, aby uniknąć przypadkowego swipe'a po zebraniu power-upa
// inputLocked = true; // Możemy na chwilę zablokować input
// LK.setTimeout(function () { inputLocked = false; }, 50); // Krótka blokada
return; // Zbieramy tylko jeden power-up na kliknięcie, kończymy obsługę tego eventu
}
}
}
}
// Reszta logiki z oryginalnej funkcji game.down
if (inputLocked) {
return;
}
swipeStart = {
x: x,
y: y,
time: Date.now()
};
var noteUnderCursor = findNoteAt(x, y, 'trap');
if (noteUnderCursor && !noteUnderCursor.judged && noteUnderCursor.isInHitWindow()) {
noteUnderCursor.judged = true;
noteUnderCursor.showHitFeedback('miss');
resetCombo();
LK.effects.flashScreen(0xff0000, 400);
if (!gameOverFlag) {
playerCurrentHP = Math.max(0, playerCurrentHP - 1);
updatePlayerHpDisplay();
console.log("Player HP after TRAP: " + playerCurrentHP);
}
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;
}, 200);
return;
}
noteUnderCursor = findNoteAt(x, y, 'tap');
if (noteUnderCursor && !noteUnderCursor.judged && noteUnderCursor.isInHitWindow()) {
var result = noteUnderCursor.getHitAccuracy();
noteUnderCursor.judged = true;
noteUnderCursor.showHitFeedback(result);
if (result !== 'miss') {
addScore(result);
addCombo();
if (!gameOverFlag) {
if (result === 'perfect') {
bossCurrentHP = Math.max(0, bossCurrentHP - 2);
} else if (result === 'good') {
bossCurrentHP = Math.max(0, bossCurrentHP - 1);
}
updateBossHpDisplay();
console.log("Boss HP after TAP (" + result + "): " + bossCurrentHP);
}
} else {
resetCombo();
}
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;
}, 120);
return;
}
};
game.up = function (x, y, obj) {
if (inputLocked || !swipeStart) {
swipeStart = null;
return;
}
var swipeEndX = x;
var swipeEndY = y;
var swipeEndTime = Date.now();
var dx = swipeEndX - swipeStart.x;
var dy = swipeEndY - swipeStart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var potentialSwipe = dist >= MIN_SWIPE_DISTANCE;
var swipedNoteSuccessfully = false;
if (potentialSwipe) {
var detectedDir = null;
if (Math.abs(dx) > Math.abs(dy)) {
detectedDir = dx > 0 ? 'right' : 'left';
} else {
detectedDir = dy > 0 ? 'down' : 'up';
}
var swipeBoundingBox = {
x: Math.min(swipeStart.x, swipeEndX),
y: Math.min(swipeStart.y, swipeEndY),
width: Math.abs(dx),
height: Math.abs(dy)
};
var notesHitThisSwipe = [];
for (var i = 0; i < notes.length; i++) {
var n = notes[i];
if (n.judged || n.noteType !== 'swipe') {
continue;
}
var overallSwipeTimeMatchesNote = false;
if (n.targetHitTime >= swipeStart.time - hitWindowGood && n.targetHitTime <= swipeEndTime + hitWindowGood) {
overallSwipeTimeMatchesNote = true;
}
if (!overallSwipeTimeMatchesNote) {
if (swipeStart.time >= n.targetHitTime - hitWindowGood && swipeStart.time <= n.targetHitTime + hitWindowGood || swipeEndTime >= n.targetHitTime - hitWindowGood && swipeEndTime <= n.targetHitTime + hitWindowGood) {
overallSwipeTimeMatchesNote = true;
}
}
if (!overallSwipeTimeMatchesNote) {
continue;
}
if (n.alpha === 0) {
continue;
}
var noteCurrentWidth = SWIPE_NOTE_WIDTH * n.scale.x;
var noteCurrentHeight = SWIPE_NOTE_WIDTH * n.scale.y;
var noteBoundingBox = {
x: n.x - noteCurrentWidth / 2,
y: n.y - noteCurrentHeight / 2,
width: noteCurrentWidth,
height: noteCurrentHeight
};
if (rectsIntersect(swipeBoundingBox, noteBoundingBox)) {
if (detectedDir === n.swipeDir) {
var verticalProximity = Math.abs(n.y - n.centerY);
var verticalTolerance = noteCurrentHeight / 1.5;
if (verticalProximity < verticalTolerance) {
notesHitThisSwipe.push(n);
}
}
}
}
if (notesHitThisSwipe.length > 0) {
notesHitThisSwipe.sort(function (a, b) {
var da = Math.abs(swipeEndTime - a.targetHitTime);
var db = Math.abs(swipeEndTime - b.targetHitTime);
return da - db;
});
var maxNotesToHitPerSwipe = 1;
if (notesHitThisSwipe.length > 0 && notesHitThisSwipe[0].isWiderSwipePart) {
maxNotesToHitPerSwipe = 2;
}
var notesActuallyHitCount = 0;
for (var k = 0; k < notesHitThisSwipe.length && notesActuallyHitCount < maxNotesToHitPerSwipe; k++) {
var noteToJudge = notesHitThisSwipe[k];
if (noteToJudge.judged) {
continue;
}
if (notesActuallyHitCount === 1 && notesHitThisSwipe[0].isWiderSwipePart) {
if (!noteToJudge.isWiderSwipePart || noteToJudge.mapData.widerSwipePairCenterX !== notesHitThisSwipe[0].mapData.widerSwipePairCenterX || noteToJudge.mapData.partOfWiderSwipe === notesHitThisSwipe[0].mapData.partOfWiderSwipe) {
continue;
}
}
var result = noteToJudge.getHitAccuracy();
noteToJudge.judged = true;
noteToJudge.showHitFeedback(result);
if (result !== 'miss') {
addScore(result);
addCombo();
if (!gameOverFlag) {
if (result === 'perfect') {
bossCurrentHP = Math.max(0, bossCurrentHP - 2);
} else if (result === 'good') {
bossCurrentHP = Math.max(0, bossCurrentHP - 1);
}
updateBossHpDisplay();
}
} else {
resetCombo();
}
swipedNoteSuccessfully = true;
notesActuallyHitCount++;
}
}
}
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;
}, 80);
swipeStart = null;
};
game.move = function (x, y, obj) {};
game.update = function () {
if (!hpBarsInitialized) {
setupHpBars();
lastPowerUpSpawnAttemptTime = Date.now();
hpBarsInitialized = true;
}
trySpawnRandomPowerUp();
managePowerUpItems();
spawnNotes();
for (var i = 0; i < notes.length; i++) {
if (notes[i] && notes[i].update) {
notes[i].update();
}
}
removeOldNotes();
checkGameEnd();
};
// Load the specific song for testing
loadSong("defaultTestTrack");
// loadSong("defaultTestTrack"); // Możesz to odkomentować, aby załadować starą mapę testową ===================================================================
--- original.js
+++ change.js
@@ -223,8 +223,93 @@
return 'miss';
};
return self;
});
+var PowerUpItem = Container.expand(function (powerUpType, centerXVal) {
+ var self = Container.call(this);
+ self.itemType = powerUpType;
+ self.centerX = centerXVal;
+ self.startY = 600;
+ self.centerY = 1800; // Linia uderzenia/zbierania
+ self.visualAsset = null;
+ self.collected = false;
+ self.spawnTime = Date.now();
+ self.isAtDestinationAndPotentiallyMissed = false; // Nowa flaga
+ if (self.itemType === 'potion') {
+ self.visualAsset = self.attachAsset('hpPotionAsset', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (self.itemType === 'shield') {
+ self.visualAsset = self.attachAsset('shieldAsset', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (self.itemType === 'swipeToTap') {
+ self.visualAsset = self.attachAsset('swipeToTapBuffAsset', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else {
+ console.log("Error: Nieznany typ PowerUpa przy tworzeniu assetu: " + self.itemType);
+ self.visualAsset = self.attachAsset('hpPotionAsset', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ }
+ self.alpha = 1;
+ self.scale.set(1);
+ self.x = self.centerX; // Ustawienie początkowej pozycji X
+ self.y = self.startY; // Ustawienie początkowej pozycji Y
+ self.update = function () {
+ if (self.collected) {
+ // Jeśli zebrany, nic więcej nie rób (zostanie usunięty przez managePowerUpItems)
+ return;
+ }
+ // Jeśli w poprzedniej klatce dotarł do celu i nie został zebrany, teraz go usuwamy
+ if (self.isAtDestinationAndPotentiallyMissed) {
+ if (!self.collected) {
+ // Podwójne sprawdzenie, na wszelki wypadek
+ console.log("PowerUp " + self.itemType + " ostatecznie minięty i niszczony");
+ if (self.parent) {
+ self.destroy();
+ }
+ }
+ return; // Kończymy update dla tego obiektu
+ }
+ var now = Date.now();
+ var progress = (now - self.spawnTime) / noteTravelTime;
+ if (progress >= 1) {
+ self.y = self.centerY; // Ustawiamy dokładnie na linii uderzenia
+ // Dotarł do linii. Ustawiamy flagę, ale nie niszczymy od razu.
+ // Zniszczenie (jeśli nie zebrany) nastąpi w następnej klatce update().
+ // Daje to szansę na zarejestrowanie kliknięcia w tej klatce.
+ self.isAtDestinationAndPotentiallyMissed = true;
+ // Na tym etapie jeszcze nie logujemy "minięty", bo może zostać kliknięty w tej klatce.
+ } else {
+ // Nadal w ruchu
+ self.y = self.startY + (self.centerY - self.startY) * progress;
+ }
+ };
+ self.collect = function () {
+ if (self.collected) {
+ return;
+ } // Już zebrany, nic nie rób
+ self.collected = true; // Oznacz jako zebrany
+ console.log("PowerUp " + self.itemType + " zebrany!");
+ if (collectedPowerUps.hasOwnProperty(self.itemType)) {
+ collectedPowerUps[self.itemType]++;
+ console.log("Masz teraz " + collectedPowerUps[self.itemType] + " x " + self.itemType);
+ }
+ // Natychmiast usuń wizualnie po zebraniu
+ if (self.parent) {
+ self.destroy();
+ }
+ // Zostanie usunięty z tablicy activePowerUpItems przez managePowerUpItems,
+ // ponieważ self.collected jest teraz true (lub !self.parent)
+ };
+ return self;
+});
/****
* Initialize Game
****/
@@ -234,11 +319,14 @@
/****
* Game Code
****/
+// Żółta elipsa dla buffa zmiany nut
+// Niebieski kwadrat dla tarczy
+// Zielona elipsa dla mikstury HP
+// Game Screen Dimensions & Playfield Configuration
/**** * Game Code
****/
-// Game Screen Dimensions & Playfield Configuration
var gameScreenWidth = 2048;
var gameScreenHeight = Math.round(gameScreenWidth * (2732 / 2048)); // iPad-like proportions
var playfieldWidth = 1408;
var playfieldStartX = (gameScreenWidth - playfieldWidth) / 2;
@@ -264,17 +352,31 @@
var playerHpBarContainer;
var playerHpBarFill;
var bossHpBarContainer;
var bossHpBarFill;
+var POWERUP_SPAWN_INTERVAL = 6000; // Co 6 sekund
+var lastPowerUpSpawnAttemptTime = 0;
+var POWERUP_SPAWN_CHANCE = 0.75; // 15% szansy
+var activePowerUpItems = [];
+var powerUpDisplayContainer; // Główny kontener na UI power-upów
+var hpPotionIcon, shieldIcon, swipeToTapIcon; // Referencje do ikon
+var hpPotionCountText, shieldCountText, swipeToTapCountText; // Referencje do tekstów z ilością
+var collectedPowerUps = {
+ potion: 0,
+ shield: 0,
+ swipeToTap: 0 // Klucz 'swipeToTap' dla spójności
+};
// Game State & Rhythm Logic Variables
var allSongData = {
"defaultTestTrack": {
musicAsset: null,
config: {
playerMaxHP: 10,
bossMaxHP: 20
},
- rawRhythmMap: [{
+ rawRhythmMap: [
+ // Istniejące nuty
+ {
time: 1000,
type: 'tap',
columnIndex: 0
}, {
@@ -316,11 +418,98 @@
}, {
time: 7400,
type: 'tap',
columnIndex: 0
+ },
+ // Dodatkowe nuty dla przedłużenia mapy
+ {
+ time: 9000,
+ type: 'tap',
+ columnIndex: 1
+ }, {
+ time: 10000,
+ type: 'swipe',
+ swipeDir: 'left',
+ columnIndex: 2
+ }, {
+ time: 11000,
+ type: 'tap',
+ columnIndex: 3
+ }, {
+ time: 12000,
+ type: 'swipe',
+ swipeDir: 'right',
+ columnIndex: 0
+ }, {
+ time: 13000,
+ type: 'tap',
+ columnIndex: 1
+ }, {
+ time: 14000,
+ type: 'trap',
+ columnIndex: 2
+ }, {
+ time: 15000,
+ type: 'tap',
+ columnIndex: 3
+ }, {
+ time: 16000,
+ type: 'swipe',
+ swipeDir: 'up',
+ columnIndex: 0
+ }, {
+ time: 17000,
+ type: 'swipe',
+ swipeDir: 'down',
+ columnIndex: 1
+ }, {
+ time: 18000,
+ type: 'tap',
+ columnIndex: 2
+ }, {
+ time: 19000,
+ type: 'swipe',
+ swipeDir: 'left',
+ columnIndex: 3
+ }, {
+ time: 20000,
+ type: 'tap',
+ columnIndex: 0
+ }, {
+ time: 21000,
+ type: 'tap',
+ columnIndex: 1
+ }, {
+ time: 22000,
+ type: 'swipe',
+ swipeDir: 'right',
+ columnIndex: 2
+ }, {
+ time: 23000,
+ type: 'tap',
+ columnIndex: 3
+ }, {
+ time: 24000,
+ type: 'swipe',
+ swipeDir: 'up',
+ columnIndex: 1
+ }, {
+ time: 25000,
+ type: 'swipe',
+ swipeDir: 'down',
+ columnIndex: 2
+ }, {
+ time: 26000,
+ type: 'tap',
+ columnIndex: 0
+ }, {
+ time: 27000,
+ type: 'tap',
+ columnIndex: 3
}]
},
"test3": {
+ // Pozostaje bez zmian, chyba że też chcesz go modyfikować
musicAsset: "test3",
config: {
playerMaxHP: 10,
bossMaxHP: 30
@@ -538,20 +727,15 @@
size: 100,
fill: 0xFFFFFF,
alpha: 1
});
-scoreTxt.anchor.set(0.5, 0); // Anchor center-top
-scoreTxt.x = gameScreenWidth / 2; // Centered horizontally
+scoreTxt.anchor.set(1, 0); // Anchor center-top
+scoreTxt.x = gameScreenWidth / 1;
scoreTxt.y = 20; // Position from the top of its container (gameUIContainer)
gameUIContainer.addChild(scoreTxt);
console.log("Restored scoreTxt: X=" + scoreTxt.x + " Y=" + scoreTxt.y + " Visible:" + scoreTxt.visible + " Parent: gameUIContainer");
-scoreTxt.anchor.set(0.5, 0); // Kotwiczenie na środku obiektu Tekstowego
-scoreTxt.x = 0;
-scoreTxt.y = 0; // Trochę niżej niż pierwotne 20, aby dać więcej miejsca od góry
-LK.gui.top.addChild(scoreTxt);
-console.log("Restored scoreTxt: X=" + scoreTxt.x + " Y=" + scoreTxt.y + " Visible:" + scoreTxt.visible);
var comboTxt = new Text2('Combo: 0', {
- size: 80,
+ size: 60,
fill: 0xFFFF00,
alpha: 1
});
comboTxt.anchor.set(0.5, 0.5); // Anchor center-center
@@ -652,16 +836,15 @@
}
console.log("Loaded and processed song: " + songKey + " PlayerHP: " + playerMaxHP + " BossHP: " + bossMaxHP);
}
function setupHpBars() {
- // Boss HP Bar (Top Center)
if (bossHpBarContainer) {
bossHpBarContainer.destroy();
- } // Usuń stary, jeśli istnieje
+ }
bossHpBarContainer = new Container();
bossHpBarContainer.x = gameScreenWidth / 2;
- bossHpBarContainer.y = 50; // Pozycja Y na górze
- LK.gui.top.addChild(bossHpBarContainer);
+ bossHpBarContainer.y = 100; // Adjusted Y slightly lower than scoreTxt for clarity
+ gameUIContainer.addChild(bossHpBarContainer); // Add to gameUIContainer
var bossBg = LK.getAsset('hpBarBackground', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0.5,
@@ -671,22 +854,19 @@
bossHpBarFill = LK.getAsset('bossHpFill', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0,
- // Kotwica po lewej dla łatwej zmiany szerokości
anchorY: 0.5,
- x: -hpBarWidth / 2 // Pozycja X, aby zaczynał się od lewej krawędzi tła
+ x: -hpBarWidth / 2
});
bossHpBarContainer.addChild(bossHpBarFill);
- // Player HP Bar (Bottom Center)
if (playerHpBarContainer) {
playerHpBarContainer.destroy();
- } // Usuń stary, jeśli istnieje
+ }
playerHpBarContainer = new Container();
playerHpBarContainer.x = gameScreenWidth / 2;
- // Calculate a fallback height for the game area (iPad Pro 12" retina: 2048x2732)
- playerHpBarContainer.y = gameScreenHeight - 80; // Pozycja Y na dole
- LK.gui.top.addChild(playerHpBarContainer);
+ playerHpBarContainer.y = gameScreenHeight - 80;
+ gameUIContainer.addChild(playerHpBarContainer); // Add to gameUIContainer
var playerBg = LK.getAsset('hpBarBackground', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0.5,
@@ -696,15 +876,15 @@
playerHpBarFill = LK.getAsset('playerHpFill', {
width: hpBarWidth,
height: hpBarHeight,
anchorX: 0,
- // Kotwica po lewej
anchorY: 0.5,
x: -hpBarWidth / 2
});
playerHpBarContainer.addChild(playerHpBarFill);
updateBossHpDisplay();
updatePlayerHpDisplay();
+ console.log("HP Bars setup and added to gameUIContainer. PlayerHP Y: " + playerHpBarContainer.y + " BossHP Y: " + bossHpBarContainer.y);
}
function updatePlayerHpDisplay() {
if (playerHpBarFill) {
var healthPercent = playerCurrentHP / playerMaxHP;
@@ -859,65 +1039,134 @@
console.log("GAME OVER - Player HP depleted");
if (currentMusic && typeof currentMusic.stop === 'function') {
currentMusic.stop();
}
- LK.gui.top.addChild(new Text2('GAME OVER', {
- // ZMIANA TUTAJ: add -> addChild
+ var gameOverText = new Text2('GAME OVER', {
x: gameScreenWidth / 2,
y: gameScreenHeight / 2,
anchorX: 0.5,
+ anchorY: 0.5,
+ // Ensure vertical centering
size: 100,
fill: 0xff0000
- }));
+ });
+ gameUIContainer.addChild(gameOverText); // Add to gameUIContainer
return;
}
if (bossCurrentHP <= 0) {
gameOverFlag = true;
console.log("YOU WIN - Boss HP depleted");
if (currentMusic && typeof currentMusic.stop === 'function') {
currentMusic.stop();
}
- LK.showYouWin();
+ // LK.showYouWin(); // This is an engine specific function, we'll use a text display for now
+ var winText = new Text2('YOU WIN!', {
+ x: gameScreenWidth / 2,
+ y: gameScreenHeight / 2,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ // Ensure vertical centering
+ size: 100,
+ fill: 0x00ff00
+ });
+ gameUIContainer.addChild(winText); // Add to gameUIContainer
return;
}
if (currentActiveRhythmMap && nextNoteIdx >= currentActiveRhythmMap.length && notes.length === 0) {
if (!gameOverFlag) {
+ // Check gameOverFlag again to prevent double messages if HP ran out simultaneously
gameOverFlag = true;
console.log("SONG ENDED - Boss might still be alive");
if (currentMusic && typeof currentMusic.stop === 'function') {
currentMusic.stop();
}
- LK.gui.top.addChild(new Text2('SONG ENDED', {
- // ZMIANA TUTAJ: add -> addChild (Twoja linia błędu 907)
+ var songEndedText = new Text2('SONG ENDED', {
x: gameScreenWidth / 2,
y: gameScreenHeight / 2,
anchorX: 0.5,
+ anchorY: 0.5,
+ // Ensure vertical centering
size: 100,
fill: 0xffff00
- }));
+ });
+ gameUIContainer.addChild(songEndedText); // Add to gameUIContainer
}
}
}
+function trySpawnRandomPowerUp() {
+ var now = Date.now();
+ if (now > lastPowerUpSpawnAttemptTime + POWERUP_SPAWN_INTERVAL) {
+ lastPowerUpSpawnAttemptTime = now;
+ if (Math.random() < POWERUP_SPAWN_CHANCE) {
+ var powerUpTypes = ['potion', 'shield', 'swipeToTap'];
+ var randomType = powerUpTypes[Math.floor(Math.random() * powerUpTypes.length)];
+ var randomColumnIndex = Math.floor(Math.random() * NUM_COLUMNS);
+ var spawnX = columnCenterXs[randomColumnIndex];
+ var newPowerUp = new PowerUpItem(randomType, spawnX);
+ game.addChild(newPowerUp);
+ activePowerUpItems.push(newPowerUp);
+ console.log("Spawnowano PowerUp: " + randomType + " w kolumnie " + (randomColumnIndex + 1));
+ }
+ }
+}
+function managePowerUpItems() {
+ for (var i = activePowerUpItems.length - 1; i >= 0; i--) {
+ var pItem = activePowerUpItems[i];
+ // Sprawdzamy, czy obiekt w ogóle istnieje i czy ma rodzica (czy jest na scenie)
+ // Jeśli został zebrany (collected=true) LUB nie ma rodzica (został zniszczony), to usuwamy z tablicy
+ if (!pItem || pItem.collected || !pItem.parent) {
+ activePowerUpItems.splice(i, 1);
+ } else {
+ pItem.update(); // Aktualizuj tylko, jeśli jest na scenie i niezebrany
+ }
+ }
+}
game.onNoteMiss = function (note) {
- if (!note || note.judged) {
+ if (!note) {
+ // Sprawdzamy tylko, czy nuta istnieje; warunek 'note.judged' został usunięty
return;
}
- note.judged = true;
+ // Flaga 'note.judged' jest już ustawiona na 'true' przez funkcję Note.update,
+ // zanim ta funkcja (game.onNoteMiss) została wywołana.
note.showHitFeedback('miss');
resetCombo();
- // Player HP Loss
+ // Logika utraty HP przez gracza
if (!gameOverFlag) {
// Tylko jeśli gra się jeszcze nie skończyła
playerCurrentHP = Math.max(0, playerCurrentHP - 1);
updatePlayerHpDisplay();
console.log("Player HP after miss: " + playerCurrentHP);
- // Warunek przegranej zostanie sprawdzony w checkGameEnd()
+ // Warunek przegranej zostanie sprawdzony w checkGameEnd(), które jest regularnie wywoływane w game.update()
}
if (note.parent) {
+ // Efekt wizualny dla chybionej nuty
LK.effects.flashObject(note, 0xff0000, 300);
}
};
game.down = function (x, y, obj) {
+ // Logika zbierania PowerUpów
+ for (var i = activePowerUpItems.length - 1; i >= 0; i--) {
+ var pItem = activePowerUpItems[i];
+ if (pItem && !pItem.collected && pItem.visualAsset && pItem.parent) {
+ // Upewniamy się, że pItem istnieje i jest na scenie
+ var pWidth = pItem.visualAsset.width * pItem.scale.x;
+ var pHeight = pItem.visualAsset.height * pItem.scale.y;
+ // Proste sprawdzenie kolizji AABB (axis-aligned bounding box)
+ if (x >= pItem.x - pWidth / 2 && x <= pItem.x + pWidth / 2 && y >= pItem.y - pHeight / 2 && y <= pItem.y + pHeight / 2) {
+ // Dodatkowe sprawdzenie, czy jest blisko linii uderzenia (opcjonalne, ale zalecane)
+ // Możesz dostosować tolerancję (pHeight)
+ if (Math.abs(pItem.y - hitZoneY) < pHeight * 1.5) {
+ // Zwiększona tolerancja dla łatwiejszego trafienia
+ pItem.collect();
+ // swipeStart = null; // Możemy zresetować swipeStart, aby uniknąć przypadkowego swipe'a po zebraniu power-upa
+ // inputLocked = true; // Możemy na chwilę zablokować input
+ // LK.setTimeout(function () { inputLocked = false; }, 50); // Krótka blokada
+ return; // Zbieramy tylko jeden power-up na kliknięcie, kończymy obsługę tego eventu
+ }
+ }
+ }
+ }
+ // Reszta logiki z oryginalnej funkcji game.down
if (inputLocked) {
return;
}
swipeStart = {
@@ -967,11 +1216,8 @@
inputLocked = false;
}, 120);
return;
}
- // Upewnij się, że te linie są usunięte z tego miejsca:
- // swipedNoteSuccessfully = true;
- // notesActuallyHitCount++;
};
game.up = function (x, y, obj) {
if (inputLocked || !swipeStart) {
swipeStart = null;
@@ -1089,10 +1335,13 @@
game.move = function (x, y, obj) {};
game.update = function () {
if (!hpBarsInitialized) {
setupHpBars();
+ lastPowerUpSpawnAttemptTime = Date.now();
hpBarsInitialized = true;
}
+ trySpawnRandomPowerUp();
+ managePowerUpItems();
spawnNotes();
for (var i = 0; i < notes.length; i++) {
if (notes[i] && notes[i].update) {
notes[i].update();