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);
if (self.text.style) {
self.text.style.fill = color;
} else {
self.text.setStyle({
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
@@ -308,9 +308,15 @@
if (score && rating !== 'miss' && rating !== 'hold_break') {
ratingText += '\n+' + score;
}
self.text.setText(ratingText);
- self.text.style.fill = color;
+ if (self.text.style) {
+ self.text.style.fill = color;
+ } else {
+ self.text.setStyle({
+ fill: color
+ });
+ }
self.alpha = 1;
self.scale.set(1.2);
tween(self, {
alpha: 0,