/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BlockPiece = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('blockPieceRed', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.colorAsset = 'blockPieceRed';
self.setColor = function (assetName) {
self.colorAsset = assetName;
self.removeChild(graphics);
graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.setExplodingAsset = function (assetId) {
self.removeChild(graphics);
graphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
var GamePiece = Container.expand(function () {
var self = Container.call(this);
self.blocks = [];
self.shape = [];
self.isDragging = false;
self.canPlace = false;
self.createShape = function (shapeData) {
self.shape = shapeData;
// Pick one color for the entire piece
var pieceColor = pieceColors[Math.floor(Math.random() * pieceColors.length)];
for (var i = 0; i < shapeData.length; i++) {
for (var j = 0; j < shapeData[i].length; j++) {
if (shapeData[i][j] === 1) {
var block = new BlockPiece();
block.x = j * 176; // Updated to match new CELL_SIZE
block.y = i * 176; // Updated to match new CELL_SIZE
block.setColor(pieceColor);
self.blocks.push(block);
self.addChild(block);
}
}
}
};
self.down = function (x, y, obj) {
if (!self.isDragging) {
self.isDragging = true;
draggedPiece = self;
self.originalX = self.x;
self.originalY = self.y;
self.alpha = 0.7;
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifetime = 1000;
self.maxLifetime = 1000;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += 0.5; // gravity
self.lifetime -= 16.67; // 60fps
var alpha = self.lifetime / self.maxLifetime;
graphics.alpha = alpha;
if (self.lifetime <= 0) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0a0a
});
/****
* Game Code
****/
// Increased by 35% (160 * 1.35)
// Increased by 35% (160 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
var GRID_SIZE = 9;
var CELL_SIZE = 176; // Increased by 35% (130 * 1.35)
var GRID_OFFSET_X = 1024 - GRID_SIZE * CELL_SIZE / 2; // Center grid horizontally
var GRID_OFFSET_Y = 1366 - GRID_SIZE * CELL_SIZE / 2; // Center grid vertically
var gameState = 'menu'; // 'menu', 'playing', or 'paused'
var highScore = storage.highScore || 0;
var unlockedLevels = storage.unlockedLevels || 1;
var currentChallengeLevel = 0;
var challengeGoal = 0;
var isChallengeMode = false;
var challengeTimer = 0;
var challengeTimeLimit = 0;
var challengeTimerText = null;
var level2ComboTarget = 2;
var level2MaxCombo = 0;
var playButton = null;
var playButtonText = null;
var arcadeButton = null;
var arcadeButtonText = null;
var challengeButton = null;
var challengeButtonText = null;
var levelSelectTitle = null;
var levelButtons = [];
var levelButtonTexts = [];
var backButton = null;
var backButtonText = null;
var menuTitle = null;
var highScoreText = null;
var pauseOverlay = null;
var pauseTitle = null;
var quitButton = null;
var quitButtonText = null;
var resumeButton = null;
var resumeButtonText = null;
var pauseButton = null;
var pauseButtonText = null;
var grid = [];
var gameGrid = [];
var pieceShapes = [[[1]],
// Single block
[[1, 1]],
// Line 2
[[1, 1, 1]],
// Line 3
[[1], [1]],
// Vertical 2
[[1], [1], [1]],
// Vertical 3
[[1, 1], [1, 1]],
// Square 2x2
[[1, 1, 1], [1, 0, 0]],
// L shape
[[1, 1, 1], [0, 0, 1]],
// Reverse L
[[1, 1, 0], [0, 1, 1]],
// Z shape
[[0, 1, 0], [1, 1, 1]],
// T shape
[[1, 1, 1], [1, 1, 1], [1, 1, 1]],
// 3x3 square
[[1, 1], [1, 1], [1, 1]],
// 2x3 rectangle
// Bigger satisfying shapes for early game
[[1, 1, 1, 1]],
// Line 4
[[1], [1], [1], [1]],
// Vertical 4
[[1, 1, 1], [1, 1, 1]],
// 3x2 rectangle
[[1, 1], [1, 1], [1, 1], [1, 1]],
// 2x4 rectangle
[[1, 1, 1, 1], [1, 1, 1, 1]],
// 4x2 rectangle
[[1, 1, 1], [1, 1, 1], [1, 1, 1]],
// 3x3 square
[[1, 1, 1, 1], [1, 0, 0, 0]],
// Big L shape
[[1, 1, 1, 1], [0, 0, 0, 1]]
// Big reverse L
];
var pieceColors = ['blockPieceRed', 'blockPieceCyan', 'blockPieceBlue', 'blockPieceYellow', 'blockPiecePurple', 'blockPieceGreen', 'blockPieceOrange'];
var currentPieces = [];
var draggedPiece = null;
var previewBlocks = [];
var particles = [];
var comboCounter = 0;
var lastClearTime = 0;
var comboMultiplier = 1;
var lastPlaceTime = 0;
var consecutivePlacements = 0;
var recentlyUsedShapes = []; // Track recently used shapes to avoid repetition
var isAIMode = false; // Track if AI is helping with suitable blocks
var aiModeBlocksGenerated = 0; // Count blocks generated in AI mode
var gridHadBlocks = false; // Track if grid ever had blocks placed
var cleanText = null; // Clean message display
var lastPlacedPieceColor = null; // Track color of last placed piece
// Add grid background to define the block placement area
var gridBg = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: GRID_OFFSET_X + GRID_SIZE * CELL_SIZE / 2,
y: GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE / 2
});
game.addChild(gridBg);
// Initialize grid
for (var i = 0; i < GRID_SIZE; i++) {
grid[i] = [];
gameGrid[i] = [];
for (var j = 0; j < GRID_SIZE; j++) {
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: GRID_OFFSET_X + j * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + i * CELL_SIZE + CELL_SIZE / 2
});
grid[i][j] = cell;
gameGrid[i][j] = null;
game.addChild(cell);
}
}
// Initialize UI
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var comboTxt = new Text2('', {
size: 60,
fill: 0xFFFF00
});
comboTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(comboTxt);
// Center screen combo text
var centerComboTxt = new Text2('', {
size: 120,
fill: 0xFFFF00
});
centerComboTxt.anchor.set(0.5, 0.5);
centerComboTxt.x = 1024;
centerComboTxt.y = 1366;
centerComboTxt.alpha = 0;
game.addChild(centerComboTxt);
// Piece panel
var piecePanel = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 1,
x: 1024,
y: 2732
});
game.addChild(piecePanel);
function showGameOverScreen() {
// Create game over overlay
var gameOverOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.9
});
game.addChild(gameOverOverlay);
// Game Over title
var gameOverTitle = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverTitle.anchor.set(0.5, 0.5);
gameOverTitle.x = 1024;
gameOverTitle.y = 1200;
game.addChild(gameOverTitle);
// Score display
var finalScoreText = new Text2('Score: ' + LK.getScore(), {
size: 80,
fill: 0xFFFFFF
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 1300;
game.addChild(finalScoreText);
// High Score display
var highScoreDisplayText = new Text2('High Score: ' + highScore, {
size: 60,
fill: 0xFFD700
});
highScoreDisplayText.anchor.set(0.5, 0.5);
highScoreDisplayText.x = 1024;
highScoreDisplayText.y = 1400;
game.addChild(highScoreDisplayText);
// New High Score display (if new high score)
var newHighScoreDisplay = null;
if (LK.getScore() >= highScore) {
newHighScoreDisplay = new Text2('NEW HIGH SCORE!', {
size: 60,
fill: 0xFF0000
});
newHighScoreDisplay.anchor.set(0.5, 0.5);
newHighScoreDisplay.x = 1024;
newHighScoreDisplay.y = 1500;
game.addChild(newHighScoreDisplay);
}
// Main Menu Button
var gameOverMenuButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: newHighScoreDisplay ? 1650 : 1550,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(gameOverMenuButton);
var gameOverMenuButtonText = new Text2('MAIN MENU', {
size: 60,
fill: 0xFFFFFF
});
gameOverMenuButtonText.anchor.set(0.5, 0.5);
gameOverMenuButtonText.x = 1024;
gameOverMenuButtonText.y = newHighScoreDisplay ? 1650 : 1550;
game.addChild(gameOverMenuButtonText);
gameOverMenuButton.down = function () {
// Clean up game over screen
gameOverOverlay.destroy();
gameOverTitle.destroy();
finalScoreText.destroy();
highScoreDisplayText.destroy();
if (newHighScoreDisplay) newHighScoreDisplay.destroy();
gameOverMenuButton.destroy();
gameOverMenuButtonText.destroy();
// Return to main menu
createMainMenu();
};
}
function createPauseMenu() {
// Semi-transparent overlay
pauseOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.8
});
game.addChild(pauseOverlay);
// Pause title
pauseTitle = new Text2('PAUSED', {
size: 100,
fill: 0xFFFFFF
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.x = 1024;
pauseTitle.y = 1200;
game.addChild(pauseTitle);
// Resume Button
resumeButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(resumeButton);
resumeButtonText = new Text2('RESUME', {
size: 60,
fill: 0xFFFFFF
});
resumeButtonText.anchor.set(0.5, 0.5);
resumeButtonText.x = 1024;
resumeButtonText.y = 1400;
game.addChild(resumeButtonText);
// Quit Button
quitButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(quitButton);
quitButtonText = new Text2('QUIT TO MENU', {
size: 50,
fill: 0xFFFFFF
});
quitButtonText.anchor.set(0.5, 0.5);
quitButtonText.x = 1024;
quitButtonText.y = 1600;
game.addChild(quitButtonText);
// Button handlers
resumeButton.down = function () {
resumeGame();
};
quitButton.down = function () {
quitToMenu();
};
}
function createMainMenu() {
// Title
menuTitle = new Text2('Block Exploder!', {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 800;
game.addChild(menuTitle);
// High Score
highScoreText = new Text2('High Score: ' + highScore, {
size: 60,
fill: 0xFFFF00
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 1024;
highScoreText.y = 1000;
game.addChild(highScoreText);
// Arcade Button
arcadeButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1300,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(arcadeButton);
arcadeButtonText = new Text2('ARCADE', {
size: 70,
fill: 0xFFFFFF
});
arcadeButtonText.anchor.set(0.5, 0.5);
arcadeButtonText.x = 1024;
arcadeButtonText.y = 1300;
game.addChild(arcadeButtonText);
arcadeButton.down = function () {
startGame();
};
// Challenge Button
challengeButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(challengeButton);
challengeButtonText = new Text2('CHALLENGE', {
size: 60,
fill: 0xFFFFFF
});
challengeButtonText.anchor.set(0.5, 0.5);
challengeButtonText.x = 1024;
challengeButtonText.y = 1500;
game.addChild(challengeButtonText);
challengeButton.down = function () {
showLevelSelection();
};
// Autoplay Test Button
var autoplayButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1700,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(autoplayButton);
var autoplayButtonText = new Text2('AUTOPLAY TEST', {
size: 50,
fill: 0xFFFF00
});
autoplayButtonText.anchor.set(0.5, 0.5);
autoplayButtonText.x = 1024;
autoplayButtonText.y = 1700;
game.addChild(autoplayButtonText);
autoplayButton.down = function () {
autoPlay = true;
startGame();
};
}
function showLevelSelection() {
// Hide main menu elements
if (menuTitle) menuTitle.destroy();
if (highScoreText) highScoreText.destroy();
if (arcadeButton) arcadeButton.destroy();
if (arcadeButtonText) arcadeButtonText.destroy();
if (challengeButton) challengeButton.destroy();
if (challengeButtonText) challengeButtonText.destroy();
// Reset references to null
menuTitle = null;
highScoreText = null;
arcadeButton = null;
arcadeButtonText = null;
challengeButton = null;
challengeButtonText = null;
// Create level selection title
levelSelectTitle = new Text2('SELECT LEVEL', {
size: 100,
fill: 0xFFFFFF
});
levelSelectTitle.anchor.set(0.5, 0.5);
levelSelectTitle.x = 1024;
levelSelectTitle.y = 600;
game.addChild(levelSelectTitle);
// Create level buttons (2 rows of 5)
levelButtons = [];
levelButtonTexts = [];
for (var i = 1; i <= 10; i++) {
var row = Math.floor((i - 1) / 5);
var col = (i - 1) % 5;
var buttonX = 424 + col * 240;
var buttonY = 1000 + row * 200;
var isUnlocked = i <= unlockedLevels;
var levelButton = LK.getAsset('pauseButton', {
anchorX: 0.5,
anchorY: 0.5,
x: buttonX,
y: buttonY,
scaleX: 2,
scaleY: 2
});
levelButton.tint = isUnlocked ? 0xFFFFFF : 0x666666;
game.addChild(levelButton);
levelButtons.push(levelButton);
var levelText = new Text2(i.toString(), {
size: 60,
fill: isUnlocked ? 0xFFFFFF : 0x888888
});
levelText.anchor.set(0.5, 0.5);
levelText.x = buttonX;
levelText.y = buttonY;
game.addChild(levelText);
levelButtonTexts.push(levelText);
// Add click handler for each level
if (isUnlocked) {
levelButton.levelNumber = i;
levelButton.down = function () {
startChallengeLevel(this.levelNumber);
};
}
}
// Create back button
backButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(backButton);
backButtonText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = 1024;
backButtonText.y = 1600;
game.addChild(backButtonText);
backButton.down = function () {
returnToMainMenu();
};
}
function startChallengeLevel(levelNumber) {
// Clean up level selection elements
if (levelSelectTitle) levelSelectTitle.destroy();
if (backButton) backButton.destroy();
if (backButtonText) backButtonText.destroy();
for (var i = 0; i < levelButtons.length; i++) {
if (levelButtons[i]) levelButtons[i].destroy();
}
for (var i = 0; i < levelButtonTexts.length; i++) {
if (levelButtonTexts[i]) levelButtonTexts[i].destroy();
}
levelButtons = [];
levelButtonTexts = [];
// Show level objectives screen
showLevelObjectives(levelNumber);
}
function returnToMainMenu() {
// Hide level selection elements
if (levelSelectTitle) levelSelectTitle.destroy();
if (backButton) backButton.destroy();
if (backButtonText) backButtonText.destroy();
for (var i = 0; i < levelButtons.length; i++) {
if (levelButtons[i]) levelButtons[i].destroy();
}
for (var i = 0; i < levelButtonTexts.length; i++) {
if (levelButtonTexts[i]) levelButtonTexts[i].destroy();
}
levelButtons = [];
levelButtonTexts = [];
// Reset references to null
levelSelectTitle = null;
backButton = null;
backButtonText = null;
// Show main menu again
createMainMenu();
}
function startGame() {
gameState = 'playing';
// Reset challenge mode if starting from arcade button
if (!isChallengeMode) {
currentChallengeLevel = 0;
challengeGoal = 0;
}
// Hide menu elements
if (menuTitle) menuTitle.destroy();
if (highScoreText) highScoreText.destroy();
if (arcadeButton) arcadeButton.destroy();
if (arcadeButtonText) arcadeButtonText.destroy();
if (challengeButton) challengeButton.destroy();
if (challengeButtonText) challengeButtonText.destroy();
// Clean up autoplay button if it exists
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.y === 1700 && child.x === 1024) {
child.destroy();
}
}
// Reset references to null
menuTitle = null;
highScoreText = null;
arcadeButton = null;
arcadeButtonText = null;
challengeButton = null;
challengeButtonText = null;
// Show game UI
scoreTxt.alpha = 1;
comboTxt.alpha = 1;
// Add game title below score text in arcade screen
var gameTitle = new Text2('Block Exploder!', {
size: 80,
fill: 0xFFFFFF
});
gameTitle.anchor.set(0.5, 0);
gameTitle.x = 1024;
gameTitle.y = 150;
game.addChild(gameTitle);
// Update score display for challenge mode
if (isChallengeMode) {
if (currentChallengeLevel === 2) {
scoreTxt.setText('Max Combo: 0 / ' + level2ComboTarget);
// Create timer text for Level 2
challengeTimerText = new Text2('Time: 45s', {
size: 60,
fill: 0xFF0000
});
challengeTimerText.anchor.set(0, 0);
LK.gui.topLeft.addChild(challengeTimerText);
challengeTimerText.x = 120; // Offset from top left corner
} else {
scoreTxt.setText('0 / ' + challengeGoal);
}
} else {
scoreTxt.setText('0');
}
// Show grid background and piece panel
gridBg.alpha = 1;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 1;
}
}
piecePanel.alpha = 1;
// Reset game state
LK.setScore(0);
scoreTxt.setText('0');
comboCounter = 0;
comboTxt.setText('');
// Clear grid
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j]) {
gameGrid[i][j].destroy();
gameGrid[i][j] = null;
}
}
}
// Initialize AI mode for new games
isAIMode = true;
aiModeBlocksGenerated = 0;
gridHadBlocks = false;
createNewPieces();
// Create pause button
pauseButton = LK.getAsset('pauseButton', {
anchorX: 1,
anchorY: 0,
x: 1948,
y: 100
});
game.addChild(pauseButton);
pauseButtonText = new Text2('||', {
size: 40,
fill: 0xFFFFFF
});
pauseButtonText.anchor.set(0.5, 0.5);
pauseButtonText.x = 1948 - 40;
pauseButtonText.y = 140;
game.addChild(pauseButtonText);
pauseButton.down = function () {
if (gameState === 'playing') {
gameState = 'paused';
createPauseMenu();
}
};
}
function resumeGame() {
gameState = 'playing';
// Hide pause menu elements
if (pauseOverlay) pauseOverlay.destroy();
if (pauseTitle) pauseTitle.destroy();
if (resumeButton) resumeButton.destroy();
if (resumeButtonText) resumeButtonText.destroy();
if (quitButton) quitButton.destroy();
if (quitButtonText) quitButtonText.destroy();
}
function quitToMenu() {
gameState = 'menu';
// Hide pause menu elements
if (pauseOverlay) pauseOverlay.destroy();
if (pauseTitle) pauseTitle.destroy();
if (resumeButton) resumeButton.destroy();
if (resumeButtonText) resumeButtonText.destroy();
if (quitButton) quitButton.destroy();
if (quitButtonText) quitButtonText.destroy();
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Clear grid
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j]) {
gameGrid[i][j].destroy();
gameGrid[i][j] = null;
}
}
}
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Hide pause button
if (pauseButton) pauseButton.destroy();
if (pauseButtonText) pauseButtonText.destroy();
// Clean up timer text if it exists
if (challengeTimerText) {
challengeTimerText.destroy();
challengeTimerText = null;
}
// Reset challenge mode
isChallengeMode = false;
currentChallengeLevel = 0;
challengeGoal = 0;
challengeTimer = 0;
challengeTimeLimit = 0;
level2MaxCombo = 0;
// Reset AI mode and clean tracking
isAIMode = false;
aiModeBlocksGenerated = 0;
// Reset autoplay
autoPlay = false;
autoPlayTimer = 0;
gridHadBlocks = false;
if (cleanText) {
cleanText.destroy();
cleanText = null;
}
createMainMenu();
}
function createNewPieces() {
// Clear existing pieces
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Create 3 new pieces with reduced repetition
for (var i = 0; i < 3; i++) {
var piece = new GamePiece();
var shapeIndex;
var attempts = 0;
var currentScore = LK.getScore();
if (isAIMode && aiModeBlocksGenerated < 20) {
// AI mode: provide suitable blocks for clearing - avoid very small single blocks, use manageable shapes
var aiSuitableShapes = [1, 1, 2, 2, 3, 4, 5, 6]; // 2-blocks, 3-blocks, verticals, and L shapes - no single blocks
shapeIndex = aiSuitableShapes[Math.floor(Math.random() * aiSuitableShapes.length)];
aiModeBlocksGenerated++;
} else if (currentScore < 500) {
// Very early game: heavily favor bigger, more satisfying shapes
var veryEasyShapes = [11, 12, 13, 14, 15, 16, 17, 18, 11, 12, 13, 14, 15, 16, 6, 10]; // Duplicate bigger shapes for higher probability
shapeIndex = veryEasyShapes[Math.floor(Math.random() * veryEasyShapes.length)];
} else if (currentScore < 1500) {
// Early game: mix of big shapes and medium shapes, still avoiding single blocks
var earlyShapes = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 11, 12, 13, 14, 15]; // Medium to large shapes
shapeIndex = earlyShapes[Math.floor(Math.random() * earlyShapes.length)];
} else if (currentScore < 3000) {
// Mid-early game: introduce some smaller pieces but keep it manageable
var midEasyShapes = [0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8]; // Small to medium shapes, some single blocks
shapeIndex = midEasyShapes[Math.floor(Math.random() * midEasyShapes.length)];
} else if (currentScore < 5000) {
// Mid game: balanced mix of all piece sizes
var midShapes = [0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9]; // Balanced mix, more single blocks
shapeIndex = midShapes[Math.floor(Math.random() * midShapes.length)];
} else if (currentScore < 8000) {
// Mid-late game: favor smaller pieces but still include medium ones
var midHardShapes = [0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // More small pieces
shapeIndex = midHardShapes[Math.floor(Math.random() * midHardShapes.length)];
} else {
// Late game: Try to avoid recently used shapes, all pieces available
do {
shapeIndex = Math.floor(Math.random() * pieceShapes.length);
attempts++;
} while (recentlyUsedShapes.indexOf(shapeIndex) !== -1 && attempts < 10);
}
// Add to recently used shapes and keep only last 4
recentlyUsedShapes.push(shapeIndex);
if (recentlyUsedShapes.length > 4) {
recentlyUsedShapes.shift();
}
piece.createShape(pieceShapes[shapeIndex]);
piece.x = 200 + i * 650; // Increased spacing to prevent overlapping
piece.y = 2550;
currentPieces.push(piece);
game.addChild(piece);
}
}
function getGridPosition(x, y) {
var gridX = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE);
var gridY = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE);
return {
x: gridX,
y: gridY
};
}
function canPlacePiece(piece, gridX, gridY) {
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var checkX = gridX + j;
var checkY = gridY + i;
if (checkX < 0 || checkX >= GRID_SIZE || checkY < 0 || checkY >= GRID_SIZE) {
return false;
}
if (gameGrid[checkY][checkX] !== null) {
return false;
}
}
}
}
return true;
}
function placePiece(piece, gridX, gridY) {
// Store the color of the piece being placed
lastPlacedPieceColor = piece.blocks[0].colorAsset;
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var placeX = gridX + j;
var placeY = gridY + i;
var block = new BlockPiece();
block.x = GRID_OFFSET_X + placeX * CELL_SIZE + CELL_SIZE / 2;
block.y = GRID_OFFSET_Y + placeY * CELL_SIZE + CELL_SIZE / 2;
block.setColor(piece.blocks[0].colorAsset);
block.gridX = placeX;
block.gridY = placeY;
gameGrid[placeY][placeX] = block;
game.addChild(block);
}
}
}
// Track consecutive placements for combo continuation
var currentTime = Date.now();
if (currentTime - lastPlaceTime < 3000) {
consecutivePlacements++;
} else {
consecutivePlacements = 1;
}
lastPlaceTime = currentTime;
// Track that grid has had blocks and disable AI mode after sufficient placements
if (!gridHadBlocks) {
gridHadBlocks = true;
}
if (isAIMode && aiModeBlocksGenerated >= 20) {
isAIMode = false; // Disable AI mode after initial guidance
}
LK.getSound('blockPlace').play();
}
function clearPreview() {
// Restore original block colors from stored values
for (var i = 0; i < originalBlockColors.length; i++) {
var colorData = originalBlockColors[i];
if (colorData.block && colorData.block.setColor) {
colorData.block.setColor(colorData.originalColor);
colorData.block.tint = 0xFFFFFF;
}
}
originalBlockColors = [];
for (var i = 0; i < previewBlocks.length; i++) {
previewBlocks[i].destroy();
}
previewBlocks = [];
}
function getCompletingLines(piece, gridX, gridY) {
var completingRows = [];
var completingCols = [];
var tempGrid = [];
// Create temporary grid copy
for (var i = 0; i < GRID_SIZE; i++) {
tempGrid[i] = [];
for (var j = 0; j < GRID_SIZE; j++) {
tempGrid[i][j] = gameGrid[i][j];
}
}
// Simulate placing the piece
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var placeX = gridX + j;
var placeY = gridY + i;
if (placeX >= 0 && placeX < GRID_SIZE && placeY >= 0 && placeY < GRID_SIZE) {
tempGrid[placeY][placeX] = true; // Mark as occupied
}
}
}
}
// Check for complete rows
for (var i = 0; i < GRID_SIZE; i++) {
var rowComplete = true;
for (var j = 0; j < GRID_SIZE; j++) {
if (tempGrid[i][j] === null) {
rowComplete = false;
break;
}
}
if (rowComplete) {
completingRows.push(i);
}
}
// Check for complete columns
for (var j = 0; j < GRID_SIZE; j++) {
var colComplete = true;
for (var i = 0; i < GRID_SIZE; i++) {
if (tempGrid[i][j] === null) {
colComplete = false;
break;
}
}
if (colComplete) {
completingCols.push(j);
}
}
return {
rows: completingRows,
cols: completingCols
};
}
// Add array to store original colors for preview restoration
var originalBlockColors = [];
function showPreview(piece, gridX, gridY) {
clearPreview();
if (canPlacePiece(piece, gridX, gridY)) {
// Get piece color for tinting exploding lines
var pieceColor = piece.blocks[0].colorAsset;
var colorMap = {
'blockPieceRed': 0xff6b6b,
'blockPieceCyan': 0x4ecdc4,
'blockPieceBlue': 0x45b7d1,
'blockPieceYellow': 0xf7dc6f,
'blockPiecePurple': 0xbb8fce,
'blockPieceGreen': 0x58d68d,
'blockPieceOrange': 0xf39c12
};
var tintColor = colorMap[pieceColor] || 0xffffff;
// Check which lines would complete
var completingLines = getCompletingLines(piece, gridX, gridY);
// Show preview for the piece itself
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var previewX = gridX + j;
var previewY = gridY + i;
var preview = LK.getAsset('previewBlock', {
anchorX: 0.5,
anchorY: 0.5,
x: GRID_OFFSET_X + previewX * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + previewY * CELL_SIZE + CELL_SIZE / 2,
alpha: 0.5,
tint: tintColor
});
previewBlocks.push(preview);
game.addChild(preview);
}
}
}
// Show preview for completing rows and columns - replace blocks with piece color
if (completingLines && (completingLines.rows.length > 0 || completingLines.cols.length > 0)) {
// Store original colors before changing them
originalBlockColors = [];
// Preview completing rows - replace existing blocks with piece color
for (var r = 0; r < completingLines.rows.length; r++) {
var row = completingLines.rows[r];
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[row][j] !== null) {
// Store original color before changing
originalBlockColors.push({
block: gameGrid[row][j],
originalColor: gameGrid[row][j].colorAsset
});
gameGrid[row][j].setColor(pieceColor);
}
}
}
// Preview completing columns - replace existing blocks with piece color
for (var c = 0; c < completingLines.cols.length; c++) {
var col = completingLines.cols[c];
for (var i = 0; i < GRID_SIZE; i++) {
if (gameGrid[i][col] !== null) {
// Check if we already stored this block's color to avoid duplicates
var alreadyStored = false;
for (var k = 0; k < originalBlockColors.length; k++) {
if (originalBlockColors[k].block === gameGrid[i][col]) {
alreadyStored = true;
break;
}
}
if (!alreadyStored) {
originalBlockColors.push({
block: gameGrid[i][col],
originalColor: gameGrid[i][col].colorAsset
});
}
gameGrid[i][col].setColor(pieceColor);
}
}
}
}
}
}
function checkLines() {
var clearedBlocks = [];
var completedRows = 0;
var completedCols = 0;
// Check for complete rows
for (var i = 0; i < GRID_SIZE; i++) {
var rowComplete = true;
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j] === null) {
rowComplete = false;
break;
}
}
if (rowComplete) {
completedRows++;
for (var j = 0; j < GRID_SIZE; j++) {
clearedBlocks.push({
x: j,
y: i
});
}
}
}
// Check for complete columns
for (var j = 0; j < GRID_SIZE; j++) {
var colComplete = true;
for (var i = 0; i < GRID_SIZE; i++) {
if (gameGrid[i][j] === null) {
colComplete = false;
break;
}
}
if (colComplete) {
completedCols++;
for (var i = 0; i < GRID_SIZE; i++) {
clearedBlocks.push({
x: j,
y: i
});
}
}
}
if (clearedBlocks.length > 0) {
clearBlocks(clearedBlocks, completedRows + completedCols);
return true;
}
return false;
}
function clearBlocks(blockPositions, lineCount) {
var currentTime = Date.now();
// Check if this clear is within 3 blocks of the last clear
var timeSinceLastClear = currentTime - lastClearTime;
if (timeSinceLastClear < 5000) {
// 5 seconds window for combo - continue from where we left off and add line count
comboCounter += lineCount || 1;
} else {
comboCounter = lineCount || 1;
}
lastClearTime = currentTime;
// Calculate multiplier based on combo
comboMultiplier = comboCounter;
var uniqueBlocks = [];
var seen = {};
for (var i = 0; i < blockPositions.length; i++) {
var pos = blockPositions[i];
var key = pos.x + "," + pos.y;
if (!seen[key] && gameGrid[pos.y][pos.x] !== null) {
seen[key] = true;
uniqueBlocks.push(pos);
}
}
// Change block appearance before exploding - use last placed piece color
for (var i = 0; i < uniqueBlocks.length; i++) {
var pos = uniqueBlocks[i];
var block = gameGrid[pos.y][pos.x];
if (block && lastPlacedPieceColor) {
// Replace the block with last placed piece color asset before exploding
block.setColor(lastPlacedPieceColor);
}
}
// Delay explosion effects slightly to show the new appearance
LK.setTimeout(function () {
for (var i = 0; i < uniqueBlocks.length; i++) {
var pos = uniqueBlocks[i];
var block = gameGrid[pos.y][pos.x];
if (block) {
createExplosion(block.x, block.y, lastPlacedPieceColor);
block.destroy();
gameGrid[pos.y][pos.x] = null;
}
}
}, 200);
// Screen effects
var flashColor = comboCounter > 3 ? 0xff0000 : 0xffffff;
var flashDuration = Math.min(comboCounter * 100 + 200, 800);
LK.effects.flashScreen(flashColor, flashDuration);
// Update score with combo multiplier
var basePoints = uniqueBlocks.length * 10;
var finalPoints = basePoints * comboMultiplier;
LK.setScore(LK.getScore() + Math.floor(finalPoints));
// Track max combo for Level 2
if (isChallengeMode && currentChallengeLevel === 2) {
if (comboCounter > level2MaxCombo) {
level2MaxCombo = comboCounter;
}
scoreTxt.setText('Max Combo: ' + level2MaxCombo + ' / ' + level2ComboTarget);
} else if (isChallengeMode) {
scoreTxt.setText(LK.getScore() + ' / ' + challengeGoal);
} else {
scoreTxt.setText(LK.getScore());
}
// Update combo display
if (comboCounter > 1) {
if (comboCounter >= 4) {
comboTxt.setText("PERFECT COMBO x" + comboCounter);
} else {
comboTxt.setText("COMBO x" + comboCounter);
}
// Show center screen combo animation
showCenterCombo(comboCounter);
// Animate combo text
tween(comboTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut
});
tween(comboTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut
});
// Add color flash to combo text
tween(comboTxt, {
tint: 0xFFFF00
}, {
duration: 100,
onFinish: function onFinish() {
tween(comboTxt, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
} else {
comboTxt.setText("");
// Show center screen combo for first clear too
showCenterCombo(comboCounter);
// Reset combo text color
comboTxt.tint = 0xFFFFFF;
}
// Play sound
if (comboCounter >= 4) {
LK.getSound('perfectCombo').play();
LK.getSound('lineClear').play();
} else if (comboCounter >= 2) {
LK.getSound('comboClear').play();
LK.getSound('lineClear').play();
} else {
LK.getSound('lineClear').play();
}
}
function showCenterCombo(combo) {
// Stop any existing combo animation
tween.stop(centerComboTxt);
// Set combo text
if (combo >= 4) {
centerComboTxt.setText("PERFECT COMBO!");
centerComboTxt.fill = 0xFF00FF; // Magenta for perfect combo
} else {
centerComboTxt.setText("COMBO x" + combo + "!");
centerComboTxt.fill = 0xFFFF00; // Yellow for regular combo
}
centerComboTxt.scaleX = 0.5;
centerComboTxt.scaleY = 0.5;
centerComboTxt.alpha = 0;
// Animate in
tween(centerComboTxt, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold for a moment then animate out
tween(centerComboTxt, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0
}, {
duration: 800,
easing: tween.easeIn
});
}
});
}
function createExplosion(x, y, color) {
var particleCount = Math.min(comboCounter * 2 + 5, 20);
for (var i = 0; i < particleCount; i++) {
var particle = new Particle();
particle.x = x;
particle.y = y;
particle.velocityX = (Math.random() - 0.5) * 10;
particle.velocityY = (Math.random() - 0.5) * 10 - 5;
particle.lifetime = 500 + Math.random() * 500;
particle.maxLifetime = particle.lifetime;
var particleGraphics = particle.children[0];
// Convert asset name to approximate color for particles
var colorMap = {
'blockPieceRed': 0xff6b6b,
'blockPieceCyan': 0x4ecdc4,
'blockPieceBlue': 0x45b7d1,
'blockPieceYellow': 0xf7dc6f,
'blockPiecePurple': 0xbb8fce,
'blockPieceGreen': 0x58d68d,
'blockPieceOrange': 0xf39c12
};
particleGraphics.tint = colorMap[color] || 0xffffff;
particles.push(particle);
game.addChild(particle);
}
}
function checkGridClean() {
if (!gridHadBlocks) return false; // Don't trigger clean if no blocks were ever placed
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j] !== null) {
return false; // Grid is not clean
}
}
}
return true; // Grid is completely clean
}
function showCleanMessage() {
// Create and show "CLEAN!" message
cleanText = new Text2('CLEAN!', {
size: 150,
fill: 0x00FF00
});
cleanText.anchor.set(0.5, 0.5);
cleanText.x = 1024;
cleanText.y = 1366;
cleanText.alpha = 0;
cleanText.scaleX = 0.5;
cleanText.scaleY = 0.5;
game.addChild(cleanText);
// Animate the clean message
tween(cleanText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold message briefly then fade out
tween(cleanText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
if (cleanText) {
cleanText.destroy();
cleanText = null;
}
}
});
}
});
// Add bonus points
LK.setScore(LK.getScore() + 500);
// Update score display
if (isChallengeMode && currentChallengeLevel === 2) {
scoreTxt.setText('Max Combo: ' + level2MaxCombo + ' / ' + level2ComboTarget);
} else if (isChallengeMode) {
scoreTxt.setText(LK.getScore() + ' / ' + challengeGoal);
} else {
scoreTxt.setText(LK.getScore());
}
// Play clean sound
LK.getSound('clean').play();
// Screen flash effect
LK.effects.flashScreen(0x00FF00, 1000);
}
function checkGameOver() {
for (var i = 0; i < currentPieces.length; i++) {
var piece = currentPieces[i];
var canPlace = false;
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (canPlacePiece(piece, col, row)) {
canPlace = true;
break;
}
}
if (canPlace) break;
}
if (canPlace) return false;
}
return true;
}
game.move = function (x, y, obj) {
if (gameState === 'playing' && draggedPiece) {
draggedPiece.x = x;
draggedPiece.y = y;
var gridPos = getGridPosition(x, y);
showPreview(draggedPiece, gridPos.x, gridPos.y);
}
};
game.down = function (x, y, obj) {
// No longer trigger pause menu from empty space taps
};
game.up = function (x, y, obj) {
if (gameState === 'playing' && draggedPiece) {
var gridPos = getGridPosition(x, y);
if (canPlacePiece(draggedPiece, gridPos.x, gridPos.y)) {
placePiece(draggedPiece, gridPos.x, gridPos.y);
// Remove piece from current pieces
var index = currentPieces.indexOf(draggedPiece);
if (index > -1) {
currentPieces.splice(index, 1);
}
draggedPiece.destroy();
// Check for clears
var blocksWereCleared = checkLines();
// Check if grid is completely clean after clearing blocks
if (blocksWereCleared && checkGridClean()) {
showCleanMessage();
}
// Create new pieces if all are used
if (currentPieces.length === 0) {
createNewPieces();
}
} else {
// Return piece to original position
tween(draggedPiece, {
x: draggedPiece.originalX,
y: draggedPiece.originalY,
alpha: 1
}, {
duration: 300
});
draggedPiece.isDragging = false;
}
clearPreview();
draggedPiece = null;
} else if (gameState === 'playing') {
// Clear preview if user touches somewhere else while not dragging
clearPreview();
}
};
// Auto-play mode for testing
var autoPlay = false;
var autoPlayTimer = 0;
game.update = function () {
// Clean up particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (particle.lifetime <= 0) {
particles.splice(i, 1);
}
}
// Auto-play logic for testing
if (autoPlay && gameState === 'playing' && currentPieces.length > 0) {
autoPlayTimer += 16.67; // 60fps frame time
if (autoPlayTimer >= 1000) {
// Every 1 second
autoPlayTimer = 0;
// Try to place the first available piece
var piece = currentPieces[0];
var placed = false;
// Try to find a valid placement
for (var row = 0; row < GRID_SIZE && !placed; row++) {
for (var col = 0; col < GRID_SIZE && !placed; col++) {
if (canPlacePiece(piece, col, row)) {
placePiece(piece, col, row);
// Remove piece from current pieces
var index = currentPieces.indexOf(piece);
if (index > -1) {
currentPieces.splice(index, 1);
}
piece.destroy();
// Check for clears
var blocksWereCleared = checkLines();
// Check if grid is completely clean after clearing blocks
if (blocksWereCleared && checkGridClean()) {
showCleanMessage();
}
// Create new pieces if all are used
if (currentPieces.length === 0) {
createNewPieces();
}
placed = true;
}
}
}
}
}
// Handle Level 2 timer countdown
if (gameState === 'playing' && isChallengeMode && currentChallengeLevel === 2 && challengeTimeLimit > 0) {
challengeTimer -= 16.67; // Subtract frame time (60fps)
var remainingSeconds = Math.ceil(challengeTimer / 1000);
if (challengeTimerText) {
challengeTimerText.setText('Time: ' + Math.max(0, remainingSeconds) + 's');
// Change color as time runs low
if (remainingSeconds <= 10) {
challengeTimerText.fill = 0xFF0000; // Red
} else if (remainingSeconds <= 20) {
challengeTimerText.fill = 0xFFFF00; // Yellow
}
}
// Check if time ran out
if (challengeTimer <= 0 && level2MaxCombo < level2ComboTarget) {
// Time's up and didn't achieve 2x combo - game over
if (challengeTimerText) {
challengeTimerText.destroy();
challengeTimerText = null;
}
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Reset challenge mode
isChallengeMode = false;
currentChallengeLevel = 0;
challengeGoal = 0;
challengeTimer = 0;
challengeTimeLimit = 0;
level2MaxCombo = 0;
gameState = 'menu';
createMainMenu();
return;
}
}
// Check challenge completion
var challengeCompleted = false;
if (gameState === 'playing' && isChallengeMode) {
if (currentChallengeLevel === 1 && LK.getScore() >= challengeGoal) {
challengeCompleted = true;
} else if (currentChallengeLevel === 2 && level2MaxCombo >= level2ComboTarget) {
challengeCompleted = true;
} else if (currentChallengeLevel > 2 && LK.getScore() >= challengeGoal) {
challengeCompleted = true;
}
}
if (challengeCompleted) {
// Challenge completed!
if (challengeTimerText) {
challengeTimerText.destroy();
challengeTimerText = null;
}
// Special handling for level 3 completion to unlock level 4
var wasLevel4Unlocked = false;
if (currentChallengeLevel === 3 && LK.getScore() >= 3000 && unlockedLevels < 4) {
unlockedLevels = 4;
storage.unlockedLevels = unlockedLevels;
wasLevel4Unlocked = true;
} else if (currentChallengeLevel >= unlockedLevels && currentChallengeLevel < 10) {
unlockedLevels = currentChallengeLevel + 1;
storage.unlockedLevels = unlockedLevels;
}
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Reset challenge mode
isChallengeMode = false;
currentChallengeLevel = 0;
challengeGoal = 0;
challengeTimer = 0;
challengeTimeLimit = 0;
level2MaxCombo = 0;
gameState = 'menu';
showCustomWinScreen(wasLevel4Unlocked);
return;
}
// Check game over
if (gameState === 'playing' && currentPieces.length > 0 && checkGameOver()) {
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Hide pause button
if (pauseButton) pauseButton.destroy();
if (pauseButtonText) pauseButtonText.destroy();
gameState = 'menu';
showGameOverScreen();
}
};
// Initialize game
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
// Hide grid background and grid initially
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
createMainMenu();
LK.playMusic('bgmusic');
function showCustomWinScreen(unlockedLevel4) {
// Store the completed level for button logic
var completedLevel = currentChallengeLevel;
var nextLevel = completedLevel + 1;
var hasNextLevel = nextLevel <= 10 && unlockedLevels >= nextLevel;
// Create win overlay
var winOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.9
});
game.addChild(winOverlay);
// Win title
var winTitle = new Text2('YOU WIN!', {
size: 120,
fill: 0x00FF00
});
winTitle.anchor.set(0.5, 0.5);
winTitle.x = 1024;
winTitle.y = unlockedLevel4 ? 1100 : 1200;
game.addChild(winTitle);
// Level 4 unlock congratulations message
var congratsText = null;
if (unlockedLevel4) {
congratsText = new Text2('CONGRATULATIONS!\nLEVEL 4 UNLOCKED!', {
size: 80,
fill: 0xFFD700
});
congratsText.anchor.set(0.5, 0.5);
congratsText.x = 1024;
congratsText.y = 1250;
game.addChild(congratsText);
}
// Next Level Button (show if there's a next level available)
var nextButton = null;
var nextButtonText = null;
var buttonYOffset = unlockedLevel4 ? 200 : 0;
if (hasNextLevel) {
nextButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400 + buttonYOffset,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(nextButton);
nextButtonText = new Text2('LEVEL ' + nextLevel, {
size: 60,
fill: 0xFFFFFF
});
nextButtonText.anchor.set(0.5, 0.5);
nextButtonText.x = 1024;
nextButtonText.y = 1400 + buttonYOffset;
game.addChild(nextButtonText);
nextButton.down = function () {
// Clean up win screen
winOverlay.destroy();
winTitle.destroy();
if (congratsText) congratsText.destroy();
nextButton.destroy();
nextButtonText.destroy();
mainMenuButton.destroy();
mainMenuButtonText.destroy();
// Start next level
startChallengeLevel(nextLevel);
};
}
// Main Menu Button
var mainMenuButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: (nextButton ? 1600 : 1400) + buttonYOffset,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(mainMenuButton);
var mainMenuButtonText = new Text2('MAIN MENU', {
size: 60,
fill: 0xFFFFFF
});
mainMenuButtonText.anchor.set(0.5, 0.5);
mainMenuButtonText.x = 1024;
mainMenuButtonText.y = (nextButton ? 1600 : 1400) + buttonYOffset;
game.addChild(mainMenuButtonText);
mainMenuButton.down = function () {
// Clean up win screen
winOverlay.destroy();
winTitle.destroy();
if (congratsText) congratsText.destroy();
if (nextButton) nextButton.destroy();
if (nextButtonText) nextButtonText.destroy();
mainMenuButton.destroy();
mainMenuButtonText.destroy();
// Return to main menu
createMainMenu();
};
}
function showLevelObjectives(levelNumber) {
// Create objectives overlay
var objectivesOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.9
});
game.addChild(objectivesOverlay);
// Level title
var levelTitle = new Text2('LEVEL ' + levelNumber, {
size: 120,
fill: 0xFFFFFF
});
levelTitle.anchor.set(0.5, 0.5);
levelTitle.x = 1024;
levelTitle.y = 1000;
game.addChild(levelTitle);
// Objective text based on level
var objectiveText = '';
if (levelNumber === 1) {
objectiveText = 'OBJECTIVE:\nScore 1000 points\nto complete this level';
} else if (levelNumber === 2) {
objectiveText = 'OBJECTIVE:\nAchieve a 2x combo\nwithin 45 seconds';
} else if (levelNumber === 3) {
objectiveText = 'OBJECTIVE:\nScore 3000 points\nto unlock Level 4';
} else if (levelNumber === 4) {
objectiveText = 'OBJECTIVE:\nScore 4000 points\nto complete this level';
} else {
objectiveText = 'OBJECTIVE:\nScore ' + levelNumber * 1000 + ' points\nto complete this level';
}
var objectiveDisplay = new Text2(objectiveText, {
size: 80,
fill: 0xFFFF00
});
objectiveDisplay.anchor.set(0.5, 0.5);
objectiveDisplay.x = 1024;
objectiveDisplay.y = 1300;
game.addChild(objectiveDisplay);
// Start button
var startButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(startButton);
var startButtonText = new Text2('START', {
size: 60,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 1024;
startButtonText.y = 1600;
game.addChild(startButtonText);
startButton.down = function () {
// Clean up objectives screen
objectivesOverlay.destroy();
levelTitle.destroy();
objectiveDisplay.destroy();
startButton.destroy();
startButtonText.destroy();
// Set challenge mode variables
isChallengeMode = true;
currentChallengeLevel = levelNumber;
if (levelNumber === 1) {
challengeGoal = 1000; // Level 1 = 1000 points
challengeTimeLimit = 0; // No time limit for level 1
} else if (levelNumber === 2) {
challengeGoal = 0; // Level 2 is not about points
challengeTimeLimit = 45000; // 45 seconds in milliseconds
level2MaxCombo = 0; // Reset max combo for level 2
} else {
challengeGoal = levelNumber * 1000; // Other levels
challengeTimeLimit = 0;
}
challengeTimer = challengeTimeLimit; // Set initial timer
startGame();
};
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BlockPiece = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('blockPieceRed', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.colorAsset = 'blockPieceRed';
self.setColor = function (assetName) {
self.colorAsset = assetName;
self.removeChild(graphics);
graphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
};
self.setExplodingAsset = function (assetId) {
self.removeChild(graphics);
graphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
var GamePiece = Container.expand(function () {
var self = Container.call(this);
self.blocks = [];
self.shape = [];
self.isDragging = false;
self.canPlace = false;
self.createShape = function (shapeData) {
self.shape = shapeData;
// Pick one color for the entire piece
var pieceColor = pieceColors[Math.floor(Math.random() * pieceColors.length)];
for (var i = 0; i < shapeData.length; i++) {
for (var j = 0; j < shapeData[i].length; j++) {
if (shapeData[i][j] === 1) {
var block = new BlockPiece();
block.x = j * 176; // Updated to match new CELL_SIZE
block.y = i * 176; // Updated to match new CELL_SIZE
block.setColor(pieceColor);
self.blocks.push(block);
self.addChild(block);
}
}
}
};
self.down = function (x, y, obj) {
if (!self.isDragging) {
self.isDragging = true;
draggedPiece = self;
self.originalX = self.x;
self.originalY = self.y;
self.alpha = 0.7;
}
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.lifetime = 1000;
self.maxLifetime = 1000;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += 0.5; // gravity
self.lifetime -= 16.67; // 60fps
var alpha = self.lifetime / self.maxLifetime;
graphics.alpha = alpha;
if (self.lifetime <= 0) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0a0a
});
/****
* Game Code
****/
// Increased by 35% (160 * 1.35)
// Increased by 35% (160 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
// Increased by 35% (120 * 1.35)
var GRID_SIZE = 9;
var CELL_SIZE = 176; // Increased by 35% (130 * 1.35)
var GRID_OFFSET_X = 1024 - GRID_SIZE * CELL_SIZE / 2; // Center grid horizontally
var GRID_OFFSET_Y = 1366 - GRID_SIZE * CELL_SIZE / 2; // Center grid vertically
var gameState = 'menu'; // 'menu', 'playing', or 'paused'
var highScore = storage.highScore || 0;
var unlockedLevels = storage.unlockedLevels || 1;
var currentChallengeLevel = 0;
var challengeGoal = 0;
var isChallengeMode = false;
var challengeTimer = 0;
var challengeTimeLimit = 0;
var challengeTimerText = null;
var level2ComboTarget = 2;
var level2MaxCombo = 0;
var playButton = null;
var playButtonText = null;
var arcadeButton = null;
var arcadeButtonText = null;
var challengeButton = null;
var challengeButtonText = null;
var levelSelectTitle = null;
var levelButtons = [];
var levelButtonTexts = [];
var backButton = null;
var backButtonText = null;
var menuTitle = null;
var highScoreText = null;
var pauseOverlay = null;
var pauseTitle = null;
var quitButton = null;
var quitButtonText = null;
var resumeButton = null;
var resumeButtonText = null;
var pauseButton = null;
var pauseButtonText = null;
var grid = [];
var gameGrid = [];
var pieceShapes = [[[1]],
// Single block
[[1, 1]],
// Line 2
[[1, 1, 1]],
// Line 3
[[1], [1]],
// Vertical 2
[[1], [1], [1]],
// Vertical 3
[[1, 1], [1, 1]],
// Square 2x2
[[1, 1, 1], [1, 0, 0]],
// L shape
[[1, 1, 1], [0, 0, 1]],
// Reverse L
[[1, 1, 0], [0, 1, 1]],
// Z shape
[[0, 1, 0], [1, 1, 1]],
// T shape
[[1, 1, 1], [1, 1, 1], [1, 1, 1]],
// 3x3 square
[[1, 1], [1, 1], [1, 1]],
// 2x3 rectangle
// Bigger satisfying shapes for early game
[[1, 1, 1, 1]],
// Line 4
[[1], [1], [1], [1]],
// Vertical 4
[[1, 1, 1], [1, 1, 1]],
// 3x2 rectangle
[[1, 1], [1, 1], [1, 1], [1, 1]],
// 2x4 rectangle
[[1, 1, 1, 1], [1, 1, 1, 1]],
// 4x2 rectangle
[[1, 1, 1], [1, 1, 1], [1, 1, 1]],
// 3x3 square
[[1, 1, 1, 1], [1, 0, 0, 0]],
// Big L shape
[[1, 1, 1, 1], [0, 0, 0, 1]]
// Big reverse L
];
var pieceColors = ['blockPieceRed', 'blockPieceCyan', 'blockPieceBlue', 'blockPieceYellow', 'blockPiecePurple', 'blockPieceGreen', 'blockPieceOrange'];
var currentPieces = [];
var draggedPiece = null;
var previewBlocks = [];
var particles = [];
var comboCounter = 0;
var lastClearTime = 0;
var comboMultiplier = 1;
var lastPlaceTime = 0;
var consecutivePlacements = 0;
var recentlyUsedShapes = []; // Track recently used shapes to avoid repetition
var isAIMode = false; // Track if AI is helping with suitable blocks
var aiModeBlocksGenerated = 0; // Count blocks generated in AI mode
var gridHadBlocks = false; // Track if grid ever had blocks placed
var cleanText = null; // Clean message display
var lastPlacedPieceColor = null; // Track color of last placed piece
// Add grid background to define the block placement area
var gridBg = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: GRID_OFFSET_X + GRID_SIZE * CELL_SIZE / 2,
y: GRID_OFFSET_Y + GRID_SIZE * CELL_SIZE / 2
});
game.addChild(gridBg);
// Initialize grid
for (var i = 0; i < GRID_SIZE; i++) {
grid[i] = [];
gameGrid[i] = [];
for (var j = 0; j < GRID_SIZE; j++) {
var cell = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
x: GRID_OFFSET_X + j * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + i * CELL_SIZE + CELL_SIZE / 2
});
grid[i][j] = cell;
gameGrid[i][j] = null;
game.addChild(cell);
}
}
// Initialize UI
var scoreTxt = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var comboTxt = new Text2('', {
size: 60,
fill: 0xFFFF00
});
comboTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(comboTxt);
// Center screen combo text
var centerComboTxt = new Text2('', {
size: 120,
fill: 0xFFFF00
});
centerComboTxt.anchor.set(0.5, 0.5);
centerComboTxt.x = 1024;
centerComboTxt.y = 1366;
centerComboTxt.alpha = 0;
game.addChild(centerComboTxt);
// Piece panel
var piecePanel = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 1,
x: 1024,
y: 2732
});
game.addChild(piecePanel);
function showGameOverScreen() {
// Create game over overlay
var gameOverOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.9
});
game.addChild(gameOverOverlay);
// Game Over title
var gameOverTitle = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverTitle.anchor.set(0.5, 0.5);
gameOverTitle.x = 1024;
gameOverTitle.y = 1200;
game.addChild(gameOverTitle);
// Score display
var finalScoreText = new Text2('Score: ' + LK.getScore(), {
size: 80,
fill: 0xFFFFFF
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 1300;
game.addChild(finalScoreText);
// High Score display
var highScoreDisplayText = new Text2('High Score: ' + highScore, {
size: 60,
fill: 0xFFD700
});
highScoreDisplayText.anchor.set(0.5, 0.5);
highScoreDisplayText.x = 1024;
highScoreDisplayText.y = 1400;
game.addChild(highScoreDisplayText);
// New High Score display (if new high score)
var newHighScoreDisplay = null;
if (LK.getScore() >= highScore) {
newHighScoreDisplay = new Text2('NEW HIGH SCORE!', {
size: 60,
fill: 0xFF0000
});
newHighScoreDisplay.anchor.set(0.5, 0.5);
newHighScoreDisplay.x = 1024;
newHighScoreDisplay.y = 1500;
game.addChild(newHighScoreDisplay);
}
// Main Menu Button
var gameOverMenuButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: newHighScoreDisplay ? 1650 : 1550,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(gameOverMenuButton);
var gameOverMenuButtonText = new Text2('MAIN MENU', {
size: 60,
fill: 0xFFFFFF
});
gameOverMenuButtonText.anchor.set(0.5, 0.5);
gameOverMenuButtonText.x = 1024;
gameOverMenuButtonText.y = newHighScoreDisplay ? 1650 : 1550;
game.addChild(gameOverMenuButtonText);
gameOverMenuButton.down = function () {
// Clean up game over screen
gameOverOverlay.destroy();
gameOverTitle.destroy();
finalScoreText.destroy();
highScoreDisplayText.destroy();
if (newHighScoreDisplay) newHighScoreDisplay.destroy();
gameOverMenuButton.destroy();
gameOverMenuButtonText.destroy();
// Return to main menu
createMainMenu();
};
}
function createPauseMenu() {
// Semi-transparent overlay
pauseOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.8
});
game.addChild(pauseOverlay);
// Pause title
pauseTitle = new Text2('PAUSED', {
size: 100,
fill: 0xFFFFFF
});
pauseTitle.anchor.set(0.5, 0.5);
pauseTitle.x = 1024;
pauseTitle.y = 1200;
game.addChild(pauseTitle);
// Resume Button
resumeButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(resumeButton);
resumeButtonText = new Text2('RESUME', {
size: 60,
fill: 0xFFFFFF
});
resumeButtonText.anchor.set(0.5, 0.5);
resumeButtonText.x = 1024;
resumeButtonText.y = 1400;
game.addChild(resumeButtonText);
// Quit Button
quitButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(quitButton);
quitButtonText = new Text2('QUIT TO MENU', {
size: 50,
fill: 0xFFFFFF
});
quitButtonText.anchor.set(0.5, 0.5);
quitButtonText.x = 1024;
quitButtonText.y = 1600;
game.addChild(quitButtonText);
// Button handlers
resumeButton.down = function () {
resumeGame();
};
quitButton.down = function () {
quitToMenu();
};
}
function createMainMenu() {
// Title
menuTitle = new Text2('Block Exploder!', {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 800;
game.addChild(menuTitle);
// High Score
highScoreText = new Text2('High Score: ' + highScore, {
size: 60,
fill: 0xFFFF00
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 1024;
highScoreText.y = 1000;
game.addChild(highScoreText);
// Arcade Button
arcadeButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1300,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(arcadeButton);
arcadeButtonText = new Text2('ARCADE', {
size: 70,
fill: 0xFFFFFF
});
arcadeButtonText.anchor.set(0.5, 0.5);
arcadeButtonText.x = 1024;
arcadeButtonText.y = 1300;
game.addChild(arcadeButtonText);
arcadeButton.down = function () {
startGame();
};
// Challenge Button
challengeButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(challengeButton);
challengeButtonText = new Text2('CHALLENGE', {
size: 60,
fill: 0xFFFFFF
});
challengeButtonText.anchor.set(0.5, 0.5);
challengeButtonText.x = 1024;
challengeButtonText.y = 1500;
game.addChild(challengeButtonText);
challengeButton.down = function () {
showLevelSelection();
};
// Autoplay Test Button
var autoplayButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1700,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(autoplayButton);
var autoplayButtonText = new Text2('AUTOPLAY TEST', {
size: 50,
fill: 0xFFFF00
});
autoplayButtonText.anchor.set(0.5, 0.5);
autoplayButtonText.x = 1024;
autoplayButtonText.y = 1700;
game.addChild(autoplayButtonText);
autoplayButton.down = function () {
autoPlay = true;
startGame();
};
}
function showLevelSelection() {
// Hide main menu elements
if (menuTitle) menuTitle.destroy();
if (highScoreText) highScoreText.destroy();
if (arcadeButton) arcadeButton.destroy();
if (arcadeButtonText) arcadeButtonText.destroy();
if (challengeButton) challengeButton.destroy();
if (challengeButtonText) challengeButtonText.destroy();
// Reset references to null
menuTitle = null;
highScoreText = null;
arcadeButton = null;
arcadeButtonText = null;
challengeButton = null;
challengeButtonText = null;
// Create level selection title
levelSelectTitle = new Text2('SELECT LEVEL', {
size: 100,
fill: 0xFFFFFF
});
levelSelectTitle.anchor.set(0.5, 0.5);
levelSelectTitle.x = 1024;
levelSelectTitle.y = 600;
game.addChild(levelSelectTitle);
// Create level buttons (2 rows of 5)
levelButtons = [];
levelButtonTexts = [];
for (var i = 1; i <= 10; i++) {
var row = Math.floor((i - 1) / 5);
var col = (i - 1) % 5;
var buttonX = 424 + col * 240;
var buttonY = 1000 + row * 200;
var isUnlocked = i <= unlockedLevels;
var levelButton = LK.getAsset('pauseButton', {
anchorX: 0.5,
anchorY: 0.5,
x: buttonX,
y: buttonY,
scaleX: 2,
scaleY: 2
});
levelButton.tint = isUnlocked ? 0xFFFFFF : 0x666666;
game.addChild(levelButton);
levelButtons.push(levelButton);
var levelText = new Text2(i.toString(), {
size: 60,
fill: isUnlocked ? 0xFFFFFF : 0x888888
});
levelText.anchor.set(0.5, 0.5);
levelText.x = buttonX;
levelText.y = buttonY;
game.addChild(levelText);
levelButtonTexts.push(levelText);
// Add click handler for each level
if (isUnlocked) {
levelButton.levelNumber = i;
levelButton.down = function () {
startChallengeLevel(this.levelNumber);
};
}
}
// Create back button
backButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 0.3,
scaleY: 0.3
});
game.addChild(backButton);
backButtonText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = 1024;
backButtonText.y = 1600;
game.addChild(backButtonText);
backButton.down = function () {
returnToMainMenu();
};
}
function startChallengeLevel(levelNumber) {
// Clean up level selection elements
if (levelSelectTitle) levelSelectTitle.destroy();
if (backButton) backButton.destroy();
if (backButtonText) backButtonText.destroy();
for (var i = 0; i < levelButtons.length; i++) {
if (levelButtons[i]) levelButtons[i].destroy();
}
for (var i = 0; i < levelButtonTexts.length; i++) {
if (levelButtonTexts[i]) levelButtonTexts[i].destroy();
}
levelButtons = [];
levelButtonTexts = [];
// Show level objectives screen
showLevelObjectives(levelNumber);
}
function returnToMainMenu() {
// Hide level selection elements
if (levelSelectTitle) levelSelectTitle.destroy();
if (backButton) backButton.destroy();
if (backButtonText) backButtonText.destroy();
for (var i = 0; i < levelButtons.length; i++) {
if (levelButtons[i]) levelButtons[i].destroy();
}
for (var i = 0; i < levelButtonTexts.length; i++) {
if (levelButtonTexts[i]) levelButtonTexts[i].destroy();
}
levelButtons = [];
levelButtonTexts = [];
// Reset references to null
levelSelectTitle = null;
backButton = null;
backButtonText = null;
// Show main menu again
createMainMenu();
}
function startGame() {
gameState = 'playing';
// Reset challenge mode if starting from arcade button
if (!isChallengeMode) {
currentChallengeLevel = 0;
challengeGoal = 0;
}
// Hide menu elements
if (menuTitle) menuTitle.destroy();
if (highScoreText) highScoreText.destroy();
if (arcadeButton) arcadeButton.destroy();
if (arcadeButtonText) arcadeButtonText.destroy();
if (challengeButton) challengeButton.destroy();
if (challengeButtonText) challengeButtonText.destroy();
// Clean up autoplay button if it exists
for (var i = game.children.length - 1; i >= 0; i--) {
var child = game.children[i];
if (child.y === 1700 && child.x === 1024) {
child.destroy();
}
}
// Reset references to null
menuTitle = null;
highScoreText = null;
arcadeButton = null;
arcadeButtonText = null;
challengeButton = null;
challengeButtonText = null;
// Show game UI
scoreTxt.alpha = 1;
comboTxt.alpha = 1;
// Add game title below score text in arcade screen
var gameTitle = new Text2('Block Exploder!', {
size: 80,
fill: 0xFFFFFF
});
gameTitle.anchor.set(0.5, 0);
gameTitle.x = 1024;
gameTitle.y = 150;
game.addChild(gameTitle);
// Update score display for challenge mode
if (isChallengeMode) {
if (currentChallengeLevel === 2) {
scoreTxt.setText('Max Combo: 0 / ' + level2ComboTarget);
// Create timer text for Level 2
challengeTimerText = new Text2('Time: 45s', {
size: 60,
fill: 0xFF0000
});
challengeTimerText.anchor.set(0, 0);
LK.gui.topLeft.addChild(challengeTimerText);
challengeTimerText.x = 120; // Offset from top left corner
} else {
scoreTxt.setText('0 / ' + challengeGoal);
}
} else {
scoreTxt.setText('0');
}
// Show grid background and piece panel
gridBg.alpha = 1;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 1;
}
}
piecePanel.alpha = 1;
// Reset game state
LK.setScore(0);
scoreTxt.setText('0');
comboCounter = 0;
comboTxt.setText('');
// Clear grid
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j]) {
gameGrid[i][j].destroy();
gameGrid[i][j] = null;
}
}
}
// Initialize AI mode for new games
isAIMode = true;
aiModeBlocksGenerated = 0;
gridHadBlocks = false;
createNewPieces();
// Create pause button
pauseButton = LK.getAsset('pauseButton', {
anchorX: 1,
anchorY: 0,
x: 1948,
y: 100
});
game.addChild(pauseButton);
pauseButtonText = new Text2('||', {
size: 40,
fill: 0xFFFFFF
});
pauseButtonText.anchor.set(0.5, 0.5);
pauseButtonText.x = 1948 - 40;
pauseButtonText.y = 140;
game.addChild(pauseButtonText);
pauseButton.down = function () {
if (gameState === 'playing') {
gameState = 'paused';
createPauseMenu();
}
};
}
function resumeGame() {
gameState = 'playing';
// Hide pause menu elements
if (pauseOverlay) pauseOverlay.destroy();
if (pauseTitle) pauseTitle.destroy();
if (resumeButton) resumeButton.destroy();
if (resumeButtonText) resumeButtonText.destroy();
if (quitButton) quitButton.destroy();
if (quitButtonText) quitButtonText.destroy();
}
function quitToMenu() {
gameState = 'menu';
// Hide pause menu elements
if (pauseOverlay) pauseOverlay.destroy();
if (pauseTitle) pauseTitle.destroy();
if (resumeButton) resumeButton.destroy();
if (resumeButtonText) resumeButtonText.destroy();
if (quitButton) quitButton.destroy();
if (quitButtonText) quitButtonText.destroy();
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Clear grid
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j]) {
gameGrid[i][j].destroy();
gameGrid[i][j] = null;
}
}
}
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Hide pause button
if (pauseButton) pauseButton.destroy();
if (pauseButtonText) pauseButtonText.destroy();
// Clean up timer text if it exists
if (challengeTimerText) {
challengeTimerText.destroy();
challengeTimerText = null;
}
// Reset challenge mode
isChallengeMode = false;
currentChallengeLevel = 0;
challengeGoal = 0;
challengeTimer = 0;
challengeTimeLimit = 0;
level2MaxCombo = 0;
// Reset AI mode and clean tracking
isAIMode = false;
aiModeBlocksGenerated = 0;
// Reset autoplay
autoPlay = false;
autoPlayTimer = 0;
gridHadBlocks = false;
if (cleanText) {
cleanText.destroy();
cleanText = null;
}
createMainMenu();
}
function createNewPieces() {
// Clear existing pieces
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Create 3 new pieces with reduced repetition
for (var i = 0; i < 3; i++) {
var piece = new GamePiece();
var shapeIndex;
var attempts = 0;
var currentScore = LK.getScore();
if (isAIMode && aiModeBlocksGenerated < 20) {
// AI mode: provide suitable blocks for clearing - avoid very small single blocks, use manageable shapes
var aiSuitableShapes = [1, 1, 2, 2, 3, 4, 5, 6]; // 2-blocks, 3-blocks, verticals, and L shapes - no single blocks
shapeIndex = aiSuitableShapes[Math.floor(Math.random() * aiSuitableShapes.length)];
aiModeBlocksGenerated++;
} else if (currentScore < 500) {
// Very early game: heavily favor bigger, more satisfying shapes
var veryEasyShapes = [11, 12, 13, 14, 15, 16, 17, 18, 11, 12, 13, 14, 15, 16, 6, 10]; // Duplicate bigger shapes for higher probability
shapeIndex = veryEasyShapes[Math.floor(Math.random() * veryEasyShapes.length)];
} else if (currentScore < 1500) {
// Early game: mix of big shapes and medium shapes, still avoiding single blocks
var earlyShapes = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 11, 12, 13, 14, 15]; // Medium to large shapes
shapeIndex = earlyShapes[Math.floor(Math.random() * earlyShapes.length)];
} else if (currentScore < 3000) {
// Mid-early game: introduce some smaller pieces but keep it manageable
var midEasyShapes = [0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8]; // Small to medium shapes, some single blocks
shapeIndex = midEasyShapes[Math.floor(Math.random() * midEasyShapes.length)];
} else if (currentScore < 5000) {
// Mid game: balanced mix of all piece sizes
var midShapes = [0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9]; // Balanced mix, more single blocks
shapeIndex = midShapes[Math.floor(Math.random() * midShapes.length)];
} else if (currentScore < 8000) {
// Mid-late game: favor smaller pieces but still include medium ones
var midHardShapes = [0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // More small pieces
shapeIndex = midHardShapes[Math.floor(Math.random() * midHardShapes.length)];
} else {
// Late game: Try to avoid recently used shapes, all pieces available
do {
shapeIndex = Math.floor(Math.random() * pieceShapes.length);
attempts++;
} while (recentlyUsedShapes.indexOf(shapeIndex) !== -1 && attempts < 10);
}
// Add to recently used shapes and keep only last 4
recentlyUsedShapes.push(shapeIndex);
if (recentlyUsedShapes.length > 4) {
recentlyUsedShapes.shift();
}
piece.createShape(pieceShapes[shapeIndex]);
piece.x = 200 + i * 650; // Increased spacing to prevent overlapping
piece.y = 2550;
currentPieces.push(piece);
game.addChild(piece);
}
}
function getGridPosition(x, y) {
var gridX = Math.floor((x - GRID_OFFSET_X) / CELL_SIZE);
var gridY = Math.floor((y - GRID_OFFSET_Y) / CELL_SIZE);
return {
x: gridX,
y: gridY
};
}
function canPlacePiece(piece, gridX, gridY) {
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var checkX = gridX + j;
var checkY = gridY + i;
if (checkX < 0 || checkX >= GRID_SIZE || checkY < 0 || checkY >= GRID_SIZE) {
return false;
}
if (gameGrid[checkY][checkX] !== null) {
return false;
}
}
}
}
return true;
}
function placePiece(piece, gridX, gridY) {
// Store the color of the piece being placed
lastPlacedPieceColor = piece.blocks[0].colorAsset;
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var placeX = gridX + j;
var placeY = gridY + i;
var block = new BlockPiece();
block.x = GRID_OFFSET_X + placeX * CELL_SIZE + CELL_SIZE / 2;
block.y = GRID_OFFSET_Y + placeY * CELL_SIZE + CELL_SIZE / 2;
block.setColor(piece.blocks[0].colorAsset);
block.gridX = placeX;
block.gridY = placeY;
gameGrid[placeY][placeX] = block;
game.addChild(block);
}
}
}
// Track consecutive placements for combo continuation
var currentTime = Date.now();
if (currentTime - lastPlaceTime < 3000) {
consecutivePlacements++;
} else {
consecutivePlacements = 1;
}
lastPlaceTime = currentTime;
// Track that grid has had blocks and disable AI mode after sufficient placements
if (!gridHadBlocks) {
gridHadBlocks = true;
}
if (isAIMode && aiModeBlocksGenerated >= 20) {
isAIMode = false; // Disable AI mode after initial guidance
}
LK.getSound('blockPlace').play();
}
function clearPreview() {
// Restore original block colors from stored values
for (var i = 0; i < originalBlockColors.length; i++) {
var colorData = originalBlockColors[i];
if (colorData.block && colorData.block.setColor) {
colorData.block.setColor(colorData.originalColor);
colorData.block.tint = 0xFFFFFF;
}
}
originalBlockColors = [];
for (var i = 0; i < previewBlocks.length; i++) {
previewBlocks[i].destroy();
}
previewBlocks = [];
}
function getCompletingLines(piece, gridX, gridY) {
var completingRows = [];
var completingCols = [];
var tempGrid = [];
// Create temporary grid copy
for (var i = 0; i < GRID_SIZE; i++) {
tempGrid[i] = [];
for (var j = 0; j < GRID_SIZE; j++) {
tempGrid[i][j] = gameGrid[i][j];
}
}
// Simulate placing the piece
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var placeX = gridX + j;
var placeY = gridY + i;
if (placeX >= 0 && placeX < GRID_SIZE && placeY >= 0 && placeY < GRID_SIZE) {
tempGrid[placeY][placeX] = true; // Mark as occupied
}
}
}
}
// Check for complete rows
for (var i = 0; i < GRID_SIZE; i++) {
var rowComplete = true;
for (var j = 0; j < GRID_SIZE; j++) {
if (tempGrid[i][j] === null) {
rowComplete = false;
break;
}
}
if (rowComplete) {
completingRows.push(i);
}
}
// Check for complete columns
for (var j = 0; j < GRID_SIZE; j++) {
var colComplete = true;
for (var i = 0; i < GRID_SIZE; i++) {
if (tempGrid[i][j] === null) {
colComplete = false;
break;
}
}
if (colComplete) {
completingCols.push(j);
}
}
return {
rows: completingRows,
cols: completingCols
};
}
// Add array to store original colors for preview restoration
var originalBlockColors = [];
function showPreview(piece, gridX, gridY) {
clearPreview();
if (canPlacePiece(piece, gridX, gridY)) {
// Get piece color for tinting exploding lines
var pieceColor = piece.blocks[0].colorAsset;
var colorMap = {
'blockPieceRed': 0xff6b6b,
'blockPieceCyan': 0x4ecdc4,
'blockPieceBlue': 0x45b7d1,
'blockPieceYellow': 0xf7dc6f,
'blockPiecePurple': 0xbb8fce,
'blockPieceGreen': 0x58d68d,
'blockPieceOrange': 0xf39c12
};
var tintColor = colorMap[pieceColor] || 0xffffff;
// Check which lines would complete
var completingLines = getCompletingLines(piece, gridX, gridY);
// Show preview for the piece itself
for (var i = 0; i < piece.shape.length; i++) {
for (var j = 0; j < piece.shape[i].length; j++) {
if (piece.shape[i][j] === 1) {
var previewX = gridX + j;
var previewY = gridY + i;
var preview = LK.getAsset('previewBlock', {
anchorX: 0.5,
anchorY: 0.5,
x: GRID_OFFSET_X + previewX * CELL_SIZE + CELL_SIZE / 2,
y: GRID_OFFSET_Y + previewY * CELL_SIZE + CELL_SIZE / 2,
alpha: 0.5,
tint: tintColor
});
previewBlocks.push(preview);
game.addChild(preview);
}
}
}
// Show preview for completing rows and columns - replace blocks with piece color
if (completingLines && (completingLines.rows.length > 0 || completingLines.cols.length > 0)) {
// Store original colors before changing them
originalBlockColors = [];
// Preview completing rows - replace existing blocks with piece color
for (var r = 0; r < completingLines.rows.length; r++) {
var row = completingLines.rows[r];
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[row][j] !== null) {
// Store original color before changing
originalBlockColors.push({
block: gameGrid[row][j],
originalColor: gameGrid[row][j].colorAsset
});
gameGrid[row][j].setColor(pieceColor);
}
}
}
// Preview completing columns - replace existing blocks with piece color
for (var c = 0; c < completingLines.cols.length; c++) {
var col = completingLines.cols[c];
for (var i = 0; i < GRID_SIZE; i++) {
if (gameGrid[i][col] !== null) {
// Check if we already stored this block's color to avoid duplicates
var alreadyStored = false;
for (var k = 0; k < originalBlockColors.length; k++) {
if (originalBlockColors[k].block === gameGrid[i][col]) {
alreadyStored = true;
break;
}
}
if (!alreadyStored) {
originalBlockColors.push({
block: gameGrid[i][col],
originalColor: gameGrid[i][col].colorAsset
});
}
gameGrid[i][col].setColor(pieceColor);
}
}
}
}
}
}
function checkLines() {
var clearedBlocks = [];
var completedRows = 0;
var completedCols = 0;
// Check for complete rows
for (var i = 0; i < GRID_SIZE; i++) {
var rowComplete = true;
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j] === null) {
rowComplete = false;
break;
}
}
if (rowComplete) {
completedRows++;
for (var j = 0; j < GRID_SIZE; j++) {
clearedBlocks.push({
x: j,
y: i
});
}
}
}
// Check for complete columns
for (var j = 0; j < GRID_SIZE; j++) {
var colComplete = true;
for (var i = 0; i < GRID_SIZE; i++) {
if (gameGrid[i][j] === null) {
colComplete = false;
break;
}
}
if (colComplete) {
completedCols++;
for (var i = 0; i < GRID_SIZE; i++) {
clearedBlocks.push({
x: j,
y: i
});
}
}
}
if (clearedBlocks.length > 0) {
clearBlocks(clearedBlocks, completedRows + completedCols);
return true;
}
return false;
}
function clearBlocks(blockPositions, lineCount) {
var currentTime = Date.now();
// Check if this clear is within 3 blocks of the last clear
var timeSinceLastClear = currentTime - lastClearTime;
if (timeSinceLastClear < 5000) {
// 5 seconds window for combo - continue from where we left off and add line count
comboCounter += lineCount || 1;
} else {
comboCounter = lineCount || 1;
}
lastClearTime = currentTime;
// Calculate multiplier based on combo
comboMultiplier = comboCounter;
var uniqueBlocks = [];
var seen = {};
for (var i = 0; i < blockPositions.length; i++) {
var pos = blockPositions[i];
var key = pos.x + "," + pos.y;
if (!seen[key] && gameGrid[pos.y][pos.x] !== null) {
seen[key] = true;
uniqueBlocks.push(pos);
}
}
// Change block appearance before exploding - use last placed piece color
for (var i = 0; i < uniqueBlocks.length; i++) {
var pos = uniqueBlocks[i];
var block = gameGrid[pos.y][pos.x];
if (block && lastPlacedPieceColor) {
// Replace the block with last placed piece color asset before exploding
block.setColor(lastPlacedPieceColor);
}
}
// Delay explosion effects slightly to show the new appearance
LK.setTimeout(function () {
for (var i = 0; i < uniqueBlocks.length; i++) {
var pos = uniqueBlocks[i];
var block = gameGrid[pos.y][pos.x];
if (block) {
createExplosion(block.x, block.y, lastPlacedPieceColor);
block.destroy();
gameGrid[pos.y][pos.x] = null;
}
}
}, 200);
// Screen effects
var flashColor = comboCounter > 3 ? 0xff0000 : 0xffffff;
var flashDuration = Math.min(comboCounter * 100 + 200, 800);
LK.effects.flashScreen(flashColor, flashDuration);
// Update score with combo multiplier
var basePoints = uniqueBlocks.length * 10;
var finalPoints = basePoints * comboMultiplier;
LK.setScore(LK.getScore() + Math.floor(finalPoints));
// Track max combo for Level 2
if (isChallengeMode && currentChallengeLevel === 2) {
if (comboCounter > level2MaxCombo) {
level2MaxCombo = comboCounter;
}
scoreTxt.setText('Max Combo: ' + level2MaxCombo + ' / ' + level2ComboTarget);
} else if (isChallengeMode) {
scoreTxt.setText(LK.getScore() + ' / ' + challengeGoal);
} else {
scoreTxt.setText(LK.getScore());
}
// Update combo display
if (comboCounter > 1) {
if (comboCounter >= 4) {
comboTxt.setText("PERFECT COMBO x" + comboCounter);
} else {
comboTxt.setText("COMBO x" + comboCounter);
}
// Show center screen combo animation
showCenterCombo(comboCounter);
// Animate combo text
tween(comboTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut
});
tween(comboTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut
});
// Add color flash to combo text
tween(comboTxt, {
tint: 0xFFFF00
}, {
duration: 100,
onFinish: function onFinish() {
tween(comboTxt, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
} else {
comboTxt.setText("");
// Show center screen combo for first clear too
showCenterCombo(comboCounter);
// Reset combo text color
comboTxt.tint = 0xFFFFFF;
}
// Play sound
if (comboCounter >= 4) {
LK.getSound('perfectCombo').play();
LK.getSound('lineClear').play();
} else if (comboCounter >= 2) {
LK.getSound('comboClear').play();
LK.getSound('lineClear').play();
} else {
LK.getSound('lineClear').play();
}
}
function showCenterCombo(combo) {
// Stop any existing combo animation
tween.stop(centerComboTxt);
// Set combo text
if (combo >= 4) {
centerComboTxt.setText("PERFECT COMBO!");
centerComboTxt.fill = 0xFF00FF; // Magenta for perfect combo
} else {
centerComboTxt.setText("COMBO x" + combo + "!");
centerComboTxt.fill = 0xFFFF00; // Yellow for regular combo
}
centerComboTxt.scaleX = 0.5;
centerComboTxt.scaleY = 0.5;
centerComboTxt.alpha = 0;
// Animate in
tween(centerComboTxt, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold for a moment then animate out
tween(centerComboTxt, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0
}, {
duration: 800,
easing: tween.easeIn
});
}
});
}
function createExplosion(x, y, color) {
var particleCount = Math.min(comboCounter * 2 + 5, 20);
for (var i = 0; i < particleCount; i++) {
var particle = new Particle();
particle.x = x;
particle.y = y;
particle.velocityX = (Math.random() - 0.5) * 10;
particle.velocityY = (Math.random() - 0.5) * 10 - 5;
particle.lifetime = 500 + Math.random() * 500;
particle.maxLifetime = particle.lifetime;
var particleGraphics = particle.children[0];
// Convert asset name to approximate color for particles
var colorMap = {
'blockPieceRed': 0xff6b6b,
'blockPieceCyan': 0x4ecdc4,
'blockPieceBlue': 0x45b7d1,
'blockPieceYellow': 0xf7dc6f,
'blockPiecePurple': 0xbb8fce,
'blockPieceGreen': 0x58d68d,
'blockPieceOrange': 0xf39c12
};
particleGraphics.tint = colorMap[color] || 0xffffff;
particles.push(particle);
game.addChild(particle);
}
}
function checkGridClean() {
if (!gridHadBlocks) return false; // Don't trigger clean if no blocks were ever placed
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
if (gameGrid[i][j] !== null) {
return false; // Grid is not clean
}
}
}
return true; // Grid is completely clean
}
function showCleanMessage() {
// Create and show "CLEAN!" message
cleanText = new Text2('CLEAN!', {
size: 150,
fill: 0x00FF00
});
cleanText.anchor.set(0.5, 0.5);
cleanText.x = 1024;
cleanText.y = 1366;
cleanText.alpha = 0;
cleanText.scaleX = 0.5;
cleanText.scaleY = 0.5;
game.addChild(cleanText);
// Animate the clean message
tween(cleanText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold message briefly then fade out
tween(cleanText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1000,
easing: tween.easeIn,
onFinish: function onFinish() {
if (cleanText) {
cleanText.destroy();
cleanText = null;
}
}
});
}
});
// Add bonus points
LK.setScore(LK.getScore() + 500);
// Update score display
if (isChallengeMode && currentChallengeLevel === 2) {
scoreTxt.setText('Max Combo: ' + level2MaxCombo + ' / ' + level2ComboTarget);
} else if (isChallengeMode) {
scoreTxt.setText(LK.getScore() + ' / ' + challengeGoal);
} else {
scoreTxt.setText(LK.getScore());
}
// Play clean sound
LK.getSound('clean').play();
// Screen flash effect
LK.effects.flashScreen(0x00FF00, 1000);
}
function checkGameOver() {
for (var i = 0; i < currentPieces.length; i++) {
var piece = currentPieces[i];
var canPlace = false;
for (var row = 0; row < GRID_SIZE; row++) {
for (var col = 0; col < GRID_SIZE; col++) {
if (canPlacePiece(piece, col, row)) {
canPlace = true;
break;
}
}
if (canPlace) break;
}
if (canPlace) return false;
}
return true;
}
game.move = function (x, y, obj) {
if (gameState === 'playing' && draggedPiece) {
draggedPiece.x = x;
draggedPiece.y = y;
var gridPos = getGridPosition(x, y);
showPreview(draggedPiece, gridPos.x, gridPos.y);
}
};
game.down = function (x, y, obj) {
// No longer trigger pause menu from empty space taps
};
game.up = function (x, y, obj) {
if (gameState === 'playing' && draggedPiece) {
var gridPos = getGridPosition(x, y);
if (canPlacePiece(draggedPiece, gridPos.x, gridPos.y)) {
placePiece(draggedPiece, gridPos.x, gridPos.y);
// Remove piece from current pieces
var index = currentPieces.indexOf(draggedPiece);
if (index > -1) {
currentPieces.splice(index, 1);
}
draggedPiece.destroy();
// Check for clears
var blocksWereCleared = checkLines();
// Check if grid is completely clean after clearing blocks
if (blocksWereCleared && checkGridClean()) {
showCleanMessage();
}
// Create new pieces if all are used
if (currentPieces.length === 0) {
createNewPieces();
}
} else {
// Return piece to original position
tween(draggedPiece, {
x: draggedPiece.originalX,
y: draggedPiece.originalY,
alpha: 1
}, {
duration: 300
});
draggedPiece.isDragging = false;
}
clearPreview();
draggedPiece = null;
} else if (gameState === 'playing') {
// Clear preview if user touches somewhere else while not dragging
clearPreview();
}
};
// Auto-play mode for testing
var autoPlay = false;
var autoPlayTimer = 0;
game.update = function () {
// Clean up particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (particle.lifetime <= 0) {
particles.splice(i, 1);
}
}
// Auto-play logic for testing
if (autoPlay && gameState === 'playing' && currentPieces.length > 0) {
autoPlayTimer += 16.67; // 60fps frame time
if (autoPlayTimer >= 1000) {
// Every 1 second
autoPlayTimer = 0;
// Try to place the first available piece
var piece = currentPieces[0];
var placed = false;
// Try to find a valid placement
for (var row = 0; row < GRID_SIZE && !placed; row++) {
for (var col = 0; col < GRID_SIZE && !placed; col++) {
if (canPlacePiece(piece, col, row)) {
placePiece(piece, col, row);
// Remove piece from current pieces
var index = currentPieces.indexOf(piece);
if (index > -1) {
currentPieces.splice(index, 1);
}
piece.destroy();
// Check for clears
var blocksWereCleared = checkLines();
// Check if grid is completely clean after clearing blocks
if (blocksWereCleared && checkGridClean()) {
showCleanMessage();
}
// Create new pieces if all are used
if (currentPieces.length === 0) {
createNewPieces();
}
placed = true;
}
}
}
}
}
// Handle Level 2 timer countdown
if (gameState === 'playing' && isChallengeMode && currentChallengeLevel === 2 && challengeTimeLimit > 0) {
challengeTimer -= 16.67; // Subtract frame time (60fps)
var remainingSeconds = Math.ceil(challengeTimer / 1000);
if (challengeTimerText) {
challengeTimerText.setText('Time: ' + Math.max(0, remainingSeconds) + 's');
// Change color as time runs low
if (remainingSeconds <= 10) {
challengeTimerText.fill = 0xFF0000; // Red
} else if (remainingSeconds <= 20) {
challengeTimerText.fill = 0xFFFF00; // Yellow
}
}
// Check if time ran out
if (challengeTimer <= 0 && level2MaxCombo < level2ComboTarget) {
// Time's up and didn't achieve 2x combo - game over
if (challengeTimerText) {
challengeTimerText.destroy();
challengeTimerText = null;
}
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Reset challenge mode
isChallengeMode = false;
currentChallengeLevel = 0;
challengeGoal = 0;
challengeTimer = 0;
challengeTimeLimit = 0;
level2MaxCombo = 0;
gameState = 'menu';
createMainMenu();
return;
}
}
// Check challenge completion
var challengeCompleted = false;
if (gameState === 'playing' && isChallengeMode) {
if (currentChallengeLevel === 1 && LK.getScore() >= challengeGoal) {
challengeCompleted = true;
} else if (currentChallengeLevel === 2 && level2MaxCombo >= level2ComboTarget) {
challengeCompleted = true;
} else if (currentChallengeLevel > 2 && LK.getScore() >= challengeGoal) {
challengeCompleted = true;
}
}
if (challengeCompleted) {
// Challenge completed!
if (challengeTimerText) {
challengeTimerText.destroy();
challengeTimerText = null;
}
// Special handling for level 3 completion to unlock level 4
var wasLevel4Unlocked = false;
if (currentChallengeLevel === 3 && LK.getScore() >= 3000 && unlockedLevels < 4) {
unlockedLevels = 4;
storage.unlockedLevels = unlockedLevels;
wasLevel4Unlocked = true;
} else if (currentChallengeLevel >= unlockedLevels && currentChallengeLevel < 10) {
unlockedLevels = currentChallengeLevel + 1;
storage.unlockedLevels = unlockedLevels;
}
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Reset challenge mode
isChallengeMode = false;
currentChallengeLevel = 0;
challengeGoal = 0;
challengeTimer = 0;
challengeTimeLimit = 0;
level2MaxCombo = 0;
gameState = 'menu';
showCustomWinScreen(wasLevel4Unlocked);
return;
}
// Check game over
if (gameState === 'playing' && currentPieces.length > 0 && checkGameOver()) {
// Update high score if needed
var currentScore = LK.getScore();
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Clear game elements
for (var i = 0; i < currentPieces.length; i++) {
currentPieces[i].destroy();
}
currentPieces = [];
// Hide game UI
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
centerComboTxt.alpha = 0;
// Hide grid background and piece panel
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
// Hide pause button
if (pauseButton) pauseButton.destroy();
if (pauseButtonText) pauseButtonText.destroy();
gameState = 'menu';
showGameOverScreen();
}
};
// Initialize game
scoreTxt.alpha = 0;
comboTxt.alpha = 0;
// Hide grid background and grid initially
gridBg.alpha = 0;
for (var i = 0; i < GRID_SIZE; i++) {
for (var j = 0; j < GRID_SIZE; j++) {
grid[i][j].alpha = 0;
}
}
piecePanel.alpha = 0;
createMainMenu();
LK.playMusic('bgmusic');
function showCustomWinScreen(unlockedLevel4) {
// Store the completed level for button logic
var completedLevel = currentChallengeLevel;
var nextLevel = completedLevel + 1;
var hasNextLevel = nextLevel <= 10 && unlockedLevels >= nextLevel;
// Create win overlay
var winOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.9
});
game.addChild(winOverlay);
// Win title
var winTitle = new Text2('YOU WIN!', {
size: 120,
fill: 0x00FF00
});
winTitle.anchor.set(0.5, 0.5);
winTitle.x = 1024;
winTitle.y = unlockedLevel4 ? 1100 : 1200;
game.addChild(winTitle);
// Level 4 unlock congratulations message
var congratsText = null;
if (unlockedLevel4) {
congratsText = new Text2('CONGRATULATIONS!\nLEVEL 4 UNLOCKED!', {
size: 80,
fill: 0xFFD700
});
congratsText.anchor.set(0.5, 0.5);
congratsText.x = 1024;
congratsText.y = 1250;
game.addChild(congratsText);
}
// Next Level Button (show if there's a next level available)
var nextButton = null;
var nextButtonText = null;
var buttonYOffset = unlockedLevel4 ? 200 : 0;
if (hasNextLevel) {
nextButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400 + buttonYOffset,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(nextButton);
nextButtonText = new Text2('LEVEL ' + nextLevel, {
size: 60,
fill: 0xFFFFFF
});
nextButtonText.anchor.set(0.5, 0.5);
nextButtonText.x = 1024;
nextButtonText.y = 1400 + buttonYOffset;
game.addChild(nextButtonText);
nextButton.down = function () {
// Clean up win screen
winOverlay.destroy();
winTitle.destroy();
if (congratsText) congratsText.destroy();
nextButton.destroy();
nextButtonText.destroy();
mainMenuButton.destroy();
mainMenuButtonText.destroy();
// Start next level
startChallengeLevel(nextLevel);
};
}
// Main Menu Button
var mainMenuButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: (nextButton ? 1600 : 1400) + buttonYOffset,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(mainMenuButton);
var mainMenuButtonText = new Text2('MAIN MENU', {
size: 60,
fill: 0xFFFFFF
});
mainMenuButtonText.anchor.set(0.5, 0.5);
mainMenuButtonText.x = 1024;
mainMenuButtonText.y = (nextButton ? 1600 : 1400) + buttonYOffset;
game.addChild(mainMenuButtonText);
mainMenuButton.down = function () {
// Clean up win screen
winOverlay.destroy();
winTitle.destroy();
if (congratsText) congratsText.destroy();
if (nextButton) nextButton.destroy();
if (nextButtonText) nextButtonText.destroy();
mainMenuButton.destroy();
mainMenuButtonText.destroy();
// Return to main menu
createMainMenu();
};
}
function showLevelObjectives(levelNumber) {
// Create objectives overlay
var objectivesOverlay = LK.getAsset('gridBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2,
scaleY: 2.5,
alpha: 0.9
});
game.addChild(objectivesOverlay);
// Level title
var levelTitle = new Text2('LEVEL ' + levelNumber, {
size: 120,
fill: 0xFFFFFF
});
levelTitle.anchor.set(0.5, 0.5);
levelTitle.x = 1024;
levelTitle.y = 1000;
game.addChild(levelTitle);
// Objective text based on level
var objectiveText = '';
if (levelNumber === 1) {
objectiveText = 'OBJECTIVE:\nScore 1000 points\nto complete this level';
} else if (levelNumber === 2) {
objectiveText = 'OBJECTIVE:\nAchieve a 2x combo\nwithin 45 seconds';
} else if (levelNumber === 3) {
objectiveText = 'OBJECTIVE:\nScore 3000 points\nto unlock Level 4';
} else if (levelNumber === 4) {
objectiveText = 'OBJECTIVE:\nScore 4000 points\nto complete this level';
} else {
objectiveText = 'OBJECTIVE:\nScore ' + levelNumber * 1000 + ' points\nto complete this level';
}
var objectiveDisplay = new Text2(objectiveText, {
size: 80,
fill: 0xFFFF00
});
objectiveDisplay.anchor.set(0.5, 0.5);
objectiveDisplay.x = 1024;
objectiveDisplay.y = 1300;
game.addChild(objectiveDisplay);
// Start button
var startButton = LK.getAsset('piecePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1600,
scaleX: 0.4,
scaleY: 0.25
});
game.addChild(startButton);
var startButtonText = new Text2('START', {
size: 60,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 1024;
startButtonText.y = 1600;
game.addChild(startButtonText);
startButton.down = function () {
// Clean up objectives screen
objectivesOverlay.destroy();
levelTitle.destroy();
objectiveDisplay.destroy();
startButton.destroy();
startButtonText.destroy();
// Set challenge mode variables
isChallengeMode = true;
currentChallengeLevel = levelNumber;
if (levelNumber === 1) {
challengeGoal = 1000; // Level 1 = 1000 points
challengeTimeLimit = 0; // No time limit for level 1
} else if (levelNumber === 2) {
challengeGoal = 0; // Level 2 is not about points
challengeTimeLimit = 45000; // 45 seconds in milliseconds
level2MaxCombo = 0; // Reset max combo for level 2
} else {
challengeGoal = levelNumber * 1000; // Other levels
challengeTimeLimit = 0;
}
challengeTimer = challengeTimeLimit; // Set initial timer
startGame();
};
}