/**** * 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('Banger'); } // 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();
/****
* 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('Banger');
}
// 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();