/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.isBomb = true;
self.speed = 172.5;
if (isInfiniteMode) {
self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase)
} else if (isHellMode) {
self.speed *= 4; // 4x speed in hell mode
}
self.caught = false;
self.thrown = false;
self.throwVelocityX = 0;
self.throwVelocityY = 0;
self.update = function () {
if (self.thrown) {
self.x += self.throwVelocityX;
self.y += self.throwVelocityY;
} else if (!self.caught) {
self.y += self.speed * (1 / 60);
}
};
return self;
});
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
});
self.heldPackage = null;
self.targetRotation = 0;
self.update = function () {
var rotationDiff = self.targetRotation - self.rotation;
while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
self.rotation += rotationDiff * 0.1;
if (self.heldPackage) {
// Position package in front of character based on rotation
var packageDistance = 90; // Distance in front of character
var packageX = self.x + Math.cos(self.rotation) * packageDistance;
var packageY = self.y + Math.sin(self.rotation) * packageDistance;
// Smoothly tween package to new position for better visual flow
tween(self.heldPackage, {
x: packageX,
y: packageY,
rotation: self.rotation
}, {
duration: 100,
easing: tween.easeOut
});
}
};
return self;
});
var Confetti = Container.expand(function (colorValue) {
var self = Container.call(this);
var confettiGraphics = self.attachAsset('confetti', {
anchorX: 0.5,
anchorY: 0.5
});
confettiGraphics.tint = colorValue;
self.velocityX = (Math.random() - 0.5) * 400;
self.velocityY = (Math.random() - 0.5) * 400;
self.life = 60;
self.update = function () {
self.x += self.velocityX * (1 / 60);
self.y += self.velocityY * (1 / 60);
self.velocityY += 300 * (1 / 60);
self.life--;
if (self.life <= 0) {
self.alpha = 0;
}
};
return self;
});
var Package = Container.expand(function (colorValue) {
var self = Container.call(this);
var assetName = 'yellowpacket';
if (colorValue === 0x0066ff) assetName = 'bluepacket';else if (colorValue === 0xff0000) assetName = 'redpacket';else if (colorValue === 0xff8800) assetName = 'orangepacket';
var packageGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.colorValue = colorValue || 0xffffff;
self.speed = 172.5; // 150 * 1.15 = 15% increase
if (isInfiniteMode) {
self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase)
} else if (isHellMode) {
self.speed *= 4; // 4x speed in hell mode
}
self.caught = false;
self.thrown = false;
self.throwVelocityX = 0;
self.throwVelocityY = 0;
self.update = function () {
if (self.thrown) {
self.x += self.throwVelocityX;
self.y += self.throwVelocityY;
} else if (!self.caught) {
self.y += self.speed * (1 / 60);
}
};
return self;
});
var TargetBox = Container.expand(function (colorValue) {
var self = Container.call(this);
var assetName = 'targetYellow';
if (colorValue === 0x0066ff) assetName = 'targetBlue';else if (colorValue === 0xff0000) assetName = 'targetRed';else if (colorValue === 0xff8800) assetName = 'targetOrange';
var targetGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.colorValue = colorValue;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var gameWidth = 2048;
var gameHeight = 2732;
var arenaSize = 1600;
var arenaX = gameWidth / 2;
var arenaY = gameHeight / 2;
// Game arena
var arena = game.addChild(LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5,
x: arenaX,
y: arenaY
}));
// Character
var character = game.addChild(new Character());
character.x = arenaX;
character.y = arenaY;
// Target boxes at corners
var targets = [];
var targetColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange
var targetPositions = [{
x: arenaX - arenaSize / 2 + 180,
y: arenaY - arenaSize / 2 + 180
},
// top-left
{
x: arenaX + arenaSize / 2 - 180,
y: arenaY - arenaSize / 2 + 180
},
// top-right
{
x: arenaX - arenaSize / 2 + 180,
y: arenaY + arenaSize / 2 - 180
},
// bottom-left
{
x: arenaX + arenaSize / 2 - 180,
y: arenaY + arenaSize / 2 - 180
} // bottom-right
];
for (var i = 0; i < 4; i++) {
var target = game.addChild(new TargetBox(targetColors[i]));
target.x = targetPositions[i].x;
target.y = targetPositions[i].y;
targets.push(target);
}
// Game state
var packages = [];
var confettiParticles = [];
var gameTime = 0;
var gameDuration = 30000; // 30 seconds for levels 1-3, 240000 for final level
var packageSpawnInterval = 2000; // 2 seconds
var lastPackageSpawn = 0;
var gameStarted = false;
var gameEnded = false;
var gameReady = false;
var currentLevel = 1;
var levelCompleted = false;
var finalLevelStarted = false;
var isHellMode = false;
var hellModeSelected = false;
var isInfiniteMode = false;
var infiniteModeSelected = false;
var infiniteMusicTimer = 0;
var infiniteMusicInterval = 108000; // 108 seconds in milliseconds
// Arena animation variables
var arenaAnimationTimes = [11, 23, 31, 41, 53, 63, 75, 85, 96, 107, 116, 128, 137, 149, 157, 169, 180, 190, 202, 214, 222, 232, 241, 253, 263, 271, 280, 290, 301, 310, 321, 329, 339, 349, 361, 370, 382, 394, 405, 414, 423, 435, 444, 452, 461, 471, 479, 490, 500, 510, 519, 527, 538, 546, 558, 567, 575, 584, 596, 608];
var currentArenaAnimationIndex = 0;
var isArenaAnimating = false;
var arenaAnimationStep = 0;
// Bomb mechanic variables
var lastBombSpawn = 0;
var bombSpawnInterval = 3000; // 3 seconds
var bombHoldTimer = 0;
var bombWarningText = null;
// Predetermined color sequence for level 1
var colorSequenceLevel1 = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff];
// Level 2 color sequence and timing intervals (in milliseconds)
var colorSequenceLevel2 = [0xff0000, 0xff8800, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0x0066ff, 0xffff00];
var timingIntervalsLevel2 = [1720, 2150, 1290, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1720, 1290, 1720, 1720, 1720, 1720, 1720, 2150, 1720, 1290];
// Level 3 color sequence and timing intervals (in milliseconds)
var colorSequenceLevel3 = [0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x0066ff, 0xffff00];
var timingIntervalsLevel3 = [1720, 1720, 2150, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1290, 1720];
// Final level color sequence (longer sequence for 4 minutes)
var colorSequenceFinal = [0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000];
var currentSequenceIndex = 0;
var currentTimingIndex = 0;
// UI
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var timeTxt = new Text2('Time: 30', {
size: 60,
fill: 0xFFFFFF
});
timeTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(timeTxt);
// Level display
var levelText = new Text2('LEVEL 1', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
// Ready button
var readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI
var normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
var hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
// Infinite button (only for level 1)
var infiniteButton = LK.getAsset('infinite1', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2 + 430,
scaleX: 4.76,
scaleY: 4.76
});
game.addChild(infiniteButton);
// Infinite button animation variables
var infiniteTextures = ['infinite1', 'infinite2', 'infinite3', 'infinite4', 'infinite5', 'infinite6', 'infinite7', 'infinite8'];
var currentInfiniteTextureIndex = 0;
var infiniteAnimationTimer = 0;
var infiniteAnimationSpeed = 4; // Change texture every 4 frames (about 15 fps at 60fps)
// Helper functions
function getColorName(colorValue) {
switch (colorValue) {
case 0xffff00:
return 'Yellow';
case 0x0066ff:
return 'Blue';
case 0xff0000:
return 'Red';
case 0xff8800:
return 'Orange';
default:
return 'Unknown';
}
}
function spawnPackage() {
var currentColorSequence;
var packageColor;
if (isInfiniteMode) {
// Infinite mode: completely random colors
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
packageColor = randomColors[Math.floor(Math.random() * randomColors.length)];
} else {
if (currentLevel === 1) {
currentColorSequence = colorSequenceLevel1;
} else if (currentLevel === 2) {
currentColorSequence = colorSequenceLevel2;
} else if (currentLevel === 3) {
currentColorSequence = colorSequenceLevel3;
} else {
currentColorSequence = colorSequenceFinal;
}
if (currentSequenceIndex >= currentColorSequence.length) return;
packageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
}
var newPackage = new Package(packageColor);
newPackage.x = character.x; // Spawn directly above character
newPackage.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena
packages.push(newPackage);
game.addChild(newPackage);
// Hell mode: 20% chance to spawn a second package 0.05 seconds later
if (isHellMode && Math.random() < 0.2) {
LK.setTimeout(function () {
// Get next color from sequence for the second package
var secondPackageColor;
if (currentSequenceIndex < currentColorSequence.length) {
secondPackageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
} else if (currentLevel === 4) {
// For final level, loop back to beginning
currentSequenceIndex = 0;
secondPackageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
} else {
// Use random color if sequence exhausted
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
secondPackageColor = randomColors[Math.floor(Math.random() * randomColors.length)];
}
var secondPackage = new Package(secondPackageColor);
secondPackage.x = character.x;
secondPackage.y = arenaY - arenaSize / 2 - 200;
packages.push(secondPackage);
game.addChild(secondPackage);
}, 150); // 0.15 seconds = 150ms
}
}
function spawnBomb() {
var newBomb = new Bomb();
newBomb.x = character.x; // Spawn directly above character
newBomb.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena
packages.push(newBomb);
game.addChild(newBomb);
}
function createConfettiExplosion(x, y, color) {
var allColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange
// Create more particles for bigger explosion (25 instead of 15)
for (var i = 0; i < 25; i++) {
var confettiColor;
if (i < 18) {
// Most particles (72%) use the main color
confettiColor = color;
} else {
// Some particles (28%) use random other colors
confettiColor = allColors[Math.floor(Math.random() * allColors.length)];
}
var confetti = new Confetti(confettiColor);
confetti.x = x;
confetti.y = y;
// Scale up confetti particles by 1.5x for bigger effect
tween(confetti, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 50,
easing: tween.easeOut
});
confettiParticles.push(confetti);
game.addChild(confetti);
}
}
function startArenaAnimation() {
if (isArenaAnimating) return;
isArenaAnimating = true;
arenaAnimationStep = 0;
// Start animation sequence
animateArenaStep();
}
function animateArenaStep() {
var arenaAssets = ['arena2', 'arena3', 'arena4'];
if (arenaAnimationStep < arenaAssets.length) {
// Change to next animation frame
var newArena = LK.getAsset(arenaAssets[arenaAnimationStep], {
anchorX: 0.5,
anchorY: 0.5,
x: arenaX,
y: arenaY,
scaleX: 12,
// Scale to match original arena size
scaleY: 12
});
// Add new arena behind all other objects (at index 1, after original arena)
game.addChildAt(newArena, 1);
// Auto-remove after 1.6 seconds
LK.setTimeout(function () {
newArena.destroy();
}, 1600);
arenaAnimationStep++;
// Schedule next step after 400ms
LK.setTimeout(function () {
animateArenaStep();
}, 400);
} else {
// Animation complete
isArenaAnimating = false;
}
}
function createFireExplosion(x, y) {
var flameColors = [0xFF4500, 0xFF6347, 0xFF0000, 0xFFA500, 0xFFD700]; // Orange, tomato, red, orange, gold
// Create 30 flame particles for explosion effect
for (var i = 0; i < 30; i++) {
var flameColor = flameColors[Math.floor(Math.random() * flameColors.length)];
var flame = new Confetti(flameColor);
flame.x = x;
flame.y = y;
// Enhanced velocities for explosive effect
flame.velocityX = (Math.random() - 0.5) * 600;
flame.velocityY = (Math.random() - 0.5) * 600;
flame.life = 90; // Medium life for flames
// Scale and fade effect using tween
tween(flame, {
scaleX: 2,
scaleY: 2
}, {
duration: 80,
easing: tween.easeOut
});
tween(flame, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut
});
confettiParticles.push(flame);
game.addChild(flame);
}
}
function createFireworks() {
var fireworksColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x00ff00, 0xff00ff, 0x00ffff];
// Create multiple firework explosions across the screen
for (var f = 0; f < 8; f++) {
var fireworkX = Math.random() * gameWidth;
var fireworkY = Math.random() * (gameHeight * 0.6) + gameHeight * 0.2;
var fireworkColor = fireworksColors[Math.floor(Math.random() * fireworksColors.length)];
// Create 40 particles per firework for spectacular effect
for (var i = 0; i < 40; i++) {
var firework = new Confetti(fireworkColor);
firework.x = fireworkX;
firework.y = fireworkY;
// Enhanced velocities for bigger explosion
firework.velocityX = (Math.random() - 0.5) * 800;
firework.velocityY = (Math.random() - 0.5) * 600 - 200;
firework.life = 120; // Longer life for fireworks
// Scale and fade effect using tween
tween(firework, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 100,
easing: tween.easeOut
});
tween(firework, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut
});
confettiParticles.push(firework);
game.addChild(firework);
}
}
}
function checkPackageTargetCollision(packageObj, target) {
var dx = packageObj.x - target.x;
var dy = packageObj.y - target.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Orange target gets larger hitbox (50% larger than base 120 = 180)
if (target.colorValue === 0xff8800) {
return distance < 180; // Orange target gets bigger hitbox
}
return distance < 156; // Increased from 104 to 156 (1.5x scale) for other targets
}
function throwPackage(targetX, targetY) {
if (!character.heldPackage) {
// Empty click penalty
LK.setScore(LK.getScore() - 15);
LK.getSound('miss').play();
return;
}
var packageToThrow = character.heldPackage;
character.heldPackage = null;
// Clear bomb warning if it exists
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
var dx = targetX - packageToThrow.x;
var dy = targetY - packageToThrow.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var speed = 800;
packageToThrow.throwVelocityX = dx / distance * speed * (1 / 60);
packageToThrow.throwVelocityY = dy / distance * speed * (1 / 60);
packageToThrow.thrown = true;
LK.getSound('throw').play();
}
// Event handlers
game.move = function (x, y, obj) {
var dx = x - character.x;
var dy = y - character.y;
character.targetRotation = Math.atan2(dy, dx);
};
game.down = function (x, y, obj) {
if (!gameReady) {
// Check infinite button first (only for level 1)
if (currentLevel === 1) {
var infiniteButtonX = gameWidth / 2;
var infiniteButtonY = gameHeight / 2 + 430;
var infiniteButtonWidth = 476; // 4.76x scale of 200px width = 952, so half is 476
var infiniteButtonHeight = 110; // 4.76x scale of 46.1px height = 219, so half is 110
if (x >= infiniteButtonX - infiniteButtonWidth && x <= infiniteButtonX + infiniteButtonWidth && y >= infiniteButtonY - infiniteButtonHeight && y <= infiniteButtonY + infiniteButtonHeight) {
// Start infinite mode
isInfiniteMode = true;
infiniteModeSelected = true;
hellModeSelected = true; // Skip mode selection since infinite mode is already chosen
gameReady = true;
gameStarted = true;
gameTime = 0;
lastPackageSpawn = 0;
// Set score to 10000 for infinite mode
LK.setScore(10000);
// Clean up UI elements and stop animation
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
if (levelText && levelText.parent) {
levelText.destroy();
}
if (readyButton && readyButton.parent) {
readyButton.destroy();
}
if (normalModeButton && normalModeButton.parent) {
normalModeButton.destroy();
}
if (hellModeButton && hellModeButton.parent) {
hellModeButton.destroy();
}
// Start infinite mode music
LK.playMusic('bgmusicinfinite');
return;
}
}
// Check mode selection if not selected
if (!hellModeSelected) {
// Check normal mode button
var normalButtonX = gameWidth / 2 - 220;
var normalButtonY = gameHeight / 2 + 130;
var normalButtonWidth = 150;
var normalButtonHeight = 60;
if (x >= normalButtonX - normalButtonWidth && x <= normalButtonX + normalButtonWidth && y >= normalButtonY - normalButtonHeight && y <= normalButtonY + normalButtonHeight) {
isHellMode = false;
hellModeSelected = true;
normalModeButton.destroy();
hellModeButton.destroy();
// Hide infinite button when normal mode is selected
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
// Remove passive effect from ready button
readyButton.tint = 0xFFFFFF;
readyButton.alpha = 1;
return;
}
// Check hell mode button
var hellButtonX = gameWidth / 2 + 220;
var hellButtonY = gameHeight / 2 + 130;
var hellButtonWidth = 100;
var hellButtonHeight = 100;
if (x >= hellButtonX - hellButtonWidth && x <= hellButtonX + hellButtonWidth && y >= hellButtonY - hellButtonHeight && y <= hellButtonY + hellButtonHeight) {
isHellMode = true;
hellModeSelected = true;
normalModeButton.destroy();
hellModeButton.destroy();
// Hide infinite button when hell mode is selected
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
// Remove passive effect from ready button
readyButton.tint = 0xFFFFFF;
readyButton.alpha = 1;
return;
}
return;
}
// Check if clicked on ready button area (only after mode selected)
if (hellModeSelected) {
var buttonX = gameWidth / 2;
var buttonY = gameHeight / 2;
var buttonWidth = 200;
var buttonHeight = 100;
if (x >= buttonX - buttonWidth && x <= buttonX + buttonWidth && y >= buttonY - buttonHeight && y <= buttonY + buttonHeight) {
gameReady = true;
readyButton.destroy();
levelText.destroy();
}
}
return;
}
if (gameEnded) return;
throwPackage(x, y);
};
// Main game update
game.update = function () {
if (!gameReady) return;
if (!gameStarted) {
gameStarted = true;
gameTime = 0; // Reset game time when starting
lastPackageSpawn = 0; // Reset package spawn timer
// Remove infinite button when game starts (level 1 only)
if (currentLevel === 1 && infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
if (currentLevel === 1) {
LK.getSound('level1').play();
LK.playMusic('bgmusic');
} else if (currentLevel === 2) {
LK.getSound('level2').play();
LK.playMusic('bgmusic');
} else if (currentLevel === 3) {
LK.getSound('level3').play();
LK.playMusic('bgmusic3');
} else if (currentLevel === 4) {
LK.getSound('final').play();
LK.playMusic('bgmusicfinal');
gameDuration = 240000; // 4 minutes for final level
finalLevelStarted = true;
}
}
if (gameEnded) return;
// Arena animation trigger at predetermined times - works for all levels
var currentGameTimeInSeconds = Math.floor(gameTime / 1000);
if (currentArenaAnimationIndex < arenaAnimationTimes.length && currentGameTimeInSeconds >= arenaAnimationTimes[currentArenaAnimationIndex]) {
startArenaAnimation();
currentArenaAnimationIndex++;
}
// Check for insufficient score in final level
if (currentLevel === 4) {
if (LK.getScore() < -200) {
gameEnded = true;
LK.showGameOver();
return;
}
}
gameTime += 1000 / 60; // Increment game time only after ready
if (isInfiniteMode) {
var elapsedTime = Math.floor(gameTime / 1000);
timeTxt.setText('Time: ' + elapsedTime);
} else {
var remainingTime = Math.max(0, Math.ceil((gameDuration - gameTime) / 1000));
timeTxt.setText('Time: ' + remainingTime);
}
// Infinite mode music timer
if (isInfiniteMode) {
infiniteMusicTimer += 1000 / 60;
if (infiniteMusicTimer >= infiniteMusicInterval) {
LK.playMusic('bgmusicinfinite');
infiniteMusicTimer = 0;
}
}
// Check for level completion (skip for infinite mode)
if (!isInfiniteMode && gameTime >= gameDuration) {
if (currentLevel === 1) {
// Level 1 completed, prepare for level 2
currentLevel = 2;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for next level
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show level 2 ready screen
levelText = new Text2('LEVEL 2', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for level 2
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else if (currentLevel === 2) {
// Level 2 completed, prepare for level 3
currentLevel = 3;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for level 3
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show level 3 ready screen
levelText = new Text2('LEVEL 3', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for level 3
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else if (currentLevel === 3) {
// Level 3 completed, prepare for final level
currentLevel = 4;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for final level
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show final level ready screen
levelText = new Text2('FINAL LEVEL', {
size: 120,
fill: 0xFFD700
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for final level
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else {
// Final level completed - you win with fireworks
gameEnded = true;
createFireworks();
LK.getSound('gameend').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 5000);
return;
}
}
// Hell mode and infinite mode bomb spawning for all levels
if ((isHellMode || isInfiniteMode) && gameTime - lastBombSpawn >= bombSpawnInterval) {
if (Math.random() < 0.25) {
// 25% chance
spawnBomb();
}
lastBombSpawn = gameTime;
}
// Infinite mode game over condition when score reaches 0
if (isInfiniteMode && LK.getScore() <= 0) {
gameEnded = true;
LK.showGameOver();
return;
}
// Play bomb sound continuously when bomb is present in hell mode or infinite mode for all levels
if ((isHellMode || isInfiniteMode) && character.heldPackage && character.heldPackage.isBomb) {
if (!LK.getSound('bomb').isPlaying) {
LK.getSound('bomb').play();
}
}
// Bomb holding penalty (10 points every 0.1 seconds = every 6 frames at 60fps)
if (character.heldPackage && character.heldPackage.isBomb) {
bombHoldTimer++;
if (bombHoldTimer >= 6) {
// 0.1 seconds at 60fps
LK.setScore(LK.getScore() - 10);
bombHoldTimer = 0;
}
// Show bomb warning if not already shown
if (!bombWarningText) {
bombWarningText = new Text2('BOMB! THROW AWAY!', {
size: 60,
fill: 0xFF0000
});
bombWarningText.anchor.set(0.5, 0.5);
bombWarningText.x = gameWidth / 2;
bombWarningText.y = 200;
game.addChild(bombWarningText);
// Flash effect
tween(bombWarningText, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (bombWarningText) {
tween(bombWarningText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
} else {
bombHoldTimer = 0;
// Clear bomb warning if not holding bomb
if (bombWarningText && (!character.heldPackage || !character.heldPackage.isBomb)) {
bombWarningText.destroy();
bombWarningText = null;
}
}
// Spawn packages
var currentColorSequence;
if (isInfiniteMode) {
// Infinite mode uses completely random colors
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
currentColorSequence = randomColors;
} else if (currentLevel === 1) {
currentColorSequence = colorSequenceLevel1;
} else if (currentLevel === 2) {
currentColorSequence = colorSequenceLevel2;
} else {
currentColorSequence = colorSequenceLevel3;
}
var currentSpawnInterval;
if (isInfiniteMode) {
// Infinite mode random intervals: 15% - 0.4s, 30% - 0.8s, 54% - 1.2s, 1% - 0.2s
var randomValue = Math.random();
if (randomValue < 0.15) {
currentSpawnInterval = 400; // 0.4 seconds
} else if (randomValue < 0.45) {
currentSpawnInterval = 800; // 0.8 seconds
} else if (randomValue < 0.99) {
currentSpawnInterval = 1200; // 1.2 seconds
} else {
currentSpawnInterval = 200; // 0.2 seconds
}
} else if (currentLevel === 1) {
currentSpawnInterval = packageSpawnInterval; // Keep 2000ms for level 1
} else if (currentLevel === 2) {
// Level 2 uses custom timing intervals
if (currentTimingIndex < timingIntervalsLevel2.length) {
currentSpawnInterval = timingIntervalsLevel2[currentTimingIndex];
} else {
currentSpawnInterval = 1720; // Default fallback
}
} else if (currentLevel === 3) {
// Level 3 uses custom timing intervals
if (currentTimingIndex < timingIntervalsLevel3.length) {
currentSpawnInterval = timingIntervalsLevel3[currentTimingIndex];
} else {
currentSpawnInterval = 1720; // Default fallback
}
} else if (currentLevel === 4) {
// Final level uses progressive timing based on game time
var progressiveTiming = [1500, 1875]; // 0-30 seconds
if (gameTime >= 30000 && gameTime < 60000) {
progressiveTiming = [1125, 1500, 1875]; // 30-60 seconds
} else if (gameTime >= 60000 && gameTime < 180000) {
progressiveTiming = [1125, 1500]; // 60-180 seconds
} else if (gameTime >= 180000) {
progressiveTiming = [750, 1125, 1500]; // 180-240 seconds
}
currentSpawnInterval = progressiveTiming[Math.floor(Math.random() * progressiveTiming.length)];
}
if (gameTime - lastPackageSpawn >= currentSpawnInterval) {
// For final level, allow sequence to loop when exhausted
if (currentLevel === 4 && currentSequenceIndex >= currentColorSequence.length) {
currentSequenceIndex = 0; // Reset to beginning of sequence
}
// Only spawn if we have colors available or it's the final level or infinite mode
if (currentSequenceIndex < currentColorSequence.length || currentLevel === 4 || isInfiniteMode) {
spawnPackage();
if (currentLevel === 2 || currentLevel === 3) {
currentTimingIndex++;
}
lastPackageSpawn = gameTime;
}
}
// Update packages
for (var i = packages.length - 1; i >= 0; i--) {
var pkg = packages[i];
if (pkg.lastY === undefined) pkg.lastY = pkg.y;
if (pkg.lastCaught === undefined) pkg.lastCaught = false;
if (pkg.lastThrown === undefined) pkg.lastThrown = false;
// Check if package reached character (catch)
if (!pkg.caught && !pkg.thrown) {
var distanceToCharacter = Math.sqrt(Math.pow(pkg.x - character.x, 2) + Math.pow(pkg.y - character.y, 2));
if (distanceToCharacter < 75) {
if (!character.heldPackage) {
pkg.caught = true;
character.heldPackage = pkg;
LK.getSound('catch').play();
}
}
}
// Check if package fell off screen (drop penalty)
if (!pkg.caught && !pkg.thrown && pkg.y > arenaY + arenaSize / 2 + 100) {
LK.setScore(LK.getScore() - 150);
pkg.destroy();
packages.splice(i, 1);
continue;
}
// Check thrown package collisions with targets
if (pkg.thrown) {
var hitTarget = false;
for (var j = 0; j < targets.length; j++) {
var target = targets[j];
if (checkPackageTargetCollision(pkg, target)) {
if (pkg.isBomb) {
// Bomb hit target - create flame explosion effect and apply penalty
LK.setScore(LK.getScore() - 500);
createFireExplosion(target.x, target.y);
LK.getSound('blast').play();
} else if (pkg.colorValue === target.colorValue) {
// Correct color match
LK.setScore(LK.getScore() + 100);
createConfettiExplosion(target.x, target.y, pkg.colorValue);
LK.getSound('hit').play();
} else {
// Wrong color penalty
LK.setScore(LK.getScore() - 60);
LK.getSound('miss').play();
}
hitTarget = true;
break;
}
}
if (hitTarget || pkg.x < 0 || pkg.x > gameWidth || pkg.y < 0 || pkg.y > gameHeight) {
if (!hitTarget) {
// Only apply missed throw penalty for non-bomb packages
if (!pkg.isBomb) {
LK.setScore(LK.getScore() - 60);
LK.getSound('miss').play();
}
}
pkg.destroy();
packages.splice(i, 1);
continue;
}
}
pkg.lastY = pkg.y;
pkg.lastCaught = pkg.caught;
pkg.lastThrown = pkg.thrown;
}
// Update confetti
for (var i = confettiParticles.length - 1; i >= 0; i--) {
var confetti = confettiParticles[i];
if (confetti.alpha <= 0) {
confetti.destroy();
confettiParticles.splice(i, 1);
}
}
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
};
// Infinite button animation - runs independently of game update
var infiniteButtonQueue = []; // Queue to track buttons for delayed destruction
LK.on('tick', function () {
// Animate infinite button (only in level 1 before game starts)
if (currentLevel === 1 && !gameStarted && infiniteButton && infiniteButton.parent) {
infiniteAnimationTimer++;
if (infiniteAnimationTimer >= infiniteAnimationSpeed) {
currentInfiniteTextureIndex = (currentInfiniteTextureIndex + 1) % infiniteTextures.length;
// Add current button to queue for delayed destruction
infiniteButtonQueue.push(infiniteButton);
// Create new button with next texture at 4.76x scale and fixed position
infiniteButton = LK.getAsset(infiniteTextures[currentInfiniteTextureIndex], {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2 + 430,
scaleX: 4.76,
scaleY: 4.76
});
game.addChild(infiniteButton);
// If we have 3 or more buttons in queue, destroy the oldest one
if (infiniteButtonQueue.length >= 3) {
var buttonToDestroy = infiniteButtonQueue.shift(); // Remove first (oldest) button from queue
// Fade out and destroy the oldest button with 0.1 second delay
tween(buttonToDestroy, {
alpha: 0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove old button after fade out
if (buttonToDestroy.parent) {
game.removeChild(buttonToDestroy);
}
buttonToDestroy.destroy();
}
});
}
infiniteAnimationTimer = 0;
}
}
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.isBomb = true;
self.speed = 172.5;
if (isInfiniteMode) {
self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase)
} else if (isHellMode) {
self.speed *= 4; // 4x speed in hell mode
}
self.caught = false;
self.thrown = false;
self.throwVelocityX = 0;
self.throwVelocityY = 0;
self.update = function () {
if (self.thrown) {
self.x += self.throwVelocityX;
self.y += self.throwVelocityY;
} else if (!self.caught) {
self.y += self.speed * (1 / 60);
}
};
return self;
});
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
});
self.heldPackage = null;
self.targetRotation = 0;
self.update = function () {
var rotationDiff = self.targetRotation - self.rotation;
while (rotationDiff > Math.PI) rotationDiff -= 2 * Math.PI;
while (rotationDiff < -Math.PI) rotationDiff += 2 * Math.PI;
self.rotation += rotationDiff * 0.1;
if (self.heldPackage) {
// Position package in front of character based on rotation
var packageDistance = 90; // Distance in front of character
var packageX = self.x + Math.cos(self.rotation) * packageDistance;
var packageY = self.y + Math.sin(self.rotation) * packageDistance;
// Smoothly tween package to new position for better visual flow
tween(self.heldPackage, {
x: packageX,
y: packageY,
rotation: self.rotation
}, {
duration: 100,
easing: tween.easeOut
});
}
};
return self;
});
var Confetti = Container.expand(function (colorValue) {
var self = Container.call(this);
var confettiGraphics = self.attachAsset('confetti', {
anchorX: 0.5,
anchorY: 0.5
});
confettiGraphics.tint = colorValue;
self.velocityX = (Math.random() - 0.5) * 400;
self.velocityY = (Math.random() - 0.5) * 400;
self.life = 60;
self.update = function () {
self.x += self.velocityX * (1 / 60);
self.y += self.velocityY * (1 / 60);
self.velocityY += 300 * (1 / 60);
self.life--;
if (self.life <= 0) {
self.alpha = 0;
}
};
return self;
});
var Package = Container.expand(function (colorValue) {
var self = Container.call(this);
var assetName = 'yellowpacket';
if (colorValue === 0x0066ff) assetName = 'bluepacket';else if (colorValue === 0xff0000) assetName = 'redpacket';else if (colorValue === 0xff8800) assetName = 'orangepacket';
var packageGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.colorValue = colorValue || 0xffffff;
self.speed = 172.5; // 150 * 1.15 = 15% increase
if (isInfiniteMode) {
self.speed = 172.5 * 3.6; // 3.6x faster than normal speed (20% increase)
} else if (isHellMode) {
self.speed *= 4; // 4x speed in hell mode
}
self.caught = false;
self.thrown = false;
self.throwVelocityX = 0;
self.throwVelocityY = 0;
self.update = function () {
if (self.thrown) {
self.x += self.throwVelocityX;
self.y += self.throwVelocityY;
} else if (!self.caught) {
self.y += self.speed * (1 / 60);
}
};
return self;
});
var TargetBox = Container.expand(function (colorValue) {
var self = Container.call(this);
var assetName = 'targetYellow';
if (colorValue === 0x0066ff) assetName = 'targetBlue';else if (colorValue === 0xff0000) assetName = 'targetRed';else if (colorValue === 0xff8800) assetName = 'targetOrange';
var targetGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.colorValue = colorValue;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
var gameWidth = 2048;
var gameHeight = 2732;
var arenaSize = 1600;
var arenaX = gameWidth / 2;
var arenaY = gameHeight / 2;
// Game arena
var arena = game.addChild(LK.getAsset('arena', {
anchorX: 0.5,
anchorY: 0.5,
x: arenaX,
y: arenaY
}));
// Character
var character = game.addChild(new Character());
character.x = arenaX;
character.y = arenaY;
// Target boxes at corners
var targets = [];
var targetColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange
var targetPositions = [{
x: arenaX - arenaSize / 2 + 180,
y: arenaY - arenaSize / 2 + 180
},
// top-left
{
x: arenaX + arenaSize / 2 - 180,
y: arenaY - arenaSize / 2 + 180
},
// top-right
{
x: arenaX - arenaSize / 2 + 180,
y: arenaY + arenaSize / 2 - 180
},
// bottom-left
{
x: arenaX + arenaSize / 2 - 180,
y: arenaY + arenaSize / 2 - 180
} // bottom-right
];
for (var i = 0; i < 4; i++) {
var target = game.addChild(new TargetBox(targetColors[i]));
target.x = targetPositions[i].x;
target.y = targetPositions[i].y;
targets.push(target);
}
// Game state
var packages = [];
var confettiParticles = [];
var gameTime = 0;
var gameDuration = 30000; // 30 seconds for levels 1-3, 240000 for final level
var packageSpawnInterval = 2000; // 2 seconds
var lastPackageSpawn = 0;
var gameStarted = false;
var gameEnded = false;
var gameReady = false;
var currentLevel = 1;
var levelCompleted = false;
var finalLevelStarted = false;
var isHellMode = false;
var hellModeSelected = false;
var isInfiniteMode = false;
var infiniteModeSelected = false;
var infiniteMusicTimer = 0;
var infiniteMusicInterval = 108000; // 108 seconds in milliseconds
// Arena animation variables
var arenaAnimationTimes = [11, 23, 31, 41, 53, 63, 75, 85, 96, 107, 116, 128, 137, 149, 157, 169, 180, 190, 202, 214, 222, 232, 241, 253, 263, 271, 280, 290, 301, 310, 321, 329, 339, 349, 361, 370, 382, 394, 405, 414, 423, 435, 444, 452, 461, 471, 479, 490, 500, 510, 519, 527, 538, 546, 558, 567, 575, 584, 596, 608];
var currentArenaAnimationIndex = 0;
var isArenaAnimating = false;
var arenaAnimationStep = 0;
// Bomb mechanic variables
var lastBombSpawn = 0;
var bombSpawnInterval = 3000; // 3 seconds
var bombHoldTimer = 0;
var bombWarningText = null;
// Predetermined color sequence for level 1
var colorSequenceLevel1 = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff8800, 0xff0000, 0xffff00, 0x0066ff];
// Level 2 color sequence and timing intervals (in milliseconds)
var colorSequenceLevel2 = [0xff0000, 0xff8800, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0x0066ff, 0xffff00];
var timingIntervalsLevel2 = [1720, 2150, 1290, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1720, 1290, 1720, 1720, 1720, 1720, 1720, 2150, 1720, 1290];
// Level 3 color sequence and timing intervals (in milliseconds)
var colorSequenceLevel3 = [0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x0066ff, 0xffff00];
var timingIntervalsLevel3 = [1720, 1720, 2150, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 2150, 1720, 1290, 1720, 1720, 1720, 1720, 2150, 1290, 1720];
// Final level color sequence (longer sequence for 4 minutes)
var colorSequenceFinal = [0xff0000, 0x0066ff, 0xffff00, 0xff8800, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xff0000, 0x0066ff, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0x0066ff, 0xff0000, 0xff8800, 0xffff00, 0xff8800, 0x0066ff, 0xff0000, 0xffff00, 0x0066ff, 0xff8800, 0xffff00, 0xff0000, 0xff8800, 0xffff00, 0x0066ff, 0xff0000, 0xffff00, 0xff8800, 0x0066ff, 0xff0000];
var currentSequenceIndex = 0;
var currentTimingIndex = 0;
// UI
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var timeTxt = new Text2('Time: 30', {
size: 60,
fill: 0xFFFFFF
});
timeTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(timeTxt);
// Level display
var levelText = new Text2('LEVEL 1', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
// Ready button
var readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI
var normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
var hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
// Infinite button (only for level 1)
var infiniteButton = LK.getAsset('infinite1', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2 + 430,
scaleX: 4.76,
scaleY: 4.76
});
game.addChild(infiniteButton);
// Infinite button animation variables
var infiniteTextures = ['infinite1', 'infinite2', 'infinite3', 'infinite4', 'infinite5', 'infinite6', 'infinite7', 'infinite8'];
var currentInfiniteTextureIndex = 0;
var infiniteAnimationTimer = 0;
var infiniteAnimationSpeed = 4; // Change texture every 4 frames (about 15 fps at 60fps)
// Helper functions
function getColorName(colorValue) {
switch (colorValue) {
case 0xffff00:
return 'Yellow';
case 0x0066ff:
return 'Blue';
case 0xff0000:
return 'Red';
case 0xff8800:
return 'Orange';
default:
return 'Unknown';
}
}
function spawnPackage() {
var currentColorSequence;
var packageColor;
if (isInfiniteMode) {
// Infinite mode: completely random colors
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
packageColor = randomColors[Math.floor(Math.random() * randomColors.length)];
} else {
if (currentLevel === 1) {
currentColorSequence = colorSequenceLevel1;
} else if (currentLevel === 2) {
currentColorSequence = colorSequenceLevel2;
} else if (currentLevel === 3) {
currentColorSequence = colorSequenceLevel3;
} else {
currentColorSequence = colorSequenceFinal;
}
if (currentSequenceIndex >= currentColorSequence.length) return;
packageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
}
var newPackage = new Package(packageColor);
newPackage.x = character.x; // Spawn directly above character
newPackage.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena
packages.push(newPackage);
game.addChild(newPackage);
// Hell mode: 20% chance to spawn a second package 0.05 seconds later
if (isHellMode && Math.random() < 0.2) {
LK.setTimeout(function () {
// Get next color from sequence for the second package
var secondPackageColor;
if (currentSequenceIndex < currentColorSequence.length) {
secondPackageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
} else if (currentLevel === 4) {
// For final level, loop back to beginning
currentSequenceIndex = 0;
secondPackageColor = currentColorSequence[currentSequenceIndex];
currentSequenceIndex++;
} else {
// Use random color if sequence exhausted
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
secondPackageColor = randomColors[Math.floor(Math.random() * randomColors.length)];
}
var secondPackage = new Package(secondPackageColor);
secondPackage.x = character.x;
secondPackage.y = arenaY - arenaSize / 2 - 200;
packages.push(secondPackage);
game.addChild(secondPackage);
}, 150); // 0.15 seconds = 150ms
}
}
function spawnBomb() {
var newBomb = new Bomb();
newBomb.x = character.x; // Spawn directly above character
newBomb.y = arenaY - arenaSize / 2 - 200; // Spawn higher above arena
packages.push(newBomb);
game.addChild(newBomb);
}
function createConfettiExplosion(x, y, color) {
var allColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800]; // yellow, blue, red, orange
// Create more particles for bigger explosion (25 instead of 15)
for (var i = 0; i < 25; i++) {
var confettiColor;
if (i < 18) {
// Most particles (72%) use the main color
confettiColor = color;
} else {
// Some particles (28%) use random other colors
confettiColor = allColors[Math.floor(Math.random() * allColors.length)];
}
var confetti = new Confetti(confettiColor);
confetti.x = x;
confetti.y = y;
// Scale up confetti particles by 1.5x for bigger effect
tween(confetti, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 50,
easing: tween.easeOut
});
confettiParticles.push(confetti);
game.addChild(confetti);
}
}
function startArenaAnimation() {
if (isArenaAnimating) return;
isArenaAnimating = true;
arenaAnimationStep = 0;
// Start animation sequence
animateArenaStep();
}
function animateArenaStep() {
var arenaAssets = ['arena2', 'arena3', 'arena4'];
if (arenaAnimationStep < arenaAssets.length) {
// Change to next animation frame
var newArena = LK.getAsset(arenaAssets[arenaAnimationStep], {
anchorX: 0.5,
anchorY: 0.5,
x: arenaX,
y: arenaY,
scaleX: 12,
// Scale to match original arena size
scaleY: 12
});
// Add new arena behind all other objects (at index 1, after original arena)
game.addChildAt(newArena, 1);
// Auto-remove after 1.6 seconds
LK.setTimeout(function () {
newArena.destroy();
}, 1600);
arenaAnimationStep++;
// Schedule next step after 400ms
LK.setTimeout(function () {
animateArenaStep();
}, 400);
} else {
// Animation complete
isArenaAnimating = false;
}
}
function createFireExplosion(x, y) {
var flameColors = [0xFF4500, 0xFF6347, 0xFF0000, 0xFFA500, 0xFFD700]; // Orange, tomato, red, orange, gold
// Create 30 flame particles for explosion effect
for (var i = 0; i < 30; i++) {
var flameColor = flameColors[Math.floor(Math.random() * flameColors.length)];
var flame = new Confetti(flameColor);
flame.x = x;
flame.y = y;
// Enhanced velocities for explosive effect
flame.velocityX = (Math.random() - 0.5) * 600;
flame.velocityY = (Math.random() - 0.5) * 600;
flame.life = 90; // Medium life for flames
// Scale and fade effect using tween
tween(flame, {
scaleX: 2,
scaleY: 2
}, {
duration: 80,
easing: tween.easeOut
});
tween(flame, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut
});
confettiParticles.push(flame);
game.addChild(flame);
}
}
function createFireworks() {
var fireworksColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800, 0x00ff00, 0xff00ff, 0x00ffff];
// Create multiple firework explosions across the screen
for (var f = 0; f < 8; f++) {
var fireworkX = Math.random() * gameWidth;
var fireworkY = Math.random() * (gameHeight * 0.6) + gameHeight * 0.2;
var fireworkColor = fireworksColors[Math.floor(Math.random() * fireworksColors.length)];
// Create 40 particles per firework for spectacular effect
for (var i = 0; i < 40; i++) {
var firework = new Confetti(fireworkColor);
firework.x = fireworkX;
firework.y = fireworkY;
// Enhanced velocities for bigger explosion
firework.velocityX = (Math.random() - 0.5) * 800;
firework.velocityY = (Math.random() - 0.5) * 600 - 200;
firework.life = 120; // Longer life for fireworks
// Scale and fade effect using tween
tween(firework, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 100,
easing: tween.easeOut
});
tween(firework, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut
});
confettiParticles.push(firework);
game.addChild(firework);
}
}
}
function checkPackageTargetCollision(packageObj, target) {
var dx = packageObj.x - target.x;
var dy = packageObj.y - target.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Orange target gets larger hitbox (50% larger than base 120 = 180)
if (target.colorValue === 0xff8800) {
return distance < 180; // Orange target gets bigger hitbox
}
return distance < 156; // Increased from 104 to 156 (1.5x scale) for other targets
}
function throwPackage(targetX, targetY) {
if (!character.heldPackage) {
// Empty click penalty
LK.setScore(LK.getScore() - 15);
LK.getSound('miss').play();
return;
}
var packageToThrow = character.heldPackage;
character.heldPackage = null;
// Clear bomb warning if it exists
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
var dx = targetX - packageToThrow.x;
var dy = targetY - packageToThrow.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var speed = 800;
packageToThrow.throwVelocityX = dx / distance * speed * (1 / 60);
packageToThrow.throwVelocityY = dy / distance * speed * (1 / 60);
packageToThrow.thrown = true;
LK.getSound('throw').play();
}
// Event handlers
game.move = function (x, y, obj) {
var dx = x - character.x;
var dy = y - character.y;
character.targetRotation = Math.atan2(dy, dx);
};
game.down = function (x, y, obj) {
if (!gameReady) {
// Check infinite button first (only for level 1)
if (currentLevel === 1) {
var infiniteButtonX = gameWidth / 2;
var infiniteButtonY = gameHeight / 2 + 430;
var infiniteButtonWidth = 476; // 4.76x scale of 200px width = 952, so half is 476
var infiniteButtonHeight = 110; // 4.76x scale of 46.1px height = 219, so half is 110
if (x >= infiniteButtonX - infiniteButtonWidth && x <= infiniteButtonX + infiniteButtonWidth && y >= infiniteButtonY - infiniteButtonHeight && y <= infiniteButtonY + infiniteButtonHeight) {
// Start infinite mode
isInfiniteMode = true;
infiniteModeSelected = true;
hellModeSelected = true; // Skip mode selection since infinite mode is already chosen
gameReady = true;
gameStarted = true;
gameTime = 0;
lastPackageSpawn = 0;
// Set score to 10000 for infinite mode
LK.setScore(10000);
// Clean up UI elements and stop animation
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
if (levelText && levelText.parent) {
levelText.destroy();
}
if (readyButton && readyButton.parent) {
readyButton.destroy();
}
if (normalModeButton && normalModeButton.parent) {
normalModeButton.destroy();
}
if (hellModeButton && hellModeButton.parent) {
hellModeButton.destroy();
}
// Start infinite mode music
LK.playMusic('bgmusicinfinite');
return;
}
}
// Check mode selection if not selected
if (!hellModeSelected) {
// Check normal mode button
var normalButtonX = gameWidth / 2 - 220;
var normalButtonY = gameHeight / 2 + 130;
var normalButtonWidth = 150;
var normalButtonHeight = 60;
if (x >= normalButtonX - normalButtonWidth && x <= normalButtonX + normalButtonWidth && y >= normalButtonY - normalButtonHeight && y <= normalButtonY + normalButtonHeight) {
isHellMode = false;
hellModeSelected = true;
normalModeButton.destroy();
hellModeButton.destroy();
// Hide infinite button when normal mode is selected
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
// Remove passive effect from ready button
readyButton.tint = 0xFFFFFF;
readyButton.alpha = 1;
return;
}
// Check hell mode button
var hellButtonX = gameWidth / 2 + 220;
var hellButtonY = gameHeight / 2 + 130;
var hellButtonWidth = 100;
var hellButtonHeight = 100;
if (x >= hellButtonX - hellButtonWidth && x <= hellButtonX + hellButtonWidth && y >= hellButtonY - hellButtonHeight && y <= hellButtonY + hellButtonHeight) {
isHellMode = true;
hellModeSelected = true;
normalModeButton.destroy();
hellModeButton.destroy();
// Hide infinite button when hell mode is selected
if (infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
// Clean up any queued infinite buttons from animation
for (var q = infiniteButtonQueue.length - 1; q >= 0; q--) {
if (infiniteButtonQueue[q] && infiniteButtonQueue[q].parent) {
infiniteButtonQueue[q].destroy();
}
}
infiniteButtonQueue = []; // Clear the queue
// Remove passive effect from ready button
readyButton.tint = 0xFFFFFF;
readyButton.alpha = 1;
return;
}
return;
}
// Check if clicked on ready button area (only after mode selected)
if (hellModeSelected) {
var buttonX = gameWidth / 2;
var buttonY = gameHeight / 2;
var buttonWidth = 200;
var buttonHeight = 100;
if (x >= buttonX - buttonWidth && x <= buttonX + buttonWidth && y >= buttonY - buttonHeight && y <= buttonY + buttonHeight) {
gameReady = true;
readyButton.destroy();
levelText.destroy();
}
}
return;
}
if (gameEnded) return;
throwPackage(x, y);
};
// Main game update
game.update = function () {
if (!gameReady) return;
if (!gameStarted) {
gameStarted = true;
gameTime = 0; // Reset game time when starting
lastPackageSpawn = 0; // Reset package spawn timer
// Remove infinite button when game starts (level 1 only)
if (currentLevel === 1 && infiniteButton && infiniteButton.parent) {
infiniteButton.destroy();
infiniteButton = null;
}
if (currentLevel === 1) {
LK.getSound('level1').play();
LK.playMusic('bgmusic');
} else if (currentLevel === 2) {
LK.getSound('level2').play();
LK.playMusic('bgmusic');
} else if (currentLevel === 3) {
LK.getSound('level3').play();
LK.playMusic('bgmusic3');
} else if (currentLevel === 4) {
LK.getSound('final').play();
LK.playMusic('bgmusicfinal');
gameDuration = 240000; // 4 minutes for final level
finalLevelStarted = true;
}
}
if (gameEnded) return;
// Arena animation trigger at predetermined times - works for all levels
var currentGameTimeInSeconds = Math.floor(gameTime / 1000);
if (currentArenaAnimationIndex < arenaAnimationTimes.length && currentGameTimeInSeconds >= arenaAnimationTimes[currentArenaAnimationIndex]) {
startArenaAnimation();
currentArenaAnimationIndex++;
}
// Check for insufficient score in final level
if (currentLevel === 4) {
if (LK.getScore() < -200) {
gameEnded = true;
LK.showGameOver();
return;
}
}
gameTime += 1000 / 60; // Increment game time only after ready
if (isInfiniteMode) {
var elapsedTime = Math.floor(gameTime / 1000);
timeTxt.setText('Time: ' + elapsedTime);
} else {
var remainingTime = Math.max(0, Math.ceil((gameDuration - gameTime) / 1000));
timeTxt.setText('Time: ' + remainingTime);
}
// Infinite mode music timer
if (isInfiniteMode) {
infiniteMusicTimer += 1000 / 60;
if (infiniteMusicTimer >= infiniteMusicInterval) {
LK.playMusic('bgmusicinfinite');
infiniteMusicTimer = 0;
}
}
// Check for level completion (skip for infinite mode)
if (!isInfiniteMode && gameTime >= gameDuration) {
if (currentLevel === 1) {
// Level 1 completed, prepare for level 2
currentLevel = 2;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for next level
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show level 2 ready screen
levelText = new Text2('LEVEL 2', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for level 2
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else if (currentLevel === 2) {
// Level 2 completed, prepare for level 3
currentLevel = 3;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for level 3
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show level 3 ready screen
levelText = new Text2('LEVEL 3', {
size: 120,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for level 3
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else if (currentLevel === 3) {
// Level 3 completed, prepare for final level
currentLevel = 4;
levelCompleted = true;
gameStarted = false;
gameReady = false;
gameEnded = false;
gameTime = 0;
lastPackageSpawn = 0;
currentSequenceIndex = 0;
currentTimingIndex = 0;
currentArenaAnimationIndex = 0; // Reset arena animation timing
hellModeSelected = false; // Reset mode selection for final level
// Reset bomb mechanics
lastBombSpawn = 0;
bombHoldTimer = 0;
if (bombWarningText) {
bombWarningText.destroy();
bombWarningText = null;
}
// Reset character state
character.heldPackage = null;
character.x = arenaX;
character.y = arenaY;
character.rotation = 0;
character.targetRotation = 0;
// Clear all packages
for (var p = packages.length - 1; p >= 0; p--) {
packages[p].destroy();
packages.splice(p, 1);
}
// Clear all confetti
for (var c = confettiParticles.length - 1; c >= 0; c--) {
confettiParticles[c].destroy();
confettiParticles.splice(c, 1);
}
// Show final level ready screen
levelText = new Text2('FINAL LEVEL', {
size: 120,
fill: 0xFFD700
});
levelText.anchor.set(0.5, 0.5);
levelText.x = gameWidth / 2;
levelText.y = gameHeight / 2 - 250;
game.addChild(levelText);
readyButton = LK.getAsset('ready', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2
});
readyButton.tint = 0x666666; // Dark grey passive effect
readyButton.alpha = 0.5; // Additional passive effect
game.addChild(readyButton);
// Hell mode selection UI for final level
normalModeButton = new Text2('NORMAL', {
size: 120,
fill: 0x00FF00
});
normalModeButton.anchor.set(0.5, 0.5);
normalModeButton.x = gameWidth / 2 - 220;
normalModeButton.y = gameHeight / 2 + 130;
game.addChild(normalModeButton);
hellModeButton = LK.getAsset('hell', {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2 + 220,
y: gameHeight / 2 + 130,
scaleX: 2,
scaleY: 2
});
game.addChild(hellModeButton);
return;
} else {
// Final level completed - you win with fireworks
gameEnded = true;
createFireworks();
LK.getSound('gameend').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 5000);
return;
}
}
// Hell mode and infinite mode bomb spawning for all levels
if ((isHellMode || isInfiniteMode) && gameTime - lastBombSpawn >= bombSpawnInterval) {
if (Math.random() < 0.25) {
// 25% chance
spawnBomb();
}
lastBombSpawn = gameTime;
}
// Infinite mode game over condition when score reaches 0
if (isInfiniteMode && LK.getScore() <= 0) {
gameEnded = true;
LK.showGameOver();
return;
}
// Play bomb sound continuously when bomb is present in hell mode or infinite mode for all levels
if ((isHellMode || isInfiniteMode) && character.heldPackage && character.heldPackage.isBomb) {
if (!LK.getSound('bomb').isPlaying) {
LK.getSound('bomb').play();
}
}
// Bomb holding penalty (10 points every 0.1 seconds = every 6 frames at 60fps)
if (character.heldPackage && character.heldPackage.isBomb) {
bombHoldTimer++;
if (bombHoldTimer >= 6) {
// 0.1 seconds at 60fps
LK.setScore(LK.getScore() - 10);
bombHoldTimer = 0;
}
// Show bomb warning if not already shown
if (!bombWarningText) {
bombWarningText = new Text2('BOMB! THROW AWAY!', {
size: 60,
fill: 0xFF0000
});
bombWarningText.anchor.set(0.5, 0.5);
bombWarningText.x = gameWidth / 2;
bombWarningText.y = 200;
game.addChild(bombWarningText);
// Flash effect
tween(bombWarningText, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (bombWarningText) {
tween(bombWarningText, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
});
}
} else {
bombHoldTimer = 0;
// Clear bomb warning if not holding bomb
if (bombWarningText && (!character.heldPackage || !character.heldPackage.isBomb)) {
bombWarningText.destroy();
bombWarningText = null;
}
}
// Spawn packages
var currentColorSequence;
if (isInfiniteMode) {
// Infinite mode uses completely random colors
var randomColors = [0xffff00, 0x0066ff, 0xff0000, 0xff8800];
currentColorSequence = randomColors;
} else if (currentLevel === 1) {
currentColorSequence = colorSequenceLevel1;
} else if (currentLevel === 2) {
currentColorSequence = colorSequenceLevel2;
} else {
currentColorSequence = colorSequenceLevel3;
}
var currentSpawnInterval;
if (isInfiniteMode) {
// Infinite mode random intervals: 15% - 0.4s, 30% - 0.8s, 54% - 1.2s, 1% - 0.2s
var randomValue = Math.random();
if (randomValue < 0.15) {
currentSpawnInterval = 400; // 0.4 seconds
} else if (randomValue < 0.45) {
currentSpawnInterval = 800; // 0.8 seconds
} else if (randomValue < 0.99) {
currentSpawnInterval = 1200; // 1.2 seconds
} else {
currentSpawnInterval = 200; // 0.2 seconds
}
} else if (currentLevel === 1) {
currentSpawnInterval = packageSpawnInterval; // Keep 2000ms for level 1
} else if (currentLevel === 2) {
// Level 2 uses custom timing intervals
if (currentTimingIndex < timingIntervalsLevel2.length) {
currentSpawnInterval = timingIntervalsLevel2[currentTimingIndex];
} else {
currentSpawnInterval = 1720; // Default fallback
}
} else if (currentLevel === 3) {
// Level 3 uses custom timing intervals
if (currentTimingIndex < timingIntervalsLevel3.length) {
currentSpawnInterval = timingIntervalsLevel3[currentTimingIndex];
} else {
currentSpawnInterval = 1720; // Default fallback
}
} else if (currentLevel === 4) {
// Final level uses progressive timing based on game time
var progressiveTiming = [1500, 1875]; // 0-30 seconds
if (gameTime >= 30000 && gameTime < 60000) {
progressiveTiming = [1125, 1500, 1875]; // 30-60 seconds
} else if (gameTime >= 60000 && gameTime < 180000) {
progressiveTiming = [1125, 1500]; // 60-180 seconds
} else if (gameTime >= 180000) {
progressiveTiming = [750, 1125, 1500]; // 180-240 seconds
}
currentSpawnInterval = progressiveTiming[Math.floor(Math.random() * progressiveTiming.length)];
}
if (gameTime - lastPackageSpawn >= currentSpawnInterval) {
// For final level, allow sequence to loop when exhausted
if (currentLevel === 4 && currentSequenceIndex >= currentColorSequence.length) {
currentSequenceIndex = 0; // Reset to beginning of sequence
}
// Only spawn if we have colors available or it's the final level or infinite mode
if (currentSequenceIndex < currentColorSequence.length || currentLevel === 4 || isInfiniteMode) {
spawnPackage();
if (currentLevel === 2 || currentLevel === 3) {
currentTimingIndex++;
}
lastPackageSpawn = gameTime;
}
}
// Update packages
for (var i = packages.length - 1; i >= 0; i--) {
var pkg = packages[i];
if (pkg.lastY === undefined) pkg.lastY = pkg.y;
if (pkg.lastCaught === undefined) pkg.lastCaught = false;
if (pkg.lastThrown === undefined) pkg.lastThrown = false;
// Check if package reached character (catch)
if (!pkg.caught && !pkg.thrown) {
var distanceToCharacter = Math.sqrt(Math.pow(pkg.x - character.x, 2) + Math.pow(pkg.y - character.y, 2));
if (distanceToCharacter < 75) {
if (!character.heldPackage) {
pkg.caught = true;
character.heldPackage = pkg;
LK.getSound('catch').play();
}
}
}
// Check if package fell off screen (drop penalty)
if (!pkg.caught && !pkg.thrown && pkg.y > arenaY + arenaSize / 2 + 100) {
LK.setScore(LK.getScore() - 150);
pkg.destroy();
packages.splice(i, 1);
continue;
}
// Check thrown package collisions with targets
if (pkg.thrown) {
var hitTarget = false;
for (var j = 0; j < targets.length; j++) {
var target = targets[j];
if (checkPackageTargetCollision(pkg, target)) {
if (pkg.isBomb) {
// Bomb hit target - create flame explosion effect and apply penalty
LK.setScore(LK.getScore() - 500);
createFireExplosion(target.x, target.y);
LK.getSound('blast').play();
} else if (pkg.colorValue === target.colorValue) {
// Correct color match
LK.setScore(LK.getScore() + 100);
createConfettiExplosion(target.x, target.y, pkg.colorValue);
LK.getSound('hit').play();
} else {
// Wrong color penalty
LK.setScore(LK.getScore() - 60);
LK.getSound('miss').play();
}
hitTarget = true;
break;
}
}
if (hitTarget || pkg.x < 0 || pkg.x > gameWidth || pkg.y < 0 || pkg.y > gameHeight) {
if (!hitTarget) {
// Only apply missed throw penalty for non-bomb packages
if (!pkg.isBomb) {
LK.setScore(LK.getScore() - 60);
LK.getSound('miss').play();
}
}
pkg.destroy();
packages.splice(i, 1);
continue;
}
}
pkg.lastY = pkg.y;
pkg.lastCaught = pkg.caught;
pkg.lastThrown = pkg.thrown;
}
// Update confetti
for (var i = confettiParticles.length - 1; i >= 0; i--) {
var confetti = confettiParticles[i];
if (confetti.alpha <= 0) {
confetti.destroy();
confettiParticles.splice(i, 1);
}
}
// Update score display
scoreTxt.setText('Score: ' + LK.getScore());
};
// Infinite button animation - runs independently of game update
var infiniteButtonQueue = []; // Queue to track buttons for delayed destruction
LK.on('tick', function () {
// Animate infinite button (only in level 1 before game starts)
if (currentLevel === 1 && !gameStarted && infiniteButton && infiniteButton.parent) {
infiniteAnimationTimer++;
if (infiniteAnimationTimer >= infiniteAnimationSpeed) {
currentInfiniteTextureIndex = (currentInfiniteTextureIndex + 1) % infiniteTextures.length;
// Add current button to queue for delayed destruction
infiniteButtonQueue.push(infiniteButton);
// Create new button with next texture at 4.76x scale and fixed position
infiniteButton = LK.getAsset(infiniteTextures[currentInfiniteTextureIndex], {
anchorX: 0.5,
anchorY: 0.5,
x: gameWidth / 2,
y: gameHeight / 2 + 430,
scaleX: 4.76,
scaleY: 4.76
});
game.addChild(infiniteButton);
// If we have 3 or more buttons in queue, destroy the oldest one
if (infiniteButtonQueue.length >= 3) {
var buttonToDestroy = infiniteButtonQueue.shift(); // Remove first (oldest) button from queue
// Fade out and destroy the oldest button with 0.1 second delay
tween(buttonToDestroy, {
alpha: 0
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove old button after fade out
if (buttonToDestroy.parent) {
game.removeChild(buttonToDestroy);
}
buttonToDestroy.destroy();
}
});
}
infiniteAnimationTimer = 0;
}
}
});
bire bir aynı daha açık parlak sarı
birebir aynısı sarı-gri renkleri biraz daha koyu gri ile karıştır mavi-gri renklerin maviliğini biraz arttır. biraz da kırmızı-gri kareleri düzel
birebir aynısı. karelerin yerlerini değiştir dizilimi bozmadan
fitili ateşlenmiş parlak üzerinde yeşil tehlike işareti olan siyah bomba. In-Game asset. 2d. High contrast. No shadows
müzik temalı cool ready yazısı biraz da sprey boya punk tarzı. In-Game asset. 2d. High contrast. No shadows
dehşet saçan bir hell mode yazısı tek line olsun geniş kısa. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
açık mavi dj masası ekipmanı. In-Game asset. 2d. High contrast. No shadows
aynı stil daha parlak ve altınımsı mikrofon ve kulaklığı kaldır
orange cool dj keyboard rgb keys. In-Game asset. 2d. High contrast. No shadows
red cool dj headset rgp neon. In-Game asset. 2d. High contrast. No shadows