/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
starsCollected: 0
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.5;
self.bounceStrength = -0.7;
self.isAlive = true;
self.update = function () {
if (!self.isAlive) {
return;
}
// Apply gravity
if (game.gravityDirection === 'down') {
self.velocityY += self.gravity;
} else if (game.gravityDirection === 'up') {
self.velocityY -= self.gravity;
} else if (game.gravityDirection === 'left') {
self.velocityX -= self.gravity;
} else if (game.gravityDirection === 'right') {
self.velocityX += self.gravity;
}
// Apply movement
self.x += self.velocityX;
self.y += self.velocityY;
// Friction
self.velocityX *= 0.99;
self.velocityY *= 0.99;
// Contain within game bounds
if (self.x < 40) {
self.x = 40;
self.velocityX *= self.bounceStrength;
LK.getSound('bounce').play();
} else if (self.x > 2048 - 40) {
self.x = 2048 - 40;
self.velocityX *= self.bounceStrength;
LK.getSound('bounce').play();
}
if (self.y < 40) {
self.y = 40;
self.velocityY *= self.bounceStrength;
LK.getSound('bounce').play();
} else if (self.y > 2732 - 40) {
self.y = 2732 - 40;
self.velocityY *= self.bounceStrength;
LK.getSound('bounce').play();
}
// Spin the ball based on velocity
ballGraphics.rotation += self.velocityX * 0.02;
};
self.reset = function (x, y) {
self.x = x;
self.y = y;
self.velocityX = 0;
self.velocityY = 0;
self.isAlive = true;
ballGraphics.alpha = 1;
};
self.die = function () {
if (!self.isAlive) {
return;
}
self.isAlive = false;
LK.getSound('death').play();
// Shrink and fade out
tween(ballGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
LK.setTimeout(function () {
game.resetLevel();
}, 500);
}
});
};
return self;
});
var GravityArrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrowIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
self.updateDirection = function (direction) {
// Reset rotation
arrowGraphics.rotation = 0;
if (direction === 'down') {
arrowGraphics.rotation = 0;
} else if (direction === 'up') {
arrowGraphics.rotation = Math.PI;
} else if (direction === 'left') {
arrowGraphics.rotation = Math.PI / 2;
} else if (direction === 'right') {
arrowGraphics.rotation = -Math.PI / 2;
}
};
return self;
});
var Hazard = Container.expand(function () {
var self = Container.call(this);
var hazardGraphics = self.attachAsset('hazard', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate the hazard to look dangerous
hazardGraphics.rotation = Math.PI / 4;
self.checkCollision = function (ball) {
if (!ball.isAlive) {
return;
}
var distance = Math.sqrt(Math.pow(self.x - ball.x, 2) + Math.pow(self.y - ball.y, 2));
if (distance < 70) {
ball.die();
}
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
var platformGraphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = platformGraphics.width;
self.height = platformGraphics.height;
self.checkCollision = function (ball) {
if (!ball.isAlive) {
return;
}
var ballLeft = ball.x - 40;
var ballRight = ball.x + 40;
var ballTop = ball.y - 40;
var ballBottom = ball.y + 40;
var platformLeft = self.x - self.width / 2;
var platformRight = self.x + self.width / 2;
var platformTop = self.y - self.height / 2;
var platformBottom = self.y + self.height / 2;
// Check if collision is possible
if (ballRight < platformLeft || ballLeft > platformRight || ballBottom < platformTop || ballTop > platformBottom) {
return;
}
// Calculate previous position
var prevBallLeft = ball.x - ball.velocityX - 40;
var prevBallRight = ball.x - ball.velocityX + 40;
var prevBallTop = ball.y - ball.velocityY - 40;
var prevBallBottom = ball.y - ball.velocityY + 40;
// Determine collision side
if (prevBallBottom <= platformTop && ballBottom > platformTop) {
// Collision from top
ball.y = platformTop - 39;
ball.velocityY *= ball.bounceStrength;
LK.getSound('bounce').play();
} else if (prevBallTop >= platformBottom && ballTop < platformBottom) {
// Collision from bottom
ball.y = platformBottom + 39;
ball.velocityY *= ball.bounceStrength;
LK.getSound('bounce').play();
} else if (prevBallRight <= platformLeft && ballRight > platformLeft) {
// Collision from left
ball.x = platformLeft - 39;
ball.velocityX *= ball.bounceStrength;
LK.getSound('bounce').play();
} else if (prevBallLeft >= platformRight && ballLeft < platformRight) {
// Collision from right
ball.x = platformRight + 39;
ball.velocityX *= ball.bounceStrength;
LK.getSound('bounce').play();
}
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
var portalGraphics = self.attachAsset('portal', {
anchorX: 0.5,
anchorY: 0.5
});
// Make portal spin
function spin() {
tween(portalGraphics, {
rotation: portalGraphics.rotation + Math.PI * 2
}, {
duration: 3000,
onFinish: spin
});
}
spin();
self.checkCollision = function (ball) {
if (!ball.isAlive) {
return;
}
var distance = Math.sqrt(Math.pow(self.x - ball.x, 2) + Math.pow(self.y - ball.y, 2));
if (distance < 70 && game.canCompleteLevel()) {
LK.getSound('levelComplete').play();
// Save progress
storage.currentLevel = game.currentLevel + 1;
storage.starsCollected += game.starsCollected;
ball.isAlive = false;
// Animate completion
tween(ball, {
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
// Advance to next level or win
if (game.currentLevel >= game.levelDesigns.length) {
LK.showYouWin();
} else {
game.loadLevel(game.currentLevel + 1);
storage.currentLevel = game.currentLevel; // Save progress to start from the next level
}
}
});
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
// Make star pulsate
function pulsate() {
tween(starGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(starGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: pulsate
});
}
});
}
pulsate();
self.checkCollision = function (ball) {
if (self.collected || !ball.isAlive) {
return;
}
var distance = Math.sqrt(Math.pow(self.x - ball.x, 2) + Math.pow(self.y - ball.y, 2));
if (distance < 65) {
self.collected = true;
game.starsCollected++;
// Update star counter
game.updateStarCounter();
LK.getSound('collect').play();
// Animate collection
tween(starGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.visible = false;
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2C3E50
});
/****
* Game Code
****/
// Game state
game.currentLevel = storage.currentLevel || 1;
game.gravityDirection = 'down';
game.starsCollected = 0;
game.totalStarsInLevel = 0;
// Game objects
var ball;
var platforms = [];
var hazards = [];
var stars = [];
var portal;
var gravityArrow;
// UI elements
var levelText;
var starsText;
var instructionText;
// Level designs
game.levelDesigns = [
// Level 1
{
ballStart: {
x: 300,
y: 300
},
platforms: [{
x: 300,
y: 500,
width: 400,
height: 40
}, {
x: 800,
y: 800,
width: 400,
height: 40
}, {
x: 1300,
y: 1200,
width: 400,
height: 40
}],
hazards: [{
x: 800,
y: 600
}],
stars: [{
x: 800,
y: 700
}, {
x: 1300,
y: 1000
}],
portal: {
x: 1700,
y: 1200
}
},
// Level 2
{
ballStart: {
x: 200,
y: 200
},
platforms: [{
x: 200,
y: 400,
width: 400,
height: 40
}, {
x: 600,
y: 800,
width: 400,
height: 40
}, {
x: 1000,
y: 1200,
width: 400,
height: 40
}, {
x: 1400,
y: 800,
width: 400,
height: 40
}, {
x: 1800,
y: 400,
width: 400,
height: 40
}],
hazards: [{
x: 600,
y: 600
}, {
x: 1400,
y: 600
}, {
x: 1000,
y: 1000
}],
stars: [{
x: 600,
y: 700
}, {
x: 1000,
y: 1100
}, {
x: 1400,
y: 700
}],
portal: {
x: 1800,
y: 300
}
},
// Level 3
{
ballStart: {
x: 200,
y: 2500
},
platforms: [{
x: 200,
y: 2600,
width: 400,
height: 40
}, {
x: 700,
y: 2400,
width: 400,
height: 40
}, {
x: 1200,
y: 2200,
width: 400,
height: 40
}, {
x: 1700,
y: 2000,
width: 400,
height: 40
}, {
x: 1200,
y: 1800,
width: 400,
height: 40
}, {
x: 700,
y: 1600,
width: 400,
height: 40
}, {
x: 200,
y: 1400,
width: 400,
height: 40
}, {
x: 700,
y: 1200,
width: 400,
height: 40
}, {
x: 1200,
y: 1000,
width: 400,
height: 40
}, {
x: 1700,
y: 800,
width: 400,
height: 40
}],
hazards: [{
x: 500,
y: 2400
}, {
x: 1000,
y: 2200
}, {
x: 1500,
y: 2000
}, {
x: 1000,
y: 1800
}, {
x: 500,
y: 1600
}, {
x: 500,
y: 1200
}, {
x: 1000,
y: 1000
}, {
x: 1500,
y: 800
}],
stars: [{
x: 700,
y: 2300
}, {
x: 1200,
y: 2100
}, {
x: 1700,
y: 1900
}, {
x: 1200,
y: 1700
}, {
x: 700,
y: 1500
}],
portal: {
x: 1700,
y: 700
}
}],
// Level 4
{
ballStart: {
x: 100,
y: 100
},
platforms: [{
x: 200,
y: 300,
width: 400,
height: 40
}, {
x: 600,
y: 600,
width: 400,
height: 40
}, {
x: 1000,
y: 900,
width: 400,
height: 40
}, {
x: 1400,
y: 1200,
width: 400,
height: 40
}, {
x: 1800,
y: 1500,
width: 400,
height: 40
}],
hazards: [{
x: 400,
y: 450
}, {
x: 800,
y: 750
}, {
x: 1200,
y: 1050
}, {
x: 1600,
y: 1350
}],
stars: [{
x: 300,
y: 350
}, {
x: 700,
y: 650
}, {
x: 1100,
y: 950
}, {
x: 1500,
y: 1250
}],
portal: {
x: 1900,
y: 1600
}
},
// Level 5
{
ballStart: {
x: 100,
y: 2500
},
platforms: [{
x: 200,
y: 2600,
width: 400,
height: 40
}, {
x: 700,
y: 2400,
width: 400,
height: 40
}, {
x: 1200,
y: 2200,
width: 400,
height: 40
}, {
x: 1700,
y: 2000,
width: 400,
height: 40
}, {
x: 1200,
y: 1800,
width: 400,
height: 40
}, {
x: 700,
y: 1600,
width: 400,
height: 40
}, {
x: 200,
y: 1400,
width: 400,
height: 40
}, {
x: 700,
y: 1200,
width: 400,
height: 40
}, {
x: 1200,
y: 1000,
width: 400,
height: 40
}, {
x: 1700,
y: 800,
width: 400,
height: 40
}],
hazards: [{
x: 500,
y: 2400
}, {
x: 1000,
y: 2200
}, {
x: 1500,
y: 2000
}, {
x: 1000,
y: 1800
}, {
x: 500,
y: 1600
}, {
x: 500,
y: 1200
}, {
x: 1000,
y: 1000
}, {
x: 1500,
y: 800
}],
stars: [{
x: 700,
y: 2300
}, {
x: 1200,
y: 2100
}, {
x: 1700,
y: 1900
}, {
x: 1200,
y: 1700
}, {
x: 700,
y: 1500
}],
portal: {
x: 1700,
y: 700
}
};
// Initialize UI
game.initUI = function () {
// Level text
levelText = new Text2('Level ' + game.currentLevel, {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(1, 0);
LK.gui.topRight.addChild(levelText);
// Stars counter
starsText = new Text2('Stars: 0', {
size: 80,
fill: 0xFFFFFF
});
starsText.anchor.set(0.5, 0);
LK.gui.top.addChild(starsText);
// Instructions
instructionText = new Text2('Tap to flip gravity', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionText);
// Gravity direction indicator
gravityArrow = new GravityArrow();
gravityArrow.x = 120;
gravityArrow.y = 120;
LK.gui.topLeft.addChild(gravityArrow);
gravityArrow.updateDirection(game.gravityDirection);
};
// Update star counter
game.updateStarCounter = function () {
starsText.setText('Stars: ' + game.starsCollected + '/' + game.totalStarsInLevel);
};
// Load level
game.loadLevel = function (levelNumber) {
// Clean up previous level if exists
game.clearLevel();
// Set current level
game.currentLevel = levelNumber;
if (game.currentLevel > game.levelDesigns.length) {
game.currentLevel = game.levelDesigns.length;
}
// Update level text
if (levelText) {
levelText.setText('Level ' + game.currentLevel);
}
// Reset stars collected
game.starsCollected = 0;
// Get level design
var levelDesign = game.levelDesigns[game.currentLevel - 1];
// Create ball
ball = new Ball();
ball.reset(levelDesign.ballStart.x, levelDesign.ballStart.y);
game.addChild(ball);
// Create platforms
levelDesign.platforms.forEach(function (platformData) {
var platform = new Platform();
platform.x = platformData.x;
platform.y = platformData.y;
// Custom platform size if specified
if (platformData.width && platformData.height) {
platform.width = platformData.width;
platform.height = platformData.height;
platform.scaleX = platformData.width / 400;
platform.scaleY = platformData.height / 40;
}
platforms.push(platform);
game.addChild(platform);
});
// Create hazards
levelDesign.hazards.forEach(function (hazardData) {
var hazard = new Hazard();
hazard.x = hazardData.x;
hazard.y = hazardData.y;
hazards.push(hazard);
game.addChild(hazard);
});
// Create stars
game.totalStarsInLevel = levelDesign.stars.length;
levelDesign.stars.forEach(function (starData) {
var star = new Star();
star.x = starData.x;
star.y = starData.y;
stars.push(star);
game.addChild(star);
});
// Create portal
portal = new Portal();
portal.x = levelDesign.portal.x;
portal.y = levelDesign.portal.y;
game.addChild(portal);
// Update stars counter
game.updateStarCounter();
// Reset gravity
game.gravityDirection = 'down';
if (gravityArrow) {
gravityArrow.updateDirection(game.gravityDirection);
}
};
// Clear level
game.clearLevel = function () {
if (ball) {
ball.destroy();
ball = null;
}
platforms.forEach(function (platform) {
platform.destroy();
});
platforms = [];
hazards.forEach(function (hazard) {
hazard.destroy();
});
hazards = [];
stars.forEach(function (star) {
star.destroy();
});
stars = [];
if (portal) {
portal.destroy();
portal = null;
}
};
// Reset current level
game.resetLevel = function () {
game.loadLevel(game.currentLevel);
};
// Check if level can be completed
game.canCompleteLevel = function () {
return game.starsCollected === game.totalStarsInLevel;
};
// Initialize the game
game.initUI();
game.loadLevel(game.currentLevel);
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Handle input to flip gravity
game.down = function (x, y, obj) {
// Calculate direction vector from ball to touch point
var directionX = x - ball.x;
var directionY = y - ball.y;
var magnitude = Math.sqrt(directionX * directionX + directionY * directionY);
// Normalize direction vector and set ball velocity
ball.velocityX = directionX / magnitude * 10; // Adjust speed as needed
ball.velocityY = directionY / magnitude * 10; // Adjust speed as needed
// Play flip sound
LK.getSound('flip').play();
};
// Update game
game.update = function () {
if (!ball || !ball.isAlive) {
return;
}
// Check collisions with platforms
platforms.forEach(function (platform) {
platform.checkCollision(ball);
});
// Check collisions with hazards
hazards.forEach(function (hazard) {
hazard.checkCollision(ball);
});
// Check collisions with stars
stars.forEach(function (star) {
star.checkCollision(ball);
});
// Check collision with portal
if (portal) {
portal.checkCollision(ball);
}
// Check if ball is too far from play area (fallen off)
if (ball.y > 3500 || ball.y < -500 || ball.x > 2500 || ball.x < -500) {
ball.die();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
starsCollected: 0
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.5;
self.bounceStrength = -0.7;
self.isAlive = true;
self.update = function () {
if (!self.isAlive) {
return;
}
// Apply gravity
if (game.gravityDirection === 'down') {
self.velocityY += self.gravity;
} else if (game.gravityDirection === 'up') {
self.velocityY -= self.gravity;
} else if (game.gravityDirection === 'left') {
self.velocityX -= self.gravity;
} else if (game.gravityDirection === 'right') {
self.velocityX += self.gravity;
}
// Apply movement
self.x += self.velocityX;
self.y += self.velocityY;
// Friction
self.velocityX *= 0.99;
self.velocityY *= 0.99;
// Contain within game bounds
if (self.x < 40) {
self.x = 40;
self.velocityX *= self.bounceStrength;
LK.getSound('bounce').play();
} else if (self.x > 2048 - 40) {
self.x = 2048 - 40;
self.velocityX *= self.bounceStrength;
LK.getSound('bounce').play();
}
if (self.y < 40) {
self.y = 40;
self.velocityY *= self.bounceStrength;
LK.getSound('bounce').play();
} else if (self.y > 2732 - 40) {
self.y = 2732 - 40;
self.velocityY *= self.bounceStrength;
LK.getSound('bounce').play();
}
// Spin the ball based on velocity
ballGraphics.rotation += self.velocityX * 0.02;
};
self.reset = function (x, y) {
self.x = x;
self.y = y;
self.velocityX = 0;
self.velocityY = 0;
self.isAlive = true;
ballGraphics.alpha = 1;
};
self.die = function () {
if (!self.isAlive) {
return;
}
self.isAlive = false;
LK.getSound('death').play();
// Shrink and fade out
tween(ballGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
LK.setTimeout(function () {
game.resetLevel();
}, 500);
}
});
};
return self;
});
var GravityArrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrowIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
self.updateDirection = function (direction) {
// Reset rotation
arrowGraphics.rotation = 0;
if (direction === 'down') {
arrowGraphics.rotation = 0;
} else if (direction === 'up') {
arrowGraphics.rotation = Math.PI;
} else if (direction === 'left') {
arrowGraphics.rotation = Math.PI / 2;
} else if (direction === 'right') {
arrowGraphics.rotation = -Math.PI / 2;
}
};
return self;
});
var Hazard = Container.expand(function () {
var self = Container.call(this);
var hazardGraphics = self.attachAsset('hazard', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate the hazard to look dangerous
hazardGraphics.rotation = Math.PI / 4;
self.checkCollision = function (ball) {
if (!ball.isAlive) {
return;
}
var distance = Math.sqrt(Math.pow(self.x - ball.x, 2) + Math.pow(self.y - ball.y, 2));
if (distance < 70) {
ball.die();
}
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
var platformGraphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = platformGraphics.width;
self.height = platformGraphics.height;
self.checkCollision = function (ball) {
if (!ball.isAlive) {
return;
}
var ballLeft = ball.x - 40;
var ballRight = ball.x + 40;
var ballTop = ball.y - 40;
var ballBottom = ball.y + 40;
var platformLeft = self.x - self.width / 2;
var platformRight = self.x + self.width / 2;
var platformTop = self.y - self.height / 2;
var platformBottom = self.y + self.height / 2;
// Check if collision is possible
if (ballRight < platformLeft || ballLeft > platformRight || ballBottom < platformTop || ballTop > platformBottom) {
return;
}
// Calculate previous position
var prevBallLeft = ball.x - ball.velocityX - 40;
var prevBallRight = ball.x - ball.velocityX + 40;
var prevBallTop = ball.y - ball.velocityY - 40;
var prevBallBottom = ball.y - ball.velocityY + 40;
// Determine collision side
if (prevBallBottom <= platformTop && ballBottom > platformTop) {
// Collision from top
ball.y = platformTop - 39;
ball.velocityY *= ball.bounceStrength;
LK.getSound('bounce').play();
} else if (prevBallTop >= platformBottom && ballTop < platformBottom) {
// Collision from bottom
ball.y = platformBottom + 39;
ball.velocityY *= ball.bounceStrength;
LK.getSound('bounce').play();
} else if (prevBallRight <= platformLeft && ballRight > platformLeft) {
// Collision from left
ball.x = platformLeft - 39;
ball.velocityX *= ball.bounceStrength;
LK.getSound('bounce').play();
} else if (prevBallLeft >= platformRight && ballLeft < platformRight) {
// Collision from right
ball.x = platformRight + 39;
ball.velocityX *= ball.bounceStrength;
LK.getSound('bounce').play();
}
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
var portalGraphics = self.attachAsset('portal', {
anchorX: 0.5,
anchorY: 0.5
});
// Make portal spin
function spin() {
tween(portalGraphics, {
rotation: portalGraphics.rotation + Math.PI * 2
}, {
duration: 3000,
onFinish: spin
});
}
spin();
self.checkCollision = function (ball) {
if (!ball.isAlive) {
return;
}
var distance = Math.sqrt(Math.pow(self.x - ball.x, 2) + Math.pow(self.y - ball.y, 2));
if (distance < 70 && game.canCompleteLevel()) {
LK.getSound('levelComplete').play();
// Save progress
storage.currentLevel = game.currentLevel + 1;
storage.starsCollected += game.starsCollected;
ball.isAlive = false;
// Animate completion
tween(ball, {
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
// Advance to next level or win
if (game.currentLevel >= game.levelDesigns.length) {
LK.showYouWin();
} else {
game.loadLevel(game.currentLevel + 1);
storage.currentLevel = game.currentLevel; // Save progress to start from the next level
}
}
});
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
// Make star pulsate
function pulsate() {
tween(starGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(starGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: pulsate
});
}
});
}
pulsate();
self.checkCollision = function (ball) {
if (self.collected || !ball.isAlive) {
return;
}
var distance = Math.sqrt(Math.pow(self.x - ball.x, 2) + Math.pow(self.y - ball.y, 2));
if (distance < 65) {
self.collected = true;
game.starsCollected++;
// Update star counter
game.updateStarCounter();
LK.getSound('collect').play();
// Animate collection
tween(starGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.visible = false;
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2C3E50
});
/****
* Game Code
****/
// Game state
game.currentLevel = storage.currentLevel || 1;
game.gravityDirection = 'down';
game.starsCollected = 0;
game.totalStarsInLevel = 0;
// Game objects
var ball;
var platforms = [];
var hazards = [];
var stars = [];
var portal;
var gravityArrow;
// UI elements
var levelText;
var starsText;
var instructionText;
// Level designs
game.levelDesigns = [
// Level 1
{
ballStart: {
x: 300,
y: 300
},
platforms: [{
x: 300,
y: 500,
width: 400,
height: 40
}, {
x: 800,
y: 800,
width: 400,
height: 40
}, {
x: 1300,
y: 1200,
width: 400,
height: 40
}],
hazards: [{
x: 800,
y: 600
}],
stars: [{
x: 800,
y: 700
}, {
x: 1300,
y: 1000
}],
portal: {
x: 1700,
y: 1200
}
},
// Level 2
{
ballStart: {
x: 200,
y: 200
},
platforms: [{
x: 200,
y: 400,
width: 400,
height: 40
}, {
x: 600,
y: 800,
width: 400,
height: 40
}, {
x: 1000,
y: 1200,
width: 400,
height: 40
}, {
x: 1400,
y: 800,
width: 400,
height: 40
}, {
x: 1800,
y: 400,
width: 400,
height: 40
}],
hazards: [{
x: 600,
y: 600
}, {
x: 1400,
y: 600
}, {
x: 1000,
y: 1000
}],
stars: [{
x: 600,
y: 700
}, {
x: 1000,
y: 1100
}, {
x: 1400,
y: 700
}],
portal: {
x: 1800,
y: 300
}
},
// Level 3
{
ballStart: {
x: 200,
y: 2500
},
platforms: [{
x: 200,
y: 2600,
width: 400,
height: 40
}, {
x: 700,
y: 2400,
width: 400,
height: 40
}, {
x: 1200,
y: 2200,
width: 400,
height: 40
}, {
x: 1700,
y: 2000,
width: 400,
height: 40
}, {
x: 1200,
y: 1800,
width: 400,
height: 40
}, {
x: 700,
y: 1600,
width: 400,
height: 40
}, {
x: 200,
y: 1400,
width: 400,
height: 40
}, {
x: 700,
y: 1200,
width: 400,
height: 40
}, {
x: 1200,
y: 1000,
width: 400,
height: 40
}, {
x: 1700,
y: 800,
width: 400,
height: 40
}],
hazards: [{
x: 500,
y: 2400
}, {
x: 1000,
y: 2200
}, {
x: 1500,
y: 2000
}, {
x: 1000,
y: 1800
}, {
x: 500,
y: 1600
}, {
x: 500,
y: 1200
}, {
x: 1000,
y: 1000
}, {
x: 1500,
y: 800
}],
stars: [{
x: 700,
y: 2300
}, {
x: 1200,
y: 2100
}, {
x: 1700,
y: 1900
}, {
x: 1200,
y: 1700
}, {
x: 700,
y: 1500
}],
portal: {
x: 1700,
y: 700
}
}],
// Level 4
{
ballStart: {
x: 100,
y: 100
},
platforms: [{
x: 200,
y: 300,
width: 400,
height: 40
}, {
x: 600,
y: 600,
width: 400,
height: 40
}, {
x: 1000,
y: 900,
width: 400,
height: 40
}, {
x: 1400,
y: 1200,
width: 400,
height: 40
}, {
x: 1800,
y: 1500,
width: 400,
height: 40
}],
hazards: [{
x: 400,
y: 450
}, {
x: 800,
y: 750
}, {
x: 1200,
y: 1050
}, {
x: 1600,
y: 1350
}],
stars: [{
x: 300,
y: 350
}, {
x: 700,
y: 650
}, {
x: 1100,
y: 950
}, {
x: 1500,
y: 1250
}],
portal: {
x: 1900,
y: 1600
}
},
// Level 5
{
ballStart: {
x: 100,
y: 2500
},
platforms: [{
x: 200,
y: 2600,
width: 400,
height: 40
}, {
x: 700,
y: 2400,
width: 400,
height: 40
}, {
x: 1200,
y: 2200,
width: 400,
height: 40
}, {
x: 1700,
y: 2000,
width: 400,
height: 40
}, {
x: 1200,
y: 1800,
width: 400,
height: 40
}, {
x: 700,
y: 1600,
width: 400,
height: 40
}, {
x: 200,
y: 1400,
width: 400,
height: 40
}, {
x: 700,
y: 1200,
width: 400,
height: 40
}, {
x: 1200,
y: 1000,
width: 400,
height: 40
}, {
x: 1700,
y: 800,
width: 400,
height: 40
}],
hazards: [{
x: 500,
y: 2400
}, {
x: 1000,
y: 2200
}, {
x: 1500,
y: 2000
}, {
x: 1000,
y: 1800
}, {
x: 500,
y: 1600
}, {
x: 500,
y: 1200
}, {
x: 1000,
y: 1000
}, {
x: 1500,
y: 800
}],
stars: [{
x: 700,
y: 2300
}, {
x: 1200,
y: 2100
}, {
x: 1700,
y: 1900
}, {
x: 1200,
y: 1700
}, {
x: 700,
y: 1500
}],
portal: {
x: 1700,
y: 700
}
};
// Initialize UI
game.initUI = function () {
// Level text
levelText = new Text2('Level ' + game.currentLevel, {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(1, 0);
LK.gui.topRight.addChild(levelText);
// Stars counter
starsText = new Text2('Stars: 0', {
size: 80,
fill: 0xFFFFFF
});
starsText.anchor.set(0.5, 0);
LK.gui.top.addChild(starsText);
// Instructions
instructionText = new Text2('Tap to flip gravity', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(instructionText);
// Gravity direction indicator
gravityArrow = new GravityArrow();
gravityArrow.x = 120;
gravityArrow.y = 120;
LK.gui.topLeft.addChild(gravityArrow);
gravityArrow.updateDirection(game.gravityDirection);
};
// Update star counter
game.updateStarCounter = function () {
starsText.setText('Stars: ' + game.starsCollected + '/' + game.totalStarsInLevel);
};
// Load level
game.loadLevel = function (levelNumber) {
// Clean up previous level if exists
game.clearLevel();
// Set current level
game.currentLevel = levelNumber;
if (game.currentLevel > game.levelDesigns.length) {
game.currentLevel = game.levelDesigns.length;
}
// Update level text
if (levelText) {
levelText.setText('Level ' + game.currentLevel);
}
// Reset stars collected
game.starsCollected = 0;
// Get level design
var levelDesign = game.levelDesigns[game.currentLevel - 1];
// Create ball
ball = new Ball();
ball.reset(levelDesign.ballStart.x, levelDesign.ballStart.y);
game.addChild(ball);
// Create platforms
levelDesign.platforms.forEach(function (platformData) {
var platform = new Platform();
platform.x = platformData.x;
platform.y = platformData.y;
// Custom platform size if specified
if (platformData.width && platformData.height) {
platform.width = platformData.width;
platform.height = platformData.height;
platform.scaleX = platformData.width / 400;
platform.scaleY = platformData.height / 40;
}
platforms.push(platform);
game.addChild(platform);
});
// Create hazards
levelDesign.hazards.forEach(function (hazardData) {
var hazard = new Hazard();
hazard.x = hazardData.x;
hazard.y = hazardData.y;
hazards.push(hazard);
game.addChild(hazard);
});
// Create stars
game.totalStarsInLevel = levelDesign.stars.length;
levelDesign.stars.forEach(function (starData) {
var star = new Star();
star.x = starData.x;
star.y = starData.y;
stars.push(star);
game.addChild(star);
});
// Create portal
portal = new Portal();
portal.x = levelDesign.portal.x;
portal.y = levelDesign.portal.y;
game.addChild(portal);
// Update stars counter
game.updateStarCounter();
// Reset gravity
game.gravityDirection = 'down';
if (gravityArrow) {
gravityArrow.updateDirection(game.gravityDirection);
}
};
// Clear level
game.clearLevel = function () {
if (ball) {
ball.destroy();
ball = null;
}
platforms.forEach(function (platform) {
platform.destroy();
});
platforms = [];
hazards.forEach(function (hazard) {
hazard.destroy();
});
hazards = [];
stars.forEach(function (star) {
star.destroy();
});
stars = [];
if (portal) {
portal.destroy();
portal = null;
}
};
// Reset current level
game.resetLevel = function () {
game.loadLevel(game.currentLevel);
};
// Check if level can be completed
game.canCompleteLevel = function () {
return game.starsCollected === game.totalStarsInLevel;
};
// Initialize the game
game.initUI();
game.loadLevel(game.currentLevel);
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Handle input to flip gravity
game.down = function (x, y, obj) {
// Calculate direction vector from ball to touch point
var directionX = x - ball.x;
var directionY = y - ball.y;
var magnitude = Math.sqrt(directionX * directionX + directionY * directionY);
// Normalize direction vector and set ball velocity
ball.velocityX = directionX / magnitude * 10; // Adjust speed as needed
ball.velocityY = directionY / magnitude * 10; // Adjust speed as needed
// Play flip sound
LK.getSound('flip').play();
};
// Update game
game.update = function () {
if (!ball || !ball.isAlive) {
return;
}
// Check collisions with platforms
platforms.forEach(function (platform) {
platform.checkCollision(ball);
});
// Check collisions with hazards
hazards.forEach(function (hazard) {
hazard.checkCollision(ball);
});
// Check collisions with stars
stars.forEach(function (star) {
star.checkCollision(ball);
});
// Check collision with portal
if (portal) {
portal.checkCollision(ball);
}
// Check if ball is too far from play area (fallen off)
if (ball.y > 3500 || ball.y < -500 || ball.x > 2500 || ball.x < -500) {
ball.die();
}
};