/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Fog tile (covers unseen areas)
var FogTile = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('fog', {
anchorX: 0.5,
anchorY: 0.5
});
self.alpha = 0.92;
return self;
});
// MapTile: floor, wall, exit, item
var MapTile = Container.expand(function () {
var self = Container.call(this);
self.type = 'floor'; // default
self.asset = null;
self.xIndex = 0;
self.yIndex = 0;
self.hasItem = false;
self.itemAsset = null;
self.isExit = false;
self.setType = function (type) {
self.type = type;
if (self.asset) {
self.removeChild(self.asset);
}
if (type === 'floor') {
self.asset = self.attachAsset('floor', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'wall') {
self.asset = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'exit') {
self.asset = self.attachAsset('exit', {
anchorX: 0.5,
anchorY: 0.5
});
self.isExit = true;
}
};
self.placeItem = function () {
self.hasItem = true;
if (!self.itemAsset) {
self.itemAsset = self.attachAsset('item', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
self.removeItem = function () {
self.hasItem = false;
if (self.itemAsset) {
self.removeChild(self.itemAsset);
self.itemAsset = null;
}
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.xIndex = 0;
self.yIndex = 0;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Map generation constants
var MAP_WIDTH = 20;
var MAP_HEIGHT = 15;
var TILE_SIZE = 160;
var MAP_OFFSET_X = (2048 - MAP_WIDTH * TILE_SIZE) / 2;
var MAP_OFFSET_Y = 200;
// Camera variables
var camera = {
x: 0,
y: 0,
targetX: 0,
targetY: 0
};
var SCREEN_CENTER_X = 2048 / 2;
var SCREEN_CENTER_Y = 2732 / 2;
// Generate random map
function generateRandomMap() {
var map = [];
// Initialize with walls
for (var y = 0; y < MAP_HEIGHT; y++) {
map[y] = [];
for (var x = 0; x < MAP_WIDTH; x++) {
map[y][x] = 1; // wall
}
}
// Create random rooms and corridors
for (var y = 1; y < MAP_HEIGHT - 1; y += 2) {
for (var x = 1; x < MAP_WIDTH - 1; x += 2) {
map[y][x] = 0; // floor
// Random corridors
if (Math.random() < 0.6 && x + 2 < MAP_WIDTH - 1) {
map[y][x + 1] = 0; // horizontal corridor
}
if (Math.random() < 0.6 && y + 2 < MAP_HEIGHT - 1) {
map[y + 1][x] = 0; // vertical corridor
}
}
}
// Place exit
var exitX = MAP_WIDTH - 2;
var exitY = MAP_HEIGHT - 2;
map[exitY][exitX] = 2; // exit
// Place 5 items randomly on floor tiles
var itemsPlaced = 0;
while (itemsPlaced < 5) {
var randX = Math.floor(Math.random() * (MAP_WIDTH - 2)) + 1;
var randY = Math.floor(Math.random() * (MAP_HEIGHT - 2)) + 1;
if (map[randY][randX] === 0 && !(randX === 1 && randY === 1)) {
map[randY][randX] = 3; // item
itemsPlaced++;
}
}
return map;
}
var mapData = generateRandomMap();
// --- GLOBALS ---
var mapTiles = []; // 2D array of MapTile
var fogTiles = []; // 2D array of FogTile
var player = null;
var playerStart = {
x: 1,
y: 1
};
var exitPos = {
x: 7,
y: 9
};
var itemsTotal = 0;
var itemsCollected = 0;
var visionRadius = 1.5; // tiles
var moveLock = false; // prevent double moves
var arrowButtons = {};
var lastPlayerPos = {
x: 1,
y: 1
};
// --- GUI ---
var itemsTxt = new Text2('Items: 0/0', {
size: 90,
fill: "#fff"
});
itemsTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(itemsTxt);
// --- MAP GENERATION ---
for (var y = 0; y < MAP_HEIGHT; y++) {
mapTiles[y] = [];
fogTiles[y] = [];
for (var x = 0; x < MAP_WIDTH; x++) {
var tile = new MapTile();
tile.xIndex = x;
tile.yIndex = y;
var type = mapData[y][x];
if (type === 1) {
tile.setType('wall');
} else if (type === 2) {
tile.setType('exit');
exitPos.x = x;
exitPos.y = y;
} else {
tile.setType('floor');
}
if (type === 3) {
tile.placeItem();
itemsTotal++;
}
tile.x = MAP_OFFSET_X + x * TILE_SIZE + TILE_SIZE / 2;
tile.y = MAP_OFFSET_Y + y * TILE_SIZE + TILE_SIZE / 2;
game.addChild(tile);
mapTiles[y][x] = tile;
// Fog
var fog = new FogTile();
fog.x = tile.x;
fog.y = tile.y;
game.addChild(fog);
fogTiles[y][x] = fog;
}
}
itemsTxt.setText('Items: 0/' + itemsTotal);
// --- PLAYER ---
player = new Player();
player.xIndex = playerStart.x;
player.yIndex = playerStart.y;
player.x = MAP_OFFSET_X + player.xIndex * TILE_SIZE + TILE_SIZE / 2;
player.y = MAP_OFFSET_Y + player.yIndex * TILE_SIZE + TILE_SIZE / 2;
game.addChild(player);
// --- FOG OF WAR ---
function updateFog() {
for (var y = 0; y < MAP_HEIGHT; y++) {
for (var x = 0; x < MAP_WIDTH; x++) {
var dx = x - player.xIndex;
var dy = y - player.yIndex;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= visionRadius) {
// Reveal
fogTiles[y][x].alpha = 0;
} else if (dist <= visionRadius + 1) {
// Faded memory
fogTiles[y][x].alpha = 0.5;
} else {
// Full fog
fogTiles[y][x].alpha = 0.92;
}
}
}
}
updateFog();
// --- CAMERA SYSTEM ---
function updateCamera() {
// Calculate target camera position to center player on screen
var playerWorldX = MAP_OFFSET_X + player.xIndex * TILE_SIZE + TILE_SIZE / 2;
var playerWorldY = MAP_OFFSET_Y + player.yIndex * TILE_SIZE + TILE_SIZE / 2;
camera.targetX = SCREEN_CENTER_X - playerWorldX;
camera.targetY = SCREEN_CENTER_Y - playerWorldY;
// Smooth camera movement
camera.x += (camera.targetX - camera.x) * 0.15;
camera.y += (camera.targetY - camera.y) * 0.15;
// Apply camera offset to game container
game.x = camera.x;
game.y = camera.y;
}
// --- MOVEMENT LOGIC ---
function canMoveTo(x, y) {
if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) return false;
var tile = mapTiles[y][x];
if (tile.type === 'wall') return false;
return true;
}
function movePlayer(dx, dy) {
if (moveLock) return;
var nx = player.xIndex + dx;
var ny = player.yIndex + dy;
if (!canMoveTo(nx, ny)) {
// Bump animation
var bumpX = player.x + dx * 20;
var bumpY = player.y + dy * 20;
moveLock = true;
tween(player, {
x: bumpX,
y: bumpY
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(player, {
x: MAP_OFFSET_X + player.xIndex * TILE_SIZE + TILE_SIZE / 2,
y: MAP_OFFSET_Y + player.yIndex * TILE_SIZE + TILE_SIZE / 2
}, {
duration: 80,
onFinish: function onFinish() {
moveLock = false;
}
});
}
});
return;
}
lastPlayerPos.x = player.xIndex;
lastPlayerPos.y = player.yIndex;
player.xIndex = nx;
player.yIndex = ny;
moveLock = true;
tween(player, {
x: MAP_OFFSET_X + nx * TILE_SIZE + TILE_SIZE / 2,
y: MAP_OFFSET_Y + ny * TILE_SIZE + TILE_SIZE / 2
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: function onFinish() {
moveLock = false;
afterMove();
}
});
updateFog();
updateCamera();
}
function afterMove() {
// Check for item
var tile = mapTiles[player.yIndex][player.xIndex];
if (tile.hasItem) {
tile.removeItem();
itemsCollected++;
itemsTxt.setText('Items: ' + itemsCollected + '/' + itemsTotal);
LK.effects.flashObject(player, 0xffe066, 400);
}
// Check for exit
if (tile.isExit) {
if (itemsCollected >= itemsTotal) {
LK.showYouWin();
} else {
// Flash exit red if not all items collected
LK.effects.flashObject(tile, 0xff0000, 400);
}
}
}
// --- ARROW BUTTONS ---
// Create arrow buttons in GUI layer to follow player
function createGUIArrowButton(id, x, y, rotation, moveFn) {
var btn = LK.getAsset(id, {
anchorX: 0.5,
anchorY: 0.5
});
btn.x = x;
btn.y = y;
btn.scaleX = 0.8;
btn.scaleY = 0.8;
btn.rotation = rotation;
btn.alpha = 0.9;
btn.interactive = true;
btn.down = function () {
moveFn();
};
LK.gui.bottom.addChild(btn);
return btn;
}
// Place arrow buttons at bottom of GUI, arranged in a D-pad layout - moved higher and smaller
var btnSpacing = 160; // spacing between buttons (reduced)
arrowButtons.up = createGUIArrowButton('arrowUp', 0, -btnSpacing - 250, 0, function () {
movePlayer(0, -1);
});
arrowButtons.down = createGUIArrowButton('arrowDown', 0, btnSpacing - 250, Math.PI, function () {
movePlayer(0, 1);
});
arrowButtons.left = createGUIArrowButton('arrowLeft', -btnSpacing, -250, -Math.PI / 2, function () {
movePlayer(-1, 0);
});
arrowButtons.right = createGUIArrowButton('arrowRight', btnSpacing, -250, Math.PI / 2, function () {
movePlayer(1, 0);
});
// --- TOUCH DRAG MOVEMENT (OPTIONAL) ---
// For MVP, only arrow buttons. (No swipe/drag movement.)
// --- GAME UPDATE ---
game.update = function () {
// Update camera smoothly each frame
updateCamera();
};
// --- GAME OVER HANDLING ---
// Handled by LK.showYouWin()
// --- INITIAL FOG UPDATE ---
updateFog();
// --- INITIAL CAMERA SETUP ---
updateCamera(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Fog tile (covers unseen areas)
var FogTile = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('fog', {
anchorX: 0.5,
anchorY: 0.5
});
self.alpha = 0.92;
return self;
});
// MapTile: floor, wall, exit, item
var MapTile = Container.expand(function () {
var self = Container.call(this);
self.type = 'floor'; // default
self.asset = null;
self.xIndex = 0;
self.yIndex = 0;
self.hasItem = false;
self.itemAsset = null;
self.isExit = false;
self.setType = function (type) {
self.type = type;
if (self.asset) {
self.removeChild(self.asset);
}
if (type === 'floor') {
self.asset = self.attachAsset('floor', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'wall') {
self.asset = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'exit') {
self.asset = self.attachAsset('exit', {
anchorX: 0.5,
anchorY: 0.5
});
self.isExit = true;
}
};
self.placeItem = function () {
self.hasItem = true;
if (!self.itemAsset) {
self.itemAsset = self.attachAsset('item', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
self.removeItem = function () {
self.hasItem = false;
if (self.itemAsset) {
self.removeChild(self.itemAsset);
self.itemAsset = null;
}
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.xIndex = 0;
self.yIndex = 0;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Map generation constants
var MAP_WIDTH = 20;
var MAP_HEIGHT = 15;
var TILE_SIZE = 160;
var MAP_OFFSET_X = (2048 - MAP_WIDTH * TILE_SIZE) / 2;
var MAP_OFFSET_Y = 200;
// Camera variables
var camera = {
x: 0,
y: 0,
targetX: 0,
targetY: 0
};
var SCREEN_CENTER_X = 2048 / 2;
var SCREEN_CENTER_Y = 2732 / 2;
// Generate random map
function generateRandomMap() {
var map = [];
// Initialize with walls
for (var y = 0; y < MAP_HEIGHT; y++) {
map[y] = [];
for (var x = 0; x < MAP_WIDTH; x++) {
map[y][x] = 1; // wall
}
}
// Create random rooms and corridors
for (var y = 1; y < MAP_HEIGHT - 1; y += 2) {
for (var x = 1; x < MAP_WIDTH - 1; x += 2) {
map[y][x] = 0; // floor
// Random corridors
if (Math.random() < 0.6 && x + 2 < MAP_WIDTH - 1) {
map[y][x + 1] = 0; // horizontal corridor
}
if (Math.random() < 0.6 && y + 2 < MAP_HEIGHT - 1) {
map[y + 1][x] = 0; // vertical corridor
}
}
}
// Place exit
var exitX = MAP_WIDTH - 2;
var exitY = MAP_HEIGHT - 2;
map[exitY][exitX] = 2; // exit
// Place 5 items randomly on floor tiles
var itemsPlaced = 0;
while (itemsPlaced < 5) {
var randX = Math.floor(Math.random() * (MAP_WIDTH - 2)) + 1;
var randY = Math.floor(Math.random() * (MAP_HEIGHT - 2)) + 1;
if (map[randY][randX] === 0 && !(randX === 1 && randY === 1)) {
map[randY][randX] = 3; // item
itemsPlaced++;
}
}
return map;
}
var mapData = generateRandomMap();
// --- GLOBALS ---
var mapTiles = []; // 2D array of MapTile
var fogTiles = []; // 2D array of FogTile
var player = null;
var playerStart = {
x: 1,
y: 1
};
var exitPos = {
x: 7,
y: 9
};
var itemsTotal = 0;
var itemsCollected = 0;
var visionRadius = 1.5; // tiles
var moveLock = false; // prevent double moves
var arrowButtons = {};
var lastPlayerPos = {
x: 1,
y: 1
};
// --- GUI ---
var itemsTxt = new Text2('Items: 0/0', {
size: 90,
fill: "#fff"
});
itemsTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(itemsTxt);
// --- MAP GENERATION ---
for (var y = 0; y < MAP_HEIGHT; y++) {
mapTiles[y] = [];
fogTiles[y] = [];
for (var x = 0; x < MAP_WIDTH; x++) {
var tile = new MapTile();
tile.xIndex = x;
tile.yIndex = y;
var type = mapData[y][x];
if (type === 1) {
tile.setType('wall');
} else if (type === 2) {
tile.setType('exit');
exitPos.x = x;
exitPos.y = y;
} else {
tile.setType('floor');
}
if (type === 3) {
tile.placeItem();
itemsTotal++;
}
tile.x = MAP_OFFSET_X + x * TILE_SIZE + TILE_SIZE / 2;
tile.y = MAP_OFFSET_Y + y * TILE_SIZE + TILE_SIZE / 2;
game.addChild(tile);
mapTiles[y][x] = tile;
// Fog
var fog = new FogTile();
fog.x = tile.x;
fog.y = tile.y;
game.addChild(fog);
fogTiles[y][x] = fog;
}
}
itemsTxt.setText('Items: 0/' + itemsTotal);
// --- PLAYER ---
player = new Player();
player.xIndex = playerStart.x;
player.yIndex = playerStart.y;
player.x = MAP_OFFSET_X + player.xIndex * TILE_SIZE + TILE_SIZE / 2;
player.y = MAP_OFFSET_Y + player.yIndex * TILE_SIZE + TILE_SIZE / 2;
game.addChild(player);
// --- FOG OF WAR ---
function updateFog() {
for (var y = 0; y < MAP_HEIGHT; y++) {
for (var x = 0; x < MAP_WIDTH; x++) {
var dx = x - player.xIndex;
var dy = y - player.yIndex;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= visionRadius) {
// Reveal
fogTiles[y][x].alpha = 0;
} else if (dist <= visionRadius + 1) {
// Faded memory
fogTiles[y][x].alpha = 0.5;
} else {
// Full fog
fogTiles[y][x].alpha = 0.92;
}
}
}
}
updateFog();
// --- CAMERA SYSTEM ---
function updateCamera() {
// Calculate target camera position to center player on screen
var playerWorldX = MAP_OFFSET_X + player.xIndex * TILE_SIZE + TILE_SIZE / 2;
var playerWorldY = MAP_OFFSET_Y + player.yIndex * TILE_SIZE + TILE_SIZE / 2;
camera.targetX = SCREEN_CENTER_X - playerWorldX;
camera.targetY = SCREEN_CENTER_Y - playerWorldY;
// Smooth camera movement
camera.x += (camera.targetX - camera.x) * 0.15;
camera.y += (camera.targetY - camera.y) * 0.15;
// Apply camera offset to game container
game.x = camera.x;
game.y = camera.y;
}
// --- MOVEMENT LOGIC ---
function canMoveTo(x, y) {
if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) return false;
var tile = mapTiles[y][x];
if (tile.type === 'wall') return false;
return true;
}
function movePlayer(dx, dy) {
if (moveLock) return;
var nx = player.xIndex + dx;
var ny = player.yIndex + dy;
if (!canMoveTo(nx, ny)) {
// Bump animation
var bumpX = player.x + dx * 20;
var bumpY = player.y + dy * 20;
moveLock = true;
tween(player, {
x: bumpX,
y: bumpY
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(player, {
x: MAP_OFFSET_X + player.xIndex * TILE_SIZE + TILE_SIZE / 2,
y: MAP_OFFSET_Y + player.yIndex * TILE_SIZE + TILE_SIZE / 2
}, {
duration: 80,
onFinish: function onFinish() {
moveLock = false;
}
});
}
});
return;
}
lastPlayerPos.x = player.xIndex;
lastPlayerPos.y = player.yIndex;
player.xIndex = nx;
player.yIndex = ny;
moveLock = true;
tween(player, {
x: MAP_OFFSET_X + nx * TILE_SIZE + TILE_SIZE / 2,
y: MAP_OFFSET_Y + ny * TILE_SIZE + TILE_SIZE / 2
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: function onFinish() {
moveLock = false;
afterMove();
}
});
updateFog();
updateCamera();
}
function afterMove() {
// Check for item
var tile = mapTiles[player.yIndex][player.xIndex];
if (tile.hasItem) {
tile.removeItem();
itemsCollected++;
itemsTxt.setText('Items: ' + itemsCollected + '/' + itemsTotal);
LK.effects.flashObject(player, 0xffe066, 400);
}
// Check for exit
if (tile.isExit) {
if (itemsCollected >= itemsTotal) {
LK.showYouWin();
} else {
// Flash exit red if not all items collected
LK.effects.flashObject(tile, 0xff0000, 400);
}
}
}
// --- ARROW BUTTONS ---
// Create arrow buttons in GUI layer to follow player
function createGUIArrowButton(id, x, y, rotation, moveFn) {
var btn = LK.getAsset(id, {
anchorX: 0.5,
anchorY: 0.5
});
btn.x = x;
btn.y = y;
btn.scaleX = 0.8;
btn.scaleY = 0.8;
btn.rotation = rotation;
btn.alpha = 0.9;
btn.interactive = true;
btn.down = function () {
moveFn();
};
LK.gui.bottom.addChild(btn);
return btn;
}
// Place arrow buttons at bottom of GUI, arranged in a D-pad layout - moved higher and smaller
var btnSpacing = 160; // spacing between buttons (reduced)
arrowButtons.up = createGUIArrowButton('arrowUp', 0, -btnSpacing - 250, 0, function () {
movePlayer(0, -1);
});
arrowButtons.down = createGUIArrowButton('arrowDown', 0, btnSpacing - 250, Math.PI, function () {
movePlayer(0, 1);
});
arrowButtons.left = createGUIArrowButton('arrowLeft', -btnSpacing, -250, -Math.PI / 2, function () {
movePlayer(-1, 0);
});
arrowButtons.right = createGUIArrowButton('arrowRight', btnSpacing, -250, Math.PI / 2, function () {
movePlayer(1, 0);
});
// --- TOUCH DRAG MOVEMENT (OPTIONAL) ---
// For MVP, only arrow buttons. (No swipe/drag movement.)
// --- GAME UPDATE ---
game.update = function () {
// Update camera smoothly each frame
updateCamera();
};
// --- GAME OVER HANDLING ---
// Handled by LK.showYouWin()
// --- INITIAL FOG UPDATE ---
updateFog();
// --- INITIAL CAMERA SETUP ---
updateCamera();