User prompt
Hard moddaki nota gelme hızını biraz azalt.
User prompt
Davul notasına tıklayınca davula vurma efekti veya animasyonu olsun. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Menüdeki davul yazısında davul notasının 3 saniye sonra kaybolacağı yazısını 5 saniye ile değiştir.
User prompt
Menüdeki yazıyı değiştir.
User prompt
Menüdeki davulun kaybettirme süresinin yazısını 5 saniyeyle değiştirm
User prompt
5000 puandan sonra lanelar 10 saniye boyunca çok hızlı bir şekilde rastgele renklere boyansınlar. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
İnsanların hepsi aynı anda zıpladıklarında laneların renkleri rastgele şekilde değişin.
User prompt
5 çeşit insan olsun hepsine farklı insanlar ver.
User prompt
Please fix the bug: 'ReferenceError: isNotDrumArea is not defined' in or related to this line: 'return !(x > CENTER_EXCLUSION_X && x < CENTER_EXCLUSION_X + CENTER_EXCLUSION_WIDTH);' Line Number: 554
User prompt
Oyun başlarken ekranın hem sağında hemde solunda sadece 3 insan olsun her 500 puandan bir her iki tarafa bir insan eklensin her iki tarafta en fazla 50 insan olabilsin.
User prompt
Davul notasları 10 saniye arayla çıksın easy modda hard modda 5 saniye arayla extrem modda 3 saniye arayla çıksın.
User prompt
Davulun kaybettirme süresini 5 saniye yap.
User prompt
İngilizce olsun yazı.
User prompt
Menüdeki davul yazısını düzenle yani artık direk oyun başında çıktığını yaz.
User prompt
Oyuna insanlar geri gelsin ama davulun asetinin orda olmasınlar.
User prompt
Davul 5000 puandan sonra değil direk oyun başladığında gelsin.
User prompt
Davula tıklayınca davul soundu çalsın.
User prompt
5000 puandan sonra davulun çıkacağı yerdeki insanlar silinsin.
User prompt
Menüdeki davulun ne olduğunu anlatan yazıyı menün en aşşağısına koy.
User prompt
Davul yazısını ekranın en aşşağısına yaz.
User prompt
Menüye davulun 5000 puanda çıktığını ve rastgele zamanda olan oluşan davul notlarına basmaları gerektiğini yazı olarak ekle.
User prompt
Drumnotenin üstünde süresi yazsın.
User prompt
Davunlun aseti yeni bir aset olsun.
User prompt
5000 puandan sonra ekranın sol ve sağ tarafına iki tane davul koy davulun üzerinde rastgele zamanda notalar belirsin ve davula tıklayınca notalar yok olsun ve bize puan gelsin eğer notaya 3 saniye içinde dokunmazsak kaybedelim. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
5000 puandan sonra insanlar yok olsun ve lanelar sağ ve sola doğru daha fazla gitmeye başlasınlar. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ // DrumNote class for notes on drums var DrumNote = Container.expand(function () { var self = Container.call(this); self.spawnTime = 0; self.isLeft = true; self.hit = false; self.lifeTime = 180; // 3 seconds at 60fps var noteGraphics = self.attachAsset('drumNote', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (!self.hit && songTicks - self.spawnTime > self.lifeTime) { // Note expired - game over LK.showGameOver(); } }; self.onHit = function () { if (self.hit) return; self.hit = true; // Explosion effect var particleCount = 6; var colors = [0xFFD700, 0xFFAA00, 0xFFFF00]; for (var p = 0; p < particleCount; p++) { var particle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, width: 30 + Math.random() * 40, height: 30 + Math.random() * 40 }); particle.tint = colors[Math.floor(Math.random() * colors.length)]; if (self.parent) self.parent.addChild(particle); var angle = Math.PI * 2 * p / particleCount; var distance = 100 + Math.random() * 100; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { if (particle.parent) particle.destroy(); } }); } tween(self, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); // 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; // Create explosion effect with particles var particleCount = 8; var colors = [0xFFD700, 0xFF4444, 0x44FF44, 0x4444FF, 0xFF44FF, 0x44FFFF]; for (var p = 0; p < particleCount; p++) { var particle = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, width: 20 + Math.random() * 30, height: 20 + Math.random() * 30 }); particle.tint = colors[Math.floor(Math.random() * colors.length)]; if (self.parent) self.parent.addChild(particle); // Random direction for explosion var angle = Math.PI * 2 * p / particleCount + (Math.random() - 0.5) * 0.5; var distance = 80 + Math.random() * 120; var targetX = self.x + Math.cos(angle) * distance; var targetY = self.y + Math.sin(angle) * distance; // Animate particle explosion tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 300 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { if (particle.parent) particle.destroy(); } }); } // 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; // Drum variables var leftDrum = null; var rightDrum = null; var drumNotes = []; var drumsActive = false; var nextDrumNoteTime = 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 // --- Per-mode high score keys --- function getCurrentModeKey() { if (typeof extremeMode !== "undefined" && extremeMode) return "extreme"; if (typeof hardMode !== "undefined" && hardMode) return "hard"; return "easy"; } function getHighScoreKey() { return "highScore_" + getCurrentModeKey(); } function getHighScore() { var key = getHighScoreKey(); return typeof storage[key] !== "undefined" ? storage[key] : 0; } function setHighScore(val) { var key = getHighScoreKey(); storage[key] = val; } var highScore = getHighScore(); 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 var laneContainers = []; // Containers to group lane elements together for (var i = 0; i < NUM_LANES; i++) { // Create a container for each lane to group all elements var laneContainer = new Container(); var laneX = LANE_START_X + i * (LANE_WIDTH + LANE_SPACING); laneContainer.x = laneX; laneContainer.baseX = laneX; // Store base position for movement game.addChild(laneContainer); laneContainers.push(laneContainer); // Lane background var lane = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0, x: 0, //{2r} // Relative to container y: 0, width: LANE_WIDTH, height: LANE_HEIGHT }); laneContainer.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: 0, //{2w} // Relative to container 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 }); laneContainer.addChild(target); // Add a second, inner target for a "bullseye" effect var innerTarget = LK.getAsset('target', { anchorX: 0.5, anchorY: 0.5, x: 0, //{2B} // Relative to container y: TARGET_Y + 40, //{1W} // Move inner target further back as well width: LANE_WIDTH - 30, height: TARGET_HEIGHT - 20 }); laneContainer.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 = 0; // Relative to container targetOnText.y = TARGET_Y + 40; laneContainer.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) { if (typeof laneContainers !== "undefined" && laneContainers[laneIdx]) { return laneContainers[laneIdx].x; } return LANE_START_X + laneIdx * (LANE_WIDTH + LANE_SPACING); } // Start continuous random lane movement function startLaneMovement() { for (var i = 0; i < laneContainers.length; i++) { moveLaneRandomly(laneContainers[i], i); } } // Move a single lane to a random position function moveLaneRandomly(laneContainer, index) { if (!laneContainer || songEnded) return; // Random offset from base position (-80 to +80 pixels) var randomOffset = (Math.random() - 0.5) * 160; var targetX = laneContainer.baseX + randomOffset; // Random duration (3-5 seconds) var duration = 3000 + Math.random() * 2000; tween(laneContainer, { x: targetX }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { // Continue moving after reaching target moveLaneRandomly(laneContainer, index); } }); } // --- 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 = getHighScore(); 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; // Reset drum state drumsActive = false; drumNotes.length = 0; if (leftDrum) { leftDrum.destroy(); leftDrum = null; } if (rightDrum) { rightDrum.destroy(); rightDrum = null; } // Start lane movement animations startLaneMovement(); if (typeof extremeMode !== "undefined" && extremeMode) { noteSpeedMultiplier = 1.5; // Slower than before for extreme mode (spawn rate unchanged) // 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 if (typeof easyMode !== "undefined" && easyMode) { noteSpeedMultiplier = 1.0; // Show Easy Mode label in gameplay if (typeof hardModeLabel !== "undefined" && hardModeLabel && hardModeLabel.parent) { hardModeLabel.destroy(); } if (typeof extremeModeLabel !== "undefined" && extremeModeLabel && extremeModeLabel.parent) { extremeModeLabel.destroy(); } if (typeof easyModeLabel !== "undefined" && easyModeLabel && easyModeLabel.parent) { easyModeLabel.destroy(); } easyModeLabel = new Text2('EASY MODE', { size: 80, fill: 0x44FF44, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); easyModeLabel.anchor.set(0.5, 0); easyModeLabel.x = GAME_WIDTH / 2; easyModeLabel.y = 40; LK.gui.top.addChild(easyModeLabel); } 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) { if (menuActive) return; // Prevent gameplay input until menu is dismissed // Only allow input if song is running if (!songStarted || songEnded) return; // Check drum hits first if drums are active if (drumsActive) { // Check left drum if (leftDrum) { var leftDrumLeft = leftDrum.x - 150; var leftDrumRight = leftDrum.x + 150; var leftDrumTop = leftDrum.y - 150; var leftDrumBottom = leftDrum.y + 150; if (x >= leftDrumLeft && x <= leftDrumRight && y >= leftDrumTop && y <= leftDrumBottom) { // Hit left drum - check for drum notes for (var d = drumNotes.length - 1; d >= 0; d--) { var drumNote = drumNotes[d]; if (!drumNote.hit && drumNote.isLeft) { drumNote.onHit(); score += 200; scoreTxt.setText(score + ''); // Update high score if (score > highScore) { highScore = score; setHighScore(highScore); highScoreTxt.setText('High Score: ' + highScore); } LK.getSound('tap').play(); // Flash drum LK.effects.flashObject(leftDrum, 0xFFD700, 200); return; } } } } // Check right drum if (rightDrum) { var rightDrumLeft = rightDrum.x - 150; var rightDrumRight = rightDrum.x + 150; var rightDrumTop = rightDrum.y - 150; var rightDrumBottom = rightDrum.y + 150; if (x >= rightDrumLeft && x <= rightDrumRight && y >= rightDrumTop && y <= rightDrumBottom) { // Hit right drum - check for drum notes for (var d = drumNotes.length - 1; d >= 0; d--) { var drumNote = drumNotes[d]; if (!drumNote.hit && !drumNote.isLeft) { drumNote.onHit(); score += 200; scoreTxt.setText(score + ''); // Update high score if (score > highScore) { highScore = score; setHighScore(highScore); highScoreTxt.setText('High Score: ' + highScore); } LK.getSound('tap').play(); // Flash drum LK.effects.flashObject(rightDrum, 0xFFD700, 200); 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; setHighScore(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; // Create drums when score reaches 5000 if (!drumsActive && score >= 5000) { drumsActive = true; // Create left drum leftDrum = LK.getAsset('drum', { anchorX: 0.5, anchorY: 0.5, x: 200, y: GAME_HEIGHT / 2 }); game.addChild(leftDrum); // Create right drum rightDrum = LK.getAsset('drum', { anchorX: 0.5, anchorY: 0.5, x: GAME_WIDTH - 200, y: GAME_HEIGHT / 2 }); game.addChild(rightDrum); // Set initial next drum note time nextDrumNoteTime = songTicks + 60 + Math.floor(Math.random() * 120); } // Spawn drum notes if (drumsActive && songTicks >= nextDrumNoteTime) { var drumNote = new DrumNote(); drumNote.spawnTime = songTicks; // Randomly choose left or right drum drumNote.isLeft = Math.random() < 0.5; if (drumNote.isLeft) { drumNote.x = leftDrum.x; drumNote.y = leftDrum.y; } else { drumNote.x = rightDrum.x; drumNote.y = rightDrum.y; } drumNotes.push(drumNote); game.addChild(drumNote); // Schedule next drum note (random interval between 1-3 seconds) nextDrumNoteTime = songTicks + 60 + Math.floor(Math.random() * 120); } // Update drum notes for (var d = drumNotes.length - 1; d >= 0; d--) { var drumNote = drumNotes[d]; drumNote.update(); if (drumNote.destroyed) { drumNotes.splice(d, 1); } } // Gradually increase noteSpeedMultiplier (very slow ramp, e.g. +0.0002 per tick) // In normal mode, do NOT increase speed faster after 5000 points if (noteSpeedMultiplier < 2.0) { 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 tapToStartText = new Text2('Tap to Start', { size: 90, fill: 0xFFFFFF }); tapToStartText.anchor.set(0.5, 0.5); tapToStartText.x = GAME_WIDTH / 2; // Move even higher above instructions (was -60, now -120 for more space above instructions) tapToStartText.y = GAME_HEIGHT / 2 - 120; // Create a box behind the text var tapBoxPaddingX = 60; var tapBoxPaddingY = 30; var tapBox = LK.getAsset('lane', { anchorX: 0.5, anchorY: 0.5, x: tapToStartText.x, y: tapToStartText.y, width: tapToStartText.width + tapBoxPaddingX, height: tapToStartText.height + tapBoxPaddingY, color: 0xFFFFFF }); tapBox.alpha = 0.18; startMenuOverlay.addChild(tapBox); startMenuOverlay.addChild(tapToStartText); 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: ' + getHighScore(), { 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); // --- Easy Mode Button --- var easyModeBtn = new Text2('Easy Mode', { size: 80, fill: 0x44FF44, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); easyModeBtn.anchor.set(0.5, 0.5); easyModeBtn.x = GAME_WIDTH / 2; easyModeBtn.y = GAME_HEIGHT / 2 + 480; easyModeBtn.alpha = 0.92; easyModeBtn._isEasyModeBtn = true; startMenuOverlay.addChild(easyModeBtn); // --- 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; var easyMode = true; // Default to easy mode game.down = function (x, y, obj) { if (menuActive) { // Check if tap is on Easy Mode button var easyBtnLeft = easyModeBtn.x - easyModeBtn.width / 2; var easyBtnRight = easyModeBtn.x + easyModeBtn.width / 2; var easyBtnTop = easyModeBtn.y - easyModeBtn.height / 2; var easyBtnBottom = easyModeBtn.y + easyModeBtn.height / 2; if (x >= easyBtnLeft && x <= easyBtnRight && y >= easyBtnTop && y <= easyBtnBottom) { // Toggle easy mode ON and visually indicate easyMode = true; hardMode = false; extremeMode = false; easyModeBtn.setText('Easy Mode: ON'); easyModeBtn.fill = 0x66FF66; easyModeBtn.alpha = 1; hardModeBtn.setText('Hard Mode'); hardModeBtn.fill = 0xFF2222; hardModeBtn.alpha = 0.92; extremeModeBtn.setText('Extreme Mode'); extremeModeBtn.fill = 0xFF00FF; extremeModeBtn.alpha = 0.92; // Update high score in menu overlay if (typeof highScoreMenuText !== "undefined") { highScoreMenuText.setText('High Score: ' + getHighScore()); } // Optionally, flash or animate tween(easyModeBtn, { scaleX: 1.2, scaleY: 1.2 }, { duration: 120, yoyo: true, repeat: 1, onFinish: function onFinish() { easyModeBtn.scaleX = 1; easyModeBtn.scaleY = 1; } }); return; } // 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; easyMode = false; extremeMode = false; hardModeBtn.setText('Hard Mode: ON'); hardModeBtn.fill = 0xFF4444; hardModeBtn.alpha = 1; easyModeBtn.setText('Easy Mode'); easyModeBtn.fill = 0x44FF44; easyModeBtn.alpha = 0.92; extremeModeBtn.setText('Extreme Mode'); extremeModeBtn.fill = 0xFF00FF; extremeModeBtn.alpha = 0.92; // Update high score in menu overlay if (typeof highScoreMenuText !== "undefined") { highScoreMenuText.setText('High Score: ' + getHighScore()); } // 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; easyMode = false; extremeModeBtn.setText('Extreme Mode: ON'); extremeModeBtn.fill = 0xFF66FF; extremeModeBtn.alpha = 1; hardModeBtn.setText('Hard Mode'); hardModeBtn.fill = 0xFF2222; hardModeBtn.alpha = 0.92; easyModeBtn.setText('Easy Mode'); easyModeBtn.fill = 0x44FF44; easyModeBtn.alpha = 0.92; // Update high score in menu overlay if (typeof highScoreMenuText !== "undefined") { highScoreMenuText.setText('High Score: ' + getHighScore()); } // 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; // Check drum hits first if drums are active if (drumsActive) { // Check left drum if (leftDrum) { var leftDrumLeft = leftDrum.x - 150; var leftDrumRight = leftDrum.x + 150; var leftDrumTop = leftDrum.y - 150; var leftDrumBottom = leftDrum.y + 150; if (x >= leftDrumLeft && x <= leftDrumRight && y >= leftDrumTop && y <= leftDrumBottom) { // Hit left drum - check for drum notes for (var d = drumNotes.length - 1; d >= 0; d--) { var drumNote = drumNotes[d]; if (!drumNote.hit && drumNote.isLeft) { drumNote.onHit(); score += 200; scoreTxt.setText(score + ''); // Update high score if (score > highScore) { highScore = score; setHighScore(highScore); highScoreTxt.setText('High Score: ' + highScore); } LK.getSound('tap').play(); // Flash drum LK.effects.flashObject(leftDrum, 0xFFD700, 200); return; } } } } // Check right drum if (rightDrum) { var rightDrumLeft = rightDrum.x - 150; var rightDrumRight = rightDrum.x + 150; var rightDrumTop = rightDrum.y - 150; var rightDrumBottom = rightDrum.y + 150; if (x >= rightDrumLeft && x <= rightDrumRight && y >= rightDrumTop && y <= rightDrumBottom) { // Hit right drum - check for drum notes for (var d = drumNotes.length - 1; d >= 0; d--) { var drumNote = drumNotes[d]; if (!drumNote.hit && !drumNote.isLeft) { drumNote.onHit(); score += 200; scoreTxt.setText(score + ''); // Update high score if (score > highScore) { highScore = score; setHighScore(highScore); highScoreTxt.setText('High Score: ' + highScore); } LK.getSound('tap').play(); // Flash drum LK.effects.flashObject(rightDrum, 0xFFD700, 200); 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; setHighScore(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
@@ -8,8 +8,74 @@
/****
* Classes
****/
+// DrumNote class for notes on drums
+var DrumNote = Container.expand(function () {
+ var self = Container.call(this);
+ self.spawnTime = 0;
+ self.isLeft = true;
+ self.hit = false;
+ self.lifeTime = 180; // 3 seconds at 60fps
+ var noteGraphics = self.attachAsset('drumNote', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.update = function () {
+ if (!self.hit && songTicks - self.spawnTime > self.lifeTime) {
+ // Note expired - game over
+ LK.showGameOver();
+ }
+ };
+ self.onHit = function () {
+ if (self.hit) return;
+ self.hit = true;
+ // Explosion effect
+ var particleCount = 6;
+ var colors = [0xFFD700, 0xFFAA00, 0xFFFF00];
+ for (var p = 0; p < particleCount; p++) {
+ var particle = LK.getAsset('centerCircle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: self.x,
+ y: self.y,
+ width: 30 + Math.random() * 40,
+ height: 30 + Math.random() * 40
+ });
+ particle.tint = colors[Math.floor(Math.random() * colors.length)];
+ if (self.parent) self.parent.addChild(particle);
+ var angle = Math.PI * 2 * p / particleCount;
+ var distance = 100 + Math.random() * 100;
+ var targetX = self.x + Math.cos(angle) * distance;
+ var targetY = self.y + Math.sin(angle) * distance;
+ tween(particle, {
+ x: targetX,
+ y: targetY,
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 400,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ if (particle.parent) particle.destroy();
+ }
+ });
+ }
+ tween(self, {
+ alpha: 0,
+ scaleX: 1.5,
+ scaleY: 1.5
+ }, {
+ duration: 200,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ self.destroy();
+ }
+ });
+ };
+ return self;
+});
// Note class for falling notes
var Note = Container.expand(function () {
var self = Container.call(this);
// Lane index (0-3)
@@ -210,11 +276,11 @@
/****
* Game Code
****/
-// Notes are destroyed by tapping them directly before they reach the white target area at the bottom
-// 4 note lanes, each with a different color for clarity
// --- 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
@@ -239,8 +305,14 @@
var songStarted = false;
var lastTick = 0;
// Note speed multiplier for gradual speed up
var noteSpeedMultiplier = 1.0;
+// Drum variables
+var leftDrum = null;
+var rightDrum = null;
+var drumNotes = [];
+var drumsActive = false;
+var nextDrumNoteTime = 0;
// --- UI Elements ---
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
@@ -487,12 +559,10 @@
}
// Move a single lane to a random position
function moveLaneRandomly(laneContainer, index) {
if (!laneContainer || songEnded) return;
- // Increase movement range after 5000 points
- var movementRange = score >= 5000 ? 320 : 160; // Double the range after 5000 points
- // Random offset from base position
- var randomOffset = (Math.random() - 0.5) * movementRange;
+ // Random offset from base position (-80 to +80 pixels)
+ var randomOffset = (Math.random() - 0.5) * 160;
var targetX = laneContainer.baseX + randomOffset;
// Random duration (3-5 seconds)
var duration = 3000 + Math.random() * 2000;
tween(laneContainer, {
@@ -537,10 +607,19 @@
}
}
// Reset nextNoteTick and noteSpeedMultiplier to ensure smooth start
nextNoteTick = 60;
- // Reset people hidden flag
- game._peopleHidden = false;
+ // Reset drum state
+ drumsActive = false;
+ drumNotes.length = 0;
+ if (leftDrum) {
+ leftDrum.destroy();
+ leftDrum = null;
+ }
+ if (rightDrum) {
+ rightDrum.destroy();
+ rightDrum = null;
+ }
// Start lane movement animations
startLaneMovement();
if (typeof extremeMode !== "undefined" && extremeMode) {
noteSpeedMultiplier = 1.5; // Slower than before for extreme mode (spawn rate unchanged)
@@ -620,8 +699,67 @@
game.down = function (x, y, obj) {
if (menuActive) return; // Prevent gameplay input until menu is dismissed
// Only allow input if song is running
if (!songStarted || songEnded) return;
+ // Check drum hits first if drums are active
+ if (drumsActive) {
+ // Check left drum
+ if (leftDrum) {
+ var leftDrumLeft = leftDrum.x - 150;
+ var leftDrumRight = leftDrum.x + 150;
+ var leftDrumTop = leftDrum.y - 150;
+ var leftDrumBottom = leftDrum.y + 150;
+ if (x >= leftDrumLeft && x <= leftDrumRight && y >= leftDrumTop && y <= leftDrumBottom) {
+ // Hit left drum - check for drum notes
+ for (var d = drumNotes.length - 1; d >= 0; d--) {
+ var drumNote = drumNotes[d];
+ if (!drumNote.hit && drumNote.isLeft) {
+ drumNote.onHit();
+ score += 200;
+ scoreTxt.setText(score + '');
+ // Update high score
+ if (score > highScore) {
+ highScore = score;
+ setHighScore(highScore);
+ highScoreTxt.setText('High Score: ' + highScore);
+ }
+ LK.getSound('tap').play();
+ // Flash drum
+ LK.effects.flashObject(leftDrum, 0xFFD700, 200);
+ return;
+ }
+ }
+ }
+ }
+ // Check right drum
+ if (rightDrum) {
+ var rightDrumLeft = rightDrum.x - 150;
+ var rightDrumRight = rightDrum.x + 150;
+ var rightDrumTop = rightDrum.y - 150;
+ var rightDrumBottom = rightDrum.y + 150;
+ if (x >= rightDrumLeft && x <= rightDrumRight && y >= rightDrumTop && y <= rightDrumBottom) {
+ // Hit right drum - check for drum notes
+ for (var d = drumNotes.length - 1; d >= 0; d--) {
+ var drumNote = drumNotes[d];
+ if (!drumNote.hit && !drumNote.isLeft) {
+ drumNote.onHit();
+ score += 200;
+ scoreTxt.setText(score + '');
+ // Update high score
+ if (score > highScore) {
+ highScore = score;
+ setHighScore(highScore);
+ highScoreTxt.setText('High Score: ' + highScore);
+ }
+ LK.getSound('tap').play();
+ // Flash drum
+ LK.effects.flashObject(rightDrum, 0xFFD700, 200);
+ 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)
@@ -818,8 +956,56 @@
// --- Main Game Loop ---
game.update = function () {
if (!songStarted || songEnded) return;
songTicks += 1;
+ // Create drums when score reaches 5000
+ if (!drumsActive && score >= 5000) {
+ drumsActive = true;
+ // Create left drum
+ leftDrum = LK.getAsset('drum', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 200,
+ y: GAME_HEIGHT / 2
+ });
+ game.addChild(leftDrum);
+ // Create right drum
+ rightDrum = LK.getAsset('drum', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: GAME_WIDTH - 200,
+ y: GAME_HEIGHT / 2
+ });
+ game.addChild(rightDrum);
+ // Set initial next drum note time
+ nextDrumNoteTime = songTicks + 60 + Math.floor(Math.random() * 120);
+ }
+ // Spawn drum notes
+ if (drumsActive && songTicks >= nextDrumNoteTime) {
+ var drumNote = new DrumNote();
+ drumNote.spawnTime = songTicks;
+ // Randomly choose left or right drum
+ drumNote.isLeft = Math.random() < 0.5;
+ if (drumNote.isLeft) {
+ drumNote.x = leftDrum.x;
+ drumNote.y = leftDrum.y;
+ } else {
+ drumNote.x = rightDrum.x;
+ drumNote.y = rightDrum.y;
+ }
+ drumNotes.push(drumNote);
+ game.addChild(drumNote);
+ // Schedule next drum note (random interval between 1-3 seconds)
+ nextDrumNoteTime = songTicks + 60 + Math.floor(Math.random() * 120);
+ }
+ // Update drum notes
+ for (var d = drumNotes.length - 1; d >= 0; d--) {
+ var drumNote = drumNotes[d];
+ drumNote.update();
+ if (drumNote.destroyed) {
+ drumNotes.splice(d, 1);
+ }
+ }
// Gradually increase noteSpeedMultiplier (very slow ramp, e.g. +0.0002 per tick)
// In normal mode, do NOT increase speed faster after 5000 points
if (noteSpeedMultiplier < 2.0) {
noteSpeedMultiplier += 0.0002;
@@ -889,41 +1075,13 @@
if (note.destroyed) {
notes.splice(i, 1);
}
}
- // Hide people after 5000 points
- if (score >= 5000) {
- // Hide all people if not already hidden
- if (!game._peopleHidden) {
- game._peopleHidden = true;
- // Fade out all people
- for (var p = 0; p < peopleLeft.length; p++) {
- if (peopleLeft[p] && peopleLeft[p].alpha > 0) {
- tween(peopleLeft[p], {
- alpha: 0
- }, {
- duration: 500,
- easing: tween.easeOut
- });
- }
- }
- for (var p = 0; p < peopleRight.length; p++) {
- if (peopleRight[p] && peopleRight[p].alpha > 0) {
- tween(peopleRight[p], {
- alpha: 0
- }, {
- duration: 500,
- easing: tween.easeOut
- });
- }
- }
- }
- }
// --- 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 and score < 5000
- if (songTicks - game._lastPeopleMoveTick > 120 && songTicks >= game._peopleMovePausedUntil && score < 5000) {
+ // 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;
@@ -1187,8 +1345,67 @@
// Restore original input handler for gameplay
game.down = function (x, y, obj) {
// Only allow input if song is running
if (!songStarted || songEnded) return;
+ // Check drum hits first if drums are active
+ if (drumsActive) {
+ // Check left drum
+ if (leftDrum) {
+ var leftDrumLeft = leftDrum.x - 150;
+ var leftDrumRight = leftDrum.x + 150;
+ var leftDrumTop = leftDrum.y - 150;
+ var leftDrumBottom = leftDrum.y + 150;
+ if (x >= leftDrumLeft && x <= leftDrumRight && y >= leftDrumTop && y <= leftDrumBottom) {
+ // Hit left drum - check for drum notes
+ for (var d = drumNotes.length - 1; d >= 0; d--) {
+ var drumNote = drumNotes[d];
+ if (!drumNote.hit && drumNote.isLeft) {
+ drumNote.onHit();
+ score += 200;
+ scoreTxt.setText(score + '');
+ // Update high score
+ if (score > highScore) {
+ highScore = score;
+ setHighScore(highScore);
+ highScoreTxt.setText('High Score: ' + highScore);
+ }
+ LK.getSound('tap').play();
+ // Flash drum
+ LK.effects.flashObject(leftDrum, 0xFFD700, 200);
+ return;
+ }
+ }
+ }
+ }
+ // Check right drum
+ if (rightDrum) {
+ var rightDrumLeft = rightDrum.x - 150;
+ var rightDrumRight = rightDrum.x + 150;
+ var rightDrumTop = rightDrum.y - 150;
+ var rightDrumBottom = rightDrum.y + 150;
+ if (x >= rightDrumLeft && x <= rightDrumRight && y >= rightDrumTop && y <= rightDrumBottom) {
+ // Hit right drum - check for drum notes
+ for (var d = drumNotes.length - 1; d >= 0; d--) {
+ var drumNote = drumNotes[d];
+ if (!drumNote.hit && !drumNote.isLeft) {
+ drumNote.onHit();
+ score += 200;
+ scoreTxt.setText(score + '');
+ // Update high score
+ if (score > highScore) {
+ highScore = score;
+ setHighScore(highScore);
+ highScoreTxt.setText('High Score: ' + highScore);
+ }
+ LK.getSound('tap').play();
+ // Flash drum
+ LK.effects.flashObject(rightDrum, 0xFFD700, 200);
+ 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)