/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// --- HexTile: A single hexagon on the board ---
var HexTile = Container.expand(function () {
var self = Container.call(this);
// Color: 'pink', 'blue', 'yellow', 'green', 'purple', 'orange'
self.color = 'pink';
self.gridQ = 0; // axial q
self.gridR = 0; // axial r
self.occupied = false;
self.face = null;
// Attach hex asset
self.setColor = function (color) {
self.color = color;
if (self.hex) self.removeChild(self.hex);
self.hex = self.attachAsset('hex_' + color, {
anchorX: 0.5,
anchorY: 0.5
});
// Add face
if (self.face) self.removeChild(self.face);
self.face = self.attachAsset('face', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 0.7,
scaleY: 0.7
});
};
self.setEmpty = function () {
self.occupied = false;
if (self.hex) self.hex.visible = false;
if (self.face) self.face.visible = false;
};
self.setFilled = function (color) {
self.occupied = true;
self.setColor(color);
self.hex.visible = true;
self.face.visible = true;
};
// Pop animation
self.pop = function () {
if (!self.occupied) return;
self.setEmpty();
};
return self;
});
// --- Piece: A draggable group of 1-5 hexes in a shape ---
var Piece = Container.expand(function () {
var self = Container.call(this);
// Shape: array of {q, r, color}
self.shape = [];
self.hexes = [];
self.dragging = false;
self.valid = true; // is current placement valid
// Generate a piece from a shape definition
self.setShape = function (shape) {
self.shape = shape;
// Remove old hexes
for (var i = 0; i < self.hexes.length; ++i) self.removeChild(self.hexes[i]);
self.hexes = [];
// Add new hexes
for (var i = 0; i < shape.length; ++i) {
var hex = self.attachAsset('hex_' + shape[i].color, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
// Add face
var face = self.attachAsset('face', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 0.7,
scaleY: 0.7
});
hex.addChild(face);
self.hexes.push(hex);
}
// Position hexes
for (var i = 0; i < shape.length; ++i) {
var pos = axialToPixel(shape[i].q, shape[i].r, pieceHexSize);
self.hexes[i].x = pos.x;
self.hexes[i].y = pos.y;
}
};
// Highlight for valid/invalid placement
self.setValid = function (valid) {
self.valid = valid;
for (var i = 0; i < self.hexes.length; ++i) {
self.hexes[i].alpha = valid ? 1 : 0.4;
}
};
return self;
});
/****
* Initialize Game
****/
/****
* Helper Functions
****/
// Hex math
var game = new LK.Game({
title: "Cloud Pop Puzzle",
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- Add main background image ---
var bgImg = LK.getAsset('Background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1,
scaleY: 1,
alpha: 1
});
game.addChild(bgImg);
// Play game music
LK.playMusic('game_music2');
// --- Music On/Off Toggle Button ---
var musicOn = true;
var musicBtn = new Text2('🔊', {
size: 100,
fill: "#fff"
});
musicBtn.anchor.set(1, 0); // Top-right corner
musicBtn.x = -40; // Offset from right edge (gui.right is at x=width)
musicBtn.y = 40; // Offset from top edge
musicBtn.interactive = true;
musicBtn.buttonMode = true;
musicBtn.entityName = 'music_toggle_btn';
musicBtn.down = function (x, y, obj) {
musicOn = !musicOn;
if (musicOn) {
musicBtn.setText('🔊');
LK.playMusic('game_music2');
} else {
musicBtn.setText('🔇');
LK.stopMusic();
}
};
// Add to GUI top right (avoid top left due to menu)
LK.gui.topRight.addChild(musicBtn);
// --- Add dreamy cloud background ---
// Hex tile shapes (pastel colors)
// Cloud background (large, white, semi-transparent ellipses)
// Faces (as colored ellipses for now, can be replaced with images later)
// Pop effect
// Sound effects
/****
* Helper Functions
****/
// Hex math
function _typeof6(o) {
"@babel/helpers - typeof";
return _typeof6 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof6(o);
}
function _typeof5(o) {
"@babel/helpers - typeof";
return _typeof5 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof5(o);
}
function _typeof4(o) {
"@babel/helpers - typeof";
return _typeof4 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof4(o);
}
function _typeof3(o) {
"@babel/helpers - typeof";
return _typeof3 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof3(o);
}
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var sqrt3 = Math.sqrt(3);
function axialToPixel(q, r, size) {
// Flat-topped hex
var x = size * (3 / 2 * q);
var y = size * (sqrt3 * (r + q / 2));
return {
x: x,
y: y
};
}
function pixelToAxial(x, y, size) {
var q = 2 / 3 * x / size;
var r = (-1 / 3 * x + sqrt3 / 3 * y) / size;
return hexRound(q, r);
}
function hexRound(q, r) {
var x = q;
var z = r;
var y = -x - z;
var rx = Math.round(x);
var ry = Math.round(y);
var rz = Math.round(z);
var x_diff = Math.abs(rx - x);
var y_diff = Math.abs(ry - y);
var z_diff = Math.abs(rz - z);
if (x_diff > y_diff && x_diff > z_diff) rx = -ry - rz;else if (y_diff > z_diff) ry = -rx - rz;else rz = -rx - ry;
return {
q: rx,
r: rz
};
}
// Generate random color
function randomColor() {
var arr = ['pink', 'blue', 'yellow', 'green', 'purple', 'orange'];
return arr[Math.floor(Math.random() * arr.length)];
}
// Piece shape library (axial coordinates, always includes 0,0)
// Only allow shapes with at most 4 hexes
var pieceShapes = [
// Single
[{
q: 0,
r: 0
}],
// Line 2
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}],
// 1x2 Overlapping (vertical overlap: q=0,r=0 and q=0,r=1)
[{
q: 0,
r: 0
}, {
q: 0,
r: 1
}],
// Line 3
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 2,
r: 0
}],
// L shape
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 1,
r: 1
}],
// Triangle
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 0,
r: 1
}],
// Line 4
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 2,
r: 0
}, {
q: 3,
r: 0
}],
// Big L (4)
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 2,
r: 0
}, {
q: 2,
r: 1
}]];
// Generate a random piece shape with random colors
function randomPieceShape() {
var base = pieceShapes[Math.floor(Math.random() * pieceShapes.length)];
var color = randomColor();
var arr = [];
for (var i = 0; i < base.length; ++i) {
arr.push({
q: base[i].q,
r: base[i].r,
color: color
});
}
return arr;
}
/****
* Game Board Setup
****/
// Board size
var boardRadius = 5; // 5 rings from center (total diameter 11)
var boardHexSize = 90; // px, for board tiles (increased by 50%)
var pieceHexSize = boardHexSize; // px, for piece tiles (match board tile size)
// Board center in pixels
var boardCenterX = 2048 / 2;
var boardCenterY = 1200;
// Board data: array of {q, r, tile}
var boardTiles = [];
// Build board: all hexes with |q|+|r|+|s| <= boardRadius
for (var q = -boardRadius; q <= boardRadius; ++q) {
for (var r = -boardRadius; r <= boardRadius; ++r) {
var s = -q - r;
if (Math.abs(q) <= boardRadius && Math.abs(r) <= boardRadius && Math.abs(s) <= boardRadius) {
// Create tile
var tile = new HexTile();
tile.gridQ = q;
tile.gridR = r;
tile.setEmpty();
var pos = axialToPixel(q, r, boardHexSize);
tile.x = boardCenterX + pos.x;
tile.y = boardCenterY + pos.y;
// Add a unique asset for every cell (behind the tile) with entity name 'cell_background2'
var cellAsset = LK.getAsset('cell_background', {
anchorX: 0.5,
anchorY: 0.5,
x: tile.x,
y: tile.y,
scaleX: boardHexSize / 100,
scaleY: boardHexSize / 100,
alpha: 0.22,
entityName: 'cell_background2'
});
game.addChild(cellAsset);
boardTiles.push({
q: q,
r: r,
tile: tile
});
}
}
}
// Helper: find tile at q,r
function getTile(q, r) {
for (var i = 0; i < boardTiles.length; ++i) {
if (boardTiles[i].q === q && boardTiles[i].r === r) return boardTiles[i].tile;
}
return null;
}
// --- Add game board image '1' behind tiles ---
var boardImg1 = LK.getAsset('1', {
anchorX: 0.5,
anchorY: 0.5,
x: boardCenterX,
y: boardCenterY,
scaleX: 12,
scaleY: 12,
alpha: 0.18
});
game.addChild(boardImg1);
// --- Add board tiles to game ---
for (var i = 0; i < boardTiles.length; ++i) {
game.addChild(boardTiles[i].tile);
}
// --- Score display ---
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Piece tray (bottom center) ---
var trayY = 2732 - 550; // Move tray higher (was 2732 - 350)
var trayX = 2048 / 2;
var traySpacing = 600; // Increase spacing between tray pieces (was 480)
var trayPieces = [null, null, null]; // 3 pieces at a time
// --- Generate new pieces in tray ---
function refillTray() {
for (var i = 0; i < 3; ++i) {
if (trayPieces[i]) {
trayPieces[i].destroy();
trayPieces[i] = null;
}
// Add tray background behind each piece
var trayBg = LK.getAsset('tray_background', {
anchorX: 0.0,
anchorY: 0.0,
x: trayX + (i - 1) * traySpacing - 350 / 2 - 80,
// shift left by 80px
y: trayY - 350 / 2,
scaleX: pieceHexSize / 100 * 1.5,
scaleY: pieceHexSize / 100 * 1.5,
alpha: 0.5
});
game.addChild(trayBg);
var piece = new Piece();
piece.setShape(randomPieceShape());
piece.x = trayX + (i - 1) * traySpacing - 80; // shift left by 80px
piece.y = trayY;
trayPieces[i] = piece;
game.addChild(piece);
}
}
refillTray();
// --- Drag and drop logic ---
var draggingPiece = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var dragStartX = 0;
var dragStartY = 0;
// Helper: get board position under a pixel
function getBoardPosUnder(x, y) {
// Convert to board-local
var px = x - boardCenterX;
var py = y - boardCenterY;
var axial = pixelToAxial(px, py, boardHexSize);
return axial;
}
// Helper: can place piece at board q,r
function canPlacePiece(piece, q, r) {
for (var i = 0; i < piece.shape.length; ++i) {
var dq = piece.shape[i].q;
var dr = piece.shape[i].r;
var tile = getTile(q + dq, r + dr);
if (!tile || tile.occupied) return false;
}
return true;
}
// Helper: place piece at board q,r
function placePiece(piece, q, r) {
for (var i = 0; i < piece.shape.length; ++i) {
var dq = piece.shape[i].q;
var dr = piece.shape[i].r;
var color = piece.shape[i].color;
var tile = getTile(q + dq, r + dr);
if (tile) {
tile.setFilled(color);
// Add entity name '1' to this slot
tile.entityName = '1';
}
}
// Play combo sound when a piece is placed
LK.getSound('combo').play();
}
// Helper: remove piece from tray
function removePieceFromTray(piece) {
for (var i = 0; i < 3; ++i) {
if (trayPieces[i] === piece) {
trayPieces[i].destroy();
trayPieces[i] = null;
}
}
}
// --- Game move events ---
game.down = function (x, y, obj) {
// Check if a piece in tray is touched
for (var i = 0; i < 3; ++i) {
var piece = trayPieces[i];
if (!piece) continue;
var local = piece.toLocal(game.toGlobal({
x: x,
y: y
}));
// Check if touch is within piece bounds
for (var j = 0; j < piece.hexes.length; ++j) {
var hx = piece.hexes[j].x;
var hy = piece.hexes[j].y;
var dx = local.x - hx;
var dy = local.y - hy;
if (dx * dx + dy * dy < pieceHexSize * pieceHexSize * 0.6) {
draggingPiece = piece;
dragStartX = piece.x;
dragStartY = piece.y;
dragOffsetX = x - piece.x;
dragOffsetY = y - piece.y;
piece.dragging = true;
piece.setValid(true);
// Bring to front
game.addChild(piece);
return;
}
}
}
};
game.move = function (x, y, obj) {
if (!draggingPiece) return;
// Move piece
draggingPiece.x = x - dragOffsetX;
draggingPiece.y = y - dragOffsetY;
// Snap to board if close
var pos = getBoardPosUnder(draggingPiece.x, draggingPiece.y);
var canPlace = canPlacePiece(draggingPiece, pos.q, pos.r);
draggingPiece.setValid(canPlace);
// Snap visual to nearest hex if close enough
if (canPlace) {
var snapPos = axialToPixel(pos.q, pos.r, boardHexSize);
draggingPiece.x = boardCenterX + snapPos.x;
draggingPiece.y = boardCenterY + snapPos.y;
}
};
game.up = function (x, y, obj) {
if (!draggingPiece) return;
// Try to place
var pos = getBoardPosUnder(draggingPiece.x, draggingPiece.y);
if (canPlacePiece(draggingPiece, pos.q, pos.r)) {
// Snap piece visually to the grid before placing
var snapPos = axialToPixel(pos.q, pos.r, boardHexSize);
draggingPiece.x = boardCenterX + snapPos.x;
draggingPiece.y = boardCenterY + snapPos.y;
// Place
placePiece(draggingPiece, pos.q, pos.r);
removePieceFromTray(draggingPiece);
draggingPiece.destroy();
draggingPiece = null;
// Check for matches and refill tray if all used
checkAndPopMatches();
if (trayPieces.every(function (p) {
return !p;
})) {
refillTray();
}
// Check for game over
if (!canAnyPieceBePlaced()) {
LK.showGameOver();
}
} else {
// Return to tray (center to tray position)
var trayIndex = -1;
for (var i = 0; i < 3; ++i) {
if (trayPieces[i] === draggingPiece) {
trayIndex = i;
break;
}
}
var trayTargetX = trayX + (trayIndex - 1) * traySpacing;
var trayTargetY = trayY;
tween(draggingPiece, {
x: trayTargetX,
y: trayTargetY
}, {
duration: 200,
easing: tween.easeOut
});
draggingPiece.setValid(true);
draggingPiece.dragging = false;
draggingPiece = null;
}
};
// --- Line clearing logic ---
// Find all full lines (horizontal or vertical), clear them, and chain
function checkAndPopMatches() {
var popped = false;
var combos = 0;
var linesToClear = [];
// Helper: get all q/r/s values present on the board
var qSet = {};
var rSet = {};
var sSet = {};
for (var i = 0; i < boardTiles.length; ++i) {
var t = boardTiles[i];
qSet[t.q] = true;
rSet[t.r] = true;
sSet[-t.q - t.r] = true;
}
// Helper: get all tiles in a line (by q, r, or s)
function getLineTiles(axis, value) {
var arr = [];
for (var i = 0; i < boardTiles.length; ++i) {
var t = boardTiles[i];
if (axis === 'q' && t.q === value || axis === 'r' && t.r === value || axis === 's' && -t.q - t.r === value) {
arr.push(t.tile);
}
}
return arr;
}
// Check all q lines (vertical)
for (var q in qSet) {
var tiles = getLineTiles('q', parseInt(q));
if (tiles.length === 0) continue;
var allOccupied = true;
for (var i = 0; i < tiles.length; ++i) {
if (!tiles[i].occupied) {
allOccupied = false;
break;
}
}
if (allOccupied) {
linesToClear.push(tiles);
}
}
// Check all r lines (diagonal)
for (var r in rSet) {
var tiles = getLineTiles('r', parseInt(r));
if (tiles.length === 0) continue;
var allOccupied = true;
for (var i = 0; i < tiles.length; ++i) {
if (!tiles[i].occupied) {
allOccupied = false;
break;
}
}
if (allOccupied) {
linesToClear.push(tiles);
}
}
// Check all s lines (other diagonal)
for (var s in sSet) {
var tiles = getLineTiles('s', parseInt(s));
if (tiles.length === 0) continue;
var allOccupied = true;
for (var i = 0; i < tiles.length; ++i) {
if (!tiles[i].occupied) {
allOccupied = false;
break;
}
}
if (allOccupied) {
linesToClear.push(tiles);
}
}
// Remove duplicates (tiles may be in multiple lines)
var clearedTiles = {};
for (var i = 0; i < linesToClear.length; ++i) {
for (var j = 0; j < linesToClear[i].length; ++j) {
clearedTiles[linesToClear[i][j].gridQ + ',' + linesToClear[i][j].gridR] = linesToClear[i][j];
}
}
var clearedArr = [];
for (var k in clearedTiles) clearedArr.push(clearedTiles[k]);
if (clearedArr.length > 0) {
for (var i = 0; i < clearedArr.length; ++i) {
clearedArr[i].pop();
}
score += clearedArr.length;
popped = true;
combos = linesToClear.length;
}
if (popped) {
scoreTxt.setText(score);
if (combos > 1) {
LK.getSound('combo').play();
}
// Chain: after pop, check again after short delay
LK.setTimeout(checkAndPopMatches, 400);
}
}
// --- Can any piece be placed? ---
function canAnyPieceBePlaced() {
for (var i = 0; i < 3; ++i) {
var piece = trayPieces[i];
if (!piece) continue;
// Try all board positions
for (var j = 0; j < boardTiles.length; ++j) {
var q = boardTiles[j].q;
var r = boardTiles[j].r;
if (canPlacePiece(piece, q, r)) return true;
}
}
return false;
}
// --- Game update (not used for now) ---
game.update = function () {
// No per-frame logic needed
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// --- HexTile: A single hexagon on the board ---
var HexTile = Container.expand(function () {
var self = Container.call(this);
// Color: 'pink', 'blue', 'yellow', 'green', 'purple', 'orange'
self.color = 'pink';
self.gridQ = 0; // axial q
self.gridR = 0; // axial r
self.occupied = false;
self.face = null;
// Attach hex asset
self.setColor = function (color) {
self.color = color;
if (self.hex) self.removeChild(self.hex);
self.hex = self.attachAsset('hex_' + color, {
anchorX: 0.5,
anchorY: 0.5
});
// Add face
if (self.face) self.removeChild(self.face);
self.face = self.attachAsset('face', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 0.7,
scaleY: 0.7
});
};
self.setEmpty = function () {
self.occupied = false;
if (self.hex) self.hex.visible = false;
if (self.face) self.face.visible = false;
};
self.setFilled = function (color) {
self.occupied = true;
self.setColor(color);
self.hex.visible = true;
self.face.visible = true;
};
// Pop animation
self.pop = function () {
if (!self.occupied) return;
self.setEmpty();
};
return self;
});
// --- Piece: A draggable group of 1-5 hexes in a shape ---
var Piece = Container.expand(function () {
var self = Container.call(this);
// Shape: array of {q, r, color}
self.shape = [];
self.hexes = [];
self.dragging = false;
self.valid = true; // is current placement valid
// Generate a piece from a shape definition
self.setShape = function (shape) {
self.shape = shape;
// Remove old hexes
for (var i = 0; i < self.hexes.length; ++i) self.removeChild(self.hexes[i]);
self.hexes = [];
// Add new hexes
for (var i = 0; i < shape.length; ++i) {
var hex = self.attachAsset('hex_' + shape[i].color, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
// Add face
var face = self.attachAsset('face', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 0.7,
scaleY: 0.7
});
hex.addChild(face);
self.hexes.push(hex);
}
// Position hexes
for (var i = 0; i < shape.length; ++i) {
var pos = axialToPixel(shape[i].q, shape[i].r, pieceHexSize);
self.hexes[i].x = pos.x;
self.hexes[i].y = pos.y;
}
};
// Highlight for valid/invalid placement
self.setValid = function (valid) {
self.valid = valid;
for (var i = 0; i < self.hexes.length; ++i) {
self.hexes[i].alpha = valid ? 1 : 0.4;
}
};
return self;
});
/****
* Initialize Game
****/
/****
* Helper Functions
****/
// Hex math
var game = new LK.Game({
title: "Cloud Pop Puzzle",
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- Add main background image ---
var bgImg = LK.getAsset('Background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1,
scaleY: 1,
alpha: 1
});
game.addChild(bgImg);
// Play game music
LK.playMusic('game_music2');
// --- Music On/Off Toggle Button ---
var musicOn = true;
var musicBtn = new Text2('🔊', {
size: 100,
fill: "#fff"
});
musicBtn.anchor.set(1, 0); // Top-right corner
musicBtn.x = -40; // Offset from right edge (gui.right is at x=width)
musicBtn.y = 40; // Offset from top edge
musicBtn.interactive = true;
musicBtn.buttonMode = true;
musicBtn.entityName = 'music_toggle_btn';
musicBtn.down = function (x, y, obj) {
musicOn = !musicOn;
if (musicOn) {
musicBtn.setText('🔊');
LK.playMusic('game_music2');
} else {
musicBtn.setText('🔇');
LK.stopMusic();
}
};
// Add to GUI top right (avoid top left due to menu)
LK.gui.topRight.addChild(musicBtn);
// --- Add dreamy cloud background ---
// Hex tile shapes (pastel colors)
// Cloud background (large, white, semi-transparent ellipses)
// Faces (as colored ellipses for now, can be replaced with images later)
// Pop effect
// Sound effects
/****
* Helper Functions
****/
// Hex math
function _typeof6(o) {
"@babel/helpers - typeof";
return _typeof6 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof6(o);
}
function _typeof5(o) {
"@babel/helpers - typeof";
return _typeof5 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof5(o);
}
function _typeof4(o) {
"@babel/helpers - typeof";
return _typeof4 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof4(o);
}
function _typeof3(o) {
"@babel/helpers - typeof";
return _typeof3 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof3(o);
}
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var sqrt3 = Math.sqrt(3);
function axialToPixel(q, r, size) {
// Flat-topped hex
var x = size * (3 / 2 * q);
var y = size * (sqrt3 * (r + q / 2));
return {
x: x,
y: y
};
}
function pixelToAxial(x, y, size) {
var q = 2 / 3 * x / size;
var r = (-1 / 3 * x + sqrt3 / 3 * y) / size;
return hexRound(q, r);
}
function hexRound(q, r) {
var x = q;
var z = r;
var y = -x - z;
var rx = Math.round(x);
var ry = Math.round(y);
var rz = Math.round(z);
var x_diff = Math.abs(rx - x);
var y_diff = Math.abs(ry - y);
var z_diff = Math.abs(rz - z);
if (x_diff > y_diff && x_diff > z_diff) rx = -ry - rz;else if (y_diff > z_diff) ry = -rx - rz;else rz = -rx - ry;
return {
q: rx,
r: rz
};
}
// Generate random color
function randomColor() {
var arr = ['pink', 'blue', 'yellow', 'green', 'purple', 'orange'];
return arr[Math.floor(Math.random() * arr.length)];
}
// Piece shape library (axial coordinates, always includes 0,0)
// Only allow shapes with at most 4 hexes
var pieceShapes = [
// Single
[{
q: 0,
r: 0
}],
// Line 2
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}],
// 1x2 Overlapping (vertical overlap: q=0,r=0 and q=0,r=1)
[{
q: 0,
r: 0
}, {
q: 0,
r: 1
}],
// Line 3
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 2,
r: 0
}],
// L shape
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 1,
r: 1
}],
// Triangle
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 0,
r: 1
}],
// Line 4
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 2,
r: 0
}, {
q: 3,
r: 0
}],
// Big L (4)
[{
q: 0,
r: 0
}, {
q: 1,
r: 0
}, {
q: 2,
r: 0
}, {
q: 2,
r: 1
}]];
// Generate a random piece shape with random colors
function randomPieceShape() {
var base = pieceShapes[Math.floor(Math.random() * pieceShapes.length)];
var color = randomColor();
var arr = [];
for (var i = 0; i < base.length; ++i) {
arr.push({
q: base[i].q,
r: base[i].r,
color: color
});
}
return arr;
}
/****
* Game Board Setup
****/
// Board size
var boardRadius = 5; // 5 rings from center (total diameter 11)
var boardHexSize = 90; // px, for board tiles (increased by 50%)
var pieceHexSize = boardHexSize; // px, for piece tiles (match board tile size)
// Board center in pixels
var boardCenterX = 2048 / 2;
var boardCenterY = 1200;
// Board data: array of {q, r, tile}
var boardTiles = [];
// Build board: all hexes with |q|+|r|+|s| <= boardRadius
for (var q = -boardRadius; q <= boardRadius; ++q) {
for (var r = -boardRadius; r <= boardRadius; ++r) {
var s = -q - r;
if (Math.abs(q) <= boardRadius && Math.abs(r) <= boardRadius && Math.abs(s) <= boardRadius) {
// Create tile
var tile = new HexTile();
tile.gridQ = q;
tile.gridR = r;
tile.setEmpty();
var pos = axialToPixel(q, r, boardHexSize);
tile.x = boardCenterX + pos.x;
tile.y = boardCenterY + pos.y;
// Add a unique asset for every cell (behind the tile) with entity name 'cell_background2'
var cellAsset = LK.getAsset('cell_background', {
anchorX: 0.5,
anchorY: 0.5,
x: tile.x,
y: tile.y,
scaleX: boardHexSize / 100,
scaleY: boardHexSize / 100,
alpha: 0.22,
entityName: 'cell_background2'
});
game.addChild(cellAsset);
boardTiles.push({
q: q,
r: r,
tile: tile
});
}
}
}
// Helper: find tile at q,r
function getTile(q, r) {
for (var i = 0; i < boardTiles.length; ++i) {
if (boardTiles[i].q === q && boardTiles[i].r === r) return boardTiles[i].tile;
}
return null;
}
// --- Add game board image '1' behind tiles ---
var boardImg1 = LK.getAsset('1', {
anchorX: 0.5,
anchorY: 0.5,
x: boardCenterX,
y: boardCenterY,
scaleX: 12,
scaleY: 12,
alpha: 0.18
});
game.addChild(boardImg1);
// --- Add board tiles to game ---
for (var i = 0; i < boardTiles.length; ++i) {
game.addChild(boardTiles[i].tile);
}
// --- Score display ---
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Piece tray (bottom center) ---
var trayY = 2732 - 550; // Move tray higher (was 2732 - 350)
var trayX = 2048 / 2;
var traySpacing = 600; // Increase spacing between tray pieces (was 480)
var trayPieces = [null, null, null]; // 3 pieces at a time
// --- Generate new pieces in tray ---
function refillTray() {
for (var i = 0; i < 3; ++i) {
if (trayPieces[i]) {
trayPieces[i].destroy();
trayPieces[i] = null;
}
// Add tray background behind each piece
var trayBg = LK.getAsset('tray_background', {
anchorX: 0.0,
anchorY: 0.0,
x: trayX + (i - 1) * traySpacing - 350 / 2 - 80,
// shift left by 80px
y: trayY - 350 / 2,
scaleX: pieceHexSize / 100 * 1.5,
scaleY: pieceHexSize / 100 * 1.5,
alpha: 0.5
});
game.addChild(trayBg);
var piece = new Piece();
piece.setShape(randomPieceShape());
piece.x = trayX + (i - 1) * traySpacing - 80; // shift left by 80px
piece.y = trayY;
trayPieces[i] = piece;
game.addChild(piece);
}
}
refillTray();
// --- Drag and drop logic ---
var draggingPiece = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
var dragStartX = 0;
var dragStartY = 0;
// Helper: get board position under a pixel
function getBoardPosUnder(x, y) {
// Convert to board-local
var px = x - boardCenterX;
var py = y - boardCenterY;
var axial = pixelToAxial(px, py, boardHexSize);
return axial;
}
// Helper: can place piece at board q,r
function canPlacePiece(piece, q, r) {
for (var i = 0; i < piece.shape.length; ++i) {
var dq = piece.shape[i].q;
var dr = piece.shape[i].r;
var tile = getTile(q + dq, r + dr);
if (!tile || tile.occupied) return false;
}
return true;
}
// Helper: place piece at board q,r
function placePiece(piece, q, r) {
for (var i = 0; i < piece.shape.length; ++i) {
var dq = piece.shape[i].q;
var dr = piece.shape[i].r;
var color = piece.shape[i].color;
var tile = getTile(q + dq, r + dr);
if (tile) {
tile.setFilled(color);
// Add entity name '1' to this slot
tile.entityName = '1';
}
}
// Play combo sound when a piece is placed
LK.getSound('combo').play();
}
// Helper: remove piece from tray
function removePieceFromTray(piece) {
for (var i = 0; i < 3; ++i) {
if (trayPieces[i] === piece) {
trayPieces[i].destroy();
trayPieces[i] = null;
}
}
}
// --- Game move events ---
game.down = function (x, y, obj) {
// Check if a piece in tray is touched
for (var i = 0; i < 3; ++i) {
var piece = trayPieces[i];
if (!piece) continue;
var local = piece.toLocal(game.toGlobal({
x: x,
y: y
}));
// Check if touch is within piece bounds
for (var j = 0; j < piece.hexes.length; ++j) {
var hx = piece.hexes[j].x;
var hy = piece.hexes[j].y;
var dx = local.x - hx;
var dy = local.y - hy;
if (dx * dx + dy * dy < pieceHexSize * pieceHexSize * 0.6) {
draggingPiece = piece;
dragStartX = piece.x;
dragStartY = piece.y;
dragOffsetX = x - piece.x;
dragOffsetY = y - piece.y;
piece.dragging = true;
piece.setValid(true);
// Bring to front
game.addChild(piece);
return;
}
}
}
};
game.move = function (x, y, obj) {
if (!draggingPiece) return;
// Move piece
draggingPiece.x = x - dragOffsetX;
draggingPiece.y = y - dragOffsetY;
// Snap to board if close
var pos = getBoardPosUnder(draggingPiece.x, draggingPiece.y);
var canPlace = canPlacePiece(draggingPiece, pos.q, pos.r);
draggingPiece.setValid(canPlace);
// Snap visual to nearest hex if close enough
if (canPlace) {
var snapPos = axialToPixel(pos.q, pos.r, boardHexSize);
draggingPiece.x = boardCenterX + snapPos.x;
draggingPiece.y = boardCenterY + snapPos.y;
}
};
game.up = function (x, y, obj) {
if (!draggingPiece) return;
// Try to place
var pos = getBoardPosUnder(draggingPiece.x, draggingPiece.y);
if (canPlacePiece(draggingPiece, pos.q, pos.r)) {
// Snap piece visually to the grid before placing
var snapPos = axialToPixel(pos.q, pos.r, boardHexSize);
draggingPiece.x = boardCenterX + snapPos.x;
draggingPiece.y = boardCenterY + snapPos.y;
// Place
placePiece(draggingPiece, pos.q, pos.r);
removePieceFromTray(draggingPiece);
draggingPiece.destroy();
draggingPiece = null;
// Check for matches and refill tray if all used
checkAndPopMatches();
if (trayPieces.every(function (p) {
return !p;
})) {
refillTray();
}
// Check for game over
if (!canAnyPieceBePlaced()) {
LK.showGameOver();
}
} else {
// Return to tray (center to tray position)
var trayIndex = -1;
for (var i = 0; i < 3; ++i) {
if (trayPieces[i] === draggingPiece) {
trayIndex = i;
break;
}
}
var trayTargetX = trayX + (trayIndex - 1) * traySpacing;
var trayTargetY = trayY;
tween(draggingPiece, {
x: trayTargetX,
y: trayTargetY
}, {
duration: 200,
easing: tween.easeOut
});
draggingPiece.setValid(true);
draggingPiece.dragging = false;
draggingPiece = null;
}
};
// --- Line clearing logic ---
// Find all full lines (horizontal or vertical), clear them, and chain
function checkAndPopMatches() {
var popped = false;
var combos = 0;
var linesToClear = [];
// Helper: get all q/r/s values present on the board
var qSet = {};
var rSet = {};
var sSet = {};
for (var i = 0; i < boardTiles.length; ++i) {
var t = boardTiles[i];
qSet[t.q] = true;
rSet[t.r] = true;
sSet[-t.q - t.r] = true;
}
// Helper: get all tiles in a line (by q, r, or s)
function getLineTiles(axis, value) {
var arr = [];
for (var i = 0; i < boardTiles.length; ++i) {
var t = boardTiles[i];
if (axis === 'q' && t.q === value || axis === 'r' && t.r === value || axis === 's' && -t.q - t.r === value) {
arr.push(t.tile);
}
}
return arr;
}
// Check all q lines (vertical)
for (var q in qSet) {
var tiles = getLineTiles('q', parseInt(q));
if (tiles.length === 0) continue;
var allOccupied = true;
for (var i = 0; i < tiles.length; ++i) {
if (!tiles[i].occupied) {
allOccupied = false;
break;
}
}
if (allOccupied) {
linesToClear.push(tiles);
}
}
// Check all r lines (diagonal)
for (var r in rSet) {
var tiles = getLineTiles('r', parseInt(r));
if (tiles.length === 0) continue;
var allOccupied = true;
for (var i = 0; i < tiles.length; ++i) {
if (!tiles[i].occupied) {
allOccupied = false;
break;
}
}
if (allOccupied) {
linesToClear.push(tiles);
}
}
// Check all s lines (other diagonal)
for (var s in sSet) {
var tiles = getLineTiles('s', parseInt(s));
if (tiles.length === 0) continue;
var allOccupied = true;
for (var i = 0; i < tiles.length; ++i) {
if (!tiles[i].occupied) {
allOccupied = false;
break;
}
}
if (allOccupied) {
linesToClear.push(tiles);
}
}
// Remove duplicates (tiles may be in multiple lines)
var clearedTiles = {};
for (var i = 0; i < linesToClear.length; ++i) {
for (var j = 0; j < linesToClear[i].length; ++j) {
clearedTiles[linesToClear[i][j].gridQ + ',' + linesToClear[i][j].gridR] = linesToClear[i][j];
}
}
var clearedArr = [];
for (var k in clearedTiles) clearedArr.push(clearedTiles[k]);
if (clearedArr.length > 0) {
for (var i = 0; i < clearedArr.length; ++i) {
clearedArr[i].pop();
}
score += clearedArr.length;
popped = true;
combos = linesToClear.length;
}
if (popped) {
scoreTxt.setText(score);
if (combos > 1) {
LK.getSound('combo').play();
}
// Chain: after pop, check again after short delay
LK.setTimeout(checkAndPopMatches, 400);
}
}
// --- Can any piece be placed? ---
function canAnyPieceBePlaced() {
for (var i = 0; i < 3; ++i) {
var piece = trayPieces[i];
if (!piece) continue;
// Try all board positions
for (var j = 0; j < boardTiles.length; ++j) {
var q = boardTiles[j].q;
var r = boardTiles[j].r;
if (canPlacePiece(piece, q, r)) return true;
}
}
return false;
}
// --- Game update (not used for now) ---
game.update = function () {
// No per-frame logic needed
};
cloud. In-Game asset. 2d. High contrast. No shadows
cloud pink. In-Game asset. 2d. High contrast. No shadows
cloud orange. In-Game asset. 2d. High contrast. No shadows
cloud green. In-Game asset. 2d. High contrast. No shadows
cloud purple. In-Game asset. 2d. High contrast. No shadows
cloud yellow. In-Game asset. 2d. High contrast. No shadows
Background for relaxing puzzle game. In-Game asset. 2d. High contrast. No shadows
linear features cute face frameless. In-Game asset. 2d. High contrast. No shadows
White square with tight round corners, flat shaded, hyper casual game. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.. In-Game asset. 2d. High contrast. No shadows
White hexagon with tight round corners, flat shaded, hyper casual game. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.. In-Game asset. 2d. High contrast. No shadows