Code edit (1 edits merged)
Please save this source code
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
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
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Note = Container.expand(function (noteType, swipeDir, targetHitTimeFull) {
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 = 2048 / 2;
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; // Initially invisible, will become visible when it's time
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; // Ensure it's invisible before visual spawn time
return;
}
if (self.alpha === 0) {
// First time it becomes visible
self.alpha = 1;
}
var elapsedTimeSinceSpawn = now - self.visualSpawnTime;
var progress = elapsedTimeSinceSpawn / noteTravelTime;
if (progress < 0) {
progress = 0;
}
if (progress > 1) {
progress = 1;
}
var scale = self.scaleStart + (self.scaleEnd - self.scaleStart) * progress;
self.scale.x = scale;
self.scale.y = scale;
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);
} else {
// Trap notes just disappear if not hit, no miss penalty unless clicked
}
}
};
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 rhythmMap = [{
time: 1000,
type: 'tap'
}, {
time: 1800,
type: 'tap'
}, {
time: 2600,
type: 'swipe',
swipeDir: 'left'
}, {
time: 3400,
type: 'tap'
}, {
time: 4200,
type: 'swipe',
swipeDir: 'right'
}, {
time: 5000,
type: 'trap'
}, {
time: 5800,
type: 'tap'
}, {
time: 6600,
type: 'swipe',
swipeDir: 'up'
}, {
time: 7400,
type: 'tap'
}, {
time: 8200,
type: 'trap'
}, {
time: 9000,
type: 'swipe',
swipeDir: 'down'
}, {
time: 9800,
type: 'tap'
}, {
time: 10600,
type: 'tap'
}, {
time: 11400,
type: 'swipe',
swipeDir: 'left'
}, {
time: 12200,
type: 'trap'
}, {
time: 13000,
type: 'tap'
}];
var noteTravelTime = 1200;
var hitWindowPerfect = 120;
var hitWindowGood = 260;
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 = 2048 / 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 = 2048 / 2;
comboTxt.y = 130;
LK.gui.top.addChild(comboTxt);
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 n = new Note(noteData.type, noteData.swipeDir, noteTargetHitTime);
n.x = n.centerX;
n.y = n.startY;
n.scale.x = n.scaleStart;
n.scale.y = n.scaleStart;
n.alpha = 0; // Will be set to 1 in its own update when it's time
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) {
// Failsafe for notes that somehow weren't judged
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 dx = x - n.x;
var dy = y - n.y;
var noteRadiusOrWidth = n.noteAsset.width * n.scale.x / 2;
var noteHeight = n.noteAsset.height * n.scale.y / 2;
if (typeToFind === 'swipe') {
if (dx >= -noteRadiusOrWidth && dx <= noteRadiusOrWidth && dy >= -noteHeight && dy <= noteHeight) {
if (timeDiff < smallestTimeDiff) {
bestNote = n;
smallestTimeDiff = timeDiff;
}
}
} else {
// tap or trap
if (dx * dx + dy * dy <= noteRadiusOrWidth * noteRadiusOrWidth) {
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 checkGameEnd() {
if (nextNoteIdx >= rhythmMap.length && notes.length === 0) {
// A small delay before showing "You Win" to let last feedback animations play
LK.setTimeout(function () {
if (nextNoteIdx >= rhythmMap.length && notes.length === 0) {
// Double check in case of quick restart
LK.showYouWin();
}
}, 1000);
}
}
game.onNoteMiss = function (note) {
if (note.judged) return; // Already handled
note.judged = true;
note.showHitFeedback('miss');
resetCombo();
LK.effects.flashObject(note, 0xff0000, 300);
};
game.down = function (x, y, obj) {
if (inputLocked) {
return;
}
swipeStart = {
x: x,
y: y,
time: Date.now(),
note: null
};
var trap = findNoteAt(x, y, 'trap');
if (trap && !trap.judged && trap.isInHitWindow()) {
trap.judged = true;
trap.showHitFeedback('miss');
resetCombo();
LK.effects.flashScreen(0xff0000, 400);
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;
}, 200);
return;
}
var tap = findNoteAt(x, y, 'tap');
if (tap && !tap.judged && tap.isInHitWindow()) {
var result = tap.getHitAccuracy();
tap.judged = true;
tap.showHitFeedback(result);
if (result !== 'miss') {
addScore(result);
addCombo();
} else {
resetCombo();
}
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;
}, 120);
return;
}
// For swipe, we only record the start here if it's over a swipe note.
// The actual swipe action is judged on game.up
var swipeNoteUnderCursor = findNoteAt(x, y, 'swipe');
if (swipeNoteUnderCursor && !swipeNoteUnderCursor.judged && swipeNoteUnderCursor.isInHitWindow()) {
swipeStart.note = swipeNoteUnderCursor;
}
};
game.up = function (x, y, obj) {
if (inputLocked || !swipeStart) {
swipeStart = null;
return;
}
var noteToJudge = swipeStart.note;
if (!noteToJudge || noteToJudge.judged || noteToJudge.noteType !== 'swipe') {
swipeStart = null;
return;
}
// Only judge if the swipe ended within a reasonable time window of the note's target time
if (!noteToJudge.isInHitWindow()) {
// swipeStart = null; // Optional: could also mark as miss if swipe was too late/early
return;
}
var dx = x - swipeStart.x;
var dy = y - swipeStart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
swipeStart = null;
return;
}
var detectedDir = null;
if (Math.abs(dx) > Math.abs(dy)) {
detectedDir = dx > 0 ? 'right' : 'left';
} else {
detectedDir = dy > 0 ? 'down' : 'up';
}
if (detectedDir === noteToJudge.swipeDir) {
var result = noteToJudge.getHitAccuracy();
noteToJudge.judged = true;
noteToJudge.showHitFeedback(result);
if (result !== 'miss') {
addScore(result);
addCombo();
} else {
resetCombo();
}
} else {
noteToJudge.judged = true;
noteToJudge.showHitFeedback('miss');
resetCombo();
LK.effects.flashObject(noteToJudge, 0xff0000, 300);
}
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;
}, 120);
swipeStart = null;
};
game.move = function (x, y, obj) {
// Potential drag handling for future, or for a different type of note
};
game.update = function () {
var now = Date.now();
spawnNotes();
for (var i = 0; i < notes.length; i++) {
if (notes[i]) {
// Check if note still exists
notes[i].update();
}
}
removeOldNotes();
checkGameEnd();
};
resetGameState();
gameStartTime = Date.now(); ===================================================================
--- original.js
+++ change.js
@@ -5,54 +5,57 @@
/****
* Classes
****/
-// Note Types: 'tap', 'swipe', 'trap'
-// Each note spawns at a given time (ms), type, and swipeDir (for swipe notes)
-var Note = Container.expand(function () {
+var Note = Container.expand(function (noteType, swipeDir, targetHitTimeFull) {
var self = Container.call(this);
- // Properties
- self.noteType = 'tap'; // 'tap', 'swipe', 'trap'
- self.swipeDir = null; // 'left', 'right', 'up', 'down' (for swipe notes)
- self.spawnTime = 0; // ms
- self.hit = false; // Whether this note has been hit
- self.judged = false; // Whether this note has been judged (hit/miss)
- self.scaleStart = 0.3; // Initial scale
- self.scaleEnd = 1.2; // Final scale at hit time
+ 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 = 2048 / 2;
- self.centerY = 1800; // Target area (bottom center)
- self.startY = 600; // Where notes start scaling up from
- // Attach asset based on note type
- var noteAsset;
+ self.centerY = 1800;
+ self.startY = 600;
+ self.noteAsset = null;
if (self.noteType === 'tap') {
- noteAsset = self.attachAsset('tapNote', {
+ self.noteAsset = self.attachAsset('tapNote', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.noteType === 'swipe') {
- noteAsset = self.attachAsset('swipeNote', {
+ 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') {
- noteAsset = self.attachAsset('trapNote', {
+ self.noteAsset = self.attachAsset('trapNote', {
anchorX: 0.5,
anchorY: 0.5
});
}
- self.noteAsset = noteAsset;
- // For swipe notes, add a direction indicator (arrow)
- if (self.noteType === 'swipe') {
- 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;
- }
- // For hit feedback
+ self.alpha = 0; // Initially invisible, will become visible when it's time
self.showHitFeedback = function (result) {
var feedback = LK.getAsset('hitFeedback', {
anchorX: 0.5,
anchorY: 0.5,
@@ -61,9 +64,15 @@
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;
+ 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,
@@ -71,46 +80,60 @@
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
- feedback.destroy();
+ if (feedback.parent) {
+ feedback.destroy();
+ }
}
});
};
- // Called every tick
self.update = function () {
- // Progress: 0 (spawn) to 1 (hit time)
var now = Date.now();
- var progress = (now - self.spawnTime) / noteTravelTime;
- if (progress < 0) progress = 0;
- if (progress > 1) progress = 1;
- // Scale up and move toward target
+ if (now < self.visualSpawnTime) {
+ self.alpha = 0; // Ensure it's invisible before visual spawn time
+ return;
+ }
+ if (self.alpha === 0) {
+ // First time it becomes visible
+ self.alpha = 1;
+ }
+ var elapsedTimeSinceSpawn = now - self.visualSpawnTime;
+ var progress = elapsedTimeSinceSpawn / noteTravelTime;
+ if (progress < 0) {
+ progress = 0;
+ }
+ if (progress > 1) {
+ progress = 1;
+ }
var scale = self.scaleStart + (self.scaleEnd - self.scaleStart) * progress;
self.scale.x = scale;
self.scale.y = scale;
- // Y position: from startY to centerY
self.x = self.centerX;
self.y = self.startY + (self.centerY - self.startY) * progress;
- // If not yet judged and past hit window, mark as miss
- if (!self.judged && now > self.spawnTime + hitWindowGood) {
+ if (!self.judged && now > self.targetHitTime + hitWindowGood) {
self.judged = true;
if (self.noteType !== 'trap') {
- // Missed note
game.onNoteMiss(self);
+ } else {
+ // Trap notes just disappear if not hit, no miss penalty unless clicked
}
}
};
- // For hit detection
self.isInHitWindow = function () {
var now = Date.now();
- var dt = Math.abs(now - self.spawnTime);
+ var dt = Math.abs(now - self.targetHitTime);
return dt <= hitWindowGood;
};
self.getHitAccuracy = function () {
var now = Date.now();
- var dt = Math.abs(now - self.spawnTime);
- if (dt <= hitWindowPerfect) return 'perfect';
- if (dt <= hitWindowGood) return 'good';
+ var dt = Math.abs(now - self.targetHitTime);
+ if (dt <= hitWindowPerfect) {
+ return 'perfect';
+ }
+ if (dt <= hitWindowGood) {
+ return 'good';
+ }
return 'miss';
};
return self;
});
@@ -124,14 +147,8 @@
/****
* Game Code
****/
-// Hit Feedback: White circle
-// Trap Note: Red triangle (simulate with a red ellipse for simplicity)
-// Swipe Note: Green rectangle
-// Tap Note: Blue circle
-// --- Rhythm Map (ms, type, [swipeDir]) ---
-// For MVP, a short hardcoded map
var rhythmMap = [{
time: 1000,
type: 'tap'
}, {
@@ -184,201 +201,173 @@
}, {
time: 13000,
type: 'tap'
}];
-// --- Timing Windows (ms) ---
-var noteTravelTime = 1200; // ms: time from spawn to hit area
-var hitWindowPerfect = 120; // ms
-var hitWindowGood = 260; // ms
-// --- State ---
+var noteTravelTime = 1200;
+var hitWindowPerfect = 120;
+var hitWindowGood = 260;
var notes = [];
var nextNoteIdx = 0;
var gameStartTime = 0;
var score = 0;
var combo = 0;
var maxCombo = 0;
-var lastInput = null; // {x, y, time}
-var swipeStart = null; // {x, y, time}
-var inputLocked = false; // Prevent double hits
-// --- GUI ---
+var swipeStart = null;
+var inputLocked = false;
var scoreTxt = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
+scoreTxt.x = 2048 / 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 = 2048 / 2;
+comboTxt.y = 130;
LK.gui.top.addChild(comboTxt);
-comboTxt.y = 120;
-// --- Helper: Reset State ---
function resetGameState() {
+ notes.forEach(function (n) {
+ if (n && n.parent) n.destroy();
+ });
notes = [];
nextNoteIdx = 0;
score = 0;
combo = 0;
maxCombo = 0;
- lastInput = null;
swipeStart = null;
inputLocked = false;
scoreTxt.setText('Score: 0');
comboTxt.setText('Combo: 0');
}
-// --- Helper: Spawn Notes ---
function spawnNotes() {
var now = Date.now();
while (nextNoteIdx < rhythmMap.length) {
var noteData = rhythmMap[nextNoteIdx];
- if (noteData.time - noteTravelTime <= now - gameStartTime) {
- // Spawn note
- var n = new Note();
- n.noteType = noteData.type;
- n.spawnTime = gameStartTime + noteData.time;
- if (n.noteType === 'swipe') n.swipeDir = noteData.swipeDir;
- // Re-attach asset for correct type
- n.removeChildren();
- if (n.noteType === 'tap') {
- n.noteAsset = n.attachAsset('tapNote', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- } else if (n.noteType === 'swipe') {
- n.noteAsset = n.attachAsset('swipeNote', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- var arrow = new Text2('', {
- size: 80,
- fill: 0xFFFFFF
- });
- arrow.anchor.set(0.5, 0.5);
- if (n.swipeDir === 'left') arrow.setText('←');else if (n.swipeDir === 'right') arrow.setText('→');else if (n.swipeDir === 'up') arrow.setText('↑');else if (n.swipeDir === 'down') arrow.setText('↓');
- n.addChild(arrow);
- n.arrow = arrow;
- } else if (n.noteType === 'trap') {
- n.noteAsset = n.attachAsset('trapNote', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- }
- n.scale.x = n.scaleStart;
- n.scale.y = n.scaleStart;
+ var noteTargetHitTime = gameStartTime + noteData.time;
+ var noteVisualSpawnTime = noteTargetHitTime - noteTravelTime;
+ if (noteVisualSpawnTime <= now) {
+ var n = new Note(noteData.type, noteData.swipeDir, noteTargetHitTime);
n.x = n.centerX;
n.y = n.startY;
+ n.scale.x = n.scaleStart;
+ n.scale.y = n.scaleStart;
+ n.alpha = 0; // Will be set to 1 in its own update when it's time
notes.push(n);
game.addChild(n);
nextNoteIdx++;
} else {
break;
}
}
}
-// --- Helper: Remove Old Notes ---
function removeOldNotes() {
var now = Date.now();
for (var i = notes.length - 1; i >= 0; i--) {
var n = notes[i];
- if (n.judged && now > n.spawnTime + hitWindowGood + 400) {
- n.destroy();
+ 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) {
+ // Failsafe for notes that somehow weren't judged
+ if (n.parent) {
+ n.destroy();
+ }
+ notes.splice(i, 1);
}
}
}
-// --- Helper: Find Closest Note in Hit Area ---
-function findNoteAt(x, y, type) {
+function findNoteAt(x, y, typeToFind) {
var now = Date.now();
- var best = null;
- var bestDt = 99999;
+ var bestNote = null;
+ var smallestTimeDiff = hitWindowGood + 1;
for (var i = 0; i < notes.length; i++) {
var n = notes[i];
- if (n.judged) continue;
- if (n.noteType !== type) continue;
- // Only consider notes in hit window
- var dt = Math.abs(now - n.spawnTime);
- if (dt > hitWindowGood) continue;
- // Check if input is within note's area (use scaled size)
+ if (n.judged || n.noteType !== typeToFind) {
+ continue;
+ }
+ var timeDiff = Math.abs(now - n.targetHitTime);
+ if (timeDiff > hitWindowGood) {
+ continue;
+ }
var dx = x - n.x;
var dy = y - n.y;
- var r = n.noteAsset.width * n.scale.x / 2;
- if (type === 'swipe') {
- // Rectangle: width/height
- var w = n.noteAsset.width * n.scale.x / 2;
- var h = n.noteAsset.height * n.scale.y / 2;
- if (dx < -w || dx > w || dy < -h || dy > h) continue;
+ var noteRadiusOrWidth = n.noteAsset.width * n.scale.x / 2;
+ var noteHeight = n.noteAsset.height * n.scale.y / 2;
+ if (typeToFind === 'swipe') {
+ if (dx >= -noteRadiusOrWidth && dx <= noteRadiusOrWidth && dy >= -noteHeight && dy <= noteHeight) {
+ if (timeDiff < smallestTimeDiff) {
+ bestNote = n;
+ smallestTimeDiff = timeDiff;
+ }
+ }
} else {
- // Circle: distance
- if (dx * dx + dy * dy > r * r) continue;
+ // tap or trap
+ if (dx * dx + dy * dy <= noteRadiusOrWidth * noteRadiusOrWidth) {
+ if (timeDiff < smallestTimeDiff) {
+ bestNote = n;
+ smallestTimeDiff = timeDiff;
+ }
+ }
}
- if (dt < bestDt) {
- best = n;
- bestDt = dt;
- }
}
- return best;
+ return bestNote;
}
-// --- Helper: Find Trap Note at Position ---
-function findTrapNoteAt(x, y) {
- var now = Date.now();
- for (var i = 0; i < notes.length; i++) {
- var n = notes[i];
- if (n.judged) continue;
- if (n.noteType !== 'trap') continue;
- var dt = Math.abs(now - n.spawnTime);
- if (dt > hitWindowGood) continue;
- var dx = x - n.x;
- var dy = y - n.y;
- var r = n.noteAsset.width * n.scale.x / 2;
- if (dx * dx + dy * dy <= r * r) return n;
- }
- return null;
-}
-// --- Helper: Score/Combo ---
function addScore(result) {
- if (result === 'perfect') score += 100;else if (result === 'good') score += 50;
+ if (result === 'perfect') {
+ score += 100;
+ } else if (result === 'good') {
+ score += 50;
+ }
scoreTxt.setText('Score: ' + score);
}
function addCombo() {
combo += 1;
- if (combo > maxCombo) maxCombo = combo;
+ if (combo > maxCombo) {
+ maxCombo = combo;
+ }
comboTxt.setText('Combo: ' + combo);
}
function resetCombo() {
combo = 0;
comboTxt.setText('Combo: 0');
}
-// --- Game Over/Win ---
function checkGameEnd() {
if (nextNoteIdx >= rhythmMap.length && notes.length === 0) {
- // All notes finished
- LK.showYouWin();
+ // A small delay before showing "You Win" to let last feedback animations play
+ LK.setTimeout(function () {
+ if (nextNoteIdx >= rhythmMap.length && notes.length === 0) {
+ // Double check in case of quick restart
+ LK.showYouWin();
+ }
+ }, 1000);
}
}
-// --- Miss Handler ---
game.onNoteMiss = function (note) {
+ if (note.judged) return; // Already handled
note.judged = true;
note.showHitFeedback('miss');
resetCombo();
LK.effects.flashObject(note, 0xff0000, 300);
};
-// --- Input Handling ---
game.down = function (x, y, obj) {
- if (inputLocked) return;
- lastInput = {
- x: x,
- y: y,
- time: Date.now()
- };
+ if (inputLocked) {
+ return;
+ }
swipeStart = {
x: x,
y: y,
- time: Date.now()
+ time: Date.now(),
+ note: null
};
- // Check for trap note
- var trap = findTrapNoteAt(x, y);
- if (trap && !trap.judged) {
+ var trap = findNoteAt(x, y, 'trap');
+ if (trap && !trap.judged && trap.isInHitWindow()) {
trap.judged = true;
trap.showHitFeedback('miss');
resetCombo();
LK.effects.flashScreen(0xff0000, 400);
@@ -387,11 +376,10 @@
inputLocked = false;
}, 200);
return;
}
- // Check for tap note
var tap = findNoteAt(x, y, 'tap');
- if (tap && !tap.judged) {
+ if (tap && !tap.judged && tap.isInHitWindow()) {
var result = tap.getHitAccuracy();
tap.judged = true;
tap.showHitFeedback(result);
if (result !== 'miss') {
@@ -405,75 +393,78 @@
inputLocked = false;
}, 120);
return;
}
- // Check for swipe note (start of swipe)
- var swipe = findNoteAt(x, y, 'swipe');
- if (swipe && !swipe.judged) {
- swipeStart.note = swipe;
+ // For swipe, we only record the start here if it's over a swipe note.
+ // The actual swipe action is judged on game.up
+ var swipeNoteUnderCursor = findNoteAt(x, y, 'swipe');
+ if (swipeNoteUnderCursor && !swipeNoteUnderCursor.judged && swipeNoteUnderCursor.isInHitWindow()) {
+ swipeStart.note = swipeNoteUnderCursor;
}
};
game.up = function (x, y, obj) {
- if (inputLocked) return;
- if (!swipeStart) return;
- var swipe = swipeStart.note;
- if (!swipe || swipe.judged) {
+ if (inputLocked || !swipeStart) {
swipeStart = null;
return;
}
+ var noteToJudge = swipeStart.note;
+ if (!noteToJudge || noteToJudge.judged || noteToJudge.noteType !== 'swipe') {
+ swipeStart = null;
+ return;
+ }
+ // Only judge if the swipe ended within a reasonable time window of the note's target time
+ if (!noteToJudge.isInHitWindow()) {
+ // swipeStart = null; // Optional: could also mark as miss if swipe was too late/early
+ return;
+ }
var dx = x - swipeStart.x;
var dy = y - swipeStart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
swipeStart = null;
- return; // Not a swipe
+ return;
}
- // Determine direction
- var dir = null;
+ var detectedDir = null;
if (Math.abs(dx) > Math.abs(dy)) {
- dir = dx > 0 ? 'right' : 'left';
+ detectedDir = dx > 0 ? 'right' : 'left';
} else {
- dir = dy > 0 ? 'down' : 'up';
+ detectedDir = dy > 0 ? 'down' : 'up';
}
- if (dir === swipe.swipeDir) {
- var result = swipe.getHitAccuracy();
- swipe.judged = true;
- swipe.showHitFeedback(result);
+ if (detectedDir === noteToJudge.swipeDir) {
+ var result = noteToJudge.getHitAccuracy();
+ noteToJudge.judged = true;
+ noteToJudge.showHitFeedback(result);
if (result !== 'miss') {
addScore(result);
addCombo();
} else {
resetCombo();
}
} else {
- // Wrong direction
- swipe.judged = true;
- swipe.showHitFeedback('miss');
+ noteToJudge.judged = true;
+ noteToJudge.showHitFeedback('miss');
resetCombo();
- LK.effects.flashObject(swipe, 0xff0000, 300);
+ LK.effects.flashObject(noteToJudge, 0xff0000, 300);
}
inputLocked = true;
LK.setTimeout(function () {
inputLocked = false;
}, 120);
swipeStart = null;
};
game.move = function (x, y, obj) {
- // No drag needed
+ // Potential drag handling for future, or for a different type of note
};
-// --- Main Update Loop ---
game.update = function () {
var now = Date.now();
- // Spawn notes as needed
spawnNotes();
- // Update all notes
for (var i = 0; i < notes.length; i++) {
- notes[i].update();
+ if (notes[i]) {
+ // Check if note still exists
+ notes[i].update();
+ }
}
- // Remove old notes
removeOldNotes();
- // End check
checkGameEnd();
};
-// --- Start Game ---
resetGameState();
gameStartTime = Date.now();
\ No newline at end of file