User prompt
Add the music
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'self.text.style.fill = color;' Line Number: 330
Code edit (1 edits merged)
Please save this source code
User prompt
Rhythm Battle: Beat Masters
Initial prompt
Friday night funkin
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Arrow = Container.expand(function (direction, speed, holdDuration) { var self = Container.call(this); self.direction = direction || 'up'; self.speed = speed || 10; self.holdDuration = holdDuration || 0; self.isHold = self.holdDuration > 0; self.isActive = true; self.wasHit = false; self.holdActive = false; self.holdTime = 0; // Get arrow asset based on direction var arrowAsset = 'arrow_' + self.direction; var arrow = self.attachAsset(arrowAsset, { anchorX: 0.5, anchorY: 0.5 }); // Rotate arrow based on direction if (self.direction === 'up') { arrow.rotation = 0; } else if (self.direction === 'down') { arrow.rotation = Math.PI; } else if (self.direction === 'left') { arrow.rotation = -Math.PI / 2; } else if (self.direction === 'right') { arrow.rotation = Math.PI / 2; } // If it's a hold note, create an extension if (self.isHold) { self.holdExtension = self.addChild(LK.getAsset(arrowAsset, { anchorX: 0.5, anchorY: 0, y: arrow.height / 2, scaleY: self.holdDuration / 100, // Scale based on duration alpha: 0.5 })); // Rotate the hold extension if (self.direction === 'up') { self.holdExtension.rotation = 0; } else if (self.direction === 'down') { self.holdExtension.rotation = Math.PI; self.holdExtension.y = -arrow.height / 2; self.holdExtension.anchorY = 1; } else if (self.direction === 'left') { self.holdExtension.rotation = -Math.PI / 2; self.holdExtension.x = -arrow.height / 2; self.holdExtension.y = 0; self.holdExtension.anchorX = 1; self.holdExtension.anchorY = 0.5; } else if (self.direction === 'right') { self.holdExtension.rotation = Math.PI / 2; self.holdExtension.x = arrow.height / 2; self.holdExtension.y = 0; self.holdExtension.anchorX = 0; self.holdExtension.anchorY = 0.5; } } self.update = function () { // Move arrow upward self.y -= self.speed; // Check if the arrow is off screen and should be removed if (self.y < -300 && self.isActive) { if (!self.wasHit) { // Player missed the arrow game.handleMiss(); } self.isActive = false; LK.setTimeout(function () { self.destroy(); var index = arrows.indexOf(self); if (index > -1) { arrows.splice(index, 1); } }, 100); } }; self.hit = function (rating) { if (self.isActive && !self.wasHit) { self.wasHit = true; if (self.isHold) { self.holdActive = true; return 'hold_start'; } else { tween(self, { alpha: 0 }, { duration: 200 }); LK.setTimeout(function () { self.isActive = false; }, 200); return rating; } } return null; }; self.updateHold = function (holdActive) { if (self.isHold && self.wasHit) { self.holdTime += 1; if (holdActive) { // Player is holding correctly self.holdExtension.alpha = 0.8; // Check if hold is complete if (self.holdTime >= self.holdDuration) { self.isActive = false; tween(self, { alpha: 0 }, { duration: 200 }); return 'hold_complete'; } return 'holding'; } else { // Player released too early self.holdExtension.alpha = 0.3; self.isActive = false; tween(self, { alpha: 0 }, { duration: 200 }); return 'hold_break'; } } return null; }; return self; }); var Character = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; var sprite = self.attachAsset(isPlayer ? 'player' : 'opponent', { anchorX: 0.5, anchorY: 1 }); self.setIdle = function () { tween(sprite, { scaleY: 1 }, { duration: 200 }); }; self.setAction = function () { tween(sprite, { scaleY: 1.05 }, { duration: 100, onFinish: function onFinish() { tween(sprite, { scaleY: 1 }, { duration: 200 }); } }); }; return self; }); var HealthBar = Container.expand(function (isPlayer) { var self = Container.call(this); self.health = 100; self.isPlayer = isPlayer; // Background bar var barBg = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); // Health fill var fillAsset = isPlayer ? 'healthFill' : 'opponentHealthFill'; self.fillBar = self.attachAsset(fillAsset, { anchorX: 0, anchorY: 0.5, x: 2, scaleX: 796 // Width - 4 for padding }); // Label self.label = new Text2(isPlayer ? "YOU" : "CPU", { size: 40, fill: 0xFFFFFF }); self.label.anchor.set(0.5); self.label.x = barBg.width / 2; self.label.y = 0; self.addChild(self.label); self.updateHealth = function (amount) { self.health = Math.max(0, Math.min(100, self.health + amount)); tween(self.fillBar, { scaleX: 796 * (self.health / 100) }, { duration: 300, easing: tween.easeOut }); if (self.health <= 0) { return true; // Health depleted } return false; }; return self; }); var HitArea = Container.expand(function (direction) { var self = Container.call(this); self.direction = direction; self.isPressed = false; var hitBox = self.attachAsset('hitArea', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); var arrow = self.attachAsset('arrow_' + direction, { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); // Rotate arrow based on direction if (direction === 'up') { arrow.rotation = 0; } else if (direction === 'down') { arrow.rotation = Math.PI; } else if (direction === 'left') { arrow.rotation = -Math.PI / 2; } else if (direction === 'right') { arrow.rotation = Math.PI / 2; } self.down = function (x, y, obj) { self.isPressed = true; hitBox.alpha = 0.7; game.handleInput(self.direction); }; self.up = function (x, y, obj) { self.isPressed = false; hitBox.alpha = 0.3; game.handleInputRelease(self.direction); }; self.setHighlight = function (highlight) { if (highlight) { tween(hitBox, { alpha: 0.7 }, { duration: 100 }); tween(arrow, { alpha: 1 }, { duration: 100 }); } else { tween(hitBox, { alpha: 0.3 }, { duration: 100 }); tween(arrow, { alpha: 0.7 }, { duration: 100 }); } }; return self; }); var RatingDisplay = Container.expand(function () { var self = Container.call(this); self.text = new Text2('', { size: 80, fill: 0xFFFFFF }); self.text.anchor.set(0.5); self.addChild(self.text); self.show = function (rating, score) { var ratingText = ''; var color = "#FFFFFF"; switch (rating) { case 'perfect': ratingText = 'PERFECT!'; color = "#00FFFF"; break; case 'good': ratingText = 'GOOD'; color = "#00FF00"; break; case 'okay': ratingText = 'OKAY'; color = "#FFFF00"; break; case 'miss': ratingText = 'MISS'; color = "#FF0000"; break; case 'hold_complete': ratingText = 'HOLD BONUS!'; color = "#FF00FF"; break; case 'hold_break': ratingText = 'HOLD BROKEN!'; color = "#FF8800"; break; } if (score && rating !== 'miss' && rating !== 'hold_break') { ratingText += '\n+' + score; } self.text.setText(ratingText); self.text.style.fill = color; self.alpha = 1; self.scale.set(1.2); tween(self, { alpha: 0, y: self.y - 50 }, { duration: 800, easing: tween.easeOut }); tween(self.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.elasticOut }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Game variables var arrows = []; var hitAreas = {}; var activeHoldNotes = {}; var gameStarted = false; var score = 0; var combo = 0; var currentSong = 'song1'; var songBPM = 120; var noteSpeed = 10; var currentDifficulty = 1; var beatTimer = 0; var nextNoteTime = 0; var lastBeat = 0; var beatInterval = 60 / songBPM * 60; // Convert to frames var arrowTypes = ['up', 'down', 'left', 'right']; // Create game elements var playerCharacter, opponentCharacter; var playerHealth, opponentHealth; var ratingDisplay; var scoreText, comboText; var gameTimingBar; // Initialize game function initGame() { // Create UI elements scoreText = new Text2('SCORE: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.x = 2048 / 2; scoreText.y = 50; LK.gui.top.addChild(scoreText); comboText = new Text2('', { size: 50, fill: 0xFFFFFF }); comboText.anchor.set(0.5, 0); comboText.x = 2048 / 2; comboText.y = 120; LK.gui.top.addChild(comboText); // Create health bars playerHealth = new HealthBar(true); playerHealth.x = 150; playerHealth.y = 100; game.addChild(playerHealth); opponentHealth = new HealthBar(false); opponentHealth.x = 2048 - 950; opponentHealth.y = 100; game.addChild(opponentHealth); // Create characters playerCharacter = new Character(true); playerCharacter.x = 600; playerCharacter.y = 2732 - 100; game.addChild(playerCharacter); opponentCharacter = new Character(false); opponentCharacter.x = 2048 - 600; opponentCharacter.y = 2732 - 100; game.addChild(opponentCharacter); // Create hit areas var hitAreaY = 2732 - 300; var hitAreaSpacing = 220; var centerX = 2048 / 2; hitAreas.left = new HitArea('left'); hitAreas.left.x = centerX - hitAreaSpacing - hitAreaSpacing / 2; hitAreas.left.y = hitAreaY; game.addChild(hitAreas.left); hitAreas.down = new HitArea('down'); hitAreas.down.x = centerX - hitAreaSpacing / 2; hitAreas.down.y = hitAreaY; game.addChild(hitAreas.down); hitAreas.up = new HitArea('up'); hitAreas.up.x = centerX + hitAreaSpacing / 2; hitAreas.up.y = hitAreaY; game.addChild(hitAreas.up); hitAreas.right = new HitArea('right'); hitAreas.right.x = centerX + hitAreaSpacing + hitAreaSpacing / 2; hitAreas.right.y = hitAreaY; game.addChild(hitAreas.right); // Create rating display ratingDisplay = new RatingDisplay(); ratingDisplay.x = 2048 / 2; ratingDisplay.y = hitAreaY - 300; game.addChild(ratingDisplay); // Reset game state resetGameState(); // Start game startGame(); } function resetGameState() { score = 0; combo = 0; arrows = []; activeHoldNotes = {}; beatTimer = 0; nextNoteTime = 60; // Start with a small delay lastBeat = 0; updateScore(); updateCombo(); playerHealth.updateHealth(100 - playerHealth.health); opponentHealth.updateHealth(100 - opponentHealth.health); } function startGame() { gameStarted = true; LK.playMusic(currentSong); } // Arrow spawn pattern generators function generateRandomPattern() { var pattern = []; var numNotes = 1 + Math.floor(Math.random() * 2); // 1-2 notes at once // Add some randomness based on difficulty if (currentDifficulty > 1 && Math.random() < 0.3) { numNotes += 1; } // Occasionally add hold notes var shouldAddHold = currentDifficulty > 1 && Math.random() < 0.2; // Select random directions var usedDirections = {}; for (var i = 0; i < numNotes; i++) { var direction; do { direction = arrowTypes[Math.floor(Math.random() * arrowTypes.length)]; } while (usedDirections[direction]); usedDirections[direction] = true; var holdDuration = 0; if (shouldAddHold && i === 0) { // Only make one note a hold note at most holdDuration = 20 + Math.floor(Math.random() * 40); // Hold for 20-60 frames } pattern.push({ direction: direction, holdDuration: holdDuration }); } return pattern; } // Game input handlers game.handleInput = function (direction) { if (!gameStarted) return; var hitAreaY = hitAreas[direction].y; var closestArrow = null; var closestDistance = Infinity; // Find the closest arrow of the correct direction for (var i = 0; i < arrows.length; i++) { var arrow = arrows[i]; if (arrow.direction === direction && arrow.isActive && !arrow.wasHit) { var distance = Math.abs(arrow.y - hitAreaY); if (distance < closestDistance) { closestDistance = distance; closestArrow = arrow; } } } // Check if any arrow is close enough to hit if (closestArrow && closestDistance < 100) { // Determine rating based on distance var rating; var points = 0; if (closestDistance < 20) { rating = 'perfect'; points = 100; LK.getSound('perfect').play(); } else if (closestDistance < 50) { rating = 'good'; points = 50; LK.getSound('hit').play(); } else { rating = 'okay'; points = 25; LK.getSound('hit').play(); } // Apply combo bonus if (combo > 10) { points = Math.floor(points * (1 + combo / 100)); } // Hit the arrow var result = closestArrow.hit(rating); // Handle the result if (result === 'hold_start') { // Start tracking the hold note activeHoldNotes[direction] = closestArrow; hitAreas[direction].setHighlight(true); } else { // Regular note hit handleRating(result, points); } // Show player animation playerCharacter.setAction(); } else { // Check if we're re-pressing a hold note that was active if (activeHoldNotes[direction]) { activeHoldNotes[direction].holdActive = true; hitAreas[direction].setHighlight(true); } else { // Miss if no arrows to hit handleRating('miss', 0); LK.getSound('miss').play(); } } }; game.handleInputRelease = function (direction) { if (!gameStarted) return; hitAreas[direction].setHighlight(false); // Check if we're releasing a hold note if (activeHoldNotes[direction]) { activeHoldNotes[direction].holdActive = false; } }; game.handleMiss = function () { handleRating('miss', 0); LK.getSound('miss').play(); }; function handleRating(rating, points) { if (rating === 'miss' || rating === 'hold_break') { // Break combo on miss combo = 0; // Damage player health var gameOver = playerHealth.updateHealth(-5); if (gameOver) { endGame(false); } } else { // Increase combo combo++; // Add score score += points; // Damage opponent health var playerWon = opponentHealth.updateHealth(-5); if (playerWon) { endGame(true); } } // Show rating ratingDisplay.show(rating, points); // Update UI updateScore(); updateCombo(); } function updateScore() { scoreText.setText('SCORE: ' + score); } function updateCombo() { if (combo > 1) { comboText.setText('COMBO: ' + combo + 'x'); } else { comboText.setText(''); } } function spawnArrow(direction, holdDuration) { var hitAreaY = hitAreas[direction].y; var hitAreaX = hitAreas[direction].x; var arrow = new Arrow(direction, noteSpeed, holdDuration); arrow.x = hitAreaX; arrow.y = hitAreaY + 1000; // Start below screen game.addChild(arrow); arrows.push(arrow); } function endGame(playerWon) { gameStarted = false; LK.stopMusic(); if (playerWon) { LK.showYouWin(); } else { LK.showGameOver(); } } // Game update loop game.update = function () { if (!gameStarted) { return; } // Update beat timer beatTimer++; // Check if it's time to spawn a new set of arrows if (beatTimer >= nextNoteTime) { // Generate a pattern based on the current beat var pattern = generateRandomPattern(); // Spawn the arrows for (var i = 0; i < pattern.length; i++) { spawnArrow(pattern[i].direction, pattern[i].holdDuration); } // Opponent animation when they "hit" notes opponentCharacter.setAction(); // Set time for next note var beatMultiplier = 1; // Vary the timing for more complexity if (Math.random() < 0.3) { beatMultiplier = 0.5; // Eighth note } else if (Math.random() < 0.2) { beatMultiplier = 0.25; // Sixteenth note } else if (Math.random() < 0.1) { beatMultiplier = 2; // Half note } nextNoteTime += beatInterval * beatMultiplier; lastBeat = beatTimer; } // Update hold notes for (var direction in activeHoldNotes) { var holdNote = activeHoldNotes[direction]; if (holdNote) { var result = holdNote.updateHold(hitAreas[direction].isPressed); if (result === 'hold_complete') { // Hold was completed successfully handleRating('hold_complete', 150); activeHoldNotes[direction] = null; hitAreas[direction].setHighlight(false); } else if (result === 'hold_break') { // Hold was broken early handleRating('hold_break', 0); activeHoldNotes[direction] = null; hitAreas[direction].setHighlight(false); } } } // Update arrows for (var a = arrows.length - 1; a >= 0; a--) { var arrow = arrows[a]; if (!arrow.isActive) { arrows.splice(a, 1); arrow.destroy(); } } }; // Initialize the game on start initGame();
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,667 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1");
+
+/****
+* Classes
+****/
+var Arrow = Container.expand(function (direction, speed, holdDuration) {
+ var self = Container.call(this);
+ self.direction = direction || 'up';
+ self.speed = speed || 10;
+ self.holdDuration = holdDuration || 0;
+ self.isHold = self.holdDuration > 0;
+ self.isActive = true;
+ self.wasHit = false;
+ self.holdActive = false;
+ self.holdTime = 0;
+ // Get arrow asset based on direction
+ var arrowAsset = 'arrow_' + self.direction;
+ var arrow = self.attachAsset(arrowAsset, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Rotate arrow based on direction
+ if (self.direction === 'up') {
+ arrow.rotation = 0;
+ } else if (self.direction === 'down') {
+ arrow.rotation = Math.PI;
+ } else if (self.direction === 'left') {
+ arrow.rotation = -Math.PI / 2;
+ } else if (self.direction === 'right') {
+ arrow.rotation = Math.PI / 2;
+ }
+ // If it's a hold note, create an extension
+ if (self.isHold) {
+ self.holdExtension = self.addChild(LK.getAsset(arrowAsset, {
+ anchorX: 0.5,
+ anchorY: 0,
+ y: arrow.height / 2,
+ scaleY: self.holdDuration / 100,
+ // Scale based on duration
+ alpha: 0.5
+ }));
+ // Rotate the hold extension
+ if (self.direction === 'up') {
+ self.holdExtension.rotation = 0;
+ } else if (self.direction === 'down') {
+ self.holdExtension.rotation = Math.PI;
+ self.holdExtension.y = -arrow.height / 2;
+ self.holdExtension.anchorY = 1;
+ } else if (self.direction === 'left') {
+ self.holdExtension.rotation = -Math.PI / 2;
+ self.holdExtension.x = -arrow.height / 2;
+ self.holdExtension.y = 0;
+ self.holdExtension.anchorX = 1;
+ self.holdExtension.anchorY = 0.5;
+ } else if (self.direction === 'right') {
+ self.holdExtension.rotation = Math.PI / 2;
+ self.holdExtension.x = arrow.height / 2;
+ self.holdExtension.y = 0;
+ self.holdExtension.anchorX = 0;
+ self.holdExtension.anchorY = 0.5;
+ }
+ }
+ self.update = function () {
+ // Move arrow upward
+ self.y -= self.speed;
+ // Check if the arrow is off screen and should be removed
+ if (self.y < -300 && self.isActive) {
+ if (!self.wasHit) {
+ // Player missed the arrow
+ game.handleMiss();
+ }
+ self.isActive = false;
+ LK.setTimeout(function () {
+ self.destroy();
+ var index = arrows.indexOf(self);
+ if (index > -1) {
+ arrows.splice(index, 1);
+ }
+ }, 100);
+ }
+ };
+ self.hit = function (rating) {
+ if (self.isActive && !self.wasHit) {
+ self.wasHit = true;
+ if (self.isHold) {
+ self.holdActive = true;
+ return 'hold_start';
+ } else {
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 200
+ });
+ LK.setTimeout(function () {
+ self.isActive = false;
+ }, 200);
+ return rating;
+ }
+ }
+ return null;
+ };
+ self.updateHold = function (holdActive) {
+ if (self.isHold && self.wasHit) {
+ self.holdTime += 1;
+ if (holdActive) {
+ // Player is holding correctly
+ self.holdExtension.alpha = 0.8;
+ // Check if hold is complete
+ if (self.holdTime >= self.holdDuration) {
+ self.isActive = false;
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 200
+ });
+ return 'hold_complete';
+ }
+ return 'holding';
+ } else {
+ // Player released too early
+ self.holdExtension.alpha = 0.3;
+ self.isActive = false;
+ tween(self, {
+ alpha: 0
+ }, {
+ duration: 200
+ });
+ return 'hold_break';
+ }
+ }
+ return null;
+ };
+ return self;
+});
+var Character = Container.expand(function (isPlayer) {
+ var self = Container.call(this);
+ self.isPlayer = isPlayer;
+ var sprite = self.attachAsset(isPlayer ? 'player' : 'opponent', {
+ anchorX: 0.5,
+ anchorY: 1
+ });
+ self.setIdle = function () {
+ tween(sprite, {
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ };
+ self.setAction = function () {
+ tween(sprite, {
+ scaleY: 1.05
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ tween(sprite, {
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ }
+ });
+ };
+ return self;
+});
+var HealthBar = Container.expand(function (isPlayer) {
+ var self = Container.call(this);
+ self.health = 100;
+ self.isPlayer = isPlayer;
+ // Background bar
+ var barBg = self.attachAsset('healthBar', {
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ // Health fill
+ var fillAsset = isPlayer ? 'healthFill' : 'opponentHealthFill';
+ self.fillBar = self.attachAsset(fillAsset, {
+ anchorX: 0,
+ anchorY: 0.5,
+ x: 2,
+ scaleX: 796 // Width - 4 for padding
+ });
+ // Label
+ self.label = new Text2(isPlayer ? "YOU" : "CPU", {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ self.label.anchor.set(0.5);
+ self.label.x = barBg.width / 2;
+ self.label.y = 0;
+ self.addChild(self.label);
+ self.updateHealth = function (amount) {
+ self.health = Math.max(0, Math.min(100, self.health + amount));
+ tween(self.fillBar, {
+ scaleX: 796 * (self.health / 100)
+ }, {
+ duration: 300,
+ easing: tween.easeOut
+ });
+ if (self.health <= 0) {
+ return true; // Health depleted
+ }
+ return false;
+ };
+ return self;
+});
+var HitArea = Container.expand(function (direction) {
+ var self = Container.call(this);
+ self.direction = direction;
+ self.isPressed = false;
+ var hitBox = self.attachAsset('hitArea', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.3
+ });
+ var arrow = self.attachAsset('arrow_' + direction, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ });
+ // Rotate arrow based on direction
+ if (direction === 'up') {
+ arrow.rotation = 0;
+ } else if (direction === 'down') {
+ arrow.rotation = Math.PI;
+ } else if (direction === 'left') {
+ arrow.rotation = -Math.PI / 2;
+ } else if (direction === 'right') {
+ arrow.rotation = Math.PI / 2;
+ }
+ self.down = function (x, y, obj) {
+ self.isPressed = true;
+ hitBox.alpha = 0.7;
+ game.handleInput(self.direction);
+ };
+ self.up = function (x, y, obj) {
+ self.isPressed = false;
+ hitBox.alpha = 0.3;
+ game.handleInputRelease(self.direction);
+ };
+ self.setHighlight = function (highlight) {
+ if (highlight) {
+ tween(hitBox, {
+ alpha: 0.7
+ }, {
+ duration: 100
+ });
+ tween(arrow, {
+ alpha: 1
+ }, {
+ duration: 100
+ });
+ } else {
+ tween(hitBox, {
+ alpha: 0.3
+ }, {
+ duration: 100
+ });
+ tween(arrow, {
+ alpha: 0.7
+ }, {
+ duration: 100
+ });
+ }
+ };
+ return self;
+});
+var RatingDisplay = Container.expand(function () {
+ var self = Container.call(this);
+ self.text = new Text2('', {
+ size: 80,
+ fill: 0xFFFFFF
+ });
+ self.text.anchor.set(0.5);
+ self.addChild(self.text);
+ self.show = function (rating, score) {
+ var ratingText = '';
+ var color = "#FFFFFF";
+ switch (rating) {
+ case 'perfect':
+ ratingText = 'PERFECT!';
+ color = "#00FFFF";
+ break;
+ case 'good':
+ ratingText = 'GOOD';
+ color = "#00FF00";
+ break;
+ case 'okay':
+ ratingText = 'OKAY';
+ color = "#FFFF00";
+ break;
+ case 'miss':
+ ratingText = 'MISS';
+ color = "#FF0000";
+ break;
+ case 'hold_complete':
+ ratingText = 'HOLD BONUS!';
+ color = "#FF00FF";
+ break;
+ case 'hold_break':
+ ratingText = 'HOLD BROKEN!';
+ color = "#FF8800";
+ break;
+ }
+ if (score && rating !== 'miss' && rating !== 'hold_break') {
+ ratingText += '\n+' + score;
+ }
+ self.text.setText(ratingText);
+ self.text.style.fill = color;
+ self.alpha = 1;
+ self.scale.set(1.2);
+ tween(self, {
+ alpha: 0,
+ y: self.y - 50
+ }, {
+ duration: 800,
+ easing: tween.easeOut
+ });
+ tween(self.scale, {
+ x: 1,
+ y: 1
+ }, {
+ duration: 300,
+ easing: tween.elasticOut
+ });
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x222222
+});
+
+/****
+* Game Code
+****/
+// Game variables
+var arrows = [];
+var hitAreas = {};
+var activeHoldNotes = {};
+var gameStarted = false;
+var score = 0;
+var combo = 0;
+var currentSong = 'song1';
+var songBPM = 120;
+var noteSpeed = 10;
+var currentDifficulty = 1;
+var beatTimer = 0;
+var nextNoteTime = 0;
+var lastBeat = 0;
+var beatInterval = 60 / songBPM * 60; // Convert to frames
+var arrowTypes = ['up', 'down', 'left', 'right'];
+// Create game elements
+var playerCharacter, opponentCharacter;
+var playerHealth, opponentHealth;
+var ratingDisplay;
+var scoreText, comboText;
+var gameTimingBar;
+// Initialize game
+function initGame() {
+ // Create UI elements
+ scoreText = new Text2('SCORE: 0', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ scoreText.anchor.set(0.5, 0);
+ scoreText.x = 2048 / 2;
+ scoreText.y = 50;
+ LK.gui.top.addChild(scoreText);
+ comboText = new Text2('', {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ comboText.anchor.set(0.5, 0);
+ comboText.x = 2048 / 2;
+ comboText.y = 120;
+ LK.gui.top.addChild(comboText);
+ // Create health bars
+ playerHealth = new HealthBar(true);
+ playerHealth.x = 150;
+ playerHealth.y = 100;
+ game.addChild(playerHealth);
+ opponentHealth = new HealthBar(false);
+ opponentHealth.x = 2048 - 950;
+ opponentHealth.y = 100;
+ game.addChild(opponentHealth);
+ // Create characters
+ playerCharacter = new Character(true);
+ playerCharacter.x = 600;
+ playerCharacter.y = 2732 - 100;
+ game.addChild(playerCharacter);
+ opponentCharacter = new Character(false);
+ opponentCharacter.x = 2048 - 600;
+ opponentCharacter.y = 2732 - 100;
+ game.addChild(opponentCharacter);
+ // Create hit areas
+ var hitAreaY = 2732 - 300;
+ var hitAreaSpacing = 220;
+ var centerX = 2048 / 2;
+ hitAreas.left = new HitArea('left');
+ hitAreas.left.x = centerX - hitAreaSpacing - hitAreaSpacing / 2;
+ hitAreas.left.y = hitAreaY;
+ game.addChild(hitAreas.left);
+ hitAreas.down = new HitArea('down');
+ hitAreas.down.x = centerX - hitAreaSpacing / 2;
+ hitAreas.down.y = hitAreaY;
+ game.addChild(hitAreas.down);
+ hitAreas.up = new HitArea('up');
+ hitAreas.up.x = centerX + hitAreaSpacing / 2;
+ hitAreas.up.y = hitAreaY;
+ game.addChild(hitAreas.up);
+ hitAreas.right = new HitArea('right');
+ hitAreas.right.x = centerX + hitAreaSpacing + hitAreaSpacing / 2;
+ hitAreas.right.y = hitAreaY;
+ game.addChild(hitAreas.right);
+ // Create rating display
+ ratingDisplay = new RatingDisplay();
+ ratingDisplay.x = 2048 / 2;
+ ratingDisplay.y = hitAreaY - 300;
+ game.addChild(ratingDisplay);
+ // Reset game state
+ resetGameState();
+ // Start game
+ startGame();
+}
+function resetGameState() {
+ score = 0;
+ combo = 0;
+ arrows = [];
+ activeHoldNotes = {};
+ beatTimer = 0;
+ nextNoteTime = 60; // Start with a small delay
+ lastBeat = 0;
+ updateScore();
+ updateCombo();
+ playerHealth.updateHealth(100 - playerHealth.health);
+ opponentHealth.updateHealth(100 - opponentHealth.health);
+}
+function startGame() {
+ gameStarted = true;
+ LK.playMusic(currentSong);
+}
+// Arrow spawn pattern generators
+function generateRandomPattern() {
+ var pattern = [];
+ var numNotes = 1 + Math.floor(Math.random() * 2); // 1-2 notes at once
+ // Add some randomness based on difficulty
+ if (currentDifficulty > 1 && Math.random() < 0.3) {
+ numNotes += 1;
+ }
+ // Occasionally add hold notes
+ var shouldAddHold = currentDifficulty > 1 && Math.random() < 0.2;
+ // Select random directions
+ var usedDirections = {};
+ for (var i = 0; i < numNotes; i++) {
+ var direction;
+ do {
+ direction = arrowTypes[Math.floor(Math.random() * arrowTypes.length)];
+ } while (usedDirections[direction]);
+ usedDirections[direction] = true;
+ var holdDuration = 0;
+ if (shouldAddHold && i === 0) {
+ // Only make one note a hold note at most
+ holdDuration = 20 + Math.floor(Math.random() * 40); // Hold for 20-60 frames
+ }
+ pattern.push({
+ direction: direction,
+ holdDuration: holdDuration
+ });
+ }
+ return pattern;
+}
+// Game input handlers
+game.handleInput = function (direction) {
+ if (!gameStarted) return;
+ var hitAreaY = hitAreas[direction].y;
+ var closestArrow = null;
+ var closestDistance = Infinity;
+ // Find the closest arrow of the correct direction
+ for (var i = 0; i < arrows.length; i++) {
+ var arrow = arrows[i];
+ if (arrow.direction === direction && arrow.isActive && !arrow.wasHit) {
+ var distance = Math.abs(arrow.y - hitAreaY);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestArrow = arrow;
+ }
+ }
+ }
+ // Check if any arrow is close enough to hit
+ if (closestArrow && closestDistance < 100) {
+ // Determine rating based on distance
+ var rating;
+ var points = 0;
+ if (closestDistance < 20) {
+ rating = 'perfect';
+ points = 100;
+ LK.getSound('perfect').play();
+ } else if (closestDistance < 50) {
+ rating = 'good';
+ points = 50;
+ LK.getSound('hit').play();
+ } else {
+ rating = 'okay';
+ points = 25;
+ LK.getSound('hit').play();
+ }
+ // Apply combo bonus
+ if (combo > 10) {
+ points = Math.floor(points * (1 + combo / 100));
+ }
+ // Hit the arrow
+ var result = closestArrow.hit(rating);
+ // Handle the result
+ if (result === 'hold_start') {
+ // Start tracking the hold note
+ activeHoldNotes[direction] = closestArrow;
+ hitAreas[direction].setHighlight(true);
+ } else {
+ // Regular note hit
+ handleRating(result, points);
+ }
+ // Show player animation
+ playerCharacter.setAction();
+ } else {
+ // Check if we're re-pressing a hold note that was active
+ if (activeHoldNotes[direction]) {
+ activeHoldNotes[direction].holdActive = true;
+ hitAreas[direction].setHighlight(true);
+ } else {
+ // Miss if no arrows to hit
+ handleRating('miss', 0);
+ LK.getSound('miss').play();
+ }
+ }
+};
+game.handleInputRelease = function (direction) {
+ if (!gameStarted) return;
+ hitAreas[direction].setHighlight(false);
+ // Check if we're releasing a hold note
+ if (activeHoldNotes[direction]) {
+ activeHoldNotes[direction].holdActive = false;
+ }
+};
+game.handleMiss = function () {
+ handleRating('miss', 0);
+ LK.getSound('miss').play();
+};
+function handleRating(rating, points) {
+ if (rating === 'miss' || rating === 'hold_break') {
+ // Break combo on miss
+ combo = 0;
+ // Damage player health
+ var gameOver = playerHealth.updateHealth(-5);
+ if (gameOver) {
+ endGame(false);
+ }
+ } else {
+ // Increase combo
+ combo++;
+ // Add score
+ score += points;
+ // Damage opponent health
+ var playerWon = opponentHealth.updateHealth(-5);
+ if (playerWon) {
+ endGame(true);
+ }
+ }
+ // Show rating
+ ratingDisplay.show(rating, points);
+ // Update UI
+ updateScore();
+ updateCombo();
+}
+function updateScore() {
+ scoreText.setText('SCORE: ' + score);
+}
+function updateCombo() {
+ if (combo > 1) {
+ comboText.setText('COMBO: ' + combo + 'x');
+ } else {
+ comboText.setText('');
+ }
+}
+function spawnArrow(direction, holdDuration) {
+ var hitAreaY = hitAreas[direction].y;
+ var hitAreaX = hitAreas[direction].x;
+ var arrow = new Arrow(direction, noteSpeed, holdDuration);
+ arrow.x = hitAreaX;
+ arrow.y = hitAreaY + 1000; // Start below screen
+ game.addChild(arrow);
+ arrows.push(arrow);
+}
+function endGame(playerWon) {
+ gameStarted = false;
+ LK.stopMusic();
+ if (playerWon) {
+ LK.showYouWin();
+ } else {
+ LK.showGameOver();
+ }
+}
+// Game update loop
+game.update = function () {
+ if (!gameStarted) {
+ return;
+ }
+ // Update beat timer
+ beatTimer++;
+ // Check if it's time to spawn a new set of arrows
+ if (beatTimer >= nextNoteTime) {
+ // Generate a pattern based on the current beat
+ var pattern = generateRandomPattern();
+ // Spawn the arrows
+ for (var i = 0; i < pattern.length; i++) {
+ spawnArrow(pattern[i].direction, pattern[i].holdDuration);
+ }
+ // Opponent animation when they "hit" notes
+ opponentCharacter.setAction();
+ // Set time for next note
+ var beatMultiplier = 1;
+ // Vary the timing for more complexity
+ if (Math.random() < 0.3) {
+ beatMultiplier = 0.5; // Eighth note
+ } else if (Math.random() < 0.2) {
+ beatMultiplier = 0.25; // Sixteenth note
+ } else if (Math.random() < 0.1) {
+ beatMultiplier = 2; // Half note
+ }
+ nextNoteTime += beatInterval * beatMultiplier;
+ lastBeat = beatTimer;
+ }
+ // Update hold notes
+ for (var direction in activeHoldNotes) {
+ var holdNote = activeHoldNotes[direction];
+ if (holdNote) {
+ var result = holdNote.updateHold(hitAreas[direction].isPressed);
+ if (result === 'hold_complete') {
+ // Hold was completed successfully
+ handleRating('hold_complete', 150);
+ activeHoldNotes[direction] = null;
+ hitAreas[direction].setHighlight(false);
+ } else if (result === 'hold_break') {
+ // Hold was broken early
+ handleRating('hold_break', 0);
+ activeHoldNotes[direction] = null;
+ hitAreas[direction].setHighlight(false);
+ }
+ }
+ }
+ // Update arrows
+ for (var a = arrows.length - 1; a >= 0; a--) {
+ var arrow = arrows[a];
+ if (!arrow.isActive) {
+ arrows.splice(a, 1);
+ arrow.destroy();
+ }
+ }
+};
+// Initialize the game on start
+initGame();
\ No newline at end of file