User prompt
Oyunda lanelar yavaş bir şekilde rastgele yerlere sürekli hareket etsinler. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Notaların patlama efekti olsun. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Her modda aldığım high score farklı olsun
User prompt
Tap to start tuşuna basılmadıkça oyun başlamasın
User prompt
Normal modda 5000 puandan sonra gelen hızlanmayı kaldır
User prompt
Menüye easy mod seçeneği ekle bu oyunun normal halindeki hali olsun.
User prompt
Tap to start etrafında bir kare olsun
User prompt
Tap to start yazısını birazdaha yukarı götür
User prompt
Tap to start yazısını birazdaha yukarı götür
User prompt
Tap to start yazısı how to play yazısının üstünde yazsın
User prompt
Exterem moddaki hızı yavaşlat ama doğma hızı aynı kalsın
User prompt
Oyuna birde exterem zorlukta mod ekle bu oyun modunda notalar çok fazla hızlı doğsun.
User prompt
Hard modda nota doğma hızı artsın
User prompt
Menüye bir seçenek ekle ona tıklayınca oyunun zor modu açılsın o modda oyun çok daha hızlı olsun
User prompt
Show Hard Mode label on gameplay if hard mode is active
User prompt
Oyun 5000 puandan sonra hızlansın
User prompt
High score menüde gözüksün
User prompt
High score farklı bir yere koy
User prompt
Oyuna high score ekle ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Pembe notalar iki kez dokununca yok olsun. Menüde böyle bişey olsuğu yazsın.
User prompt
İnsanlar düzgün hareket etmiyor
User prompt
Please fix the bug: 'ReferenceError: DoubleTapNote is not defined' in or related to this line: 'note = new DoubleTapNote();' Line Number: 671
User prompt
Please fix the bug: 'ReferenceError: Note is not defined' in or related to this line: 'note = new Note();' Line Number: 593
User prompt
Please fix the bug: 'People is not defined' in or related to this line: 'var leftPerson = new People();' Line Number: 179
User prompt
Yeni bir nota türü ekle yeni eklenen nota türüne 2 kez tıklayınca yok olsun.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
// Note class for falling notes
var Note = Container.expand(function () {
var self = Container.call(this);
// Lane index (0-3)
self.lane = 0;
self.hit = false;
self.missed = false;
self.speed = 0; // pixels per tick
self.time = 0; // time (in ticks) when note should reach the target
self.spawned = false;
// Attach correct note asset based on lane
self.setLane = function (laneIdx) {
self.lane = laneIdx;
var assetId = 'note' + (laneIdx + 1);
var noteAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
width: NOTE_WIDTH,
height: NOTE_HEIGHT
});
};
// Called every tick
self.update = function () {
if (!self.spawned) return;
self.y += self.speed;
};
// Called when note is hit
self.onHit = function () {
if (self.hit || self.missed) return;
self.hit = true;
// Animate note
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
// Called when note is missed
self.onMiss = function () {
if (self.hit || self.missed) return;
self.missed = true;
LK.getSound('miss').play({
fade: {
start: 1,
end: 0,
duration: 1000
}
});
tween(self, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
// People class for animated people at lane edges
var People = Container.expand(function () {
var self = Container.call(this);
self.isJumping = false;
self.baseY = 0;
// Attach a body (ellipse, larger, lower color)
var body = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 1,
width: 100,
height: 180,
y: 0
});
// Arms removed: only body remains
// Save baseY for jump animation
self.baseY = self.y;
// Animate jump
self.jump = function () {
if (self.isJumping) return;
self.isJumping = true;
var jumpHeight = 120;
var jumpDuration = 180;
var originalY = self.y;
// --- Screen shake effect when people jump ---
if (typeof game !== "undefined" && typeof tween !== "undefined") {
// Only shake if not already shaking
if (!game._isShaking) {
game._isShaking = true;
var originalGameX = game.x || 0;
var originalGameY = game.y || 0;
// Determine if all people are jumping at once for stronger shake
var allJumping = false;
if (typeof peopleLeft !== "undefined" && typeof peopleRight !== "undefined") {
var allLeftJumping = true;
for (var i = 0; i < peopleLeft.length; i++) {
if (!peopleLeft[i].isJumping) {
allLeftJumping = false;
break;
}
}
var allRightJumping = true;
for (var i = 0; i < peopleRight.length; i++) {
if (!peopleRight[i].isJumping) {
allRightJumping = false;
break;
}
}
allJumping = allLeftJumping && allRightJumping;
}
var shakeAmount = allJumping ? 120 : 40; // much stronger shake if all people are jumping
var shakeDuration = allJumping ? 260 : 180;
// Shake out
tween(game, {
x: originalGameX + (Math.random() - 0.5) * shakeAmount,
y: originalGameY + (Math.random() - 0.5) * shakeAmount
}, {
duration: shakeDuration,
onFinish: function onFinish() {
// Shake back
tween(game, {
x: originalGameX,
y: originalGameY
}, {
duration: shakeDuration,
onFinish: function onFinish() {
game._isShaking = false;
}
});
}
});
}
}
tween(self, {
y: originalY - jumpHeight
}, {
duration: jumpDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
y: originalY
}, {
duration: jumpDuration,
easing: tween.easeIn,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// --- UI Elements ---
// 4 note lanes, each with a different color for clarity
// Notes are destroyed by tapping them directly before they reach the white target area at the bottom
var NUM_LANES = 4;
var LANE_WIDTH = 200;
var LANE_SPACING = 40;
var NOTE_WIDTH = 340; // Wider notes
var NOTE_HEIGHT = 340; // Taller notes (stretches upward)
var TARGET_HEIGHT = 60; // White target area is now just a visual "danger" zone, reduced height for smaller targets
var LANE_HEIGHT = 2200;
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var LANE_TOTAL_WIDTH = NUM_LANES * LANE_WIDTH + (NUM_LANES - 1) * LANE_SPACING;
var LANE_START_X = (GAME_WIDTH - LANE_TOTAL_WIDTH) / 2 + LANE_WIDTH / 2;
var TARGET_Y = GAME_HEIGHT - 420; // Target zone Y position (moved higher)
// --- Game State ---
var notes = []; // All active notes
var noteIndex = 0; // Index of next note to spawn
var songTicks = 0; // Ticks since song start
var score = 0;
var combo = 0;
var maxCombo = 0;
var misses = 0;
var maxMisses = 10;
var songEnded = false;
var songStarted = false;
var lastTick = 0;
// Note speed multiplier for gradual speed up
var noteSpeedMultiplier = 1.0;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High Score UI
var highScore = storage.highScore || 0;
var highScoreTxt = new Text2('High Score: ' + highScore, {
size: 60,
fill: 0xFFD700
});
// Move high score to top-right, with some margin from the edge
highScoreTxt.anchor.set(1, 0); // right aligned, top
highScoreTxt.x = LK.gui.width - 40; // 40px margin from right
highScoreTxt.y = 30; // 30px from top
LK.gui.top.addChild(highScoreTxt);
var comboTxt = new Text2('', {
size: 70,
fill: 0xFFE066
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.y = 130;
// --- Mode Labels ---
var hardMode = typeof hardMode !== "undefined" ? hardMode : false;
var extremeMode = typeof extremeMode !== "undefined" ? extremeMode : false;
var hardModeLabel = null;
var extremeModeLabel = null;
if (extremeMode) {
extremeModeLabel = new Text2('EXTREME MODE', {
size: 80,
fill: 0xFF00FF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
extremeModeLabel.anchor.set(0.5, 0);
extremeModeLabel.x = GAME_WIDTH / 2;
extremeModeLabel.y = 40;
LK.gui.top.addChild(extremeModeLabel);
} else if (hardMode) {
hardModeLabel = new Text2('HARD MODE', {
size: 80,
fill: 0xFF2222,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
hardModeLabel.anchor.set(0.5, 0);
hardModeLabel.x = GAME_WIDTH / 2;
hardModeLabel.y = 40;
LK.gui.top.addChild(hardModeLabel);
}
// Removed red 'misses' text from the top GUI. Misses are now only shown in white below each target.
// --- Add Background ---
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: GAME_WIDTH,
height: GAME_HEIGHT
});
game.addChild(background);
// --- Lanes and Targets ---
var lanes = [];
var targets = [];
var peopleLeft = [];
var peopleRight = [];
var targetMissTexts = []; // Miss counters for each target
for (var i = 0; i < NUM_LANES; i++) {
// Lane background
var laneX = LANE_START_X + i * (LANE_WIDTH + LANE_SPACING);
var lane = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0,
x: laneX,
y: 0,
width: LANE_WIDTH,
height: LANE_HEIGHT
});
game.addChild(lane);
lanes.push(lane);
// Visually improved target: larger, more distinct, with a colored border effect
var target = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
x: laneX,
y: TARGET_Y + 40,
//{1R} // Move target further back (higher y = lower on screen)
width: LANE_WIDTH + 40,
// Make target slightly wider for better visuals
height: TARGET_HEIGHT + 40 // Make target slightly taller for better visuals
});
game.addChild(target);
// Add a second, inner target for a "bullseye" effect
var innerTarget = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
x: laneX,
y: TARGET_Y + 40,
//{1W} // Move inner target further back as well
width: LANE_WIDTH - 30,
height: TARGET_HEIGHT - 20
});
game.addChild(innerTarget);
// (removed: misses counter above target, now shown on the target itself)
// Add a misses counter directly ON the target (replaces label text with misses count)
var targetOnText = new Text2('0', {
size: 54,
fill: 0x3399FF // Blue (not black)
});
targetOnText.anchor.set(0.5, 0.5);
targetOnText.x = laneX;
targetOnText.y = TARGET_Y + 40;
game.addChild(targetOnText);
// Store this as the main misses counter for this target (for updating)
targetMissTexts[i] = targetOnText;
// Store both for later effects
targets.push({
outer: target,
inner: innerTarget
});
}
// Place people only outside the lanes, never over the road/lane area
// --- Randomized, non-overlapping, not-in-center people placement ---
peopleLeft = [];
peopleRight = [];
var NUM_PEOPLE_PER_EDGE = 16; // Increased people per side for more crowd
var PERSON_RADIUS = 90; // Half of body width, for spacing
var OUTER_X_OFFSET = 120; // How far outside the lane edge to place people
// Allow people to go all the way to the screen edge, not just near the lanes
var FAR_LEFT_X_MIN = 60;
var FAR_LEFT_X_MAX = getLaneX(0) - LANE_WIDTH / 2 - OUTER_X_OFFSET - 10;
var FAR_RIGHT_X_MIN = getLaneX(NUM_LANES - 1) + LANE_WIDTH / 2 + OUTER_X_OFFSET + 10;
var FAR_RIGHT_X_MAX = GAME_WIDTH - 60;
var Y_MIN = 200; // Don't go too high
var Y_MAX = TARGET_Y - 60; // Don't go too low
var CENTER_EXCLUSION_X = GAME_WIDTH / 2 - 320; // Exclude a wide center band
var CENTER_EXCLUSION_WIDTH = 640; // Center band width to avoid
var MAX_ATTEMPTS = 40; // Max tries to find a non-overlapping spot
function isFarEnough(x, y, arr) {
for (var i = 0; i < arr.length; i++) {
var dx = x - arr[i].x;
var dy = y - arr[i].y;
if (Math.sqrt(dx * dx + dy * dy) < PERSON_RADIUS * 2.1) return false;
}
return true;
}
function isNotCenter(x) {
return !(x > CENTER_EXCLUSION_X && x < CENTER_EXCLUSION_X + CENTER_EXCLUSION_WIDTH);
}
// Left edge: randomize y, keep x always outside leftmost lane, avoid center, avoid overlap
for (var i = 0; i < NUM_PEOPLE_PER_EDGE; i++) {
var leftPerson = new People();
var attempts = 0;
var px, py;
do {
py = Y_MIN + Math.random() * (Y_MAX - Y_MIN);
// Randomize x across the full left empty area
px = FAR_LEFT_X_MIN + Math.random() * (FAR_LEFT_X_MAX - FAR_LEFT_X_MIN);
attempts++;
} while ((!isFarEnough(px, py, peopleLeft) || !isNotCenter(px)) && attempts < MAX_ATTEMPTS);
leftPerson.x = px;
leftPerson.y = py;
game.addChild(leftPerson);
peopleLeft.push(leftPerson);
}
// Right edge: randomize y, keep x always outside rightmost lane, avoid center, avoid overlap
for (var i = 0; i < NUM_PEOPLE_PER_EDGE; i++) {
var rightPerson = new People();
var attempts = 0;
var px, py;
do {
py = Y_MIN + Math.random() * (Y_MAX - Y_MIN);
// Randomize x across the full right empty area
px = FAR_RIGHT_X_MIN + Math.random() * (FAR_RIGHT_X_MAX - FAR_RIGHT_X_MIN);
attempts++;
} while ((!isFarEnough(px, py, peopleRight) || !isNotCenter(px)) && attempts < MAX_ATTEMPTS);
rightPerson.x = px;
rightPerson.y = py;
game.addChild(rightPerson);
peopleRight.push(rightPerson);
}
// --- Song Data (Random Infinite Notes) ---
// Each note: {lane: 0-3, time: tick when note should reach target}
// We'll generate notes on the fly, at random lanes and random intervals
var bpm = 60;
var ticksPerBeat = 60 * 60 / bpm; // 60fps
var NOTE_TRAVEL_TICKS = 180; // 3 seconds at 60fps
// Infinite random note generator state
var nextNoteTick = 60; // When the next note should appear (in songTicks)
function getRandomLane() {
return Math.floor(Math.random() * NUM_LANES);
}
function getRandomInterval() {
// In extreme mode, spawn notes extremely frequently (interval is 0.12x to 0.35x of ticksPerBeat)
if (typeof extremeMode !== "undefined" && extremeMode) {
return Math.floor(ticksPerBeat * (0.12 + Math.random() * 0.23));
}
// In hard mode, spawn notes more frequently (interval is 0.3x to 1.0x of ticksPerBeat)
if (typeof hardMode !== "undefined" && hardMode) {
return Math.floor(ticksPerBeat * (0.3 + Math.random() * 0.7));
}
// Random interval between notes: 0.5x to 1.5x of ticksPerBeat
return Math.floor(ticksPerBeat * (0.5 + Math.random()));
}
// --- Helper Functions ---
function getLaneX(laneIdx) {
return LANE_START_X + laneIdx * (LANE_WIDTH + LANE_SPACING);
}
// --- Game Logic ---
// Start song/music
function startSong() {
if (songStarted) return;
songStarted = true;
LK.playMusic('song1');
songTicks = 0;
noteIndex = 0;
score = 0;
combo = 0;
maxCombo = 0;
misses = 0;
songEnded = false;
scoreTxt.setText('0');
comboTxt.setText('');
highScore = storage.highScore || 0;
highScoreTxt.setText('High Score: ' + highScore);
notes.length = 0;
// Reset per-target misses counters (on the target itself)
if (typeof targets !== "undefined" && typeof targetMissTexts !== "undefined") {
for (var i = 0; i < targetMissTexts.length; i++) {
if (targetMissTexts[i]) {
targetMissTexts[i].setText('0');
targetMissTexts[i].setStyle({
fill: 0x3399FF
});
}
if (targets[i]) targets[i].missCount = 0;
}
}
// Reset nextNoteTick and noteSpeedMultiplier to ensure smooth start
nextNoteTick = 60;
if (typeof extremeMode !== "undefined" && extremeMode) {
noteSpeedMultiplier = 3.0; // Even faster for extreme mode
// Remove any previous mode labels
if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
hardModeLabel.destroy();
}
if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
extremeModeLabel.destroy();
}
extremeModeLabel = new Text2('EXTREME MODE', {
size: 80,
fill: 0xFF00FF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
extremeModeLabel.anchor.set(0.5, 0);
extremeModeLabel.x = GAME_WIDTH / 2;
extremeModeLabel.y = 40;
LK.gui.top.addChild(extremeModeLabel);
} else if (typeof hardMode !== "undefined" && hardMode) {
noteSpeedMultiplier = 2.0; // Much faster for hard mode
// Show Hard Mode label in gameplay
if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
hardModeLabel.destroy();
}
if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
extremeModeLabel.destroy();
}
hardModeLabel = new Text2('HARD MODE', {
size: 80,
fill: 0xFF2222,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
hardModeLabel.anchor.set(0.5, 0);
hardModeLabel.x = GAME_WIDTH / 2;
hardModeLabel.y = 40;
LK.gui.top.addChild(hardModeLabel);
} else {
noteSpeedMultiplier = 1.0;
if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
hardModeLabel.destroy();
}
if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
extremeModeLabel.destroy();
}
}
}
// End song/game
function endSong(win) {
if (songEnded) return;
songEnded = true;
LK.stopMusic();
// No win or game over, just stop music and mark as ended
}
// --- Input Handling ---
game.down = function (x, y, obj) {
// Only allow input if song is running
if (!songStarted || songEnded) return;
// Pause people movement for 2 seconds (120 ticks) after a note is hit
if (typeof game._peopleMovePausedUntil === "undefined") game._peopleMovePausedUntil = 0;
game._peopleMovePausedUntil = songTicks + 120;
// Check if tap is on any note (from topmost to bottom)
var hit = false;
var _loop = function _loop() {
note = notes[i];
if (note.hit || note.missed) return 0; // continue
// Get note bounds
noteLeft = note.x - NOTE_WIDTH / 2;
noteRight = note.x + NOTE_WIDTH / 2;
noteTop = note.y - NOTE_HEIGHT / 2;
noteBottom = note.y + NOTE_HEIGHT / 2;
if (x >= noteLeft && x <= noteRight && y >= noteTop && y <= noteBottom) {
// Hit!
note.onHit();
hit = true;
score += 100;
combo += 1;
if (combo > maxCombo) maxCombo = combo;
scoreTxt.setText(score + '');
comboTxt.setText(combo > 1 ? combo + ' Combo!' : '');
// High score logic
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
LK.getSound('tap').play();
// Make a random subset of people jump when any note is hit!
// Every 10 combo, make all people jump and apply a stronger shake
if (typeof peopleLeft !== "undefined" && typeof peopleRight !== "undefined") {
if (combo > 0 && combo % 50 === 0) {
// --- MASSIVE SHAKE for every 50 combo ---
// All people jump!
for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
if (peopleLeft[iAll]) peopleLeft[iAll].jump();
}
for (var iAll = 0; iAll < peopleRight.length; iAll++) {
if (peopleRight[iAll]) peopleRight[iAll].jump();
}
// Massive shake: override game.x/y with a huge shake
if (typeof game !== "undefined" && typeof tween !== "undefined") {
if (!game._isShaking) {
game._isShaking = true;
var originalGameX = game.x || 0;
var originalGameY = game.y || 0;
var shakeAmount = 420; // extremely strong shake
var shakeDuration = 420;
tween(game, {
x: originalGameX + (Math.random() - 0.5) * shakeAmount,
y: originalGameY + (Math.random() - 0.5) * shakeAmount
}, {
duration: shakeDuration,
onFinish: function onFinish() {
tween(game, {
x: originalGameX,
y: originalGameY
}, {
duration: shakeDuration,
onFinish: function onFinish() {
game._isShaking = false;
}
});
}
});
}
}
} else if (combo > 0 && combo % 10 === 0) {
// All people jump!
for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
if (peopleLeft[iAll]) peopleLeft[iAll].jump();
}
for (var iAll = 0; iAll < peopleRight.length; iAll++) {
if (peopleRight[iAll]) peopleRight[iAll].jump();
}
// Stronger shake: override game.x/y with a bigger shake
if (typeof game !== "undefined" && typeof tween !== "undefined") {
if (!game._isShaking) {
game._isShaking = true;
var originalGameX = game.x || 0;
var originalGameY = game.y || 0;
var shakeAmount = 60; // much stronger shake
var shakeDuration = 120;
tween(game, {
x: originalGameX + (Math.random() - 0.5) * shakeAmount,
y: originalGameY + (Math.random() - 0.5) * shakeAmount
}, {
duration: shakeDuration,
onFinish: function onFinish() {
tween(game, {
x: originalGameX,
y: originalGameY
}, {
duration: shakeDuration,
onFinish: function onFinish() {
game._isShaking = false;
}
});
}
});
}
}
} else {
// Helper to get unique random indices
var getRandomIndices = function getRandomIndices(arrLen, count) {
var indices = [];
var used = [];
while (indices.length < count && indices.length < arrLen) {
var idx = Math.floor(Math.random() * arrLen);
if (!used[idx]) {
indices.push(idx);
used[idx] = true;
}
}
return indices;
};
// How many people to jump per side? 1-3 random per side
leftJumpCount = 1 + Math.floor(Math.random() * 3);
rightJumpCount = 1 + Math.floor(Math.random() * 3);
leftIndices = getRandomIndices(peopleLeft.length, leftJumpCount);
rightIndices = getRandomIndices(peopleRight.length, rightJumpCount);
for (j = 0; j < leftIndices.length; j++) {
idx = leftIndices[j];
if (peopleLeft[idx]) peopleLeft[idx].jump();
}
for (j = 0; j < rightIndices.length; j++) {
idx = rightIndices[j];
if (peopleRight[idx]) peopleRight[idx].jump();
}
}
}
// Show floating feedback text based on combo
feedbackText = '';
if (combo >= 30) {
feedbackText = 'PERFECT!';
} else if (combo >= 15) {
feedbackText = 'GREAT!';
} else if (combo >= 5) {
feedbackText = 'GOOD!';
}
if (feedbackText) {
fbTxt = new Text2(feedbackText, {
size: 120,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
fbTxt.anchor.set(0.5, 0.5);
fbTxt.x = GAME_WIDTH / 2;
fbTxt.y = GAME_HEIGHT / 2 - 200;
fbTxt.alpha = 1;
game.addChild(fbTxt);
tween(fbTxt, {
y: fbTxt.y - 120,
alpha: 0
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
fbTxt.destroy();
}
});
}
return 1; // break
}
},
note,
noteLeft,
noteRight,
noteTop,
noteBottom,
leftJumpCount,
rightJumpCount,
leftIndices,
rightIndices,
j,
idx,
j,
idx,
feedbackText,
fbTxt,
_ret;
for (var i = notes.length - 1; i >= 0; i--) {
_ret = _loop();
if (_ret === 0) continue;
if (_ret === 1) break;
}
if (!hit) {
// Missed tap (no note hit)
combo = 0;
comboTxt.setText('');
// No misses for tap misses, no flash
}
};
// --- Main Game Loop ---
game.update = function () {
if (!songStarted || songEnded) return;
songTicks += 1;
// Gradually increase noteSpeedMultiplier (very slow ramp, e.g. +0.0002 per tick)
// After 5000 points, increase speed faster
if (noteSpeedMultiplier < 2.0) {
if (score >= 5000) {
noteSpeedMultiplier += 0.0012; // much faster ramp after 5000 points
} else {
noteSpeedMultiplier += 0.0002;
}
if (noteSpeedMultiplier > 2.0) noteSpeedMultiplier = 2.0;
}
// Spawn notes as needed (random, infinite)
// After 5000 points, do not increase note spawn count; only speed increases
var notesToSpawn = 1;
// No change to notesToSpawn after 5000 points
while (songTicks >= nextNoteTick - NOTE_TRAVEL_TICKS) {
for (var spawnIdx = 0; spawnIdx < notesToSpawn; spawnIdx++) {
var lane = getRandomLane();
var noteTime = nextNoteTick;
var note = new Note();
note.setLane(lane);
note.x = getLaneX(lane);
note.y = -NOTE_HEIGHT / 2;
note.speed = (TARGET_Y + NOTE_HEIGHT / 2) / NOTE_TRAVEL_TICKS * noteSpeedMultiplier;
note.time = noteTime;
note.spawned = true;
notes.push(note);
game.addChild(note);
}
// Schedule next note
nextNoteTick += getRandomInterval();
}
// Update notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
note.update();
// If note reached target zone and not hit, mark as missed
if (!note.hit && !note.missed && note.y >= TARGET_Y + TARGET_HEIGHT / 2) {
note.onMiss();
combo = 0;
misses += 1;
comboTxt.setText('');
// Increment and show per-target misses in white above each target
if (!targets[note.lane].missCount) targets[note.lane].missCount = 0;
targets[note.lane].missCount += 1;
if (targetMissTexts && targetMissTexts[note.lane]) {
targetMissTexts[note.lane].setText(targets[note.lane].missCount + '');
targetMissTexts[note.lane].setStyle({
fill: 0x3399FF
});
}
LK.getSound('miss').play({
fade: {
start: 1,
end: 0,
duration: 1000
}
});
LK.effects.flashObject(targets[note.lane].outer, 0xff0000, 200);
LK.effects.flashObject(targets[note.lane].inner, 0xff6666, 200);
if (misses >= maxMisses) {
// No game over, just keep going
}
// Check if any target's miss count exceeds 10, trigger game over
for (var t = 0; t < targets.length; t++) {
if (targets[t].missCount && targets[t].missCount > 10) {
LK.showGameOver();
return;
}
}
}
// Remove destroyed notes
if (note.destroyed) {
notes.splice(i, 1);
}
}
// --- Move people to new random positions every 2 seconds ---
if (typeof game._lastPeopleMoveTick === "undefined") game._lastPeopleMoveTick = 0;
if (typeof game._peopleMovePausedUntil === "undefined") game._peopleMovePausedUntil = 0;
// Only move people if not in the 2s pause after a note hit
if (songTicks - game._lastPeopleMoveTick > 120 && songTicks >= game._peopleMovePausedUntil) {
// Helper to move a person to a new random position (non-overlapping, not center)
var movePerson = function movePerson(person, arr, isLeft) {
var attempts = 0;
var px, py;
do {
py = Y_MIN + Math.random() * (Y_MAX - Y_MIN);
if (isLeft) {
// Move anywhere in the left empty area
px = FAR_LEFT_X_MIN + Math.random() * (FAR_LEFT_X_MAX - FAR_LEFT_X_MIN);
} else {
// Move anywhere in the right empty area
px = FAR_RIGHT_X_MIN + Math.random() * (FAR_RIGHT_X_MAX - FAR_RIGHT_X_MIN);
}
attempts++;
} while ((!isFarEnough(px, py, arr) || !isNotCenter(px)) && attempts < MAX_ATTEMPTS);
// Animate to new position (move very slowly, never teleport)
tween(person, {
x: px,
y: py
}, {
duration: 3200,
// much slower movement (3.2 seconds)
easing: tween.easeInOut
});
person.baseY = py;
}; // Move left people
// every 2 seconds at 60fps
game._lastPeopleMoveTick = songTicks;
for (var i = 0; i < peopleLeft.length; i++) {
movePerson(peopleLeft[i], peopleLeft, true);
}
// Move right people
for (var i = 0; i < peopleRight.length; i++) {
movePerson(peopleRight[i], peopleRight, false);
}
}
// No win condition, keep game running
};
// --- Start Menu Overlay ---
var startMenuOverlay = new Container();
var overlayBg = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2,
width: GAME_WIDTH,
height: GAME_HEIGHT,
color: 0x000000
});
overlayBg.alpha = 0.85;
startMenuOverlay.addChild(overlayBg);
var titleText = new Text2('BEAT TAPPER', {
size: 120,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
titleText.anchor.set(0.5, 0.5);
titleText.x = GAME_WIDTH / 2;
titleText.y = GAME_HEIGHT / 2 - 400;
startMenuOverlay.addChild(titleText);
var instructionsText = new Text2("How to Play:\n\n" + "• Tap the falling notes when they reach the targets.\n" + "• Don't let too many notes pass the targets!\n" + "• Each lane shows your misses. If any lane gets more than 10 misses, it's game over.\n" + "• Try to get the highest combo and score!", {
size: 44,
fill: 0xFFFFFF,
align: "center"
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = GAME_WIDTH / 2;
instructionsText.y = GAME_HEIGHT / 2 + 120;
startMenuOverlay.addChild(instructionsText);
var highScoreMenuText = new Text2('High Score: ' + (storage.highScore || 0), {
size: 70,
fill: 0xFFD700,
align: "center"
});
highScoreMenuText.anchor.set(0.5, 0.5);
highScoreMenuText.x = GAME_WIDTH / 2;
highScoreMenuText.y = GAME_HEIGHT / 2 + 320;
startMenuOverlay.addChild(highScoreMenuText);
var tapToStartText = new Text2('Tap to Start', {
size: 90,
fill: 0xFFFFFF
});
tapToStartText.anchor.set(0.5, 0.5);
tapToStartText.x = GAME_WIDTH / 2;
tapToStartText.y = GAME_HEIGHT / 2 + 480;
startMenuOverlay.addChild(tapToStartText);
// --- Hard Mode Button ---
var hardModeBtn = new Text2('Hard Mode', {
size: 80,
fill: 0xFF2222,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
hardModeBtn.anchor.set(0.5, 0.5);
hardModeBtn.x = GAME_WIDTH / 2;
hardModeBtn.y = GAME_HEIGHT / 2 + 620;
hardModeBtn.alpha = 0.92;
hardModeBtn._isHardModeBtn = true;
startMenuOverlay.addChild(hardModeBtn);
// --- Extreme Mode Button ---
var extremeModeBtn = new Text2('Extreme Mode', {
size: 80,
fill: 0xFF00FF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
extremeModeBtn.anchor.set(0.5, 0.5);
extremeModeBtn.x = GAME_WIDTH / 2;
extremeModeBtn.y = GAME_HEIGHT / 2 + 760;
extremeModeBtn.alpha = 0.92;
extremeModeBtn._isExtremeModeBtn = true;
startMenuOverlay.addChild(extremeModeBtn);
game.addChild(startMenuOverlay);
var menuActive = true;
var hardMode = false;
var extremeMode = false;
game.down = function (x, y, obj) {
if (menuActive) {
// Check if tap is on Hard Mode button
var btnLeft = hardModeBtn.x - hardModeBtn.width / 2;
var btnRight = hardModeBtn.x + hardModeBtn.width / 2;
var btnTop = hardModeBtn.y - hardModeBtn.height / 2;
var btnBottom = hardModeBtn.y + hardModeBtn.height / 2;
if (x >= btnLeft && x <= btnRight && y >= btnTop && y <= btnBottom) {
// Toggle hard mode ON and visually indicate
hardMode = true;
extremeMode = false;
hardModeBtn.setText('Hard Mode: ON');
hardModeBtn.fill = 0xFF4444;
hardModeBtn.alpha = 1;
extremeModeBtn.setText('Extreme Mode');
extremeModeBtn.fill = 0xFF00FF;
extremeModeBtn.alpha = 0.92;
// Optionally, flash or animate
tween(hardModeBtn, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
yoyo: true,
repeat: 1,
onFinish: function onFinish() {
hardModeBtn.scaleX = 1;
hardModeBtn.scaleY = 1;
}
});
return;
}
// Check if tap is on Extreme Mode button
var ebtnLeft = extremeModeBtn.x - extremeModeBtn.width / 2;
var ebtnRight = extremeModeBtn.x + extremeModeBtn.width / 2;
var ebtnTop = extremeModeBtn.y - extremeModeBtn.height / 2;
var ebtnBottom = extremeModeBtn.y + extremeModeBtn.height / 2;
if (x >= ebtnLeft && x <= ebtnRight && y >= ebtnTop && y <= ebtnBottom) {
// Toggle extreme mode ON and visually indicate
extremeMode = true;
hardMode = false;
extremeModeBtn.setText('Extreme Mode: ON');
extremeModeBtn.fill = 0xFF66FF;
extremeModeBtn.alpha = 1;
hardModeBtn.setText('Hard Mode');
hardModeBtn.fill = 0xFF2222;
hardModeBtn.alpha = 0.92;
// Optionally, flash or animate
tween(extremeModeBtn, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
yoyo: true,
repeat: 1,
onFinish: function onFinish() {
extremeModeBtn.scaleX = 1;
extremeModeBtn.scaleY = 1;
}
});
return;
}
menuActive = false;
startMenuOverlay.destroy();
startSong();
// Restore original input handler for gameplay
game.down = function (x, y, obj) {
// Only allow input if song is running
if (!songStarted || songEnded) return;
// Pause people movement for 2 seconds (120 ticks) after a note is hit
if (typeof game._peopleMovePausedUntil === "undefined") game._peopleMovePausedUntil = 0;
game._peopleMovePausedUntil = songTicks + 120;
// Check if tap is on any note (from topmost to bottom)
var hit = false;
var _loop = function _loop() {
note = notes[i];
if (note.hit || note.missed) return 0; // continue
// Get note bounds
noteLeft = note.x - NOTE_WIDTH / 2;
noteRight = note.x + NOTE_WIDTH / 2;
noteTop = note.y - NOTE_HEIGHT / 2;
noteBottom = note.y + NOTE_HEIGHT / 2;
if (x >= noteLeft && x <= noteRight && y >= noteTop && y <= noteBottom) {
// Hit!
note.onHit();
hit = true;
score += 100;
combo += 1;
if (combo > maxCombo) maxCombo = combo;
scoreTxt.setText(score + '');
comboTxt.setText(combo > 1 ? combo + ' Combo!' : '');
// High score logic
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
LK.getSound('tap').play();
// Make a random subset of people jump when any note is hit!
// Every 10 combo, make all people jump and apply a stronger shake
if (typeof peopleLeft !== "undefined" && typeof peopleRight !== "undefined") {
if (combo > 0 && combo % 50 === 0) {
// --- MASSIVE SHAKE for every 50 combo ---
// All people jump!
for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
if (peopleLeft[iAll]) peopleLeft[iAll].jump();
}
for (var iAll = 0; iAll < peopleRight.length; iAll++) {
if (peopleRight[iAll]) peopleRight[iAll].jump();
}
// Massive shake: override game.x/y with a huge shake
if (typeof game !== "undefined" && typeof tween !== "undefined") {
if (!game._isShaking) {
game._isShaking = true;
var originalGameX = game.x || 0;
var originalGameY = game.y || 0;
var shakeAmount = 420; // extremely strong shake
var shakeDuration = 420;
tween(game, {
x: originalGameX + (Math.random() - 0.5) * shakeAmount,
y: originalGameY + (Math.random() - 0.5) * shakeAmount
}, {
duration: shakeDuration,
onFinish: function onFinish() {
tween(game, {
x: originalGameX,
y: originalGameY
}, {
duration: shakeDuration,
onFinish: function onFinish() {
game._isShaking = false;
}
});
}
});
}
}
} else if (combo > 0 && combo % 10 === 0) {
// All people jump!
for (var iAll = 0; iAll < peopleLeft.length; iAll++) {
if (peopleLeft[iAll]) peopleLeft[iAll].jump();
}
for (var iAll = 0; iAll < peopleRight.length; iAll++) {
if (peopleRight[iAll]) peopleRight[iAll].jump();
}
// Stronger shake: override game.x/y with a bigger shake
if (typeof game !== "undefined" && typeof tween !== "undefined") {
if (!game._isShaking) {
game._isShaking = true;
var originalGameX = game.x || 0;
var originalGameY = game.y || 0;
var shakeAmount = 60; // much stronger shake
var shakeDuration = 120;
tween(game, {
x: originalGameX + (Math.random() - 0.5) * shakeAmount,
y: originalGameY + (Math.random() - 0.5) * shakeAmount
}, {
duration: shakeDuration,
onFinish: function onFinish() {
tween(game, {
x: originalGameX,
y: originalGameY
}, {
duration: shakeDuration,
onFinish: function onFinish() {
game._isShaking = false;
}
});
}
});
}
}
} else {
// Helper to get unique random indices
var getRandomIndices = function getRandomIndices(arrLen, count) {
var indices = [];
var used = [];
while (indices.length < count && indices.length < arrLen) {
var idx = Math.floor(Math.random() * arrLen);
if (!used[idx]) {
indices.push(idx);
used[idx] = true;
}
}
return indices;
};
// How many people to jump per side? 1-3 random per side
leftJumpCount = 1 + Math.floor(Math.random() * 3);
rightJumpCount = 1 + Math.floor(Math.random() * 3);
leftIndices = getRandomIndices(peopleLeft.length, leftJumpCount);
rightIndices = getRandomIndices(peopleRight.length, rightJumpCount);
for (j = 0; j < leftIndices.length; j++) {
idx = leftIndices[j];
if (peopleLeft[idx]) peopleLeft[idx].jump();
}
for (j = 0; j < rightIndices.length; j++) {
idx = rightIndices[j];
if (peopleRight[idx]) peopleRight[idx].jump();
}
}
}
// Show floating feedback text based on combo
feedbackText = '';
if (combo >= 30) {
feedbackText = 'PERFECT!';
} else if (combo >= 15) {
feedbackText = 'GREAT!';
} else if (combo >= 5) {
feedbackText = 'GOOD!';
}
if (feedbackText) {
fbTxt = new Text2(feedbackText, {
size: 120,
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
fbTxt.anchor.set(0.5, 0.5);
fbTxt.x = GAME_WIDTH / 2;
fbTxt.y = GAME_HEIGHT / 2 - 200;
fbTxt.alpha = 1;
game.addChild(fbTxt);
tween(fbTxt, {
y: fbTxt.y - 120,
alpha: 0
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
fbTxt.destroy();
}
});
}
return 1; // break
}
},
note,
noteLeft,
noteRight,
noteTop,
noteBottom,
leftJumpCount,
rightJumpCount,
leftIndices,
rightIndices,
j,
idx,
j,
idx,
feedbackText,
fbTxt,
_ret;
for (var i = notes.length - 1; i >= 0; i--) {
_ret = _loop();
if (_ret === 0) continue;
if (_ret === 1) break;
}
if (!hit) {
// Missed tap (no note hit)
combo = 0;
comboTxt.setText('');
// No misses for tap misses, no flash
}
};
}
};
// Do not start the song immediately; wait for user tap
// startSong();; ===================================================================
--- original.js
+++ change.js
@@ -230,12 +230,24 @@
});
comboTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(comboTxt);
comboTxt.y = 130;
-// --- Hard Mode Label ---
+// --- Mode Labels ---
var hardMode = typeof hardMode !== "undefined" ? hardMode : false;
+var extremeMode = typeof extremeMode !== "undefined" ? extremeMode : false;
var hardModeLabel = null;
-if (hardMode) {
+var extremeModeLabel = null;
+if (extremeMode) {
+ extremeModeLabel = new Text2('EXTREME MODE', {
+ size: 80,
+ fill: 0xFF00FF,
+ font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
+ });
+ extremeModeLabel.anchor.set(0.5, 0);
+ extremeModeLabel.x = GAME_WIDTH / 2;
+ extremeModeLabel.y = 40;
+ LK.gui.top.addChild(extremeModeLabel);
+} else if (hardMode) {
hardModeLabel = new Text2('HARD MODE', {
size: 80,
fill: 0xFF2222,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
@@ -387,8 +399,12 @@
function getRandomLane() {
return Math.floor(Math.random() * NUM_LANES);
}
function getRandomInterval() {
+ // In extreme mode, spawn notes extremely frequently (interval is 0.12x to 0.35x of ticksPerBeat)
+ if (typeof extremeMode !== "undefined" && extremeMode) {
+ return Math.floor(ticksPerBeat * (0.12 + Math.random() * 0.23));
+ }
// In hard mode, spawn notes more frequently (interval is 0.3x to 1.0x of ticksPerBeat)
if (typeof hardMode !== "undefined" && hardMode) {
return Math.floor(ticksPerBeat * (0.3 + Math.random() * 0.7));
}
@@ -430,14 +446,35 @@
}
}
// Reset nextNoteTick and noteSpeedMultiplier to ensure smooth start
nextNoteTick = 60;
- if (typeof hardMode !== "undefined" && hardMode) {
+ if (typeof extremeMode !== "undefined" && extremeMode) {
+ noteSpeedMultiplier = 3.0; // Even faster for extreme mode
+ // Remove any previous mode labels
+ if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
+ hardModeLabel.destroy();
+ }
+ if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
+ extremeModeLabel.destroy();
+ }
+ extremeModeLabel = new Text2('EXTREME MODE', {
+ size: 80,
+ fill: 0xFF00FF,
+ font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
+ });
+ extremeModeLabel.anchor.set(0.5, 0);
+ extremeModeLabel.x = GAME_WIDTH / 2;
+ extremeModeLabel.y = 40;
+ LK.gui.top.addChild(extremeModeLabel);
+ } else if (typeof hardMode !== "undefined" && hardMode) {
noteSpeedMultiplier = 2.0; // Much faster for hard mode
// Show Hard Mode label in gameplay
if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
hardModeLabel.destroy();
}
+ if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
+ extremeModeLabel.destroy();
+ }
hardModeLabel = new Text2('HARD MODE', {
size: 80,
fill: 0xFF2222,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
@@ -450,8 +487,11 @@
noteSpeedMultiplier = 1.0;
if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) {
hardModeLabel.destroy();
}
+ if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) {
+ extremeModeLabel.destroy();
+ }
}
}
// End song/game
function endSong(win) {
@@ -839,11 +879,24 @@
hardModeBtn.y = GAME_HEIGHT / 2 + 620;
hardModeBtn.alpha = 0.92;
hardModeBtn._isHardModeBtn = true;
startMenuOverlay.addChild(hardModeBtn);
+// --- Extreme Mode Button ---
+var extremeModeBtn = new Text2('Extreme Mode', {
+ size: 80,
+ fill: 0xFF00FF,
+ font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
+});
+extremeModeBtn.anchor.set(0.5, 0.5);
+extremeModeBtn.x = GAME_WIDTH / 2;
+extremeModeBtn.y = GAME_HEIGHT / 2 + 760;
+extremeModeBtn.alpha = 0.92;
+extremeModeBtn._isExtremeModeBtn = true;
+startMenuOverlay.addChild(extremeModeBtn);
game.addChild(startMenuOverlay);
var menuActive = true;
var hardMode = false;
+var extremeMode = false;
game.down = function (x, y, obj) {
if (menuActive) {
// Check if tap is on Hard Mode button
var btnLeft = hardModeBtn.x - hardModeBtn.width / 2;
@@ -852,11 +905,15 @@
var btnBottom = hardModeBtn.y + hardModeBtn.height / 2;
if (x >= btnLeft && x <= btnRight && y >= btnTop && y <= btnBottom) {
// Toggle hard mode ON and visually indicate
hardMode = true;
+ extremeMode = false;
hardModeBtn.setText('Hard Mode: ON');
hardModeBtn.fill = 0xFF4444;
hardModeBtn.alpha = 1;
+ extremeModeBtn.setText('Extreme Mode');
+ extremeModeBtn.fill = 0xFF00FF;
+ extremeModeBtn.alpha = 0.92;
// Optionally, flash or animate
tween(hardModeBtn, {
scaleX: 1.2,
scaleY: 1.2
@@ -870,8 +927,38 @@
}
});
return;
}
+ // Check if tap is on Extreme Mode button
+ var ebtnLeft = extremeModeBtn.x - extremeModeBtn.width / 2;
+ var ebtnRight = extremeModeBtn.x + extremeModeBtn.width / 2;
+ var ebtnTop = extremeModeBtn.y - extremeModeBtn.height / 2;
+ var ebtnBottom = extremeModeBtn.y + extremeModeBtn.height / 2;
+ if (x >= ebtnLeft && x <= ebtnRight && y >= ebtnTop && y <= ebtnBottom) {
+ // Toggle extreme mode ON and visually indicate
+ extremeMode = true;
+ hardMode = false;
+ extremeModeBtn.setText('Extreme Mode: ON');
+ extremeModeBtn.fill = 0xFF66FF;
+ extremeModeBtn.alpha = 1;
+ hardModeBtn.setText('Hard Mode');
+ hardModeBtn.fill = 0xFF2222;
+ hardModeBtn.alpha = 0.92;
+ // Optionally, flash or animate
+ tween(extremeModeBtn, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 120,
+ yoyo: true,
+ repeat: 1,
+ onFinish: function onFinish() {
+ extremeModeBtn.scaleX = 1;
+ extremeModeBtn.scaleY = 1;
+ }
+ });
+ return;
+ }
menuActive = false;
startMenuOverlay.destroy();
startSong();
// Restore original input handler for gameplay