User prompt
Test amacli eklediğin 1000 puan butonunu kaldır
User prompt
Menu gibi bir şey eklemek istiyorum. Mesela play e basınca şu anki oynanış açılacak. Bir de sınırsız mod yazicaz. Oraya tıklayınca da harita seçip oyuncu istediği haritada sınırsız oynayacak.
User prompt
Endless modda bölüm seçince istenilen bölüm açılmıyor
User prompt
Yapar mısın
User prompt
Yapar mısın
User prompt
Yapar mısın
User prompt
Ama bu şekilde çalışmıyor. Bir sorun var hangi phase i seçersen seçeyim 1. Phase açılıyor
User prompt
Endless mode da seçtiğim phase neden açılmıyor?
User prompt
Endless modda sectigim phase açılmıyor. İlk phase açılıyor
User prompt
Play story değil sadece play yazsın
User prompt
Peki başla o zaman
User prompt
Test amaçlı sağ üste bir buton ekle otomatik 1000 puan kazansın her tıklandığında
User prompt
Peki underwater aşaması aşırı kasıyor ne yapabiliriz?
User prompt
Daha sonra 2/3 kat arttır demiştim. Yani karakter örnek veriyorum 30 puan kazanacaksa 20 puan kazandım
User prompt
Kazanılan puanı 2/3 oranında azalt. Mesela 30 puan kazanılıyorsa 20 ye düşsün
User prompt
Karakterin kazandığı puanları iki katina çıkarır mısın
User prompt
Score pop up lari ile alakalı her şeyi kaldır din mı bir daha görmek istemiyorum performansı düşürüyor
User prompt
Please fix the bug: 'Uncaught ReferenceError: showScorePopup is not defined' in or related to this line: 'showScorePopup(totalScore);' Line Number: 3214
User prompt
Oyunca puan kazandığında +15 gibi sayılar yazıyor ya karakterin üzerinde. O score pop up ile alakalı her şeyi kaldır
User prompt
Bununla alakalı her şeyi kaldır pls
User prompt
1 ve 2. Phase için jump force u azalt
User prompt
Moving core u biraz büyülü
User prompt
Hayir tam tersi
User prompt
Laserin top yerine değince can gitsin kırmızı çizgi yerine değince gitmesin
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AmbientParticle = Container.expand(function () {
var self = Container.call(this);
var particleGfx = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = (Math.random() - 0.5) * 2;
self.velocityY = (Math.random() - 0.5) * 2;
self.life = 180;
self.maxLife = 180;
self.floatPhase = Math.random() * Math.PI * 2;
self.update = function () {
self.floatPhase += 0.02;
self.x += self.velocityX + fastSin(self.floatPhase) * 0.5;
self.y += self.velocityY + fastCos(self.floatPhase) * 0.3;
self.life--;
var alpha = Math.min(1, self.life / 60) * Math.min(1, (self.maxLife - self.life) / 60) * 0.4;
particleGfx.alpha = alpha;
particleGfx.scaleX = 0.5;
particleGfx.scaleY = 0.5;
if (self.life <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var BackgroundShape = Container.expand(function () {
var self = Container.call(this);
var shapeGfx = self.attachAsset('bgShape', {
anchorX: 0.5,
anchorY: 0.5
});
self.pulsePhase = Math.random() * Math.PI * 2;
self.oscillatePhase = Math.random() * Math.PI * 2;
self.floatSpeed = 0.02 + Math.random() * 0.03;
self.pulseSpeed = 0.05 + Math.random() * 0.05;
self.baseScale = 0.5 + Math.random() * 1.0;
self.baseAlpha = 0.1 + Math.random() * 0.2;
self.update = function () {
// Positional updates should be smooth
if (isMobile) {
self.x -= gameSpeed * 0.2;
} else {
self.oscillatePhase += self.floatSpeed;
self.x += fastSin(self.oscillatePhase) * 0.5;
self.y += fastCos(self.oscillatePhase * 0.7) * 0.3;
self.x -= gameSpeed * 0.2;
}
// Throttle visual updates
var throttleFactor = isMobile ? 4 : 2;
if (LK.ticks % throttleFactor === 0) {
if (isMobile) {
self.pulsePhase += self.pulseSpeed * throttleFactor;
var scale = self.baseScale * (1 + beatPulse * 0.3);
shapeGfx.scaleX = scale;
shapeGfx.scaleY = scale;
shapeGfx.alpha = self.baseAlpha;
} else {
// Desktop: full effects
self.pulsePhase += self.pulseSpeed * throttleFactor;
var beatScale = 1 + beatPulse * 0.3;
var pulseScale = 1 + fastSin(self.pulsePhase) * 0.2;
shapeGfx.scaleX = self.baseScale * beatScale * pulseScale;
shapeGfx.scaleY = self.baseScale * beatScale * pulseScale;
shapeGfx.alpha = self.baseAlpha + beatPulse * 0.1;
}
}
};
return self;
});
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubbleGfx = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityY = -1 - Math.random() * 2;
self.velocityX = (Math.random() - 0.5) * 0.5;
self.life = 300 + Math.random() * 200;
self.maxLife = self.life;
self.floatPhase = Math.random() * Math.PI * 2;
self.update = function () {
if (!self.visible) return;
// Position and life updates must be smooth and consistent
self.x += self.velocityX;
self.y += self.velocityY;
self.floatPhase += 0.05;
var floatX = fastSin(self.floatPhase) * 0.8;
self.x += floatX;
self.life--;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
var alpha = Math.min(1, self.life / 60) * Math.min(1, (self.maxLife - self.life) / 60) * 0.7;
bubbleGfx.alpha = alpha;
var scale = 0.8 + fastSin(self.floatPhase * 0.8) * 0.3;
bubbleGfx.scaleX = scale;
bubbleGfx.scaleY = scale;
}
if (self.life <= 0 || self.y < -50) {
self.shouldDestroy = true;
}
};
return self;
});
var BurstParticle = Container.expand(function () {
var self = Container.call(this);
var particleGfx = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.life = 40;
self.maxLife = 40;
self.initialScale = 1.5;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= 0.95;
self.velocityY *= 0.95;
self.life--;
var alpha = self.life / self.maxLife;
particleGfx.alpha = alpha;
var scale = self.initialScale * alpha;
particleGfx.scaleX = scale;
particleGfx.scaleY = scale;
if (self.life <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var Bush = Container.expand(function () {
var self = Container.call(this);
var bushGfx = self.attachAsset('bush', {
anchorX: 0.5,
anchorY: 1
});
// Larger scale for nature phase
bushGfx.scaleX = 1.5;
bushGfx.scaleY = 1.5;
self.speed = -6;
self.rustlePhase = Math.random() * Math.PI * 2;
self.update = function () {
if (!self.visible) return;
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 4 : 2;
if (LK.ticks % throttleFactor === 0) {
// Gentle rustling animation
self.rustlePhase += 0.08 * throttleFactor;
var rustle = fastSin(self.rustlePhase) * 0.05;
bushGfx.scaleX = 1 + rustle;
bushGfx.scaleY = 1 + rustle * 0.5;
}
};
return self;
});
var Cloud = Container.expand(function () {
var self = Container.call(this);
var cloudGlow = self.attachAsset('cloudGlow', {
anchorX: 0.5,
anchorY: 0.5
});
var cloudCore = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloudGlow.alpha = 0.4;
cloudCore.alpha = 0.9;
self.speed = -4;
self.floatPhase = Math.random() * Math.PI * 2;
self.glowPhase = Math.random() * Math.PI * 2;
self.bouncePhase = 0;
self.isBouncing = false;
self.bounceIntensity = 0;
self.mistTimer = 0;
self.update = function () {
if (!self.visible) return;
// Keep movement smooth
self.x += self.speed;
self.floatPhase += 0.03;
var floatY = fastSin(self.floatPhase) * 8;
self.y += floatY * 0.1;
// Bounce animation needs to be responsive
if (self.isBouncing) {
self.bouncePhase += 0.3;
self.bounceIntensity = fastSin(self.bouncePhase) * 0.5;
cloudCore.scaleY = 1 - self.bounceIntensity * 0.2;
cloudGlow.scaleY = 1 - self.bounceIntensity * 0.15;
if (self.bouncePhase >= Math.PI) {
self.isBouncing = false;
self.bouncePhase = 0;
cloudCore.scaleY = 1;
cloudGlow.scaleY = 1;
}
}
// Throttle non-essential visual updates
var throttleFactor = isMobile ? 4 : 2;
if (LK.ticks % throttleFactor === 0) {
// Soft glow pulsing
self.glowPhase += 0.05 * throttleFactor;
var glowIntensity = 0.3 + fastSin(self.glowPhase) * 0.1;
cloudGlow.alpha = glowIntensity;
}
// Create mist particles
self.mistTimer++;
if (self.mistTimer >= 40) {
self.mistTimer = 0;
self.createMist();
}
};
self.bounce = function () {
if (!self.isBouncing) {
self.isBouncing = true;
self.bouncePhase = 0;
// Play soft bounce sound
if (Math.random() < 0.3) {
LK.getSound('echoChime').play();
}
}
};
self.createMist = function () {
// Mist creation disabled
};
return self;
});
var Coral = Container.expand(function () {
var self = Container.call(this);
var coralGfx = self.attachAsset('coral', {
anchorX: 0.5,
anchorY: 1
});
self.speed = -4;
self.pulsePhase = Math.random() * Math.PI * 2;
self.update = function () {
if (!self.visible) return;
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
self.pulsePhase += 0.03 * throttleFactor;
var pulse = 1 + fastSin(self.pulsePhase) * 0.15;
coralGfx.scaleX = pulse;
coralGfx.scaleY = pulse;
var colorShift = Math.floor(fastSin(self.pulsePhase * 0.5) * 80);
coralGfx.tint = 0xff69b4 + colorShift;
}
};
return self;
});
var DataStreamParticle = Container.expand(function () {
var self = Container.call(this);
var streamGfx = self.attachAsset('dataStream', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = -1 - Math.random() * 3;
self.velocityY = (Math.random() - 0.5) * 2;
self.life = 300 + Math.random() * 200;
self.maxLife = self.life;
self.twinklePhase = Math.random() * Math.PI * 2;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.twinklePhase += 0.1;
self.life--;
// Twinkling effect
var twinkle = fastSin(self.twinklePhase) * 0.3;
var fadeAlpha = Math.min(1, self.life / 60) * Math.min(1, (self.maxLife - self.life) / 60);
streamGfx.alpha = (0.3 + twinkle + beatPulse * 0.2) * fadeAlpha;
streamGfx.scaleX = 0.8 + beatPulse * 0.4;
streamGfx.scaleY = 0.8 + beatPulse * 0.4;
if (self.life <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var ExplosionPart = Container.expand(function () {
var self = Container.call(this);
var partGfx = self.attachAsset('explosionPart', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.rotationSpeed = 0;
self.life = 40;
self.maxLife = 40;
self.gravity = 0.3;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
self.velocityX *= 0.98;
partGfx.rotation += self.rotationSpeed;
self.life--;
var alpha = self.life / self.maxLife;
partGfx.alpha = alpha;
// Color transition from bright orange to dark red
var colorProgress = 1 - alpha;
var red = 255;
var green = Math.floor(170 * (1 - colorProgress));
var blue = 0;
partGfx.tint = red << 16 | green << 8 | blue;
var scale = alpha * (1 + (1 - alpha) * 0.5);
partGfx.scaleX = scale;
partGfx.scaleY = scale;
if (self.life <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var Fish = Container.expand(function () {
var self = Container.call(this);
var fishGfx = self.attachAsset('fish', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -4 - Math.random() * 3;
self.swimPhase = Math.random() * Math.PI * 2;
self.verticalAmplitude = 30 + Math.random() * 20;
self.baseY = 0;
self.update = function () {
if (!self.visible) return;
// Keep movement smooth
self.x += self.speed;
self.swimPhase += 0.08;
var swimY = fastSin(self.swimPhase) * self.verticalAmplitude;
self.y = self.baseY + swimY;
// Throttle visual effects
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
var scale = 1 + fastSin(self.swimPhase * 1.2) * 0.1;
fishGfx.scaleX = scale;
fishGfx.scaleY = scale;
fishGfx.rotation = fastSin(self.swimPhase) * 0.2;
var colorShift = Math.floor(fastSin(self.swimPhase) * 50);
fishGfx.tint = 0xff6600 + colorShift;
}
};
return self;
});
var GroundSegment = Container.expand(function () {
var self = Container.call(this);
var groundGfx = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
var shimmerGfx = self.attachAsset('groundShimmer', {
anchorX: 0,
anchorY: 0
});
shimmerGfx.y = -8;
self.shimmerPhase = Math.random() * Math.PI * 2;
self.wavePhase = Math.random() * Math.PI * 2;
self.beatIntensity = 0;
self.update = function () {
// Motion and wave must be smooth
self.x -= gameSpeed;
self.wavePhase += gameSpeed * 0.02;
var waveOffset = fastSin(self.wavePhase) * 3;
groundGfx.y = waveOffset;
shimmerGfx.y = -8 + waveOffset;
// Throttle other visual effects
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
// Reflective shimmer effect
self.shimmerPhase += 0.08 * throttleFactor;
shimmerGfx.alpha = 0.4 + fastSin(self.shimmerPhase) * 0.3;
shimmerGfx.scaleX = 1 + fastSin(self.shimmerPhase * 0.7) * 0.1;
// Beat-synced effects
self.beatIntensity = beatPulse;
var glowIntensity = 0.7 + self.beatIntensity * 0.3;
groundGfx.alpha = glowIntensity;
// Color changes based on beat
var beatColor = Math.floor(self.beatIntensity * 80);
groundGfx.tint = 0x4488ff + beatColor;
shimmerGfx.tint = 0x88ccff + (beatColor << 4);
}
};
return self;
});
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartGfx = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -8;
self.pulsePhase = Math.random() * Math.PI * 2;
self.glowPhase = Math.random() * Math.PI * 2;
self.isPulsing = false;
self.pulseTimer = 0;
self.maxPulseTime = 60; // 1 second at 60fps
self.sparkleTimer = 0;
self.update = function () {
if (!self.visible) return;
// Movement and state logic must be smooth
self.x += self.speed;
self.pulsePhase += 0.08;
var floatY = fastSin(self.pulsePhase) * 8;
self.y += floatY * 0.05;
if (beatPulse > 0.7 && !self.isPulsing) {
self.isPulsing = true;
self.pulseTimer = self.maxPulseTime;
}
if (self.isPulsing) {
self.pulseTimer--;
if (self.pulseTimer <= 0) {
self.isPulsing = false;
}
}
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
if (self.isPulsing) {
// Bright glow and scale during pulse
var pulseIntensity = self.pulseTimer / self.maxPulseTime;
heartGfx.alpha = 0.8 + pulseIntensity * 0.2;
heartGfx.scaleX = 1.0 + pulseIntensity * 0.4;
heartGfx.scaleY = 1.0 + pulseIntensity * 0.4;
heartGfx.tint = 0xff1144; // Bright red when pulsing
} else {
// Normal state - gentle glow
self.glowPhase += 0.05 * throttleFactor;
heartGfx.alpha = 0.7 + fastSin(self.glowPhase) * 0.2;
heartGfx.scaleX = 1.0 + fastSin(self.glowPhase) * 0.1;
heartGfx.scaleY = 1.0 + fastSin(self.glowPhase) * 0.1;
heartGfx.tint = 0xff3366; // Normal pink-red
}
}
};
return self;
});
var HolographicElement = Container.expand(function () {
var self = Container.call(this);
var elementType = Math.floor(Math.random() * 3);
var elementGfx;
if (elementType === 0) {
elementGfx = self.attachAsset('waveform', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (elementType === 1) {
elementGfx = self.attachAsset('speaker', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
elementGfx = self.attachAsset('freqBar', {
anchorX: 0.5,
anchorY: 1
});
}
self.fadePhase = 0;
self.maxFadePhase = 300 + Math.random() * 200;
self.pulsePhase = Math.random() * Math.PI * 2;
self.driftSpeed = -0.5 - Math.random() * 1.5;
self.baseScale = 2 + Math.random() * 3;
self.update = function () {
self.fadePhase++;
self.pulsePhase += 0.03;
self.x += self.driftSpeed;
// Fade in and out effect
var fadeProgress = self.fadePhase / self.maxFadePhase;
var fadeAlpha;
if (fadeProgress < 0.2) {
fadeAlpha = fadeProgress / 0.2;
} else if (fadeProgress > 0.8) {
fadeAlpha = (1 - fadeProgress) / 0.2;
} else {
fadeAlpha = 1;
}
// Very faint holographic appearance
elementGfx.alpha = fadeAlpha * (0.05 + beatPulse * 0.1);
// Pulse with beat
var scale = self.baseScale * (1 + fastSin(self.pulsePhase) * 0.1 + beatPulse * 0.2);
elementGfx.scaleX = scale;
elementGfx.scaleY = scale;
// Color tinting for holographic effect
elementGfx.tint = 0x4466aa + Math.floor(beatPulse * 50);
if (self.fadePhase >= self.maxFadePhase) {
self.shouldDestroy = true;
}
};
return self;
});
var LaserBeam = Container.expand(function () {
var self = Container.call(this);
// Laser core that moves up and down
var laserCore = self.attachAsset('laserCore', {
anchorX: 0.5,
anchorY: 0.5
});
// Main laser beam
var laserBeam = self.attachAsset('laserBeam', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -6;
self.verticalSpeed = 3;
self.direction = 1; // 1 for down, -1 for up
self.warningTimer = 60; // Warning phase before activation
self.isActive = false;
self.pulsePhase = 0;
self.coreY = 0;
self.maxTravel = 150;
// Initially hide the beam, show only core
laserBeam.alpha = 0;
laserCore.y = -self.maxTravel;
self.update = function () {
if (!self.visible) return;
// Movement and state logic must be smooth
self.x += self.speed;
// Warning phase is critical feedback, keep it responsive
if (self.warningTimer > 0) {
self.warningTimer--;
// Blinking warning effect
laserCore.alpha = fastSin(self.warningTimer * 0.3) * 0.5 + 0.5;
laserCore.tint = 0xff0000; // Red warning
if (self.warningTimer === 0) {
self.isActive = true;
laserBeam.alpha = 0.9;
}
}
// Active laser movement must be smooth
if (self.isActive) {
self.coreY += self.verticalSpeed * self.direction;
laserCore.y = self.coreY;
// Reverse direction at limits
if (self.coreY >= self.maxTravel || self.coreY <= -self.maxTravel) {
self.direction *= -1;
}
// Throttle visual pulse effects
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
self.pulsePhase += 0.15 * throttleFactor;
var intensity = 0.8 + fastSin(self.pulsePhase) * 0.2 + beatPulse * 0.3;
laserBeam.alpha = intensity;
laserCore.alpha = 1.0;
// Bright laser colors
laserBeam.tint = 0xff0088 + Math.floor(beatPulse * 100);
laserCore.tint = 0xffffff;
// Scale effects
var scale = 1 + beatPulse * 0.4;
laserBeam.scaleX = scale;
laserCore.scaleX = 1 + beatPulse * 0.6;
laserCore.scaleY = 1 + beatPulse * 0.6;
}
}
};
return self;
});
var LightLine = Container.expand(function () {
var self = Container.call(this);
var lineGfx = self.attachAsset('lightLine', {
anchorX: 0,
anchorY: 0.5
});
self.speed = -2 - Math.random() * 4;
self.pulsePhase = Math.random() * Math.PI * 2;
self.glowPhase = Math.random() * Math.PI * 2;
self.baseAlpha = 0.2 + Math.random() * 0.3;
self.update = function () {
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
self.pulsePhase += 0.08 * throttleFactor;
self.glowPhase += 0.05 * throttleFactor;
// Flowing glow effect
lineGfx.alpha = self.baseAlpha + fastSin(self.glowPhase) * 0.2 + beatPulse * 0.2;
lineGfx.scaleY = 0.8 + fastSin(self.pulsePhase) * 0.4 + beatPulse * 0.3;
// Color shift with beat
var colorShift = Math.floor(beatPulse * 100);
lineGfx.tint = 0x6644ff + (colorShift << 8);
}
};
return self;
});
var NatureGroundSegment = Container.expand(function () {
var self = Container.call(this);
var groundGfx = self.attachAsset('natureGround', {
anchorX: 0,
anchorY: 0
});
// Larger scale for nature phase
groundGfx.scaleX = 1.4;
groundGfx.scaleY = 1.4;
self.update = function () {
self.x -= gameSpeed + 1;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGfx = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1
});
self.speed = -6;
self.pulsePhase = 0;
self.update = function () {
if (!self.visible) return;
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
// Pulse effect
self.pulsePhase += 0.15 * throttleFactor;
var scale = 1 + fastSin(self.pulsePhase) * 0.1;
obstacleGfx.scaleX = scale;
obstacleGfx.scaleY = scale;
// Beat sync glow
if (beatPulse > 0.5) {
obstacleGfx.tint = 0xffffff;
} else {
obstacleGfx.tint = 0xff0044;
}
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGfx = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.life = 60;
self.maxLife = 60;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += 0.2;
self.life--;
particleGfx.alpha = self.life / self.maxLife;
if (self.life <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Create main glow aura behind character
var mainGlow = self.attachAsset('playerGlow', {
anchorX: 0.5,
anchorY: 0.5
});
mainGlow.alpha = 0.3;
mainGlow.y = -10;
// Create body parts with neon light appearance
var head = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 0.5
});
var body = self.attachAsset('playerBody', {
anchorX: 0.5,
anchorY: 0
});
var oxygenTank = self.attachAsset('oxygenTank', {
anchorX: 0.5,
anchorY: 0.5
});
var leftArm = self.attachAsset('playerArm', {
anchorX: 0.5,
anchorY: 0
});
var rightArm = self.attachAsset('playerArm', {
anchorX: 0.5,
anchorY: 0
});
var leftLeg = self.attachAsset('playerLeg', {
anchorX: 0.5,
anchorY: 0
});
var rightLeg = self.attachAsset('playerLeg', {
anchorX: 0.5,
anchorY: 0
});
// Position body parts relative to center
head.y = -30;
body.y = -12;
leftArm.x = -10;
leftArm.y = -8;
rightArm.x = 10;
rightArm.y = -8;
leftLeg.x = -6;
leftLeg.y = 13;
rightLeg.x = 6;
rightLeg.y = 13;
// Position oxygen tank on player's back (behind body)
oxygenTank.x = -8; // Position behind player (to the left when facing right)
oxygenTank.y = -5; // Slightly higher than body center
oxygenTank.visible = false; // Initially hidden
// Set initial electric blue color with glow effect
head.tint = 0x00ccff;
body.tint = 0x0099ff;
leftArm.tint = 0x0088ff;
rightArm.tint = 0x0088ff;
leftLeg.tint = 0x0077ff;
rightLeg.tint = 0x0077ff;
self.velocityY = 0;
self.velocityX = 0;
self.isGrounded = false;
self.isInvincible = false;
self.invincibilityFlashOn = false;
self.isDashing = false;
self.dashCooldown = 0;
self.canDoubleJump = false;
self.hasUsedDoubleJump = false;
self.hasJumped = false; // Track if player has actually jumped
self.glowPhase = 0;
self.walkPhase = 0;
self.constantSpeed = 3; // Constant walking speed
self.hoverPhase = 0;
self.beatReactionTimer = 0;
self.currentHue = 0; // For color shifting
self.comboStreakColor = 0x00ccff; // Base electric blue
self.update = function () {
// Floating effect - character hovers above ground
self.hoverPhase += 0.08;
var hoverOffset = fastSin(self.hoverPhase) * 3;
// Variable jump system - apply additional upward force while jump is held
if (isJumpPressed && jumpHoldTime < maxJumpHoldTime && self.velocityY < 0) {
var holdForce = (maxJumpHoldTime - jumpHoldTime) / maxJumpHoldTime * -1.5;
self.velocityY += holdForce;
jumpHoldTime++;
}
// Simplified gravity system
if (!self.isGrounded) {
if (gamePhase === 5) {
// Underwater phase - reduced gravity and buoyancy
self.velocityY += 0.3; // Much slower falling underwater
// Add slight upward buoyancy force
self.velocityY -= 0.1;
} else if (gamePhase === 3) {
// Sky level - increased gravity
self.velocityY += 0.55;
} else {
// Normal/speed phase - standard gravity
self.velocityY += 0.6;
}
}
// Apply velocity with underwater resistance
if (gamePhase === 5) {
// Underwater movement - add resistance
self.velocityX *= 0.95; // Water resistance
self.velocityY *= 0.98; // Slower vertical movement
}
self.y += self.velocityY;
self.x += self.velocityX;
// Constant forward movement when grounded
if (self.isGrounded && !self.isDashing) {
self.x += self.constantSpeed;
}
// Ground collision with hover offset (only in normal/speed phases)
if (gamePhase !== 3) {
var groundThreshold = groundLevel - 35 + hoverOffset;
if (self.y >= groundThreshold) {
self.y = groundThreshold;
self.velocityY = 0;
self.isGrounded = true;
self.canDoubleJump = false;
self.hasUsedDoubleJump = false;
self.hasJumped = false; // Reset jump flag when grounded
}
} else {
// Sky level - check if player is actually on a cloud or seagull
var onPlatform = false;
// Check if standing on any cloud
for (var i = 0; i < clouds.length; i++) {
var cloud = clouds[i];
if (self.intersects(cloud) && self.velocityY >= 0 && self.y <= cloud.y) {
onPlatform = true;
break;
}
}
// Check if standing on any seagull
if (!onPlatform) {
for (var i = 0; i < seagulls.length; i++) {
var seagull = seagulls[i];
if (self.intersects(seagull) && self.velocityY >= 0 && self.y <= seagull.y) {
onPlatform = true;
break;
}
}
}
// If not on any platform, player should fall
if (!onPlatform) {
// Track if player was grounded before falling
var wasGrounded = self.isGrounded;
self.isGrounded = false;
// If player just started falling (was on platform, now falling)
if (wasGrounded) {
// When falling from platform, enable double jump capability
self.hasJumped = false;
self.canDoubleJump = true;
self.hasUsedDoubleJump = false;
}
} else {
// Player is on a platform
self.isGrounded = true;
self.canDoubleJump = false;
self.hasUsedDoubleJump = false;
self.hasJumped = false;
}
}
// Simple backward movement
if (self.isGrounded && self.velocityX === 0 && !self.isDashing) {
self.x -= gameSpeed * 0.8; // Smooth per-frame movement
}
// Dash cooldown
if (self.dashCooldown > 0) {
self.dashCooldown--;
}
// Simplified animation on mobile
if (self.isGrounded && !self.isDashing) {
self.walkPhase += 0.25;
if (isMobile) {
// Mobile: update rotations only every 3 frames
if (LK.ticks % 3 === 0) {
var armRot = fastSin(self.walkPhase) * 0.6;
leftArm.rotation = armRot;
rightArm.rotation = -armRot;
var legRot = fastSin(self.walkPhase + Math.PI) * 0.4;
leftLeg.rotation = legRot;
rightLeg.rotation = fastSin(self.walkPhase) * 0.4;
head.y = -30;
}
} else {
// Desktop: full animation
leftArm.rotation = fastSin(self.walkPhase) * 0.6;
rightArm.rotation = -fastSin(self.walkPhase) * 0.6;
leftLeg.rotation = fastSin(self.walkPhase + Math.PI) * 0.4;
rightLeg.rotation = fastSin(self.walkPhase) * 0.4;
var beatBob = beatPulse > 0.5 ? beatPulse * 2 : 0;
head.y = -30 + fastSin(self.walkPhase * 2) * 2 + beatBob;
}
} else {
// Reset positions when not walking
leftArm.rotation = 0;
rightArm.rotation = 0;
leftLeg.rotation = 0;
rightLeg.rotation = 0;
head.y = -30;
}
// Simplified glow effects for mobile
if (isMobile) {
// Mobile: update glow only every 4 frames
if (LK.ticks % 4 === 0) {
self.glowPhase += 0.48; // 4x speed to compensate
var totalGlow = self.beatReactionTimer > 0 ? 1.0 : 0.9;
head.alpha = totalGlow;
body.alpha = totalGlow;
leftArm.alpha = totalGlow;
rightArm.alpha = totalGlow;
leftLeg.alpha = totalGlow;
rightLeg.alpha = totalGlow;
mainGlow.alpha = 0.3;
}
} else {
// Desktop: full effects
self.glowPhase += 0.12;
var baseGlow = 0.9 + fastSin(self.glowPhase) * 0.1;
var beatGlow = beatPulse * 0.3;
var totalGlow = Math.min(1.0, baseGlow + beatGlow);
// Beat reaction timer
if (self.beatReactionTimer > 0) {
self.beatReactionTimer--;
totalGlow = 1.0;
}
// Apply glow to all parts
head.alpha = totalGlow;
body.alpha = totalGlow;
leftArm.alpha = totalGlow;
rightArm.alpha = totalGlow;
leftLeg.alpha = totalGlow;
rightLeg.alpha = totalGlow;
// Main glow pulsing
mainGlow.alpha = 0.2 + beatPulse * 0.3;
mainGlow.scaleX = 1 + fastSin(self.glowPhase * 0.7) * 0.1 + beatPulse * 0.2;
mainGlow.scaleY = 1 + fastSin(self.glowPhase * 0.7) * 0.1 + beatPulse * 0.2;
}
// Color shifting based on combo streak and game phase
if (combo > 0) {
self.currentHue += 0.02;
var hueShift = fastSin(self.currentHue) * 60;
if (gamePhase === 4) {
// Nature phase - Mario classic red and blue colors
self.comboStreakColor = 0xff3333 + Math.floor(hueShift);
} else if (gamePhase === 3) {
// Sky phase - soft golden colors
self.comboStreakColor = 0xffcc66 + Math.floor(hueShift);
} else if (gamePhase === 2) {
// Speed phase - orange/red colors
self.comboStreakColor = 0xff8800 + Math.floor(hueShift);
} else {
// Normal phase - blue colors
self.comboStreakColor = 0x0088ff + Math.floor(hueShift);
}
// Apply combo colors
head.tint = self.comboStreakColor + 0x2200;
body.tint = self.comboStreakColor;
leftArm.tint = self.comboStreakColor - 0x1100;
rightArm.tint = self.comboStreakColor - 0x1100;
leftLeg.tint = self.comboStreakColor - 0x2200;
rightLeg.tint = self.comboStreakColor - 0x2200;
mainGlow.tint = self.comboStreakColor;
} else {
// Use pre-calculated colors
var phaseKey = 'normal';
if (gamePhase === 5) {
phaseKey = 'underwater';
oxygenTank.visible = true;
oxygenTank.tint = 0x666666;
} else if (gamePhase === 4) {
phaseKey = 'nature';
oxygenTank.visible = false;
} else if (gamePhase === 3) {
phaseKey = 'sky';
oxygenTank.visible = false;
} else if (gamePhase === 2) {
phaseKey = 'speed';
oxygenTank.visible = false;
} else {
oxygenTank.visible = false;
}
// Apply cached colors
var colors = colorCache.phaseColors[phaseKey];
head.tint = colors.head;
body.tint = colors.body;
leftArm.tint = colors.arms;
rightArm.tint = colors.arms;
leftLeg.tint = colors.legs;
rightLeg.tint = colors.legs;
mainGlow.tint = colors.glow;
self.currentHue = 0;
}
if (self.isInvincible) {
if (self.invincibilityFlashOn) {
var flashAlpha = 0.3;
head.alpha = flashAlpha;
body.alpha = flashAlpha;
leftArm.alpha = flashAlpha;
rightArm.alpha = flashAlpha;
leftLeg.alpha = flashAlpha;
rightLeg.alpha = flashAlpha;
}
// main glow invincibility effect
mainGlow.alpha = 0.2 + fastSin(LK.ticks * 0.3) * 0.1; // Reduced transparency glow
mainGlow.scaleX = 2.5 + fastSin(LK.ticks * 0.2) * 0.5; // Much larger scale
mainGlow.scaleY = 2.5 + fastSin(LK.ticks * 0.2) * 0.5; // Much larger scale
mainGlow.tint = 0xffffff; // Pure white glow for maximum visibility
}
// Underwater platform collision detection (phase 5 only)
if (gamePhase === 5) {
// Calculate player bounds considering full player size including all body parts
// Head extends 30px up from center, legs extend 35px down from center
var playerTopOffset = 35; // Distance from center to top of head
var playerBottomOffset = 35; // Distance from center to bottom of legs
var playerTop = self.y - playerTopOffset;
var playerBottom = self.y + playerBottomOffset;
// Top platform collision - solid barrier, player cannot pass through
// Platform bottom is at y=900, reduce buffer to make collision tighter
if (playerTop < 910) {
self.y = 910 + playerTopOffset; // Position player so top of head touches platform edge
if (self.velocityY < 0) {
self.velocityY = 0; // Stop vertical movement completely
}
self.isGrounded = false; // Player is not grounded when hitting top platform
}
// Bottom platform collision - solid floor, player stands on it
// Platform top is at y=1800, reduce buffer to make collision tighter
if (playerBottom > 1790) {
self.y = 1790 - playerBottomOffset; // Position player so bottom of legs touches platform edge
if (self.velocityY > 0) {
self.velocityY = 0; // Stop falling completely
}
self.isGrounded = true; // Player is grounded when hitting bottom platform
}
}
// Keep player on screen
if (self.x < 0) self.x = 0;
if (self.x > 2048) self.x = 2048;
};
// Variable jump method
self.jump = function () {
if (self.isGrounded) {
// Calculate jump force based on hold time
var holdRatio = Math.min(jumpHoldTime / maxJumpHoldTime, 1);
var currentJumpForce, currentMinForce, currentMaxForce;
if (gamePhase === 5) {
// Underwater phase - use reduced jump power
currentMinForce = underwaterMinJumpForce;
currentMaxForce = underwaterMaxJumpForce;
} else {
// Other phases - use normal jump power
currentMinForce = minJumpForce;
currentMaxForce = maxJumpForce;
}
currentJumpForce = currentMinForce + (currentMaxForce - currentMinForce) * holdRatio;
self.velocityY = currentJumpForce;
self.isGrounded = false;
self.canDoubleJump = true;
self.hasUsedDoubleJump = false;
self.hasJumped = true;
self.createJumpEffect();
// Play phase-specific jump sound
if (gamePhase === 2) {
LK.getSound('jumpSpeed').play();
} else if (gamePhase === 3) {
LK.getSound('jumpSky').play();
} else if (gamePhase === 4) {
var jumpNatureSound = LK.getSound('jumpNature');
jumpNatureSound.pitch = 0.97 + Math.random() * 0.06; // Random pitch between 0.97 and 1.03
jumpNatureSound.play();
} else if (gamePhase === 5) {
LK.getSound('jumpUnderwater').play();
} else {
LK.getSound('jump').play();
}
} else if (gamePhase === 5) {
// Underwater phase - unlimited swimming with reduced power
self.velocityY = underwaterDoubleJumpForce;
// Swimming visual effects
LK.effects.flashObject(self, 0x00ccff, 300);
for (var i = 0; i < 6; i++) {
var particle = new Particle();
particle.x = self.x + (Math.random() - 0.5) * 60;
particle.y = self.y + 10;
particle.velocityY = Math.random() * 6 + 2;
particle.velocityX = (Math.random() - 0.5) * 10;
var particleGfx = particle.children[0];
particleGfx.tint = 0x00ccff;
game.addChild(particle);
particles.push(particle);
}
if (gamePhase === 5) {
LK.getSound('jumpUnderwater').play();
} else {
LK.getSound('jump').play();
}
} else if (self.canDoubleJump && !self.hasUsedDoubleJump) {
// Double jump with fixed force (other phases)
var currentDoubleJumpForce = gamePhase === 5 ? underwaterDoubleJumpForce : doubleJumpForce;
self.velocityY = currentDoubleJumpForce;
self.hasUsedDoubleJump = true;
// Double jump visual effects
LK.effects.flashObject(self, 0x00ffff, 300);
for (var i = 0; i < 8; i++) {
var particle = new Particle();
particle.x = self.x + (Math.random() - 0.5) * 60;
particle.y = self.y + 10;
particle.velocityY = Math.random() * 8 + 3;
particle.velocityX = (Math.random() - 0.5) * 12;
var particleGfx = particle.children[0];
particleGfx.tint = 0x00ffff;
game.addChild(particle);
particles.push(particle);
}
if (gamePhase === 2) {
LK.getSound('jumpSpeed').play();
} else if (gamePhase === 3) {
LK.getSound('jumpSky').play();
} else if (gamePhase === 4) {
var jumpNatureSound = LK.getSound('jumpNature');
jumpNatureSound.pitch = 0.97 + Math.random() * 0.06; // Random pitch between 0.97 and 1.03
jumpNatureSound.play();
} else if (gamePhase === 5) {
LK.getSound('jumpUnderwater').play();
} else {
LK.getSound('jump').play();
}
}
};
self.dash = function () {
if (self.dashCooldown <= 0) {
self.velocityX = 15;
self.isDashing = true;
self.dashCooldown = 120; // 2 seconds at 60fps
// Play phase-specific dash sound
if (gamePhase === 2) {
LK.getSound('dashSpeed').play();
} else if (gamePhase === 3) {
LK.getSound('dashSky').play();
} else if (gamePhase === 4) {
LK.getSound('dashNature').play();
} else if (gamePhase === 5) {
LK.getSound('dashUnderwater').play();
} else {
LK.getSound('dash').play();
}
self.createDashEffect();
tween(self, {
velocityX: 0
}, {
duration: 400
});
LK.setTimeout(function () {
self.isDashing = false;
}, 400);
}
};
self.createJumpEffect = function () {
for (var i = 0; i < 5; i++) {
var particle = new Particle();
particle.x = self.x + (Math.random() - 0.5) * 40;
particle.y = self.y + 20;
particle.velocityY = Math.random() * 5 + 2;
particle.velocityX = (Math.random() - 0.5) * 8;
game.addChild(particle);
particles.push(particle);
}
};
self.createDashEffect = function () {
for (var i = 0; i < 8; i++) {
var particle = new Particle();
particle.x = self.x - 30;
particle.y = self.y + (Math.random() - 0.5) * 40;
particle.velocityY = (Math.random() - 0.5) * 6;
particle.velocityX = -Math.random() * 10 - 5;
game.addChild(particle);
particles.push(particle);
}
};
// React to beats with flash and pulse
self.reactToBeat = function () {
self.beatReactionTimer = 8; // Flash for 8 frames
// Bright flash effect
tween(self, {
alpha: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1.0
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
// Scale pulse
tween(self, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 250,
easing: tween.easeInOut
});
}
});
};
return self;
});
var Rock = Container.expand(function () {
var self = Container.call(this);
var rockGfx = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 1
});
// Larger scale for nature phase
rockGfx.scaleX = 2.0; // Increased from 1.5 to 2.0
rockGfx.scaleY = 2.0; // Increased from 1.5 to 2.0
self.speed = -6;
self.update = function () {
if (!self.visible) return;
self.x += self.speed;
};
return self;
});
var RotatingCube = Container.expand(function () {
var self = Container.call(this);
var cubeGfx = self.attachAsset('rotatingCube', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -6;
self.rotationSpeed = 0.1 + Math.random() * 0.1;
self.pulsePhase = 0;
self.glowPhase = Math.random() * Math.PI * 2;
self.update = function () {
if (!self.visible) return;
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
// Continuous rotation
cubeGfx.rotation += self.rotationSpeed * throttleFactor;
// Pulse effect with beat
self.pulsePhase += 0.1 * throttleFactor;
var scale = 1 + fastSin(self.pulsePhase) * 0.2 + beatPulse * 0.3;
cubeGfx.scaleX = scale;
cubeGfx.scaleY = scale;
// Glow effect
self.glowPhase += 0.08 * throttleFactor;
cubeGfx.alpha = 0.8 + fastSin(self.glowPhase) * 0.2 + beatPulse * 0.2;
// Color shifting
var colorShift = Math.floor(beatPulse * 100);
cubeGfx.tint = 0xff4400 + (colorShift << 4);
}
};
return self;
});
var Seagull = Container.expand(function () {
var self = Container.call(this);
// Create seagull body
var body = self.attachAsset('seagullBody', {
anchorX: 0.5,
anchorY: 0.5
});
// Create wings
var leftWing = self.attachAsset('seagullWing', {
anchorX: 0.8,
anchorY: 0.5
});
var rightWing = self.attachAsset('seagullWing', {
anchorX: 0.2,
anchorY: 0.5
});
// Position wings
leftWing.x = -15;
leftWing.y = -3;
rightWing.x = 15;
rightWing.y = -3;
// Flight properties
self.speed = -5 - Math.random() * 2; // Slightly faster than clouds
self.wingPhase = Math.random() * Math.PI * 2;
self.wingSpeed = 0.3 + Math.random() * 0.2;
self.floatPhase = Math.random() * Math.PI * 2;
self.floatAmplitude = 15 + Math.random() * 10;
self.baseY = 0;
self.update = function () {
if (!self.visible) return;
// Keep movement smooth
self.x += self.speed;
self.floatPhase += 0.02;
var floatOffset = fastSin(self.floatPhase) * self.floatAmplitude;
self.y = self.baseY + floatOffset;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
// Wing flapping animation
self.wingPhase += self.wingSpeed * throttleFactor;
var wingFlap = fastSin(self.wingPhase);
leftWing.rotation = wingFlap * 0.4;
rightWing.rotation = -wingFlap * 0.4;
leftWing.scaleY = 0.8 + wingFlap * 0.3;
rightWing.scaleY = 0.8 + wingFlap * 0.3;
// Subtle size pulsing for life-like movement
var sizePulse = 1 + fastSin(self.wingPhase * 0.7) * 0.1;
body.scaleX = sizePulse;
body.scaleY = sizePulse;
// Slight transparency for ethereal sky feel
body.alpha = 0.9;
leftWing.alpha = 0.85;
rightWing.alpha = 0.85;
}
};
return self;
});
var Seaweed = Container.expand(function () {
var self = Container.call(this);
var seaweedGfx = self.attachAsset('seaweed', {
anchorX: 0.5,
anchorY: 1
});
self.speed = -3;
self.swayPhase = Math.random() * Math.PI * 2;
self.height = 80 + Math.random() * 40;
seaweedGfx.scaleY = self.height / 80;
self.update = function () {
if (!self.visible) return;
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
self.swayPhase += 0.06 * throttleFactor;
var sway = fastSin(self.swayPhase) * 0.3;
seaweedGfx.rotation = sway;
seaweedGfx.scaleX = 1 + fastSin(self.swayPhase * 0.7) * 0.1;
var greenShift = Math.floor(fastSin(self.swayPhase) * 30);
seaweedGfx.tint = 0x228b22 + (greenShift << 8);
}
};
return self;
});
var Shark = Container.expand(function () {
var self = Container.call(this);
var sharkGfx = self.attachAsset('shark', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -4; // Same speed as ground movement
self.swimPhase = Math.random() * Math.PI * 2;
self.verticalAmplitude = 0; // No vertical movement
self.baseY = 0;
self.update = function () {
if (!self.visible) return;
// Keep movement smooth
self.x += self.speed;
self.y = self.baseY;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
// Simple scale animation for life-like movement
self.swimPhase += 0.05 * throttleFactor;
var scale = 1 + fastSin(self.swimPhase * 0.8) * 0.08;
sharkGfx.scaleX = scale;
sharkGfx.scaleY = scale;
// No rotation for straight movement
sharkGfx.rotation = 0;
var colorShift = Math.floor(fastSin(self.swimPhase) * 30);
sharkGfx.tint = 0x333333 + colorShift;
}
};
return self;
});
var ShatterPart = Container.expand(function () {
var self = Container.call(this);
var shatterGfx = self.attachAsset('shatter', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.rotationSpeed = 0;
self.life = 60;
self.maxLife = 60;
self.gravity = 0.2;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
self.velocityX *= 0.96;
shatterGfx.rotation += self.rotationSpeed;
self.life--;
var alpha = self.life / self.maxLife;
shatterGfx.alpha = alpha;
var scale = alpha * 0.8;
shatterGfx.scaleX = scale;
shatterGfx.scaleY = scale;
if (self.life <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var SunRay = Container.expand(function () {
var self = Container.call(this);
var rayGfx = self.attachAsset('sunRay', {
anchorX: 0,
anchorY: 0.5
});
self.speed = -1.5;
self.fadePhase = 0;
self.maxFadePhase = 600;
self.glowPhase = Math.random() * Math.PI * 2;
self.angle = (Math.random() - 0.5) * 0.3;
rayGfx.rotation = self.angle;
rayGfx.alpha = 0.3;
self.update = function () {
self.x += self.speed;
self.fadePhase++;
self.glowPhase += 0.02;
// Fade in and out effect
var fadeProgress = self.fadePhase / self.maxFadePhase;
var fadeAlpha;
if (fadeProgress < 0.3) {
fadeAlpha = fadeProgress / 0.3;
} else if (fadeProgress > 0.7) {
fadeAlpha = (1 - fadeProgress) / 0.3;
} else {
fadeAlpha = 1;
}
// Gentle glow pulsing
var glow = 0.8 + fastSin(self.glowPhase) * 0.2;
rayGfx.alpha = fadeAlpha * glow * 0.25;
if (self.fadePhase >= self.maxFadePhase) {
self.shouldDestroy = true;
}
};
return self;
});
var TrailParticle = Container.expand(function () {
var self = Container.call(this);
var particleGfx = self.attachAsset('trailGlow', {
anchorX: 0.5,
anchorY: 0.5
});
self.life = 40;
self.maxLife = 40;
self.targetX = 0;
self.targetY = 0;
self.followSpeed = 0.12;
self.pulsePhase = Math.random() * Math.PI * 2;
self.update = function () {
// Follow target with smooth movement
self.x += (self.targetX - self.x) * self.followSpeed;
self.y += (self.targetY - self.y) * self.followSpeed;
self.life--;
self.pulsePhase += 0.15;
// Bright motion trail that pulses with beat
var fadeAlpha = self.life / self.maxLife;
var pulseEffect = 1 + fastSin(self.pulsePhase) * 0.3 + beatPulse * 0.4;
particleGfx.alpha = fadeAlpha * 0.8 * pulseEffect;
var scale = fadeAlpha * 1.2 * pulseEffect;
particleGfx.scaleX = scale;
particleGfx.scaleY = scale;
// Simplified tint for mobile
if (isMobile && LK.ticks % 2 !== 0) {
// Skip tint updates on odd frames for mobile
} else {
// Update tint
if (gamePhase === 5) {
particleGfx.tint = player ? player.comboStreakColor : 0x00ccff;
} else if (gamePhase === 4) {
particleGfx.tint = player ? player.comboStreakColor : 0xff3333;
} else if (gamePhase === 3) {
particleGfx.tint = player ? player.comboStreakColor : 0xffee99;
} else if (gamePhase === 2) {
particleGfx.tint = player ? player.comboStreakColor : 0xff8800;
} else {
particleGfx.tint = player ? player.comboStreakColor : 0x00aaff;
}
}
if (self.life <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var Tree = Container.expand(function () {
var self = Container.call(this);
// Tree trunk
var trunk = self.attachAsset('tree', {
anchorX: 0.5,
anchorY: 1
});
// Tree leaves
var leaves = self.attachAsset('treeLeaves', {
anchorX: 0.5,
anchorY: 1
});
leaves.y = -120;
leaves.scaleX = 1.8;
leaves.scaleY = 1.8;
// Larger trunk for nature phase
trunk.scaleX = 1.5;
trunk.scaleY = 1.5;
self.speed = -6;
self.swayPhase = Math.random() * Math.PI * 2;
self.update = function () {
if (!self.visible) return;
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 4 : 2;
if (LK.ticks % throttleFactor === 0) {
// Gentle swaying animation
self.swayPhase += 0.03 * throttleFactor;
var sway = fastSin(self.swayPhase) * 0.1;
trunk.rotation = sway;
leaves.rotation = sway * 1.5;
// Slight scale variation for life
var breathe = 1 + fastSin(self.swayPhase * 0.7) * 0.05;
leaves.scaleX = 1.8 * breathe;
leaves.scaleY = 1.8 * breathe;
}
};
return self;
});
var WaterCurrent = Container.expand(function () {
var self = Container.call(this);
var currentGfx = self.attachAsset('waterCurrent', {
anchorX: 0,
anchorY: 0.5
});
self.speed = -2 - Math.random() * 3;
self.flowPhase = Math.random() * Math.PI * 2;
self.baseAlpha = 0.3 + Math.random() * 0.2;
self.update = function () {
self.x += self.speed;
// Throttle visual updates
var throttleFactor = isMobile ? 3 : 2;
if (LK.ticks % throttleFactor === 0) {
self.flowPhase += 0.1 * throttleFactor;
currentGfx.alpha = self.baseAlpha + fastSin(self.flowPhase) * 0.2;
currentGfx.scaleY = 0.8 + fastSin(self.flowPhase * 1.5) * 0.4;
var colorShift = Math.floor(fastSin(self.flowPhase) * 40);
currentGfx.tint = 0x4488aa + (colorShift << 4);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0520
});
/****
* Game Code
****/
// Mobile detection - handle undefined navigator in sandbox
var isMobile = false;
try {
if (typeof navigator !== 'undefined' && navigator.userAgent) {
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
} catch (e) {
// Navigator not available in sandbox, default to false
isMobile = false;
}
// Trigonometric Lookup Tables for performance
var sinLUT = [];
var cosLUT = [];
var LUT_SIZE = 360; // 360 entries for ~1-degree precision
var LUT_FACTOR = LUT_SIZE / (Math.PI * 2);
for (var i = 0; i < LUT_SIZE; i++) {
var angle = i / LUT_FACTOR;
sinLUT.push(Math.sin(angle));
cosLUT.push(Math.cos(angle));
}
function fastSin(rad) {
var index = Math.floor(rad * LUT_FACTOR);
return sinLUT[(index % LUT_SIZE + LUT_SIZE) % LUT_SIZE];
}
function fastCos(rad) {
var index = Math.floor(rad * LUT_FACTOR);
return cosLUT[(index % LUT_SIZE + LUT_SIZE) % LUT_SIZE];
}
// Pre-calculated color values
var colorCache = {
beatColors: [],
phaseColors: {
normal: {
head: 0x00ccff,
body: 0x0099ff,
arms: 0x0088ff,
legs: 0x0077ff,
glow: 0x0088ff
},
speed: {
head: 0xff9900,
body: 0xff7700,
arms: 0xff6600,
legs: 0xff5500,
glow: 0xff8800
},
sky: {
head: 0xffffdd,
body: 0xffeeaa,
arms: 0xffdd88,
legs: 0xffcc66,
glow: 0xffee99
},
nature: {
head: 0xff0000,
body: 0x0066ff,
arms: 0xffcc99,
legs: 0x8b4513,
glow: 0xff6666
},
underwater: {
head: 0x00cccc,
body: 0x0099cc,
arms: 0x0088bb,
legs: 0x0077aa,
glow: 0x00ccff
}
}
};
// Pre-calculate beat colors
for (var i = 0; i <= 100; i++) {
colorCache.beatColors[i] = Math.floor(i * 0.8);
}
// Game variables
var player;
var playerLives = 3;
var maxLives = 5;
var hearts = [];
var heartSpawnTimer = 0;
var heartSpawnInterval = 900; // 15 seconds at 60fps
var lifePopupTimer = 0;
var lifePopupText = null;
var collisionCooldown = 0; // Prevent rapid successive damage
var invincibilityFrames = 0; // Global invincibility system
var maxInvincibilityFrames = 120; // 2 seconds at 60fps
var isPlayerFlashing = false;
var obstacles = [];
var rotatingCubes = [];
var laserBeams = [];
var explosionParts = [];
var shatterParts = [];
var particles = [];
var trailParticles = [];
var ambientParticles = [];
var burstParticles = [];
// Object pools for particle recycling
var explosionPartsPool = [];
var shatterPartsPool = [];
var particlesPool = [];
var trailParticlesPool = [];
var burstParticlesPool = [];
var ambientParticlesPool = [];
var obstaclesPool = [];
var rotatingCubesPool = [];
var laserBeamsPool = [];
var cloudsPool = [];
var seagullsPool = [];
var treesPool = [];
var rocksPool = [];
var bushesPool = [];
var heartsPool = [];
var fishPool = [];
var sharksPool = [];
var seaweedPool = [];
var coralsPool = [];
var bubblesPool = [];
var backgroundShapes = [];
var lightLines = [];
var dataStreamParticles = [];
var holographicElements = [];
var backgroundShapesPool = [];
var lightLinesPool = [];
var dataStreamParticlesPool = [];
var holographicElementsPool = [];
var groundLevel = 2200;
var gameSpeed = 6;
var spawnTimer = 0;
var beatTimer = 0;
var beatInterval = 60; // 60 frames = 1 second at 60fps
var beatPulse = 0;
var combo = 0;
var perfectHits = 0;
var trailTimer = 0;
var ambientTimer = 0;
var bgShapeTimer = 0;
var lightLineTimer = 0;
var dataStreamTimer = 0;
var holographicTimer = 0;
// Game phase variables - fixed progression
var gamePhase = 1; // 1 = normal, 2 = speed phase at 1000 points, 3 = sky level at 2000 points, 4 = nature phase at 3000 points
var speedTransitioned = false;
var skyTransitioned = false;
var natureTransitioned = false;
var underwaterTransitioned = false;
// Underwater phase elements
var bubbles = [];
var fish = [];
var sharks = [];
var seaweed = [];
var corals = [];
var waterCurrents = [];
var waterCurrentsPool = [];
var underwaterPlatformTop = null;
var underwaterPlatformBottom = null;
var bubbleTimer = 0;
var fishSpawnTimer = 0;
var sharkSpawnTimer = 0;
var seaweedSpawnTimer = 0;
var coralSpawnTimer = 0;
var currentSpawnTimer = 0;
var clouds = [];
var sunRays = [];
var sunRaysPool = [];
var seagulls = [];
var sunRayTimer = 0;
var cloudSpawnTimer = 0;
var seagullSpawnTimer = 0;
// Nature phase elements
var trees = [];
var rocks = [];
var bushes = [];
var waterPuddles = [];
var natureGround = [];
var treeSpawnTimer = 0;
var rockSpawnTimer = 0;
var bushSpawnTimer = 0;
var puddleSpawnTimer = 0;
// Camera variables
var cameraTargetX = 0;
var cameraTargetY = 0;
var cameraShakeIntensity = 0;
var cameraShakeDecay = 0.9;
var cameraZoom = 1;
var cameraTargetZoom = 1;
var cameraFollowSpeed = 0.1;
// UI
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0x00FFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 100;
scoreText.y = 100;
LK.gui.topLeft.addChild(scoreText);
var comboText = new Text2('Combo: 0', {
size: 60,
fill: 0xFFFF00
});
comboText.anchor.set(0, 0);
comboText.x = 100;
comboText.y = 200;
LK.gui.topLeft.addChild(comboText);
// Lives display in top right - using heart symbols
var livesContainer = new Container();
livesContainer.x = -100;
livesContainer.y = 100;
LK.gui.topRight.addChild(livesContainer);
var heartSymbols = [];
function updateLivesDisplay() {
// Clear existing hearts
for (var i = 0; i < heartSymbols.length; i++) {
livesContainer.removeChild(heartSymbols[i]);
heartSymbols[i].destroy();
}
heartSymbols = [];
// Create heart symbols for current lives
for (var i = 0; i < playerLives; i++) {
var heartSymbol = LK.getAsset('heart', {
anchorX: 1,
anchorY: 0,
scaleX: 0.8,
scaleY: 0.8
});
heartSymbol.x = -i * 35;
heartSymbol.y = 0;
heartSymbol.tint = 0xff3366;
livesContainer.addChild(heartSymbol);
heartSymbols.push(heartSymbol);
}
}
// Initialize lives display
updateLivesDisplay();
// Create ground with shimmer effects
var ground = [];
for (var i = 0; i < 12; i++) {
var groundSegment = new GroundSegment();
groundSegment.x = i * 300;
groundSegment.y = groundLevel;
ground.push(groundSegment);
game.addChild(groundSegment);
}
// Create nature ground segments for phase 4
for (var i = 0; i < 15; i++) {
var natureGroundSegment = new NatureGroundSegment();
natureGroundSegment.x = i * 200 - 300; // Use 200px spacing for 500px wide segments to create 300px overlap
natureGroundSegment.y = groundLevel;
natureGround.push(natureGroundSegment);
game.addChild(natureGroundSegment);
// Initially hide nature ground
natureGroundSegment.visible = false;
}
// Create background container
var backgroundContainer = new Container();
game.addChild(backgroundContainer);
// Add custom nature background image
var natureBackgroundImage = LK.getAsset('natureBackground', {
anchorX: 0,
anchorY: 0,
scaleX: 1,
scaleY: 1,
x: -400
});
backgroundContainer.addChild(natureBackgroundImage);
// Initially hide the background
backgroundContainer.visible = false;
// Create player
player = new Player();
player.x = 300;
player.y = groundLevel - 30;
game.addChild(player);
// Background pulse effect
function updateBackgroundPulse() {
var intensity = beatPulse * 0.2;
var fastSection = gameSpeed > 8 ? 1 : 0; // Detect fast sections
if (gamePhase === 5) {
// Underwater phase - deep ocean blue with green tints
var baseRed = 10 + Math.floor(intensity * 20) + fastSection * 10;
var baseGreen = 60 + Math.floor(intensity * 40) + fastSection * 20;
var baseBlue = 120 + Math.floor(intensity * 60) + fastSection * 40;
} else if (gamePhase === 4) {
// Nature phase - Mario classic bright green and blue palette
var baseRed = 70 + Math.floor(intensity * 20) + fastSection * 10;
var baseGreen = 180 + Math.floor(intensity * 50) + fastSection * 30;
var baseBlue = 255;
} else if (gamePhase === 3) {
// Sky phase - gradient from soft blue to pinkish-purple
var baseRed = 180 + Math.floor(intensity * 40) + fastSection * 20;
var baseGreen = 220 + Math.floor(intensity * 20) + fastSection * 10;
var baseBlue = 255;
} else if (gamePhase === 2) {
// Speed phase - orange/red colors
var baseRed = 60 + Math.floor(intensity * 50) + fastSection * 40;
var baseGreen = 20 + Math.floor(intensity * 30) + fastSection * 20;
var baseBlue = 5 + Math.floor(intensity * 15) + fastSection * 10;
} else {
// Normal phase - deep black to dark purple with intensity variations
var baseRed = 10 + Math.floor(intensity * 30) + fastSection * 20;
var baseGreen = 5 + Math.floor(intensity * 20) + fastSection * 10;
var baseBlue = 32 + Math.floor(intensity * 40) + fastSection * 30;
}
var backgroundColor = baseRed << 16 | baseGreen << 8 | baseBlue;
game.setBackgroundColor(backgroundColor);
}
// Camera system with smooth scrolling and beat sync
function updateCamera() {
// Smooth camera follow player
cameraTargetX = -player.x + 400; // Offset to keep player visible
// Adjust vertical camera position based on game phase
if (gamePhase === 4) {
// Nature phase - much higher camera position for elevated view
cameraTargetY = -player.y + 1200; // Much higher view for nature phase
} else {
// Other phases - standard centering
cameraTargetY = -player.y + 1600; // Vertical centering
}
// Apply smooth camera movement
game.x += (cameraTargetX - game.x) * cameraFollowSpeed;
game.y += (cameraTargetY - game.y) * cameraFollowSpeed;
// Apply camera shake (disabled in nature phase)
if (cameraShakeIntensity > 0 && gamePhase !== 4) {
game.x += (Math.random() - 0.5) * cameraShakeIntensity;
game.y += (Math.random() - 0.5) * cameraShakeIntensity;
cameraShakeIntensity *= cameraShakeDecay;
}
// Beat-synced zoom effect with smooth interpolation (disabled in nature phase)
var beatZoomEffect = gamePhase === 4 ? 1 : 1 + beatPulse * 0.08; // Disable beat zoom in nature phase
// Base zoom levels for different phases
var baseZoom = 1.0;
if (gamePhase === 4) {
// Nature phase - zoomed in for closer detail
baseZoom = 1.3;
} else if (gamePhase === 3) {
// Sky level - slightly closer view
baseZoom = 1.15;
}
// Combine base zoom with beat effect
cameraTargetZoom = baseZoom * beatZoomEffect;
// Smooth zoom interpolation
cameraZoom += (cameraTargetZoom - cameraZoom) * 0.05;
game.scaleX = cameraZoom;
game.scaleY = cameraZoom;
}
// Spawn obstacles on beat
function spawnObstacle() {
if (gamePhase === 5) {
// Underwater phase - spawn aquatic obstacles with increased frequency for fish, sharks, coral and seaweed
var obstacleType = Math.random();
if (obstacleType < 0.45) {
// Increased from 0.35 to 0.45 - more fish/piranhas
spawnFish();
} else if (obstacleType < 0.60) {
spawnSeaweed();
} else if (obstacleType < 0.75) {
spawnTopSeaweed();
} else if (obstacleType < 0.90) {
// Increased from 0.85 to 0.90 - more sharks
spawnShark();
} else {
spawnCoral();
}
return;
} else if (gamePhase === 4) {
// Nature phase - spawn natural obstacles with reduced frequency
var obstacleType = Math.random();
if (obstacleType < 0.3) {
// Reduced from 0.5 to 0.3 - fewer rocks
spawnRock();
} else if (obstacleType < 0.45) {
// Reduced bush spawn frequency - only spawn between 0.3-0.45
spawnBush();
}
// No obstacle spawned for obstacleType >= 0.45 (55% chance of no spawn)
return;
} else if (gamePhase === 3) {
// Sky level - mostly spawn clouds instead of obstacles
if (Math.random() < 0.3) {
spawnCloud();
}
return;
}
var obstacleType = Math.random();
if (obstacleType < 0.4) {
// Regular obstacle
var obstacle;
if (obstaclesPool.length > 0) {
obstacle = obstaclesPool.pop();
obstacle.visible = true;
} else {
obstacle = new Obstacle();
game.addChild(obstacle);
}
obstacle.x = 2200;
obstacle.y = groundLevel;
obstacle.pulsePhase = 0;
obstacles.push(obstacle);
} else if (obstacleType < 0.7) {
// Rotating cube
var cube;
if (rotatingCubesPool.length > 0) {
cube = rotatingCubesPool.pop();
cube.visible = true;
} else {
cube = new RotatingCube();
game.addChild(cube);
}
cube.x = 2200;
cube.y = groundLevel - 60 - Math.random() * 80;
cube.pulsePhase = 0;
cube.rotation = 0;
rotatingCubes.push(cube);
} else {
// Laser beam
var laser;
if (laserBeamsPool.length > 0) {
laser = laserBeamsPool.pop();
laser.visible = true;
} else {
laser = new LaserBeam();
game.addChild(laser);
}
laser.x = 2200;
laser.y = groundLevel - 200;
laser.isActive = false;
laser.warningTimer = 60;
laser.direction = 1;
laser.coreY = -laser.maxTravel;
laser.children[1].alpha = 0; // laserBeam
laser.children[0].y = -laser.maxTravel; // laserCore
laserBeams.push(laser);
}
}
// Create player trail particles with limit
function createTrailParticles() {
trailTimer++;
var interval = isMobile ? 8 : 4; // Double interval on mobile
var maxParticles = isMobile ? 8 : 15; // Half particles on mobile
if (trailTimer >= interval && trailParticles.length < maxParticles) {
trailTimer = 0;
var trail;
// Try to get from pool first
if (trailParticlesPool.length > 0) {
trail = trailParticlesPool.pop();
trail.visible = true;
trail.life = trail.maxLife;
} else {
trail = new TrailParticle();
}
trail.x = player.x + (Math.random() - 0.5) * 20;
trail.y = player.y + (Math.random() - 0.5) * 20;
trail.targetX = player.x - 50;
trail.targetY = player.y + 10;
trailParticles.push(trail);
if (!trail.parent) {
game.addChild(trail);
}
}
}
// Create radial burst particles for perfect hits
function createPerfectBurst(x, y, color) {
for (var i = 0; i < 12; i++) {
var angle = i / 12 * Math.PI * 2;
var speed = 8 + Math.random() * 4;
var burst;
if (burstParticlesPool.length > 0) {
burst = burstParticlesPool.pop();
burst.visible = true;
burst.life = burst.maxLife;
} else {
burst = new BurstParticle();
}
burst.x = x;
burst.y = y;
burst.velocityX = fastCos(angle) * speed;
burst.velocityY = fastSin(angle) * speed;
var burstGfx = burst.children[0];
burstGfx.tint = color;
burstParticles.push(burst);
if (!burst.parent) {
game.addChild(burst);
}
}
}
// Create ambient floating particles with limit
function createAmbientParticles() {
ambientTimer++;
if (ambientTimer >= 30 && ambientParticles.length < 20) {
// Limit to 20 ambient particles
ambientTimer = 0;
var ambient;
if (ambientParticlesPool.length > 0) {
ambient = ambientParticlesPool.pop();
ambient.visible = true;
ambient.life = ambient.maxLife;
} else {
ambient = new AmbientParticle();
}
ambient.x = player.x + 1200 + Math.random() * 400;
ambient.y = Math.random() * 2732;
var ambientGfx = ambient.children[0];
ambientGfx.tint = 0x888888 + Math.floor(Math.random() * 0x444444);
ambientParticles.push(ambient);
if (!ambient.parent) {
game.addChild(ambient);
}
}
}
// Create beat-synced particle wave
function createBeatWave() {
for (var i = 0; i < 8; i++) {
var wave;
if (burstParticlesPool.length > 0) {
wave = burstParticlesPool.pop();
wave.visible = true;
wave.life = 60;
wave.maxLife = 60;
} else {
wave = new BurstParticle();
}
wave.x = -200 + i * 300;
wave.y = groundLevel - 100;
wave.velocityX = 6;
wave.velocityY = -2 + (Math.random() - 0.5) * 4;
wave.life = 60;
wave.maxLife = 60;
var waveGfx = wave.children[0];
waveGfx.tint = 0x00ffff;
burstParticles.push(wave);
if (!wave.parent) {
game.addChild(wave);
}
}
}
// Create background abstract shapes
function createBackgroundShapes() {
bgShapeTimer++;
if (bgShapeTimer >= 120) {
bgShapeTimer = 0;
var bgShape;
if (backgroundShapesPool.length > 0) {
bgShape = backgroundShapesPool.pop();
bgShape.visible = true;
} else {
bgShape = new BackgroundShape();
}
bgShape.x = 2200 + Math.random() * 400;
bgShape.y = Math.random() * 2000;
backgroundShapes.push(bgShape);
if (!bgShape.parent) {
game.addChild(bgShape);
}
}
}
// Create flowing light lines
function createLightLines() {
lightLineTimer++;
if (lightLineTimer >= 180) {
lightLineTimer = 0;
var lightLine;
if (lightLinesPool.length > 0) {
lightLine = lightLinesPool.pop();
lightLine.visible = true;
} else {
lightLine = new LightLine();
}
lightLine.x = 2200;
lightLine.y = Math.random() * 2732;
lightLine.rotation = (Math.random() - 0.5) * 0.5;
lightLines.push(lightLine);
if (!lightLine.parent) {
game.addChild(lightLine);
}
}
}
// Create data stream particles
function createDataStreamParticles() {
dataStreamTimer++;
if (dataStreamTimer >= 15) {
dataStreamTimer = 0;
var dataStream;
if (dataStreamParticlesPool.length > 0) {
dataStream = dataStreamParticlesPool.pop();
dataStream.visible = true;
dataStream.life = 300 + Math.random() * 200;
dataStream.maxLife = dataStream.life;
dataStream.shouldDestroy = false;
} else {
dataStream = new DataStreamParticle();
}
dataStream.x = 2200 + Math.random() * 200;
dataStream.y = Math.random() * 2732;
dataStreamParticles.push(dataStream);
if (!dataStream.parent) {
game.addChild(dataStream);
}
}
}
// Create holographic musical elements
function createHolographicElements() {
holographicTimer++;
if (holographicTimer >= 600) {
// Less frequent appearance
holographicTimer = 0;
var holographic;
if (holographicElementsPool.length > 0) {
holographic = holographicElementsPool.pop();
holographic.visible = true;
holographic.fadePhase = 0;
holographic.shouldDestroy = false;
} else {
holographic = new HolographicElement();
}
holographic.x = 1500 + Math.random() * 800;
holographic.y = 200 + Math.random() * 1800;
holographicElements.push(holographic);
if (!holographic.parent) {
game.addChild(holographic);
}
}
}
// Create explosion effect when obstacle is destroyed
function createExplosion(x, y, color) {
// Reduce particle count on mobile
var particleCount = isMobile ? 4 : 8;
for (var i = 0; i < particleCount; i++) {
var explosion;
// Try to get from pool first
if (explosionPartsPool.length > 0) {
explosion = explosionPartsPool.pop();
explosion.visible = true;
explosion.life = explosion.maxLife;
} else {
explosion = new ExplosionPart();
}
explosion.x = x + (Math.random() - 0.5) * 40;
explosion.y = y + (Math.random() - 0.5) * 40;
explosion.velocityX = (Math.random() - 0.5) * 12;
explosion.velocityY = -Math.random() * 8 - 2;
explosion.rotationSpeed = (Math.random() - 0.5) * 0.3;
var explosionGfx = explosion.children[0];
explosionGfx.tint = color;
explosionParts.push(explosion);
if (!explosion.parent) {
game.addChild(explosion);
}
}
}
// Create shatter effect when obstacle breaks
function createShatter(x, y, originalColor) {
for (var i = 0; i < 6; i++) {
var shatter = new ShatterPart();
shatter.x = x + (Math.random() - 0.5) * 30;
shatter.y = y + (Math.random() - 0.5) * 30;
shatter.velocityX = (Math.random() - 0.5) * 8;
shatter.velocityY = -Math.random() * 6 - 1;
shatter.rotationSpeed = (Math.random() - 0.5) * 0.2;
var shatterGfx = shatter.children[0];
shatterGfx.tint = originalColor;
shatterParts.push(shatter);
game.addChild(shatter);
}
}
// Spawn floating clouds for sky level
function spawnCloud() {
var cloud;
if (cloudsPool.length > 0) {
cloud = cloudsPool.pop();
cloud.visible = true;
} else {
cloud = new Cloud();
game.addChild(cloud);
}
cloud.x = 2200;
cloud.y = groundLevel - 100 - Math.random() * 300;
cloud.isBouncing = false;
cloud.bouncePhase = 0;
cloud.children[1].scaleY = 1; // cloudCore
cloud.children[0].scaleY = 1; // cloudGlow
clouds.push(cloud);
}
// Create warm sunlight rays
function createSunRays() {
sunRayTimer++;
if (sunRayTimer >= 400) {
sunRayTimer = 0;
var sunRay;
if (sunRaysPool.length > 0) {
sunRay = sunRaysPool.pop();
sunRay.visible = true;
sunRay.fadePhase = 0;
sunRay.shouldDestroy = false;
} else {
sunRay = new SunRay();
}
sunRay.x = 1800 + Math.random() * 600;
sunRay.y = 200 + Math.random() * 800;
sunRays.push(sunRay);
if (!sunRay.parent) {
game.addChild(sunRay);
}
}
}
// Create flying seagulls above clouds
function spawnSeagull() {
var seagull;
if (seagullsPool.length > 0) {
seagull = seagullsPool.pop();
seagull.visible = true;
} else {
seagull = new Seagull();
game.addChild(seagull);
}
seagull.x = 2200 + Math.random() * 200;
seagull.y = groundLevel - 450 - Math.random() * 200; // Spawn 450-650 pixels above ground, well above clouds
seagull.baseY = seagull.y;
seagulls.push(seagull);
}
// Nature phase spawning functions
function spawnTree() {
var tree;
if (treesPool.length > 0) {
tree = treesPool.pop();
tree.visible = true;
} else {
tree = new Tree();
game.addChild(tree);
}
tree.x = 2200;
tree.y = groundLevel;
trees.push(tree);
}
function spawnRock() {
var rock;
if (rocksPool.length > 0) {
rock = rocksPool.pop();
rock.visible = true;
} else {
rock = new Rock();
game.addChild(rock);
}
rock.x = 2200;
rock.y = groundLevel;
rocks.push(rock);
}
function spawnBush() {
var bush;
if (bushesPool.length > 0) {
bush = bushesPool.pop();
bush.visible = true;
} else {
bush = new Bush();
game.addChild(bush);
}
bush.x = 2200;
bush.y = groundLevel;
bushes.push(bush);
}
// Spawn heart pickup
// Underwater phase spawning functions
function spawnBubbles() {
for (var i = 0; i < 3; i++) {
var bubble;
if (bubblesPool.length > 0) {
bubble = bubblesPool.pop();
bubble.visible = true;
} else {
bubble = new Bubble();
game.addChild(bubble);
}
bubble.x = player.x + 1000 + Math.random() * 500;
bubble.y = groundLevel + Math.random() * 400;
bubble.life = 300 + Math.random() * 200;
bubble.maxLife = bubble.life;
bubble.shouldDestroy = false;
bubbles.push(bubble);
}
}
function spawnFish() {
var fishObj;
if (fishPool.length > 0) {
fishObj = fishPool.pop();
fishObj.visible = true;
} else {
fishObj = new Fish();
game.addChild(fishObj);
}
fishObj.x = 2200;
// Spawn fish between underwater platforms (y=910 to y=1790) - respecting platform boundaries
fishObj.y = 910 + Math.random() * (1790 - 910);
fishObj.baseY = fishObj.y;
fish.push(fishObj);
game.addChild(fishObj);
}
function spawnSeaweed() {
var seaweedObj;
if (seaweedPool.length > 0) {
seaweedObj = seaweedPool.pop();
seaweedObj.visible = true;
} else {
seaweedObj = new Seaweed();
game.addChild(seaweedObj);
}
seaweedObj.x = 2200;
// Spawn seaweed at bottom platform level (deniz tabanında)
seaweedObj.y = 1800;
seaweed.push(seaweedObj);
game.addChild(seaweedObj);
}
function spawnTopSeaweed() {
var seaweedObj;
if (seaweedPool.length > 0) {
seaweedObj = seaweedPool.pop();
seaweedObj.visible = true;
} else {
seaweedObj = new Seaweed();
game.addChild(seaweedObj);
}
seaweedObj.x = 2200;
// Flip seaweed upside down to hang from top platform
seaweedObj.scaleY = -1;
// Position to completely eliminate gap with platform
seaweedObj.y = 900;
seaweed.push(seaweedObj);
game.addChild(seaweedObj);
}
function spawnCoral() {
var coral;
if (coralsPool.length > 0) {
coral = coralsPool.pop();
coral.visible = true;
} else {
coral = new Coral();
game.addChild(coral);
}
coral.x = 2200;
// Spawn coral at bottom platform level (deniz tabanında)
coral.y = 1800;
corals.push(coral);
game.addChild(coral);
}
function spawnWaterCurrent() {
var current;
if (waterCurrentsPool.length > 0) {
current = waterCurrentsPool.pop();
current.visible = true;
} else {
current = new WaterCurrent();
}
current.x = 2200;
// Spawn water currents between underwater platforms (y=910 to y=1790) - respecting platform boundaries
current.y = 910 + Math.random() * (1790 - 910);
waterCurrents.push(current);
if (!current.parent) {
game.addChild(current);
}
}
function spawnShark() {
var shark;
if (sharksPool.length > 0) {
shark = sharksPool.pop();
shark.visible = true;
} else {
shark = new Shark();
game.addChild(shark);
}
shark.x = 2200;
// Spawn sharks between underwater platforms (y=910 to y=1790) - respecting platform boundaries
shark.y = 910 + Math.random() * (1790 - 910);
shark.baseY = shark.y;
sharks.push(shark);
game.addChild(shark);
}
function spawnHeart() {
var heart;
if (heartsPool.length > 0) {
heart = heartsPool.pop();
heart.visible = true;
} else {
heart = new Heart();
game.addChild(heart);
}
heart.x = 2200;
heart.isPulsing = false;
heart.pulseTimer = 0;
// Spawn hearts above clouds in sky level (where seagulls are)
if (gamePhase === 3) {
// Sky level - spawn above clouds, at seagull level
heart.y = groundLevel - 450 - Math.random() * 200; // Same height as seagulls
} else if (gamePhase === 5) {
// Underwater phase - spawn between platforms (y=910 to y=1790)
heart.y = 910 + Math.random() * (1790 - 910);
} else {
// Normal/speed phase - original position
heart.y = groundLevel - 200 - Math.random() * 300;
}
hearts.push(heart);
game.addChild(heart);
}
// Create life gain popup
function showLifeGainPopup(amount) {
if (lifePopupText) {
lifePopupText.destroy();
}
var popupText = '+' + amount + ' LIFE';
lifePopupText = new Text2(popupText, {
size: 80,
fill: 0xff3366
});
lifePopupText.anchor.set(0.5, 0.5);
lifePopupText.x = 1024;
lifePopupText.y = 1000;
lifePopupText.alpha = 0;
game.addChild(lifePopupText);
// Animate popup
tween(lifePopupText, {
alpha: 1,
y: 800
}, {
duration: 300,
easing: tween.easeOut
});
// Fade out after showing
LK.setTimeout(function () {
if (lifePopupText) {
tween(lifePopupText, {
alpha: 0,
y: 600
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
if (lifePopupText) {
lifePopupText.destroy();
lifePopupText = null;
}
}
});
}
}, 1500);
lifePopupTimer = 120; // 2 seconds
}
// Check collisions
function checkCollisions() {
// Early exit if player is invincible
if (invincibilityFrames > 0 && !player.isDashing) return;
// Spatial partitioning - only check nearby objects
var checkRange = 300; // Only check objects within 300px of player
var playerX = player.x;
var playerY = player.y;
// Alternate collision checks between different obstacle types on different frames
var frameOffset = LK.ticks % 3;
// Check regular obstacles only on frames 0 and 1
if (frameOffset === 0 || frameOffset === 1) {
// Regular obstacle collisions
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Skip if too far away or not visible
if (Math.abs(obstacle.x - playerX) > checkRange || !obstacle.visible) continue;
if (player.intersects(obstacle)) {
if (player.isDashing) {
// Destroy obstacle with explosion effect
createExplosion(obstacle.x, obstacle.y, 0xff4400);
createShatter(obstacle.x, obstacle.y, 0xff0044);
LK.getSound('successHit').play();
var scoreGain = 33; //{gQ} // Reduced to 2/3 (50*2/3=33.33≈33)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
// Start invincibility period
invincibilityFrames = maxInvincibilityFrames;
// Intense camera shake on collision
cameraShakeIntensity = 20;
tween(game, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOut
});
}
});
}
// Remove obstacle with swap-and-pop
var obj = obstacles[i];
obstacles[i] = obstacles[obstacles.length - 1];
obstacles.pop();
obj.visible = false;
if (obstaclesPool.length < 20) {
obstaclesPool.push(obj);
} else {
obj.destroy();
}
}
}
}
// Check rotating cubes only on frames 1 and 2
if (frameOffset === 1 || frameOffset === 2) {
// Rotating cube collisions
for (var i = rotatingCubes.length - 1; i >= 0; i--) {
var cube = rotatingCubes[i];
// Skip if too far away or not visible
if (Math.abs(cube.x - playerX) > checkRange || !cube.visible) continue;
if (player.intersects(cube)) {
if (player.isDashing) {
// Destroy cube with spectacular explosion
createExplosion(cube.x, cube.y, 0xff8800);
createShatter(cube.x, cube.y, 0xff4400);
LK.getSound('successHit').play();
var scoreGain = 47; //{hx} // Reduced to 2/3 (70*2/3=46.67≈47)
LK.setScore(LK.getScore() + scoreGain);
combo++;
cameraShakeIntensity = 8;
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
// Start invincibility period
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 25;
}
// Remove cube with swap-and-pop
var obj = rotatingCubes[i];
rotatingCubes[i] = rotatingCubes[rotatingCubes.length - 1];
rotatingCubes.pop();
obj.visible = false;
if (rotatingCubesPool.length < 20) {
rotatingCubesPool.push(obj);
} else {
obj.destroy();
}
}
}
}
// Check laser beams only on frames 0 and 2 (lasers are more critical)
if (frameOffset === 0 || frameOffset === 2) {
// Laser beam collisions
for (var i = laserBeams.length - 1; i >= 0; i--) {
var laser = laserBeams[i];
// Skip if not visible or not active
if (!laser.visible) continue;
if (laser.isActive) {
// Check collision specifically with the laser core (moving white circle), not the red beam
var laserBeam = laser.children[1]; // The red laser beam component
var laserCore = laser.children[0]; // The moving white core component
// Only damage player if they hit the moving laser core (white circle)
var hitLaserCore = player.intersects(laserCore);
if (hitLaserCore) {
if (player.isDashing) {
// Player can dash through laser - destroy it with explosion effect
createExplosion(laser.x, laser.y, 0xff0088);
createShatter(laser.x, laser.y, 0xff0088);
LK.getSound('successHit').play();
var scoreGain = 67; // Reduced to 2/3 (100*2/3=66.67≈67)
LK.setScore(LK.getScore() + scoreGain);
combo++;
cameraShakeIntensity = 12;
// Remove laser with swap-and-pop
var obj = laserBeams[i];
laserBeams[i] = laserBeams[laserBeams.length - 1];
laserBeams.pop();
obj.visible = false;
if (laserBeamsPool.length < 10) {
laserBeamsPool.push(obj);
} else {
obj.destroy();
}
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible and not dashing
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0088, 600);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
// Start invincibility period
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 30;
// Create explosion at collision point
createExplosion(player.x, player.y, 0xff0088);
// Remove laser with swap-and-pop
var obj = laserBeams[i];
laserBeams[i] = laserBeams[laserBeams.length - 1];
laserBeams.pop();
obj.visible = false;
if (laserBeamsPool.length < 10) {
laserBeamsPool.push(obj);
} else {
obj.destroy();
}
}
}
}
}
}
// Cloud collisions for sky level (always check for platform mechanics)
for (var i = 0, len = clouds.length; i < len; i++) {
var cloud = clouds[i];
// Skip if not visible
if (!cloud.visible) continue;
if (player.intersects(cloud)) {
// Landing on cloud
if (player.velocityY > 0 && player.y < cloud.y) {
player.y = cloud.y - 30;
player.velocityY = 0; // Stop bouncing
player.isGrounded = true; // Enable jumping
player.canDoubleJump = false; // Reset to allow first jump from cloud
player.hasUsedDoubleJump = false;
player.hasJumped = false; // Reset jump flag when landing on cloud
cloud.bounce();
// Soft landing sound effect
if (Math.random() < 0.5) {
LK.getSound('windWhoosh').play();
}
LK.setScore(LK.getScore() + 20); // Reduced to 2/3 (30*2/3=20)
}
}
}
// Seagull collisions - can land on seagulls
for (var i = 0, len = seagulls.length; i < len; i++) {
var seagull = seagulls[i];
// Skip if not visible
if (!seagull.visible) continue;
if (player.intersects(seagull)) {
// Landing on seagull
if (player.velocityY > 0 && player.y < seagull.y) {
player.y = seagull.y - 35;
player.velocityY = 0;
player.isGrounded = true;
player.canDoubleJump = false; // Reset to allow first jump from seagull
player.hasUsedDoubleJump = false;
player.hasJumped = false; // Reset jump flag when landing on seagull
// Gentle landing sound
if (Math.random() < 0.3) {
LK.getSound('echoChime').play();
}
LK.setScore(LK.getScore() + 27); // Reduced to 2/3 (40*2/3=26.67≈27)
}
}
}
// Rock collisions (nature phase) - rocks always damage player
for (var i = rocks.length - 1; i >= 0; i--) {
var rock = rocks[i];
// Skip if not visible
if (!rock.visible) continue;
if (player.intersects(rock)) {
if (player.isDashing) {
// Destroy rock with explosion effect
createExplosion(rock.x, rock.y, 0x696969);
createShatter(rock.x, rock.y, 0x696969);
LK.getSound('successHit').play();
var scoreGain = 40; // Reduced to 2/3 (60*2/3=40)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
// Start invincibility period
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 20;
}
// Remove rock with swap-and-pop
var obj = rocks[i];
rocks[i] = rocks[rocks.length - 1];
rocks.pop();
obj.visible = false;
if (rocksPool.length < 15) {
rocksPool.push(obj);
} else {
obj.destroy();
}
}
}
// Bush collisions (nature phase) - bushes damage player
for (var i = bushes.length - 1; i >= 0; i--) {
var bush = bushes[i];
// Skip if not visible
if (!bush.visible) continue;
if (player.intersects(bush)) {
if (player.isDashing) {
// Destroy bush with green explosion
createExplosion(bush.x, bush.y, 0x228B22);
LK.getSound('successHit').play();
var scoreGain = 27; // Reduced to 2/3 (40*2/3=26.67≈27)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
// Start invincibility period
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 15;
}
// Remove bush with swap-and-pop
var obj = bushes[i];
bushes[i] = bushes[bushes.length - 1];
bushes.pop();
obj.visible = false;
if (bushesPool.length < 15) {
bushesPool.push(obj);
} else {
obj.destroy();
}
}
}
// Tree collisions (nature phase) - tree trunks damage player
for (var i = trees.length - 1; i >= 0; i--) {
var tree = trees[i];
// Skip if not visible
if (!tree.visible) continue;
// Adjust the collision detection to be slightly smaller than the leaves graphic
// The treeLeaves graphic is 175px wide and 131.25px tall
// Player graphic is roughly 40px wide and 55px tall (based on body part sizes)
// We need to create a smaller collision box around the leaves.
// The leaves graphic has an anchor of 0.5, 1.
// Its y position is tree.y - 120.
// The top of the leaves graphic is at tree.y - 120 - 131.25 * leaves.scaleY * 0.5 (since anchor is 0.5, 1).
// With leaves.scaleY = 1.8, the top is approximately tree.y - 120 - 131.25 * 1.8 * 0.5 = tree.y - 120 - 118.125 = tree.y - 238.125
// The bottom of the leaves graphic is at tree.y - 120 + 131.25 * leaves.scaleY * 0.5 = tree.y - 120 + 118.125 = tree.y - 1.875
// The left edge is at tree.x - 175 * leaves.scaleX * 0.5 = tree.x - 175 * 1.8 * 0.5 = tree.x - 157.5
// The right edge is at tree.x + 175 * leaves.scaleX * 0.5 = tree.x + 175 * 1.8 * 0.5 = tree.x + 157.5
// The player's collision box is roughly centered around player.x and player.y
// Player top is player.y - 35, player bottom is player.y + 35.
// Player left is player.x - 20, player right is player.x + 20.
// To reduce the collision area of the leaves, we can check if the player's bounding box intersects with a smaller rectangle within the leaves.
// Let's make the collision area of the leaves a bit smaller on all sides.
// We can define a reduced bounding box for the leaves.
var leavesCollisionTop = tree.y - 120 - 131.25 * tree.children[1].scaleY * 0.5 + 25; // Move collision area 25px down from the top of the leaves
var leavesCollisionBottom = tree.y - 120 + 131.25 * tree.children[1].scaleY * 0.5 - 15; // Move collision area 15px up from the bottom of the leaves
var leavesCollisionLeft = tree.x - 175 * tree.children[1].scaleX * 0.5 + 15; // Move collision area 15px right from the left of the leaves
var leavesCollisionRight = tree.x + 175 * tree.children[1].scaleX * 0.5 - 15; // Move collision area 15px left from the right of the leaves
// Check if the player's bounding box intersects with the reduced leaves collision box
var playerTop = player.y - 35;
var playerBottom = player.y + 35;
var playerLeft = player.x - 20;
var playerRight = player.x + 20;
var intersectsLeaves = !(playerBottom < leavesCollisionTop || playerTop > leavesCollisionBottom || playerRight < leavesCollisionLeft || playerLeft > leavesCollisionRight);
if (intersectsLeaves) {
if (player.isDashing) {
// Destroy tree with explosion effect
createExplosion(tree.x, tree.y, 0x8B4513);
createShatter(tree.x, tree.y, 0x228B22);
LK.getSound('successHit').play();
var scoreGain = 53; // Reduced to 2/3 (80*2/3=53.33≈53)
LK.setScore(LK.getScore() + scoreGain);
combo++;
// Remove tree with swap-and-pop
var obj = trees[i];
trees[i] = trees[trees.length - 1];
trees.pop();
obj.visible = false;
if (treesPool.length < 10) {
treesPool.push(obj);
} else {
obj.destroy();
}
} else if (invincibilityFrames === 0) {
// Tree trunk damages player (only if not invincible)
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
// Start invincibility period
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 20;
}
}
}
// Fish collisions (underwater phase)
for (var i = fish.length - 1; i >= 0; i--) {
var fishObj = fish[i];
// Skip if not visible
if (!fishObj.visible) continue;
if (player.intersects(fishObj)) {
if (player.isDashing) {
// Destroy fish with splash effect
createExplosion(fishObj.x, fishObj.y, 0xff6600);
LK.getSound('successHit').play();
var scoreGain = 33; //{kg} // Reduced to 2/3 (50*2/3=33.33≈33)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 20;
}
var obj = fish[i];
fish[i] = fish[fish.length - 1];
fish.pop();
obj.visible = false;
if (fishPool.length < 20) {
fishPool.push(obj);
} else {
obj.destroy();
}
}
}
// Seaweed collisions (underwater phase)
for (var i = seaweed.length - 1; i >= 0; i--) {
var seaweedObj = seaweed[i];
// Skip if not visible
if (!seaweedObj.visible) continue;
if (player.intersects(seaweedObj)) {
if (player.isDashing) {
createExplosion(seaweedObj.x, seaweedObj.y, 0x228b22);
LK.getSound('successHit').play();
var scoreGain = 20; // Reduced to 2/3 (30*2/3=20)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 15;
}
// Remove seaweed with swap-and-pop
var obj = seaweed[i];
seaweed[i] = seaweed[seaweed.length - 1];
seaweed.pop();
obj.visible = false;
if (seaweedPool.length < 20) {
seaweedPool.push(obj);
} else {
obj.destroy();
}
}
}
// Coral collisions (underwater phase)
for (var i = corals.length - 1; i >= 0; i--) {
var coral = corals[i];
// Skip if not visible
if (!coral.visible) continue;
if (player.intersects(coral)) {
if (player.isDashing) {
createExplosion(coral.x, coral.y, 0xff69b4);
LK.getSound('successHit').play();
var scoreGain = 47; //{l7} // Reduced to 2/3 (70*2/3=46.67≈47)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 20;
}
// Remove coral with swap-and-pop
var obj = corals[i];
corals[i] = corals[corals.length - 1];
corals.pop();
obj.visible = false;
if (coralsPool.length < 15) {
coralsPool.push(obj);
} else {
obj.destroy();
}
}
}
// Shark collisions (underwater phase)
for (var i = sharks.length - 1; i >= 0; i--) {
var shark = sharks[i];
// Skip if not visible
if (!shark.visible) continue;
if (player.intersects(shark)) {
if (player.isDashing) {
createExplosion(shark.x, shark.y, 0x333333);
LK.getSound('successHit').play();
var scoreGain = 67; // Reduced to 2/3 (100*2/3=66.67≈67)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
LK.effects.flashScreen(0xff0000, 500);
combo = 0;
playerLives--; // Player loses health when taking damage
updateLivesDisplay();
invincibilityFrames = maxInvincibilityFrames;
cameraShakeIntensity = 25; // Higher shake for shark collision
}
// Remove shark with swap-and-pop
var obj = sharks[i];
sharks[i] = sharks[sharks.length - 1];
sharks.pop();
obj.visible = false;
if (sharksPool.length < 5) {
sharksPool.push(obj);
} else {
obj.destroy();
}
}
}
// Heart pickup collisions
for (var i = hearts.length - 1; i >= 0; i--) {
var heart = hearts[i];
// Skip if not visible
if (!heart.visible) continue;
if (player.intersects(heart)) {
var livesGained = heart.isPulsing ? 2 : 1;
// Phase 3 (sky level) allows unlimited health collection for phase 4 preparation
if (gamePhase === 3) {
playerLives += livesGained; // No limit in sky phase
} else {
playerLives = Math.min(maxLives, playerLives + livesGained); // Normal limit for other phases
}
updateLivesDisplay();
// Play life gain sound
LK.getSound('gainLife').play();
// Show popup
showLifeGainPopup(livesGained);
// Create sparkle burst effect
createPerfectBurst(heart.x, heart.y, 0xff6699);
// Camera shake for life gain
cameraShakeIntensity = 8;
// Remove heart with swap-and-pop
var obj = hearts[i];
hearts[i] = hearts[hearts.length - 1];
hearts.pop();
obj.visible = false;
if (heartsPool.length < 10) {
heartsPool.push(obj);
} else {
obj.destroy();
}
}
}
}
// Beat detection and rhythm mechanics
function updateBeat() {
beatTimer++;
// Simple beat simulation (in real game this would sync with music)
if (beatTimer >= beatInterval) {
beatTimer = 0;
beatPulse = 1;
// Make player react to every beat
if (player) {
player.reactToBeat();
}
// Beat-synced camera shake and zoom (disabled in nature phase)
if (gamePhase !== 4) {
cameraShakeIntensity = 8;
tween(game, {
scaleX: 1.08,
scaleY: 1.08
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(game, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
// Spawn obstacles on beat
if (Math.random() < 0.7) {
spawnObstacle();
}
// Create beat-synced particle wave
createBeatWave();
}
// Beat pulse decay
beatPulse *= 0.9;
}
// Variable jump system
var jumpForce = -12; // Base jump force (reduced significantly)
var doubleJumpForce = -10; // Fixed double jump force (reduced significantly)
var isJumpPressed = false;
var jumpHoldTime = 0;
var maxJumpHoldTime = 20; // Maximum frames to hold jump
var minJumpForce = -6; // Minimum jump force (reduced further for phases 1&2)
var maxJumpForce = -12; // Maximum jump force (reduced further for phases 1&2)
// Underwater phase reduced jump forces
var underwaterJumpForce = -3; // Much weaker for underwater swimming
var underwaterDoubleJumpForce = -2.5; // Much weaker double jump underwater
var underwaterMinJumpForce = -1.5; // Much weaker minimum force underwater
var underwaterMaxJumpForce = -4; // Much weaker maximum force underwater
// Touch controls with variable jump
game.down = function (x, y, obj) {
// Adjust touch area based on game phase
var jumpThreshold = 1024; // Default jump area is left half
if (gamePhase === 4) {
// In phase 4, increase dash area, decrease jump area
jumpThreshold = 500; // Make jump area smaller on the left
}
if (x < jumpThreshold) {
// Left side - start variable jump
isJumpPressed = true;
jumpHoldTime = 0;
// Start jump immediately
player.jump();
// Check if jump was on beat
if (player.velocityY !== 0 && beatPulse > 0.7) {
perfectHits++;
combo++;
var scoreBonus = player.hasUsedDoubleJump ? 20 : 13; // Reduced to 2/3 (30*2/3=20, 20*2/3=13.33≈13)
var totalScore = scoreBonus + combo * 3; // Reduced combo multiplier to 2/3 (4*2/3=2.67≈3)
LK.setScore(LK.getScore() + totalScore);
LK.getSound('successHit').play();
LK.effects.flashObject(player, 0x00ff00, 200);
cameraShakeIntensity = 5;
createPerfectBurst(player.x, player.y, 0x00ff00);
}
} else {
// Right side - dash
player.dash();
// Check if dash was on beat
if (beatPulse > 0.7) {
perfectHits++;
combo++;
var totalScore = 20 + combo * 4; // Reduced to 2/3 (30*2/3=20, 6*2/3=4)
LK.setScore(LK.getScore() + totalScore);
LK.getSound('successHit').play();
LK.effects.flashObject(player, 0xffff00, 200);
cameraShakeIntensity = 6;
createPerfectBurst(player.x, player.y, 0xffff00);
}
}
};
// Touch up handler - stop variable jump
game.up = function (x, y, obj) {
// Use the same threshold as game.down for consistency
var jumpThreshold = 1024; // Default jump area is left half
if (gamePhase === 4) {
jumpThreshold = 500; // Match the jump area in game.down
}
if (x < jumpThreshold) {
// Left side - stop jump hold
isJumpPressed = false;
jumpHoldTime = 0;
}
};
// Main game loop
game.update = function () {
updateBeat();
updateBackgroundPulse();
updateCamera();
// Update collision cooldown
if (collisionCooldown > 0) {
collisionCooldown--;
}
// Update invincibility frames and player flashing
if (invincibilityFrames > 0) {
invincibilityFrames--;
player.isInvincible = true;
// Flash player during invincibility
var flashRate = 8; // Flash every 8 frames
player.invincibilityFlashOn = Math.floor(invincibilityFrames / flashRate) % 2 === 0;
isPlayerFlashing = true;
} else {
// Restore normal appearance when invincibility ends
if (isPlayerFlashing) {
player.isInvincible = false;
player.invincibilityFlashOn = false;
isPlayerFlashing = false;
}
}
checkCollisions();
// Update ground segments
for (var i = 0, len = ground.length; i < len; i++) {
if (ground[i].x <= -300) {
ground[i].x += 3600; // 12 * 300
}
}
// Update nature ground segments
for (var i = 0, len = natureGround.length; i < len; i++) {
if (natureGround[i].x <= -500) {
natureGround[i].x += 3000; // 15 * 200
}
}
// Update and remove obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i].x < -100) {
var obj = obstacles[i];
obstacles[i] = obstacles[obstacles.length - 1];
obstacles.pop();
obj.visible = false;
if (obstaclesPool.length < 20) {
obstaclesPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing obstacles
var scoreGain = 7; //{nh} // Reduced to 2/3 (10*2/3=6.67≈7)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Update and remove rotating cubes
for (var i = rotatingCubes.length - 1; i >= 0; i--) {
if (rotatingCubes[i].x < -100) {
var obj = rotatingCubes[i];
rotatingCubes[i] = rotatingCubes[rotatingCubes.length - 1];
rotatingCubes.pop();
obj.visible = false;
if (rotatingCubesPool.length < 20) {
rotatingCubesPool.push(obj);
} else {
obj.destroy();
}
var scoreGain = 11; //{nv} // Reduced to 2/3 (16*2/3=10.67≈11)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Update and remove laser beams
for (var i = laserBeams.length - 1; i >= 0; i--) {
if (laserBeams[i].x < -100) {
var obj = laserBeams[i];
laserBeams[i] = laserBeams[laserBeams.length - 1];
laserBeams.pop();
obj.visible = false;
if (laserBeamsPool.length < 10) {
laserBeamsPool.push(obj);
} else {
obj.destroy();
}
var scoreGain = 13; // Reduced to 2/3 (20*2/3=13.33≈13)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Combined particle update loop with object pooling
var allParticles = [{
arr: explosionParts,
pool: explosionPartsPool
}, {
arr: shatterParts,
pool: shatterPartsPool
}, {
arr: particles,
pool: particlesPool
}, {
arr: trailParticles,
pool: trailParticlesPool
}, {
arr: burstParticles,
pool: burstParticlesPool
}, {
arr: ambientParticles,
pool: ambientParticlesPool
}];
// Single loop for all particle types
for (var p = 0, pLen = allParticles.length; p < pLen; p++) {
var particleData = allParticles[p];
var arr = particleData.arr;
var pool = particleData.pool;
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i].shouldDestroy) {
var particleToPool = arr[i];
// Swap-and-pop
arr[i] = arr[arr.length - 1];
arr.pop();
// Return to pool instead of destroying
particleToPool.visible = false;
particleToPool.shouldDestroy = false;
if (pool && pool.length < 50) {
// Limit pool size
pool.push(particleToPool);
} else {
particleToPool.destroy();
}
}
}
}
// Combined background elements update
var bgElements = [{
arr: backgroundShapes,
pool: backgroundShapesPool,
threshold: -200
}, {
arr: lightLines,
pool: lightLinesPool,
threshold: -500
}, {
arr: dataStreamParticles,
pool: dataStreamParticlesPool,
threshold: -100
}, {
arr: holographicElements,
pool: holographicElementsPool,
threshold: -400
}];
// Process in batches on mobile
var batchSize = isMobile ? 2 : bgElements.length;
var startIdx = LK.ticks % 2 * 2; // Alternate between first/second half on mobile
for (var b = startIdx, bLen = Math.min(startIdx + batchSize, bgElements.length); b < bLen; b++) {
var element = bgElements[b];
if (!element) continue;
var arr = element.arr;
var pool = element.pool;
var threshold = element.threshold;
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i].shouldDestroy !== undefined && arr[i].shouldDestroy || arr[i].x < threshold) {
var obj = arr[i];
arr[i] = arr[arr.length - 1];
arr.pop();
obj.visible = false;
if (pool && pool.length < 30) {
pool.push(obj);
} else {
obj.destroy();
}
}
}
}
// Update and remove clouds
for (var i = clouds.length - 1; i >= 0; i--) {
if (clouds[i].x < -200) {
var obj = clouds[i];
clouds[i] = clouds[clouds.length - 1];
clouds.pop();
obj.visible = false;
if (cloudsPool.length < 20) {
cloudsPool.push(obj);
} else {
obj.destroy();
}
}
}
// Update and remove sun rays
for (var i = sunRays.length - 1; i >= 0; i--) {
if (sunRays[i].shouldDestroy || sunRays[i].x < -500) {
var obj = sunRays[i];
sunRays[i] = sunRays[sunRays.length - 1];
sunRays.pop();
obj.visible = false;
if (sunRaysPool.length < 15) {
sunRaysPool.push(obj);
} else {
obj.destroy();
}
}
}
// Update and remove seagulls
for (var i = seagulls.length - 1; i >= 0; i--) {
if (seagulls[i].x < -100) {
var obj = seagulls[i];
seagulls[i] = seagulls[seagulls.length - 1];
seagulls.pop();
obj.visible = false;
if (seagullsPool.length < 15) {
seagullsPool.push(obj);
} else {
obj.destroy();
}
}
}
// Update and remove hearts
for (var i = hearts.length - 1; i >= 0; i--) {
if (hearts[i].x < -100) {
var obj = hearts[i];
hearts[i] = hearts[hearts.length - 1];
hearts.pop();
obj.visible = false;
if (heartsPool.length < 10) {
heartsPool.push(obj);
} else {
obj.destroy();
}
}
}
// Combined underwater elements update - only process if in underwater phase
if (gamePhase === 5) {
// Handle fish separately to add scoring
for (var i = fish.length - 1; i >= 0; i--) {
if (!fish[i]) continue;
if (fish[i].x < -100) {
var obj = fish[i];
fish[i] = fish[fish.length - 1];
fish.pop();
obj.visible = false;
if (fishPool.length < 20) {
fishPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing fish in underwater phase
var scoreGain = 7; //{p8} // Reduced to 2/3 (10*2/3=6.67≈7)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle sharks separately to add scoring
for (var i = sharks.length - 1; i >= 0; i--) {
if (!sharks[i]) continue;
if (sharks[i].x < -100) {
var obj = sharks[i];
sharks[i] = sharks[sharks.length - 1];
sharks.pop();
obj.visible = false;
if (sharksPool.length < 5) {
sharksPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing sharks in underwater phase
var scoreGain = 16; // Reduced to 2/3 (24*2/3=16)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle seaweed separately to add scoring
for (var i = seaweed.length - 1; i >= 0; i--) {
if (!seaweed[i]) continue;
if (seaweed[i].x < -100) {
var obj = seaweed[i];
seaweed[i] = seaweed[seaweed.length - 1];
seaweed.pop();
obj.visible = false;
if (seaweedPool.length < 20) {
seaweedPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing seaweed in underwater phase
var scoreGain = 5; //{pz} // Reduced to 2/3 (8*2/3=5.33≈5)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle corals separately to add scoring
for (var i = corals.length - 1; i >= 0; i--) {
if (!corals[i]) continue;
if (corals[i].x < -100) {
var obj = corals[i];
corals[i] = corals[corals.length - 1];
corals.pop();
obj.visible = false;
if (coralsPool.length < 15) {
coralsPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing corals in underwater phase
var scoreGain = 9; // Reduced to 2/3 (14*2/3=9.33≈9)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle remaining underwater elements (bubbles, waterCurrents) without scoring
var underwaterElements = [{
arr: bubbles,
pool: bubblesPool,
poolSize: 30,
checkDestroy: true
}, {
arr: waterCurrents,
pool: waterCurrentsPool,
poolSize: 20,
checkDestroy: false
}];
for (var u = 0, uLen = underwaterElements.length; u < uLen; u++) {
var element = underwaterElements[u];
if (!element) continue; // Skip if element is undefined
var arr = element.arr;
if (!arr) continue; // Skip if arr is undefined
for (var i = arr.length - 1; i >= 0; i--) {
if (!arr[i]) continue; // Skip if array element is undefined
var shouldRemove = arr[i] && arr[i].x < -100; // Ensure arr[i] is defined before accessing x
if (element.checkDestroy && arr[i].shouldDestroy) {
shouldRemove = true;
}
if (shouldRemove) {
var obj = arr[i];
arr[i] = arr[arr.length - 1];
arr.pop();
obj.visible = false;
if (element.pool && element.pool.length < element.poolSize) {
element.pool.push(obj);
} else {
obj.destroy();
}
}
}
}
}
// Lives display is updated only when lives change
// Spawn hearts occasionally
heartSpawnTimer++;
if (heartSpawnTimer >= heartSpawnInterval) {
heartSpawnTimer = 0;
// Phase 3 allows unlimited heart spawning, other phases check maxLives limit
if (gamePhase === 3 || playerLives < maxLives) {
spawnHeart();
}
}
// Update life popup timer
if (lifePopupTimer > 0) {
lifePopupTimer--;
}
// Update and remove nature elements
for (var i = trees.length - 1; i >= 0; i--) {
if (trees[i].x < -100) {
var obj = trees[i];
trees[i] = trees[trees.length - 1];
trees.pop();
obj.visible = false;
if (treesPool.length < 10) {
treesPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing trees in nature phase
var scoreGain = 11; //{qj} // Reduced to 2/3 (16*2/3=10.67≈11)
LK.setScore(LK.getScore() + scoreGain);
}
}
for (var i = rocks.length - 1; i >= 0; i--) {
if (rocks[i].x < -100) {
var obj = rocks[i];
rocks[i] = rocks[rocks.length - 1];
rocks.pop();
obj.visible = false;
if (rocksPool.length < 15) {
rocksPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing rocks in nature phase
var scoreGain = 8; // Reduced to 2/3 (12*2/3=8)
LK.setScore(LK.getScore() + scoreGain);
}
}
for (var i = bushes.length - 1; i >= 0; i--) {
if (bushes[i].x < -100) {
var obj = bushes[i];
bushes[i] = bushes[bushes.length - 1];
bushes.pop();
obj.visible = false;
if (bushesPool.length < 15) {
bushesPool.push(obj);
} else {
obj.destroy();
}
// Bonus points for passing bushes in nature phase
var scoreGain = 5; //{qK} // Reduced to 2/3 (8*2/3=5.33≈5)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Create continuous particle effects
createTrailParticles();
createAmbientParticles();
// Create background environment effects
if (gamePhase === 5) {
// Underwater phase effects
bubbleTimer++;
if (bubbleTimer >= 40) {
bubbleTimer = 0;
spawnBubbles();
}
currentSpawnTimer++;
if (currentSpawnTimer >= 180) {
currentSpawnTimer = 0;
spawnWaterCurrent();
}
sharkSpawnTimer++;
if (sharkSpawnTimer >= 400) {
// Less frequent than fish spawning
sharkSpawnTimer = 0;
spawnShark();
}
} else if (gamePhase === 4) {
// Nature phase effects
// Spawn trees for background
treeSpawnTimer++;
if (treeSpawnTimer >= 200) {
treeSpawnTimer = 0;
spawnTree();
}
} else if (gamePhase === 3) {
// Sky level effects
createSunRays();
// Spawn clouds regularly
cloudSpawnTimer++;
if (cloudSpawnTimer >= 150) {
cloudSpawnTimer = 0;
spawnCloud();
}
// Spawn seagulls occasionally above clouds
seagullSpawnTimer++;
if (seagullSpawnTimer >= 300 + Math.random() * 400) {
seagullSpawnTimer = 0;
spawnSeagull();
// Add elegant entrance animation
var newSeagull = seagulls[seagulls.length - 1];
newSeagull.alpha = 0;
tween(newSeagull, {
alpha: 0.9
}, {
duration: 1000,
easing: tween.easeOut
});
}
} else {
// Normal/speed phase effects
createBackgroundShapes();
createLightLines();
createDataStreamParticles();
createHolographicElements();
}
// Show/hide custom background based on game phase
if (gamePhase === 4) {
backgroundContainer.visible = true;
} else {
backgroundContainer.visible = false;
}
// Show/hide nature ground based on game phase
for (var i = 0, len = natureGround.length; i < len; i++) {
if (gamePhase === 4) {
natureGround[i].visible = true;
} else if (gamePhase === 5) {
natureGround[i].visible = false;
} else {
natureGround[i].visible = false;
}
}
// Show/hide underwater platforms based on game phase
if (underwaterPlatformTop) {
underwaterPlatformTop.visible = gamePhase === 5;
}
if (underwaterPlatformBottom) {
underwaterPlatformBottom.visible = gamePhase === 5;
}
// Throttled UI updates (every 2-3 frames)
if (LK.ticks % 2 === 0) {
scoreText.setText('Score: ' + LK.getScore());
comboText.setText('Combo: ' + combo);
}
if (LK.ticks % 3 === 0) {
// Update dash cooldown display
if (player.dashCooldown > 0) {
var cooldownProgress = player.dashCooldown / 120; // 120 is max cooldown
var secondsLeft = Math.ceil(player.dashCooldown / 60);
dashCooldownText.setText('DASH: ' + secondsLeft + 's');
dashCooldownText.fill = 0xff4400;
dashCooldownFill.scaleX = 0.8 * cooldownProgress;
dashCooldownFill.alpha = 0.8;
// Pulse effect when almost ready
if (player.dashCooldown <= 30) {
var pulseAlpha = 0.5 + fastSin(LK.ticks * 0.3) * 0.3;
dashCooldownText.alpha = pulseAlpha;
dashCooldownFill.alpha = pulseAlpha;
} else {
dashCooldownText.alpha = 1;
}
} else {
dashCooldownText.setText('DASH READY');
dashCooldownText.fill = 0x00FF00;
dashCooldownText.alpha = 1;
dashCooldownFill.scaleX = 0;
dashCooldownFill.alpha = 0;
}
}
// Game over if no lives left
if (playerLives <= 0) {
LK.showGameOver();
}
// Gradually increase difficulty
if (LK.ticks % 1800 == 0) {
// Every 30 seconds
gameSpeed += 0.5;
beatInterval = Math.max(30, beatInterval - 2);
}
// Game over condition (if player falls off screen)
if (gamePhase === 3) {
// Sky level - portal respawn system
if (player.y > 2732 + 100) {
// Respawn from top with portal effect
player.y = -100;
player.velocityY = 0;
player.isGrounded = false;
// Portal effect
LK.effects.flashScreen(0xaaccff, 800);
cameraShakeIntensity = 15;
// Play whoosh sound for portal
LK.getSound('windWhoosh').play();
}
} else {
// Normal/speed phase - game over
if (player.y > 2732 + 100) {
LK.showGameOver();
}
}
// Speed phase transition at 1000 points
if (LK.getScore() >= 1000 && !speedTransitioned) {
speedTransitioned = true;
gamePhase = 2;
gameSpeed += 3; // Increase speed
beatInterval = Math.max(40, beatInterval - 10); // Faster beats
// Clear all previous phase obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obj = obstacles[i];
obstacles[i] = obstacles[obstacles.length - 1];
obstacles.pop();
obj.visible = false;
if (obstaclesPool.length < 20) {
obstaclesPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = rotatingCubes.length - 1; i >= 0; i--) {
var obj = rotatingCubes[i];
rotatingCubes[i] = rotatingCubes[rotatingCubes.length - 1];
rotatingCubes.pop();
obj.visible = false;
if (rotatingCubesPool.length < 20) {
rotatingCubesPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = laserBeams.length - 1; i >= 0; i--) {
var obj = laserBeams[i];
laserBeams[i] = laserBeams[laserBeams.length - 1];
laserBeams.pop();
obj.visible = false;
if (laserBeamsPool.length < 10) {
laserBeamsPool.push(obj);
} else {
obj.destroy();
}
}
// Transition to speed phase music with fade
LK.playMusic('speedMusic', {
fade: {
start: 0,
end: 0.8,
duration: 1500
}
});
// Visual effects for speed phase
LK.effects.flashScreen(0xff8800, 1500);
cameraShakeIntensity = 15;
}
// Sky level transition at 2000 points - dreamy sky environment
if (LK.getScore() >= 2000 && !skyTransitioned) {
skyTransitioned = true;
gamePhase = 3;
// Clear all previous phase obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obj = obstacles[i];
obstacles[i] = obstacles[obstacles.length - 1];
obstacles.pop();
obj.visible = false;
if (obstaclesPool.length < 20) {
obstaclesPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = rotatingCubes.length - 1; i >= 0; i--) {
var obj = rotatingCubes[i];
rotatingCubes[i] = rotatingCubes[rotatingCubes.length - 1];
rotatingCubes.pop();
obj.visible = false;
if (rotatingCubesPool.length < 20) {
rotatingCubesPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = laserBeams.length - 1; i >= 0; i--) {
var obj = laserBeams[i];
laserBeams[i] = laserBeams[laserBeams.length - 1];
laserBeams.pop();
obj.visible = false;
if (laserBeamsPool.length < 10) {
laserBeamsPool.push(obj);
} else {
obj.destroy();
}
}
// Hide ground segments for sky level
for (var i = 0, len = ground.length; i < len; i++) {
ground[i].visible = false;
}
// Also hide nature ground during sky phase
for (var i = 0, len = natureGround.length; i < len; i++) {
natureGround[i].visible = false;
}
// Adjust player position for sky level
groundLevel = 1800;
player.y = groundLevel - 30;
// Ensure at least one cloud is positioned where the player can land on it
// Create an immediate landing cloud near the player
var landingCloud = new Cloud();
landingCloud.x = player.x + 100; // Position slightly ahead of player
landingCloud.y = player.y + 50; // Position below player for easy landing
clouds.push(landingCloud);
game.addChild(landingCloud);
// Create many more clouds for a smooth transition with better coverage
for (var i = 0; i < 8; i++) {
var transitionCloud = new Cloud();
transitionCloud.x = player.x + 200 + i * 180; // Closer spacing between clouds
transitionCloud.y = groundLevel - 80 - Math.random() * 150; // Slightly higher and less random variation
clouds.push(transitionCloud);
game.addChild(transitionCloud);
}
// Show phase transition message
var phaseTransitionText = new Text2('Stock up on hearts before nature strikes!', {
size: 70,
fill: 0xff0055
});
phaseTransitionText.anchor.set(0.5, 0.5);
phaseTransitionText.x = 724;
phaseTransitionText.y = 1266;
phaseTransitionText.alpha = 0;
game.addChild(phaseTransitionText);
// Animate transition message
tween(phaseTransitionText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Keep message visible for 3 seconds, then fade out
LK.setTimeout(function () {
tween(phaseTransitionText, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeIn,
onFinish: function onFinish() {
phaseTransitionText.destroy();
}
});
}, 3000);
}
});
// Set camera zoom target for sky level
cameraTargetZoom = 1.15; // Slightly less zoom
// Transition to sky level ambient music with fade
LK.playMusic('skyLevelMusic', {
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
// Play ambient wind sound
LK.getSound('windWhoosh').play();
// Visual effect for sky transition
LK.effects.flashScreen(0xffffcc, 3000);
// Camera shake for transition
cameraShakeIntensity = 20;
}
// Nature phase transition at 3000 points - character returns to earth
if (LK.getScore() >= 3000 && !natureTransitioned) {
natureTransitioned = true;
gamePhase = 4;
// Clear all sky phase obstacles (clouds and seagulls)
for (var i = clouds.length - 1; i >= 0; i--) {
var obj = clouds[i];
clouds[i] = clouds[clouds.length - 1];
clouds.pop();
obj.visible = false;
if (cloudsPool.length < 20) {
cloudsPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = seagulls.length - 1; i >= 0; i--) {
var obj = seagulls[i];
seagulls[i] = seagulls[seagulls.length - 1];
seagulls.pop();
obj.visible = false;
if (seagullsPool.length < 15) {
seagullsPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = sunRays.length - 1; i >= 0; i--) {
sunRays[i].destroy();
sunRays.splice(i, 1);
}
// Hide regular ground segments and show nature ground
for (var i = 0, len = ground.length; i < len; i++) {
ground[i].visible = false;
}
for (var i = 0, len = natureGround.length; i < len; i++) {
natureGround[i].visible = true;
}
// Adjust ground level back to original
groundLevel = 2200;
player.y = groundLevel - 30;
// Set camera zoom for nature phase (closer view)
cameraTargetZoom = 1.3;
// Continue using nature music but adjust volume for nature phase
LK.playMusic('natureMusic', {
fade: {
start: 0.8,
end: 1.0,
duration: 1000
}
});
// Visual effect for nature transition
LK.effects.flashScreen(0x90EE90, 2500);
cameraShakeIntensity = 25;
// Play nature ambient sounds
LK.getSound('windWhoosh').play();
}
// Underwater phase transition at 4000 points - deep sea exploration
if (LK.getScore() >= 4000 && !underwaterTransitioned) {
underwaterTransitioned = true;
gamePhase = 5;
// Clear all nature phase obstacles
for (var i = trees.length - 1; i >= 0; i--) {
var obj = trees[i];
trees[i] = trees[trees.length - 1];
trees.pop();
obj.visible = false;
if (treesPool.length < 10) {
treesPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = rocks.length - 1; i >= 0; i--) {
var obj = rocks[i];
rocks[i] = rocks[rocks.length - 1];
rocks.pop();
obj.visible = false;
if (rocksPool.length < 15) {
rocksPool.push(obj);
} else {
obj.destroy();
}
}
for (var i = bushes.length - 1; i >= 0; i--) {
var obj = bushes[i];
bushes[i] = bushes[bushes.length - 1];
bushes.pop();
obj.visible = false;
if (bushesPool.length < 15) {
bushesPool.push(obj);
} else {
obj.destroy();
}
}
// Show nature ground for underwater floor
for (var i = 0, len = natureGround.length; i < len; i++) {
natureGround[i].visible = true;
// Tint darker for ocean floor
natureGround[i].tint = 0x004466;
}
// Create underwater platforms (top and bottom barriers) with reduced spacing
if (!underwaterPlatformTop) {
underwaterPlatformTop = LK.getAsset('underwaterPlatformTop', {
anchorX: 0,
anchorY: 0,
x: -1024,
y: 750
});
game.addChild(underwaterPlatformTop);
}
if (!underwaterPlatformBottom) {
underwaterPlatformBottom = LK.getAsset('underwaterPlatformBottom', {
anchorX: 0,
anchorY: 1,
x: -1024,
y: 1950
});
game.addChild(underwaterPlatformBottom);
}
// Maintain ground level
groundLevel = 2200;
player.y = groundLevel - 30;
// Set camera zoom for underwater phase - zoomed in for better visibility
cameraTargetZoom = 1.5;
// Transition to underwater ambient music
LK.playMusic('underwaterMusic', {
fade: {
start: 0,
end: 0.8,
duration: 2000
}
});
// Play bubble sound for transition
LK.getSound('bubbleSound').play();
// Visual effect for underwater transition
LK.effects.flashScreen(0x004466, 3000);
cameraShakeIntensity = 20;
}
// New win condition at higher score (moved to 5000)
if (LK.getScore() >= 5000) {
LK.showYouWin();
}
};
// Dash cooldown display
var dashCooldownContainer = new Container();
dashCooldownContainer.x = 100;
dashCooldownContainer.y = -150;
LK.gui.bottomLeft.addChild(dashCooldownContainer);
var dashCooldownText = new Text2('DASH READY', {
size: 50,
fill: 0x00FF00
});
dashCooldownText.anchor.set(0, 1);
dashCooldownContainer.addChild(dashCooldownText);
var dashCooldownBar = LK.getAsset('ground', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.3
});
dashCooldownBar.y = -60;
dashCooldownBar.tint = 0x0088ff;
dashCooldownContainer.addChild(dashCooldownBar);
var dashCooldownFill = LK.getAsset('ground', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.3
});
dashCooldownFill.y = -60;
dashCooldownFill.tint = 0xff8800;
dashCooldownContainer.addChild(dashCooldownFill);
// Start the music
LK.playMusic('mainBgMusic');
; ===================================================================
--- original.js
+++ change.js
@@ -2489,9 +2489,9 @@
// Destroy obstacle with explosion effect
createExplosion(obstacle.x, obstacle.y, 0xff4400);
createShatter(obstacle.x, obstacle.y, 0xff0044);
LK.getSound('successHit').play();
- var scoreGain = 33; //{gQ} // Reduced by 2/3
+ var scoreGain = 33; //{gQ} // Reduced to 2/3 (50*2/3=33.33≈33)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible
@@ -2546,9 +2546,9 @@
// Destroy cube with spectacular explosion
createExplosion(cube.x, cube.y, 0xff8800);
createShatter(cube.x, cube.y, 0xff4400);
LK.getSound('successHit').play();
- var scoreGain = 47; //{hx} // Reduced by 2/3
+ var scoreGain = 47; //{hx} // Reduced to 2/3 (70*2/3=46.67≈47)
LK.setScore(LK.getScore() + scoreGain);
combo++;
cameraShakeIntensity = 8;
} else if (invincibilityFrames === 0) {
@@ -2593,9 +2593,9 @@
// Player can dash through laser - destroy it with explosion effect
createExplosion(laser.x, laser.y, 0xff0088);
createShatter(laser.x, laser.y, 0xff0088);
LK.getSound('successHit').play();
- var scoreGain = 67; // Reduced by 2/3 - High score for dashing through laser
+ var scoreGain = 67; // Reduced to 2/3 (100*2/3=66.67≈67)
LK.setScore(LK.getScore() + scoreGain);
combo++;
cameraShakeIntensity = 12;
// Remove laser with swap-and-pop
@@ -2653,9 +2653,9 @@
// Soft landing sound effect
if (Math.random() < 0.5) {
LK.getSound('windWhoosh').play();
}
- LK.setScore(LK.getScore() + 20); // Reduced by 2/3
+ LK.setScore(LK.getScore() + 20); // Reduced to 2/3 (30*2/3=20)
}
}
}
// Seagull collisions - can land on seagulls
@@ -2675,9 +2675,9 @@
// Gentle landing sound
if (Math.random() < 0.3) {
LK.getSound('echoChime').play();
}
- LK.setScore(LK.getScore() + 27); // Reduced by 2/3
+ LK.setScore(LK.getScore() + 27); // Reduced to 2/3 (40*2/3=26.67≈27)
}
}
}
// Rock collisions (nature phase) - rocks always damage player
@@ -2690,9 +2690,9 @@
// Destroy rock with explosion effect
createExplosion(rock.x, rock.y, 0x696969);
createShatter(rock.x, rock.y, 0x696969);
LK.getSound('successHit').play();
- var scoreGain = 40; // Reduced by 2/3
+ var scoreGain = 40; // Reduced to 2/3 (60*2/3=40)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible
@@ -2726,9 +2726,9 @@
if (player.isDashing) {
// Destroy bush with green explosion
createExplosion(bush.x, bush.y, 0x228B22);
LK.getSound('successHit').play();
- var scoreGain = 27; // Reduced by 2/3
+ var scoreGain = 27; // Reduced to 2/3 (40*2/3=26.67≈27)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
// Only take damage if not invincible
@@ -2790,9 +2790,9 @@
// Destroy tree with explosion effect
createExplosion(tree.x, tree.y, 0x8B4513);
createShatter(tree.x, tree.y, 0x228B22);
LK.getSound('successHit').play();
- var scoreGain = 53; // Reduced by 2/3
+ var scoreGain = 53; // Reduced to 2/3 (80*2/3=53.33≈53)
LK.setScore(LK.getScore() + scoreGain);
combo++;
// Remove tree with swap-and-pop
var obj = trees[i];
@@ -2826,9 +2826,9 @@
if (player.isDashing) {
// Destroy fish with splash effect
createExplosion(fishObj.x, fishObj.y, 0xff6600);
LK.getSound('successHit').play();
- var scoreGain = 33; //{kg} // Reduced by 2/3
+ var scoreGain = 33; //{kg} // Reduced to 2/3 (50*2/3=33.33≈33)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
@@ -2858,9 +2858,9 @@
if (player.intersects(seaweedObj)) {
if (player.isDashing) {
createExplosion(seaweedObj.x, seaweedObj.y, 0x228b22);
LK.getSound('successHit').play();
- var scoreGain = 20; // Reduced by 2/3
+ var scoreGain = 20; // Reduced to 2/3 (30*2/3=20)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
@@ -2891,9 +2891,9 @@
if (player.intersects(coral)) {
if (player.isDashing) {
createExplosion(coral.x, coral.y, 0xff69b4);
LK.getSound('successHit').play();
- var scoreGain = 47; //{l7} // Reduced by 2/3
+ var scoreGain = 47; //{l7} // Reduced to 2/3 (70*2/3=46.67≈47)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
@@ -2924,9 +2924,9 @@
if (player.intersects(shark)) {
if (player.isDashing) {
createExplosion(shark.x, shark.y, 0x333333);
LK.getSound('successHit').play();
- var scoreGain = 67; // Reduced by 2/3 - Higher score for defeating larger enemy
+ var scoreGain = 67; // Reduced to 2/3 (100*2/3=66.67≈67)
LK.setScore(LK.getScore() + scoreGain);
combo++;
} else if (invincibilityFrames === 0) {
LK.getSound('loseLife').play();
@@ -3055,10 +3055,10 @@
// Check if jump was on beat
if (player.velocityY !== 0 && beatPulse > 0.7) {
perfectHits++;
combo++;
- var scoreBonus = player.hasUsedDoubleJump ? 20 : 13; // Reduced by 2/3
- var totalScore = scoreBonus + combo * 3; // Reduced combo multiplier by 2/3
+ var scoreBonus = player.hasUsedDoubleJump ? 20 : 13; // Reduced to 2/3 (30*2/3=20, 20*2/3=13.33≈13)
+ var totalScore = scoreBonus + combo * 3; // Reduced combo multiplier to 2/3 (4*2/3=2.67≈3)
LK.setScore(LK.getScore() + totalScore);
LK.getSound('successHit').play();
LK.effects.flashObject(player, 0x00ff00, 200);
cameraShakeIntensity = 5;
@@ -3070,9 +3070,9 @@
// Check if dash was on beat
if (beatPulse > 0.7) {
perfectHits++;
combo++;
- var totalScore = 20 + combo * 4; // Reduced by 2/3
+ var totalScore = 20 + combo * 4; // Reduced to 2/3 (30*2/3=20, 6*2/3=4)
LK.setScore(LK.getScore() + totalScore);
LK.getSound('successHit').play();
LK.effects.flashObject(player, 0xffff00, 200);
cameraShakeIntensity = 6;
@@ -3143,9 +3143,9 @@
} else {
obj.destroy();
}
// Bonus points for passing obstacles
- var scoreGain = 7; //{nh} // Reduced by 2/3
+ var scoreGain = 7; //{nh} // Reduced to 2/3 (10*2/3=6.67≈7)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Update and remove rotating cubes
@@ -3159,9 +3159,9 @@
rotatingCubesPool.push(obj);
} else {
obj.destroy();
}
- var scoreGain = 11; //{nv} // Reduced by 2/3
+ var scoreGain = 11; //{nv} // Reduced to 2/3 (16*2/3=10.67≈11)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Update and remove laser beams
@@ -3175,9 +3175,9 @@
laserBeamsPool.push(obj);
} else {
obj.destroy();
}
- var scoreGain = 13; // Reduced by 2/3
+ var scoreGain = 13; // Reduced to 2/3 (20*2/3=13.33≈13)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Combined particle update loop with object pooling
@@ -3335,9 +3335,9 @@
} else {
obj.destroy();
}
// Bonus points for passing fish in underwater phase
- var scoreGain = 7; //{p8} // Reduced by 2/3
+ var scoreGain = 7; //{p8} // Reduced to 2/3 (10*2/3=6.67≈7)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle sharks separately to add scoring
@@ -3353,9 +3353,9 @@
} else {
obj.destroy();
}
// Bonus points for passing sharks in underwater phase
- var scoreGain = 16; // Reduced by 2/3
+ var scoreGain = 16; // Reduced to 2/3 (24*2/3=16)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle seaweed separately to add scoring
@@ -3371,9 +3371,9 @@
} else {
obj.destroy();
}
// Bonus points for passing seaweed in underwater phase
- var scoreGain = 5; //{pz} // Reduced by 2/3
+ var scoreGain = 5; //{pz} // Reduced to 2/3 (8*2/3=5.33≈5)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle corals separately to add scoring
@@ -3389,9 +3389,9 @@
} else {
obj.destroy();
}
// Bonus points for passing corals in underwater phase
- var scoreGain = 9; // Reduced by 2/3
+ var scoreGain = 9; // Reduced to 2/3 (14*2/3=9.33≈9)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Handle remaining underwater elements (bubbles, waterCurrents) without scoring
@@ -3457,9 +3457,9 @@
} else {
obj.destroy();
}
// Bonus points for passing trees in nature phase
- var scoreGain = 11; //{qj} // Reduced by 2/3
+ var scoreGain = 11; //{qj} // Reduced to 2/3 (16*2/3=10.67≈11)
LK.setScore(LK.getScore() + scoreGain);
}
}
for (var i = rocks.length - 1; i >= 0; i--) {
@@ -3473,9 +3473,9 @@
} else {
obj.destroy();
}
// Bonus points for passing rocks in nature phase
- var scoreGain = 8; // Reduced by 2/3
+ var scoreGain = 8; // Reduced to 2/3 (12*2/3=8)
LK.setScore(LK.getScore() + scoreGain);
}
}
for (var i = bushes.length - 1; i >= 0; i--) {
@@ -3489,9 +3489,9 @@
} else {
obj.destroy();
}
// Bonus points for passing bushes in nature phase
- var scoreGain = 5; //{qK} // Reduced by 2/3
+ var scoreGain = 5; //{qK} // Reduced to 2/3 (8*2/3=5.33≈5)
LK.setScore(LK.getScore() + scoreGain);
}
}
// Create continuous particle effects
2d rock for game. In-Game asset. 2d. High contrast. No shadows
agaç gövdesi 2d. In-Game asset. 2d. High contrast. No shadows
bush 2d. In-Game asset. 2d. High contrast. No shadows
nehiri kaldır
oxygen tank 2d. In-Game asset. 2d. High contrast. No shadows
shark shadow 2d. In-Game asset. 2d. High contrast. No shadows
2 yapraklı deniz yosunu 2d. In-Game asset. 2d. High contrast. No shadows
piranha 2d. In-Game asset. 2d. High contrast. No shadows
deniz mercanı 2d. In-Game asset. 2d. High contrast. No shadows
jump
Sound effect
dash
Sound effect
failureMiss
Sound effect
mainBgMusic
Music
skyLevelMusic
Music
speedMusic
Music
natureMusic
Music
jumpNature
Sound effect
jumpUnderwater
Sound effect
dashNature
Sound effect
dashUnderwater
Sound effect
underwaterMusic
Music
jumpSpeed
Sound effect
dashSpeed
Sound effect