/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
collectedKeys: 0,
lives: 3
});
/****
* Classes
****/
var Door = Container.expand(function () {
var self = Container.call(this);
var doorGraphics = self.attachAsset('door', {
anchorX: 0.5,
anchorY: 0.5
});
self.isLocked = true;
self.unlock = function () {
self.isLocked = false;
tween(doorGraphics, {
alpha: 0.3
}, {
duration: 500
});
LK.getSound('unlockDoor').play();
};
return self;
});
var Explorer = Container.expand(function () {
var self = Container.call(this);
var explorerGraphics = self.attachAsset('explorer', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.isMoving = false;
self.direction = null;
self.targetX = 0;
self.targetY = 0;
self.hasKey = false;
self.moveToward = function (x, y) {
self.isMoving = true;
self.targetX = x;
self.targetY = y;
// Calculate direction
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
// Determine primary direction based on larger delta
if (Math.abs(dx) > Math.abs(dy)) {
self.direction = dx > 0 ? 'right' : 'left';
} else {
self.direction = dy > 0 ? 'down' : 'up';
}
};
self.stopMoving = function () {
self.isMoving = false;
self.direction = null;
};
self.update = function () {
if (self.isMoving) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.stopMoving();
} else {
var ratio = self.speed / distance;
self.x += dx * ratio;
self.y += dy * ratio;
}
}
};
self.collectKey = function () {
self.hasKey = true;
tween(explorerGraphics, {
tint: 0xFFD700
}, {
duration: 300,
onFinish: function onFinish() {
tween(explorerGraphics, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
};
self.useKey = function () {
self.hasKey = false;
storage.collectedKeys++;
};
self.takeDamage = function () {
LK.effects.flashObject(self, 0xFF0000, 500);
storage.lives--;
LK.getSound('damage').play();
if (storage.lives <= 0) {
LK.showGameOver();
}
};
return self;
});
var FallingRock = Container.expand(function () {
var self = Container.call(this);
var rockGraphics = self.attachAsset('rock', {
anchorX: 0.5,
anchorY: 0.5
});
self.isTriggered = false;
self.isFalling = false;
self.initialY = 0;
self.fallSpeed = 0;
self.resetTimer = 0;
self.resetDelay = 180; // 3 seconds
self.trigger = function () {
if (!self.isTriggered && !self.isFalling) {
self.isTriggered = true;
self.fallSpeed = 2; // Start with slow fall
// Shake before falling
tween(rockGraphics, {
x: 5
}, {
duration: 100,
onFinish: function onFinish() {
tween(rockGraphics, {
x: -5
}, {
duration: 100,
onFinish: function onFinish() {
tween(rockGraphics, {
x: 0
}, {
duration: 100,
onFinish: function onFinish() {
self.isFalling = true;
self.isTriggered = false;
}
});
}
});
}
});
}
};
self.reset = function () {
self.y = self.initialY;
self.isFalling = false;
self.fallSpeed = 0;
self.resetTimer = 0;
};
self.update = function () {
if (self.isFalling) {
self.fallSpeed += 0.2; // Acceleration
self.y += self.fallSpeed;
// Check if rock has fallen out of view
if (self.y > 2832) {
self.isFalling = false;
self.resetTimer = 0;
}
} else if (self.y !== self.initialY) {
// Reset timer
self.resetTimer++;
if (self.resetTimer >= self.resetDelay) {
self.reset();
}
}
};
return self;
});
var Key = Container.expand(function () {
var self = Container.call(this);
var keyGraphics = self.attachAsset('key', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Float animation
keyGraphics.y = Math.sin(LK.ticks / 20) * 5;
// Rotate slowly
keyGraphics.rotation += 0.01;
};
return self;
});
var River = Container.expand(function () {
var self = Container.call(this);
var riverGraphics = self.attachAsset('river', {
anchorX: 0.5,
anchorY: 0.5
});
self.hasBridge = false;
self.addBridge = function () {
if (!self.hasBridge) {
var bridge = self.attachAsset('bridge', {
anchorX: 0.5,
anchorY: 0.5
});
self.hasBridge = true;
return bridge;
}
};
self.update = function () {
// Flowing water animation
riverGraphics.tint = 0x1E88E5 + Math.floor(Math.sin(LK.ticks / 20) * 20) * 0x000101;
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
self.isActive = false;
self.timer = 0;
self.cycleTime = 120; // 2 seconds at 60fps
self.update = function () {
self.timer++;
if (self.timer >= self.cycleTime) {
self.timer = 0;
self.isActive = !self.isActive;
if (self.isActive) {
tween(spikeGraphics, {
scaleY: 1
}, {
duration: 300
});
} else {
tween(spikeGraphics, {
scaleY: 0.2
}, {
duration: 300
});
}
}
};
// Initialize in retracted state
spikeGraphics.scaleY = 0.2;
return self;
});
var Treasure = Container.expand(function () {
var self = Container.call(this);
var treasureGraphics = self.attachAsset('treasure', {
anchorX: 0.5,
anchorY: 0.5
});
self.isCollected = false;
self.collect = function () {
if (!self.isCollected) {
self.isCollected = true;
tween(treasureGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
onFinish: function onFinish() {
tween(treasureGraphics, {
scaleX: 0,
scaleY: 0
}, {
duration: 300
});
}
});
LK.setScore(LK.getScore() + 100);
LK.getSound('collectTreasure').play();
// Check if all treasures are collected
var allCollected = true;
for (var i = 0; i < treasures.length; i++) {
if (!treasures[i].isCollected) {
allCollected = false;
break;
}
}
if (allCollected) {
// Level complete
storage.level++;
LK.showYouWin();
}
}
};
self.update = function () {
if (!self.isCollected) {
// Shine animation
var glowIntensity = (Math.sin(LK.ticks / 30) + 1) / 2;
treasureGraphics.alpha = 0.7 + glowIntensity * 0.3;
}
};
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x607D8B // Cave-like dark blue-gray
});
/****
* Game Code
****/
// Game variables
var tileSize = 100;
var gridWidth = 20;
var gridHeight = 27;
var explorer;
var walls = [];
var keys = [];
var doors = [];
var treasures = [];
var spikes = [];
var fallingRocks = [];
var rivers = [];
var bridges = [];
var levelGrid = [];
var gameStarted = false;
// UI Elements
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
var levelTxt = new Text2('Level: ' + storage.level, {
size: 60,
fill: 0xFFFFFF
});
var keysTxt = new Text2('Keys: ' + storage.collectedKeys, {
size: 60,
fill: 0xFFD700
});
var livesTxt = new Text2('Lives: ' + storage.lives, {
size: 60,
fill: 0xFF5252
});
// Initialize game
function initializeGame() {
// Clear previous game elements
resetGameElements();
// Generate level based on current level
generateLevel(storage.level);
// Initialize explorer
createExplorer();
// Setup UI
setupUI();
// Start game
gameStarted = true;
// Play background music
LK.playMusic('caveMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
}
function resetGameElements() {
// Remove all existing game elements
for (var i = 0; i < walls.length; i++) {
walls[i].destroy();
}
walls = [];
for (var i = 0; i < keys.length; i++) {
keys[i].destroy();
}
keys = [];
for (var i = 0; i < doors.length; i++) {
doors[i].destroy();
}
doors = [];
for (var i = 0; i < treasures.length; i++) {
treasures[i].destroy();
}
treasures = [];
for (var i = 0; i < spikes.length; i++) {
spikes[i].destroy();
}
spikes = [];
for (var i = 0; i < fallingRocks.length; i++) {
fallingRocks[i].destroy();
}
fallingRocks = [];
for (var i = 0; i < rivers.length; i++) {
rivers[i].destroy();
}
rivers = [];
for (var i = 0; i < bridges.length; i++) {
bridges[i].destroy();
}
bridges = [];
levelGrid = [];
if (explorer) {
explorer.destroy();
explorer = null;
}
}
function createExplorer() {
explorer = new Explorer();
game.addChild(explorer);
// Position explorer at start position
for (var y = 0; y < gridHeight; y++) {
for (var x = 0; x < gridWidth; x++) {
if (levelGrid[y][x] === 'S') {
explorer.x = x * tileSize + tileSize / 2;
explorer.y = y * tileSize + tileSize / 2;
break;
}
}
}
}
function setupUI() {
// Setup score text
scoreTxt.setText(LK.getScore());
scoreTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreTxt);
// Setup level text
levelTxt.setText('Level: ' + storage.level);
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 80;
LK.gui.top.addChild(levelTxt);
// Setup keys text
keysTxt.setText('Keys: ' + storage.collectedKeys);
keysTxt.anchor.set(0, 0);
keysTxt.y = 80;
LK.gui.topRight.addChild(keysTxt);
// Setup lives text
livesTxt.setText('Lives: ' + storage.lives);
livesTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(livesTxt);
}
function generateLevel(level) {
// Define different level layouts
var layouts = [];
// Level 1: Simple tutorial level
layouts[1] = ["WWWWWWWWWWWWWWWWWWWW", "W W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W W W", "W W W WWWWWWWWWW W W", "W W W W W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W W W", "W WWWWWWWWWWWWWWWW W", "W W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W W W W", "W W W WWWWWWWWW W W W", "W W W W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W TS W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
// Level 2: Introducing spikes
layouts[2] = ["WWWWWWWWWWWWWWWWWWWW", "W W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W W W", "W W W WWWWWWWWWW W W", "W W W W W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W X W W W", "W WWWWWWWWWWWWWWWW W", "W X X W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W X W W W", "W W W WWWWWWWWW W W W", "W W W X W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W TS W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
// Level 3: Introducing falling rocks
layouts[3] = ["WWWWWWWWWWWWWWWWWWWW", "W W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W F W W", "W W W WWWWWWWWWW W W", "W W W W F W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W F X W W W", "W WWWWWWWWWWWWWWWW W", "W X F X W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W X F W W W", "W W W WWWWWWWWW W W W", "W W W F X W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W TS W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
// Level 4: Introducing rivers
layouts[4] = ["WWWWWWWWWWWWWWWWWWWW", "WS W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W F W W", "W W W WWWWWWWWWW W W", "W W W W F W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W F X W W W", "WRRRRRRRRRRRRRRRRR W", "W X F X W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W X F W W W", "W W W WWWWWWWWW W W W", "W W W F X W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W T W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
// Get layout for current level or use level 4 for any higher levels
var currentLayout = layouts[Math.min(level, 4)];
// Parse level layout
for (var y = 0; y < currentLayout.length; y++) {
levelGrid[y] = [];
for (var x = 0; x < currentLayout[y].length; x++) {
var cellType = currentLayout[y][x];
levelGrid[y][x] = cellType;
var xPos = x * tileSize + tileSize / 2;
var yPos = y * tileSize + tileSize / 2;
switch (cellType) {
case 'W':
// Wall
var wall = new Wall();
wall.x = xPos;
wall.y = yPos;
game.addChild(wall);
walls.push(wall);
break;
case 'K':
// Key
var key = new Key();
key.x = xPos;
key.y = yPos;
game.addChild(key);
keys.push(key);
break;
case 'D':
// Door
var door = new Door();
door.x = xPos;
door.y = yPos;
game.addChild(door);
doors.push(door);
break;
case 'T':
// Treasure
var treasure = new Treasure();
treasure.x = xPos;
treasure.y = yPos;
game.addChild(treasure);
treasures.push(treasure);
break;
case 'X':
// Spike trap
var spike = new Spike();
spike.x = xPos;
spike.y = yPos;
game.addChild(spike);
spikes.push(spike);
break;
case 'F':
// Falling rock
var rock = new FallingRock();
rock.x = xPos;
rock.y = yPos;
rock.initialY = yPos;
game.addChild(rock);
fallingRocks.push(rock);
break;
case 'R':
// River
var river = new River();
river.x = xPos;
river.y = yPos;
game.addChild(river);
rivers.push(river);
break;
}
}
}
}
function getGridPosition(x, y) {
var gridX = Math.floor(x / tileSize);
var gridY = Math.floor(y / tileSize);
if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
return {
x: gridX,
y: gridY
};
}
return null;
}
function isWalkable(gridX, gridY) {
if (gridX < 0 || gridX >= gridWidth || gridY < 0 || gridY >= gridHeight) {
return false;
}
var cellType = levelGrid[gridY][gridX];
// Check if cell is walkable
if (cellType === 'W') {
return false;
}
// Check if door is locked
if (cellType === 'D') {
var door = getDoorAt(gridX, gridY);
return door && !door.isLocked;
}
// Check if river has bridge
if (cellType === 'R') {
var river = getRiverAt(gridX, gridY);
return river && river.hasBridge;
}
return true;
}
function getDoorAt(gridX, gridY) {
for (var i = 0; i < doors.length; i++) {
var doorPos = getGridPosition(doors[i].x, doors[i].y);
if (doorPos && doorPos.x === gridX && doorPos.y === gridY) {
return doors[i];
}
}
return null;
}
function getRiverAt(gridX, gridY) {
for (var i = 0; i < rivers.length; i++) {
var riverPos = getGridPosition(rivers[i].x, rivers[i].y);
if (riverPos && riverPos.x === gridX && riverPos.y === gridY) {
return rivers[i];
}
}
return null;
}
// Update UI
function updateUI() {
scoreTxt.setText(LK.getScore());
levelTxt.setText('Level: ' + storage.level);
keysTxt.setText('Keys: ' + storage.collectedKeys);
livesTxt.setText('Lives: ' + storage.lives);
}
// Check for collisions
function checkCollisions() {
if (!explorer) {
return;
}
var explorerPos = getGridPosition(explorer.x, explorer.y);
if (!explorerPos) {
return;
}
var cellType = levelGrid[explorerPos.y][explorerPos.x];
// Check key collision
if (cellType === 'K') {
for (var i = 0; i < keys.length; i++) {
var keyPos = getGridPosition(keys[i].x, keys[i].y);
if (keyPos && keyPos.x === explorerPos.x && keyPos.y === explorerPos.y) {
explorer.collectKey();
keys[i].destroy();
keys.splice(i, 1);
levelGrid[explorerPos.y][explorerPos.x] = ' ';
LK.getSound('collect').play();
break;
}
}
}
// Check door collision
if (cellType === 'D') {
var door = getDoorAt(explorerPos.x, explorerPos.y);
if (door && door.isLocked && explorer.hasKey) {
door.unlock();
explorer.useKey();
keysTxt.setText('Keys: ' + storage.collectedKeys);
}
}
// Check treasure collision
if (cellType === 'T') {
for (var i = 0; i < treasures.length; i++) {
var treasurePos = getGridPosition(treasures[i].x, treasures[i].y);
if (treasurePos && treasurePos.x === explorerPos.x && treasurePos.y === explorerPos.y && !treasures[i].isCollected) {
treasures[i].collect();
break;
}
}
}
// Check spike collision
for (var i = 0; i < spikes.length; i++) {
if (spikes[i].isActive && explorer.intersects(spikes[i])) {
explorer.takeDamage();
// Move player to a safe position
var safeX = explorer.x;
var safeY = explorer.y - tileSize;
if (isWalkable(Math.floor(safeX / tileSize), Math.floor(safeY / tileSize))) {
explorer.x = safeX;
explorer.y = safeY;
}
}
}
// Check falling rock collision
for (var i = 0; i < fallingRocks.length; i++) {
var rock = fallingRocks[i];
// Trigger rock if explorer is underneath
var rockPos = getGridPosition(rock.x, rock.y);
if (rockPos && explorerPos.x === rockPos.x && explorerPos.y > rockPos.y && !rock.isTriggered && !rock.isFalling) {
rock.trigger();
}
// Check if rock hits explorer
if (rock.isFalling && explorer.intersects(rock)) {
explorer.takeDamage();
rock.reset();
}
}
// Check river collision (player falls in if there's no bridge)
for (var i = 0; i < rivers.length; i++) {
var river = rivers[i];
var riverPos = getGridPosition(river.x, river.y);
if (riverPos && explorerPos.x === riverPos.x && explorerPos.y === riverPos.y && !river.hasBridge) {
explorer.takeDamage();
// Move player to a safe position
var safeX = explorer.x;
var safeY = explorer.y - tileSize;
if (isWalkable(Math.floor(safeX / tileSize), Math.floor(safeY / tileSize))) {
explorer.x = safeX;
explorer.y = safeY;
}
}
}
}
function moveExplorer(targetX, targetY) {
if (!explorer || explorer.isMoving) {
return;
}
var currentGridPos = getGridPosition(explorer.x, explorer.y);
var targetGridPos = getGridPosition(targetX, targetY);
if (!currentGridPos || !targetGridPos) {
return;
}
// Only allow movement to adjacent cells
var dx = Math.abs(targetGridPos.x - currentGridPos.x);
var dy = Math.abs(targetGridPos.y - currentGridPos.y);
if (dx === 1 && dy === 0 || dx === 0 && dy === 1) {
if (isWalkable(targetGridPos.x, targetGridPos.y)) {
var cellCenterX = targetGridPos.x * tileSize + tileSize / 2;
var cellCenterY = targetGridPos.y * tileSize + tileSize / 2;
explorer.moveToward(cellCenterX, cellCenterY);
}
}
}
// Handle touch/mouse events
function handleGameInput(x, y, obj) {
if (!gameStarted) {
return;
}
moveExplorer(x, y);
}
// Start game when page loads
initializeGame();
// Handle touch/mouse events
game.down = handleGameInput;
// Game update loop
game.update = function () {
if (!gameStarted) {
return;
}
// Update UI
updateUI();
// Check collisions
checkCollisions();
// Add bridge to first river after collecting all keys (for level 4)
if (storage.level >= 4 && storage.collectedKeys >= 1) {
for (var i = 0; i < rivers.length; i++) {
if (!rivers[i].hasBridge) {
rivers[i].addBridge();
break;
}
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,715 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1", {
+ level: 1,
+ collectedKeys: 0,
+ lives: 3
+});
+
+/****
+* Classes
+****/
+var Door = Container.expand(function () {
+ var self = Container.call(this);
+ var doorGraphics = self.attachAsset('door', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.isLocked = true;
+ self.unlock = function () {
+ self.isLocked = false;
+ tween(doorGraphics, {
+ alpha: 0.3
+ }, {
+ duration: 500
+ });
+ LK.getSound('unlockDoor').play();
+ };
+ return self;
+});
+var Explorer = Container.expand(function () {
+ var self = Container.call(this);
+ var explorerGraphics = self.attachAsset('explorer', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.speed = 10;
+ self.isMoving = false;
+ self.direction = null;
+ self.targetX = 0;
+ self.targetY = 0;
+ self.hasKey = false;
+ self.moveToward = function (x, y) {
+ self.isMoving = true;
+ self.targetX = x;
+ self.targetY = y;
+ // Calculate direction
+ var dx = self.targetX - self.x;
+ var dy = self.targetY - self.y;
+ // Determine primary direction based on larger delta
+ if (Math.abs(dx) > Math.abs(dy)) {
+ self.direction = dx > 0 ? 'right' : 'left';
+ } else {
+ self.direction = dy > 0 ? 'down' : 'up';
+ }
+ };
+ self.stopMoving = function () {
+ self.isMoving = false;
+ self.direction = null;
+ };
+ self.update = function () {
+ if (self.isMoving) {
+ var dx = self.targetX - self.x;
+ var dy = self.targetY - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < self.speed) {
+ self.x = self.targetX;
+ self.y = self.targetY;
+ self.stopMoving();
+ } else {
+ var ratio = self.speed / distance;
+ self.x += dx * ratio;
+ self.y += dy * ratio;
+ }
+ }
+ };
+ self.collectKey = function () {
+ self.hasKey = true;
+ tween(explorerGraphics, {
+ tint: 0xFFD700
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ tween(explorerGraphics, {
+ tint: 0xFFFFFF
+ }, {
+ duration: 300
+ });
+ }
+ });
+ };
+ self.useKey = function () {
+ self.hasKey = false;
+ storage.collectedKeys++;
+ };
+ self.takeDamage = function () {
+ LK.effects.flashObject(self, 0xFF0000, 500);
+ storage.lives--;
+ LK.getSound('damage').play();
+ if (storage.lives <= 0) {
+ LK.showGameOver();
+ }
+ };
+ return self;
+});
+var FallingRock = Container.expand(function () {
+ var self = Container.call(this);
+ var rockGraphics = self.attachAsset('rock', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.isTriggered = false;
+ self.isFalling = false;
+ self.initialY = 0;
+ self.fallSpeed = 0;
+ self.resetTimer = 0;
+ self.resetDelay = 180; // 3 seconds
+ self.trigger = function () {
+ if (!self.isTriggered && !self.isFalling) {
+ self.isTriggered = true;
+ self.fallSpeed = 2; // Start with slow fall
+ // Shake before falling
+ tween(rockGraphics, {
+ x: 5
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ tween(rockGraphics, {
+ x: -5
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ tween(rockGraphics, {
+ x: 0
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ self.isFalling = true;
+ self.isTriggered = false;
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ };
+ self.reset = function () {
+ self.y = self.initialY;
+ self.isFalling = false;
+ self.fallSpeed = 0;
+ self.resetTimer = 0;
+ };
+ self.update = function () {
+ if (self.isFalling) {
+ self.fallSpeed += 0.2; // Acceleration
+ self.y += self.fallSpeed;
+ // Check if rock has fallen out of view
+ if (self.y > 2832) {
+ self.isFalling = false;
+ self.resetTimer = 0;
+ }
+ } else if (self.y !== self.initialY) {
+ // Reset timer
+ self.resetTimer++;
+ if (self.resetTimer >= self.resetDelay) {
+ self.reset();
+ }
+ }
+ };
+ return self;
+});
+var Key = Container.expand(function () {
+ var self = Container.call(this);
+ var keyGraphics = self.attachAsset('key', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.update = function () {
+ // Float animation
+ keyGraphics.y = Math.sin(LK.ticks / 20) * 5;
+ // Rotate slowly
+ keyGraphics.rotation += 0.01;
+ };
+ return self;
+});
+var River = Container.expand(function () {
+ var self = Container.call(this);
+ var riverGraphics = self.attachAsset('river', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.hasBridge = false;
+ self.addBridge = function () {
+ if (!self.hasBridge) {
+ var bridge = self.attachAsset('bridge', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.hasBridge = true;
+ return bridge;
+ }
+ };
+ self.update = function () {
+ // Flowing water animation
+ riverGraphics.tint = 0x1E88E5 + Math.floor(Math.sin(LK.ticks / 20) * 20) * 0x000101;
+ };
+ return self;
+});
+var Spike = Container.expand(function () {
+ var self = Container.call(this);
+ var spikeGraphics = self.attachAsset('spike', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.isActive = false;
+ self.timer = 0;
+ self.cycleTime = 120; // 2 seconds at 60fps
+ self.update = function () {
+ self.timer++;
+ if (self.timer >= self.cycleTime) {
+ self.timer = 0;
+ self.isActive = !self.isActive;
+ if (self.isActive) {
+ tween(spikeGraphics, {
+ scaleY: 1
+ }, {
+ duration: 300
+ });
+ } else {
+ tween(spikeGraphics, {
+ scaleY: 0.2
+ }, {
+ duration: 300
+ });
+ }
+ }
+ };
+ // Initialize in retracted state
+ spikeGraphics.scaleY = 0.2;
+ return self;
+});
+var Treasure = Container.expand(function () {
+ var self = Container.call(this);
+ var treasureGraphics = self.attachAsset('treasure', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.isCollected = false;
+ self.collect = function () {
+ if (!self.isCollected) {
+ self.isCollected = true;
+ tween(treasureGraphics, {
+ scaleX: 1.3,
+ scaleY: 1.3
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ tween(treasureGraphics, {
+ scaleX: 0,
+ scaleY: 0
+ }, {
+ duration: 300
+ });
+ }
+ });
+ LK.setScore(LK.getScore() + 100);
+ LK.getSound('collectTreasure').play();
+ // Check if all treasures are collected
+ var allCollected = true;
+ for (var i = 0; i < treasures.length; i++) {
+ if (!treasures[i].isCollected) {
+ allCollected = false;
+ break;
+ }
+ }
+ if (allCollected) {
+ // Level complete
+ storage.level++;
+ LK.showYouWin();
+ }
+ }
+ };
+ self.update = function () {
+ if (!self.isCollected) {
+ // Shine animation
+ var glowIntensity = (Math.sin(LK.ticks / 30) + 1) / 2;
+ treasureGraphics.alpha = 0.7 + glowIntensity * 0.3;
+ }
+ };
+ return self;
+});
+var Wall = Container.expand(function () {
+ var self = Container.call(this);
+ var wallGraphics = self.attachAsset('wall', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x607D8B // Cave-like dark blue-gray
+});
+
+/****
+* Game Code
+****/
+// Game variables
+var tileSize = 100;
+var gridWidth = 20;
+var gridHeight = 27;
+var explorer;
+var walls = [];
+var keys = [];
+var doors = [];
+var treasures = [];
+var spikes = [];
+var fallingRocks = [];
+var rivers = [];
+var bridges = [];
+var levelGrid = [];
+var gameStarted = false;
+// UI Elements
+var scoreTxt = new Text2('0', {
+ size: 80,
+ fill: 0xFFFFFF
+});
+var levelTxt = new Text2('Level: ' + storage.level, {
+ size: 60,
+ fill: 0xFFFFFF
+});
+var keysTxt = new Text2('Keys: ' + storage.collectedKeys, {
+ size: 60,
+ fill: 0xFFD700
+});
+var livesTxt = new Text2('Lives: ' + storage.lives, {
+ size: 60,
+ fill: 0xFF5252
+});
+// Initialize game
+function initializeGame() {
+ // Clear previous game elements
+ resetGameElements();
+ // Generate level based on current level
+ generateLevel(storage.level);
+ // Initialize explorer
+ createExplorer();
+ // Setup UI
+ setupUI();
+ // Start game
+ gameStarted = true;
+ // Play background music
+ LK.playMusic('caveMusic', {
+ fade: {
+ start: 0,
+ end: 0.3,
+ duration: 1000
+ }
+ });
+}
+function resetGameElements() {
+ // Remove all existing game elements
+ for (var i = 0; i < walls.length; i++) {
+ walls[i].destroy();
+ }
+ walls = [];
+ for (var i = 0; i < keys.length; i++) {
+ keys[i].destroy();
+ }
+ keys = [];
+ for (var i = 0; i < doors.length; i++) {
+ doors[i].destroy();
+ }
+ doors = [];
+ for (var i = 0; i < treasures.length; i++) {
+ treasures[i].destroy();
+ }
+ treasures = [];
+ for (var i = 0; i < spikes.length; i++) {
+ spikes[i].destroy();
+ }
+ spikes = [];
+ for (var i = 0; i < fallingRocks.length; i++) {
+ fallingRocks[i].destroy();
+ }
+ fallingRocks = [];
+ for (var i = 0; i < rivers.length; i++) {
+ rivers[i].destroy();
+ }
+ rivers = [];
+ for (var i = 0; i < bridges.length; i++) {
+ bridges[i].destroy();
+ }
+ bridges = [];
+ levelGrid = [];
+ if (explorer) {
+ explorer.destroy();
+ explorer = null;
+ }
+}
+function createExplorer() {
+ explorer = new Explorer();
+ game.addChild(explorer);
+ // Position explorer at start position
+ for (var y = 0; y < gridHeight; y++) {
+ for (var x = 0; x < gridWidth; x++) {
+ if (levelGrid[y][x] === 'S') {
+ explorer.x = x * tileSize + tileSize / 2;
+ explorer.y = y * tileSize + tileSize / 2;
+ break;
+ }
+ }
+ }
+}
+function setupUI() {
+ // Setup score text
+ scoreTxt.setText(LK.getScore());
+ scoreTxt.anchor.set(1, 0);
+ LK.gui.topRight.addChild(scoreTxt);
+ // Setup level text
+ levelTxt.setText('Level: ' + storage.level);
+ levelTxt.anchor.set(0.5, 0);
+ levelTxt.y = 80;
+ LK.gui.top.addChild(levelTxt);
+ // Setup keys text
+ keysTxt.setText('Keys: ' + storage.collectedKeys);
+ keysTxt.anchor.set(0, 0);
+ keysTxt.y = 80;
+ LK.gui.topRight.addChild(keysTxt);
+ // Setup lives text
+ livesTxt.setText('Lives: ' + storage.lives);
+ livesTxt.anchor.set(0, 0);
+ LK.gui.topRight.addChild(livesTxt);
+}
+function generateLevel(level) {
+ // Define different level layouts
+ var layouts = [];
+ // Level 1: Simple tutorial level
+ layouts[1] = ["WWWWWWWWWWWWWWWWWWWW", "W W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W W W", "W W W WWWWWWWWWW W W", "W W W W W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W W W", "W WWWWWWWWWWWWWWWW W", "W W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W W W W", "W W W WWWWWWWWW W W W", "W W W W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W TS W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
+ // Level 2: Introducing spikes
+ layouts[2] = ["WWWWWWWWWWWWWWWWWWWW", "W W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W W W", "W W W WWWWWWWWWW W W", "W W W W W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W X W W W", "W WWWWWWWWWWWWWWWW W", "W X X W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W X W W W", "W W W WWWWWWWWW W W W", "W W W X W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W TS W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
+ // Level 3: Introducing falling rocks
+ layouts[3] = ["WWWWWWWWWWWWWWWWWWWW", "W W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W F W W", "W W W WWWWWWWWWW W W", "W W W W F W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W F X W W W", "W WWWWWWWWWWWWWWWW W", "W X F X W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W X F W W W", "W W W WWWWWWWWW W W W", "W W W F X W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W TS W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
+ // Level 4: Introducing rivers
+ layouts[4] = ["WWWWWWWWWWWWWWWWWWWW", "WS W W", "W WWWWWW W WWWWWWW W", "W W K W W", "W W WWWWWWWWWWWW W W", "W W W F W W", "W W W WWWWWWWWWW W W", "W W W W F W W W", "W W W W WWWWWW W W W", "W W W W W W W W W", "W W W W W WW W W W W", "W W W W W W W W", "W WWWWW WWWWWWWW W W", "W F X W W W", "WRRRRRRRRRRRRRRRRR W", "W X F X W", "W WWWWWWWW WWWWWWW W", "W W W W W", "W W WWWW WWWWWWW W W", "W W W X F W W W", "W W W WWWWWWWWW W W W", "W W W F X W W W W", "W W WWWWWWWWW W W W W", "W W DW W W W W", "W WWWWWWWWWWW W W T W", "W W W", "WWWWWWWWWWWWWWWWWWWWW"];
+ // Get layout for current level or use level 4 for any higher levels
+ var currentLayout = layouts[Math.min(level, 4)];
+ // Parse level layout
+ for (var y = 0; y < currentLayout.length; y++) {
+ levelGrid[y] = [];
+ for (var x = 0; x < currentLayout[y].length; x++) {
+ var cellType = currentLayout[y][x];
+ levelGrid[y][x] = cellType;
+ var xPos = x * tileSize + tileSize / 2;
+ var yPos = y * tileSize + tileSize / 2;
+ switch (cellType) {
+ case 'W':
+ // Wall
+ var wall = new Wall();
+ wall.x = xPos;
+ wall.y = yPos;
+ game.addChild(wall);
+ walls.push(wall);
+ break;
+ case 'K':
+ // Key
+ var key = new Key();
+ key.x = xPos;
+ key.y = yPos;
+ game.addChild(key);
+ keys.push(key);
+ break;
+ case 'D':
+ // Door
+ var door = new Door();
+ door.x = xPos;
+ door.y = yPos;
+ game.addChild(door);
+ doors.push(door);
+ break;
+ case 'T':
+ // Treasure
+ var treasure = new Treasure();
+ treasure.x = xPos;
+ treasure.y = yPos;
+ game.addChild(treasure);
+ treasures.push(treasure);
+ break;
+ case 'X':
+ // Spike trap
+ var spike = new Spike();
+ spike.x = xPos;
+ spike.y = yPos;
+ game.addChild(spike);
+ spikes.push(spike);
+ break;
+ case 'F':
+ // Falling rock
+ var rock = new FallingRock();
+ rock.x = xPos;
+ rock.y = yPos;
+ rock.initialY = yPos;
+ game.addChild(rock);
+ fallingRocks.push(rock);
+ break;
+ case 'R':
+ // River
+ var river = new River();
+ river.x = xPos;
+ river.y = yPos;
+ game.addChild(river);
+ rivers.push(river);
+ break;
+ }
+ }
+ }
+}
+function getGridPosition(x, y) {
+ var gridX = Math.floor(x / tileSize);
+ var gridY = Math.floor(y / tileSize);
+ if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
+ return {
+ x: gridX,
+ y: gridY
+ };
+ }
+ return null;
+}
+function isWalkable(gridX, gridY) {
+ if (gridX < 0 || gridX >= gridWidth || gridY < 0 || gridY >= gridHeight) {
+ return false;
+ }
+ var cellType = levelGrid[gridY][gridX];
+ // Check if cell is walkable
+ if (cellType === 'W') {
+ return false;
+ }
+ // Check if door is locked
+ if (cellType === 'D') {
+ var door = getDoorAt(gridX, gridY);
+ return door && !door.isLocked;
+ }
+ // Check if river has bridge
+ if (cellType === 'R') {
+ var river = getRiverAt(gridX, gridY);
+ return river && river.hasBridge;
+ }
+ return true;
+}
+function getDoorAt(gridX, gridY) {
+ for (var i = 0; i < doors.length; i++) {
+ var doorPos = getGridPosition(doors[i].x, doors[i].y);
+ if (doorPos && doorPos.x === gridX && doorPos.y === gridY) {
+ return doors[i];
+ }
+ }
+ return null;
+}
+function getRiverAt(gridX, gridY) {
+ for (var i = 0; i < rivers.length; i++) {
+ var riverPos = getGridPosition(rivers[i].x, rivers[i].y);
+ if (riverPos && riverPos.x === gridX && riverPos.y === gridY) {
+ return rivers[i];
+ }
+ }
+ return null;
+}
+// Update UI
+function updateUI() {
+ scoreTxt.setText(LK.getScore());
+ levelTxt.setText('Level: ' + storage.level);
+ keysTxt.setText('Keys: ' + storage.collectedKeys);
+ livesTxt.setText('Lives: ' + storage.lives);
+}
+// Check for collisions
+function checkCollisions() {
+ if (!explorer) {
+ return;
+ }
+ var explorerPos = getGridPosition(explorer.x, explorer.y);
+ if (!explorerPos) {
+ return;
+ }
+ var cellType = levelGrid[explorerPos.y][explorerPos.x];
+ // Check key collision
+ if (cellType === 'K') {
+ for (var i = 0; i < keys.length; i++) {
+ var keyPos = getGridPosition(keys[i].x, keys[i].y);
+ if (keyPos && keyPos.x === explorerPos.x && keyPos.y === explorerPos.y) {
+ explorer.collectKey();
+ keys[i].destroy();
+ keys.splice(i, 1);
+ levelGrid[explorerPos.y][explorerPos.x] = ' ';
+ LK.getSound('collect').play();
+ break;
+ }
+ }
+ }
+ // Check door collision
+ if (cellType === 'D') {
+ var door = getDoorAt(explorerPos.x, explorerPos.y);
+ if (door && door.isLocked && explorer.hasKey) {
+ door.unlock();
+ explorer.useKey();
+ keysTxt.setText('Keys: ' + storage.collectedKeys);
+ }
+ }
+ // Check treasure collision
+ if (cellType === 'T') {
+ for (var i = 0; i < treasures.length; i++) {
+ var treasurePos = getGridPosition(treasures[i].x, treasures[i].y);
+ if (treasurePos && treasurePos.x === explorerPos.x && treasurePos.y === explorerPos.y && !treasures[i].isCollected) {
+ treasures[i].collect();
+ break;
+ }
+ }
+ }
+ // Check spike collision
+ for (var i = 0; i < spikes.length; i++) {
+ if (spikes[i].isActive && explorer.intersects(spikes[i])) {
+ explorer.takeDamage();
+ // Move player to a safe position
+ var safeX = explorer.x;
+ var safeY = explorer.y - tileSize;
+ if (isWalkable(Math.floor(safeX / tileSize), Math.floor(safeY / tileSize))) {
+ explorer.x = safeX;
+ explorer.y = safeY;
+ }
+ }
+ }
+ // Check falling rock collision
+ for (var i = 0; i < fallingRocks.length; i++) {
+ var rock = fallingRocks[i];
+ // Trigger rock if explorer is underneath
+ var rockPos = getGridPosition(rock.x, rock.y);
+ if (rockPos && explorerPos.x === rockPos.x && explorerPos.y > rockPos.y && !rock.isTriggered && !rock.isFalling) {
+ rock.trigger();
+ }
+ // Check if rock hits explorer
+ if (rock.isFalling && explorer.intersects(rock)) {
+ explorer.takeDamage();
+ rock.reset();
+ }
+ }
+ // Check river collision (player falls in if there's no bridge)
+ for (var i = 0; i < rivers.length; i++) {
+ var river = rivers[i];
+ var riverPos = getGridPosition(river.x, river.y);
+ if (riverPos && explorerPos.x === riverPos.x && explorerPos.y === riverPos.y && !river.hasBridge) {
+ explorer.takeDamage();
+ // Move player to a safe position
+ var safeX = explorer.x;
+ var safeY = explorer.y - tileSize;
+ if (isWalkable(Math.floor(safeX / tileSize), Math.floor(safeY / tileSize))) {
+ explorer.x = safeX;
+ explorer.y = safeY;
+ }
+ }
+ }
+}
+function moveExplorer(targetX, targetY) {
+ if (!explorer || explorer.isMoving) {
+ return;
+ }
+ var currentGridPos = getGridPosition(explorer.x, explorer.y);
+ var targetGridPos = getGridPosition(targetX, targetY);
+ if (!currentGridPos || !targetGridPos) {
+ return;
+ }
+ // Only allow movement to adjacent cells
+ var dx = Math.abs(targetGridPos.x - currentGridPos.x);
+ var dy = Math.abs(targetGridPos.y - currentGridPos.y);
+ if (dx === 1 && dy === 0 || dx === 0 && dy === 1) {
+ if (isWalkable(targetGridPos.x, targetGridPos.y)) {
+ var cellCenterX = targetGridPos.x * tileSize + tileSize / 2;
+ var cellCenterY = targetGridPos.y * tileSize + tileSize / 2;
+ explorer.moveToward(cellCenterX, cellCenterY);
+ }
+ }
+}
+// Handle touch/mouse events
+function handleGameInput(x, y, obj) {
+ if (!gameStarted) {
+ return;
+ }
+ moveExplorer(x, y);
+}
+// Start game when page loads
+initializeGame();
+// Handle touch/mouse events
+game.down = handleGameInput;
+// Game update loop
+game.update = function () {
+ if (!gameStarted) {
+ return;
+ }
+ // Update UI
+ updateUI();
+ // Check collisions
+ checkCollisions();
+ // Add bridge to first river after collecting all keys (for level 4)
+ if (storage.level >= 4 && storage.collectedKeys >= 1) {
+ for (var i = 0; i < rivers.length; i++) {
+ if (!rivers[i].hasBridge) {
+ rivers[i].addBridge();
+ break;
+ }
+ }
+ }
+};
\ No newline at end of file
small rock. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a magic crackers boom effect. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
create a glod color key. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows