/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
masterVolume: 0.7,
musicVolume: 0.8,
sfxVolume: 0.8,
graphicsQuality: "high",
playerName: "Player",
currentRank: "noob I",
lifetimeScore: 0,
lastSessionScore: 0
});
/****
* Classes
****/
var Arrow = Container.expand(function (direction, column) {
var self = Container.call(this);
var arrowGraphic = self.attachAsset('arrow_' + direction, {
anchorX: 0.5,
anchorY: 0.5
});
self.direction = direction;
self.column = column;
// Calculate dynamic speed based on current level and speed multiplier
var currentSpeed = baseSpeed * speedMultiplier;
self.speed = Math.min(currentSpeed, baseSpeed * maxSpeedMultiplier); // Apply speed cap
self.hitTested = false;
self.animationPhase = Math.random() * Math.PI * 2; // Random phase for animations
self.pulseSpeed = 0.8 + Math.random() * 0.4; // Random pulse speed
self.glowIntensity = 0.3 + Math.random() * 0.7; // Random glow intensity
self.sparkleTimer = Math.random() * 60; // Random sparkle timing
// Add initial entrance animation
arrowGraphic.scaleX = 0.1;
arrowGraphic.scaleY = 0.1;
arrowGraphic.alpha = 0.5;
tween(arrowGraphic, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300,
easing: tween.bounceOut
});
self.update = function () {
self.y += self.speed;
// Add dynamic pulsing animation
self.animationPhase += self.pulseSpeed * 0.1;
arrowGraphic.scaleX = 1 + 0.1 * Math.sin(self.animationPhase);
arrowGraphic.scaleY = 1 + 0.1 * Math.sin(self.animationPhase + 1);
// Add rotation wobble
arrowGraphic.rotation = 0.1 * Math.sin(self.animationPhase * 0.7);
// Add glow effect
arrowGraphic.alpha = 0.8 + 0.2 * Math.sin(self.animationPhase * 1.5);
// Add sparkle effect periodically
self.sparkleTimer++;
if (self.sparkleTimer >= 30) {
self.sparkleTimer = 0;
var sparkle = LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
});
sparkle.x = self.x + (Math.random() - 0.5) * 60;
sparkle.y = self.y + (Math.random() - 0.5) * 60;
sparkle.tint = self.direction === 'left' ? 0xff4757 : self.direction === 'down' ? 0x5352ed : self.direction === 'up' ? 0x2ed573 : 0xffa502;
game.addChild(sparkle);
tween(sparkle, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0,
y: sparkle.y - 50
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
sparkle.destroy();
}
});
}
};
return self;
});
var ComboEffect = Container.expand(function (x, y, comboValue) {
var self = Container.call(this);
// Create combo burst effect
var comboText = new Text2(comboValue + 'x COMBO!', {
size: 80,
fill: 0xFFD700
});
comboText.anchor.set(0.5, 0.5);
comboText.x = 0;
comboText.y = 0;
comboText.scaleX = 0.1;
comboText.scaleY = 0.1;
comboText.alpha = 1;
self.addChild(comboText);
// Create sparkle effects around combo text
for (var i = 0; i < 8; i++) {
var sparkle = self.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
}));
var angle = i / 8 * Math.PI * 2;
var radius = 80;
sparkle.x = Math.cos(angle) * radius;
sparkle.y = Math.sin(angle) * radius;
sparkle.tint = 0xFFD700;
// Animate sparkles
tween(sparkle, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0,
x: Math.cos(angle) * (radius + 100),
y: Math.sin(angle) * (radius + 100)
}, {
duration: 1000,
easing: tween.easeOut
});
}
self.x = x;
self.y = y;
// Animate combo text
tween(comboText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboText, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0,
y: -50
}, {
duration: 700,
easing: tween.easeOut
});
}
});
// Destroy after animation
LK.setTimeout(function () {
self.destroy();
}, 1200);
return self;
});
var HitEffect = Container.expand(function (x, y) {
var self = Container.call(this);
var effectGraphic = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8
});
self.x = x;
self.y = y;
// Animate the effect
tween(effectGraphic, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
var MissEffect = Container.expand(function (x, y) {
var self = Container.call(this);
// Create multiple miss text elements that animate outward
var missTexts = [];
var directions = [{
x: -1,
y: -1
}, {
x: 1,
y: -1
}, {
x: -1,
y: 1
}, {
x: 1,
y: 1
}, {
x: 0,
y: -1
}, {
x: 0,
y: 1
}, {
x: -1,
y: 0
}, {
x: 1,
y: 0
}];
for (var i = 0; i < 4; i++) {
var missText = new Text2('MISS', {
size: 60 + Math.random() * 40,
fill: 0xFF3333
});
missText.anchor.set(0.5, 0.5);
missText.x = 0;
missText.y = 0;
missText.alpha = 0.9;
self.addChild(missText);
missTexts.push(missText);
}
self.x = x;
self.y = y;
// Animate miss texts flying outward
for (var i = 0; i < missTexts.length; i++) {
var text = missTexts[i];
var dir = directions[i % directions.length];
var distance = 150 + Math.random() * 100;
tween(text, {
x: dir.x * distance,
y: dir.y * distance,
alpha: 0,
rotation: (Math.random() - 0.5) * Math.PI,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 800,
easing: tween.easeOut
});
}
// Destroy after animation
LK.setTimeout(function () {
self.destroy();
}, 900);
return self;
});
var RankEffect = Container.expand(function (rankData, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.rankData = rankData;
self.effectTimer = 0;
self.particles = [];
// Create effects based on rank tier
self.createEffects = function () {
var rankName = rankData.name.toLowerCase();
if (rankName.includes('noob')) {
// Noob: Simple subtle glow
self.createBasicGlow();
} else if (rankName.includes('pro')) {
// Pro: Green energy particles
self.createEnergyParticles(0x00ff00, 3);
} else if (rankName.includes('dios')) {
// Dios: Golden aura with sparkles
self.createAura(0xffd700);
self.createSparkles(0xffd700, 5);
} else if (rankName.includes('hacker')) {
// Hacker: Digital glitch effects
self.createGlitchEffect(0xff00ff);
self.createDataStreams(0xff00ff);
} else if (rankName.includes('imparable')) {
// Imparable: Cyan lightning bolts
self.createLightning(0x00ffff);
self.createEnergyField(0x00ffff);
} else if (rankName.includes('leyenda')) {
// Leyenda: Star constellation effect
self.createStarConstellation(0xff8800);
self.createCosmicDust(0xff8800);
} else if (rankName.includes('supremo')) {
// Supremo: Enhanced rainbow prismatic effect with multiple layers
self.createPrismaticEffect();
self.createEnergyWaves(0xff0088);
self.createSupremoAura();
self.createRainbowBurst();
self.createPowerRings();
} else if (rankName.includes('infinito')) {
// Infinito: Basic transcendent effects without infinity symbols
self.createDimensionalRift();
self.createCosmicStorm();
self.createEternityField();
}
};
self.createBasicGlow = function () {
var glow = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.2
});
glow.tint = rankData.color;
tween(glow, {
scaleX: 0.7,
scaleY: 0.7,
alpha: 0.1
}, {
duration: 2000,
easing: tween.easeInOut
});
};
self.createEnergyParticles = function (color, count) {
for (var i = 0; i < count; i++) {
LK.setTimeout(function () {
var particle = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
});
particle.tint = color;
var angle = Math.random() * Math.PI * 2;
var distance = 30 + Math.random() * 50;
particle.x = Math.cos(angle) * 20;
particle.y = Math.sin(angle) * 20;
tween(particle, {
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}, i * 200);
}
};
self.createAura = function (color) {
var aura = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.1
});
aura.tint = color;
tween(aura, {
scaleX: 2,
scaleY: 2,
alpha: 0.3,
rotation: Math.PI
}, {
duration: 3000,
easing: tween.easeInOut
});
};
self.createSparkles = function (color, count) {
for (var i = 0; i < count; i++) {
var sparkle = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0
});
sparkle.tint = color;
var radius = 60;
var angle = i / count * Math.PI * 2;
sparkle.x = Math.cos(angle) * radius;
sparkle.y = Math.sin(angle) * radius;
self.particles.push({
obj: sparkle,
angle: angle,
radius: radius,
speed: 0.02 + Math.random() * 0.02
});
}
};
self.createStarConstellation = function (color) {
// Create multiple stars in a constellation pattern
var starPositions = [{
x: 0,
y: -40
}, {
x: 30,
y: -20
}, {
x: 40,
y: 10
}, {
x: 20,
y: 30
}, {
x: -20,
y: 30
}, {
x: -40,
y: 10
}, {
x: -30,
y: -20
}];
for (var i = 0; i < starPositions.length; i++) {
var star = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.6
});
star.x = starPositions[i].x;
star.y = starPositions[i].y;
star.tint = color;
// Make stars twinkle
(function (starObj) {
tween(starObj, {
scaleX: 0.15,
scaleY: 0.15,
alpha: 1
}, {
duration: 500 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(starObj, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.6
}, {
duration: 500 + Math.random() * 1000,
easing: tween.easeInOut
});
}
});
})(star);
}
};
self.createGlitchEffect = function (color) {
var glitchLayers = [];
for (var i = 0; i < 3; i++) {
var glitch = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1,
alpha: 0.2
});
glitch.tint = color;
glitchLayers.push(glitch);
}
// Animate glitch layers
glitchLayers.forEach(function (layer, index) {
tween(layer, {
x: (Math.random() - 0.5) * 10,
y: (Math.random() - 0.5) * 10,
alpha: Math.random() * 0.3
}, {
duration: 100 + Math.random() * 200,
easing: tween.linear,
onFinish: function onFinish() {
tween(layer, {
x: 0,
y: 0,
alpha: 0.2
}, {
duration: 100 + Math.random() * 200,
easing: tween.linear
});
}
});
});
};
self.createDataStreams = function (color) {
// Create vertical data stream effect
for (var i = 0; i < 5; i++) {
LK.setTimeout(function () {
var stream = self.attachAsset('arrow_down', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.3,
alpha: 0.6
});
stream.tint = color;
stream.x = (Math.random() - 0.5) * 80;
stream.y = -40;
tween(stream, {
y: 40,
alpha: 0,
scaleY: 0.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
stream.destroy();
}
});
}, i * 200);
}
};
self.createLightning = function (color) {
var lightning = self.attachAsset('arrow_down', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 2,
alpha: 0
});
lightning.tint = color;
lightning.rotation = Math.random() * Math.PI * 2;
tween(lightning, {
alpha: 0.8,
scaleX: 0.5,
scaleY: 3
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(lightning, {
alpha: 0,
scaleX: 0.1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
lightning.destroy();
}
});
}
});
};
self.createEnergyField = function (color) {
// Create an energy field effect
var field = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.2
});
field.tint = color;
// Animate energy field pulsing
tween(field, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.4,
rotation: Math.PI
}, {
duration: 1500,
easing: tween.easeInOut
});
};
self.createCosmicDust = function (color) {
// Create floating cosmic dust particles
for (var i = 0; i < 8; i++) {
var dust = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.03 + Math.random() * 0.05,
scaleY: 0.03 + Math.random() * 0.05,
alpha: 0.4 + Math.random() * 0.4
});
dust.tint = color;
var radius = 40 + Math.random() * 60;
var angle = Math.random() * Math.PI * 2;
dust.x = Math.cos(angle) * radius;
dust.y = Math.sin(angle) * radius;
// Animate dust floating
tween(dust, {
x: dust.x + (Math.random() - 0.5) * 40,
y: dust.y + (Math.random() - 0.5) * 40,
alpha: 0,
scaleX: 0.01,
scaleY: 0.01
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
dust.destroy();
}
});
}
};
self.createPrismaticEffect = function () {
// Create enhanced rainbow prismatic effect with multiple layers
var colors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x9400D3];
// Create three layers of prisms for depth
for (var layer = 0; layer < 3; layer++) {
for (var i = 0; i < colors.length; i++) {
var prism = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + layer * 0.15,
scaleY: 0.3 + layer * 0.15,
alpha: 0.5 - layer * 0.15
});
prism.tint = colors[i];
var angle = i / colors.length * Math.PI * 2 + layer * 0.2;
var radius = 50 + layer * 20;
prism.x = Math.cos(angle) * radius;
prism.y = Math.sin(angle) * radius;
self.particles.push({
obj: prism,
angle: angle,
radius: radius,
speed: 0.03 + layer * 0.01,
layer: layer
});
}
}
// Add central crown effect
var crown = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.4
});
crown.tint = 0xFFD700;
crown.rotation = 0;
self.crownEffect = crown;
};
self.createEnergyWaves = function (color) {
// Create multiple types of expanding energy waves
for (var i = 0; i < 5; i++) {
LK.setTimeout(function () {
var wave = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7
});
wave.tint = color;
// Alternate between circular and diamond shapes
if (i % 2 === 0) {
wave.rotation = Math.PI / 4;
}
tween(wave, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: wave.rotation + Math.PI * 2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
wave.destroy();
}
});
}, i * 300);
}
// Add pulsing core energy
var core = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
core.tint = 0xFFFFFF;
self.energyCore = core;
};
self.createDimensionalRift = function () {
// Create enhanced swirling dimensional rifts with multiple layers
for (var layer = 0; layer < 3; layer++) {
for (var i = 0; i < 8; i++) {
var rift = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5 - i * 0.15 - layer * 0.3,
scaleY: 0.08 + i * 0.02 + layer * 0.02,
alpha: 0.8 - i * 0.08 - layer * 0.2
});
var colors = [0x9400D3, 0xFF00FF, 0x00FFFF, 0xFFFFFF, 0xFF00FF, 0x00FF00, 0xFFFF00, 0xFF0000];
rift.tint = colors[i % colors.length];
rift.rotation = i * Math.PI / 4 + layer * Math.PI / 8;
// Animate each rift with complex timing and effects
(function (riftObj, index, layerIndex) {
var baseRotation = riftObj.rotation;
tween(riftObj, {
rotation: baseRotation + Math.PI * 8,
scaleX: 0.1,
scaleY: 3.0 - index * 0.2 - layerIndex * 0.3,
alpha: 0.9,
x: Math.cos(baseRotation) * (50 + layerIndex * 30),
y: Math.sin(baseRotation) * (50 + layerIndex * 30)
}, {
duration: 3000 + index * 300 + layerIndex * 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(riftObj, {
rotation: baseRotation + Math.PI * 16,
scaleX: 2.5 - index * 0.15 - layerIndex * 0.3,
scaleY: 0.08 + index * 0.02 + layerIndex * 0.02,
alpha: 0,
x: 0,
y: 0
}, {
duration: 3000 + index * 300 + layerIndex * 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
riftObj.destroy();
}
});
}
});
})(rift, i, layer);
}
}
// Add enhanced space-time distortion waves
self.createSpaceTimeWaves();
// Add enhanced quantum particles
self.createQuantumParticles();
// Add portal vortex effect
self.createPortalVortex();
};
self.createSupremoAura = function () {
// Create multi-layered aura effect
for (var i = 0; i < 4; i++) {
var aura = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2 + i * 0.3,
scaleY: 1.2 + i * 0.3,
alpha: 0.3 - i * 0.05
});
var colors = [0xFF0088, 0xFF00FF, 0x00FFFF, 0xFFFF00];
aura.tint = colors[i % colors.length];
// Animate each aura layer differently
(function (auraObj, index) {
tween(auraObj, {
rotation: Math.PI * 2,
scaleX: auraObj.scaleX + 0.2,
scaleY: auraObj.scaleY + 0.2,
alpha: 0.1
}, {
duration: 3000 + index * 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(auraObj, {
rotation: 0,
scaleX: 1.2 + index * 0.3,
scaleY: 1.2 + index * 0.3,
alpha: 0.3 - index * 0.05
}, {
duration: 3000 + index * 500,
easing: tween.easeInOut
});
}
});
})(aura, i);
}
};
self.createRainbowBurst = function () {
// Create periodic rainbow burst effect
self.rainbowBurstTimer = 0;
};
self.createPowerRings = function () {
// Create rotating power rings around the character
var ringColors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF];
for (var i = 0; i < 6; i++) {
var ring = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 2,
alpha: 0.6
});
ring.tint = ringColors[i];
ring.rotation = i * Math.PI / 3;
self.particles.push({
obj: ring,
angle: i * Math.PI / 3,
radius: 0,
speed: 0.05,
type: 'powerRing'
});
}
};
self.createSpaceTimeWaves = function () {
// Create expanding space-time distortion waves
for (var i = 0; i < 8; i++) {
LK.setTimeout(function () {
var wave = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.9
});
wave.tint = 0xFFFFFF;
tween(wave, {
scaleX: 4,
scaleY: 4,
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
wave.destroy();
}
});
}, i * 400);
}
};
self.createQuantumParticles = function () {
// Create quantum particle field effect
for (var i = 0; i < 20; i++) {
var quantum = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0.8
});
var angle = Math.random() * Math.PI * 2;
var radius = 30 + Math.random() * 70;
quantum.x = Math.cos(angle) * radius;
quantum.y = Math.sin(angle) * radius;
quantum.tint = 0xFFFFFF;
self.particles.push({
obj: quantum,
angle: angle,
radius: radius,
speed: 0.1 + Math.random() * 0.05,
phase: Math.random() * Math.PI * 2,
type: 'quantum'
});
}
};
self.createCosmicStorm = function () {
// Create enhanced swirling cosmic storm effect with multiple particle types
for (var i = 0; i < 30; i++) {
var storm = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05 + Math.random() * 0.15,
scaleY: 0.05 + Math.random() * 0.15,
alpha: 0.8 + Math.random() * 0.2
});
var angle = Math.random() * Math.PI * 2;
var radius = 30 + Math.random() * 100;
storm.x = Math.cos(angle) * radius;
storm.y = Math.sin(angle) * radius;
// Ultra rainbow colors for cosmic storm
var hue = Math.random() * 360;
storm.tint = self.hslToRgb(hue / 360, 1, 0.8);
// Animate storm particles with complex paths
(function (stormObj, initialAngle, initialRadius) {
var orbitRadius = 20 + Math.random() * 60;
var orbitSpeed = 0.02 + Math.random() * 0.04;
tween(stormObj, {
x: Math.cos(initialAngle + Math.PI) * orbitRadius,
y: Math.sin(initialAngle + Math.PI) * orbitRadius,
scaleX: stormObj.scaleX * 2,
scaleY: stormObj.scaleY * 2,
alpha: 0.4,
rotation: Math.PI * 4
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(stormObj, {
x: Math.cos(initialAngle) * initialRadius,
y: Math.sin(initialAngle) * initialRadius,
scaleX: 0.05 + Math.random() * 0.15,
scaleY: 0.05 + Math.random() * 0.15,
alpha: 0.8 + Math.random() * 0.2,
rotation: 0
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut
});
}
});
})(storm, angle, radius);
self.particles.push({
obj: storm,
angle: angle,
radius: radius,
speed: 0.1 + Math.random() * 0.05,
orbitRadius: 20 + Math.random() * 60,
type: 'cosmicStorm'
});
}
};
self.createPortalVortex = function () {
// Create swirling portal vortex effect
for (var v = 0; v < 12; v++) {
var vortex = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.3,
alpha: 0.7
});
var vortexAngle = v / 12 * Math.PI * 2;
var vortexRadius = 80;
vortex.x = Math.cos(vortexAngle) * vortexRadius;
vortex.y = Math.sin(vortexAngle) * vortexRadius;
vortex.tint = 0xFF00FF;
vortex.rotation = vortexAngle + Math.PI / 2;
// Animate vortex spiraling inward
tween(vortex, {
x: Math.cos(vortexAngle) * 10,
y: Math.sin(vortexAngle) * 10,
rotation: vortexAngle + Math.PI * 6,
scaleX: 0.02,
scaleY: 0.1,
alpha: 0
}, {
duration: 2000,
easing: tween.easeIn,
onFinish: function onFinish() {
vortex.destroy();
}
});
}
};
self.createEternityField = function () {
// Create enhanced eternal energy field with multiple layers
var eternityCore = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
alpha: 0.3
});
eternityCore.tint = 0xFFFFFF;
self.eternityCore = eternityCore;
// Animate eternity core with pulsing
tween(eternityCore, {
scaleX: 3,
scaleY: 3,
alpha: 0.1,
rotation: Math.PI * 2
}, {
duration: 4000,
easing: tween.easeInOut
});
// Create multiple orbital rings
for (var r = 0; r < 3; r++) {
for (var i = 0; i < 12; i++) {
var orbital = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + r * 0.1,
scaleY: 0.2 + r * 0.1,
alpha: 0.9 - r * 0.2
});
var orbitAngle = i * Math.PI / 6;
var orbitRadius = 60 + r * 40;
orbital.x = Math.cos(orbitAngle) * orbitRadius;
orbital.y = Math.sin(orbitAngle) * orbitRadius;
// Dynamic color for each orbital
var hue = (i * 30 + r * 120) % 360;
orbital.tint = self.hslToRgb(hue / 360, 1, 0.8);
// Animate orbital elements
(function (orbitalObj, angle, radius, ring) {
tween(orbitalObj, {
x: Math.cos(angle + Math.PI * 2) * radius,
y: Math.sin(angle + Math.PI * 2) * radius,
scaleX: orbitalObj.scaleX * 1.5,
scaleY: orbitalObj.scaleY * 1.5,
alpha: orbitalObj.alpha * 0.5,
rotation: Math.PI * 4
}, {
duration: 5000 + ring * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(orbitalObj, {
x: Math.cos(angle) * radius,
y: Math.sin(angle) * radius,
scaleX: 0.2 + ring * 0.1,
scaleY: 0.2 + ring * 0.1,
alpha: 0.9 - ring * 0.2,
rotation: 0
}, {
duration: 5000 + ring * 1000,
easing: tween.easeInOut
});
}
});
})(orbital, orbitAngle, orbitRadius, r);
self.particles.push({
obj: orbital,
angle: orbitAngle,
radius: orbitRadius,
speed: 0.03 + r * 0.02,
type: 'eternal',
pulsePhase: i * Math.PI / 6,
ring: r
});
}
}
// Create energy web connections
for (var w = 0; w < 20; w++) {
var web = self.attachAsset('arrow_down', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 1.5,
alpha: 0.2
});
var webAngle = Math.random() * Math.PI * 2;
var webRadius = 50 + Math.random() * 50;
web.x = Math.cos(webAngle) * webRadius;
web.y = Math.sin(webAngle) * webRadius;
web.rotation = webAngle;
web.tint = 0x00FFFF;
tween(web, {
scaleY: 2.5,
alpha: 0.4
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(web, {
scaleY: 1.5,
alpha: 0.2
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut
});
}
});
}
};
self.update = function () {
// Only animate effects when in menu state (start screen)
if (gameState === 'start') {
// Update particle animations
for (var i = 0; i < self.particles.length; i++) {
var particle = self.particles[i];
if (particle.type === 'powerRing') {
// Rotate power rings
particle.angle += particle.speed;
particle.obj.rotation = particle.angle;
} else if (particle.type === 'quantum') {
// Update quantum particles with phase animation
particle.phase += 0.1;
particle.obj.alpha = 0.5 + 0.3 * Math.sin(particle.phase);
particle.angle += particle.speed;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
} else if (particle.type === 'cosmicStorm') {
// Update cosmic storm particles
particle.angle += particle.speed;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
} else if (particle.type === 'eternal') {
// Update eternal orbital particles
particle.angle += particle.speed;
particle.pulsePhase += 0.05;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
particle.obj.alpha = 0.9 - particle.ring * 0.2 + 0.3 * Math.sin(particle.pulsePhase);
} else {
// Update regular sparkle particles
particle.angle += particle.speed;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
}
}
// Update special effects based on rank
var rankName = self.rankData.name.toLowerCase();
if (rankName.includes('supremo')) {
// Update crown effect rotation
if (self.crownEffect) {
self.crownEffect.rotation += 0.02;
}
// Update energy core pulsing
if (self.energyCore) {
self.energyCore.alpha = 0.6 + 0.4 * Math.sin(LK.ticks * 0.1);
self.energyCore.scaleX = 0.5 + 0.1 * Math.sin(LK.ticks * 0.08);
self.energyCore.scaleY = 0.5 + 0.1 * Math.sin(LK.ticks * 0.08);
}
} else if (rankName.includes('infinito')) {
// Update eternity core
if (self.eternityCore) {
self.eternityCore.rotation += 0.01;
self.eternityCore.alpha = 0.2 + 0.2 * Math.sin(LK.ticks * 0.05);
}
}
}
// During gameplay (playing, countdown, gameover states), freeze all rank effect animations
// Effects remain completely static to avoid distracting from gameplay
};
// Helper function to convert HSL to RGB
self.hslToRgb = function (h, s, l) {
var r, g, b;
if (s === 0) {
r = g = b = l;
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return Math.floor(r * 255) << 16 | Math.floor(g * 255) << 8 | Math.floor(b * 255);
};
self.createRainbowBurstEffect = function () {
// Create spectacular rainbow burst
var burstCount = 12;
for (var i = 0; i < burstCount; i++) {
var burst = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
});
var angle = i / burstCount * Math.PI * 2;
var hue = i / burstCount * 360;
burst.tint = self.hslToRgb(hue / 360, 1, 0.5);
burst.x = 0;
burst.y = 0;
tween(burst, {
x: Math.cos(angle) * 100,
y: Math.sin(angle) * 100,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
burst.destroy();
}
});
}
};
// Initialize effects
self.createEffects();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c2c54
});
/****
* Game Code
****/
// Game constants
var COLUMN_COUNT = 4;
var currentRankEffect = null;
var COLUMN_WIDTH = 2048 / COLUMN_COUNT;
var TARGET_Y = 2400;
var SPAWN_Y = -100;
var HIT_ZONE_HEIGHT = 80;
// Game state
var arrows = [];
var score = 0;
var combo = 0;
var missCount = 0;
var maxMisses = 10;
var gameStarted = false;
var gameState = 'start'; // 'start', 'countdown', 'playing', 'gameover'
var countdownValue = 3;
var countdownTimer = 0;
var arrowSpawnTimer = 0;
var spawnInterval = 45; // frames between arrow spawns
// Health bar system
var healthBar = 50; // Current health (0-100)
var maxHealth = 100;
var healthPerHit = 15; // Health gained per successful hit (increased)
var healthPerMiss = 8; // Health lost per miss (reduced for fairness)
var currentLevelHits = 0; // Current hits in this level (tracking for statistics)
// Level system with progressive speed increases
var level = 1;
var baseSpeed = 8;
var baseSpawnInterval = 45;
var speedMultiplier = 1.0; // Speed multiplier that increases with level
var maxSpeedMultiplier = 3.0; // Maximum speed cap for balance
var scoresPerLevel = [0, 1000, 2500, 5000, 8000, 12000, 17000, 23000, 30000, 40000];
// Direction mapping
var directions = ['left', 'down', 'up', 'right'];
var columnPositions = [];
// Expanded rank system with sub-levels and reduced score requirements
var ranks = [{
name: "noob I",
minScore: 0,
color: 0x666666
}, {
name: "noob II",
minScore: 500,
color: 0x777777
}, {
name: "noob III",
minScore: 1200,
color: 0x888888
}, {
name: "pro I",
minScore: 2000,
color: 0x00aa00
}, {
name: "pro II",
minScore: 3500,
color: 0x00cc00
}, {
name: "pro III",
minScore: 5500,
color: 0x00ff00
}, {
name: "dios I",
minScore: 8000,
color: 0xccaa00
}, {
name: "dios II",
minScore: 12000,
color: 0xddbb00
}, {
name: "dios III",
minScore: 18000,
color: 0xffd700
}, {
name: "hacker I",
minScore: 25000,
color: 0xcc00cc
}, {
name: "hacker II",
minScore: 35000,
color: 0xdd00dd
}, {
name: "hacker III",
minScore: 50000,
color: 0xff00ff
}, {
name: "imparable I",
minScore: 70000,
color: 0x00cccc
}, {
name: "imparable II",
minScore: 95000,
color: 0x00dddd
}, {
name: "imparable III",
minScore: 125000,
color: 0x00ffff
}, {
name: "leyenda I",
minScore: 160000,
color: 0xff6600
}, {
name: "leyenda II",
minScore: 200000,
color: 0xff7700
}, {
name: "leyenda III",
minScore: 250000,
color: 0xff8800
}, {
name: "supremo I",
minScore: 310000,
color: 0xff0066
}, {
name: "supremo II",
minScore: 380000,
color: 0xff0077
}, {
name: "supremo III",
minScore: 460000,
color: 0xff0088
}, {
name: "infinito",
minScore: 550000,
color: 0xffffff
}];
function getRankFromScore(score) {
var currentRank = ranks[0];
for (var i = 0; i < ranks.length; i++) {
if (score >= ranks[i].minScore) {
currentRank = ranks[i];
}
}
return currentRank;
}
function updatePlayerRank(score) {
// Initialize lifetime score if it doesn't exist
if (!storage.lifetimeScore) {
storage.lifetimeScore = 0;
}
// Add current game points to lifetime total
var pointsToAdd = score - (storage.lastSessionScore || 0);
if (pointsToAdd > 0) {
storage.lifetimeScore += pointsToAdd;
storage.lastSessionScore = score;
}
var newRank = getRankFromScore(storage.lifetimeScore);
var oldRankName = storage.currentRank;
if (newRank.name !== oldRankName) {
storage.currentRank = newRank.name;
showRankUpEffect(newRank);
return true;
}
return false;
}
function showRankUpEffect(rank) {
// Create rank up text
var rankUpTxt = new Text2('RANK UP!', {
size: 100,
fill: rank.color
});
rankUpTxt.anchor.set(0.5, 0.5);
rankUpTxt.x = 0;
rankUpTxt.y = -50;
rankUpTxt.alpha = 0;
rankUpTxt.scaleX = 0.1;
rankUpTxt.scaleY = 0.1;
LK.gui.center.addChild(rankUpTxt);
// Create new rank text
var newRankTxt = new Text2(rank.name.toUpperCase(), {
size: 80,
fill: rank.color
});
newRankTxt.anchor.set(0.5, 0.5);
newRankTxt.x = 0;
newRankTxt.y = 50;
newRankTxt.alpha = 0;
newRankTxt.scaleX = 0.1;
newRankTxt.scaleY = 0.1;
LK.gui.center.addChild(newRankTxt);
// Animate rank up effect
tween(rankUpTxt, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(rankUpTxt, {
alpha: 0,
y: -150
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rankUpTxt.destroy();
}
});
}
});
// Animate new rank text
LK.setTimeout(function () {
tween(newRankTxt, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 600,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(newRankTxt, {
alpha: 0,
y: 150
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
newRankTxt.destroy();
}
});
}
});
}, 400);
// Flash screen with rank color
LK.effects.flashScreen(rank.color, 1000);
// Create large animated shield for rank up effect
var rankUpShield = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
rankUpShield.x = 0;
rankUpShield.y = 0;
rankUpShield.tint = rank.color;
LK.gui.center.addChild(rankUpShield);
// Create multiple shield layers for enhanced effect
var shieldLayers = [];
for (var sl = 0; sl < 3; sl++) {
var shieldLayer = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
shieldLayer.x = 0;
shieldLayer.y = 0;
shieldLayer.tint = rank.color;
LK.gui.center.addChild(shieldLayer);
shieldLayers.push(shieldLayer);
}
// Animate the main rank up shield
tween(rankUpShield, {
scaleX: 4,
scaleY: 4,
alpha: 1,
rotation: Math.PI * 3
}, {
duration: 1500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(rankUpShield, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: Math.PI * 6
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rankUpShield.destroy();
}
});
}
});
// Animate shield layers with staggered timing
for (var sl = 0; sl < shieldLayers.length; sl++) {
(function (layer, index) {
LK.setTimeout(function () {
tween(layer, {
scaleX: 2 + index * 0.5,
scaleY: 2 + index * 0.5,
alpha: 0.7 - index * 0.2,
rotation: Math.PI * (2 + index)
}, {
duration: 1200 + index * 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(layer, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: Math.PI * (4 + index * 2)
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
layer.destroy();
}
});
}
});
}, index * 300);
})(shieldLayers[sl], sl);
}
// Create celebration particles
for (var i = 0; i < 20; i++) {
LK.setTimeout(function () {
var particle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0.8
}));
particle.x = 1024 + (Math.random() - 0.5) * 600;
particle.y = 1366 + (Math.random() - 0.5) * 400;
particle.tint = rank.color;
var angle = Math.random() * Math.PI * 2;
var distance = 200 + Math.random() * 300;
tween(particle, {
x: particle.x + Math.cos(angle) * distance,
y: particle.y + Math.sin(angle) * distance,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}, i * 50);
}
// Update rank effect to match new rank at shield position
if (currentRankEffect) {
currentRankEffect.destroy();
}
currentRankEffect = new RankEffect(rank, -280, 135);
LK.gui.topRight.addChild(currentRankEffect);
}
// Calculate column positions
for (var i = 0; i < COLUMN_COUNT; i++) {
columnPositions.push(COLUMN_WIDTH * i + COLUMN_WIDTH / 2);
}
// Create target zones
var targetZones = [];
for (var i = 0; i < COLUMN_COUNT; i++) {
var targetZone = game.addChild(LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
targetZone.x = columnPositions[i];
targetZone.y = TARGET_Y;
targetZones.push(targetZone);
// Add enhanced pulsing animation to target zones with particle effects
tween(targetZone, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.6
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(targetZone, {
scaleX: 0.9,
scaleY: 0.9,
alpha: 0.2
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
// Add floating particles around target zone
LK.setTimeout(function () {
for (var p = 0; p < 4; p++) {
var particle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0.8
}));
var angle = p / 4 * Math.PI * 2;
var radius = 80;
particle.x = targetZone.x + Math.cos(angle) * radius;
particle.y = targetZone.y + Math.sin(angle) * radius;
particle.tint = i === 0 ? 0xff4757 : i === 1 ? 0x5352ed : i === 2 ? 0x2ed573 : 0xffa502;
tween(particle, {
scaleX: 0.15,
scaleY: 0.15,
alpha: 0,
x: targetZone.x + Math.cos(angle) * (radius + 60),
y: targetZone.y + Math.sin(angle) * (radius + 60)
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
}, Math.random() * 1000);
}
// Create control zones at bottom of screen
var controlZones = [];
// Position control zones at the very bottom for better mobile reach
var controlZoneY = TARGET_Y + 200; // Moved even further down
// Make the control zones smaller to prevent overlapping but still comfortable for mobile
var controlZoneWidth = COLUMN_WIDTH * 0.9;
var controlZoneHeight = 350; // Reduced height to prevent button overlap
for (var i = 0; i < COLUMN_COUNT; i++) {
var controlZone = game.addChild(LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
// Slightly visible for better feedback
alpha: 0.15,
width: controlZoneWidth,
height: controlZoneHeight
}));
controlZone.x = columnPositions[i];
controlZone.y = controlZoneY;
controlZone.columnIndex = i;
// Color coding for visual feedback
controlZone.tint = i === 0 ? 0xff4757 : i === 1 ? 0x5352ed : i === 2 ? 0x2ed573 : 0xffa502;
controlZones.push(controlZone);
}
// Add decorative background elements
var rockets = [];
var confetti = [];
var comets = [];
// Create rocket decorations
for (var i = 0; i < 8; i++) {
var rocket = game.addChild(LK.getAsset('arrow_up', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.8,
alpha: 0.15
}));
rocket.x = Math.random() * 2048;
rocket.y = Math.random() * 2732;
rocket.speed = 1 + Math.random() * 2;
rocket.tint = Math.random() > 0.5 ? 0xFF6B6B : 0x4ECDC4;
rockets.push(rocket);
}
// Create confetti pieces with enhanced colors
for (var i = 0; i < 25; i++) {
var confettiPiece = game.addChild(LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.2
}));
confettiPiece.x = Math.random() * 2048;
confettiPiece.y = Math.random() * 2732;
confettiPiece.speed = 0.3 + Math.random() * 1;
confettiPiece.rotationSpeed = (Math.random() - 0.5) * 0.1;
var colors = [0xFF6B6B, 0xF9CA24, 0xF0932B, 0xEB4D4B, 0x6C5CE7, 0xFF00FF, 0x00FF00];
confettiPiece.tint = colors[Math.floor(Math.random() * colors.length)];
confetti.push(confettiPiece);
}
// Create comet decorations
for (var i = 0; i < 6; i++) {
var comet = game.addChild(LK.getAsset('arrow_right', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.2,
alpha: 0.25
}));
comet.x = Math.random() * 2048;
comet.y = Math.random() * 2732;
comet.speed = 2 + Math.random() * 3;
comet.rotation = Math.random() * Math.PI * 2;
comet.tint = Math.random() > 0.5 ? 0x00FFFF : 0xFFFFFF;
comets.push(comet);
}
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 50;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
var comboTxt = new Text2('Combo: 0', {
size: 50,
fill: 0xFFD700
});
comboTxt.anchor.set(0.5, 0);
comboTxt.x = 0;
comboTxt.y = 120;
LK.gui.top.addChild(comboTxt);
var levelTxt = new Text2('Level: 1', {
size: 50,
fill: 0xFF6B6B
});
levelTxt.anchor.set(1, 0);
levelTxt.x = -50;
levelTxt.y = 50;
LK.gui.topRight.addChild(levelTxt);
// Initialize rank text with stored rank
var storedRank = storage.currentRank || "noob I";
var initialRankData = null;
for (var r = 0; r < ranks.length; r++) {
if (ranks[r].name === storedRank) {
initialRankData = ranks[r];
break;
}
}
if (!initialRankData) {
initialRankData = ranks[0]; // fallback to noob
}
var rankTxt = new Text2('RANK: ' + initialRankData.name.toUpperCase(), {
size: 45,
fill: initialRankData.color
});
rankTxt.anchor.set(1, 0);
rankTxt.x = -50;
rankTxt.y = 110;
LK.gui.topRight.addChild(rankTxt);
// Add shield icon next to rank text with enhanced properties
var rankShield = LK.getAsset('shield', {
anchorX: 1,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
rankShield.x = -280;
rankShield.y = 135;
rankShield.tint = initialRankData.color;
rankShield.rotation = 0;
LK.gui.topRight.addChild(rankShield);
// Add shield glow effect
var shieldGlow = LK.getAsset('shield', {
anchorX: 1,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.3
});
shieldGlow.x = -280;
shieldGlow.y = 135;
shieldGlow.tint = initialRankData.color;
LK.gui.topRight.addChild(shieldGlow);
// Create initial rank effect on GUI at shield position
currentRankEffect = new RankEffect(initialRankData, -280, 135);
LK.gui.topRight.addChild(currentRankEffect);
// Start shield glow animation
tween(shieldGlow, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0.1
}, {
duration: 1500,
easing: tween.easeInOut
});
// Health bar UI with improved visuals
var healthBarBg = LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.8,
alpha: 0.6
});
healthBarBg.tint = 0x333333;
healthBarBg.x = 0;
healthBarBg.y = 150;
LK.gui.top.addChild(healthBarBg);
var healthBarBorder = LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.2,
scaleY: 1,
alpha: 0.8
});
healthBarBorder.tint = 0xFFFFFF;
healthBarBorder.x = 0;
healthBarBorder.y = 150;
LK.gui.top.addChild(healthBarBorder);
var healthBarFill = LK.getAsset('target_zone', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.6
});
healthBarFill.tint = 0x00FF88;
healthBarFill.x = -280;
healthBarFill.y = 150;
LK.gui.top.addChild(healthBarFill);
var healthTxt = new Text2('FILL TO LEVEL UP', {
size: 45,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0.5, 0.5);
healthTxt.x = 0;
healthTxt.y = 200;
LK.gui.top.addChild(healthTxt);
var accuracyTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
accuracyTxt.anchor.set(0.5, 0.5);
accuracyTxt.x = 0;
accuracyTxt.y = -200;
accuracyTxt.alpha = 0;
LK.gui.center.addChild(accuracyTxt);
// Start screen elements
var startTxt = new Text2('TAP TO START', {
size: 100,
fill: 0xFFFFFF
});
startTxt.anchor.set(0.5, 0.5);
startTxt.x = 0;
startTxt.y = 0;
LK.gui.center.addChild(startTxt);
var titleTxt = new Text2('RHYTHM ARROWS', {
size: 120,
fill: 0x00FFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 0;
titleTxt.y = -300;
LK.gui.center.addChild(titleTxt);
var subtitleTxt = new Text2('Hit the arrows in rhythm!', {
size: 50,
fill: 0xFFD700
});
subtitleTxt.anchor.set(0.5, 0.5);
subtitleTxt.x = 0;
subtitleTxt.y = 100;
LK.gui.center.addChild(subtitleTxt);
var countdownTxt = new Text2('3', {
size: 200,
fill: 0xFFD700
});
countdownTxt.anchor.set(0.5, 0.5);
countdownTxt.x = 0;
countdownTxt.y = 0;
countdownTxt.alpha = 0;
LK.gui.center.addChild(countdownTxt);
// Game over screen elements
var gameOverTxt = new Text2('GAME OVER', {
size: 100,
fill: 0xFF0000
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.x = 0;
gameOverTxt.y = -200;
gameOverTxt.alpha = 0;
LK.gui.center.addChild(gameOverTxt);
var finalComboTxt = new Text2('', {
size: 60,
fill: 0xFFD700
});
finalComboTxt.anchor.set(0.5, 0.5);
finalComboTxt.x = 0;
finalComboTxt.y = -100;
finalComboTxt.alpha = 0;
LK.gui.center.addChild(finalComboTxt);
var finalMissTxt = new Text2('', {
size: 60,
fill: 0xFF6B6B
});
finalMissTxt.anchor.set(0.5, 0.5);
finalMissTxt.x = 0;
finalMissTxt.y = -40;
finalMissTxt.alpha = 0;
LK.gui.center.addChild(finalMissTxt);
var restartTxt = new Text2('TAP TO PLAY AGAIN', {
size: 80,
fill: 0x00FFFF
});
restartTxt.anchor.set(0.5, 0.5);
restartTxt.x = 0;
restartTxt.y = 100;
restartTxt.alpha = 0;
LK.gui.center.addChild(restartTxt);
// Loading screen elements
var loadingTxt = new Text2('LOADING...', {
size: 80,
fill: 0x00FFFF
});
loadingTxt.anchor.set(0.5, 0.5);
loadingTxt.x = 0;
loadingTxt.y = 0;
loadingTxt.alpha = 0;
LK.gui.center.addChild(loadingTxt);
var loadingBar = LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0.5,
alpha: 0
});
loadingBar.tint = 0x00FFFF;
loadingBar.x = 0;
loadingBar.y = 100;
LK.gui.center.addChild(loadingBar);
// Reset Points Function
function resetPlayerPoints() {
// Reset lifetime score to zero
storage.lifetimeScore = 0;
// Reset session score
storage.lastSessionScore = 0;
// Reset current rank to noob I (not hacker)
storage.currentRank = "noob I";
// Also reset the current game score to 0
score = 0;
// Update UI to reflect changes
updateUI();
// Show confirmation message
showAccuracyText('SCORE & RANK RESET!', 0xff0000);
LK.effects.flashScreen(0xff0000, 500);
}
// Reset Rank Function
function resetPlayerRank() {
// Reset current rank to noob I
storage.currentRank = "noob I";
// Update UI to reflect rank change
updateUI();
// Show confirmation message
showAccuracyText('RANK RESET TO NOOB I!', 0xff0000);
LK.effects.flashScreen(0xff0000, 500);
}
// Functions
function spawnArrow() {
var column = Math.floor(Math.random() * COLUMN_COUNT);
var direction = directions[column];
var arrow = new Arrow(direction, column);
arrow.x = columnPositions[column];
arrow.y = SPAWN_Y;
arrows.push(arrow);
game.addChild(arrow);
// Add enhanced entrance trail effect based on speed level
var trailCount = Math.min(5, 3 + Math.floor(speedMultiplier));
for (var t = 0; t < trailCount; t++) {
var trail = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + speedMultiplier * 0.1,
scaleY: 0.2 + speedMultiplier * 0.1,
alpha: 0.6 + speedMultiplier * 0.2
}));
trail.x = arrow.x + (Math.random() - 0.5) * (40 + speedMultiplier * 10);
trail.y = arrow.y - t * (30 + speedMultiplier * 5);
trail.tint = direction === 'left' ? 0xff4757 : direction === 'down' ? 0x5352ed : direction === 'up' ? 0x2ed573 : 0xffa502;
// Add intensity glow for higher speeds
if (speedMultiplier > 1.5) {
trail.tint = 0xFFFFFF; // White hot trail for high speeds
}
var trailDuration = Math.max(400, 600 - speedMultiplier * 50);
tween(trail, {
scaleX: 0.05,
scaleY: 0.05,
alpha: 0,
y: trail.y + (100 + speedMultiplier * 20)
}, {
duration: trailDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
trail.destroy();
}
});
}
// Add entrance burst effect
var burst = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
}));
burst.x = arrow.x;
burst.y = arrow.y;
burst.tint = 0xFFFFFF;
tween(burst, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
burst.destroy();
}
});
}
function checkHit(arrow) {
var distance = Math.abs(arrow.y - TARGET_Y);
if (distance <= HIT_ZONE_HEIGHT * 0.8) {
// Perfect hit - more generous perfect zone
return 'perfect';
} else if (distance <= HIT_ZONE_HEIGHT * 3) {
// Good hit - much more generous good zone
return 'good';
}
return null;
}
function processHit(arrow, accuracy) {
var points = 0;
var comboMultiplier = Math.floor(combo / 10) + 1;
if (accuracy === 'perfect') {
points = 100 * comboMultiplier;
var perfectSound = LK.getSound('hit_perfect');
perfectSound.volume = storage.masterVolume * storage.sfxVolume;
perfectSound.play();
showAccuracyText('PERFECT!', 0x00ff00);
// Extra effect for perfect hits
LK.effects.flashScreen(0x00ff00, 100);
} else if (accuracy === 'good') {
points = 50 * comboMultiplier;
var goodSound = LK.getSound('hit_good');
goodSound.volume = storage.masterVolume * storage.sfxVolume;
goodSound.play();
showAccuracyText('GOOD', 0xffff00);
}
score += points;
combo++;
// Check for rank up
updatePlayerRank(score);
// Increase health bar
healthBar = Math.min(maxHealth, healthBar + healthPerHit);
currentLevelHits++;
// Check if health bar is full to advance to next level
if (healthBar >= maxHealth) {
level++;
currentLevelHits = 0;
// Reset health bar to starting level for next challenge
healthBar = 50;
// Increase speed multiplier progressively with each level
speedMultiplier = Math.min(1.0 + (level - 1) * 0.15, maxSpeedMultiplier);
// Slightly reduce spawn interval for more challenge but keep it balanced
spawnInterval = Math.max(25, baseSpawnInterval - Math.floor((level - 1) * 1.2));
// Show enhanced level up effect with speed notification
showAccuracyText('LEVEL ' + level + '! HEALTH FULL!', 0x00ffff);
LK.effects.flashScreen(0x00ffff, 700);
// Add speed burst visual effect
tween(game, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
// Create multiple hit effects for high combos
var effectCount = Math.min(3, Math.floor(combo / 25) + 1);
for (var i = 0; i < effectCount; i++) {
LK.setTimeout(function () {
var effect = new HitEffect(arrow.x + (Math.random() - 0.5) * 50, arrow.y + (Math.random() - 0.5) * 50);
game.addChild(effect);
}, i * 50);
}
// Enhanced combo system with multiple milestone effects
if (combo % 5 === 0 && combo > 0) {
// Create spectacular combo effect
var comboEffect = new ComboEffect(arrow.x, arrow.y - 100, combo);
game.addChild(comboEffect);
// Different effects based on combo milestones
if (combo >= 100) {
// LEGENDARY combo (100+)
LK.effects.flashScreen(0xFFD700, 800);
tween(comboTxt, {
scaleX: 3,
scaleY: 3,
tint: 0xFFD700,
rotation: 0.5
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFD700,
rotation: 0
}, {
duration: 600,
easing: tween.easeOut
});
}
});
} else if (combo >= 50) {
// EPIC combo (50+)
LK.effects.flashScreen(0xFF6B6B, 500);
tween(comboTxt, {
scaleX: 2.5,
scaleY: 2.5,
tint: 0xFF6B6B,
rotation: 0.3
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFFD700,
rotation: 0
}, {
duration: 500,
easing: tween.easeOut
});
}
});
} else if (combo >= 25) {
// SUPER combo (25+)
LK.effects.flashScreen(0x4ECDC4, 300);
tween(comboTxt, {
scaleX: 2.2,
scaleY: 2.2,
tint: 0x4ECDC4
}, {
duration: 350,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFD700
}, {
duration: 450,
easing: tween.easeOut
});
}
});
} else {
// Regular combo milestone (5, 10, 15, 20)
tween(comboTxt, {
scaleX: 2,
scaleY: 2,
tint: 0x00FFFF
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1,
scaleY: 1,
tint: 0xFFD700
}, {
duration: 400,
easing: tween.easeOut
});
}
});
}
// Create confetti burst for high combos
if (combo >= 25) {
var confettiCount = Math.min(30, combo / 2);
for (var c = 0; c < confettiCount; c++) {
LK.setTimeout(function () {
var burstConfetti = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15,
alpha: 0.9
}));
burstConfetti.x = arrow.x + (Math.random() - 0.5) * 300;
burstConfetti.y = arrow.y + (Math.random() - 0.5) * 300;
var colors = [0xFF6B6B, 0x4ECDC4, 0x45B7D1, 0xF9CA24, 0xF0932B, 0xFFD700, 0x00FFFF];
burstConfetti.tint = colors[Math.floor(Math.random() * colors.length)];
tween(burstConfetti, {
y: burstConfetti.y - 400,
x: burstConfetti.x + (Math.random() - 0.5) * 200,
alpha: 0,
rotation: Math.random() * Math.PI * 4,
scaleX: 0.02,
scaleY: 0.02
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
burstConfetti.destroy();
}
});
}, c * 20);
}
}
// Create firework effect for mega combos
if (combo >= 75) {
for (var f = 0; f < 5; f++) {
var firework = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
}));
firework.x = arrow.x + (Math.random() - 0.5) * 400;
firework.y = arrow.y + (Math.random() - 0.5) * 200;
firework.tint = 0xFFFFFF;
tween(firework, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
firework.destroy();
}
});
}
}
}
// Remove arrow
arrow.destroy();
var arrowIndex = arrows.indexOf(arrow);
if (arrowIndex > -1) {
arrows.splice(arrowIndex, 1);
}
updateUI();
}
function processMiss() {
combo = 0;
missCount++;
var missSound = LK.getSound('miss');
missSound.volume = storage.masterVolume * storage.sfxVolume;
missSound.play();
showAccuracyText('MISS', 0xff0000);
// Create miss effect animation instead of screen flash
var missEffect = new MissEffect(1024, 1366);
game.addChild(missEffect);
// Shake the screen slightly for miss feedback
tween(game, {
x: 10
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: -10
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: 0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
// Decrease health bar
healthBar = Math.max(0, healthBar - healthPerMiss);
// Check if health bar reached 0
if (healthBar <= 0) {
showGameOverScreen();
}
updateUI();
}
function showAccuracyText(text, color) {
accuracyTxt.setText(text);
accuracyTxt.fill = color;
accuracyTxt.alpha = 1;
accuracyTxt.scaleX = 0.2;
accuracyTxt.scaleY = 0.2;
accuracyTxt.rotation = 0;
// Create spectacular effects based on accuracy type
if (text === 'PERFECT!') {
// Create burst of sparkles for perfect hits
for (var s = 0; s < 12; s++) {
var sparkle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
}));
sparkle.x = 1024;
sparkle.y = 1366;
sparkle.tint = s % 2 === 0 ? 0x00FFFF : 0xFFD700;
var angle = s / 12 * Math.PI * 2;
var distance = 200 + Math.random() * 100;
tween(sparkle, {
x: 1024 + Math.cos(angle) * distance,
y: 1366 + Math.sin(angle) * distance,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
sparkle.destroy();
}
});
}
// Enhanced perfect text animation
tween(accuracyTxt, {
scaleX: 2.5,
scaleY: 2.5,
rotation: 0.5
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(accuracyTxt, {
scaleX: 1.8,
scaleY: 1.8,
rotation: 0,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}
});
} else if (text === 'GOOD') {
// Create ring effect for good hits
for (var r = 0; r < 8; r++) {
var ring = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0.8
}));
ring.x = 1024;
ring.y = 1366;
ring.tint = 0xFFFF00;
var ringAngle = r / 8 * Math.PI * 2;
var ringDistance = 120;
tween(ring, {
x: 1024 + Math.cos(ringAngle) * ringDistance,
y: 1366 + Math.sin(ringAngle) * ringDistance,
scaleX: 0.4,
scaleY: 0.4,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
ring.destroy();
}
});
}
// Enhanced good text animation
tween(accuracyTxt, {
scaleX: 2.0,
scaleY: 2.0,
rotation: 0.2
}, {
duration: 250,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(accuracyTxt, {
scaleX: 1.2,
scaleY: 1.2,
rotation: 0,
alpha: 0
}, {
duration: 700,
easing: tween.easeOut
});
}
});
} else {
// Default animation for MISS
tween(accuracyTxt, {
scaleX: 1.5,
scaleY: 1.5,
rotation: text === 'PERFECT!' ? 0.3 : 0.1
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(accuracyTxt, {
scaleX: 1,
scaleY: 1,
rotation: 0,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut
});
}
});
}
}
function updateUI() {
scoreTxt.setText('Score: ' + score);
comboTxt.setText('Combo: ' + combo);
levelTxt.setText('Level: ' + level);
// Update rank display using stored rank
var storedRank = storage.currentRank || "noob I";
var currentRankData = null;
for (var r = 0; r < ranks.length; r++) {
if (ranks[r].name === storedRank) {
currentRankData = ranks[r];
break;
}
}
if (!currentRankData) {
currentRankData = ranks[0]; // fallback to noob
}
// Find next rank for progress display
var nextRank = null;
for (var nr = 0; nr < ranks.length; nr++) {
if (ranks[nr].minScore > (storage.lifetimeScore || 0)) {
nextRank = ranks[nr];
break;
}
}
var rankDisplayText = 'RANK: ' + currentRankData.name.toUpperCase();
if (nextRank) {
var progress = storage.lifetimeScore || 0;
var needed = nextRank.minScore - progress;
rankDisplayText += ' (' + needed + ' TO ' + nextRank.name.toUpperCase() + ')';
}
rankTxt.setText(rankDisplayText);
rankTxt.fill = currentRankData.color;
// Update shield color to match current rank
rankShield.tint = currentRankData.color;
shieldGlow.tint = currentRankData.color;
// Add enhanced pulsing animation to shield for visual appeal
tween(rankShield, {
scaleX: 1.1,
scaleY: 1.1,
alpha: 1,
rotation: 0.1
}, {
duration: 1000,
easing: tween.easeInOut
});
// Add rotation effect for higher ranks
if (currentRankData.name.includes('dios') || currentRankData.name.includes('hacker') || currentRankData.name.includes('imparable') || currentRankData.name.includes('leyenda') || currentRankData.name.includes('supremo') || currentRankData.name.includes('infinito')) {
tween(rankShield, {
rotation: rankShield.rotation + Math.PI * 2
}, {
duration: 3000,
easing: tween.easeInOut
});
}
// Update health bar with smooth animation
var healthPercent = healthBar / maxHealth;
var targetScaleX = 2 * healthPercent;
// Animate health bar changes smoothly
tween(healthBarFill, {
scaleX: targetScaleX
}, {
duration: 300,
easing: tween.easeOut
});
// Change color based on health level with smooth transitions
var targetColor;
if (healthPercent > 0.7) {
targetColor = 0x00FF88; // Bright green
} else if (healthPercent > 0.5) {
targetColor = 0x88FF00; // Yellow-green
} else if (healthPercent > 0.3) {
targetColor = 0xFFAA00; // Orange
} else {
targetColor = 0xFF3333; // Bright red
}
// Animate color change
tween(healthBarFill, {
tint: targetColor
}, {
duration: 200,
easing: tween.easeOut
});
// Pulse effect when health is critical
if (healthPercent <= 0.2) {
tween(healthBarFill, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeInOut
});
}
}
function startCountdown() {
gameState = 'countdown';
startTxt.alpha = 0;
// Fade out title elements
tween(titleTxt, {
alpha: 0,
y: -400
}, {
duration: 800,
easing: tween.easeOut
});
tween(subtitleTxt, {
alpha: 0,
y: 200
}, {
duration: 800,
easing: tween.easeOut
});
countdownTxt.alpha = 1;
countdownValue = 3;
countdownTimer = 0;
}
function showCountdownNumber() {
if (countdownValue > 0) {
countdownTxt.setText(countdownValue.toString());
countdownTxt.fill = countdownValue === 3 ? 0xFF0000 : countdownValue === 2 ? 0xFFA500 : 0x00FF00;
} else {
countdownTxt.setText('GO!');
countdownTxt.fill = 0x00FFFF;
}
countdownTxt.scaleX = 3;
countdownTxt.scaleY = 3;
countdownTxt.rotation = 0.5;
countdownTxt.alpha = 1;
// Animate scale down with bounce effect
tween(countdownTxt, {
scaleX: 1,
scaleY: 1,
rotation: 0
}, {
duration: 800,
easing: tween.bounceOut
});
// Animate alpha fade out after scale animation
LK.setTimeout(function () {
tween(countdownTxt, {
alpha: 0
}, {
duration: 200,
easing: tween.easeOut
});
}, 600);
}
function restartGame() {
// Show loading screen first
gameState = 'loading';
loadingTxt.alpha = 1;
loadingBar.alpha = 1;
loadingBar.scaleX = 0;
// Animate loading bar
tween(loadingBar, {
scaleX: 3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// After loading, reset game
// Reset all game variables
score = 0;
combo = 0;
missCount = 0;
level = 1;
healthBar = 50;
currentLevelHits = 0;
arrowSpawnTimer = 0;
spawnInterval = 45;
speedMultiplier = 1.0; // Reset speed multiplier to base level
gameState = 'start';
gameStarted = false;
// Reset session score tracking but keep lifetime score and current rank
storage.lastSessionScore = 0;
// Clear all arrows
for (var i = arrows.length - 1; i >= 0; i--) {
arrows[i].destroy();
}
arrows = [];
// Stop music
LK.stopMusic();
// Reset UI visibility - show title again
startTxt.alpha = 1;
titleTxt.alpha = 1; // Show title again
titleTxt.y = -300;
titleTxt.rotation = 0;
subtitleTxt.alpha = 1;
subtitleTxt.y = 100;
countdownTxt.alpha = 0;
// Properly hide all game over elements
gameOverTxt.alpha = 0;
gameOverTxt.scaleX = 1;
gameOverTxt.scaleY = 1;
finalComboTxt.alpha = 0;
finalComboTxt.y = -100;
finalMissTxt.alpha = 0;
finalMissTxt.y = -40;
restartTxt.alpha = 0;
accuracyTxt.alpha = 0;
// Stop any active tweens on game over elements
tween.stop(gameOverTxt);
tween.stop(finalComboTxt);
tween.stop(finalMissTxt);
tween.stop(restartTxt);
// Hide loading screen
loadingTxt.alpha = 0;
loadingBar.alpha = 0;
// Reset shield to stored rank state
var currentStoredRank = storage.currentRank || "noob";
var currentStoredRankData = null;
for (var sr = 0; sr < ranks.length; sr++) {
if (ranks[sr].name === currentStoredRank) {
currentStoredRankData = ranks[sr];
break;
}
}
if (!currentStoredRankData) {
currentStoredRankData = ranks[0]; // fallback to noob
}
rankShield.tint = currentStoredRankData.color;
rankShield.scaleX = 1.0;
rankShield.scaleY = 1.0;
rankShield.alpha = 1.0;
rankShield.rotation = 0;
// Reset shield glow
shieldGlow.tint = currentStoredRankData.color;
shieldGlow.scaleX = 1.2;
shieldGlow.scaleY = 1.2;
shieldGlow.alpha = 0.3;
// Update UI
updateUI();
// Keep existing rank effect if it matches current rank
if (currentRankEffect && currentRankEffect.rankData.name !== currentStoredRankData.name) {
currentRankEffect.destroy();
currentRankEffect = null;
}
// Only create new rank effect if it doesn't exist
if (!currentRankEffect) {
currentRankEffect = new RankEffect(currentStoredRankData, -280, 135);
LK.gui.topRight.addChild(currentRankEffect);
}
}
});
}
function showGameOverScreen() {
gameState = 'gameover';
// Flash screen with dramatic effect
LK.effects.flashScreen(0x8B0000, 1000);
// Show game over elements with staggered animations
gameOverTxt.alpha = 0;
gameOverTxt.scaleX = 0.1;
gameOverTxt.scaleY = 0.1;
tween(gameOverTxt, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.bounceOut
});
// Animate final stats with delay
LK.setTimeout(function () {
finalComboTxt.setText('Level Reached: ' + level);
finalComboTxt.alpha = 0;
finalComboTxt.y = -50;
tween(finalComboTxt, {
alpha: 1,
y: -100
}, {
duration: 600,
easing: tween.easeOut
});
}, 400);
LK.setTimeout(function () {
finalMissTxt.setText('Misses: ' + missCount);
finalMissTxt.alpha = 0;
finalMissTxt.y = 10;
tween(finalMissTxt, {
alpha: 1,
y: -40
}, {
duration: 600,
easing: tween.easeOut
});
}, 800);
// Remove restart text animation - keep it hidden
restartTxt.alpha = 0;
}
function handleColumnTap(columnIndex) {
// Find the closest arrow in the specified column
var closestArrow = null;
var closestDistance = Infinity;
var closestArrowIndex = -1;
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
if (!arrow.hitTested && arrow.column === columnIndex) {
var distance = Math.abs(arrow.y - TARGET_Y);
if (distance < closestDistance) {
closestDistance = distance;
closestArrow = arrow;
closestArrowIndex = i;
}
}
}
if (closestArrow) {
// Check if arrow is close enough to the target zone to register a hit
// Use a larger detection range (150 pixels) to detect arrows approaching the target
if (closestDistance <= 150) {
var accuracy = checkHit(closestArrow);
if (accuracy) {
// Remove arrow from array and destroy it
closestArrow.destroy();
arrows.splice(closestArrowIndex, 1);
// Process the hit without calling processHit (which also destroys the arrow)
var points = 0;
var comboMultiplier = Math.floor(combo / 10) + 1;
if (accuracy === 'perfect') {
points = 100 * comboMultiplier;
var perfectSound = LK.getSound('hit_perfect');
perfectSound.volume = storage.masterVolume * storage.sfxVolume;
perfectSound.play();
showAccuracyText('PERFECT!', 0x00ff00);
// Extra effect for perfect hits
LK.effects.flashScreen(0x00ff00, 100);
} else if (accuracy === 'good') {
points = 50 * comboMultiplier;
var goodSound = LK.getSound('hit_good');
goodSound.volume = storage.masterVolume * storage.sfxVolume;
goodSound.play();
showAccuracyText('GOOD', 0xffff00);
}
score += points;
combo++;
// Check for rank up
updatePlayerRank(score);
// Increase health bar
healthBar = Math.min(maxHealth, healthBar + healthPerHit);
currentLevelHits++;
// Check if health bar is full to advance to next level
if (healthBar >= maxHealth) {
level++;
currentLevelHits = 0;
// Reset health bar to starting level for next challenge
healthBar = 50;
// Increase speed multiplier progressively with each level
speedMultiplier = Math.min(1.0 + (level - 1) * 0.15, maxSpeedMultiplier);
// Slightly reduce spawn interval for more challenge but keep it balanced
spawnInterval = Math.max(25, baseSpawnInterval - Math.floor((level - 1) * 1.2));
// Show enhanced level up effect with speed notification
showAccuracyText('LEVEL ' + level + '! HEALTH FULL!', 0x00ffff);
LK.effects.flashScreen(0x00ffff, 700);
// Add speed burst visual effect
tween(game, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
// Create multiple hit effects for high combos
var effectCount = Math.min(3, Math.floor(combo / 25) + 1);
for (var i = 0; i < effectCount; i++) {
LK.setTimeout(function () {
var effect = new HitEffect(closestArrow.x + (Math.random() - 0.5) * 50, closestArrow.y + (Math.random() - 0.5) * 50);
game.addChild(effect);
}, i * 50);
}
// Enhanced combo animations with milestone effects
if (combo % 5 === 0 && combo > 0) {
// Create combo effect at hit location
var comboEffect = new ComboEffect(closestArrow.x, closestArrow.y - 100, combo);
game.addChild(comboEffect);
// Scale combo text animation based on milestone
var targetScale = combo >= 50 ? 2.5 : combo >= 25 ? 2.0 : 1.8;
var targetColor = combo >= 100 ? 0xFFD700 : combo >= 50 ? 0xFF6B6B : combo >= 25 ? 0x4ECDC4 : 0x00FFFF;
tween(comboTxt, {
scaleX: targetScale,
scaleY: targetScale,
tint: targetColor
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: combo >= 25 ? 1.2 : 1,
scaleY: combo >= 25 ? 1.2 : 1,
tint: 0xFFD700
}, {
duration: 500,
easing: tween.easeOut
});
}
});
}
updateUI();
} else {
processMiss();
}
} else {
// Arrow exists but is too far from target zone - count as miss
processMiss();
}
} else {
// Tapped when no arrow was in range for this column
processMiss();
}
}
// Input handling
var titleTapCount = 0;
var titleTapTimer = 0;
var rankResetTapCount = 0;
var rankResetTapTimer = 0;
game.down = function (x, y, obj) {
// Start countdown when screen is tapped
if (gameState === 'start') {
// Check for multiple taps on title to reset points
titleTapCount++;
titleTapTimer = 0;
// If tapped 5 times quickly, reset points
if (titleTapCount >= 5) {
resetPlayerPoints();
titleTapCount = 0;
return;
}
// Check for taps on rank shield area to reset rank
if (x >= 1700 && x <= 2048 && y >= 90 && y <= 160) {
rankResetTapCount++;
rankResetTapTimer = 0;
// If tapped rank shield 3 times quickly, reset rank
if (rankResetTapCount >= 3) {
resetPlayerRank();
rankResetTapCount = 0;
return;
}
return; // Prevent starting countdown when tapping rank area
}
startCountdown();
return; // Prevent other actions when start countdown begins
}
if (gameState === 'gameover') {
restartGame();
} else if (gameState === 'playing') {
// x10 is always active, no need to check for button tap
// Determine which column was tapped based on x position
var tappedColumn = -1;
for (var i = 0; i < COLUMN_COUNT; i++) {
var columnLeft = columnPositions[i] - COLUMN_WIDTH / 2;
var columnRight = columnPositions[i] + COLUMN_WIDTH / 2;
if (x >= columnLeft && x <= columnRight) {
tappedColumn = i;
break;
}
}
if (tappedColumn >= 0) {
// Enhanced visual feedback for control zone tap
var controlZone = controlZones[tappedColumn];
var originalAlpha = controlZone.alpha;
var originalScale = controlZone.scaleX;
// Flash and scale effect
controlZone.alpha = 0.8;
controlZone.scaleX = originalScale * 1.1;
controlZone.scaleY = controlZone.scaleY * 1.1;
tween(controlZone, {
alpha: originalAlpha,
scaleX: originalScale,
scaleY: originalScale
}, {
duration: 150,
easing: tween.easeOut
});
// Add ripple effect
var effect = new HitEffect(controlZone.x, controlZone.y - 50);
game.addChild(effect);
handleColumnTap(tappedColumn);
} else {
processMiss();
}
}
};
// Main game loop
game.update = function () {
// Enhanced rocket animations with trails and speed variation
for (var i = 0; i < rockets.length; i++) {
var rocket = rockets[i];
rocket.y -= rocket.speed + Math.sin(LK.ticks * 0.05 + i) * 0.5;
rocket.rotation += 0.02 + Math.sin(LK.ticks * 0.03 + i) * 0.01;
// Enhanced wobble with varying amplitude
rocket.x += Math.sin(LK.ticks * 0.02 + i) * (1 + Math.cos(LK.ticks * 0.01 + i));
// Scale pulsing effect
rocket.scaleX = 0.3 + 0.05 * Math.sin(LK.ticks * 0.08 + i);
rocket.scaleY = 0.8 + 0.1 * Math.sin(LK.ticks * 0.06 + i);
// Color shifting
var colorPhase = LK.ticks * 0.03 + i;
if (i % 2 === 0) {
rocket.alpha = 0.15 + 0.1 * Math.sin(colorPhase);
}
if (rocket.y < -100) {
rocket.y = 2732 + 100;
rocket.x = Math.random() * 2048;
rocket.speed = 1 + Math.random() * 3; // Randomize speed on reset
}
}
// Enhanced confetti with spiral motion and color cycling
for (var i = 0; i < confetti.length; i++) {
var piece = confetti[i];
piece.y -= piece.speed;
piece.rotation += piece.rotationSpeed + Math.sin(LK.ticks * 0.02 + i) * 0.02;
// Enhanced spiral motion
var spiralRadius = 20 + 10 * Math.sin(LK.ticks * 0.015 + i);
piece.x += Math.sin(LK.ticks * 0.01 + i) * 0.5 + Math.cos(LK.ticks * 0.02 + i) * spiralRadius * 0.02;
// Scale animation
piece.scaleX = 0.1 + 0.05 * Math.sin(LK.ticks * 0.05 + i);
piece.scaleY = 0.1 + 0.05 * Math.cos(LK.ticks * 0.04 + i);
// Alpha pulsing
piece.alpha = 0.2 + 0.15 * Math.sin(LK.ticks * 0.06 + i);
if (piece.y < -50) {
piece.y = 2732 + 50;
piece.x = Math.random() * 2048;
}
}
// Enhanced comets with dynamic trails and speed bursts
for (var i = 0; i < comets.length; i++) {
var comet = comets[i];
// Enhanced movement with speed bursts
var speedMultiplier = 1 + 0.5 * Math.sin(LK.ticks * 0.02 + i);
comet.x += Math.cos(comet.rotation) * comet.speed * speedMultiplier;
comet.y += Math.sin(comet.rotation) * comet.speed * speedMultiplier;
// Enhanced trail effect with varying opacity and scale
comet.alpha = 0.15 + 0.15 * Math.sin(LK.ticks * 0.1 + i);
comet.scaleX = 0.6 + 0.2 * Math.sin(LK.ticks * 0.08 + i);
comet.scaleY = 0.2 + 0.1 * Math.cos(LK.ticks * 0.06 + i);
// Slight rotation wobble
comet.rotation += Math.sin(LK.ticks * 0.03 + i) * 0.01;
// Reset position when off screen
if (comet.x < -100 || comet.x > 2148 || comet.y < -100 || comet.y > 2832) {
comet.x = Math.random() * 2048;
comet.y = Math.random() * 2732;
comet.rotation = Math.random() * Math.PI * 2;
comet.speed = 2 + Math.random() * 4; // Randomize speed on reset
}
}
if (gameState === 'loading') {
// Animate loading text
loadingTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks * 0.15);
return;
}
if (gameState === 'start') {
// Reset title tap count after timeout
titleTapTimer++;
if (titleTapTimer > 180) {
// 3 seconds at 60fps
titleTapCount = 0;
titleTapTimer = 0;
}
// Reset rank reset tap count after timeout
rankResetTapTimer++;
if (rankResetTapTimer > 180) {
// 3 seconds at 60fps
rankResetTapCount = 0;
rankResetTapTimer = 0;
}
// Update rank display on start screen
updateUI();
// Animate start text with rainbow effect and enhanced pulsing
startTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks * 0.15);
startTxt.scaleX = 1 + 0.1 * Math.sin(LK.ticks * 0.12);
startTxt.scaleY = 1 + 0.1 * Math.sin(LK.ticks * 0.12);
var colorPhase = LK.ticks * 0.08;
var r = Math.sin(colorPhase) * 127 + 128;
var g = Math.sin(colorPhase + 2) * 127 + 128;
var b = Math.sin(colorPhase + 4) * 127 + 128;
startTxt.fill = Math.floor(r) << 16 | Math.floor(g) << 8 | Math.floor(b);
// Enhanced title animation with floating and rotation
titleTxt.y = -300 + 15 * Math.sin(LK.ticks * 0.08);
titleTxt.rotation = 0.15 * Math.sin(LK.ticks * 0.06);
titleTxt.scaleX = 1 + 0.05 * Math.sin(LK.ticks * 0.1);
titleTxt.scaleY = 1 + 0.05 * Math.sin(LK.ticks * 0.1);
// Enhanced subtitle animation with color shifting
subtitleTxt.alpha = 0.6 + 0.4 * Math.sin(LK.ticks * 0.12);
subtitleTxt.y = 100 + 5 * Math.sin(LK.ticks * 0.1);
var subColorPhase = LK.ticks * 0.06;
var subR = Math.sin(subColorPhase + 1) * 127 + 128;
var subG = Math.sin(subColorPhase + 3) * 127 + 128;
var subB = Math.sin(subColorPhase + 5) * 127 + 128;
subtitleTxt.fill = Math.floor(subR) << 16 | Math.floor(subG) << 8 | Math.floor(subB);
return;
}
if (gameState === 'countdown') {
countdownTimer++;
if (countdownTimer === 1) {
showCountdownNumber();
} else if (countdownTimer >= 60) {
countdownValue--;
if (countdownValue > 0) {
countdownTimer = 0;
showCountdownNumber();
} else {
// Start the game
gameState = 'playing';
gameStarted = true;
countdownTxt.alpha = 0;
LK.playMusic('background_music');
}
}
return;
}
if (gameState === 'gameover') {
// Keep restart text hidden and add animated background effects
restartTxt.alpha = 0;
// Add swirling particles effect during game over
for (var p = 0; p < 3; p++) {
if (Math.random() < 0.1) {
var particle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.3
}));
particle.x = Math.random() * 2048;
particle.y = 2732 + 50;
particle.tint = Math.random() > 0.5 ? 0xFF6B6B : 0x4ECDC4;
tween(particle, {
y: -50,
rotation: Math.PI * 4,
alpha: 0,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
}
return;
}
// Game playing state
// Spawn arrows
arrowSpawnTimer++;
if (arrowSpawnTimer >= spawnInterval) {
spawnArrow();
arrowSpawnTimer = 0;
}
// Update arrows and check for misses
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
// Check if arrow passed the target zone without being hit
if (!arrow.hitTested && arrow.y > TARGET_Y + HIT_ZONE_HEIGHT) {
arrow.hitTested = true;
processMiss();
}
// Remove arrows that are off screen
if (arrow.y > 2732 + 100) {
arrow.destroy();
arrows.splice(i, 1);
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
masterVolume: 0.7,
musicVolume: 0.8,
sfxVolume: 0.8,
graphicsQuality: "high",
playerName: "Player",
currentRank: "noob I",
lifetimeScore: 0,
lastSessionScore: 0
});
/****
* Classes
****/
var Arrow = Container.expand(function (direction, column) {
var self = Container.call(this);
var arrowGraphic = self.attachAsset('arrow_' + direction, {
anchorX: 0.5,
anchorY: 0.5
});
self.direction = direction;
self.column = column;
// Calculate dynamic speed based on current level and speed multiplier
var currentSpeed = baseSpeed * speedMultiplier;
self.speed = Math.min(currentSpeed, baseSpeed * maxSpeedMultiplier); // Apply speed cap
self.hitTested = false;
self.animationPhase = Math.random() * Math.PI * 2; // Random phase for animations
self.pulseSpeed = 0.8 + Math.random() * 0.4; // Random pulse speed
self.glowIntensity = 0.3 + Math.random() * 0.7; // Random glow intensity
self.sparkleTimer = Math.random() * 60; // Random sparkle timing
// Add initial entrance animation
arrowGraphic.scaleX = 0.1;
arrowGraphic.scaleY = 0.1;
arrowGraphic.alpha = 0.5;
tween(arrowGraphic, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300,
easing: tween.bounceOut
});
self.update = function () {
self.y += self.speed;
// Add dynamic pulsing animation
self.animationPhase += self.pulseSpeed * 0.1;
arrowGraphic.scaleX = 1 + 0.1 * Math.sin(self.animationPhase);
arrowGraphic.scaleY = 1 + 0.1 * Math.sin(self.animationPhase + 1);
// Add rotation wobble
arrowGraphic.rotation = 0.1 * Math.sin(self.animationPhase * 0.7);
// Add glow effect
arrowGraphic.alpha = 0.8 + 0.2 * Math.sin(self.animationPhase * 1.5);
// Add sparkle effect periodically
self.sparkleTimer++;
if (self.sparkleTimer >= 30) {
self.sparkleTimer = 0;
var sparkle = LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
});
sparkle.x = self.x + (Math.random() - 0.5) * 60;
sparkle.y = self.y + (Math.random() - 0.5) * 60;
sparkle.tint = self.direction === 'left' ? 0xff4757 : self.direction === 'down' ? 0x5352ed : self.direction === 'up' ? 0x2ed573 : 0xffa502;
game.addChild(sparkle);
tween(sparkle, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0,
y: sparkle.y - 50
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
sparkle.destroy();
}
});
}
};
return self;
});
var ComboEffect = Container.expand(function (x, y, comboValue) {
var self = Container.call(this);
// Create combo burst effect
var comboText = new Text2(comboValue + 'x COMBO!', {
size: 80,
fill: 0xFFD700
});
comboText.anchor.set(0.5, 0.5);
comboText.x = 0;
comboText.y = 0;
comboText.scaleX = 0.1;
comboText.scaleY = 0.1;
comboText.alpha = 1;
self.addChild(comboText);
// Create sparkle effects around combo text
for (var i = 0; i < 8; i++) {
var sparkle = self.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
}));
var angle = i / 8 * Math.PI * 2;
var radius = 80;
sparkle.x = Math.cos(angle) * radius;
sparkle.y = Math.sin(angle) * radius;
sparkle.tint = 0xFFD700;
// Animate sparkles
tween(sparkle, {
scaleX: 0.3,
scaleY: 0.3,
alpha: 0,
x: Math.cos(angle) * (radius + 100),
y: Math.sin(angle) * (radius + 100)
}, {
duration: 1000,
easing: tween.easeOut
});
}
self.x = x;
self.y = y;
// Animate combo text
tween(comboText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboText, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0,
y: -50
}, {
duration: 700,
easing: tween.easeOut
});
}
});
// Destroy after animation
LK.setTimeout(function () {
self.destroy();
}, 1200);
return self;
});
var HitEffect = Container.expand(function (x, y) {
var self = Container.call(this);
var effectGraphic = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8
});
self.x = x;
self.y = y;
// Animate the effect
tween(effectGraphic, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
return self;
});
var MissEffect = Container.expand(function (x, y) {
var self = Container.call(this);
// Create multiple miss text elements that animate outward
var missTexts = [];
var directions = [{
x: -1,
y: -1
}, {
x: 1,
y: -1
}, {
x: -1,
y: 1
}, {
x: 1,
y: 1
}, {
x: 0,
y: -1
}, {
x: 0,
y: 1
}, {
x: -1,
y: 0
}, {
x: 1,
y: 0
}];
for (var i = 0; i < 4; i++) {
var missText = new Text2('MISS', {
size: 60 + Math.random() * 40,
fill: 0xFF3333
});
missText.anchor.set(0.5, 0.5);
missText.x = 0;
missText.y = 0;
missText.alpha = 0.9;
self.addChild(missText);
missTexts.push(missText);
}
self.x = x;
self.y = y;
// Animate miss texts flying outward
for (var i = 0; i < missTexts.length; i++) {
var text = missTexts[i];
var dir = directions[i % directions.length];
var distance = 150 + Math.random() * 100;
tween(text, {
x: dir.x * distance,
y: dir.y * distance,
alpha: 0,
rotation: (Math.random() - 0.5) * Math.PI,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 800,
easing: tween.easeOut
});
}
// Destroy after animation
LK.setTimeout(function () {
self.destroy();
}, 900);
return self;
});
var RankEffect = Container.expand(function (rankData, x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.rankData = rankData;
self.effectTimer = 0;
self.particles = [];
// Create effects based on rank tier
self.createEffects = function () {
var rankName = rankData.name.toLowerCase();
if (rankName.includes('noob')) {
// Noob: Simple subtle glow
self.createBasicGlow();
} else if (rankName.includes('pro')) {
// Pro: Green energy particles
self.createEnergyParticles(0x00ff00, 3);
} else if (rankName.includes('dios')) {
// Dios: Golden aura with sparkles
self.createAura(0xffd700);
self.createSparkles(0xffd700, 5);
} else if (rankName.includes('hacker')) {
// Hacker: Digital glitch effects
self.createGlitchEffect(0xff00ff);
self.createDataStreams(0xff00ff);
} else if (rankName.includes('imparable')) {
// Imparable: Cyan lightning bolts
self.createLightning(0x00ffff);
self.createEnergyField(0x00ffff);
} else if (rankName.includes('leyenda')) {
// Leyenda: Star constellation effect
self.createStarConstellation(0xff8800);
self.createCosmicDust(0xff8800);
} else if (rankName.includes('supremo')) {
// Supremo: Enhanced rainbow prismatic effect with multiple layers
self.createPrismaticEffect();
self.createEnergyWaves(0xff0088);
self.createSupremoAura();
self.createRainbowBurst();
self.createPowerRings();
} else if (rankName.includes('infinito')) {
// Infinito: Basic transcendent effects without infinity symbols
self.createDimensionalRift();
self.createCosmicStorm();
self.createEternityField();
}
};
self.createBasicGlow = function () {
var glow = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.2
});
glow.tint = rankData.color;
tween(glow, {
scaleX: 0.7,
scaleY: 0.7,
alpha: 0.1
}, {
duration: 2000,
easing: tween.easeInOut
});
};
self.createEnergyParticles = function (color, count) {
for (var i = 0; i < count; i++) {
LK.setTimeout(function () {
var particle = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.8
});
particle.tint = color;
var angle = Math.random() * Math.PI * 2;
var distance = 30 + Math.random() * 50;
particle.x = Math.cos(angle) * 20;
particle.y = Math.sin(angle) * 20;
tween(particle, {
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}, i * 200);
}
};
self.createAura = function (color) {
var aura = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.1
});
aura.tint = color;
tween(aura, {
scaleX: 2,
scaleY: 2,
alpha: 0.3,
rotation: Math.PI
}, {
duration: 3000,
easing: tween.easeInOut
});
};
self.createSparkles = function (color, count) {
for (var i = 0; i < count; i++) {
var sparkle = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0
});
sparkle.tint = color;
var radius = 60;
var angle = i / count * Math.PI * 2;
sparkle.x = Math.cos(angle) * radius;
sparkle.y = Math.sin(angle) * radius;
self.particles.push({
obj: sparkle,
angle: angle,
radius: radius,
speed: 0.02 + Math.random() * 0.02
});
}
};
self.createStarConstellation = function (color) {
// Create multiple stars in a constellation pattern
var starPositions = [{
x: 0,
y: -40
}, {
x: 30,
y: -20
}, {
x: 40,
y: 10
}, {
x: 20,
y: 30
}, {
x: -20,
y: 30
}, {
x: -40,
y: 10
}, {
x: -30,
y: -20
}];
for (var i = 0; i < starPositions.length; i++) {
var star = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.6
});
star.x = starPositions[i].x;
star.y = starPositions[i].y;
star.tint = color;
// Make stars twinkle
(function (starObj) {
tween(starObj, {
scaleX: 0.15,
scaleY: 0.15,
alpha: 1
}, {
duration: 500 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(starObj, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.6
}, {
duration: 500 + Math.random() * 1000,
easing: tween.easeInOut
});
}
});
})(star);
}
};
self.createGlitchEffect = function (color) {
var glitchLayers = [];
for (var i = 0; i < 3; i++) {
var glitch = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1,
alpha: 0.2
});
glitch.tint = color;
glitchLayers.push(glitch);
}
// Animate glitch layers
glitchLayers.forEach(function (layer, index) {
tween(layer, {
x: (Math.random() - 0.5) * 10,
y: (Math.random() - 0.5) * 10,
alpha: Math.random() * 0.3
}, {
duration: 100 + Math.random() * 200,
easing: tween.linear,
onFinish: function onFinish() {
tween(layer, {
x: 0,
y: 0,
alpha: 0.2
}, {
duration: 100 + Math.random() * 200,
easing: tween.linear
});
}
});
});
};
self.createDataStreams = function (color) {
// Create vertical data stream effect
for (var i = 0; i < 5; i++) {
LK.setTimeout(function () {
var stream = self.attachAsset('arrow_down', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.3,
alpha: 0.6
});
stream.tint = color;
stream.x = (Math.random() - 0.5) * 80;
stream.y = -40;
tween(stream, {
y: 40,
alpha: 0,
scaleY: 0.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
stream.destroy();
}
});
}, i * 200);
}
};
self.createLightning = function (color) {
var lightning = self.attachAsset('arrow_down', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 2,
alpha: 0
});
lightning.tint = color;
lightning.rotation = Math.random() * Math.PI * 2;
tween(lightning, {
alpha: 0.8,
scaleX: 0.5,
scaleY: 3
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(lightning, {
alpha: 0,
scaleX: 0.1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
lightning.destroy();
}
});
}
});
};
self.createEnergyField = function (color) {
// Create an energy field effect
var field = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.2
});
field.tint = color;
// Animate energy field pulsing
tween(field, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.4,
rotation: Math.PI
}, {
duration: 1500,
easing: tween.easeInOut
});
};
self.createCosmicDust = function (color) {
// Create floating cosmic dust particles
for (var i = 0; i < 8; i++) {
var dust = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.03 + Math.random() * 0.05,
scaleY: 0.03 + Math.random() * 0.05,
alpha: 0.4 + Math.random() * 0.4
});
dust.tint = color;
var radius = 40 + Math.random() * 60;
var angle = Math.random() * Math.PI * 2;
dust.x = Math.cos(angle) * radius;
dust.y = Math.sin(angle) * radius;
// Animate dust floating
tween(dust, {
x: dust.x + (Math.random() - 0.5) * 40,
y: dust.y + (Math.random() - 0.5) * 40,
alpha: 0,
scaleX: 0.01,
scaleY: 0.01
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
dust.destroy();
}
});
}
};
self.createPrismaticEffect = function () {
// Create enhanced rainbow prismatic effect with multiple layers
var colors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x9400D3];
// Create three layers of prisms for depth
for (var layer = 0; layer < 3; layer++) {
for (var i = 0; i < colors.length; i++) {
var prism = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3 + layer * 0.15,
scaleY: 0.3 + layer * 0.15,
alpha: 0.5 - layer * 0.15
});
prism.tint = colors[i];
var angle = i / colors.length * Math.PI * 2 + layer * 0.2;
var radius = 50 + layer * 20;
prism.x = Math.cos(angle) * radius;
prism.y = Math.sin(angle) * radius;
self.particles.push({
obj: prism,
angle: angle,
radius: radius,
speed: 0.03 + layer * 0.01,
layer: layer
});
}
}
// Add central crown effect
var crown = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.4
});
crown.tint = 0xFFD700;
crown.rotation = 0;
self.crownEffect = crown;
};
self.createEnergyWaves = function (color) {
// Create multiple types of expanding energy waves
for (var i = 0; i < 5; i++) {
LK.setTimeout(function () {
var wave = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7
});
wave.tint = color;
// Alternate between circular and diamond shapes
if (i % 2 === 0) {
wave.rotation = Math.PI / 4;
}
tween(wave, {
scaleX: 3,
scaleY: 3,
alpha: 0,
rotation: wave.rotation + Math.PI * 2
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
wave.destroy();
}
});
}, i * 300);
}
// Add pulsing core energy
var core = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
core.tint = 0xFFFFFF;
self.energyCore = core;
};
self.createDimensionalRift = function () {
// Create enhanced swirling dimensional rifts with multiple layers
for (var layer = 0; layer < 3; layer++) {
for (var i = 0; i < 8; i++) {
var rift = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5 - i * 0.15 - layer * 0.3,
scaleY: 0.08 + i * 0.02 + layer * 0.02,
alpha: 0.8 - i * 0.08 - layer * 0.2
});
var colors = [0x9400D3, 0xFF00FF, 0x00FFFF, 0xFFFFFF, 0xFF00FF, 0x00FF00, 0xFFFF00, 0xFF0000];
rift.tint = colors[i % colors.length];
rift.rotation = i * Math.PI / 4 + layer * Math.PI / 8;
// Animate each rift with complex timing and effects
(function (riftObj, index, layerIndex) {
var baseRotation = riftObj.rotation;
tween(riftObj, {
rotation: baseRotation + Math.PI * 8,
scaleX: 0.1,
scaleY: 3.0 - index * 0.2 - layerIndex * 0.3,
alpha: 0.9,
x: Math.cos(baseRotation) * (50 + layerIndex * 30),
y: Math.sin(baseRotation) * (50 + layerIndex * 30)
}, {
duration: 3000 + index * 300 + layerIndex * 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(riftObj, {
rotation: baseRotation + Math.PI * 16,
scaleX: 2.5 - index * 0.15 - layerIndex * 0.3,
scaleY: 0.08 + index * 0.02 + layerIndex * 0.02,
alpha: 0,
x: 0,
y: 0
}, {
duration: 3000 + index * 300 + layerIndex * 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
riftObj.destroy();
}
});
}
});
})(rift, i, layer);
}
}
// Add enhanced space-time distortion waves
self.createSpaceTimeWaves();
// Add enhanced quantum particles
self.createQuantumParticles();
// Add portal vortex effect
self.createPortalVortex();
};
self.createSupremoAura = function () {
// Create multi-layered aura effect
for (var i = 0; i < 4; i++) {
var aura = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2 + i * 0.3,
scaleY: 1.2 + i * 0.3,
alpha: 0.3 - i * 0.05
});
var colors = [0xFF0088, 0xFF00FF, 0x00FFFF, 0xFFFF00];
aura.tint = colors[i % colors.length];
// Animate each aura layer differently
(function (auraObj, index) {
tween(auraObj, {
rotation: Math.PI * 2,
scaleX: auraObj.scaleX + 0.2,
scaleY: auraObj.scaleY + 0.2,
alpha: 0.1
}, {
duration: 3000 + index * 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(auraObj, {
rotation: 0,
scaleX: 1.2 + index * 0.3,
scaleY: 1.2 + index * 0.3,
alpha: 0.3 - index * 0.05
}, {
duration: 3000 + index * 500,
easing: tween.easeInOut
});
}
});
})(aura, i);
}
};
self.createRainbowBurst = function () {
// Create periodic rainbow burst effect
self.rainbowBurstTimer = 0;
};
self.createPowerRings = function () {
// Create rotating power rings around the character
var ringColors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF];
for (var i = 0; i < 6; i++) {
var ring = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 2,
alpha: 0.6
});
ring.tint = ringColors[i];
ring.rotation = i * Math.PI / 3;
self.particles.push({
obj: ring,
angle: i * Math.PI / 3,
radius: 0,
speed: 0.05,
type: 'powerRing'
});
}
};
self.createSpaceTimeWaves = function () {
// Create expanding space-time distortion waves
for (var i = 0; i < 8; i++) {
LK.setTimeout(function () {
var wave = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.9
});
wave.tint = 0xFFFFFF;
tween(wave, {
scaleX: 4,
scaleY: 4,
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
wave.destroy();
}
});
}, i * 400);
}
};
self.createQuantumParticles = function () {
// Create quantum particle field effect
for (var i = 0; i < 20; i++) {
var quantum = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0.8
});
var angle = Math.random() * Math.PI * 2;
var radius = 30 + Math.random() * 70;
quantum.x = Math.cos(angle) * radius;
quantum.y = Math.sin(angle) * radius;
quantum.tint = 0xFFFFFF;
self.particles.push({
obj: quantum,
angle: angle,
radius: radius,
speed: 0.1 + Math.random() * 0.05,
phase: Math.random() * Math.PI * 2,
type: 'quantum'
});
}
};
self.createCosmicStorm = function () {
// Create enhanced swirling cosmic storm effect with multiple particle types
for (var i = 0; i < 30; i++) {
var storm = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05 + Math.random() * 0.15,
scaleY: 0.05 + Math.random() * 0.15,
alpha: 0.8 + Math.random() * 0.2
});
var angle = Math.random() * Math.PI * 2;
var radius = 30 + Math.random() * 100;
storm.x = Math.cos(angle) * radius;
storm.y = Math.sin(angle) * radius;
// Ultra rainbow colors for cosmic storm
var hue = Math.random() * 360;
storm.tint = self.hslToRgb(hue / 360, 1, 0.8);
// Animate storm particles with complex paths
(function (stormObj, initialAngle, initialRadius) {
var orbitRadius = 20 + Math.random() * 60;
var orbitSpeed = 0.02 + Math.random() * 0.04;
tween(stormObj, {
x: Math.cos(initialAngle + Math.PI) * orbitRadius,
y: Math.sin(initialAngle + Math.PI) * orbitRadius,
scaleX: stormObj.scaleX * 2,
scaleY: stormObj.scaleY * 2,
alpha: 0.4,
rotation: Math.PI * 4
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(stormObj, {
x: Math.cos(initialAngle) * initialRadius,
y: Math.sin(initialAngle) * initialRadius,
scaleX: 0.05 + Math.random() * 0.15,
scaleY: 0.05 + Math.random() * 0.15,
alpha: 0.8 + Math.random() * 0.2,
rotation: 0
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut
});
}
});
})(storm, angle, radius);
self.particles.push({
obj: storm,
angle: angle,
radius: radius,
speed: 0.1 + Math.random() * 0.05,
orbitRadius: 20 + Math.random() * 60,
type: 'cosmicStorm'
});
}
};
self.createPortalVortex = function () {
// Create swirling portal vortex effect
for (var v = 0; v < 12; v++) {
var vortex = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.3,
alpha: 0.7
});
var vortexAngle = v / 12 * Math.PI * 2;
var vortexRadius = 80;
vortex.x = Math.cos(vortexAngle) * vortexRadius;
vortex.y = Math.sin(vortexAngle) * vortexRadius;
vortex.tint = 0xFF00FF;
vortex.rotation = vortexAngle + Math.PI / 2;
// Animate vortex spiraling inward
tween(vortex, {
x: Math.cos(vortexAngle) * 10,
y: Math.sin(vortexAngle) * 10,
rotation: vortexAngle + Math.PI * 6,
scaleX: 0.02,
scaleY: 0.1,
alpha: 0
}, {
duration: 2000,
easing: tween.easeIn,
onFinish: function onFinish() {
vortex.destroy();
}
});
}
};
self.createEternityField = function () {
// Create enhanced eternal energy field with multiple layers
var eternityCore = self.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
alpha: 0.3
});
eternityCore.tint = 0xFFFFFF;
self.eternityCore = eternityCore;
// Animate eternity core with pulsing
tween(eternityCore, {
scaleX: 3,
scaleY: 3,
alpha: 0.1,
rotation: Math.PI * 2
}, {
duration: 4000,
easing: tween.easeInOut
});
// Create multiple orbital rings
for (var r = 0; r < 3; r++) {
for (var i = 0; i < 12; i++) {
var orbital = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + r * 0.1,
scaleY: 0.2 + r * 0.1,
alpha: 0.9 - r * 0.2
});
var orbitAngle = i * Math.PI / 6;
var orbitRadius = 60 + r * 40;
orbital.x = Math.cos(orbitAngle) * orbitRadius;
orbital.y = Math.sin(orbitAngle) * orbitRadius;
// Dynamic color for each orbital
var hue = (i * 30 + r * 120) % 360;
orbital.tint = self.hslToRgb(hue / 360, 1, 0.8);
// Animate orbital elements
(function (orbitalObj, angle, radius, ring) {
tween(orbitalObj, {
x: Math.cos(angle + Math.PI * 2) * radius,
y: Math.sin(angle + Math.PI * 2) * radius,
scaleX: orbitalObj.scaleX * 1.5,
scaleY: orbitalObj.scaleY * 1.5,
alpha: orbitalObj.alpha * 0.5,
rotation: Math.PI * 4
}, {
duration: 5000 + ring * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(orbitalObj, {
x: Math.cos(angle) * radius,
y: Math.sin(angle) * radius,
scaleX: 0.2 + ring * 0.1,
scaleY: 0.2 + ring * 0.1,
alpha: 0.9 - ring * 0.2,
rotation: 0
}, {
duration: 5000 + ring * 1000,
easing: tween.easeInOut
});
}
});
})(orbital, orbitAngle, orbitRadius, r);
self.particles.push({
obj: orbital,
angle: orbitAngle,
radius: orbitRadius,
speed: 0.03 + r * 0.02,
type: 'eternal',
pulsePhase: i * Math.PI / 6,
ring: r
});
}
}
// Create energy web connections
for (var w = 0; w < 20; w++) {
var web = self.attachAsset('arrow_down', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 1.5,
alpha: 0.2
});
var webAngle = Math.random() * Math.PI * 2;
var webRadius = 50 + Math.random() * 50;
web.x = Math.cos(webAngle) * webRadius;
web.y = Math.sin(webAngle) * webRadius;
web.rotation = webAngle;
web.tint = 0x00FFFF;
tween(web, {
scaleY: 2.5,
alpha: 0.4
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(web, {
scaleY: 1.5,
alpha: 0.2
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut
});
}
});
}
};
self.update = function () {
// Only animate effects when in menu state (start screen)
if (gameState === 'start') {
// Update particle animations
for (var i = 0; i < self.particles.length; i++) {
var particle = self.particles[i];
if (particle.type === 'powerRing') {
// Rotate power rings
particle.angle += particle.speed;
particle.obj.rotation = particle.angle;
} else if (particle.type === 'quantum') {
// Update quantum particles with phase animation
particle.phase += 0.1;
particle.obj.alpha = 0.5 + 0.3 * Math.sin(particle.phase);
particle.angle += particle.speed;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
} else if (particle.type === 'cosmicStorm') {
// Update cosmic storm particles
particle.angle += particle.speed;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
} else if (particle.type === 'eternal') {
// Update eternal orbital particles
particle.angle += particle.speed;
particle.pulsePhase += 0.05;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
particle.obj.alpha = 0.9 - particle.ring * 0.2 + 0.3 * Math.sin(particle.pulsePhase);
} else {
// Update regular sparkle particles
particle.angle += particle.speed;
particle.obj.x = Math.cos(particle.angle) * particle.radius;
particle.obj.y = Math.sin(particle.angle) * particle.radius;
}
}
// Update special effects based on rank
var rankName = self.rankData.name.toLowerCase();
if (rankName.includes('supremo')) {
// Update crown effect rotation
if (self.crownEffect) {
self.crownEffect.rotation += 0.02;
}
// Update energy core pulsing
if (self.energyCore) {
self.energyCore.alpha = 0.6 + 0.4 * Math.sin(LK.ticks * 0.1);
self.energyCore.scaleX = 0.5 + 0.1 * Math.sin(LK.ticks * 0.08);
self.energyCore.scaleY = 0.5 + 0.1 * Math.sin(LK.ticks * 0.08);
}
} else if (rankName.includes('infinito')) {
// Update eternity core
if (self.eternityCore) {
self.eternityCore.rotation += 0.01;
self.eternityCore.alpha = 0.2 + 0.2 * Math.sin(LK.ticks * 0.05);
}
}
}
// During gameplay (playing, countdown, gameover states), freeze all rank effect animations
// Effects remain completely static to avoid distracting from gameplay
};
// Helper function to convert HSL to RGB
self.hslToRgb = function (h, s, l) {
var r, g, b;
if (s === 0) {
r = g = b = l;
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return Math.floor(r * 255) << 16 | Math.floor(g * 255) << 8 | Math.floor(b * 255);
};
self.createRainbowBurstEffect = function () {
// Create spectacular rainbow burst
var burstCount = 12;
for (var i = 0; i < burstCount; i++) {
var burst = self.attachAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
});
var angle = i / burstCount * Math.PI * 2;
var hue = i / burstCount * 360;
burst.tint = self.hslToRgb(hue / 360, 1, 0.5);
burst.x = 0;
burst.y = 0;
tween(burst, {
x: Math.cos(angle) * 100,
y: Math.sin(angle) * 100,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
burst.destroy();
}
});
}
};
// Initialize effects
self.createEffects();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c2c54
});
/****
* Game Code
****/
// Game constants
var COLUMN_COUNT = 4;
var currentRankEffect = null;
var COLUMN_WIDTH = 2048 / COLUMN_COUNT;
var TARGET_Y = 2400;
var SPAWN_Y = -100;
var HIT_ZONE_HEIGHT = 80;
// Game state
var arrows = [];
var score = 0;
var combo = 0;
var missCount = 0;
var maxMisses = 10;
var gameStarted = false;
var gameState = 'start'; // 'start', 'countdown', 'playing', 'gameover'
var countdownValue = 3;
var countdownTimer = 0;
var arrowSpawnTimer = 0;
var spawnInterval = 45; // frames between arrow spawns
// Health bar system
var healthBar = 50; // Current health (0-100)
var maxHealth = 100;
var healthPerHit = 15; // Health gained per successful hit (increased)
var healthPerMiss = 8; // Health lost per miss (reduced for fairness)
var currentLevelHits = 0; // Current hits in this level (tracking for statistics)
// Level system with progressive speed increases
var level = 1;
var baseSpeed = 8;
var baseSpawnInterval = 45;
var speedMultiplier = 1.0; // Speed multiplier that increases with level
var maxSpeedMultiplier = 3.0; // Maximum speed cap for balance
var scoresPerLevel = [0, 1000, 2500, 5000, 8000, 12000, 17000, 23000, 30000, 40000];
// Direction mapping
var directions = ['left', 'down', 'up', 'right'];
var columnPositions = [];
// Expanded rank system with sub-levels and reduced score requirements
var ranks = [{
name: "noob I",
minScore: 0,
color: 0x666666
}, {
name: "noob II",
minScore: 500,
color: 0x777777
}, {
name: "noob III",
minScore: 1200,
color: 0x888888
}, {
name: "pro I",
minScore: 2000,
color: 0x00aa00
}, {
name: "pro II",
minScore: 3500,
color: 0x00cc00
}, {
name: "pro III",
minScore: 5500,
color: 0x00ff00
}, {
name: "dios I",
minScore: 8000,
color: 0xccaa00
}, {
name: "dios II",
minScore: 12000,
color: 0xddbb00
}, {
name: "dios III",
minScore: 18000,
color: 0xffd700
}, {
name: "hacker I",
minScore: 25000,
color: 0xcc00cc
}, {
name: "hacker II",
minScore: 35000,
color: 0xdd00dd
}, {
name: "hacker III",
minScore: 50000,
color: 0xff00ff
}, {
name: "imparable I",
minScore: 70000,
color: 0x00cccc
}, {
name: "imparable II",
minScore: 95000,
color: 0x00dddd
}, {
name: "imparable III",
minScore: 125000,
color: 0x00ffff
}, {
name: "leyenda I",
minScore: 160000,
color: 0xff6600
}, {
name: "leyenda II",
minScore: 200000,
color: 0xff7700
}, {
name: "leyenda III",
minScore: 250000,
color: 0xff8800
}, {
name: "supremo I",
minScore: 310000,
color: 0xff0066
}, {
name: "supremo II",
minScore: 380000,
color: 0xff0077
}, {
name: "supremo III",
minScore: 460000,
color: 0xff0088
}, {
name: "infinito",
minScore: 550000,
color: 0xffffff
}];
function getRankFromScore(score) {
var currentRank = ranks[0];
for (var i = 0; i < ranks.length; i++) {
if (score >= ranks[i].minScore) {
currentRank = ranks[i];
}
}
return currentRank;
}
function updatePlayerRank(score) {
// Initialize lifetime score if it doesn't exist
if (!storage.lifetimeScore) {
storage.lifetimeScore = 0;
}
// Add current game points to lifetime total
var pointsToAdd = score - (storage.lastSessionScore || 0);
if (pointsToAdd > 0) {
storage.lifetimeScore += pointsToAdd;
storage.lastSessionScore = score;
}
var newRank = getRankFromScore(storage.lifetimeScore);
var oldRankName = storage.currentRank;
if (newRank.name !== oldRankName) {
storage.currentRank = newRank.name;
showRankUpEffect(newRank);
return true;
}
return false;
}
function showRankUpEffect(rank) {
// Create rank up text
var rankUpTxt = new Text2('RANK UP!', {
size: 100,
fill: rank.color
});
rankUpTxt.anchor.set(0.5, 0.5);
rankUpTxt.x = 0;
rankUpTxt.y = -50;
rankUpTxt.alpha = 0;
rankUpTxt.scaleX = 0.1;
rankUpTxt.scaleY = 0.1;
LK.gui.center.addChild(rankUpTxt);
// Create new rank text
var newRankTxt = new Text2(rank.name.toUpperCase(), {
size: 80,
fill: rank.color
});
newRankTxt.anchor.set(0.5, 0.5);
newRankTxt.x = 0;
newRankTxt.y = 50;
newRankTxt.alpha = 0;
newRankTxt.scaleX = 0.1;
newRankTxt.scaleY = 0.1;
LK.gui.center.addChild(newRankTxt);
// Animate rank up effect
tween(rankUpTxt, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(rankUpTxt, {
alpha: 0,
y: -150
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rankUpTxt.destroy();
}
});
}
});
// Animate new rank text
LK.setTimeout(function () {
tween(newRankTxt, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 600,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(newRankTxt, {
alpha: 0,
y: 150
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
newRankTxt.destroy();
}
});
}
});
}, 400);
// Flash screen with rank color
LK.effects.flashScreen(rank.color, 1000);
// Create large animated shield for rank up effect
var rankUpShield = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
rankUpShield.x = 0;
rankUpShield.y = 0;
rankUpShield.tint = rank.color;
LK.gui.center.addChild(rankUpShield);
// Create multiple shield layers for enhanced effect
var shieldLayers = [];
for (var sl = 0; sl < 3; sl++) {
var shieldLayer = LK.getAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
shieldLayer.x = 0;
shieldLayer.y = 0;
shieldLayer.tint = rank.color;
LK.gui.center.addChild(shieldLayer);
shieldLayers.push(shieldLayer);
}
// Animate the main rank up shield
tween(rankUpShield, {
scaleX: 4,
scaleY: 4,
alpha: 1,
rotation: Math.PI * 3
}, {
duration: 1500,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(rankUpShield, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: Math.PI * 6
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
rankUpShield.destroy();
}
});
}
});
// Animate shield layers with staggered timing
for (var sl = 0; sl < shieldLayers.length; sl++) {
(function (layer, index) {
LK.setTimeout(function () {
tween(layer, {
scaleX: 2 + index * 0.5,
scaleY: 2 + index * 0.5,
alpha: 0.7 - index * 0.2,
rotation: Math.PI * (2 + index)
}, {
duration: 1200 + index * 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(layer, {
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: Math.PI * (4 + index * 2)
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
layer.destroy();
}
});
}
});
}, index * 300);
})(shieldLayers[sl], sl);
}
// Create celebration particles
for (var i = 0; i < 20; i++) {
LK.setTimeout(function () {
var particle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0.8
}));
particle.x = 1024 + (Math.random() - 0.5) * 600;
particle.y = 1366 + (Math.random() - 0.5) * 400;
particle.tint = rank.color;
var angle = Math.random() * Math.PI * 2;
var distance = 200 + Math.random() * 300;
tween(particle, {
x: particle.x + Math.cos(angle) * distance,
y: particle.y + Math.sin(angle) * distance,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0,
rotation: Math.PI * 4
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}, i * 50);
}
// Update rank effect to match new rank at shield position
if (currentRankEffect) {
currentRankEffect.destroy();
}
currentRankEffect = new RankEffect(rank, -280, 135);
LK.gui.topRight.addChild(currentRankEffect);
}
// Calculate column positions
for (var i = 0; i < COLUMN_COUNT; i++) {
columnPositions.push(COLUMN_WIDTH * i + COLUMN_WIDTH / 2);
}
// Create target zones
var targetZones = [];
for (var i = 0; i < COLUMN_COUNT; i++) {
var targetZone = game.addChild(LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
targetZone.x = columnPositions[i];
targetZone.y = TARGET_Y;
targetZones.push(targetZone);
// Add enhanced pulsing animation to target zones with particle effects
tween(targetZone, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.6
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(targetZone, {
scaleX: 0.9,
scaleY: 0.9,
alpha: 0.2
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
// Add floating particles around target zone
LK.setTimeout(function () {
for (var p = 0; p < 4; p++) {
var particle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.05,
alpha: 0.8
}));
var angle = p / 4 * Math.PI * 2;
var radius = 80;
particle.x = targetZone.x + Math.cos(angle) * radius;
particle.y = targetZone.y + Math.sin(angle) * radius;
particle.tint = i === 0 ? 0xff4757 : i === 1 ? 0x5352ed : i === 2 ? 0x2ed573 : 0xffa502;
tween(particle, {
scaleX: 0.15,
scaleY: 0.15,
alpha: 0,
x: targetZone.x + Math.cos(angle) * (radius + 60),
y: targetZone.y + Math.sin(angle) * (radius + 60)
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
}, Math.random() * 1000);
}
// Create control zones at bottom of screen
var controlZones = [];
// Position control zones at the very bottom for better mobile reach
var controlZoneY = TARGET_Y + 200; // Moved even further down
// Make the control zones smaller to prevent overlapping but still comfortable for mobile
var controlZoneWidth = COLUMN_WIDTH * 0.9;
var controlZoneHeight = 350; // Reduced height to prevent button overlap
for (var i = 0; i < COLUMN_COUNT; i++) {
var controlZone = game.addChild(LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
// Slightly visible for better feedback
alpha: 0.15,
width: controlZoneWidth,
height: controlZoneHeight
}));
controlZone.x = columnPositions[i];
controlZone.y = controlZoneY;
controlZone.columnIndex = i;
// Color coding for visual feedback
controlZone.tint = i === 0 ? 0xff4757 : i === 1 ? 0x5352ed : i === 2 ? 0x2ed573 : 0xffa502;
controlZones.push(controlZone);
}
// Add decorative background elements
var rockets = [];
var confetti = [];
var comets = [];
// Create rocket decorations
for (var i = 0; i < 8; i++) {
var rocket = game.addChild(LK.getAsset('arrow_up', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.8,
alpha: 0.15
}));
rocket.x = Math.random() * 2048;
rocket.y = Math.random() * 2732;
rocket.speed = 1 + Math.random() * 2;
rocket.tint = Math.random() > 0.5 ? 0xFF6B6B : 0x4ECDC4;
rockets.push(rocket);
}
// Create confetti pieces with enhanced colors
for (var i = 0; i < 25; i++) {
var confettiPiece = game.addChild(LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.2
}));
confettiPiece.x = Math.random() * 2048;
confettiPiece.y = Math.random() * 2732;
confettiPiece.speed = 0.3 + Math.random() * 1;
confettiPiece.rotationSpeed = (Math.random() - 0.5) * 0.1;
var colors = [0xFF6B6B, 0xF9CA24, 0xF0932B, 0xEB4D4B, 0x6C5CE7, 0xFF00FF, 0x00FF00];
confettiPiece.tint = colors[Math.floor(Math.random() * colors.length)];
confetti.push(confettiPiece);
}
// Create comet decorations
for (var i = 0; i < 6; i++) {
var comet = game.addChild(LK.getAsset('arrow_right', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.2,
alpha: 0.25
}));
comet.x = Math.random() * 2048;
comet.y = Math.random() * 2732;
comet.speed = 2 + Math.random() * 3;
comet.rotation = Math.random() * Math.PI * 2;
comet.tint = Math.random() > 0.5 ? 0x00FFFF : 0xFFFFFF;
comets.push(comet);
}
// UI Elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
scoreTxt.x = 50;
scoreTxt.y = 50;
LK.gui.topLeft.addChild(scoreTxt);
var comboTxt = new Text2('Combo: 0', {
size: 50,
fill: 0xFFD700
});
comboTxt.anchor.set(0.5, 0);
comboTxt.x = 0;
comboTxt.y = 120;
LK.gui.top.addChild(comboTxt);
var levelTxt = new Text2('Level: 1', {
size: 50,
fill: 0xFF6B6B
});
levelTxt.anchor.set(1, 0);
levelTxt.x = -50;
levelTxt.y = 50;
LK.gui.topRight.addChild(levelTxt);
// Initialize rank text with stored rank
var storedRank = storage.currentRank || "noob I";
var initialRankData = null;
for (var r = 0; r < ranks.length; r++) {
if (ranks[r].name === storedRank) {
initialRankData = ranks[r];
break;
}
}
if (!initialRankData) {
initialRankData = ranks[0]; // fallback to noob
}
var rankTxt = new Text2('RANK: ' + initialRankData.name.toUpperCase(), {
size: 45,
fill: initialRankData.color
});
rankTxt.anchor.set(1, 0);
rankTxt.x = -50;
rankTxt.y = 110;
LK.gui.topRight.addChild(rankTxt);
// Add shield icon next to rank text with enhanced properties
var rankShield = LK.getAsset('shield', {
anchorX: 1,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
rankShield.x = -280;
rankShield.y = 135;
rankShield.tint = initialRankData.color;
rankShield.rotation = 0;
LK.gui.topRight.addChild(rankShield);
// Add shield glow effect
var shieldGlow = LK.getAsset('shield', {
anchorX: 1,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.3
});
shieldGlow.x = -280;
shieldGlow.y = 135;
shieldGlow.tint = initialRankData.color;
LK.gui.topRight.addChild(shieldGlow);
// Create initial rank effect on GUI at shield position
currentRankEffect = new RankEffect(initialRankData, -280, 135);
LK.gui.topRight.addChild(currentRankEffect);
// Start shield glow animation
tween(shieldGlow, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0.1
}, {
duration: 1500,
easing: tween.easeInOut
});
// Health bar UI with improved visuals
var healthBarBg = LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.8,
alpha: 0.6
});
healthBarBg.tint = 0x333333;
healthBarBg.x = 0;
healthBarBg.y = 150;
LK.gui.top.addChild(healthBarBg);
var healthBarBorder = LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4.2,
scaleY: 1,
alpha: 0.8
});
healthBarBorder.tint = 0xFFFFFF;
healthBarBorder.x = 0;
healthBarBorder.y = 150;
LK.gui.top.addChild(healthBarBorder);
var healthBarFill = LK.getAsset('target_zone', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.6
});
healthBarFill.tint = 0x00FF88;
healthBarFill.x = -280;
healthBarFill.y = 150;
LK.gui.top.addChild(healthBarFill);
var healthTxt = new Text2('FILL TO LEVEL UP', {
size: 45,
fill: 0xFFFFFF
});
healthTxt.anchor.set(0.5, 0.5);
healthTxt.x = 0;
healthTxt.y = 200;
LK.gui.top.addChild(healthTxt);
var accuracyTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
accuracyTxt.anchor.set(0.5, 0.5);
accuracyTxt.x = 0;
accuracyTxt.y = -200;
accuracyTxt.alpha = 0;
LK.gui.center.addChild(accuracyTxt);
// Start screen elements
var startTxt = new Text2('TAP TO START', {
size: 100,
fill: 0xFFFFFF
});
startTxt.anchor.set(0.5, 0.5);
startTxt.x = 0;
startTxt.y = 0;
LK.gui.center.addChild(startTxt);
var titleTxt = new Text2('RHYTHM ARROWS', {
size: 120,
fill: 0x00FFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 0;
titleTxt.y = -300;
LK.gui.center.addChild(titleTxt);
var subtitleTxt = new Text2('Hit the arrows in rhythm!', {
size: 50,
fill: 0xFFD700
});
subtitleTxt.anchor.set(0.5, 0.5);
subtitleTxt.x = 0;
subtitleTxt.y = 100;
LK.gui.center.addChild(subtitleTxt);
var countdownTxt = new Text2('3', {
size: 200,
fill: 0xFFD700
});
countdownTxt.anchor.set(0.5, 0.5);
countdownTxt.x = 0;
countdownTxt.y = 0;
countdownTxt.alpha = 0;
LK.gui.center.addChild(countdownTxt);
// Game over screen elements
var gameOverTxt = new Text2('GAME OVER', {
size: 100,
fill: 0xFF0000
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.x = 0;
gameOverTxt.y = -200;
gameOverTxt.alpha = 0;
LK.gui.center.addChild(gameOverTxt);
var finalComboTxt = new Text2('', {
size: 60,
fill: 0xFFD700
});
finalComboTxt.anchor.set(0.5, 0.5);
finalComboTxt.x = 0;
finalComboTxt.y = -100;
finalComboTxt.alpha = 0;
LK.gui.center.addChild(finalComboTxt);
var finalMissTxt = new Text2('', {
size: 60,
fill: 0xFF6B6B
});
finalMissTxt.anchor.set(0.5, 0.5);
finalMissTxt.x = 0;
finalMissTxt.y = -40;
finalMissTxt.alpha = 0;
LK.gui.center.addChild(finalMissTxt);
var restartTxt = new Text2('TAP TO PLAY AGAIN', {
size: 80,
fill: 0x00FFFF
});
restartTxt.anchor.set(0.5, 0.5);
restartTxt.x = 0;
restartTxt.y = 100;
restartTxt.alpha = 0;
LK.gui.center.addChild(restartTxt);
// Loading screen elements
var loadingTxt = new Text2('LOADING...', {
size: 80,
fill: 0x00FFFF
});
loadingTxt.anchor.set(0.5, 0.5);
loadingTxt.x = 0;
loadingTxt.y = 0;
loadingTxt.alpha = 0;
LK.gui.center.addChild(loadingTxt);
var loadingBar = LK.getAsset('target_zone', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0.5,
alpha: 0
});
loadingBar.tint = 0x00FFFF;
loadingBar.x = 0;
loadingBar.y = 100;
LK.gui.center.addChild(loadingBar);
// Reset Points Function
function resetPlayerPoints() {
// Reset lifetime score to zero
storage.lifetimeScore = 0;
// Reset session score
storage.lastSessionScore = 0;
// Reset current rank to noob I (not hacker)
storage.currentRank = "noob I";
// Also reset the current game score to 0
score = 0;
// Update UI to reflect changes
updateUI();
// Show confirmation message
showAccuracyText('SCORE & RANK RESET!', 0xff0000);
LK.effects.flashScreen(0xff0000, 500);
}
// Reset Rank Function
function resetPlayerRank() {
// Reset current rank to noob I
storage.currentRank = "noob I";
// Update UI to reflect rank change
updateUI();
// Show confirmation message
showAccuracyText('RANK RESET TO NOOB I!', 0xff0000);
LK.effects.flashScreen(0xff0000, 500);
}
// Functions
function spawnArrow() {
var column = Math.floor(Math.random() * COLUMN_COUNT);
var direction = directions[column];
var arrow = new Arrow(direction, column);
arrow.x = columnPositions[column];
arrow.y = SPAWN_Y;
arrows.push(arrow);
game.addChild(arrow);
// Add enhanced entrance trail effect based on speed level
var trailCount = Math.min(5, 3 + Math.floor(speedMultiplier));
for (var t = 0; t < trailCount; t++) {
var trail = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + speedMultiplier * 0.1,
scaleY: 0.2 + speedMultiplier * 0.1,
alpha: 0.6 + speedMultiplier * 0.2
}));
trail.x = arrow.x + (Math.random() - 0.5) * (40 + speedMultiplier * 10);
trail.y = arrow.y - t * (30 + speedMultiplier * 5);
trail.tint = direction === 'left' ? 0xff4757 : direction === 'down' ? 0x5352ed : direction === 'up' ? 0x2ed573 : 0xffa502;
// Add intensity glow for higher speeds
if (speedMultiplier > 1.5) {
trail.tint = 0xFFFFFF; // White hot trail for high speeds
}
var trailDuration = Math.max(400, 600 - speedMultiplier * 50);
tween(trail, {
scaleX: 0.05,
scaleY: 0.05,
alpha: 0,
y: trail.y + (100 + speedMultiplier * 20)
}, {
duration: trailDuration,
easing: tween.easeOut,
onFinish: function onFinish() {
trail.destroy();
}
});
}
// Add entrance burst effect
var burst = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
}));
burst.x = arrow.x;
burst.y = arrow.y;
burst.tint = 0xFFFFFF;
tween(burst, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
burst.destroy();
}
});
}
function checkHit(arrow) {
var distance = Math.abs(arrow.y - TARGET_Y);
if (distance <= HIT_ZONE_HEIGHT * 0.8) {
// Perfect hit - more generous perfect zone
return 'perfect';
} else if (distance <= HIT_ZONE_HEIGHT * 3) {
// Good hit - much more generous good zone
return 'good';
}
return null;
}
function processHit(arrow, accuracy) {
var points = 0;
var comboMultiplier = Math.floor(combo / 10) + 1;
if (accuracy === 'perfect') {
points = 100 * comboMultiplier;
var perfectSound = LK.getSound('hit_perfect');
perfectSound.volume = storage.masterVolume * storage.sfxVolume;
perfectSound.play();
showAccuracyText('PERFECT!', 0x00ff00);
// Extra effect for perfect hits
LK.effects.flashScreen(0x00ff00, 100);
} else if (accuracy === 'good') {
points = 50 * comboMultiplier;
var goodSound = LK.getSound('hit_good');
goodSound.volume = storage.masterVolume * storage.sfxVolume;
goodSound.play();
showAccuracyText('GOOD', 0xffff00);
}
score += points;
combo++;
// Check for rank up
updatePlayerRank(score);
// Increase health bar
healthBar = Math.min(maxHealth, healthBar + healthPerHit);
currentLevelHits++;
// Check if health bar is full to advance to next level
if (healthBar >= maxHealth) {
level++;
currentLevelHits = 0;
// Reset health bar to starting level for next challenge
healthBar = 50;
// Increase speed multiplier progressively with each level
speedMultiplier = Math.min(1.0 + (level - 1) * 0.15, maxSpeedMultiplier);
// Slightly reduce spawn interval for more challenge but keep it balanced
spawnInterval = Math.max(25, baseSpawnInterval - Math.floor((level - 1) * 1.2));
// Show enhanced level up effect with speed notification
showAccuracyText('LEVEL ' + level + '! HEALTH FULL!', 0x00ffff);
LK.effects.flashScreen(0x00ffff, 700);
// Add speed burst visual effect
tween(game, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
// Create multiple hit effects for high combos
var effectCount = Math.min(3, Math.floor(combo / 25) + 1);
for (var i = 0; i < effectCount; i++) {
LK.setTimeout(function () {
var effect = new HitEffect(arrow.x + (Math.random() - 0.5) * 50, arrow.y + (Math.random() - 0.5) * 50);
game.addChild(effect);
}, i * 50);
}
// Enhanced combo system with multiple milestone effects
if (combo % 5 === 0 && combo > 0) {
// Create spectacular combo effect
var comboEffect = new ComboEffect(arrow.x, arrow.y - 100, combo);
game.addChild(comboEffect);
// Different effects based on combo milestones
if (combo >= 100) {
// LEGENDARY combo (100+)
LK.effects.flashScreen(0xFFD700, 800);
tween(comboTxt, {
scaleX: 3,
scaleY: 3,
tint: 0xFFD700,
rotation: 0.5
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFD700,
rotation: 0
}, {
duration: 600,
easing: tween.easeOut
});
}
});
} else if (combo >= 50) {
// EPIC combo (50+)
LK.effects.flashScreen(0xFF6B6B, 500);
tween(comboTxt, {
scaleX: 2.5,
scaleY: 2.5,
tint: 0xFF6B6B,
rotation: 0.3
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFFD700,
rotation: 0
}, {
duration: 500,
easing: tween.easeOut
});
}
});
} else if (combo >= 25) {
// SUPER combo (25+)
LK.effects.flashScreen(0x4ECDC4, 300);
tween(comboTxt, {
scaleX: 2.2,
scaleY: 2.2,
tint: 0x4ECDC4
}, {
duration: 350,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFD700
}, {
duration: 450,
easing: tween.easeOut
});
}
});
} else {
// Regular combo milestone (5, 10, 15, 20)
tween(comboTxt, {
scaleX: 2,
scaleY: 2,
tint: 0x00FFFF
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: 1,
scaleY: 1,
tint: 0xFFD700
}, {
duration: 400,
easing: tween.easeOut
});
}
});
}
// Create confetti burst for high combos
if (combo >= 25) {
var confettiCount = Math.min(30, combo / 2);
for (var c = 0; c < confettiCount; c++) {
LK.setTimeout(function () {
var burstConfetti = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.15,
alpha: 0.9
}));
burstConfetti.x = arrow.x + (Math.random() - 0.5) * 300;
burstConfetti.y = arrow.y + (Math.random() - 0.5) * 300;
var colors = [0xFF6B6B, 0x4ECDC4, 0x45B7D1, 0xF9CA24, 0xF0932B, 0xFFD700, 0x00FFFF];
burstConfetti.tint = colors[Math.floor(Math.random() * colors.length)];
tween(burstConfetti, {
y: burstConfetti.y - 400,
x: burstConfetti.x + (Math.random() - 0.5) * 200,
alpha: 0,
rotation: Math.random() * Math.PI * 4,
scaleX: 0.02,
scaleY: 0.02
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
burstConfetti.destroy();
}
});
}, c * 20);
}
}
// Create firework effect for mega combos
if (combo >= 75) {
for (var f = 0; f < 5; f++) {
var firework = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
}));
firework.x = arrow.x + (Math.random() - 0.5) * 400;
firework.y = arrow.y + (Math.random() - 0.5) * 200;
firework.tint = 0xFFFFFF;
tween(firework, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
firework.destroy();
}
});
}
}
}
// Remove arrow
arrow.destroy();
var arrowIndex = arrows.indexOf(arrow);
if (arrowIndex > -1) {
arrows.splice(arrowIndex, 1);
}
updateUI();
}
function processMiss() {
combo = 0;
missCount++;
var missSound = LK.getSound('miss');
missSound.volume = storage.masterVolume * storage.sfxVolume;
missSound.play();
showAccuracyText('MISS', 0xff0000);
// Create miss effect animation instead of screen flash
var missEffect = new MissEffect(1024, 1366);
game.addChild(missEffect);
// Shake the screen slightly for miss feedback
tween(game, {
x: 10
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: -10
}, {
duration: 50,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
x: 0
}, {
duration: 100,
easing: tween.easeOut
});
}
});
}
});
// Decrease health bar
healthBar = Math.max(0, healthBar - healthPerMiss);
// Check if health bar reached 0
if (healthBar <= 0) {
showGameOverScreen();
}
updateUI();
}
function showAccuracyText(text, color) {
accuracyTxt.setText(text);
accuracyTxt.fill = color;
accuracyTxt.alpha = 1;
accuracyTxt.scaleX = 0.2;
accuracyTxt.scaleY = 0.2;
accuracyTxt.rotation = 0;
// Create spectacular effects based on accuracy type
if (text === 'PERFECT!') {
// Create burst of sparkles for perfect hits
for (var s = 0; s < 12; s++) {
var sparkle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 1
}));
sparkle.x = 1024;
sparkle.y = 1366;
sparkle.tint = s % 2 === 0 ? 0x00FFFF : 0xFFD700;
var angle = s / 12 * Math.PI * 2;
var distance = 200 + Math.random() * 100;
tween(sparkle, {
x: 1024 + Math.cos(angle) * distance,
y: 1366 + Math.sin(angle) * distance,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
sparkle.destroy();
}
});
}
// Enhanced perfect text animation
tween(accuracyTxt, {
scaleX: 2.5,
scaleY: 2.5,
rotation: 0.5
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(accuracyTxt, {
scaleX: 1.8,
scaleY: 1.8,
rotation: 0,
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}
});
} else if (text === 'GOOD') {
// Create ring effect for good hits
for (var r = 0; r < 8; r++) {
var ring = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0.8
}));
ring.x = 1024;
ring.y = 1366;
ring.tint = 0xFFFF00;
var ringAngle = r / 8 * Math.PI * 2;
var ringDistance = 120;
tween(ring, {
x: 1024 + Math.cos(ringAngle) * ringDistance,
y: 1366 + Math.sin(ringAngle) * ringDistance,
scaleX: 0.4,
scaleY: 0.4,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
ring.destroy();
}
});
}
// Enhanced good text animation
tween(accuracyTxt, {
scaleX: 2.0,
scaleY: 2.0,
rotation: 0.2
}, {
duration: 250,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(accuracyTxt, {
scaleX: 1.2,
scaleY: 1.2,
rotation: 0,
alpha: 0
}, {
duration: 700,
easing: tween.easeOut
});
}
});
} else {
// Default animation for MISS
tween(accuracyTxt, {
scaleX: 1.5,
scaleY: 1.5,
rotation: text === 'PERFECT!' ? 0.3 : 0.1
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(accuracyTxt, {
scaleX: 1,
scaleY: 1,
rotation: 0,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut
});
}
});
}
}
function updateUI() {
scoreTxt.setText('Score: ' + score);
comboTxt.setText('Combo: ' + combo);
levelTxt.setText('Level: ' + level);
// Update rank display using stored rank
var storedRank = storage.currentRank || "noob I";
var currentRankData = null;
for (var r = 0; r < ranks.length; r++) {
if (ranks[r].name === storedRank) {
currentRankData = ranks[r];
break;
}
}
if (!currentRankData) {
currentRankData = ranks[0]; // fallback to noob
}
// Find next rank for progress display
var nextRank = null;
for (var nr = 0; nr < ranks.length; nr++) {
if (ranks[nr].minScore > (storage.lifetimeScore || 0)) {
nextRank = ranks[nr];
break;
}
}
var rankDisplayText = 'RANK: ' + currentRankData.name.toUpperCase();
if (nextRank) {
var progress = storage.lifetimeScore || 0;
var needed = nextRank.minScore - progress;
rankDisplayText += ' (' + needed + ' TO ' + nextRank.name.toUpperCase() + ')';
}
rankTxt.setText(rankDisplayText);
rankTxt.fill = currentRankData.color;
// Update shield color to match current rank
rankShield.tint = currentRankData.color;
shieldGlow.tint = currentRankData.color;
// Add enhanced pulsing animation to shield for visual appeal
tween(rankShield, {
scaleX: 1.1,
scaleY: 1.1,
alpha: 1,
rotation: 0.1
}, {
duration: 1000,
easing: tween.easeInOut
});
// Add rotation effect for higher ranks
if (currentRankData.name.includes('dios') || currentRankData.name.includes('hacker') || currentRankData.name.includes('imparable') || currentRankData.name.includes('leyenda') || currentRankData.name.includes('supremo') || currentRankData.name.includes('infinito')) {
tween(rankShield, {
rotation: rankShield.rotation + Math.PI * 2
}, {
duration: 3000,
easing: tween.easeInOut
});
}
// Update health bar with smooth animation
var healthPercent = healthBar / maxHealth;
var targetScaleX = 2 * healthPercent;
// Animate health bar changes smoothly
tween(healthBarFill, {
scaleX: targetScaleX
}, {
duration: 300,
easing: tween.easeOut
});
// Change color based on health level with smooth transitions
var targetColor;
if (healthPercent > 0.7) {
targetColor = 0x00FF88; // Bright green
} else if (healthPercent > 0.5) {
targetColor = 0x88FF00; // Yellow-green
} else if (healthPercent > 0.3) {
targetColor = 0xFFAA00; // Orange
} else {
targetColor = 0xFF3333; // Bright red
}
// Animate color change
tween(healthBarFill, {
tint: targetColor
}, {
duration: 200,
easing: tween.easeOut
});
// Pulse effect when health is critical
if (healthPercent <= 0.2) {
tween(healthBarFill, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeInOut
});
}
}
function startCountdown() {
gameState = 'countdown';
startTxt.alpha = 0;
// Fade out title elements
tween(titleTxt, {
alpha: 0,
y: -400
}, {
duration: 800,
easing: tween.easeOut
});
tween(subtitleTxt, {
alpha: 0,
y: 200
}, {
duration: 800,
easing: tween.easeOut
});
countdownTxt.alpha = 1;
countdownValue = 3;
countdownTimer = 0;
}
function showCountdownNumber() {
if (countdownValue > 0) {
countdownTxt.setText(countdownValue.toString());
countdownTxt.fill = countdownValue === 3 ? 0xFF0000 : countdownValue === 2 ? 0xFFA500 : 0x00FF00;
} else {
countdownTxt.setText('GO!');
countdownTxt.fill = 0x00FFFF;
}
countdownTxt.scaleX = 3;
countdownTxt.scaleY = 3;
countdownTxt.rotation = 0.5;
countdownTxt.alpha = 1;
// Animate scale down with bounce effect
tween(countdownTxt, {
scaleX: 1,
scaleY: 1,
rotation: 0
}, {
duration: 800,
easing: tween.bounceOut
});
// Animate alpha fade out after scale animation
LK.setTimeout(function () {
tween(countdownTxt, {
alpha: 0
}, {
duration: 200,
easing: tween.easeOut
});
}, 600);
}
function restartGame() {
// Show loading screen first
gameState = 'loading';
loadingTxt.alpha = 1;
loadingBar.alpha = 1;
loadingBar.scaleX = 0;
// Animate loading bar
tween(loadingBar, {
scaleX: 3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// After loading, reset game
// Reset all game variables
score = 0;
combo = 0;
missCount = 0;
level = 1;
healthBar = 50;
currentLevelHits = 0;
arrowSpawnTimer = 0;
spawnInterval = 45;
speedMultiplier = 1.0; // Reset speed multiplier to base level
gameState = 'start';
gameStarted = false;
// Reset session score tracking but keep lifetime score and current rank
storage.lastSessionScore = 0;
// Clear all arrows
for (var i = arrows.length - 1; i >= 0; i--) {
arrows[i].destroy();
}
arrows = [];
// Stop music
LK.stopMusic();
// Reset UI visibility - show title again
startTxt.alpha = 1;
titleTxt.alpha = 1; // Show title again
titleTxt.y = -300;
titleTxt.rotation = 0;
subtitleTxt.alpha = 1;
subtitleTxt.y = 100;
countdownTxt.alpha = 0;
// Properly hide all game over elements
gameOverTxt.alpha = 0;
gameOverTxt.scaleX = 1;
gameOverTxt.scaleY = 1;
finalComboTxt.alpha = 0;
finalComboTxt.y = -100;
finalMissTxt.alpha = 0;
finalMissTxt.y = -40;
restartTxt.alpha = 0;
accuracyTxt.alpha = 0;
// Stop any active tweens on game over elements
tween.stop(gameOverTxt);
tween.stop(finalComboTxt);
tween.stop(finalMissTxt);
tween.stop(restartTxt);
// Hide loading screen
loadingTxt.alpha = 0;
loadingBar.alpha = 0;
// Reset shield to stored rank state
var currentStoredRank = storage.currentRank || "noob";
var currentStoredRankData = null;
for (var sr = 0; sr < ranks.length; sr++) {
if (ranks[sr].name === currentStoredRank) {
currentStoredRankData = ranks[sr];
break;
}
}
if (!currentStoredRankData) {
currentStoredRankData = ranks[0]; // fallback to noob
}
rankShield.tint = currentStoredRankData.color;
rankShield.scaleX = 1.0;
rankShield.scaleY = 1.0;
rankShield.alpha = 1.0;
rankShield.rotation = 0;
// Reset shield glow
shieldGlow.tint = currentStoredRankData.color;
shieldGlow.scaleX = 1.2;
shieldGlow.scaleY = 1.2;
shieldGlow.alpha = 0.3;
// Update UI
updateUI();
// Keep existing rank effect if it matches current rank
if (currentRankEffect && currentRankEffect.rankData.name !== currentStoredRankData.name) {
currentRankEffect.destroy();
currentRankEffect = null;
}
// Only create new rank effect if it doesn't exist
if (!currentRankEffect) {
currentRankEffect = new RankEffect(currentStoredRankData, -280, 135);
LK.gui.topRight.addChild(currentRankEffect);
}
}
});
}
function showGameOverScreen() {
gameState = 'gameover';
// Flash screen with dramatic effect
LK.effects.flashScreen(0x8B0000, 1000);
// Show game over elements with staggered animations
gameOverTxt.alpha = 0;
gameOverTxt.scaleX = 0.1;
gameOverTxt.scaleY = 0.1;
tween(gameOverTxt, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.bounceOut
});
// Animate final stats with delay
LK.setTimeout(function () {
finalComboTxt.setText('Level Reached: ' + level);
finalComboTxt.alpha = 0;
finalComboTxt.y = -50;
tween(finalComboTxt, {
alpha: 1,
y: -100
}, {
duration: 600,
easing: tween.easeOut
});
}, 400);
LK.setTimeout(function () {
finalMissTxt.setText('Misses: ' + missCount);
finalMissTxt.alpha = 0;
finalMissTxt.y = 10;
tween(finalMissTxt, {
alpha: 1,
y: -40
}, {
duration: 600,
easing: tween.easeOut
});
}, 800);
// Remove restart text animation - keep it hidden
restartTxt.alpha = 0;
}
function handleColumnTap(columnIndex) {
// Find the closest arrow in the specified column
var closestArrow = null;
var closestDistance = Infinity;
var closestArrowIndex = -1;
for (var i = 0; i < arrows.length; i++) {
var arrow = arrows[i];
if (!arrow.hitTested && arrow.column === columnIndex) {
var distance = Math.abs(arrow.y - TARGET_Y);
if (distance < closestDistance) {
closestDistance = distance;
closestArrow = arrow;
closestArrowIndex = i;
}
}
}
if (closestArrow) {
// Check if arrow is close enough to the target zone to register a hit
// Use a larger detection range (150 pixels) to detect arrows approaching the target
if (closestDistance <= 150) {
var accuracy = checkHit(closestArrow);
if (accuracy) {
// Remove arrow from array and destroy it
closestArrow.destroy();
arrows.splice(closestArrowIndex, 1);
// Process the hit without calling processHit (which also destroys the arrow)
var points = 0;
var comboMultiplier = Math.floor(combo / 10) + 1;
if (accuracy === 'perfect') {
points = 100 * comboMultiplier;
var perfectSound = LK.getSound('hit_perfect');
perfectSound.volume = storage.masterVolume * storage.sfxVolume;
perfectSound.play();
showAccuracyText('PERFECT!', 0x00ff00);
// Extra effect for perfect hits
LK.effects.flashScreen(0x00ff00, 100);
} else if (accuracy === 'good') {
points = 50 * comboMultiplier;
var goodSound = LK.getSound('hit_good');
goodSound.volume = storage.masterVolume * storage.sfxVolume;
goodSound.play();
showAccuracyText('GOOD', 0xffff00);
}
score += points;
combo++;
// Check for rank up
updatePlayerRank(score);
// Increase health bar
healthBar = Math.min(maxHealth, healthBar + healthPerHit);
currentLevelHits++;
// Check if health bar is full to advance to next level
if (healthBar >= maxHealth) {
level++;
currentLevelHits = 0;
// Reset health bar to starting level for next challenge
healthBar = 50;
// Increase speed multiplier progressively with each level
speedMultiplier = Math.min(1.0 + (level - 1) * 0.15, maxSpeedMultiplier);
// Slightly reduce spawn interval for more challenge but keep it balanced
spawnInterval = Math.max(25, baseSpawnInterval - Math.floor((level - 1) * 1.2));
// Show enhanced level up effect with speed notification
showAccuracyText('LEVEL ' + level + '! HEALTH FULL!', 0x00ffff);
LK.effects.flashScreen(0x00ffff, 700);
// Add speed burst visual effect
tween(game, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
// Create multiple hit effects for high combos
var effectCount = Math.min(3, Math.floor(combo / 25) + 1);
for (var i = 0; i < effectCount; i++) {
LK.setTimeout(function () {
var effect = new HitEffect(closestArrow.x + (Math.random() - 0.5) * 50, closestArrow.y + (Math.random() - 0.5) * 50);
game.addChild(effect);
}, i * 50);
}
// Enhanced combo animations with milestone effects
if (combo % 5 === 0 && combo > 0) {
// Create combo effect at hit location
var comboEffect = new ComboEffect(closestArrow.x, closestArrow.y - 100, combo);
game.addChild(comboEffect);
// Scale combo text animation based on milestone
var targetScale = combo >= 50 ? 2.5 : combo >= 25 ? 2.0 : 1.8;
var targetColor = combo >= 100 ? 0xFFD700 : combo >= 50 ? 0xFF6B6B : combo >= 25 ? 0x4ECDC4 : 0x00FFFF;
tween(comboTxt, {
scaleX: targetScale,
scaleY: targetScale,
tint: targetColor
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboTxt, {
scaleX: combo >= 25 ? 1.2 : 1,
scaleY: combo >= 25 ? 1.2 : 1,
tint: 0xFFD700
}, {
duration: 500,
easing: tween.easeOut
});
}
});
}
updateUI();
} else {
processMiss();
}
} else {
// Arrow exists but is too far from target zone - count as miss
processMiss();
}
} else {
// Tapped when no arrow was in range for this column
processMiss();
}
}
// Input handling
var titleTapCount = 0;
var titleTapTimer = 0;
var rankResetTapCount = 0;
var rankResetTapTimer = 0;
game.down = function (x, y, obj) {
// Start countdown when screen is tapped
if (gameState === 'start') {
// Check for multiple taps on title to reset points
titleTapCount++;
titleTapTimer = 0;
// If tapped 5 times quickly, reset points
if (titleTapCount >= 5) {
resetPlayerPoints();
titleTapCount = 0;
return;
}
// Check for taps on rank shield area to reset rank
if (x >= 1700 && x <= 2048 && y >= 90 && y <= 160) {
rankResetTapCount++;
rankResetTapTimer = 0;
// If tapped rank shield 3 times quickly, reset rank
if (rankResetTapCount >= 3) {
resetPlayerRank();
rankResetTapCount = 0;
return;
}
return; // Prevent starting countdown when tapping rank area
}
startCountdown();
return; // Prevent other actions when start countdown begins
}
if (gameState === 'gameover') {
restartGame();
} else if (gameState === 'playing') {
// x10 is always active, no need to check for button tap
// Determine which column was tapped based on x position
var tappedColumn = -1;
for (var i = 0; i < COLUMN_COUNT; i++) {
var columnLeft = columnPositions[i] - COLUMN_WIDTH / 2;
var columnRight = columnPositions[i] + COLUMN_WIDTH / 2;
if (x >= columnLeft && x <= columnRight) {
tappedColumn = i;
break;
}
}
if (tappedColumn >= 0) {
// Enhanced visual feedback for control zone tap
var controlZone = controlZones[tappedColumn];
var originalAlpha = controlZone.alpha;
var originalScale = controlZone.scaleX;
// Flash and scale effect
controlZone.alpha = 0.8;
controlZone.scaleX = originalScale * 1.1;
controlZone.scaleY = controlZone.scaleY * 1.1;
tween(controlZone, {
alpha: originalAlpha,
scaleX: originalScale,
scaleY: originalScale
}, {
duration: 150,
easing: tween.easeOut
});
// Add ripple effect
var effect = new HitEffect(controlZone.x, controlZone.y - 50);
game.addChild(effect);
handleColumnTap(tappedColumn);
} else {
processMiss();
}
}
};
// Main game loop
game.update = function () {
// Enhanced rocket animations with trails and speed variation
for (var i = 0; i < rockets.length; i++) {
var rocket = rockets[i];
rocket.y -= rocket.speed + Math.sin(LK.ticks * 0.05 + i) * 0.5;
rocket.rotation += 0.02 + Math.sin(LK.ticks * 0.03 + i) * 0.01;
// Enhanced wobble with varying amplitude
rocket.x += Math.sin(LK.ticks * 0.02 + i) * (1 + Math.cos(LK.ticks * 0.01 + i));
// Scale pulsing effect
rocket.scaleX = 0.3 + 0.05 * Math.sin(LK.ticks * 0.08 + i);
rocket.scaleY = 0.8 + 0.1 * Math.sin(LK.ticks * 0.06 + i);
// Color shifting
var colorPhase = LK.ticks * 0.03 + i;
if (i % 2 === 0) {
rocket.alpha = 0.15 + 0.1 * Math.sin(colorPhase);
}
if (rocket.y < -100) {
rocket.y = 2732 + 100;
rocket.x = Math.random() * 2048;
rocket.speed = 1 + Math.random() * 3; // Randomize speed on reset
}
}
// Enhanced confetti with spiral motion and color cycling
for (var i = 0; i < confetti.length; i++) {
var piece = confetti[i];
piece.y -= piece.speed;
piece.rotation += piece.rotationSpeed + Math.sin(LK.ticks * 0.02 + i) * 0.02;
// Enhanced spiral motion
var spiralRadius = 20 + 10 * Math.sin(LK.ticks * 0.015 + i);
piece.x += Math.sin(LK.ticks * 0.01 + i) * 0.5 + Math.cos(LK.ticks * 0.02 + i) * spiralRadius * 0.02;
// Scale animation
piece.scaleX = 0.1 + 0.05 * Math.sin(LK.ticks * 0.05 + i);
piece.scaleY = 0.1 + 0.05 * Math.cos(LK.ticks * 0.04 + i);
// Alpha pulsing
piece.alpha = 0.2 + 0.15 * Math.sin(LK.ticks * 0.06 + i);
if (piece.y < -50) {
piece.y = 2732 + 50;
piece.x = Math.random() * 2048;
}
}
// Enhanced comets with dynamic trails and speed bursts
for (var i = 0; i < comets.length; i++) {
var comet = comets[i];
// Enhanced movement with speed bursts
var speedMultiplier = 1 + 0.5 * Math.sin(LK.ticks * 0.02 + i);
comet.x += Math.cos(comet.rotation) * comet.speed * speedMultiplier;
comet.y += Math.sin(comet.rotation) * comet.speed * speedMultiplier;
// Enhanced trail effect with varying opacity and scale
comet.alpha = 0.15 + 0.15 * Math.sin(LK.ticks * 0.1 + i);
comet.scaleX = 0.6 + 0.2 * Math.sin(LK.ticks * 0.08 + i);
comet.scaleY = 0.2 + 0.1 * Math.cos(LK.ticks * 0.06 + i);
// Slight rotation wobble
comet.rotation += Math.sin(LK.ticks * 0.03 + i) * 0.01;
// Reset position when off screen
if (comet.x < -100 || comet.x > 2148 || comet.y < -100 || comet.y > 2832) {
comet.x = Math.random() * 2048;
comet.y = Math.random() * 2732;
comet.rotation = Math.random() * Math.PI * 2;
comet.speed = 2 + Math.random() * 4; // Randomize speed on reset
}
}
if (gameState === 'loading') {
// Animate loading text
loadingTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks * 0.15);
return;
}
if (gameState === 'start') {
// Reset title tap count after timeout
titleTapTimer++;
if (titleTapTimer > 180) {
// 3 seconds at 60fps
titleTapCount = 0;
titleTapTimer = 0;
}
// Reset rank reset tap count after timeout
rankResetTapTimer++;
if (rankResetTapTimer > 180) {
// 3 seconds at 60fps
rankResetTapCount = 0;
rankResetTapTimer = 0;
}
// Update rank display on start screen
updateUI();
// Animate start text with rainbow effect and enhanced pulsing
startTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks * 0.15);
startTxt.scaleX = 1 + 0.1 * Math.sin(LK.ticks * 0.12);
startTxt.scaleY = 1 + 0.1 * Math.sin(LK.ticks * 0.12);
var colorPhase = LK.ticks * 0.08;
var r = Math.sin(colorPhase) * 127 + 128;
var g = Math.sin(colorPhase + 2) * 127 + 128;
var b = Math.sin(colorPhase + 4) * 127 + 128;
startTxt.fill = Math.floor(r) << 16 | Math.floor(g) << 8 | Math.floor(b);
// Enhanced title animation with floating and rotation
titleTxt.y = -300 + 15 * Math.sin(LK.ticks * 0.08);
titleTxt.rotation = 0.15 * Math.sin(LK.ticks * 0.06);
titleTxt.scaleX = 1 + 0.05 * Math.sin(LK.ticks * 0.1);
titleTxt.scaleY = 1 + 0.05 * Math.sin(LK.ticks * 0.1);
// Enhanced subtitle animation with color shifting
subtitleTxt.alpha = 0.6 + 0.4 * Math.sin(LK.ticks * 0.12);
subtitleTxt.y = 100 + 5 * Math.sin(LK.ticks * 0.1);
var subColorPhase = LK.ticks * 0.06;
var subR = Math.sin(subColorPhase + 1) * 127 + 128;
var subG = Math.sin(subColorPhase + 3) * 127 + 128;
var subB = Math.sin(subColorPhase + 5) * 127 + 128;
subtitleTxt.fill = Math.floor(subR) << 16 | Math.floor(subG) << 8 | Math.floor(subB);
return;
}
if (gameState === 'countdown') {
countdownTimer++;
if (countdownTimer === 1) {
showCountdownNumber();
} else if (countdownTimer >= 60) {
countdownValue--;
if (countdownValue > 0) {
countdownTimer = 0;
showCountdownNumber();
} else {
// Start the game
gameState = 'playing';
gameStarted = true;
countdownTxt.alpha = 0;
LK.playMusic('background_music');
}
}
return;
}
if (gameState === 'gameover') {
// Keep restart text hidden and add animated background effects
restartTxt.alpha = 0;
// Add swirling particles effect during game over
for (var p = 0; p < 3; p++) {
if (Math.random() < 0.1) {
var particle = game.addChild(LK.getAsset('hit_effect', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0.3
}));
particle.x = Math.random() * 2048;
particle.y = 2732 + 50;
particle.tint = Math.random() > 0.5 ? 0xFF6B6B : 0x4ECDC4;
tween(particle, {
y: -50,
rotation: Math.PI * 4,
alpha: 0,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
}
return;
}
// Game playing state
// Spawn arrows
arrowSpawnTimer++;
if (arrowSpawnTimer >= spawnInterval) {
spawnArrow();
arrowSpawnTimer = 0;
}
// Update arrows and check for misses
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
// Check if arrow passed the target zone without being hit
if (!arrow.hitTested && arrow.y > TARGET_Y + HIT_ZONE_HEIGHT) {
arrow.hitTested = true;
processMiss();
}
// Remove arrows that are off screen
if (arrow.y > 2732 + 100) {
arrow.destroy();
arrows.splice(i, 1);
}
}
};