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