/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
unlockedLevels: 1
});
/****
* Classes
****/
var DimensionSwitch = Container.expand(function () {
var self = Container.call(this);
// Create switch graphic
self.graphic = self.attachAsset('dimensionSwitch', {
anchorX: 0.5,
anchorY: 0.5
});
// Create pulsing animation
self.pulseAnimation = function () {
tween(self.graphic, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.graphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: self.pulseAnimation
});
}
});
};
// Start pulsing
self.pulseAnimation();
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
// Create goal graphic
self.graphic = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5
});
// Animate the goal
self.animateGoal = function () {
tween(self, {
rotation: Math.PI * 2
}, {
duration: 3000,
onFinish: function onFinish() {
self.rotation = 0;
self.animateGoal();
}
});
};
// Start animation
self.animateGoal();
return self;
});
var Hazard = Container.expand(function () {
var self = Container.call(this);
// Create hazard graphic
self.graphic = self.attachAsset('hazard', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var LevelButton = Container.expand(function (levelNum) {
var self = Container.call(this);
self.levelNum = levelNum;
// Create button background
self.background = self.attachAsset('levelSelectButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Create level text
self.text = new Text2(levelNum.toString(), {
size: 50,
fill: 0xFFFFFF
});
self.text.anchor.set(0.5, 0.5);
self.addChild(self.text);
// Interactive events
self.down = function (x, y, obj) {
if (storage.unlockedLevels >= self.levelNum) {
tween(self, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
}
};
self.up = function (x, y, obj) {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
onFinish: function onFinish() {
if (storage.unlockedLevels >= self.levelNum) {
storage.currentLevel = self.levelNum;
startGame();
}
}
});
};
// Update appearance based on level unlock status
self.updateAppearance = function () {
if (storage.unlockedLevels >= self.levelNum) {
self.background.alpha = 1;
self.text.alpha = 1;
} else {
self.background.alpha = 0.5;
self.text.setText("🔒");
}
};
self.updateAppearance();
return self;
});
var Platform = Container.expand(function (type) {
var self = Container.call(this);
// Platform properties
self.type = type || 'light'; // 'light' or 'shadow'
// Create platform graphic
self.graphic = self.attachAsset(self.type === 'light' ? 'lightPlatform' : 'shadowPlatform', {
anchorX: 0.5,
anchorY: 0.5
});
// Check if platform is solid in current dimension
self.isSolid = function (isDimensionLight) {
return self.type === 'light' && isDimensionLight || self.type === 'shadow' && !isDimensionLight;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Player properties
self.velocityX = 0;
self.velocityY = 0;
self.speed = 10;
self.jumpForce = -24;
self.gravity = 0.3; // Reduced gravity for slower falling
self.isGrounded = false;
self.isDimensionLight = true;
self.isDead = false;
self.isGliding = false;
self.collectedTrophies = 0;
self.totalTrophies = 0;
// Create light and shadow versions of player
self.lightForm = self.attachAsset('playerLight', {
anchorX: 0.5,
anchorY: 0.5
});
self.shadowForm = self.attachAsset('playerShadow', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
// Swap dimensions
self.swapDimension = function () {
if (self.isDead) {
return;
}
self.isDimensionLight = !self.isDimensionLight;
self.lightForm.visible = self.isDimensionLight;
self.shadowForm.visible = !self.isDimensionLight;
LK.getSound('swap').play();
// Visual effect for dimension swap
LK.effects.flashObject(self, self.isDimensionLight ? 0xFFFFFF : 0x000000, 300);
};
// Jump action
self.jump = function () {
if (self.isGrounded && !self.isDead) {
self.velocityY = self.jumpForce;
self.isGrounded = false;
LK.getSound('jump').play();
}
};
// Move left
self.moveLeft = function () {
if (!self.isDead) {
self.velocityX = -self.speed;
}
};
// Move right
self.moveRight = function () {
if (!self.isDead) {
self.velocityX = self.speed;
}
};
// Stop horizontal movement
self.stopMoving = function () {
self.velocityX = 0;
};
// Kill player
self.die = function () {
if (!self.isDead) {
self.isDead = true;
LK.getSound('death').play();
tween(self, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
// Let the game handle the reset
restartLevel();
}
});
}
};
// Physics update
self.update = function () {
if (self.isDead) {
return;
}
// Track if player is gliding
var isGliding = Math.abs(self.velocityX) > 3 && !self.isGrounded && self.velocityY > 0;
// Apply gravity with gliding effect
if (isGliding) {
// Reduced gravity when gliding
self.velocityY += self.gravity * 0.3;
// Cap fall speed while gliding
if (self.velocityY > 5) {
self.velocityY = 5;
}
} else {
// Normal gravity
self.velocityY += self.gravity;
}
// Apply velocities
self.x += self.velocityX;
self.y += self.velocityY;
// Simple friction
if (Math.abs(self.velocityX) > 0.1) {
// Less friction while gliding
self.velocityX *= isGliding ? 0.98 : 0.9;
} else {
self.velocityX = 0;
}
// Visual effect for gliding
if (isGliding && !self.isGliding) {
self.isGliding = true;
// Stretch horizontally while gliding
tween(self, {
scaleX: 1.3,
scaleY: 0.8
}, {
duration: 300
});
} else if (!isGliding && self.isGliding) {
self.isGliding = false;
// Return to normal shape
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
// Screen bounds
if (self.x < 30) {
self.x = 30;
} else if (self.x > 2048 - 30) {
self.x = 2048 - 30;
}
if (self.y > 2732) {
self.die();
}
};
return self;
});
var Trophy = Container.expand(function () {
var self = Container.call(this);
// Create trophy graphic - using goal asset with different color
self.graphic = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
// Apply gold color tint
self.graphic.tint = 0xFFD700;
// Hover animation
self.animate = function () {
tween(self, {
y: self.y - 10
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: self.y + 10
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: self.animate
});
}
});
};
// Start animation
self.animate();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game states
var STATE_MENU = 'menu';
var STATE_GAME = 'game';
var gameState = STATE_MENU;
// Game elements
var player;
var platforms = [];
var hazards = [];
var dimensionSwitches = [];
var trophies = [];
var goal;
var levelButtons = [];
var touchControls = {};
var background;
// Game logic variables
var totalLevels = 5;
var currentLevel = storage.currentLevel || 1;
var isSwapEnabled = true;
var swapCooldown = 0;
var levelComplete = false;
// Level designs
var levels = [
// Level 1: Simple introduction
{
playerStart: {
x: 300,
y: 300
},
platforms: [{
x: 300,
y: 400,
type: 'light',
width: 1
}, {
x: 600,
y: 500,
type: 'shadow',
width: 1
}, {
x: 900,
y: 600,
type: 'light',
width: 1
}, {
x: 1200,
y: 700,
type: 'shadow',
width: 1
}, {
x: 1500,
y: 800,
type: 'light',
width: 1
}],
dimensionSwitches: [{
x: 600,
y: 520
}, {
x: 1200,
y: 920
}],
hazards: [],
goal: {
x: 1500,
y: 1120
}
},
// Level 2: Platforms and hazards
{
playerStart: {
x: 200,
y: 300
},
platforms: [{
x: 200,
y: 400,
type: 'light',
width: 1
}, {
x: 500,
y: 500,
type: 'shadow',
width: 1
}, {
x: 800,
y: 600,
type: 'light',
width: 1
}, {
x: 1100,
y: 700,
type: 'shadow',
width: 1
}, {
x: 1400,
y: 800,
type: 'light',
width: 1
}, {
x: 1700,
y: 900,
type: 'shadow',
width: 1
}],
dimensionSwitches: [{
x: 500,
y: 420
}, {
x: 1100,
y: 620
}],
hazards: [{
x: 800,
y: 500
}, {
x: 1400,
y: 700
}],
goal: {
x: 1700,
y: 820
}
},
// Level 3: More complex puzzle
{
playerStart: {
x: 150,
y: 300
},
platforms: [{
x: 150,
y: 400,
type: 'light',
width: 1
}, {
x: 450,
y: 400,
type: 'shadow',
width: 1
}, {
x: 750,
y: 500,
type: 'light',
width: 1
}, {
x: 1050,
y: 500,
type: 'shadow',
width: 1
}, {
x: 1350,
y: 600,
type: 'light',
width: 1
}, {
x: 1650,
y: 600,
type: 'shadow',
width: 1
}, {
x: 1650,
y: 800,
type: 'light',
width: 1
}, {
x: 1350,
y: 800,
type: 'shadow',
width: 1
}, {
x: 1050,
y: 1000,
type: 'light',
width: 1
}, {
x: 750,
y: 1000,
type: 'shadow',
width: 1
}],
dimensionSwitches: [{
x: 450,
y: 320
}, {
x: 1050,
y: 420
}, {
x: 1650,
y: 520
}, {
x: 1050,
y: 920
}],
hazards: [{
x: 750,
y: 400
}, {
x: 1350,
y: 500
}, {
x: 1350,
y: 900
}],
goal: {
x: 750,
y: 920
}
},
// Level 4: Advanced obstacles
{
playerStart: {
x: 200,
y: 200
},
platforms: [{
x: 200,
y: 300,
type: 'light',
width: 1
}, {
x: 500,
y: 300,
type: 'shadow',
width: 1
}, {
x: 800,
y: 400,
type: 'light',
width: 1
}, {
x: 1100,
y: 400,
type: 'shadow',
width: 1
}, {
x: 1400,
y: 500,
type: 'light',
width: 1
}, {
x: 1700,
y: 500,
type: 'shadow',
width: 1
}, {
x: 1400,
y: 700,
type: 'shadow',
width: 1
}, {
x: 1100,
y: 700,
type: 'light',
width: 1
}, {
x: 800,
y: 900,
type: 'shadow',
width: 1
}, {
x: 500,
y: 900,
type: 'light',
width: 1
}, {
x: 200,
y: 1100,
type: 'shadow',
width: 1
}, {
x: 500,
y: 1100,
type: 'light',
width: 1
}],
dimensionSwitches: [{
x: 500,
y: 220
}, {
x: 1100,
y: 320
}, {
x: 1400,
y: 620
}, {
x: 800,
y: 820
}, {
x: 200,
y: 1020
}],
hazards: [{
x: 800,
y: 300
}, {
x: 1400,
y: 400
}, {
x: 1100,
y: 600
}, {
x: 500,
y: 800
}],
goal: {
x: 500,
y: 1020
}
},
// Level 5: Final challenge
{
playerStart: {
x: 150,
y: 200
},
platforms: [{
x: 150,
y: 300,
type: 'light',
width: 1
}, {
x: 450,
y: 400,
type: 'shadow',
width: 1
}, {
x: 750,
y: 500,
type: 'light',
width: 1
}, {
x: 1050,
y: 600,
type: 'shadow',
width: 1
}, {
x: 1350,
y: 700,
type: 'light',
width: 1
}, {
x: 1650,
y: 800,
type: 'shadow',
width: 1
}, {
x: 1350,
y: 1000,
type: 'shadow',
width: 1
}, {
x: 1050,
y: 1200,
type: 'light',
width: 1
}, {
x: 750,
y: 1400,
type: 'shadow',
width: 1
}, {
x: 450,
y: 1600,
type: 'light',
width: 1
}, {
x: 750,
y: 1800,
type: 'shadow',
width: 1
}, {
x: 1050,
y: 2000,
type: 'light',
width: 1
}, {
x: 1350,
y: 2200,
type: 'shadow',
width: 1
}, {
x: 1650,
y: 2400,
type: 'light',
width: 1
}],
dimensionSwitches: [{
x: 450,
y: 320
}, {
x: 1050,
y: 520
}, {
x: 1650,
y: 720
}, {
x: 1050,
y: 1120
}, {
x: 450,
y: 1520
}, {
x: 1050,
y: 1920
}, {
x: 1650,
y: 2320
}],
hazards: [{
x: 750,
y: 400
}, {
x: 1350,
y: 600
}, {
x: 1350,
y: 900
}, {
x: 750,
y: 1300
}, {
x: 750,
y: 1700
}, {
x: 1350,
y: 2100
}],
trophies: [{
x: 750,
y: 450
}, {
x: 1350,
y: 650
}, {
x: 750,
y: 1350
}, {
x: 1050,
y: 1950
}],
goal: {
x: 1650,
y: 2320
}
}];
// Initialize menu
function setupMenu() {
gameState = STATE_MENU;
clearLevel();
// Create title text
var titleText = new Text2("SHADOW SWAP", {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 400;
game.addChild(titleText);
// Create level selection text
var levelSelectText = new Text2("SELECT LEVEL", {
size: 60,
fill: 0xFFFFFF
});
levelSelectText.anchor.set(0.5, 0.5);
levelSelectText.x = 2048 / 2;
levelSelectText.y = 550;
game.addChild(levelSelectText);
// Create level selection buttons
for (var i = 0; i < totalLevels; i++) {
var button = new LevelButton(i + 1);
button.x = 2048 / 2 + (i - 2) * 250;
button.y = 700;
levelButtons.push(button);
game.addChild(button);
}
// Create instruction text
var instructionText = new Text2("CONTROLS:\n" + "- TAP LEFT/RIGHT SIDES TO MOVE\n" + "- TAP CENTER TO JUMP\n" + "- SWIPE UP TO SWAP DIMENSIONS\n" + "- MOVE HORIZONTALLY TO GLIDE\n\n" + "GOAL:\n" + "- NAVIGATE THROUGH PLATFORMS\n" + "- LIGHT PLATFORMS ARE SOLID IN LIGHT DIMENSION\n" + "- SHADOW PLATFORMS ARE SOLID IN SHADOW DIMENSION\n" + "- COLLECT ALL GOLDEN TROPHIES\n" + "- REACH THE GREEN GOAL TO COMPLETE LEVEL", {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 2048 / 2;
instructionText.y = 900;
game.addChild(instructionText);
}
// Start game with the selected level
function startGame() {
gameState = STATE_GAME;
clearLevel();
setupLevel(storage.currentLevel);
LK.playMusic('gameBgm');
}
// Clear all game elements
function clearLevel() {
// Remove all game objects
if (player) {
game.removeChild(player);
player = null;
}
platforms.forEach(function (platform) {
game.removeChild(platform);
});
platforms = [];
hazards.forEach(function (hazard) {
game.removeChild(hazard);
});
hazards = [];
dimensionSwitches.forEach(function (dimensionSwitch) {
game.removeChild(dimensionSwitch);
});
dimensionSwitches = [];
trophies.forEach(function (trophy) {
game.removeChild(trophy);
});
trophies = [];
if (goal) {
game.removeChild(goal);
goal = null;
}
levelButtons.forEach(function (button) {
game.removeChild(button);
});
levelButtons = [];
// Remove any other UI elements
for (var key in touchControls) {
if (touchControls[key]) {
game.removeChild(touchControls[key]);
}
}
touchControls = {};
// Reset game state
levelComplete = false;
isSwapEnabled = true;
swapCooldown = 0;
// Clear any existing children
while (game.children.length > 0) {
game.removeChild(game.children[0]);
}
}
// Set up a level
function setupLevel(levelNum) {
currentLevel = levelNum;
var levelData = levels[levelNum - 1] || levels[0];
// Create background
background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
background.x = 2048 / 2;
background.y = 2732 / 2;
game.addChild(background);
// Create level text
var levelText = new Text2("LEVEL " + currentLevel, {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.x = 2048 / 2;
levelText.y = 50;
game.addChild(levelText);
// Create dimension indicator
var dimensionText = new Text2("DIMENSION: LIGHT", {
size: 40,
fill: 0xFFFFFF
});
dimensionText.anchor.set(0.5, 0);
dimensionText.x = 2048 / 2;
dimensionText.y = 120;
game.addChild(dimensionText);
touchControls.dimensionText = dimensionText;
// Create trophy counter text
var trophyText = new Text2("TROPHIES: 0/0", {
size: 40,
fill: 0xFFD700
});
trophyText.anchor.set(0.5, 0);
trophyText.x = 2048 / 2;
trophyText.y = 180;
game.addChild(trophyText);
touchControls.trophyText = trophyText;
// Create player
player = new Player();
player.x = levelData.playerStart.x;
player.y = levelData.playerStart.y;
game.addChild(player);
// Create platforms
levelData.platforms.forEach(function (platformData) {
var platform = new Platform(platformData.type);
platform.x = platformData.x;
platform.y = platformData.y;
// Make all platforms 1.5x wider for easier landing
platform.graphic.scaleX = platformData.width ? platformData.width * 1.5 : 1.5;
platforms.push(platform);
game.addChild(platform);
});
// Create dimension switches
levelData.dimensionSwitches.forEach(function (switchData) {
var dimensionSwitch = new DimensionSwitch();
dimensionSwitch.x = switchData.x;
dimensionSwitch.y = switchData.y;
dimensionSwitches.push(dimensionSwitch);
game.addChild(dimensionSwitch);
});
// Create hazards (limit the number of hazards per level to make it easier)
var hazardCount = 0;
levelData.hazards.forEach(function (hazardData) {
// Only create 2/3 of the hazards to make game easier
hazardCount++;
if (hazardCount % 3 !== 0) {
// Skip every third hazard
var hazard = new Hazard();
hazard.x = hazardData.x;
hazard.y = hazardData.y;
hazards.push(hazard);
game.addChild(hazard);
}
});
// Create trophies
if (levelData.trophies) {
player.totalTrophies = levelData.trophies.length;
player.collectedTrophies = 0;
levelData.trophies.forEach(function (trophyData) {
var trophy = new Trophy();
trophy.x = trophyData.x;
trophy.y = trophyData.y;
trophy.collected = false;
trophies.push(trophy);
game.addChild(trophy);
});
// Update trophy counter
if (touchControls.trophyText) {
touchControls.trophyText.setText("TROPHIES: " + player.collectedTrophies + "/" + player.totalTrophies);
}
}
// Create goal
goal = new Goal();
goal.x = levelData.goal.x;
goal.y = levelData.goal.y;
game.addChild(goal);
// Touch controls areas (invisible)
var leftControl = LK.getAsset('lightPlatform', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
leftControl.width = 2048 / 3;
leftControl.height = 2732;
leftControl.x = 0;
leftControl.y = 0;
game.addChild(leftControl);
touchControls.left = leftControl;
var rightControl = LK.getAsset('lightPlatform', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
rightControl.width = 2048 / 3;
rightControl.height = 2732;
rightControl.x = 2048 * 2 / 3;
rightControl.y = 0;
game.addChild(rightControl);
touchControls.right = rightControl;
var centerControl = LK.getAsset('lightPlatform', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
centerControl.width = 2048 / 3;
centerControl.height = 2732;
centerControl.x = 2048 / 3;
centerControl.y = 0;
game.addChild(centerControl);
touchControls.center = centerControl;
}
// Restart current level
function restartLevel() {
clearLevel();
setupLevel(currentLevel);
}
// Complete level and move to next
function completeLevel() {
if (!levelComplete) {
// For the final level, require all trophies to be collected before completing
if (currentLevel === totalLevels && player.collectedTrophies < player.totalTrophies) {
// Show message that trophies are required
var missingTrophiesText = new Text2("COLLECT ALL TROPHIES FIRST!", {
size: 60,
fill: 0xFFD700
});
missingTrophiesText.anchor.set(0.5, 0.5);
missingTrophiesText.x = 2048 / 2;
missingTrophiesText.y = 2732 / 2;
game.addChild(missingTrophiesText);
// Flash and remove after 2 seconds
tween(missingTrophiesText, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
game.removeChild(missingTrophiesText);
}
});
return;
}
levelComplete = true;
LK.getSound('win').play();
// Update unlocked levels
if (currentLevel >= storage.unlockedLevels) {
storage.unlockedLevels = currentLevel + 1;
if (storage.unlockedLevels > totalLevels) {
storage.unlockedLevels = totalLevels;
}
}
// Show win animation
tween(player, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.showYouWin();
// Move to next level
if (currentLevel < totalLevels) {
storage.currentLevel = currentLevel + 1;
} else {
// If all levels completed, go back to menu
setupMenu();
}
}
});
}
}
// Check collisions between player and platforms
function checkPlatformCollisions() {
var wasGrounded = player.isGrounded;
player.isGrounded = false;
platforms.forEach(function (platform) {
if (platform.isSolid(player.isDimensionLight) && player.intersects(platform)) {
// Calculate collision sides
var playerBottom = player.y + player.lightForm.height / 2;
var playerTop = player.y - player.lightForm.height / 2;
var playerLeft = player.x - player.lightForm.width / 2;
var playerRight = player.x + player.lightForm.width / 2;
var platformBottom = platform.y + platform.graphic.height / 2;
var platformTop = platform.y - platform.graphic.height / 2;
var platformLeft = platform.x - platform.graphic.width / 2;
var platformRight = platform.x + platform.graphic.width / 2;
// Calculate penetration depths
var fromTop = playerBottom - platformTop;
var fromBottom = platformBottom - playerTop;
var fromLeft = playerRight - platformLeft;
var fromRight = platformRight - playerLeft;
// Find minimum penetration
var minPenetration = Math.min(fromTop, fromBottom, fromLeft, fromRight);
// Resolve collision based on minimum penetration
if (minPenetration === fromTop && player.velocityY > 0) {
player.y = platformTop - player.lightForm.height / 2;
player.velocityY = 0;
player.isGrounded = true;
} else if (minPenetration === fromBottom && player.velocityY < 0) {
player.y = platformBottom + player.lightForm.height / 2;
player.velocityY = 0;
} else if (minPenetration === fromLeft && player.velocityX > 0) {
player.x = platformLeft - player.lightForm.width / 2;
player.velocityX = 0;
} else if (minPenetration === fromRight && player.velocityX < 0) {
player.x = platformRight + player.lightForm.width / 2;
player.velocityX = 0;
}
}
});
// Landing effect
if (!wasGrounded && player.isGrounded) {
tween(player, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
}
// Check other game object interactions
function checkInteractions() {
// Check dimension switches
dimensionSwitches.forEach(function (dimensionSwitch) {
if (player.intersects(dimensionSwitch) && isSwapEnabled) {
player.swapDimension();
isSwapEnabled = false;
swapCooldown = 45; // 0.75-second cooldown at 60 FPS
// Update dimension text
if (touchControls.dimensionText) {
touchControls.dimensionText.setText("DIMENSION: " + (player.isDimensionLight ? "LIGHT" : "SHADOW"));
}
// Animate the switch
tween(dimensionSwitch, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(dimensionSwitch, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
}
});
// Check hazards
hazards.forEach(function (hazard) {
if (player.intersects(hazard)) {
player.die();
}
});
// Check trophies
trophies.forEach(function (trophy, index) {
if (!trophy.collected && player.intersects(trophy)) {
// Collect trophy
trophy.collected = true;
player.collectedTrophies++;
// Update trophy counter
if (touchControls.trophyText) {
touchControls.trophyText.setText("TROPHIES: " + player.collectedTrophies + "/" + player.totalTrophies);
}
// Trophy collection animation
tween(trophy, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
game.removeChild(trophy);
}
});
}
});
// Check goal
if (player.intersects(goal)) {
completeLevel();
}
// Swap cooldown
if (!isSwapEnabled) {
swapCooldown--;
if (swapCooldown <= 0) {
isSwapEnabled = true;
}
}
}
// Touch controls
var touchStart = {
x: 0,
y: 0
};
var isTouching = false;
var movingLeft = false;
var movingRight = false;
game.down = function (x, y, obj) {
if (gameState === STATE_GAME) {
touchStart.x = x;
touchStart.y = y;
isTouching = true;
// Check which control area was touched
if (x < 2048 / 3) {
// Left side - move left
movingLeft = true;
player.moveLeft();
} else if (x > 2048 * 2 / 3) {
// Right side - move right
movingRight = true;
player.moveRight();
} else {
// Center - jump
player.jump();
}
}
};
game.move = function (x, y, obj) {
if (gameState === STATE_GAME && isTouching) {
// Check for swipe up (dimension swap) - reduced distance required
if (y < touchStart.y - 70) {
if (isSwapEnabled) {
player.swapDimension();
isSwapEnabled = false;
swapCooldown = 45;
// Update dimension text
if (touchControls.dimensionText) {
touchControls.dimensionText.setText("DIMENSION: " + (player.isDimensionLight ? "LIGHT" : "SHADOW"));
}
}
// Reset touch to prevent multiple swipes
isTouching = false;
}
}
};
game.up = function (x, y, obj) {
if (gameState === STATE_GAME) {
isTouching = false;
// Stop horizontal movement if this was a movement control
if (movingLeft || movingRight) {
player.stopMoving();
movingLeft = false;
movingRight = false;
}
}
};
// Main game update loop
game.update = function () {
if (gameState === STATE_GAME && !levelComplete && player && !player.isDead) {
// Update player physics
player.update();
// Check collisions and interactions
checkPlatformCollisions();
checkInteractions();
// Update platform visuals based on current dimension
platforms.forEach(function (platform) {
// Make solid platforms more visible (higher alpha) in current dimension
if (platform.isSolid(player.isDimensionLight)) {
platform.graphic.alpha = 1.0;
} else {
platform.graphic.alpha = 0.5;
}
});
}
};
// Initialize the game with the menu
setupMenu(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
currentLevel: 1,
unlockedLevels: 1
});
/****
* Classes
****/
var DimensionSwitch = Container.expand(function () {
var self = Container.call(this);
// Create switch graphic
self.graphic = self.attachAsset('dimensionSwitch', {
anchorX: 0.5,
anchorY: 0.5
});
// Create pulsing animation
self.pulseAnimation = function () {
tween(self.graphic, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.graphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: self.pulseAnimation
});
}
});
};
// Start pulsing
self.pulseAnimation();
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
// Create goal graphic
self.graphic = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5
});
// Animate the goal
self.animateGoal = function () {
tween(self, {
rotation: Math.PI * 2
}, {
duration: 3000,
onFinish: function onFinish() {
self.rotation = 0;
self.animateGoal();
}
});
};
// Start animation
self.animateGoal();
return self;
});
var Hazard = Container.expand(function () {
var self = Container.call(this);
// Create hazard graphic
self.graphic = self.attachAsset('hazard', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var LevelButton = Container.expand(function (levelNum) {
var self = Container.call(this);
self.levelNum = levelNum;
// Create button background
self.background = self.attachAsset('levelSelectButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Create level text
self.text = new Text2(levelNum.toString(), {
size: 50,
fill: 0xFFFFFF
});
self.text.anchor.set(0.5, 0.5);
self.addChild(self.text);
// Interactive events
self.down = function (x, y, obj) {
if (storage.unlockedLevels >= self.levelNum) {
tween(self, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
}
};
self.up = function (x, y, obj) {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
onFinish: function onFinish() {
if (storage.unlockedLevels >= self.levelNum) {
storage.currentLevel = self.levelNum;
startGame();
}
}
});
};
// Update appearance based on level unlock status
self.updateAppearance = function () {
if (storage.unlockedLevels >= self.levelNum) {
self.background.alpha = 1;
self.text.alpha = 1;
} else {
self.background.alpha = 0.5;
self.text.setText("🔒");
}
};
self.updateAppearance();
return self;
});
var Platform = Container.expand(function (type) {
var self = Container.call(this);
// Platform properties
self.type = type || 'light'; // 'light' or 'shadow'
// Create platform graphic
self.graphic = self.attachAsset(self.type === 'light' ? 'lightPlatform' : 'shadowPlatform', {
anchorX: 0.5,
anchorY: 0.5
});
// Check if platform is solid in current dimension
self.isSolid = function (isDimensionLight) {
return self.type === 'light' && isDimensionLight || self.type === 'shadow' && !isDimensionLight;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Player properties
self.velocityX = 0;
self.velocityY = 0;
self.speed = 10;
self.jumpForce = -24;
self.gravity = 0.3; // Reduced gravity for slower falling
self.isGrounded = false;
self.isDimensionLight = true;
self.isDead = false;
self.isGliding = false;
self.collectedTrophies = 0;
self.totalTrophies = 0;
// Create light and shadow versions of player
self.lightForm = self.attachAsset('playerLight', {
anchorX: 0.5,
anchorY: 0.5
});
self.shadowForm = self.attachAsset('playerShadow', {
anchorX: 0.5,
anchorY: 0.5,
visible: false
});
// Swap dimensions
self.swapDimension = function () {
if (self.isDead) {
return;
}
self.isDimensionLight = !self.isDimensionLight;
self.lightForm.visible = self.isDimensionLight;
self.shadowForm.visible = !self.isDimensionLight;
LK.getSound('swap').play();
// Visual effect for dimension swap
LK.effects.flashObject(self, self.isDimensionLight ? 0xFFFFFF : 0x000000, 300);
};
// Jump action
self.jump = function () {
if (self.isGrounded && !self.isDead) {
self.velocityY = self.jumpForce;
self.isGrounded = false;
LK.getSound('jump').play();
}
};
// Move left
self.moveLeft = function () {
if (!self.isDead) {
self.velocityX = -self.speed;
}
};
// Move right
self.moveRight = function () {
if (!self.isDead) {
self.velocityX = self.speed;
}
};
// Stop horizontal movement
self.stopMoving = function () {
self.velocityX = 0;
};
// Kill player
self.die = function () {
if (!self.isDead) {
self.isDead = true;
LK.getSound('death').play();
tween(self, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
// Let the game handle the reset
restartLevel();
}
});
}
};
// Physics update
self.update = function () {
if (self.isDead) {
return;
}
// Track if player is gliding
var isGliding = Math.abs(self.velocityX) > 3 && !self.isGrounded && self.velocityY > 0;
// Apply gravity with gliding effect
if (isGliding) {
// Reduced gravity when gliding
self.velocityY += self.gravity * 0.3;
// Cap fall speed while gliding
if (self.velocityY > 5) {
self.velocityY = 5;
}
} else {
// Normal gravity
self.velocityY += self.gravity;
}
// Apply velocities
self.x += self.velocityX;
self.y += self.velocityY;
// Simple friction
if (Math.abs(self.velocityX) > 0.1) {
// Less friction while gliding
self.velocityX *= isGliding ? 0.98 : 0.9;
} else {
self.velocityX = 0;
}
// Visual effect for gliding
if (isGliding && !self.isGliding) {
self.isGliding = true;
// Stretch horizontally while gliding
tween(self, {
scaleX: 1.3,
scaleY: 0.8
}, {
duration: 300
});
} else if (!isGliding && self.isGliding) {
self.isGliding = false;
// Return to normal shape
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
// Screen bounds
if (self.x < 30) {
self.x = 30;
} else if (self.x > 2048 - 30) {
self.x = 2048 - 30;
}
if (self.y > 2732) {
self.die();
}
};
return self;
});
var Trophy = Container.expand(function () {
var self = Container.call(this);
// Create trophy graphic - using goal asset with different color
self.graphic = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
// Apply gold color tint
self.graphic.tint = 0xFFD700;
// Hover animation
self.animate = function () {
tween(self, {
y: self.y - 10
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: self.y + 10
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: self.animate
});
}
});
};
// Start animation
self.animate();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game states
var STATE_MENU = 'menu';
var STATE_GAME = 'game';
var gameState = STATE_MENU;
// Game elements
var player;
var platforms = [];
var hazards = [];
var dimensionSwitches = [];
var trophies = [];
var goal;
var levelButtons = [];
var touchControls = {};
var background;
// Game logic variables
var totalLevels = 5;
var currentLevel = storage.currentLevel || 1;
var isSwapEnabled = true;
var swapCooldown = 0;
var levelComplete = false;
// Level designs
var levels = [
// Level 1: Simple introduction
{
playerStart: {
x: 300,
y: 300
},
platforms: [{
x: 300,
y: 400,
type: 'light',
width: 1
}, {
x: 600,
y: 500,
type: 'shadow',
width: 1
}, {
x: 900,
y: 600,
type: 'light',
width: 1
}, {
x: 1200,
y: 700,
type: 'shadow',
width: 1
}, {
x: 1500,
y: 800,
type: 'light',
width: 1
}],
dimensionSwitches: [{
x: 600,
y: 520
}, {
x: 1200,
y: 920
}],
hazards: [],
goal: {
x: 1500,
y: 1120
}
},
// Level 2: Platforms and hazards
{
playerStart: {
x: 200,
y: 300
},
platforms: [{
x: 200,
y: 400,
type: 'light',
width: 1
}, {
x: 500,
y: 500,
type: 'shadow',
width: 1
}, {
x: 800,
y: 600,
type: 'light',
width: 1
}, {
x: 1100,
y: 700,
type: 'shadow',
width: 1
}, {
x: 1400,
y: 800,
type: 'light',
width: 1
}, {
x: 1700,
y: 900,
type: 'shadow',
width: 1
}],
dimensionSwitches: [{
x: 500,
y: 420
}, {
x: 1100,
y: 620
}],
hazards: [{
x: 800,
y: 500
}, {
x: 1400,
y: 700
}],
goal: {
x: 1700,
y: 820
}
},
// Level 3: More complex puzzle
{
playerStart: {
x: 150,
y: 300
},
platforms: [{
x: 150,
y: 400,
type: 'light',
width: 1
}, {
x: 450,
y: 400,
type: 'shadow',
width: 1
}, {
x: 750,
y: 500,
type: 'light',
width: 1
}, {
x: 1050,
y: 500,
type: 'shadow',
width: 1
}, {
x: 1350,
y: 600,
type: 'light',
width: 1
}, {
x: 1650,
y: 600,
type: 'shadow',
width: 1
}, {
x: 1650,
y: 800,
type: 'light',
width: 1
}, {
x: 1350,
y: 800,
type: 'shadow',
width: 1
}, {
x: 1050,
y: 1000,
type: 'light',
width: 1
}, {
x: 750,
y: 1000,
type: 'shadow',
width: 1
}],
dimensionSwitches: [{
x: 450,
y: 320
}, {
x: 1050,
y: 420
}, {
x: 1650,
y: 520
}, {
x: 1050,
y: 920
}],
hazards: [{
x: 750,
y: 400
}, {
x: 1350,
y: 500
}, {
x: 1350,
y: 900
}],
goal: {
x: 750,
y: 920
}
},
// Level 4: Advanced obstacles
{
playerStart: {
x: 200,
y: 200
},
platforms: [{
x: 200,
y: 300,
type: 'light',
width: 1
}, {
x: 500,
y: 300,
type: 'shadow',
width: 1
}, {
x: 800,
y: 400,
type: 'light',
width: 1
}, {
x: 1100,
y: 400,
type: 'shadow',
width: 1
}, {
x: 1400,
y: 500,
type: 'light',
width: 1
}, {
x: 1700,
y: 500,
type: 'shadow',
width: 1
}, {
x: 1400,
y: 700,
type: 'shadow',
width: 1
}, {
x: 1100,
y: 700,
type: 'light',
width: 1
}, {
x: 800,
y: 900,
type: 'shadow',
width: 1
}, {
x: 500,
y: 900,
type: 'light',
width: 1
}, {
x: 200,
y: 1100,
type: 'shadow',
width: 1
}, {
x: 500,
y: 1100,
type: 'light',
width: 1
}],
dimensionSwitches: [{
x: 500,
y: 220
}, {
x: 1100,
y: 320
}, {
x: 1400,
y: 620
}, {
x: 800,
y: 820
}, {
x: 200,
y: 1020
}],
hazards: [{
x: 800,
y: 300
}, {
x: 1400,
y: 400
}, {
x: 1100,
y: 600
}, {
x: 500,
y: 800
}],
goal: {
x: 500,
y: 1020
}
},
// Level 5: Final challenge
{
playerStart: {
x: 150,
y: 200
},
platforms: [{
x: 150,
y: 300,
type: 'light',
width: 1
}, {
x: 450,
y: 400,
type: 'shadow',
width: 1
}, {
x: 750,
y: 500,
type: 'light',
width: 1
}, {
x: 1050,
y: 600,
type: 'shadow',
width: 1
}, {
x: 1350,
y: 700,
type: 'light',
width: 1
}, {
x: 1650,
y: 800,
type: 'shadow',
width: 1
}, {
x: 1350,
y: 1000,
type: 'shadow',
width: 1
}, {
x: 1050,
y: 1200,
type: 'light',
width: 1
}, {
x: 750,
y: 1400,
type: 'shadow',
width: 1
}, {
x: 450,
y: 1600,
type: 'light',
width: 1
}, {
x: 750,
y: 1800,
type: 'shadow',
width: 1
}, {
x: 1050,
y: 2000,
type: 'light',
width: 1
}, {
x: 1350,
y: 2200,
type: 'shadow',
width: 1
}, {
x: 1650,
y: 2400,
type: 'light',
width: 1
}],
dimensionSwitches: [{
x: 450,
y: 320
}, {
x: 1050,
y: 520
}, {
x: 1650,
y: 720
}, {
x: 1050,
y: 1120
}, {
x: 450,
y: 1520
}, {
x: 1050,
y: 1920
}, {
x: 1650,
y: 2320
}],
hazards: [{
x: 750,
y: 400
}, {
x: 1350,
y: 600
}, {
x: 1350,
y: 900
}, {
x: 750,
y: 1300
}, {
x: 750,
y: 1700
}, {
x: 1350,
y: 2100
}],
trophies: [{
x: 750,
y: 450
}, {
x: 1350,
y: 650
}, {
x: 750,
y: 1350
}, {
x: 1050,
y: 1950
}],
goal: {
x: 1650,
y: 2320
}
}];
// Initialize menu
function setupMenu() {
gameState = STATE_MENU;
clearLevel();
// Create title text
var titleText = new Text2("SHADOW SWAP", {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 400;
game.addChild(titleText);
// Create level selection text
var levelSelectText = new Text2("SELECT LEVEL", {
size: 60,
fill: 0xFFFFFF
});
levelSelectText.anchor.set(0.5, 0.5);
levelSelectText.x = 2048 / 2;
levelSelectText.y = 550;
game.addChild(levelSelectText);
// Create level selection buttons
for (var i = 0; i < totalLevels; i++) {
var button = new LevelButton(i + 1);
button.x = 2048 / 2 + (i - 2) * 250;
button.y = 700;
levelButtons.push(button);
game.addChild(button);
}
// Create instruction text
var instructionText = new Text2("CONTROLS:\n" + "- TAP LEFT/RIGHT SIDES TO MOVE\n" + "- TAP CENTER TO JUMP\n" + "- SWIPE UP TO SWAP DIMENSIONS\n" + "- MOVE HORIZONTALLY TO GLIDE\n\n" + "GOAL:\n" + "- NAVIGATE THROUGH PLATFORMS\n" + "- LIGHT PLATFORMS ARE SOLID IN LIGHT DIMENSION\n" + "- SHADOW PLATFORMS ARE SOLID IN SHADOW DIMENSION\n" + "- COLLECT ALL GOLDEN TROPHIES\n" + "- REACH THE GREEN GOAL TO COMPLETE LEVEL", {
size: 40,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
instructionText.x = 2048 / 2;
instructionText.y = 900;
game.addChild(instructionText);
}
// Start game with the selected level
function startGame() {
gameState = STATE_GAME;
clearLevel();
setupLevel(storage.currentLevel);
LK.playMusic('gameBgm');
}
// Clear all game elements
function clearLevel() {
// Remove all game objects
if (player) {
game.removeChild(player);
player = null;
}
platforms.forEach(function (platform) {
game.removeChild(platform);
});
platforms = [];
hazards.forEach(function (hazard) {
game.removeChild(hazard);
});
hazards = [];
dimensionSwitches.forEach(function (dimensionSwitch) {
game.removeChild(dimensionSwitch);
});
dimensionSwitches = [];
trophies.forEach(function (trophy) {
game.removeChild(trophy);
});
trophies = [];
if (goal) {
game.removeChild(goal);
goal = null;
}
levelButtons.forEach(function (button) {
game.removeChild(button);
});
levelButtons = [];
// Remove any other UI elements
for (var key in touchControls) {
if (touchControls[key]) {
game.removeChild(touchControls[key]);
}
}
touchControls = {};
// Reset game state
levelComplete = false;
isSwapEnabled = true;
swapCooldown = 0;
// Clear any existing children
while (game.children.length > 0) {
game.removeChild(game.children[0]);
}
}
// Set up a level
function setupLevel(levelNum) {
currentLevel = levelNum;
var levelData = levels[levelNum - 1] || levels[0];
// Create background
background = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
background.x = 2048 / 2;
background.y = 2732 / 2;
game.addChild(background);
// Create level text
var levelText = new Text2("LEVEL " + currentLevel, {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.x = 2048 / 2;
levelText.y = 50;
game.addChild(levelText);
// Create dimension indicator
var dimensionText = new Text2("DIMENSION: LIGHT", {
size: 40,
fill: 0xFFFFFF
});
dimensionText.anchor.set(0.5, 0);
dimensionText.x = 2048 / 2;
dimensionText.y = 120;
game.addChild(dimensionText);
touchControls.dimensionText = dimensionText;
// Create trophy counter text
var trophyText = new Text2("TROPHIES: 0/0", {
size: 40,
fill: 0xFFD700
});
trophyText.anchor.set(0.5, 0);
trophyText.x = 2048 / 2;
trophyText.y = 180;
game.addChild(trophyText);
touchControls.trophyText = trophyText;
// Create player
player = new Player();
player.x = levelData.playerStart.x;
player.y = levelData.playerStart.y;
game.addChild(player);
// Create platforms
levelData.platforms.forEach(function (platformData) {
var platform = new Platform(platformData.type);
platform.x = platformData.x;
platform.y = platformData.y;
// Make all platforms 1.5x wider for easier landing
platform.graphic.scaleX = platformData.width ? platformData.width * 1.5 : 1.5;
platforms.push(platform);
game.addChild(platform);
});
// Create dimension switches
levelData.dimensionSwitches.forEach(function (switchData) {
var dimensionSwitch = new DimensionSwitch();
dimensionSwitch.x = switchData.x;
dimensionSwitch.y = switchData.y;
dimensionSwitches.push(dimensionSwitch);
game.addChild(dimensionSwitch);
});
// Create hazards (limit the number of hazards per level to make it easier)
var hazardCount = 0;
levelData.hazards.forEach(function (hazardData) {
// Only create 2/3 of the hazards to make game easier
hazardCount++;
if (hazardCount % 3 !== 0) {
// Skip every third hazard
var hazard = new Hazard();
hazard.x = hazardData.x;
hazard.y = hazardData.y;
hazards.push(hazard);
game.addChild(hazard);
}
});
// Create trophies
if (levelData.trophies) {
player.totalTrophies = levelData.trophies.length;
player.collectedTrophies = 0;
levelData.trophies.forEach(function (trophyData) {
var trophy = new Trophy();
trophy.x = trophyData.x;
trophy.y = trophyData.y;
trophy.collected = false;
trophies.push(trophy);
game.addChild(trophy);
});
// Update trophy counter
if (touchControls.trophyText) {
touchControls.trophyText.setText("TROPHIES: " + player.collectedTrophies + "/" + player.totalTrophies);
}
}
// Create goal
goal = new Goal();
goal.x = levelData.goal.x;
goal.y = levelData.goal.y;
game.addChild(goal);
// Touch controls areas (invisible)
var leftControl = LK.getAsset('lightPlatform', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
leftControl.width = 2048 / 3;
leftControl.height = 2732;
leftControl.x = 0;
leftControl.y = 0;
game.addChild(leftControl);
touchControls.left = leftControl;
var rightControl = LK.getAsset('lightPlatform', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
rightControl.width = 2048 / 3;
rightControl.height = 2732;
rightControl.x = 2048 * 2 / 3;
rightControl.y = 0;
game.addChild(rightControl);
touchControls.right = rightControl;
var centerControl = LK.getAsset('lightPlatform', {
anchorX: 0,
anchorY: 0,
alpha: 0
});
centerControl.width = 2048 / 3;
centerControl.height = 2732;
centerControl.x = 2048 / 3;
centerControl.y = 0;
game.addChild(centerControl);
touchControls.center = centerControl;
}
// Restart current level
function restartLevel() {
clearLevel();
setupLevel(currentLevel);
}
// Complete level and move to next
function completeLevel() {
if (!levelComplete) {
// For the final level, require all trophies to be collected before completing
if (currentLevel === totalLevels && player.collectedTrophies < player.totalTrophies) {
// Show message that trophies are required
var missingTrophiesText = new Text2("COLLECT ALL TROPHIES FIRST!", {
size: 60,
fill: 0xFFD700
});
missingTrophiesText.anchor.set(0.5, 0.5);
missingTrophiesText.x = 2048 / 2;
missingTrophiesText.y = 2732 / 2;
game.addChild(missingTrophiesText);
// Flash and remove after 2 seconds
tween(missingTrophiesText, {
alpha: 0
}, {
duration: 2000,
onFinish: function onFinish() {
game.removeChild(missingTrophiesText);
}
});
return;
}
levelComplete = true;
LK.getSound('win').play();
// Update unlocked levels
if (currentLevel >= storage.unlockedLevels) {
storage.unlockedLevels = currentLevel + 1;
if (storage.unlockedLevels > totalLevels) {
storage.unlockedLevels = totalLevels;
}
}
// Show win animation
tween(player, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.showYouWin();
// Move to next level
if (currentLevel < totalLevels) {
storage.currentLevel = currentLevel + 1;
} else {
// If all levels completed, go back to menu
setupMenu();
}
}
});
}
}
// Check collisions between player and platforms
function checkPlatformCollisions() {
var wasGrounded = player.isGrounded;
player.isGrounded = false;
platforms.forEach(function (platform) {
if (platform.isSolid(player.isDimensionLight) && player.intersects(platform)) {
// Calculate collision sides
var playerBottom = player.y + player.lightForm.height / 2;
var playerTop = player.y - player.lightForm.height / 2;
var playerLeft = player.x - player.lightForm.width / 2;
var playerRight = player.x + player.lightForm.width / 2;
var platformBottom = platform.y + platform.graphic.height / 2;
var platformTop = platform.y - platform.graphic.height / 2;
var platformLeft = platform.x - platform.graphic.width / 2;
var platformRight = platform.x + platform.graphic.width / 2;
// Calculate penetration depths
var fromTop = playerBottom - platformTop;
var fromBottom = platformBottom - playerTop;
var fromLeft = playerRight - platformLeft;
var fromRight = platformRight - playerLeft;
// Find minimum penetration
var minPenetration = Math.min(fromTop, fromBottom, fromLeft, fromRight);
// Resolve collision based on minimum penetration
if (minPenetration === fromTop && player.velocityY > 0) {
player.y = platformTop - player.lightForm.height / 2;
player.velocityY = 0;
player.isGrounded = true;
} else if (minPenetration === fromBottom && player.velocityY < 0) {
player.y = platformBottom + player.lightForm.height / 2;
player.velocityY = 0;
} else if (minPenetration === fromLeft && player.velocityX > 0) {
player.x = platformLeft - player.lightForm.width / 2;
player.velocityX = 0;
} else if (minPenetration === fromRight && player.velocityX < 0) {
player.x = platformRight + player.lightForm.width / 2;
player.velocityX = 0;
}
}
});
// Landing effect
if (!wasGrounded && player.isGrounded) {
tween(player, {
scaleX: 1.2,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
}
// Check other game object interactions
function checkInteractions() {
// Check dimension switches
dimensionSwitches.forEach(function (dimensionSwitch) {
if (player.intersects(dimensionSwitch) && isSwapEnabled) {
player.swapDimension();
isSwapEnabled = false;
swapCooldown = 45; // 0.75-second cooldown at 60 FPS
// Update dimension text
if (touchControls.dimensionText) {
touchControls.dimensionText.setText("DIMENSION: " + (player.isDimensionLight ? "LIGHT" : "SHADOW"));
}
// Animate the switch
tween(dimensionSwitch, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(dimensionSwitch, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
}
});
// Check hazards
hazards.forEach(function (hazard) {
if (player.intersects(hazard)) {
player.die();
}
});
// Check trophies
trophies.forEach(function (trophy, index) {
if (!trophy.collected && player.intersects(trophy)) {
// Collect trophy
trophy.collected = true;
player.collectedTrophies++;
// Update trophy counter
if (touchControls.trophyText) {
touchControls.trophyText.setText("TROPHIES: " + player.collectedTrophies + "/" + player.totalTrophies);
}
// Trophy collection animation
tween(trophy, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
game.removeChild(trophy);
}
});
}
});
// Check goal
if (player.intersects(goal)) {
completeLevel();
}
// Swap cooldown
if (!isSwapEnabled) {
swapCooldown--;
if (swapCooldown <= 0) {
isSwapEnabled = true;
}
}
}
// Touch controls
var touchStart = {
x: 0,
y: 0
};
var isTouching = false;
var movingLeft = false;
var movingRight = false;
game.down = function (x, y, obj) {
if (gameState === STATE_GAME) {
touchStart.x = x;
touchStart.y = y;
isTouching = true;
// Check which control area was touched
if (x < 2048 / 3) {
// Left side - move left
movingLeft = true;
player.moveLeft();
} else if (x > 2048 * 2 / 3) {
// Right side - move right
movingRight = true;
player.moveRight();
} else {
// Center - jump
player.jump();
}
}
};
game.move = function (x, y, obj) {
if (gameState === STATE_GAME && isTouching) {
// Check for swipe up (dimension swap) - reduced distance required
if (y < touchStart.y - 70) {
if (isSwapEnabled) {
player.swapDimension();
isSwapEnabled = false;
swapCooldown = 45;
// Update dimension text
if (touchControls.dimensionText) {
touchControls.dimensionText.setText("DIMENSION: " + (player.isDimensionLight ? "LIGHT" : "SHADOW"));
}
}
// Reset touch to prevent multiple swipes
isTouching = false;
}
}
};
game.up = function (x, y, obj) {
if (gameState === STATE_GAME) {
isTouching = false;
// Stop horizontal movement if this was a movement control
if (movingLeft || movingRight) {
player.stopMoving();
movingLeft = false;
movingRight = false;
}
}
};
// Main game update loop
game.update = function () {
if (gameState === STATE_GAME && !levelComplete && player && !player.isDead) {
// Update player physics
player.update();
// Check collisions and interactions
checkPlatformCollisions();
checkInteractions();
// Update platform visuals based on current dimension
platforms.forEach(function (platform) {
// Make solid platforms more visible (higher alpha) in current dimension
if (platform.isSolid(player.isDimensionLight)) {
platform.graphic.alpha = 1.0;
} else {
platform.graphic.alpha = 0.5;
}
});
}
};
// Initialize the game with the menu
setupMenu();
Switch. In-Game asset. 2d. High contrast. No shadows
Trophy. In-Game asset. 2d. High contrast. No shadows
Hazard. In-Game asset. 2d. High contrast. No shadows
A shadow ninja. In-Game asset. 2d. High contrast. No shadows
A shadow ninja. In-Game asset. 2d. High contrast. shadow
Shadow platform. In-Game asset. 2d. High contrast. No shadows