/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
currentLevel: 1,
totalGamesPlayed: 0,
achievements: {},
unlockedLevels: 1
});
/****
* Classes
****/
var Particle = Container.expand(function (x, y, color) {
var self = Container.call(this);
var graphics = self.attachAsset('feedbackPerfect', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = color || 0xFFD700;
graphics.width = 20;
graphics.height = 20;
self.x = x;
self.y = y;
self.vx = (Math.random() - 0.5) * 10;
self.vy = (Math.random() - 0.5) * 10 - 5;
self.life = 60;
self.maxLife = 60;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.vy += 0.2; // gravity
self.life--;
self.alpha = self.life / self.maxLife;
if (self.life <= 0) {
self.destroy();
}
};
return self;
});
var PowerTile = Container.expand(function (laneIndex, powerType) {
var self = Container.call(this);
var graphics = self.attachAsset('quickTile' + laneIndex, {
anchorX: 0.5,
anchorY: 0.5
});
// Tint based on power type
if (powerType === 'double') {
graphics.tint = 0xFFD700; // Gold for double score
} else if (powerType === 'slow') {
graphics.tint = 0x00FFFF; // Cyan for slow time
} else if (powerType === 'shield') {
graphics.tint = 0x00FF00; // Green for shield
}
self.speed = 12;
self.lane = laneIndex || 0;
self.powerType = powerType;
self.hit = false;
self.missed = false;
self.earlyPressed = false;
self.destroyed = false;
// Add pulsing effect
self.pulseTimer = 0;
self.update = function () {
if (self.destroyed) return;
self.y += self.speed;
// Pulsing effect
self.pulseTimer++;
graphics.scaleX = 1 + Math.sin(self.pulseTimer * 0.2) * 0.1;
graphics.scaleY = 1 + Math.sin(self.pulseTimer * 0.2) * 0.1;
// Check if tile is past hit line and not hit
if (self.y > hitLineY + 80 && !self.hit && !self.missed) {
self.missed = true;
comboCount = 0;
comboMultiplier = 1;
perfectStreak = 0;
showFeedback('MISSED', self.lane);
LK.getSound('miss').play();
LK.showGameOver();
}
};
return self;
});
var QuickTile = Container.expand(function (laneIndex) {
var self = Container.call(this);
var assetName = 'quickTile' + laneIndex;
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.lane = laneIndex || 0;
self.hit = false;
self.missed = false;
self.earlyPressed = false;
self.destroyed = false;
self.update = function () {
if (self.destroyed) return;
self.y += self.speed;
// Check if tile is past hit line and not hit
if (self.y > hitLineY + 80 && !self.hit && !self.missed) {
self.missed = true;
comboCount = 0; // Reset combo on miss
comboMultiplier = 1; // Reset multiplier
perfectStreak = 0; // Reset perfect streak
showFeedback('MISSED', self.lane);
LK.getSound('miss').play();
LK.showGameOver();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var lanes = [];
var hitLineY = 2200;
var tiles = [];
var activeLanes = [false, false, false, false];
var comboCount = 0;
var tileSpawnTimer = 0;
var feedbackTexts = [];
var tileSpawnInterval = 45;
var gameSpeed = 1;
var earlyCount = 0;
var keyPressCount = 0;
var particles = [];
var comboMultiplier = 1;
var perfectStreak = 0;
var lastHitTime = 0;
var currentLevel = storage.currentLevel || 1;
var powerTiles = [];
var activePowerUps = {
doubleScore: 0,
slowTime: 0,
shield: 0
};
// Experience and progression system
var playerExp = storage.playerExp || 0;
var playerLevel = storage.playerLevel || 1;
var skillPoints = storage.skillPoints || 0;
var unlockedSkills = storage.unlockedSkills || {};
// Skill tree definitions with meaningful effects
var skills = {
speedBoost: {
name: "Speed Boost",
desc: "Tiles move 10% slower",
cost: 2,
maxLevel: 3,
effect: function effect(level) {
return 0.1 * level;
}
},
scoreMultiplier: {
name: "Score Master",
desc: "Base score +25% per level",
cost: 3,
maxLevel: 4,
effect: function effect(level) {
return 0.25 * level;
}
},
comboExtender: {
name: "Combo Keeper",
desc: "Combo decays 50% slower per level",
cost: 2,
maxLevel: 2,
effect: function effect(level) {
return 0.5 * level;
}
},
perfectWindow: {
name: "Perfect Timing",
desc: "Perfect hit window +10 pixels per level",
cost: 4,
maxLevel: 2,
effect: function effect(level) {
return 10 * level;
}
},
powerUpDuration: {
name: "Power Mastery",
desc: "Power-ups last 50% longer per level",
cost: 3,
maxLevel: 3,
effect: function effect(level) {
return 0.5 * level;
}
}
};
// Level rewards system
var levelRewards = {
2: {
skillPoints: 1,
message: "Skill Point Earned!"
},
3: {
skillPoints: 1,
coins: 50,
message: "Skill Point + 50 Coins!"
},
5: {
skillPoints: 2,
coins: 100,
message: "2 Skill Points + 100 Coins!"
},
7: {
skillPoints: 1,
coins: 75,
message: "Skill Point + 75 Coins!"
},
10: {
skillPoints: 3,
coins: 200,
message: "3 Skill Points + 200 Coins!"
}
};
// Currency system
var coins = storage.coins || 0;
var dailyBonusCollected = storage.dailyBonusCollected || 0;
// Achievement definitions with meaningful rewards
var achievements = {
firstWin: {
name: "First Steps",
desc: "Complete your first game",
unlocked: false,
reward: {
coins: 25,
skillPoints: 1
}
},
perfectionist: {
name: "Perfectionist",
desc: "Get 50 perfect hits in a row",
unlocked: false,
reward: {
coins: 150,
skillPoints: 2
}
},
speedDemon: {
name: "Speed Demon",
desc: "Reach level 5",
unlocked: false,
reward: {
coins: 100,
skillPoints: 1
}
},
comboMaster: {
name: "Combo Master",
desc: "Achieve a 100+ combo",
unlocked: false,
reward: {
coins: 200,
skillPoints: 2
}
},
highScorer: {
name: "High Scorer",
desc: "Score 10,000 points",
unlocked: false,
reward: {
coins: 300,
skillPoints: 3
}
},
coinCollector: {
name: "Coin Collector",
desc: "Collect 500 coins total",
unlocked: false,
reward: {
coins: 100,
skillPoints: 1
}
},
marathoner: {
name: "Marathoner",
desc: "Play 25 games",
unlocked: false,
reward: {
coins: 150,
skillPoints: 2
}
}
};
// Load saved achievements
for (var key in achievements) {
if (storage.achievements && storage.achievements[key]) {
achievements[key].unlocked = true;
}
}
// Load saved skills
if (storage.unlockedSkills) {
for (var skillKey in storage.unlockedSkills) {
unlockedSkills[skillKey] = storage.unlockedSkills[skillKey];
}
}
// Create lanes
for (var i = 0; i < 4; i++) {
var lane = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
}));
lane.x = 256 + i * 512;
lane.y = 1366;
lanes.push(lane);
}
// Create hit line
var hitLine = game.addChild(LK.getAsset('hitLine', {
anchorX: 0.5,
anchorY: 0.5
}));
hitLine.x = 1024;
hitLine.y = hitLineY;
// Create lane dividers
for (var i = 1; i < 4; i++) {
var divider = game.addChild(LK.getAsset('laneDivider', {
anchorX: 0.5,
anchorY: 0.5
}));
divider.x = i * 512;
divider.y = 1366;
}
// Create UI
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var comboText = new Text2('Combo: 0', {
size: 60,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0);
comboText.y = 100;
LK.gui.top.addChild(comboText);
var earlyText = new Text2('Early: 0/3', {
size: 60,
fill: 0xFF4444
});
earlyText.anchor.set(0.5, 0);
earlyText.y = 160;
LK.gui.top.addChild(earlyText);
var streakText = new Text2('Streak: 0', {
size: 50,
fill: 0x00FFFF
});
streakText.anchor.set(0.5, 0);
streakText.y = 220;
LK.gui.top.addChild(streakText);
var multiplierText = new Text2('x1', {
size: 80,
fill: 0xFFD700
});
multiplierText.anchor.set(1, 0);
multiplierText.x = -20;
multiplierText.y = 20;
LK.gui.topRight.addChild(multiplierText);
var levelText = new Text2('Level: ' + currentLevel, {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
levelText.x = 20;
levelText.y = 20;
LK.gui.topLeft.addChild(levelText);
var highScoreText = new Text2('Best: ' + storage.highScore, {
size: 40,
fill: 0xFFD700
});
highScoreText.anchor.set(0, 0);
highScoreText.x = 20;
highScoreText.y = 80;
LK.gui.topLeft.addChild(highScoreText);
// Add progression UI elements
var expText = new Text2('EXP: ' + playerExp, {
size: 35,
fill: 0x00FFFF
});
expText.anchor.set(0, 0);
expText.x = 20;
expText.y = 120;
LK.gui.topLeft.addChild(expText);
var playerLevelText = new Text2('Player Lv: ' + playerLevel, {
size: 35,
fill: 0xFFD700
});
playerLevelText.anchor.set(0, 0);
playerLevelText.x = 20;
playerLevelText.y = 160;
LK.gui.topLeft.addChild(playerLevelText);
var coinsText = new Text2('Coins: ' + coins, {
size: 40,
fill: 0xFFD700
});
coinsText.anchor.set(1, 0);
coinsText.x = -20;
coinsText.y = 80;
LK.gui.topRight.addChild(coinsText);
var skillPointsText = new Text2('SP: ' + skillPoints, {
size: 35,
fill: 0xFF00FF
});
skillPointsText.anchor.set(1, 0);
skillPointsText.x = -20;
skillPointsText.y = 120;
LK.gui.topRight.addChild(skillPointsText);
// Update all UI on start
updateScoreDisplay();
function updateScoreDisplay() {
scoreText.setText('Score: ' + LK.getScore());
comboText.setText('Combo: ' + comboCount);
earlyText.setText('Early: ' + earlyCount + '/3');
streakText.setText('Streak: ' + perfectStreak);
multiplierText.setText('x' + comboMultiplier);
levelText.setText('Level: ' + currentLevel);
highScoreText.setText('Best: ' + storage.highScore);
expText.setText('EXP: ' + playerExp);
playerLevelText.setText('Player Lv: ' + playerLevel);
coinsText.setText('Coins: ' + coins);
skillPointsText.setText('SP: ' + skillPoints);
// Update multiplier color based on value
if (comboMultiplier >= 4) {
multiplierText.fill = 0xFF0000; // Red for 4x+
} else if (comboMultiplier >= 3) {
multiplierText.fill = 0xFF8800; // Orange for 3x
} else if (comboMultiplier >= 2) {
multiplierText.fill = 0xFFD700; // Gold for 2x
} else {
multiplierText.fill = 0xFFFFFF; // White for 1x
}
// Check achievements
checkAchievements();
}
function showFeedback(text, laneIndex) {
var assetName = 'feedback' + text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
var feedbackImage = game.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
}));
feedbackImage.x = 1024; // Center of screen
feedbackImage.y = 1366; // Center of screen vertically
feedbackImage.alpha = 1;
feedbackTexts.push(feedbackImage);
// Add score display based on feedback type
var scoreDisplay;
if (text === 'NICE') {
scoreDisplay = game.addChild(LK.getAsset('positiveScoreDisplay', {
anchorX: 0.5,
anchorY: 0.5
}));
} else {
scoreDisplay = game.addChild(LK.getAsset('negativeScoreDisplay', {
anchorX: 0.5,
anchorY: 0.5
}));
}
scoreDisplay.x = 1024; // Center of screen
scoreDisplay.y = 1450; // Below feedback
scoreDisplay.alpha = 1;
feedbackTexts.push(scoreDisplay);
// Create score text overlay
var scoreChangeText = new Text2('', {
size: 60,
fill: text === 'NICE' ? 0x00FF00 : 0xFF0000
});
scoreChangeText.anchor.set(0.5, 0.5);
scoreChangeText.x = scoreDisplay.x;
scoreChangeText.y = scoreDisplay.y;
if (text === 'NICE') {
scoreChangeText.setText('+10');
} else if (text === 'EARLY') {
scoreChangeText.setText('-30');
} else if (text === 'MISSED') {
scoreChangeText.setText('0');
}
game.addChild(scoreChangeText);
feedbackTexts.push(scoreChangeText);
tween(feedbackImage, {
alpha: 0,
y: feedbackImage.y - 100
}, {
duration: 1000,
onFinish: function onFinish() {
feedbackImage.destroy();
var index = feedbackTexts.indexOf(feedbackImage);
if (index > -1) feedbackTexts.splice(index, 1);
}
});
tween(scoreDisplay, {
alpha: 0,
y: scoreDisplay.y - 100
}, {
duration: 1000,
onFinish: function onFinish() {
scoreDisplay.destroy();
var index = feedbackTexts.indexOf(scoreDisplay);
if (index > -1) feedbackTexts.splice(index, 1);
}
});
tween(scoreChangeText, {
alpha: 0,
y: scoreChangeText.y - 100
}, {
duration: 1000,
onFinish: function onFinish() {
scoreChangeText.destroy();
var index = feedbackTexts.indexOf(scoreChangeText);
if (index > -1) feedbackTexts.splice(index, 1);
}
});
}
function spawnTile() {
// 5% chance to spawn power tile (increases with level)
var powerChance = 0.05 + currentLevel * 0.01;
if (Math.random() < powerChance) {
spawnPowerTile();
return;
}
// Adjusted spawn chances based on level
var multiTileChance = 0.3 + currentLevel * 0.05; // More multi-tiles at higher levels
var spawnTwo = Math.random() < multiTileChance;
if (spawnTwo) {
// Spawn two tiles in different lanes
var laneIndex1 = Math.floor(Math.random() * 4);
var laneIndex2;
// Make sure second lane is different from first
do {
laneIndex2 = Math.floor(Math.random() * 4);
} while (laneIndex2 === laneIndex1);
// Create first tile
var tile1 = new QuickTile(laneIndex1);
tile1.x = 256 + laneIndex1 * 512;
tile1.y = -100;
tiles.push(tile1);
game.addChild(tile1);
// Create second tile
var tile2 = new QuickTile(laneIndex2);
tile2.x = 256 + laneIndex2 * 512;
tile2.y = -100;
tiles.push(tile2);
game.addChild(tile2);
} else {
// Spawn single tile
var laneIndex = Math.floor(Math.random() * 4);
var tile = new QuickTile(laneIndex);
tile.x = 256 + laneIndex * 512;
tile.y = -100;
tiles.push(tile);
game.addChild(tile);
}
}
function createParticles(x, y, count, color) {
for (var i = 0; i < count; i++) {
var particle = new Particle(x, y, color);
particles.push(particle);
game.addChild(particle);
}
}
function checkAchievements() {
var currentScore = LK.getScore();
// High Scorer achievement
if (currentScore >= 10000 && !achievements.highScorer.unlocked) {
unlockAchievement('highScorer');
}
// Perfectionist achievement
if (perfectStreak >= 50 && !achievements.perfectionist.unlocked) {
unlockAchievement('perfectionist');
}
// Combo Master achievement
if (comboCount >= 100 && !achievements.comboMaster.unlocked) {
unlockAchievement('comboMaster');
}
// Speed Demon achievement
if (currentLevel >= 5 && !achievements.speedDemon.unlocked) {
unlockAchievement('speedDemon');
}
// Coin Collector achievement
if (coins >= 500 && !achievements.coinCollector.unlocked) {
unlockAchievement('coinCollector');
}
// Marathoner achievement
if (storage.totalGamesPlayed >= 25 && !achievements.marathoner.unlocked) {
unlockAchievement('marathoner');
}
// Update high score
if (currentScore > storage.highScore) {
storage.highScore = currentScore;
}
}
function awardExperience(amount) {
var oldLevel = playerLevel;
playerExp += amount;
storage.playerExp = playerExp;
// Level up calculation (exponential growth)
var expNeeded = playerLevel * 100 + Math.pow(playerLevel, 2) * 50;
if (playerExp >= expNeeded) {
playerLevel++;
storage.playerLevel = playerLevel;
playerExp = 0;
storage.playerExp = 0;
// Award skill points and coins for level up
var skillPointsAwarded = Math.floor(playerLevel / 3) + 1;
var coinsAwarded = playerLevel * 25;
skillPoints += skillPointsAwarded;
coins += coinsAwarded;
storage.skillPoints = skillPoints;
storage.coins = coins;
// Check for level rewards
if (levelRewards[playerLevel]) {
var reward = levelRewards[playerLevel];
if (reward.skillPoints) {
skillPoints += reward.skillPoints;
storage.skillPoints = skillPoints;
}
if (reward.coins) {
coins += reward.coins;
storage.coins = coins;
}
showLevelUpNotification(playerLevel, reward.message);
} else {
showLevelUpNotification(playerLevel, skillPointsAwarded + ' SP + ' + coinsAwarded + ' Coins!');
}
}
updateScoreDisplay();
}
function showLevelUpNotification(level, rewardText) {
var levelUpText = new Text2('PLAYER LEVEL UP!\nLevel ' + level + '\n' + rewardText, {
size: 60,
fill: 0xFF00FF
});
levelUpText.anchor.set(0.5, 0.5);
levelUpText.x = 1024;
levelUpText.y = 900;
game.addChild(levelUpText);
LK.effects.flashScreen(0xFF00FF, 1000);
tween(levelUpText, {
alpha: 0,
y: levelUpText.y - 150
}, {
duration: 3000,
onFinish: function onFinish() {
levelUpText.destroy();
}
});
}
function getSkillLevel(skillName) {
return unlockedSkills[skillName] || 0;
}
function applySkillEffects() {
// Apply skill effects to game mechanics
var speedBoostLevel = getSkillLevel('speedBoost');
var scoreMultLevel = getSkillLevel('scoreMultiplier');
var comboExtLevel = getSkillLevel('comboExtender');
var perfectWindowLevel = getSkillLevel('perfectWindow');
var powerDurationLevel = getSkillLevel('powerUpDuration');
return {
speedReduction: speedBoostLevel > 0 ? skills.speedBoost.effect(speedBoostLevel) : 0,
scoreBonus: scoreMultLevel > 0 ? skills.scoreMultiplier.effect(scoreMultLevel) : 0,
comboDecayReduction: comboExtLevel > 0 ? skills.comboExtender.effect(comboExtLevel) : 0,
perfectWindowBonus: perfectWindowLevel > 0 ? skills.perfectWindow.effect(perfectWindowLevel) : 0,
powerUpBonus: powerDurationLevel > 0 ? skills.powerUpDuration.effect(powerDurationLevel) : 0
};
}
function checkDailyBonus() {
var today = Math.floor(Date.now() / 86400000); // Days since epoch
if (dailyBonusCollected < today) {
// Award daily bonus
var dailyCoins = 50 + playerLevel * 10;
coins += dailyCoins;
storage.coins = coins;
dailyBonusCollected = today;
storage.dailyBonusCollected = today;
showPowerUpNotification('Daily Bonus: ' + dailyCoins + ' Coins!', 0xFFD700);
updateScoreDisplay();
}
}
function unlockAchievement(achievementKey) {
achievements[achievementKey].unlocked = true;
if (!storage.achievements) storage.achievements = {};
storage.achievements[achievementKey] = true;
// Award achievement rewards
var achievement = achievements[achievementKey];
if (achievement.reward) {
if (achievement.reward.coins) {
coins += achievement.reward.coins;
storage.coins = coins;
}
if (achievement.reward.skillPoints) {
skillPoints += achievement.reward.skillPoints;
storage.skillPoints = skillPoints;
}
}
// Show achievement notification
showAchievementNotification(achievement);
LK.effects.flashScreen(0xFFD700, 500);
updateScoreDisplay();
}
function showAchievementNotification(achievement) {
var rewardText = '';
if (achievement.reward) {
var rewards = [];
if (achievement.reward.coins) rewards.push(achievement.reward.coins + ' Coins');
if (achievement.reward.skillPoints) rewards.push(achievement.reward.skillPoints + ' SP');
if (rewards.length > 0) rewardText = '\nReward: ' + rewards.join(' + ');
}
var achievementText = new Text2('Achievement Unlocked!\n' + achievement.name + rewardText, {
size: 60,
fill: 0xFFD700
});
achievementText.anchor.set(0.5, 0.5);
achievementText.x = 1024;
achievementText.y = 800;
game.addChild(achievementText);
tween(achievementText, {
alpha: 0,
y: achievementText.y - 100
}, {
duration: 3000,
onFinish: function onFinish() {
achievementText.destroy();
}
});
}
function spawnPowerTile() {
var powerTypes = ['double', 'slow', 'shield'];
var powerType = powerTypes[Math.floor(Math.random() * powerTypes.length)];
var laneIndex = Math.floor(Math.random() * 4);
var powerTile = new PowerTile(laneIndex, powerType);
powerTile.x = 256 + laneIndex * 512;
powerTile.y = -100;
powerTiles.push(powerTile);
game.addChild(powerTile);
}
function activatePowerUp(powerType) {
var skillEffects = applySkillEffects();
var baseDuration = 600; // 10 seconds at 60fps
var enhancedDuration = Math.floor(baseDuration * (1 + skillEffects.powerUpBonus));
if (powerType === 'double') {
activePowerUps.doubleScore = enhancedDuration;
showPowerUpNotification('Double Score!', 0xFFD700);
} else if (powerType === 'slow') {
activePowerUps.slowTime = enhancedDuration;
showPowerUpNotification('Slow Time!', 0x00FFFF);
} else if (powerType === 'shield') {
activePowerUps.shield = enhancedDuration;
showPowerUpNotification('Shield Active!', 0x00FF00);
}
}
function showPowerUpNotification(text, color) {
var powerText = new Text2(text, {
size: 80,
fill: color
});
powerText.anchor.set(0.5, 0.5);
powerText.x = 1024;
powerText.y = 1000;
game.addChild(powerText);
tween(powerText, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: powerText.y - 100
}, {
duration: 2000,
onFinish: function onFinish() {
powerText.destroy();
}
});
}
function getLaneFromX(x) {
if (x < 512) return 0;
if (x < 1024) return 1;
if (x < 1536) return 2;
return 3;
}
function activateLane(laneIndex) {
if (laneIndex >= 0 && laneIndex < 4) {
activeLanes[laneIndex] = true;
lanes[laneIndex].removeChild(lanes[laneIndex].children[0]);
var activeLane = LK.getAsset('laneActive', {
anchorX: 0.5,
anchorY: 0.5
});
lanes[laneIndex].addChild(activeLane);
}
}
function deactivateLane(laneIndex) {
if (laneIndex >= 0 && laneIndex < 4) {
activeLanes[laneIndex] = false;
lanes[laneIndex].removeChild(lanes[laneIndex].children[0]);
var normalLane = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
});
lanes[laneIndex].addChild(normalLane);
}
}
game.down = function (x, y, obj) {
var laneIndex = getLaneFromX(x);
activateLane(laneIndex);
// Increment key press counter
keyPressCount++;
// Increase game speed by 2% every 5 key presses
if (keyPressCount % 5 === 0) {
gameSpeed *= 1.02;
}
// Add lane press animation
tween(lanes[laneIndex], {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 100,
easing: tween.easeOut
});
// Check for tile hits
var hitTile = false;
// Check power tiles first
for (var i = 0; i < powerTiles.length; i++) {
var powerTile = powerTiles[i];
if (powerTile.lane === laneIndex && !powerTile.missed && !powerTile.earlyPressed) {
var distance = Math.abs(powerTile.y - hitLineY);
if (distance <= 80 && !powerTile.hit) {
powerTile.hit = true;
activatePowerUp(powerTile.powerType);
createParticles(powerTile.x, powerTile.y, 12, 0xFFD700);
tween(powerTile, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (!powerTile.destroyed) {
powerTile.destroyed = true;
powerTile.destroy();
}
}
});
hitTile = true;
break;
}
}
}
// Check regular tiles
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (tile.lane === laneIndex && !tile.missed && !tile.earlyPressed) {
var distance = Math.abs(tile.y - hitLineY);
if (distance <= 80) {
// Handle QuickTile
if (!tile.hit) {
tile.hit = true;
// Calculate combo multiplier
comboMultiplier = Math.min(4, Math.floor(comboCount / 5) + 1);
var baseScore = 10;
// Apply power-up bonuses
if (activePowerUps.doubleScore > 0) {
baseScore *= 2;
}
var scoreToAdd = baseScore * comboMultiplier;
// Apply skill effects
var skillEffects = applySkillEffects();
var perfectWindow = 20 + skillEffects.perfectWindowBonus;
// Check for perfect timing (with skill enhancement)
var perfectTiming = distance <= perfectWindow;
if (perfectTiming) {
scoreToAdd *= 1.5; // 1.5x bonus for perfect timing
perfectStreak++;
createParticles(tile.x, tile.y, 8, 0xFFD700);
// Screen flash for perfect streak milestones
if (perfectStreak % 10 === 0) {
LK.effects.flashScreen(0xFFD700, 200);
}
} else {
perfectStreak = 0;
createParticles(tile.x, tile.y, 5, 0x00FF00);
}
// Apply score multiplier skill bonus
scoreToAdd *= 1 + skillEffects.scoreBonus;
// Award experience and coins
var expGained = perfectTiming ? 3 : 2;
var coinsGained = perfectTiming ? 2 : 1;
awardExperience(expGained);
coins += coinsGained;
storage.coins = coins;
LK.setScore(LK.getScore() + Math.floor(scoreToAdd));
comboCount++;
lastHitTime = LK.ticks;
// Decrement early count on nice hit (minimum 0)
if (earlyCount > 0) {
earlyCount--;
}
showFeedback('NICE', laneIndex);
// Use random Piano1-Piano5 sounds for each tile hit
var pianoSounds = ['Piano1', 'Piano2', 'Piano3', 'Piano4', 'Piano5'];
var randomSound = pianoSounds[Math.floor(Math.random() * pianoSounds.length)];
LK.getSound(randomSound).play();
// Visual effect
tween(tile, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
if (!tile.destroyed) {
tile.destroyed = true;
tile.destroy();
}
}
});
updateScoreDisplay();
hitTile = true;
break;
}
} else if (tile.y < hitLineY - 80) {
// Early press
tile.earlyPressed = true;
comboCount = 0; // Reset combo on early press
comboMultiplier = 1; // Reset multiplier
perfectStreak = 0; // Reset perfect streak
earlyCount++; // Increment early count
showFeedback('EARLY', laneIndex);
LK.getSound('early').play();
LK.setScore(LK.getScore() - 30); // Deduct 30 points for early press
// Check if 3 earlies reached
if (earlyCount >= 3) {
LK.showGameOver();
}
// Destroy tile immediately on early press
tween(tile, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
if (!tile.destroyed) {
tile.destroyed = true;
tile.destroy();
}
}
});
updateScoreDisplay();
hitTile = true;
break;
}
}
}
};
game.up = function (x, y, obj) {
var laneIndex = getLaneFromX(x);
deactivateLane(laneIndex);
// Add lane release animation
tween(lanes[laneIndex], {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
};
// Start background music
LK.playMusic('Music');
game.update = function () {
// Update power-ups
for (var powerType in activePowerUps) {
if (activePowerUps[powerType] > 0) {
activePowerUps[powerType]--;
}
}
// Apply slow time effect
var currentGameSpeed = gameSpeed;
if (activePowerUps.slowTime > 0) {
currentGameSpeed *= 0.5;
}
// Spawn tiles
tileSpawnTimer++;
var adjustedSpawnInterval = tileSpawnInterval;
if (activePowerUps.slowTime > 0) {
adjustedSpawnInterval *= 2;
}
if (tileSpawnTimer >= adjustedSpawnInterval) {
spawnTile();
tileSpawnTimer = 0;
// Level progression system
var targetScore = currentLevel * 1000;
if (LK.getScore() >= targetScore) {
currentLevel++;
storage.currentLevel = currentLevel;
if (currentLevel > storage.unlockedLevels) {
storage.unlockedLevels = currentLevel;
}
showPowerUpNotification('Level Up! Level ' + currentLevel, 0xFF00FF);
LK.effects.flashScreen(0xFF00FF, 800);
}
// Increase difficulty over time
if (LK.ticks % 300 === 0) {
gameSpeed += 0.15;
tileSpawnInterval = Math.max(25, tileSpawnInterval - 1);
}
}
// Clean up tiles
for (var i = tiles.length - 1; i >= 0; i--) {
var tile = tiles[i];
if (!tile || tile.destroyed) {
tiles.splice(i, 1);
continue;
}
if (tile.y > 2800 || tile.hit || tile.missed && tile.y > hitLineY + 200 || tile.earlyPressed && tile.y > 2800) {
tile.destroyed = true;
tile.destroy();
tiles.splice(i, 1);
}
}
// Clean up power tiles
for (var i = powerTiles.length - 1; i >= 0; i--) {
var powerTile = powerTiles[i];
if (!powerTile || powerTile.destroyed) {
powerTiles.splice(i, 1);
continue;
}
if (powerTile.y > 2800 || powerTile.hit || powerTile.missed && powerTile.y > hitLineY + 200) {
powerTile.destroyed = true;
powerTile.destroy();
powerTiles.splice(i, 1);
}
}
// Apply skill effects
var skillEffects = applySkillEffects();
// Update tile speeds based on game speed and skills
var effectiveSpeed = gameSpeed * (1 - skillEffects.speedReduction);
if (activePowerUps.slowTime > 0) {
effectiveSpeed *= 0.5;
}
for (var i = 0; i < tiles.length; i++) {
if (tiles[i] && !tiles[i].destroyed) {
tiles[i].speed = 12 * effectiveSpeed;
}
}
// Update power tile speeds
for (var i = 0; i < powerTiles.length; i++) {
if (powerTiles[i] && !powerTiles[i].destroyed) {
powerTiles[i].speed = 12 * effectiveSpeed;
}
}
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (!particle || particle.life <= 0) {
if (particle) particle.destroy();
particles.splice(i, 1);
}
}
// Combo decay system with skill enhancement
var skillEffects = applySkillEffects();
var decayTime = Math.floor(180 * (1 + skillEffects.comboDecayReduction));
if (LK.ticks - lastHitTime > decayTime && comboCount > 0) {
comboCount = Math.max(0, comboCount - 1);
comboMultiplier = Math.min(4, Math.floor(comboCount / 5) + 1);
updateScoreDisplay();
lastHitTime = LK.ticks; // Reset timer to prevent rapid decay
}
// Check daily bonus (once per game)
if (LK.ticks === 60) {
checkDailyBonus();
}
};
// Override game over to save progress and unlock achievements
var originalShowGameOver = LK.showGameOver;
LK.showGameOver = function () {
// Save game stats
storage.totalGamesPlayed++;
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
// Unlock first win achievement if it's the first game
if (storage.totalGamesPlayed === 1 && !achievements.firstWin.unlocked) {
unlockAchievement('firstWin');
}
// Call original game over
originalShowGameOver();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
currentLevel: 1,
totalGamesPlayed: 0,
achievements: {},
unlockedLevels: 1
});
/****
* Classes
****/
var Particle = Container.expand(function (x, y, color) {
var self = Container.call(this);
var graphics = self.attachAsset('feedbackPerfect', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.tint = color || 0xFFD700;
graphics.width = 20;
graphics.height = 20;
self.x = x;
self.y = y;
self.vx = (Math.random() - 0.5) * 10;
self.vy = (Math.random() - 0.5) * 10 - 5;
self.life = 60;
self.maxLife = 60;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.vy += 0.2; // gravity
self.life--;
self.alpha = self.life / self.maxLife;
if (self.life <= 0) {
self.destroy();
}
};
return self;
});
var PowerTile = Container.expand(function (laneIndex, powerType) {
var self = Container.call(this);
var graphics = self.attachAsset('quickTile' + laneIndex, {
anchorX: 0.5,
anchorY: 0.5
});
// Tint based on power type
if (powerType === 'double') {
graphics.tint = 0xFFD700; // Gold for double score
} else if (powerType === 'slow') {
graphics.tint = 0x00FFFF; // Cyan for slow time
} else if (powerType === 'shield') {
graphics.tint = 0x00FF00; // Green for shield
}
self.speed = 12;
self.lane = laneIndex || 0;
self.powerType = powerType;
self.hit = false;
self.missed = false;
self.earlyPressed = false;
self.destroyed = false;
// Add pulsing effect
self.pulseTimer = 0;
self.update = function () {
if (self.destroyed) return;
self.y += self.speed;
// Pulsing effect
self.pulseTimer++;
graphics.scaleX = 1 + Math.sin(self.pulseTimer * 0.2) * 0.1;
graphics.scaleY = 1 + Math.sin(self.pulseTimer * 0.2) * 0.1;
// Check if tile is past hit line and not hit
if (self.y > hitLineY + 80 && !self.hit && !self.missed) {
self.missed = true;
comboCount = 0;
comboMultiplier = 1;
perfectStreak = 0;
showFeedback('MISSED', self.lane);
LK.getSound('miss').play();
LK.showGameOver();
}
};
return self;
});
var QuickTile = Container.expand(function (laneIndex) {
var self = Container.call(this);
var assetName = 'quickTile' + laneIndex;
var graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.lane = laneIndex || 0;
self.hit = false;
self.missed = false;
self.earlyPressed = false;
self.destroyed = false;
self.update = function () {
if (self.destroyed) return;
self.y += self.speed;
// Check if tile is past hit line and not hit
if (self.y > hitLineY + 80 && !self.hit && !self.missed) {
self.missed = true;
comboCount = 0; // Reset combo on miss
comboMultiplier = 1; // Reset multiplier
perfectStreak = 0; // Reset perfect streak
showFeedback('MISSED', self.lane);
LK.getSound('miss').play();
LK.showGameOver();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var lanes = [];
var hitLineY = 2200;
var tiles = [];
var activeLanes = [false, false, false, false];
var comboCount = 0;
var tileSpawnTimer = 0;
var feedbackTexts = [];
var tileSpawnInterval = 45;
var gameSpeed = 1;
var earlyCount = 0;
var keyPressCount = 0;
var particles = [];
var comboMultiplier = 1;
var perfectStreak = 0;
var lastHitTime = 0;
var currentLevel = storage.currentLevel || 1;
var powerTiles = [];
var activePowerUps = {
doubleScore: 0,
slowTime: 0,
shield: 0
};
// Experience and progression system
var playerExp = storage.playerExp || 0;
var playerLevel = storage.playerLevel || 1;
var skillPoints = storage.skillPoints || 0;
var unlockedSkills = storage.unlockedSkills || {};
// Skill tree definitions with meaningful effects
var skills = {
speedBoost: {
name: "Speed Boost",
desc: "Tiles move 10% slower",
cost: 2,
maxLevel: 3,
effect: function effect(level) {
return 0.1 * level;
}
},
scoreMultiplier: {
name: "Score Master",
desc: "Base score +25% per level",
cost: 3,
maxLevel: 4,
effect: function effect(level) {
return 0.25 * level;
}
},
comboExtender: {
name: "Combo Keeper",
desc: "Combo decays 50% slower per level",
cost: 2,
maxLevel: 2,
effect: function effect(level) {
return 0.5 * level;
}
},
perfectWindow: {
name: "Perfect Timing",
desc: "Perfect hit window +10 pixels per level",
cost: 4,
maxLevel: 2,
effect: function effect(level) {
return 10 * level;
}
},
powerUpDuration: {
name: "Power Mastery",
desc: "Power-ups last 50% longer per level",
cost: 3,
maxLevel: 3,
effect: function effect(level) {
return 0.5 * level;
}
}
};
// Level rewards system
var levelRewards = {
2: {
skillPoints: 1,
message: "Skill Point Earned!"
},
3: {
skillPoints: 1,
coins: 50,
message: "Skill Point + 50 Coins!"
},
5: {
skillPoints: 2,
coins: 100,
message: "2 Skill Points + 100 Coins!"
},
7: {
skillPoints: 1,
coins: 75,
message: "Skill Point + 75 Coins!"
},
10: {
skillPoints: 3,
coins: 200,
message: "3 Skill Points + 200 Coins!"
}
};
// Currency system
var coins = storage.coins || 0;
var dailyBonusCollected = storage.dailyBonusCollected || 0;
// Achievement definitions with meaningful rewards
var achievements = {
firstWin: {
name: "First Steps",
desc: "Complete your first game",
unlocked: false,
reward: {
coins: 25,
skillPoints: 1
}
},
perfectionist: {
name: "Perfectionist",
desc: "Get 50 perfect hits in a row",
unlocked: false,
reward: {
coins: 150,
skillPoints: 2
}
},
speedDemon: {
name: "Speed Demon",
desc: "Reach level 5",
unlocked: false,
reward: {
coins: 100,
skillPoints: 1
}
},
comboMaster: {
name: "Combo Master",
desc: "Achieve a 100+ combo",
unlocked: false,
reward: {
coins: 200,
skillPoints: 2
}
},
highScorer: {
name: "High Scorer",
desc: "Score 10,000 points",
unlocked: false,
reward: {
coins: 300,
skillPoints: 3
}
},
coinCollector: {
name: "Coin Collector",
desc: "Collect 500 coins total",
unlocked: false,
reward: {
coins: 100,
skillPoints: 1
}
},
marathoner: {
name: "Marathoner",
desc: "Play 25 games",
unlocked: false,
reward: {
coins: 150,
skillPoints: 2
}
}
};
// Load saved achievements
for (var key in achievements) {
if (storage.achievements && storage.achievements[key]) {
achievements[key].unlocked = true;
}
}
// Load saved skills
if (storage.unlockedSkills) {
for (var skillKey in storage.unlockedSkills) {
unlockedSkills[skillKey] = storage.unlockedSkills[skillKey];
}
}
// Create lanes
for (var i = 0; i < 4; i++) {
var lane = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
}));
lane.x = 256 + i * 512;
lane.y = 1366;
lanes.push(lane);
}
// Create hit line
var hitLine = game.addChild(LK.getAsset('hitLine', {
anchorX: 0.5,
anchorY: 0.5
}));
hitLine.x = 1024;
hitLine.y = hitLineY;
// Create lane dividers
for (var i = 1; i < 4; i++) {
var divider = game.addChild(LK.getAsset('laneDivider', {
anchorX: 0.5,
anchorY: 0.5
}));
divider.x = i * 512;
divider.y = 1366;
}
// Create UI
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var comboText = new Text2('Combo: 0', {
size: 60,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0);
comboText.y = 100;
LK.gui.top.addChild(comboText);
var earlyText = new Text2('Early: 0/3', {
size: 60,
fill: 0xFF4444
});
earlyText.anchor.set(0.5, 0);
earlyText.y = 160;
LK.gui.top.addChild(earlyText);
var streakText = new Text2('Streak: 0', {
size: 50,
fill: 0x00FFFF
});
streakText.anchor.set(0.5, 0);
streakText.y = 220;
LK.gui.top.addChild(streakText);
var multiplierText = new Text2('x1', {
size: 80,
fill: 0xFFD700
});
multiplierText.anchor.set(1, 0);
multiplierText.x = -20;
multiplierText.y = 20;
LK.gui.topRight.addChild(multiplierText);
var levelText = new Text2('Level: ' + currentLevel, {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
levelText.x = 20;
levelText.y = 20;
LK.gui.topLeft.addChild(levelText);
var highScoreText = new Text2('Best: ' + storage.highScore, {
size: 40,
fill: 0xFFD700
});
highScoreText.anchor.set(0, 0);
highScoreText.x = 20;
highScoreText.y = 80;
LK.gui.topLeft.addChild(highScoreText);
// Add progression UI elements
var expText = new Text2('EXP: ' + playerExp, {
size: 35,
fill: 0x00FFFF
});
expText.anchor.set(0, 0);
expText.x = 20;
expText.y = 120;
LK.gui.topLeft.addChild(expText);
var playerLevelText = new Text2('Player Lv: ' + playerLevel, {
size: 35,
fill: 0xFFD700
});
playerLevelText.anchor.set(0, 0);
playerLevelText.x = 20;
playerLevelText.y = 160;
LK.gui.topLeft.addChild(playerLevelText);
var coinsText = new Text2('Coins: ' + coins, {
size: 40,
fill: 0xFFD700
});
coinsText.anchor.set(1, 0);
coinsText.x = -20;
coinsText.y = 80;
LK.gui.topRight.addChild(coinsText);
var skillPointsText = new Text2('SP: ' + skillPoints, {
size: 35,
fill: 0xFF00FF
});
skillPointsText.anchor.set(1, 0);
skillPointsText.x = -20;
skillPointsText.y = 120;
LK.gui.topRight.addChild(skillPointsText);
// Update all UI on start
updateScoreDisplay();
function updateScoreDisplay() {
scoreText.setText('Score: ' + LK.getScore());
comboText.setText('Combo: ' + comboCount);
earlyText.setText('Early: ' + earlyCount + '/3');
streakText.setText('Streak: ' + perfectStreak);
multiplierText.setText('x' + comboMultiplier);
levelText.setText('Level: ' + currentLevel);
highScoreText.setText('Best: ' + storage.highScore);
expText.setText('EXP: ' + playerExp);
playerLevelText.setText('Player Lv: ' + playerLevel);
coinsText.setText('Coins: ' + coins);
skillPointsText.setText('SP: ' + skillPoints);
// Update multiplier color based on value
if (comboMultiplier >= 4) {
multiplierText.fill = 0xFF0000; // Red for 4x+
} else if (comboMultiplier >= 3) {
multiplierText.fill = 0xFF8800; // Orange for 3x
} else if (comboMultiplier >= 2) {
multiplierText.fill = 0xFFD700; // Gold for 2x
} else {
multiplierText.fill = 0xFFFFFF; // White for 1x
}
// Check achievements
checkAchievements();
}
function showFeedback(text, laneIndex) {
var assetName = 'feedback' + text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
var feedbackImage = game.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
}));
feedbackImage.x = 1024; // Center of screen
feedbackImage.y = 1366; // Center of screen vertically
feedbackImage.alpha = 1;
feedbackTexts.push(feedbackImage);
// Add score display based on feedback type
var scoreDisplay;
if (text === 'NICE') {
scoreDisplay = game.addChild(LK.getAsset('positiveScoreDisplay', {
anchorX: 0.5,
anchorY: 0.5
}));
} else {
scoreDisplay = game.addChild(LK.getAsset('negativeScoreDisplay', {
anchorX: 0.5,
anchorY: 0.5
}));
}
scoreDisplay.x = 1024; // Center of screen
scoreDisplay.y = 1450; // Below feedback
scoreDisplay.alpha = 1;
feedbackTexts.push(scoreDisplay);
// Create score text overlay
var scoreChangeText = new Text2('', {
size: 60,
fill: text === 'NICE' ? 0x00FF00 : 0xFF0000
});
scoreChangeText.anchor.set(0.5, 0.5);
scoreChangeText.x = scoreDisplay.x;
scoreChangeText.y = scoreDisplay.y;
if (text === 'NICE') {
scoreChangeText.setText('+10');
} else if (text === 'EARLY') {
scoreChangeText.setText('-30');
} else if (text === 'MISSED') {
scoreChangeText.setText('0');
}
game.addChild(scoreChangeText);
feedbackTexts.push(scoreChangeText);
tween(feedbackImage, {
alpha: 0,
y: feedbackImage.y - 100
}, {
duration: 1000,
onFinish: function onFinish() {
feedbackImage.destroy();
var index = feedbackTexts.indexOf(feedbackImage);
if (index > -1) feedbackTexts.splice(index, 1);
}
});
tween(scoreDisplay, {
alpha: 0,
y: scoreDisplay.y - 100
}, {
duration: 1000,
onFinish: function onFinish() {
scoreDisplay.destroy();
var index = feedbackTexts.indexOf(scoreDisplay);
if (index > -1) feedbackTexts.splice(index, 1);
}
});
tween(scoreChangeText, {
alpha: 0,
y: scoreChangeText.y - 100
}, {
duration: 1000,
onFinish: function onFinish() {
scoreChangeText.destroy();
var index = feedbackTexts.indexOf(scoreChangeText);
if (index > -1) feedbackTexts.splice(index, 1);
}
});
}
function spawnTile() {
// 5% chance to spawn power tile (increases with level)
var powerChance = 0.05 + currentLevel * 0.01;
if (Math.random() < powerChance) {
spawnPowerTile();
return;
}
// Adjusted spawn chances based on level
var multiTileChance = 0.3 + currentLevel * 0.05; // More multi-tiles at higher levels
var spawnTwo = Math.random() < multiTileChance;
if (spawnTwo) {
// Spawn two tiles in different lanes
var laneIndex1 = Math.floor(Math.random() * 4);
var laneIndex2;
// Make sure second lane is different from first
do {
laneIndex2 = Math.floor(Math.random() * 4);
} while (laneIndex2 === laneIndex1);
// Create first tile
var tile1 = new QuickTile(laneIndex1);
tile1.x = 256 + laneIndex1 * 512;
tile1.y = -100;
tiles.push(tile1);
game.addChild(tile1);
// Create second tile
var tile2 = new QuickTile(laneIndex2);
tile2.x = 256 + laneIndex2 * 512;
tile2.y = -100;
tiles.push(tile2);
game.addChild(tile2);
} else {
// Spawn single tile
var laneIndex = Math.floor(Math.random() * 4);
var tile = new QuickTile(laneIndex);
tile.x = 256 + laneIndex * 512;
tile.y = -100;
tiles.push(tile);
game.addChild(tile);
}
}
function createParticles(x, y, count, color) {
for (var i = 0; i < count; i++) {
var particle = new Particle(x, y, color);
particles.push(particle);
game.addChild(particle);
}
}
function checkAchievements() {
var currentScore = LK.getScore();
// High Scorer achievement
if (currentScore >= 10000 && !achievements.highScorer.unlocked) {
unlockAchievement('highScorer');
}
// Perfectionist achievement
if (perfectStreak >= 50 && !achievements.perfectionist.unlocked) {
unlockAchievement('perfectionist');
}
// Combo Master achievement
if (comboCount >= 100 && !achievements.comboMaster.unlocked) {
unlockAchievement('comboMaster');
}
// Speed Demon achievement
if (currentLevel >= 5 && !achievements.speedDemon.unlocked) {
unlockAchievement('speedDemon');
}
// Coin Collector achievement
if (coins >= 500 && !achievements.coinCollector.unlocked) {
unlockAchievement('coinCollector');
}
// Marathoner achievement
if (storage.totalGamesPlayed >= 25 && !achievements.marathoner.unlocked) {
unlockAchievement('marathoner');
}
// Update high score
if (currentScore > storage.highScore) {
storage.highScore = currentScore;
}
}
function awardExperience(amount) {
var oldLevel = playerLevel;
playerExp += amount;
storage.playerExp = playerExp;
// Level up calculation (exponential growth)
var expNeeded = playerLevel * 100 + Math.pow(playerLevel, 2) * 50;
if (playerExp >= expNeeded) {
playerLevel++;
storage.playerLevel = playerLevel;
playerExp = 0;
storage.playerExp = 0;
// Award skill points and coins for level up
var skillPointsAwarded = Math.floor(playerLevel / 3) + 1;
var coinsAwarded = playerLevel * 25;
skillPoints += skillPointsAwarded;
coins += coinsAwarded;
storage.skillPoints = skillPoints;
storage.coins = coins;
// Check for level rewards
if (levelRewards[playerLevel]) {
var reward = levelRewards[playerLevel];
if (reward.skillPoints) {
skillPoints += reward.skillPoints;
storage.skillPoints = skillPoints;
}
if (reward.coins) {
coins += reward.coins;
storage.coins = coins;
}
showLevelUpNotification(playerLevel, reward.message);
} else {
showLevelUpNotification(playerLevel, skillPointsAwarded + ' SP + ' + coinsAwarded + ' Coins!');
}
}
updateScoreDisplay();
}
function showLevelUpNotification(level, rewardText) {
var levelUpText = new Text2('PLAYER LEVEL UP!\nLevel ' + level + '\n' + rewardText, {
size: 60,
fill: 0xFF00FF
});
levelUpText.anchor.set(0.5, 0.5);
levelUpText.x = 1024;
levelUpText.y = 900;
game.addChild(levelUpText);
LK.effects.flashScreen(0xFF00FF, 1000);
tween(levelUpText, {
alpha: 0,
y: levelUpText.y - 150
}, {
duration: 3000,
onFinish: function onFinish() {
levelUpText.destroy();
}
});
}
function getSkillLevel(skillName) {
return unlockedSkills[skillName] || 0;
}
function applySkillEffects() {
// Apply skill effects to game mechanics
var speedBoostLevel = getSkillLevel('speedBoost');
var scoreMultLevel = getSkillLevel('scoreMultiplier');
var comboExtLevel = getSkillLevel('comboExtender');
var perfectWindowLevel = getSkillLevel('perfectWindow');
var powerDurationLevel = getSkillLevel('powerUpDuration');
return {
speedReduction: speedBoostLevel > 0 ? skills.speedBoost.effect(speedBoostLevel) : 0,
scoreBonus: scoreMultLevel > 0 ? skills.scoreMultiplier.effect(scoreMultLevel) : 0,
comboDecayReduction: comboExtLevel > 0 ? skills.comboExtender.effect(comboExtLevel) : 0,
perfectWindowBonus: perfectWindowLevel > 0 ? skills.perfectWindow.effect(perfectWindowLevel) : 0,
powerUpBonus: powerDurationLevel > 0 ? skills.powerUpDuration.effect(powerDurationLevel) : 0
};
}
function checkDailyBonus() {
var today = Math.floor(Date.now() / 86400000); // Days since epoch
if (dailyBonusCollected < today) {
// Award daily bonus
var dailyCoins = 50 + playerLevel * 10;
coins += dailyCoins;
storage.coins = coins;
dailyBonusCollected = today;
storage.dailyBonusCollected = today;
showPowerUpNotification('Daily Bonus: ' + dailyCoins + ' Coins!', 0xFFD700);
updateScoreDisplay();
}
}
function unlockAchievement(achievementKey) {
achievements[achievementKey].unlocked = true;
if (!storage.achievements) storage.achievements = {};
storage.achievements[achievementKey] = true;
// Award achievement rewards
var achievement = achievements[achievementKey];
if (achievement.reward) {
if (achievement.reward.coins) {
coins += achievement.reward.coins;
storage.coins = coins;
}
if (achievement.reward.skillPoints) {
skillPoints += achievement.reward.skillPoints;
storage.skillPoints = skillPoints;
}
}
// Show achievement notification
showAchievementNotification(achievement);
LK.effects.flashScreen(0xFFD700, 500);
updateScoreDisplay();
}
function showAchievementNotification(achievement) {
var rewardText = '';
if (achievement.reward) {
var rewards = [];
if (achievement.reward.coins) rewards.push(achievement.reward.coins + ' Coins');
if (achievement.reward.skillPoints) rewards.push(achievement.reward.skillPoints + ' SP');
if (rewards.length > 0) rewardText = '\nReward: ' + rewards.join(' + ');
}
var achievementText = new Text2('Achievement Unlocked!\n' + achievement.name + rewardText, {
size: 60,
fill: 0xFFD700
});
achievementText.anchor.set(0.5, 0.5);
achievementText.x = 1024;
achievementText.y = 800;
game.addChild(achievementText);
tween(achievementText, {
alpha: 0,
y: achievementText.y - 100
}, {
duration: 3000,
onFinish: function onFinish() {
achievementText.destroy();
}
});
}
function spawnPowerTile() {
var powerTypes = ['double', 'slow', 'shield'];
var powerType = powerTypes[Math.floor(Math.random() * powerTypes.length)];
var laneIndex = Math.floor(Math.random() * 4);
var powerTile = new PowerTile(laneIndex, powerType);
powerTile.x = 256 + laneIndex * 512;
powerTile.y = -100;
powerTiles.push(powerTile);
game.addChild(powerTile);
}
function activatePowerUp(powerType) {
var skillEffects = applySkillEffects();
var baseDuration = 600; // 10 seconds at 60fps
var enhancedDuration = Math.floor(baseDuration * (1 + skillEffects.powerUpBonus));
if (powerType === 'double') {
activePowerUps.doubleScore = enhancedDuration;
showPowerUpNotification('Double Score!', 0xFFD700);
} else if (powerType === 'slow') {
activePowerUps.slowTime = enhancedDuration;
showPowerUpNotification('Slow Time!', 0x00FFFF);
} else if (powerType === 'shield') {
activePowerUps.shield = enhancedDuration;
showPowerUpNotification('Shield Active!', 0x00FF00);
}
}
function showPowerUpNotification(text, color) {
var powerText = new Text2(text, {
size: 80,
fill: color
});
powerText.anchor.set(0.5, 0.5);
powerText.x = 1024;
powerText.y = 1000;
game.addChild(powerText);
tween(powerText, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: powerText.y - 100
}, {
duration: 2000,
onFinish: function onFinish() {
powerText.destroy();
}
});
}
function getLaneFromX(x) {
if (x < 512) return 0;
if (x < 1024) return 1;
if (x < 1536) return 2;
return 3;
}
function activateLane(laneIndex) {
if (laneIndex >= 0 && laneIndex < 4) {
activeLanes[laneIndex] = true;
lanes[laneIndex].removeChild(lanes[laneIndex].children[0]);
var activeLane = LK.getAsset('laneActive', {
anchorX: 0.5,
anchorY: 0.5
});
lanes[laneIndex].addChild(activeLane);
}
}
function deactivateLane(laneIndex) {
if (laneIndex >= 0 && laneIndex < 4) {
activeLanes[laneIndex] = false;
lanes[laneIndex].removeChild(lanes[laneIndex].children[0]);
var normalLane = LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
});
lanes[laneIndex].addChild(normalLane);
}
}
game.down = function (x, y, obj) {
var laneIndex = getLaneFromX(x);
activateLane(laneIndex);
// Increment key press counter
keyPressCount++;
// Increase game speed by 2% every 5 key presses
if (keyPressCount % 5 === 0) {
gameSpeed *= 1.02;
}
// Add lane press animation
tween(lanes[laneIndex], {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 100,
easing: tween.easeOut
});
// Check for tile hits
var hitTile = false;
// Check power tiles first
for (var i = 0; i < powerTiles.length; i++) {
var powerTile = powerTiles[i];
if (powerTile.lane === laneIndex && !powerTile.missed && !powerTile.earlyPressed) {
var distance = Math.abs(powerTile.y - hitLineY);
if (distance <= 80 && !powerTile.hit) {
powerTile.hit = true;
activatePowerUp(powerTile.powerType);
createParticles(powerTile.x, powerTile.y, 12, 0xFFD700);
tween(powerTile, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (!powerTile.destroyed) {
powerTile.destroyed = true;
powerTile.destroy();
}
}
});
hitTile = true;
break;
}
}
}
// Check regular tiles
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (tile.lane === laneIndex && !tile.missed && !tile.earlyPressed) {
var distance = Math.abs(tile.y - hitLineY);
if (distance <= 80) {
// Handle QuickTile
if (!tile.hit) {
tile.hit = true;
// Calculate combo multiplier
comboMultiplier = Math.min(4, Math.floor(comboCount / 5) + 1);
var baseScore = 10;
// Apply power-up bonuses
if (activePowerUps.doubleScore > 0) {
baseScore *= 2;
}
var scoreToAdd = baseScore * comboMultiplier;
// Apply skill effects
var skillEffects = applySkillEffects();
var perfectWindow = 20 + skillEffects.perfectWindowBonus;
// Check for perfect timing (with skill enhancement)
var perfectTiming = distance <= perfectWindow;
if (perfectTiming) {
scoreToAdd *= 1.5; // 1.5x bonus for perfect timing
perfectStreak++;
createParticles(tile.x, tile.y, 8, 0xFFD700);
// Screen flash for perfect streak milestones
if (perfectStreak % 10 === 0) {
LK.effects.flashScreen(0xFFD700, 200);
}
} else {
perfectStreak = 0;
createParticles(tile.x, tile.y, 5, 0x00FF00);
}
// Apply score multiplier skill bonus
scoreToAdd *= 1 + skillEffects.scoreBonus;
// Award experience and coins
var expGained = perfectTiming ? 3 : 2;
var coinsGained = perfectTiming ? 2 : 1;
awardExperience(expGained);
coins += coinsGained;
storage.coins = coins;
LK.setScore(LK.getScore() + Math.floor(scoreToAdd));
comboCount++;
lastHitTime = LK.ticks;
// Decrement early count on nice hit (minimum 0)
if (earlyCount > 0) {
earlyCount--;
}
showFeedback('NICE', laneIndex);
// Use random Piano1-Piano5 sounds for each tile hit
var pianoSounds = ['Piano1', 'Piano2', 'Piano3', 'Piano4', 'Piano5'];
var randomSound = pianoSounds[Math.floor(Math.random() * pianoSounds.length)];
LK.getSound(randomSound).play();
// Visual effect
tween(tile, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
if (!tile.destroyed) {
tile.destroyed = true;
tile.destroy();
}
}
});
updateScoreDisplay();
hitTile = true;
break;
}
} else if (tile.y < hitLineY - 80) {
// Early press
tile.earlyPressed = true;
comboCount = 0; // Reset combo on early press
comboMultiplier = 1; // Reset multiplier
perfectStreak = 0; // Reset perfect streak
earlyCount++; // Increment early count
showFeedback('EARLY', laneIndex);
LK.getSound('early').play();
LK.setScore(LK.getScore() - 30); // Deduct 30 points for early press
// Check if 3 earlies reached
if (earlyCount >= 3) {
LK.showGameOver();
}
// Destroy tile immediately on early press
tween(tile, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
if (!tile.destroyed) {
tile.destroyed = true;
tile.destroy();
}
}
});
updateScoreDisplay();
hitTile = true;
break;
}
}
}
};
game.up = function (x, y, obj) {
var laneIndex = getLaneFromX(x);
deactivateLane(laneIndex);
// Add lane release animation
tween(lanes[laneIndex], {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
};
// Start background music
LK.playMusic('Music');
game.update = function () {
// Update power-ups
for (var powerType in activePowerUps) {
if (activePowerUps[powerType] > 0) {
activePowerUps[powerType]--;
}
}
// Apply slow time effect
var currentGameSpeed = gameSpeed;
if (activePowerUps.slowTime > 0) {
currentGameSpeed *= 0.5;
}
// Spawn tiles
tileSpawnTimer++;
var adjustedSpawnInterval = tileSpawnInterval;
if (activePowerUps.slowTime > 0) {
adjustedSpawnInterval *= 2;
}
if (tileSpawnTimer >= adjustedSpawnInterval) {
spawnTile();
tileSpawnTimer = 0;
// Level progression system
var targetScore = currentLevel * 1000;
if (LK.getScore() >= targetScore) {
currentLevel++;
storage.currentLevel = currentLevel;
if (currentLevel > storage.unlockedLevels) {
storage.unlockedLevels = currentLevel;
}
showPowerUpNotification('Level Up! Level ' + currentLevel, 0xFF00FF);
LK.effects.flashScreen(0xFF00FF, 800);
}
// Increase difficulty over time
if (LK.ticks % 300 === 0) {
gameSpeed += 0.15;
tileSpawnInterval = Math.max(25, tileSpawnInterval - 1);
}
}
// Clean up tiles
for (var i = tiles.length - 1; i >= 0; i--) {
var tile = tiles[i];
if (!tile || tile.destroyed) {
tiles.splice(i, 1);
continue;
}
if (tile.y > 2800 || tile.hit || tile.missed && tile.y > hitLineY + 200 || tile.earlyPressed && tile.y > 2800) {
tile.destroyed = true;
tile.destroy();
tiles.splice(i, 1);
}
}
// Clean up power tiles
for (var i = powerTiles.length - 1; i >= 0; i--) {
var powerTile = powerTiles[i];
if (!powerTile || powerTile.destroyed) {
powerTiles.splice(i, 1);
continue;
}
if (powerTile.y > 2800 || powerTile.hit || powerTile.missed && powerTile.y > hitLineY + 200) {
powerTile.destroyed = true;
powerTile.destroy();
powerTiles.splice(i, 1);
}
}
// Apply skill effects
var skillEffects = applySkillEffects();
// Update tile speeds based on game speed and skills
var effectiveSpeed = gameSpeed * (1 - skillEffects.speedReduction);
if (activePowerUps.slowTime > 0) {
effectiveSpeed *= 0.5;
}
for (var i = 0; i < tiles.length; i++) {
if (tiles[i] && !tiles[i].destroyed) {
tiles[i].speed = 12 * effectiveSpeed;
}
}
// Update power tile speeds
for (var i = 0; i < powerTiles.length; i++) {
if (powerTiles[i] && !powerTiles[i].destroyed) {
powerTiles[i].speed = 12 * effectiveSpeed;
}
}
// Update particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (!particle || particle.life <= 0) {
if (particle) particle.destroy();
particles.splice(i, 1);
}
}
// Combo decay system with skill enhancement
var skillEffects = applySkillEffects();
var decayTime = Math.floor(180 * (1 + skillEffects.comboDecayReduction));
if (LK.ticks - lastHitTime > decayTime && comboCount > 0) {
comboCount = Math.max(0, comboCount - 1);
comboMultiplier = Math.min(4, Math.floor(comboCount / 5) + 1);
updateScoreDisplay();
lastHitTime = LK.ticks; // Reset timer to prevent rapid decay
}
// Check daily bonus (once per game)
if (LK.ticks === 60) {
checkDailyBonus();
}
};
// Override game over to save progress and unlock achievements
var originalShowGameOver = LK.showGameOver;
LK.showGameOver = function () {
// Save game stats
storage.totalGamesPlayed++;
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
// Unlock first win achievement if it's the first game
if (storage.totalGamesPlayed === 1 && !achievements.firstWin.unlocked) {
unlockAchievement('firstWin');
}
// Call original game over
originalShowGameOver();
};
A line. In-Game asset. 2d. High contrast. No shadows
A "single" tile of musical note 🎵. In-Game asset. 2d. High contrast. No shadows
A single tile of musical note 🎵 blue. In-Game asset. 2d. High contrast. No shadows
A single tile of musical note green. In-Game asset. 2d. High contrast. No shadows
A single musical tile red. In-Game asset. 2d. High contrast. No shadows
An UI written "NICE". In-Game asset. 2d. High contrast. No shadows
An UI written "Missed". In-Game asset. 2d. High contrast. No shadows
An UI written "Early". In-Game asset. 2d. High contrast. No shadows
A score display written +10 on it. In-Game asset. 2d. High contrast. No shadows
A score Ui that written -30 on it. In-Game asset. 2d. High contrast. No shadows
An image of "Perfect" Written on it. In-Game asset. 2d. High contrast. No shadows