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 (7 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 (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
User prompt
Beat Swipe: Rhythm Saber
Initial prompt
Create a 2D rhythm game framework inspired by Beat Saber. Game mechanics: - Notes (circles or shapes) appear far away and grow in size as they "approach" the player, simulating a forward motion toward the screen. - There are 3 types of notes: 1. Tap notes — the player must click exactly when the note reaches maximum size. 2. Swipe notes — the player must perform a quick swipe with the mouse through the note in a given direction (left, right, up, down). 3. Trap notes — these must be avoided entirely. Clicking or swiping them results in a combo break. - Notes appear at specific times and types defined in an array. Controls: - Mouse only. Recognize: - Clicks (onMouseDown + release timing). - Swipes (track onMouseDown → onMouseUp direction and distance). Scoring: - Perfect timing (±0.1 sec) gives max points. - Good timing (±0.3 sec) gives half points. - Missed or incorrect interaction resets combo. - Trap interaction resets combo too. Other features: - Notes scale up as they approach to simulate perspective. - Show combo counter and score. - Simple placeholder graphics and dark background are fine. - Music and visuals will be added manually later. Code structure must clearly separate: - Note spawn logic. - Input detection (click + swipe). - Timing window checks. - Scoring and combo logic. Use simple assets for now (colored shapes). Add comments to explain functions.
/**** * 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; 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) { var feedback = LK.getAsset('hitFeedback', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: 0.7, scaleY: 0.7, alpha: 0.7 }); if (result === 'perfect') { feedback.tint = 0xffff00; } else if (result === 'good') { feedback.tint = 0x00ff00; } else { feedback.tint = 0xff0000; } self.addChild(feedback); tween(feedback, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 350, easing: tween.easeOut, onFinish: function onFinish() { if (feedback.parent) { feedback.destroy(); } } }); }; self.update = function () { var now = Date.now(); 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; if (progress < 0) { progress = 0; } if (progress > 1) { progress = 1; } var currentScale = self.scaleStart + (self.scaleEnd - self.scaleStart) * progress; self.scale.x = Math.max(0.01, currentScale); self.scale.y = Math.max(0.01, currentScale); self.x = self.centerX; self.y = self.startY + (self.centerY - self.startY) * progress; if (!self.judged && now > self.targetHitTime + hitWindowGood) { self.judged = true; if (self.noteType !== 'trap') { game.onNoteMiss(self); } } }; 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; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181828 }); /**** * Game Code ****/ var gameScreenWidth = 2048; var playfieldWidth = 1408; // Narrower playfield var playfieldStartX = (gameScreenWidth - playfieldWidth) / 2; // 320 var NUM_COLUMNS = 4; var columnWidth = playfieldWidth / NUM_COLUMNS; // 352 var columnCenterXs = []; for (var i = 0; i < NUM_COLUMNS; i++) { columnCenterXs.push(playfieldStartX + i * columnWidth + columnWidth / 2); } // New columnCenterXs for playfieldWidth = 1408, starting at X = 320: // columnCenterXs[0] = 320 + 176 = 496 // columnCenterXs[1] = 496 + 352 = 848 // columnCenterXs[2] = 848 + 352 = 1200 // columnCenterXs[3] = 1200 + 352 = 1552 var SWIPE_NOTE_WIDTH = 160; var allSongData = { "defaultTestTrack": { musicAsset: null, // Na razie brak przypisanego pliku muzycznego rhythmMap: [{ 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', partOfWiderSwipe: 'leftHalf', widerSwipePairCenterX: (columnCenterXs[1] + columnCenterXs[2]) / 2 }, { time: 6600, type: 'swipe', swipeDir: 'right', partOfWiderSwipe: 'rightHalf', widerSwipePairCenterX: (columnCenterXs[1] + columnCenterXs[2]) / 2 }, { time: 7400, type: 'tap', columnIndex: 0 }, { time: 8200, type: 'trap', columnIndex: 3 }, { time: 9000, type: 'swipe', swipeDir: 'down', columnIndex: 1 }, { time: 9800, type: 'swipe', swipeDir: 'left', partOfWiderSwipe: 'leftHalf', widerSwipePairCenterX: (columnCenterXs[0] + columnCenterXs[1]) / 2 }, { time: 9800, type: 'swipe', swipeDir: 'left', partOfWiderSwipe: 'rightHalf', widerSwipePairCenterX: (columnCenterXs[0] + columnCenterXs[1]) / 2 }, { time: 10600, type: 'tap', columnIndex: 3 }, { time: 11400, type: 'swipe', swipeDir: 'up', columnIndex: 0 }, { time: 12200, type: 'trap', columnIndex: 1 }, { time: 13000, type: 'tap', columnIndex: 2 }] } // Tutaj w przyszłości dodamy piosenki test1, test2, test3 z Twoimi mapami // np. "test1": { musicAsset: "test1", rhythmMap: [ ... mapa dla test1 ... ] } }; var currentActiveRhythmMap = null; // Ta zmienna będzie przechowywać mapę aktywnej piosenki var currentMusic = null; var rhythmMap = [{ 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 }, { // Wider swipe between new col 1 and col 2 (centers 848 and 1200) // Midpoint is (848 + 1200) / 2 = 1024 time: 6600, type: 'swipe', swipeDir: 'right', partOfWiderSwipe: 'leftHalf', widerSwipePairCenterX: (columnCenterXs[1] + columnCenterXs[2]) / 2 }, { time: 6600, type: 'swipe', swipeDir: 'right', partOfWiderSwipe: 'rightHalf', widerSwipePairCenterX: (columnCenterXs[1] + columnCenterXs[2]) / 2 }, { time: 7400, type: 'tap', columnIndex: 0 }, { time: 8200, type: 'trap', columnIndex: 3 }, { time: 9000, type: 'swipe', swipeDir: 'down', columnIndex: 1 }, { // Wider swipe between new col 0 and col 1 (centers 496 and 848) // Midpoint is (496 + 848) / 2 = 672 time: 9800, type: 'swipe', swipeDir: 'left', partOfWiderSwipe: 'leftHalf', widerSwipePairCenterX: (columnCenterXs[0] + columnCenterXs[1]) / 2 }, { time: 9800, type: 'swipe', swipeDir: 'left', partOfWiderSwipe: 'rightHalf', widerSwipePairCenterX: (columnCenterXs[0] + columnCenterXs[1]) / 2 }, { time: 10600, type: 'tap', columnIndex: 3 }, { time: 11400, type: 'swipe', swipeDir: 'up', columnIndex: 0 }, { time: 12200, type: 'trap', columnIndex: 1 }, { time: 13000, type: 'tap', columnIndex: 2 }]; var noteTravelTime = 1200; var hitWindowPerfect = 120; 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 scoreTxt = new Text2('Score: 0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.x = gameScreenWidth / 2; scoreTxt.y = 20; LK.gui.top.addChild(scoreTxt); var comboTxt = new Text2('Combo: 0', { size: 80, fill: 0xFFFF00 }); comboTxt.anchor.set(0.5, 0); comboTxt.x = gameScreenWidth / 2; comboTxt.y = 130; LK.gui.top.addChild(comboTxt); var hitZoneY = 1800; var hitZoneVisualWidth = playfieldWidth; // Line width matches the new playfield width var hitZoneLine = LK.getAsset('lineAsset', { anchorX: 0.5, anchorY: 0.5, x: gameScreenWidth / 2, // Centered on the whole screen, but visual width is playfieldWidth 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'); } function spawnNotes() { var now = Date.now(); while (nextNoteIdx < rhythmMap.length) { var noteData = rhythmMap[nextNoteIdx]; var noteTargetHitTime = gameStartTime + noteData.time; var noteVisualSpawnTime = noteTargetHitTime - noteTravelTime; if (noteVisualSpawnTime <= now) { var targetCenterX; if (noteData.partOfWiderSwipe && noteData.widerSwipePairCenterX !== undefined) { // Ensure the noteAsset (specifically swipeNote) is defined for SWIPE_NOTE_WIDTH // For wider swipes, their visual representation is two standard swipe notes if (noteData.partOfWiderSwipe === 'leftHalf') { targetCenterX = noteData.widerSwipePairCenterX - SWIPE_NOTE_WIDTH / 2; } else if (noteData.partOfWiderSwipe === 'rightHalf') { targetCenterX = noteData.widerSwipePairCenterX + SWIPE_NOTE_WIDTH / 2; } else { targetCenterX = columnCenterXs[noteData.columnIndex !== undefined ? noteData.columnIndex : Math.floor(NUM_COLUMNS / 2)]; } } else if (noteData.columnIndex !== undefined && noteData.columnIndex >= 0 && noteData.columnIndex < NUM_COLUMNS) { targetCenterX = columnCenterXs[noteData.columnIndex]; } else { // Fallback for notes without proper column/widerSwipe info (e.g. old map format) // Place in the center of the new, narrower playfield targetCenterX = playfieldStartX + playfieldWidth / 2; } var n = new Note(noteData.type, noteData.swipeDir, noteTargetHitTime, targetCenterX); 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]; if (n.judged && now > n.targetHitTime + hitWindowGood + 400) { if (n.parent) { n.destroy(); } notes.splice(i, 1); } else if (!n.judged && now > n.targetHitTime + noteTravelTime + 500) { 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; } // Should not happen if note is properly initialized var currentNoteWidth = currentNoteAsset.width * n.scale.x; var currentNoteHeight = currentNoteAsset.height * n.scale.y; // If swipeNote, use SWIPE_NOTE_WIDTH for consistency, as its asset is square 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); } function resetCombo() { combo = 0; comboTxt.setText('Combo: 0'); } function loadSong(songKey) { var songData = allSongData[songKey]; if (!songData) { console.log("Error: Song data not found for key: " + songKey); // Możemy tu załadować jakąś domyślną mapę lub wyświetlić błąd if (allSongData["defaultTestTrack"]) { songData = allSongData["defaultTestTrack"]; } else { currentActiveRhythmMap = []; // Pusta mapa, jeśli nic nie ma return; } } currentActiveRhythmMap = songData.rhythmMap; // Zatrzymanie poprzedniej muzyki, jeśli grała if (currentMusic && typeof currentMusic.stop === 'function') { currentMusic.stop(); } currentMusic = null; // Ładowanie i odtwarzanie nowej muzyki (jeśli zdefiniowano asset) if (songData.musicAsset) { currentMusic = LK.getSound(songData.musicAsset); // Załóżmy, że masz assety test1, test2, test3 if (currentMusic && typeof currentMusic.play === 'function') { // currentMusic.play(); // Odtwarzanie muzyki - na razie zakomentowane console.log("Attempting to play music asset: " + songData.musicAsset); } else { console.log("Warning: Music asset '" + songData.musicAsset + "' not found or not playable."); } } resetGameState(); // Resetuje m.in. notatki, wynik, combo nextNoteIdx = 0; // Upewnij się, że resetujesz indeks następnej notatki dla nowej mapy gameStartTime = Date.now(); // Ustaw czas startu dla nowej piosenki console.log("Loaded song: " + songKey); } function checkGameEnd() { if (nextNoteIdx >= rhythmMap.length && notes.length === 0) { LK.setTimeout(function () { if (nextNoteIdx >= rhythmMap.length && notes.length === 0) { LK.showYouWin(); } }, 1000); } } game.onNoteMiss = function (note) { if (!note || note.judged) { return; } note.judged = true; note.showHitFeedback('miss'); resetCombo(); if (note.parent) { LK.effects.flashObject(note, 0xff0000, 300); } }; game.down = function (x, y, obj) { 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); 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(); } 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) { 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) { if (Math.abs(n.y - n.centerY) < noteCurrentHeight / 1.5) { notesHitThisSwipe.push(n); } } } } if (notesHitThisSwipe.length > 0) { notesHitThisSwipe.sort(function (a, b) { var da = Math.abs(swipeEndTime - a.targetHitTime); // Or use n.y proximity to centerY var db = Math.abs(swipeEndTime - b.targetHitTime); return da - db; }); var maxNotesToHitPerSwipe = notesHitThisSwipe.length > 1 && notesHitThisSwipe[0].widerSwipePairCenterX !== undefined ? 2 : 1; // If it's a wider swipe candidate, allow hitting up to 2, otherwise just 1. // This check is a bit simplistic, assumes wider swipe notes are sorted first if both are hit. // A more robust way would be to check if the hit notes form a valid wider swipe pair. var notesActuallyHitCount = 0; for (var k = 0; k < notesHitThisSwipe.length && notesActuallyHitCount < maxNotesToHitPerSwipe; k++) { var noteToJudge = notesHitThisSwipe[k]; if (noteToJudge.judged) { continue; } var result = noteToJudge.getHitAccuracy(); noteToJudge.judged = true; noteToJudge.showHitFeedback(result); if (result !== 'miss') { addScore(result); addCombo(); } else { resetCombo(); } swipedNoteSuccessfully = true; notesActuallyHitCount++; } } } inputLocked = true; LK.setTimeout(function () { inputLocked = false; }, 80); swipeStart = null; }; game.move = function (x, y, obj) {}; game.update = function () { spawnNotes(); for (var i = 0; i < notes.length; i++) { if (notes[i] && notes[i].update) { notes[i].update(); } } removeOldNotes(); checkGameEnd(); }; resetGameState(); gameStartTime = Date.now();
===================================================================
--- original.js
+++ change.js
@@ -5,9 +5,8 @@
/****
* Classes
****/
-// Visual width of swipeNote asset
var Note = Container.expand(function (noteType, swipeDir, targetHitTimeFull, centerXVal) {
var self = Container.call(this);
self.noteType = noteType || 'tap';
self.swipeDir = swipeDir || null;
@@ -72,9 +71,8 @@
feedback.tint = 0x00ff00;
} else {
feedback.tint = 0xff0000;
}
- // Ensure feedback is added to the note itself, so its position is relative
self.addChild(feedback);
tween(feedback, {
alpha: 0,
scaleX: 1.5,
@@ -106,9 +104,8 @@
if (progress > 1) {
progress = 1;
}
var currentScale = self.scaleStart + (self.scaleEnd - self.scaleStart) * progress;
- // Ensure scale doesn't go to 0 or negative if times are weird
self.scale.x = Math.max(0.01, currentScale);
self.scale.y = Math.max(0.01, currentScale);
self.x = self.centerX;
self.y = self.startY + (self.centerY - self.startY) * progress;
@@ -148,16 +145,117 @@
/****
* Game Code
****/
var gameScreenWidth = 2048;
+var playfieldWidth = 1408; // Narrower playfield
+var playfieldStartX = (gameScreenWidth - playfieldWidth) / 2; // 320
var NUM_COLUMNS = 4;
-var columnWidth = gameScreenWidth / NUM_COLUMNS; // 512
+var columnWidth = playfieldWidth / NUM_COLUMNS; // 352
var columnCenterXs = [];
for (var i = 0; i < NUM_COLUMNS; i++) {
- columnCenterXs.push(i * columnWidth + columnWidth / 2);
+ columnCenterXs.push(playfieldStartX + i * columnWidth + columnWidth / 2);
}
-// columnCenterXs will be [256, 768, 1280, 1792]
+// New columnCenterXs for playfieldWidth = 1408, starting at X = 320:
+// columnCenterXs[0] = 320 + 176 = 496
+// columnCenterXs[1] = 496 + 352 = 848
+// columnCenterXs[2] = 848 + 352 = 1200
+// columnCenterXs[3] = 1200 + 352 = 1552
var SWIPE_NOTE_WIDTH = 160;
+var allSongData = {
+ "defaultTestTrack": {
+ musicAsset: null,
+ // Na razie brak przypisanego pliku muzycznego
+ rhythmMap: [{
+ 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',
+ partOfWiderSwipe: 'leftHalf',
+ widerSwipePairCenterX: (columnCenterXs[1] + columnCenterXs[2]) / 2
+ }, {
+ time: 6600,
+ type: 'swipe',
+ swipeDir: 'right',
+ partOfWiderSwipe: 'rightHalf',
+ widerSwipePairCenterX: (columnCenterXs[1] + columnCenterXs[2]) / 2
+ }, {
+ time: 7400,
+ type: 'tap',
+ columnIndex: 0
+ }, {
+ time: 8200,
+ type: 'trap',
+ columnIndex: 3
+ }, {
+ time: 9000,
+ type: 'swipe',
+ swipeDir: 'down',
+ columnIndex: 1
+ }, {
+ time: 9800,
+ type: 'swipe',
+ swipeDir: 'left',
+ partOfWiderSwipe: 'leftHalf',
+ widerSwipePairCenterX: (columnCenterXs[0] + columnCenterXs[1]) / 2
+ }, {
+ time: 9800,
+ type: 'swipe',
+ swipeDir: 'left',
+ partOfWiderSwipe: 'rightHalf',
+ widerSwipePairCenterX: (columnCenterXs[0] + columnCenterXs[1]) / 2
+ }, {
+ time: 10600,
+ type: 'tap',
+ columnIndex: 3
+ }, {
+ time: 11400,
+ type: 'swipe',
+ swipeDir: 'up',
+ columnIndex: 0
+ }, {
+ time: 12200,
+ type: 'trap',
+ columnIndex: 1
+ }, {
+ time: 13000,
+ type: 'tap',
+ columnIndex: 2
+ }]
+ }
+ // Tutaj w przyszłości dodamy piosenki test1, test2, test3 z Twoimi mapami
+ // np. "test1": { musicAsset: "test1", rhythmMap: [ ... mapa dla test1 ... ] }
+};
+var currentActiveRhythmMap = null; // Ta zmienna będzie przechowywać mapę aktywnej piosenki
+var currentMusic = null;
var rhythmMap = [{
time: 1000,
type: 'tap',
columnIndex: 0
@@ -187,9 +285,10 @@
time: 5800,
type: 'tap',
columnIndex: 2
}, {
- // Example of a "wider swipe" (horizontal right)
+ // Wider swipe between new col 1 and col 2 (centers 848 and 1200)
+ // Midpoint is (848 + 1200) / 2 = 1024
time: 6600,
type: 'swipe',
swipeDir: 'right',
partOfWiderSwipe: 'leftHalf',
@@ -213,9 +312,10 @@
type: 'swipe',
swipeDir: 'down',
columnIndex: 1
}, {
- // Example of another "wider swipe" (horizontal left)
+ // Wider swipe between new col 0 and col 1 (centers 496 and 848)
+ // Midpoint is (496 + 848) / 2 = 672
time: 9800,
type: 'swipe',
swipeDir: 'left',
partOfWiderSwipe: 'leftHalf',
@@ -272,15 +372,16 @@
comboTxt.x = gameScreenWidth / 2;
comboTxt.y = 130;
LK.gui.top.addChild(comboTxt);
var hitZoneY = 1800;
-var hitZoneWidth = gameScreenWidth * 0.95;
+var hitZoneVisualWidth = playfieldWidth; // Line width matches the new playfield width
var hitZoneLine = LK.getAsset('lineAsset', {
anchorX: 0.5,
anchorY: 0.5,
x: gameScreenWidth / 2,
+ // Centered on the whole screen, but visual width is playfieldWidth
y: hitZoneY,
- width: hitZoneWidth,
+ width: hitZoneVisualWidth,
height: 4,
alpha: 0.6
});
game.addChild(hitZoneLine);
@@ -311,20 +412,23 @@
var noteVisualSpawnTime = noteTargetHitTime - noteTravelTime;
if (noteVisualSpawnTime <= now) {
var targetCenterX;
if (noteData.partOfWiderSwipe && noteData.widerSwipePairCenterX !== undefined) {
+ // Ensure the noteAsset (specifically swipeNote) is defined for SWIPE_NOTE_WIDTH
+ // For wider swipes, their visual representation is two standard swipe notes
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
targetCenterX = columnCenterXs[noteData.columnIndex !== undefined ? noteData.columnIndex : Math.floor(NUM_COLUMNS / 2)];
}
} else if (noteData.columnIndex !== undefined && noteData.columnIndex >= 0 && noteData.columnIndex < NUM_COLUMNS) {
targetCenterX = columnCenterXs[noteData.columnIndex];
} else {
- targetCenterX = gameScreenWidth / 2; // Fallback for notes without proper column/widerSwipe info
+ // Fallback for notes without proper column/widerSwipe info (e.g. old map format)
+ // Place in the center of the new, narrower playfield
+ targetCenterX = playfieldStartX + playfieldWidth / 2;
}
var n = new Note(noteData.type, noteData.swipeDir, noteTargetHitTime, targetCenterX);
n.alpha = 0;
notes.push(n);
@@ -364,10 +468,19 @@
var timeDiff = Math.abs(now - n.targetHitTime);
if (timeDiff > hitWindowGood) {
continue;
}
- var currentNoteWidth = (n.noteType === 'swipe' ? SWIPE_NOTE_WIDTH : n.noteAsset.width) * n.scale.x;
- var currentNoteHeight = (n.noteType === 'swipe' ? SWIPE_NOTE_WIDTH : n.noteAsset.height) * n.scale.y; // Assuming swipe notes are square
+ var currentNoteAsset = n.noteAsset;
+ if (!currentNoteAsset) {
+ continue;
+ } // Should not happen if note is properly initialized
+ var currentNoteWidth = currentNoteAsset.width * n.scale.x;
+ var currentNoteHeight = currentNoteAsset.height * n.scale.y;
+ // If swipeNote, use SWIPE_NOTE_WIDTH for consistency, as its asset is square
+ 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) {
@@ -394,10 +507,43 @@
comboTxt.setText('Combo: ' + combo);
}
function resetCombo() {
combo = 0;
- comboTxt.setText('Combo: ' + combo);
+ comboTxt.setText('Combo: 0');
}
+function loadSong(songKey) {
+ var songData = allSongData[songKey];
+ if (!songData) {
+ console.log("Error: Song data not found for key: " + songKey);
+ // Możemy tu załadować jakąś domyślną mapę lub wyświetlić błąd
+ if (allSongData["defaultTestTrack"]) {
+ songData = allSongData["defaultTestTrack"];
+ } else {
+ currentActiveRhythmMap = []; // Pusta mapa, jeśli nic nie ma
+ return;
+ }
+ }
+ currentActiveRhythmMap = songData.rhythmMap;
+ // Zatrzymanie poprzedniej muzyki, jeśli grała
+ if (currentMusic && typeof currentMusic.stop === 'function') {
+ currentMusic.stop();
+ }
+ currentMusic = null;
+ // Ładowanie i odtwarzanie nowej muzyki (jeśli zdefiniowano asset)
+ if (songData.musicAsset) {
+ currentMusic = LK.getSound(songData.musicAsset); // Załóżmy, że masz assety test1, test2, test3
+ if (currentMusic && typeof currentMusic.play === 'function') {
+ // currentMusic.play(); // Odtwarzanie muzyki - na razie zakomentowane
+ console.log("Attempting to play music asset: " + songData.musicAsset);
+ } else {
+ console.log("Warning: Music asset '" + songData.musicAsset + "' not found or not playable.");
+ }
+ }
+ resetGameState(); // Resetuje m.in. notatki, wynik, combo
+ nextNoteIdx = 0; // Upewnij się, że resetujesz indeks następnej notatki dla nowej mapy
+ gameStartTime = Date.now(); // Ustaw czas startu dla nowej piosenki
+ console.log("Loaded song: " + songKey);
+}
function checkGameEnd() {
if (nextNoteIdx >= rhythmMap.length && notes.length === 0) {
LK.setTimeout(function () {
if (nextNoteIdx >= rhythmMap.length && notes.length === 0) {
@@ -457,9 +603,8 @@
}
};
game.up = function (x, y, obj) {
if (inputLocked || !swipeStart) {
- console.log("Swipe Rejected: Input locked or no swipe start");
swipeStart = null;
return;
}
var swipeEndX = x;
@@ -467,9 +612,8 @@
var swipeEndTime = Date.now();
var dx = swipeEndX - swipeStart.x;
var dy = swipeEndY - swipeStart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
- console.log("Swipe End: X=" + swipeEndX + " Y=" + swipeEndY + " Dist=" + dist);
var potentialSwipe = dist >= MIN_SWIPE_DISTANCE;
var swipedNoteSuccessfully = false;
if (potentialSwipe) {
var detectedDir = null;
@@ -477,42 +621,28 @@
detectedDir = dx > 0 ? 'right' : 'left';
} else {
detectedDir = dy > 0 ? 'down' : 'up';
}
- console.log("Potential Swipe Detected. Dir: " + detectedDir);
var swipeBoundingBox = {
x: Math.min(swipeStart.x, swipeEndX),
y: Math.min(swipeStart.y, swipeEndY),
width: Math.abs(dx),
height: Math.abs(dy)
};
- // Safer logging for swipeBoundingBox
- console.log("Swipe BBox: x=" + swipeBoundingBox.x + ", y=" + swipeBoundingBox.y + ", w=" + swipeBoundingBox.width + ", h=" + swipeBoundingBox.height);
var notesHitThisSwipe = [];
for (var i = 0; i < notes.length; i++) {
var n = notes[i];
- // console.log("Checking Note " + i + ": Type=" + n.noteType + " Judged=" + n.judged + " TargetX=" + n.centerX + " TargetY=" + n.centerY + " CurrentY=" + n.y);
if (n.judged || n.noteType !== 'swipe') {
continue;
}
- // console.log("Note " + i + " is a potential swipe target.");
var overallSwipeTimeMatchesNote = false;
- if (swipeStart.time <= n.targetHitTime + hitWindowGood && swipeEndTime >= n.targetHitTime - hitWindowGood) {
+ if (n.targetHitTime >= swipeStart.time - hitWindowGood && n.targetHitTime <= swipeEndTime + hitWindowGood) {
overallSwipeTimeMatchesNote = true;
}
- // This secondary check is redundant if the first one is inclusive enough.
- // if (!overallSwipeTimeMatchesNote) {
- // if (swipeStart.time <= n.targetHitTime + hitWindowGood && swipeEndTime >= n.targetHitTime - hitWindowGood) {
- // overallSwipeTimeMatchesNote = true;
- // }
- // }
- // console.log("Note " + i + " - overallSwipeTimeMatchesNote: " + overallSwipeTimeMatchesNote + " (SwipeStart: " + swipeStart.time + ", SwipeEnd: " + swipeEndTime + ", NoteTarget: " + n.targetHitTime + ", Window: " + hitWindowGood + ")");
if (!overallSwipeTimeMatchesNote) {
- // console.log("Note " + i + " rejected: Swipe time does not match note time window.");
continue;
}
if (n.alpha === 0) {
- // console.log("Note " + i + " rejected: Note alpha is 0 (not visible/active).");
continue;
}
var noteCurrentWidth = SWIPE_NOTE_WIDTH * n.scale.x;
var noteCurrentHeight = SWIPE_NOTE_WIDTH * n.scale.y;
@@ -521,45 +651,33 @@
y: n.y - noteCurrentHeight / 2,
width: noteCurrentWidth,
height: noteCurrentHeight
};
- // console.log("Note " + i + " BBox: x=" + noteBoundingBox.x + ", y=" + noteBoundingBox.y + ", w=" + noteBoundingBox.width + ", h=" + noteBoundingBox.height);
if (rectsIntersect(swipeBoundingBox, noteBoundingBox)) {
- console.log("Note " + i + " INTERSECTS with swipe BBox.");
if (detectedDir === n.swipeDir) {
- console.log("Note " + i + " Direction MATCH. Detected: " + detectedDir + ", Expected: " + n.swipeDir);
- var verticalCheckTolerance = noteCurrentHeight / 1.5; // Height of note plus some tolerance
- // console.log("Note " + i + " Vertical Check: |" + n.y + " - " + n.centerY + "| < " + verticalCheckTolerance + " ? Result: " + (Math.abs(n.y - n.centerY) < verticalCheckTolerance));
- if (Math.abs(n.y - n.centerY) < verticalCheckTolerance) {
- console.log("Note " + i + " PASSED vertical proximity check. Adding to hits.");
+ if (Math.abs(n.y - n.centerY) < noteCurrentHeight / 1.5) {
notesHitThisSwipe.push(n);
- } else {
- // console.log("Note " + i + " FAILED vertical proximity check.");
}
- } else {
- // console.log("Note " + i + " Direction MISMATCH. Detected: " + detectedDir + ", Expected: " + n.swipeDir);
}
- } else {
- // console.log("Note " + i + " does NOT intersect with swipe BBox.");
}
}
if (notesHitThisSwipe.length > 0) {
- console.log("Processing " + notesHitThisSwipe.length + " notes hit by this swipe.");
notesHitThisSwipe.sort(function (a, b) {
- var da = Math.abs(swipeEndTime - a.targetHitTime);
+ var da = Math.abs(swipeEndTime - a.targetHitTime); // Or use n.y proximity to centerY
var db = Math.abs(swipeEndTime - b.targetHitTime);
return da - db;
});
- var maxNotesToHitPerSwipe = 2;
+ var maxNotesToHitPerSwipe = notesHitThisSwipe.length > 1 && notesHitThisSwipe[0].widerSwipePairCenterX !== undefined ? 2 : 1;
+ // If it's a wider swipe candidate, allow hitting up to 2, otherwise just 1.
+ // This check is a bit simplistic, assumes wider swipe notes are sorted first if both are hit.
+ // A more robust way would be to check if the hit notes form a valid wider swipe pair.
var notesActuallyHitCount = 0;
- for (var k = 0; k < notesHitThisSwipe.length && k < maxNotesToHitPerSwipe; k++) {
+ for (var k = 0; k < notesHitThisSwipe.length && notesActuallyHitCount < maxNotesToHitPerSwipe; k++) {
var noteToJudge = notesHitThisSwipe[k];
if (noteToJudge.judged) {
- // console.log("Note (ID/Ref might be useful here) already judged, skipping.");
continue;
}
var result = noteToJudge.getHitAccuracy();
- console.log("Judging Note Hit! Result: " + result);
noteToJudge.judged = true;
noteToJudge.showHitFeedback(result);
if (result !== 'miss') {
addScore(result);
@@ -569,13 +687,9 @@
}
swipedNoteSuccessfully = true;
notesActuallyHitCount++;
}
- } else {
- console.log("No notes were successfully hit by this swipe action.");
}
- } else {
- console.log("Swipe too short or not considered potential.");
}
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;