User prompt
But that time I can't grab and drag figure. Pls give that function back. And pls fix that bug "some figures stuck on the way and can't get to the ground" ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Super. Pls pls slow the speed a little bit. And increase a little bit sensitivity of screen that allow to grab a figure. But it's not bad yet. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Delete cursor asset but enhance the sensitivity of the screen that when player puts finger on figure that means player can control and drag that figure immediately. And add different shapes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Cursor & Drag System (English for Developer) 1. Cursor (Pointer) System Display a visible arrow/cursor on the screen. The player can hover the cursor over a shape and then click-drag to move it. 2. Precise Drag Targeting Only the shape directly under the cursor should be draggable. Ensure smooth transition between selecting one shape and another. 3. Natural Falling All shapes should continue falling until they touch the ground or another block. Gravity should handle the fall completely, without interruption. 4. Drag Behavior Once the player releases a shape, they should be able to immediately drag another one without delay. Dragging should feel responsive and fluid, with no input lag or blocking ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
🔧 Updated Fixes & Rules (Technical English) 1. Touch-Based Movement Only A shape should only move if the user is directly touching and dragging it. No shape should move indirectly (e.g., when another shape is touched). 2. Collision Rule Shapes must not pass through each other. Implement solid collision detection to prevent overlap during movement and stacking. 3. Bomb Shape Design The bomb block must visually resemble a bomb (e.g., circular with a fuse or explosion symbol). It should be clearly different from regular blocks in shape and appearance. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Grid & Match Rules (For Developer) 1. Grid Size The game grid is 24 columns (left to right) and 24 rows (top to bottom). 2. Same-Color Match Rule If 4 or more blocks of the same color align horizontally, they should explode and give points. 3. Two-Color Match Rule If 8 or more blocks are aligned horizontally but consist of exactly two colors, they should also explode. 4. Three-Color Full Line Rule If a full row (24 blocks) contains three or more different colors, the entire row should explode and give bonus points. --- 🛠 Bug Fix Command (Touch Control Issue) Fix Touch Targeting: When dragging a shape, only the selected shape should move. Prevent cases where dragging one figure moves a different one. Ensure correct input mapping between touch position and selected object. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: setTimeout is not a function' in or related to this line: 'setTimeout(function () {' Line Number: 391
User prompt
🔧 Additional Gameplay Instructions (2D) 1. Continuous Side Movement On touch/drag, the shape moves continuously left or right across the screen, not just one cell per tap. 2. Increase Drag Speed Make the swipe/drag movement faster and smoother. 3. Random Spawn Positions Shapes should spawn not only from the top center, but also randomly from different horizontal positions at the top. --- 🧩 2D Tetris Shapes (Reference) I-shape: #### O-shape: ## ## T-shape: ### # L-shape: # # ## J-shape: # # ## S-shape: ## ## Z-shape: ## ## ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'stability is not defined' in or related to this line: 'stabilityText.setText('Stability: ' + Math.round(stability) + '%');' Line Number: 248
User prompt
🧱 Core Mechanics (Short Version) 1. Shape Types: Not only squares – use Tetris-style shapes (L, T, Z, I, etc.). 2. Stacking: Shapes fall from the top and stack when they land on others or the ground. 3. No Mid-Fall Control: Once falling, shapes can't be moved or rotated. 4. Row Clearing: Full horizontal rows disappear and give points. 5. Speed Variations: Some shapes fall faster for challenge. 6. Special Blocks: 🎁 Gift = Bonus points 💣 Bomb = Destroys nearby blocks 🔄 Wildcard = Fills nearest gap ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
But I think you can add that function that allows player to grab and drag figures. And increase different figures. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Pls add that skills that . Player can move figures with finger tap and add more complex figures. And then when blocks and figures fall down they don't disappear. A Player with his hands can move, flip to right, left, forward and backward and etc.
Code edit (1 edits merged)
Please save this source code
User prompt
ShapeStack 3D
Initial prompt
🧱 Game Concept: ShapeStack 3D Genre: 3D Physics-Based Construction Game --- 🌟 Overview: ShapeStack 3D is a creative 3D construction game where geometric figures (blocks) fall slowly from the sky. The player must catch and place them to build larger structures. The more symmetrical, balanced, and space-efficient the structure is, the more stable it becomes — unlocking the ability to build higher, stronger, and more complex objects. --- 🧩 Core Gameplay: Falling Shapes: Random 3D Lego-like shapes (cubes, pyramids, L-blocks, etc.) slowly fall from the sky. Catch and Place: The player collects the falling shapes before they hit the ground. Build Freely: The player can stack shapes on top of each other to construct buildings, sculptures, machines, or any object. Stability Score: The game analyzes how well-aligned, symmetrical, and dense the structure is. More balanced structures = higher stability More stability = more build layers unlocked --- 🎯 Objective: Build the tallest and most stable structure possible. Unlock new building materials and tools. Earn bonus points for symmetry, creative use of space, and aesthetic design. --- 🧠 Strategy Mechanics: Gameplay Element Effect Symmetry Increases stability and score Gaps/Holes Reduces structural integrity Perfect Fit Bonus points and glowing effects Tilting/Unstable build Can cause collapse unless reinforced Advanced tools Unlock after score thresholds (e.g., glue, rotation tool) --- 🖥️ User Interface: Left Panel: Current Score and Stability Meter Right Panel: Inventory of caught shapes Bottom: Controls (Rotate, Move, Place) Top: Height indicator / Build progress --- ⚙️ Technical Notes (for development): Engine: Unity (preferred for 3D and physics) Language: C# Camera: Free orbit or third-person top-down view Physics: Realistic gravity and collision (Rigidbody, colliders) Build Grid: Optional snap-to-grid feature for clean construction Save System: Ability to save and reload structures --- 🔓 Bonus Features (optional): Sandbox mode: Unlimited shapes and no time limit Challenge mode: Build within specific time or block limits Multiplayer mode: Co-build or compete for stability Creative gallery: Share your best creations online
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var GridBlock = Container.expand(function (shapeType) { var self = Container.call(this); self.shapeType = shapeType; self.gridX = 0; self.gridY = 0; var shapeGraphic = self.attachAsset(shapeType, { anchorX: 0.5, anchorY: 0.5 }); return self; }); var PlacedShape = Container.expand(function (shapeType) { var self = Container.call(this); self.shapeType = shapeType; self.isStable = true; self.isDragging = false; var shapeGraphic = self.attachAsset(shapeType, { anchorX: 0.5, anchorY: 0.5 }); self.rotate = function () { // Smooth rotation animation tween(shapeGraphic, { rotation: shapeGraphic.rotation + Math.PI / 2 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { calculateStability(); updateStability(); } }); }; self.down = function (x, y, obj) { self.isDragging = true; selectedShape = self; }; self.up = function (x, y, obj) { self.isDragging = false; if (selectedShape === self) { selectedShape = null; } calculateStability(); updateStability(); }; return self; }); var TetrisBlock = Container.expand(function (shapeType) { var self = Container.call(this); self.shapeType = shapeType; self.fallSpeed = Math.random() < 0.3 ? 4 : 2; // Some blocks fall faster self.isSpecial = specialBlocks.indexOf(shapeType) !== -1; self.gridX = Math.floor(gridWidth / 2); self.gridY = 0; self.lastMoveTime = 0; var shapeGraphic = self.attachAsset(shapeType, { anchorX: 0.5, anchorY: 0.5 }); // Position on grid self.x = self.gridX * blockSize + blockSize / 2 + (2048 - gridWidth * blockSize) / 2; self.y = self.gridY * blockSize + blockSize / 2 + 200; self.update = function () { var currentTime = LK.ticks; if (currentTime - self.lastMoveTime > 60 / self.fallSpeed) { self.lastMoveTime = currentTime; // Check if can move down if (self.gridY + 1 < gridHeight && !grid[self.gridY + 1][self.gridX]) { self.gridY++; self.y = self.gridY * blockSize + blockSize / 2 + 200; } else { // Place block in grid self.placeInGrid(); } } }; self.placeInGrid = function () { if (self.gridY >= 0 && self.gridY < gridHeight && self.gridX >= 0 && self.gridX < gridWidth) { grid[self.gridY][self.gridX] = self.shapeType; // Create visual block in grid var placedBlock = new GridBlock(self.shapeType); placedBlock.gridX = self.gridX; placedBlock.gridY = self.gridY; placedBlock.x = self.x; placedBlock.y = self.y; placedShapes.push(placedBlock); game.addChild(placedBlock); LK.getSound('place').play(); score += 10; updateScore(); checkForLines(); // Handle special blocks if (self.isSpecial) { handleSpecialBlock(self.shapeType, self.gridX, self.gridY); } } // Remove from falling shapes var index = fallingShapes.indexOf(self); if (index > -1) { fallingShapes.splice(index, 1); } self.destroy(); // Check game over if (self.gridY <= 1) { LK.showGameOver(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var fallingShapes = []; var placedShapes = []; var score = 0; var linesCleared = 0; var level = 1; var groundY = 2400; var shapeTypes = ['lblock', 'tblock', 'zblock', 'iblock']; var specialBlocks = ['gift', 'bomb', 'wildcard']; var gridWidth = 10; var gridHeight = 20; var blockSize = 80; var grid = []; // Initialize empty grid for (var row = 0; row < gridHeight; row++) { grid[row] = []; for (var col = 0; col < gridWidth; col++) { grid[row][col] = null; } } var nextShapeTimer = 0; var shapeSpawnDelay = 120; // 2 seconds at 60fps // Create ground var ground = game.addChild(LK.getAsset('ground', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: groundY })); // Create UI elements var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 120; scoreText.y = 120; LK.gui.topLeft.addChild(scoreText); var stabilityText = new Text2('Stability: 100%', { size: 40, fill: 0xFFFFFF }); stabilityText.anchor.set(0, 0); stabilityText.x = 120; stabilityText.y = 180; LK.gui.topLeft.addChild(stabilityText); // Stability bar background var stabilityBarBg = LK.getAsset('stabilityBarBg', { anchorX: 0, anchorY: 0, x: 120, y: 220 }); LK.gui.topLeft.addChild(stabilityBarBg); // Stability bar var stabilityBar = LK.getAsset('stabilityBar', { anchorX: 0, anchorY: 0, x: 120, y: 220 }); LK.gui.topLeft.addChild(stabilityBar); var inventoryText = new Text2('Inventory: 0', { size: 40, fill: 0xFFFFFF }); inventoryText.anchor.set(1, 0); LK.gui.topRight.addChild(inventoryText); var heightText = new Text2('Height: 0', { size: 40, fill: 0xFFFFFF }); heightText.anchor.set(0, 0); heightText.x = 120; heightText.y = 260; LK.gui.topLeft.addChild(heightText); function spawnShape() { var shapeType; // 10% chance for special blocks if (Math.random() < 0.1) { shapeType = specialBlocks[Math.floor(Math.random() * specialBlocks.length)]; } else { shapeType = shapeTypes[Math.floor(Math.random() * shapeTypes.length)]; } var shape = new TetrisBlock(shapeType); fallingShapes.push(shape); game.addChild(shape); } function updateInventoryDisplay() { inventoryText.setText('Inventory: ' + inventory.length); } function updateScore() { scoreText.setText('Score: ' + score); } function updateStability() { stabilityText.setText('Stability: ' + Math.round(stability) + '%'); var stabilityPercent = Math.max(0, Math.min(1, stability / 100)); stabilityBar.width = 300 * stabilityPercent; // Color based on stability if (stability > 70) { stabilityBar.tint = 0x00FF00; // Green } else if (stability > 30) { stabilityBar.tint = 0xFFFF00; // Yellow } else { stabilityBar.tint = 0xFF0000; // Red } } function updateHeight() { var currentHeight = 0; for (var i = 0; i < placedShapes.length; i++) { var shapeBottom = placedShapes[i].y + 40; var heightFromGround = groundY - shapeBottom; if (heightFromGround > currentHeight) { currentHeight = heightFromGround; } } maxHeight = Math.max(maxHeight, currentHeight); heightText.setText('Height: ' + Math.round(maxHeight / 80)); } function checkForLines() { var linesToClear = []; // Check each row for complete lines for (var row = 0; row < gridHeight; row++) { var isComplete = true; for (var col = 0; col < gridWidth; col++) { if (!grid[row][col]) { isComplete = false; break; } } if (isComplete) { linesToClear.push(row); } } // Clear complete lines if (linesToClear.length > 0) { clearLines(linesToClear); } } function clearLines(lines) { LK.getSound('lineClear').play(); var pointsPerLine = 100; score += lines.length * pointsPerLine; updateScore(); // Remove visual blocks and clear grid for (var i = 0; i < lines.length; i++) { var row = lines[i]; // Remove visual blocks for (var j = placedShapes.length - 1; j >= 0; j--) { var block = placedShapes[j]; if (block.gridY === row) { tween(block, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 300, onFinish: function onFinish() { block.destroy(); } }); placedShapes.splice(j, 1); } } // Clear grid row for (var col = 0; col < gridWidth; col++) { grid[row][col] = null; } } // Drop remaining blocks down setTimeout(function () { dropBlocks(lines); }, 400); } function dropBlocks(clearedLines) { for (var row = gridHeight - 1; row >= 0; row--) { var dropDistance = 0; for (var i = 0; i < clearedLines.length; i++) { if (clearedLines[i] > row) { dropDistance++; } } if (dropDistance > 0) { // Move grid data for (var col = 0; col < gridWidth; col++) { if (grid[row][col]) { grid[row + dropDistance][col] = grid[row][col]; grid[row][col] = null; } } // Move visual blocks for (var j = 0; j < placedShapes.length; j++) { var block = placedShapes[j]; if (block.gridY === row) { block.gridY += dropDistance; tween(block, { y: block.gridY * blockSize + blockSize / 2 + 200 }, { duration: 200, easing: tween.easeOut }); } } } } } function handleSpecialBlock(type, gridX, gridY) { if (type === 'gift') { score += 50; updateScore(); LK.effects.flashObject(game, 0xffd700, 500); } else if (type === 'bomb') { LK.getSound('bomb').play(); // Destroy blocks in 3x3 area for (var row = Math.max(0, gridY - 1); row <= Math.min(gridHeight - 1, gridY + 1); row++) { for (var col = Math.max(0, gridX - 1); col <= Math.min(gridWidth - 1, gridX + 1); col++) { if (grid[row][col]) { grid[row][col] = null; // Remove visual block for (var i = placedShapes.length - 1; i >= 0; i--) { var block = placedShapes[i]; if (block.gridX === col && block.gridY === row) { tween(block, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, onFinish: function onFinish() { block.destroy(); } }); placedShapes.splice(i, 1); break; } } } } } } else if (type === 'wildcard') { // Fill nearest gap var nearestGap = findNearestGap(gridX, gridY); if (nearestGap) { grid[nearestGap.row][nearestGap.col] = 'cube'; var wildBlock = new GridBlock('cube'); wildBlock.gridX = nearestGap.col; wildBlock.gridY = nearestGap.row; wildBlock.x = nearestGap.col * blockSize + blockSize / 2 + (2048 - gridWidth * blockSize) / 2; wildBlock.y = nearestGap.row * blockSize + blockSize / 2 + 200; placedShapes.push(wildBlock); game.addChild(wildBlock); tween(wildBlock, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.bounceOut, onFinish: function onFinish() { tween(wildBlock, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); } } } function findNearestGap(startX, startY) { var searchRadius = 1; while (searchRadius < Math.max(gridWidth, gridHeight)) { for (var row = Math.max(0, startY - searchRadius); row <= Math.min(gridHeight - 1, startY + searchRadius); row++) { for (var col = Math.max(0, startX - searchRadius); col <= Math.min(gridWidth - 1, startX + searchRadius); col++) { if (!grid[row][col]) { return { row: row, col: col }; } } } searchRadius++; } return null; } function triggerCollapse() { LK.getSound('collapse').play(); // Remove unstable shapes for (var i = placedShapes.length - 1; i >= 0; i--) { if (Math.random() < 0.3) { // 30% chance each shape collapses var shape = placedShapes[i]; tween(shape, { alpha: 0, y: shape.y + 100 }, { duration: 500, onFinish: function onFinish() { shape.destroy(); } }); placedShapes.splice(i, 1); } } score = Math.max(0, score - 50); updateScore(); } function placeShape(shape, x, y) { if (inventory.length === 0) return; var inventoryShape = inventory.shift(); var placedShape = new PlacedShape(inventoryShape.shapeType); placedShape.x = x; placedShape.y = y; // Start with smaller scale for placement animation placedShape.scaleX = 0.5; placedShape.scaleY = 0.5; placedShape.alpha = 0.7; placedShapes.push(placedShape); game.addChild(placedShape); // Animate placement with smooth scaling tween(placedShape, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300, easing: tween.elasticOut }); LK.getSound('place').play(); score += 10; // Bonus for good placement var centerX = 1024; var distanceFromCenter = Math.abs(x - centerX); if (distanceFromCenter < 100) { score += 5; // Bonus for center placement // Add bonus glow effect for center placement tween(placedShape, { tint: 0xffff00 }, { duration: 500, onFinish: function onFinish() { tween(placedShape, { tint: 0xffffff }, { duration: 500 }); } }); } updateScore(); updateInventoryDisplay(); calculateStability(); updateHeight(); inventoryShape.destroy(); } // Event handlers game.down = function (x, y, obj) { var gameAreaLeft = (2048 - gridWidth * blockSize) / 2; var gameAreaRight = gameAreaLeft + gridWidth * blockSize; // Only handle touches in game area if (x >= gameAreaLeft && x <= gameAreaRight && fallingShapes.length > 0) { var activeBlock = fallingShapes[0]; var targetGridX = Math.floor((x - gameAreaLeft) / blockSize); targetGridX = Math.max(0, Math.min(gridWidth - 1, targetGridX)); // Move block to target column if possible if (!grid[activeBlock.gridY][targetGridX]) { activeBlock.gridX = targetGridX; tween(activeBlock, { x: targetGridX * blockSize + blockSize / 2 + gameAreaLeft }, { duration: 200, easing: tween.easeOut }); } } }; game.move = function (x, y, obj) { if (selectedShape && selectedShape.isDragging) { // Smooth tween to new position for better feel tween(selectedShape, { x: x, y: Math.max(groundY - 40, y) }, { duration: 100, easing: tween.easeOut }); } }; game.update = function () { // Spawn new shapes nextShapeTimer++; if (nextShapeTimer >= shapeSpawnDelay) { spawnShape(); nextShapeTimer = 0; // Gradually increase spawn rate if (shapeSpawnDelay > 60) { shapeSpawnDelay -= 0.5; } } // Update stability periodically if (LK.ticks % 30 === 0) { calculateStability(); updateStability(); } // Check win condition if (maxHeight > 800) { // About 10 blocks high LK.showYouWin(); } // Check game over condition if (stability < 10 && placedShapes.length > 0) { LK.showGameOver(); } }; // Initialize display updateScore(); updateStability(); updateInventoryDisplay(); updateHeight();
===================================================================
--- original.js
+++ change.js
@@ -5,66 +5,17 @@
/****
* Classes
****/
-var FallingShape = Container.expand(function (shapeType) {
+var GridBlock = Container.expand(function (shapeType) {
var self = Container.call(this);
self.shapeType = shapeType;
- self.fallSpeed = 2;
- self.caught = false;
- self.placed = false;
+ self.gridX = 0;
+ self.gridY = 0;
var shapeGraphic = self.attachAsset(shapeType, {
anchorX: 0.5,
anchorY: 0.5
});
- // Add slight rotation for 3D effect
- shapeGraphic.rotation = Math.random() * 0.2 - 0.1;
- self.rotate = function () {
- shapeGraphic.rotation += Math.PI / 2;
- };
- self.update = function () {
- if (!self.caught && !self.placed) {
- self.y += self.fallSpeed;
- // Check if hit ground - don't destroy, just stop falling
- if (self.y > groundY - 40) {
- self.y = groundY - 40;
- self.placed = true;
- // Add to placed shapes when it hits ground
- var placedShape = new PlacedShape(self.shapeType);
- placedShape.x = self.x;
- placedShape.y = self.y;
- placedShapes.push(placedShape);
- game.addChild(placedShape);
- var index = fallingShapes.indexOf(self);
- if (index > -1) {
- fallingShapes.splice(index, 1);
- }
- self.destroy();
- calculateStability();
- updateHeight();
- }
- }
- };
- self["catch"] = function () {
- if (!self.caught) {
- self.caught = true;
- LK.getSound('catch').play();
- // Add catching animation
- tween(shapeGraphic, {
- scaleX: 1.2,
- scaleY: 1.2,
- alpha: 0.8
- }, {
- duration: 200,
- easing: tween.bounceOut,
- onFinish: function onFinish() {
- inventory.push(self);
- self.parent.removeChild(self);
- updateInventoryDisplay();
- }
- });
- }
- };
return self;
});
var PlacedShape = Container.expand(function (shapeType) {
var self = Container.call(this);
@@ -101,8 +52,70 @@
updateStability();
};
return self;
});
+var TetrisBlock = Container.expand(function (shapeType) {
+ var self = Container.call(this);
+ self.shapeType = shapeType;
+ self.fallSpeed = Math.random() < 0.3 ? 4 : 2; // Some blocks fall faster
+ self.isSpecial = specialBlocks.indexOf(shapeType) !== -1;
+ self.gridX = Math.floor(gridWidth / 2);
+ self.gridY = 0;
+ self.lastMoveTime = 0;
+ var shapeGraphic = self.attachAsset(shapeType, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Position on grid
+ self.x = self.gridX * blockSize + blockSize / 2 + (2048 - gridWidth * blockSize) / 2;
+ self.y = self.gridY * blockSize + blockSize / 2 + 200;
+ self.update = function () {
+ var currentTime = LK.ticks;
+ if (currentTime - self.lastMoveTime > 60 / self.fallSpeed) {
+ self.lastMoveTime = currentTime;
+ // Check if can move down
+ if (self.gridY + 1 < gridHeight && !grid[self.gridY + 1][self.gridX]) {
+ self.gridY++;
+ self.y = self.gridY * blockSize + blockSize / 2 + 200;
+ } else {
+ // Place block in grid
+ self.placeInGrid();
+ }
+ }
+ };
+ self.placeInGrid = function () {
+ if (self.gridY >= 0 && self.gridY < gridHeight && self.gridX >= 0 && self.gridX < gridWidth) {
+ grid[self.gridY][self.gridX] = self.shapeType;
+ // Create visual block in grid
+ var placedBlock = new GridBlock(self.shapeType);
+ placedBlock.gridX = self.gridX;
+ placedBlock.gridY = self.gridY;
+ placedBlock.x = self.x;
+ placedBlock.y = self.y;
+ placedShapes.push(placedBlock);
+ game.addChild(placedBlock);
+ LK.getSound('place').play();
+ score += 10;
+ updateScore();
+ checkForLines();
+ // Handle special blocks
+ if (self.isSpecial) {
+ handleSpecialBlock(self.shapeType, self.gridX, self.gridY);
+ }
+ }
+ // Remove from falling shapes
+ var index = fallingShapes.indexOf(self);
+ if (index > -1) {
+ fallingShapes.splice(index, 1);
+ }
+ self.destroy();
+ // Check game over
+ if (self.gridY <= 1) {
+ LK.showGameOver();
+ }
+ };
+ return self;
+});
/****
* Initialize Game
****/
@@ -114,22 +127,25 @@
* Game Code
****/
var fallingShapes = [];
var placedShapes = [];
-var inventory = [];
-var selectedShape = null;
-var isDragging = false;
-var dragOffset = {
- x: 0,
- y: 0
-};
-var lastTapTime = 0;
-var doubleTapDelay = 300;
var score = 0;
-var stability = 100;
-var maxHeight = 0;
+var linesCleared = 0;
+var level = 1;
var groundY = 2400;
-var shapeTypes = ['cube', 'pyramid', 'lblock', 'tblock', 'zblock', 'iblock', 'star', 'cross', 'hexagon', 'diamond', 'triangle'];
+var shapeTypes = ['lblock', 'tblock', 'zblock', 'iblock'];
+var specialBlocks = ['gift', 'bomb', 'wildcard'];
+var gridWidth = 10;
+var gridHeight = 20;
+var blockSize = 80;
+var grid = [];
+// Initialize empty grid
+for (var row = 0; row < gridHeight; row++) {
+ grid[row] = [];
+ for (var col = 0; col < gridWidth; col++) {
+ grid[row][col] = null;
+ }
+}
var nextShapeTimer = 0;
var shapeSpawnDelay = 120; // 2 seconds at 60fps
// Create ground
var ground = game.addChild(LK.getAsset('ground', {
@@ -185,12 +201,16 @@
heightText.x = 120;
heightText.y = 260;
LK.gui.topLeft.addChild(heightText);
function spawnShape() {
- var shapeType = shapeTypes[Math.floor(Math.random() * shapeTypes.length)];
- var shape = new FallingShape(shapeType);
- shape.x = Math.random() * 1800 + 124; // Keep away from edges
- shape.y = -80;
+ var shapeType;
+ // 10% chance for special blocks
+ if (Math.random() < 0.1) {
+ shapeType = specialBlocks[Math.floor(Math.random() * specialBlocks.length)];
+ } else {
+ shapeType = shapeTypes[Math.floor(Math.random() * shapeTypes.length)];
+ }
+ var shape = new TetrisBlock(shapeType);
fallingShapes.push(shape);
game.addChild(shape);
}
function updateInventoryDisplay() {
@@ -223,29 +243,175 @@
}
maxHeight = Math.max(maxHeight, currentHeight);
heightText.setText('Height: ' + Math.round(maxHeight / 80));
}
-function calculateStability() {
- if (placedShapes.length === 0) {
- stability = 100;
- return;
+function checkForLines() {
+ var linesToClear = [];
+ // Check each row for complete lines
+ for (var row = 0; row < gridHeight; row++) {
+ var isComplete = true;
+ for (var col = 0; col < gridWidth; col++) {
+ if (!grid[row][col]) {
+ isComplete = false;
+ break;
+ }
+ }
+ if (isComplete) {
+ linesToClear.push(row);
+ }
}
- var totalStability = 0;
- var centerX = 1024;
- for (var i = 0; i < placedShapes.length; i++) {
- var shape = placedShapes[i];
- var distanceFromCenter = Math.abs(shape.x - centerX);
- var heightFactor = (groundY - shape.y) / 80;
- // Shapes closer to center and lower are more stable
- var shapeStability = 100 - distanceFromCenter / 10 - heightFactor * 2;
- totalStability += Math.max(0, shapeStability);
+ // Clear complete lines
+ if (linesToClear.length > 0) {
+ clearLines(linesToClear);
}
- stability = Math.max(0, Math.min(100, totalStability / placedShapes.length));
- // Check for collapse
- if (stability < 20 && placedShapes.length > 3) {
- triggerCollapse();
+}
+function clearLines(lines) {
+ LK.getSound('lineClear').play();
+ var pointsPerLine = 100;
+ score += lines.length * pointsPerLine;
+ updateScore();
+ // Remove visual blocks and clear grid
+ for (var i = 0; i < lines.length; i++) {
+ var row = lines[i];
+ // Remove visual blocks
+ for (var j = placedShapes.length - 1; j >= 0; j--) {
+ var block = placedShapes[j];
+ if (block.gridY === row) {
+ tween(block, {
+ alpha: 0,
+ scaleX: 0,
+ scaleY: 0
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ block.destroy();
+ }
+ });
+ placedShapes.splice(j, 1);
+ }
+ }
+ // Clear grid row
+ for (var col = 0; col < gridWidth; col++) {
+ grid[row][col] = null;
+ }
}
+ // Drop remaining blocks down
+ setTimeout(function () {
+ dropBlocks(lines);
+ }, 400);
}
+function dropBlocks(clearedLines) {
+ for (var row = gridHeight - 1; row >= 0; row--) {
+ var dropDistance = 0;
+ for (var i = 0; i < clearedLines.length; i++) {
+ if (clearedLines[i] > row) {
+ dropDistance++;
+ }
+ }
+ if (dropDistance > 0) {
+ // Move grid data
+ for (var col = 0; col < gridWidth; col++) {
+ if (grid[row][col]) {
+ grid[row + dropDistance][col] = grid[row][col];
+ grid[row][col] = null;
+ }
+ }
+ // Move visual blocks
+ for (var j = 0; j < placedShapes.length; j++) {
+ var block = placedShapes[j];
+ if (block.gridY === row) {
+ block.gridY += dropDistance;
+ tween(block, {
+ y: block.gridY * blockSize + blockSize / 2 + 200
+ }, {
+ duration: 200,
+ easing: tween.easeOut
+ });
+ }
+ }
+ }
+ }
+}
+function handleSpecialBlock(type, gridX, gridY) {
+ if (type === 'gift') {
+ score += 50;
+ updateScore();
+ LK.effects.flashObject(game, 0xffd700, 500);
+ } else if (type === 'bomb') {
+ LK.getSound('bomb').play();
+ // Destroy blocks in 3x3 area
+ for (var row = Math.max(0, gridY - 1); row <= Math.min(gridHeight - 1, gridY + 1); row++) {
+ for (var col = Math.max(0, gridX - 1); col <= Math.min(gridWidth - 1, gridX + 1); col++) {
+ if (grid[row][col]) {
+ grid[row][col] = null;
+ // Remove visual block
+ for (var i = placedShapes.length - 1; i >= 0; i--) {
+ var block = placedShapes[i];
+ if (block.gridX === col && block.gridY === row) {
+ tween(block, {
+ alpha: 0,
+ scaleX: 2,
+ scaleY: 2
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ block.destroy();
+ }
+ });
+ placedShapes.splice(i, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+ } else if (type === 'wildcard') {
+ // Fill nearest gap
+ var nearestGap = findNearestGap(gridX, gridY);
+ if (nearestGap) {
+ grid[nearestGap.row][nearestGap.col] = 'cube';
+ var wildBlock = new GridBlock('cube');
+ wildBlock.gridX = nearestGap.col;
+ wildBlock.gridY = nearestGap.row;
+ wildBlock.x = nearestGap.col * blockSize + blockSize / 2 + (2048 - gridWidth * blockSize) / 2;
+ wildBlock.y = nearestGap.row * blockSize + blockSize / 2 + 200;
+ placedShapes.push(wildBlock);
+ game.addChild(wildBlock);
+ tween(wildBlock, {
+ scaleX: 1.5,
+ scaleY: 1.5
+ }, {
+ duration: 200,
+ easing: tween.bounceOut,
+ onFinish: function onFinish() {
+ tween(wildBlock, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 100
+ });
+ }
+ });
+ }
+ }
+}
+function findNearestGap(startX, startY) {
+ var searchRadius = 1;
+ while (searchRadius < Math.max(gridWidth, gridHeight)) {
+ for (var row = Math.max(0, startY - searchRadius); row <= Math.min(gridHeight - 1, startY + searchRadius); row++) {
+ for (var col = Math.max(0, startX - searchRadius); col <= Math.min(gridWidth - 1, startX + searchRadius); col++) {
+ if (!grid[row][col]) {
+ return {
+ row: row,
+ col: col
+ };
+ }
+ }
+ }
+ searchRadius++;
+ }
+ return null;
+}
function triggerCollapse() {
LK.getSound('collapse').play();
// Remove unstable shapes
for (var i = placedShapes.length - 1; i >= 0; i--) {
@@ -316,40 +482,26 @@
inventoryShape.destroy();
}
// Event handlers
game.down = function (x, y, obj) {
- var currentTime = Date.now();
- var isDoubleTap = currentTime - lastTapTime < doubleTapDelay;
- lastTapTime = currentTime;
- // Check if double-tapping on placed shape for rotation
- if (isDoubleTap) {
- for (var j = 0; j < placedShapes.length; j++) {
- var placedShape = placedShapes[j];
- var distance = Math.sqrt(Math.pow(x - placedShape.x, 2) + Math.pow(y - placedShape.y, 2));
- if (distance < 50) {
- placedShape.rotate();
- return;
- }
+ var gameAreaLeft = (2048 - gridWidth * blockSize) / 2;
+ var gameAreaRight = gameAreaLeft + gridWidth * blockSize;
+ // Only handle touches in game area
+ if (x >= gameAreaLeft && x <= gameAreaRight && fallingShapes.length > 0) {
+ var activeBlock = fallingShapes[0];
+ var targetGridX = Math.floor((x - gameAreaLeft) / blockSize);
+ targetGridX = Math.max(0, Math.min(gridWidth - 1, targetGridX));
+ // Move block to target column if possible
+ if (!grid[activeBlock.gridY][targetGridX]) {
+ activeBlock.gridX = targetGridX;
+ tween(activeBlock, {
+ x: targetGridX * blockSize + blockSize / 2 + gameAreaLeft
+ }, {
+ duration: 200,
+ easing: tween.easeOut
+ });
}
}
- // Check if clicking on falling shape
- for (var i = 0; i < fallingShapes.length; i++) {
- var shape = fallingShapes[i];
- if (shape.intersects({
- x: x,
- y: y,
- width: 1,
- height: 1
- })) {
- shape["catch"]();
- fallingShapes.splice(i, 1);
- break;
- }
- }
- // Check if placing from inventory
- if (inventory.length > 0 && y > groundY - 200) {
- placeShape(null, x, Math.max(groundY - 40, y));
- }
};
game.move = function (x, y, obj) {
if (selectedShape && selectedShape.isDragging) {
// Smooth tween to new position for better feel