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.color = shapeColors[shapeType] || 0xffffff; self.gridX = 0; self.gridY = 0; var shapeGraphic = self.attachAsset(shapeType, { anchorX: 0.5, anchorY: 0.5 }); // Add bomb fuse for bomb blocks if (shapeType === 'bomb') { var fuseGraphic = self.attachAsset('bombFuse', { anchorX: 0.5, anchorY: 1, x: 0, y: -25 }); // Animate fuse flickering var _fuseFlicker = function fuseFlicker() { tween(fuseGraphic, { alpha: 0.3 }, { duration: 200, onFinish: function onFinish() { tween(fuseGraphic, { alpha: 1 }, { duration: 200, onFinish: _fuseFlicker }); } }); }; _fuseFlicker(); } 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) { // Only set as selected if no other shape is already being dragged if (!selectedShape || !selectedShape.isDragging) { 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(Math.random() * gridWidth); // Random spawn position self.gridY = 0; self.lastMoveTime = 0; self.targetX = 0; // For smooth movement self.isMoving = false; // Track if block is currently moving var shapeGraphic = self.attachAsset(shapeType, { anchorX: 0.5, anchorY: 0.5 }); // Add bomb fuse for bomb blocks if (shapeType === 'bomb') { var fuseGraphic = self.attachAsset('bombFuse', { anchorX: 0.5, anchorY: 1, x: 0, y: -25 }); // Animate fuse flickering var _fuseFlicker2 = function fuseFlicker() { tween(fuseGraphic, { alpha: 0.3 }, { duration: 200, onFinish: function onFinish() { tween(fuseGraphic, { alpha: 1 }, { duration: 200, onFinish: _fuseFlicker2 }); } }); }; _fuseFlicker2(); } // Position on grid self.x = self.gridX * blockSize + blockSize / 2 + (2048 - gridWidth * blockSize) / 2; self.y = self.gridY * blockSize + blockSize / 2 + 200; self.targetX = self.x; // Initialize target position self.checkCollision = function (newGridX, newGridY) { // Check bounds if (newGridX < 0 || newGridX >= gridWidth || newGridY < 0 || newGridY >= gridHeight) { return true; } // Check if position is occupied if (grid[newGridY] && grid[newGridY][newGridX]) { return true; } // Check collision with other falling shapes for (var i = 0; i < fallingShapes.length; i++) { var otherShape = fallingShapes[i]; if (otherShape !== self && otherShape.gridX === newGridX && otherShape.gridY === newGridY) { return true; } } return false; }; self.moveToPosition = function (newGridX) { if (!self.checkCollision(newGridX, self.gridY)) { self.gridX = newGridX; self.targetX = self.gridX * blockSize + blockSize / 2 + (2048 - gridWidth * blockSize) / 2; self.isMoving = true; // Stop any existing movement tween tween.stop(self, { x: true }); // Ultra-fast responsive movement tween(self, { x: self.targetX }, { duration: 80, // Much faster for responsive feel easing: tween.easeOut, onFinish: function onFinish() { self.isMoving = false; } }); } }; self.update = function () { var currentTime = LK.ticks; if (currentTime - self.lastMoveTime > 60 / self.fallSpeed) { self.lastMoveTime = currentTime; // Check if can move down using collision detection if (!self.checkCollision(self.gridX, self.gridY + 1)) { 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 shapeColors = { 'lblock': 0x7ed321, 'tblock': 0xd0021b, 'zblock': 0x9013fe, 'iblock': 0x50e3c2, 'cube': 0x4a90e2, 'pyramid': 0xf5a623, 'triangle': 0x00b894, 'diamond': 0xfd79a8, 'star': 0xff6b35, 'cross': 0x1b998b, 'hexagon': 0x6c5ce7 }; var gridWidth = 24; var gridHeight = 24; var blockSize = 80; var grid = []; var stability = 100; var maxHeight = 0; var selectedShape = null; var hoveredShape = null; var cursor = null; var inventory = []; // 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 calculateStability() { // Calculate stability based on grid density and structure var totalBlocks = 0; var connectedBlocks = 0; var baseSupport = 0; // Count total blocks and base support for (var row = 0; row < gridHeight; row++) { for (var col = 0; col < gridWidth; col++) { if (grid[row][col]) { totalBlocks++; // Check if block has support below or is on ground if (row === gridHeight - 1 || grid[row + 1][col]) { baseSupport++; } // Check connections (adjacent blocks) var connections = 0; if (row > 0 && grid[row - 1][col]) connections++; if (row < gridHeight - 1 && grid[row + 1][col]) connections++; if (col > 0 && grid[row][col - 1]) connections++; if (col < gridWidth - 1 && grid[row][col + 1]) connections++; if (connections > 0) connectedBlocks++; } } } if (totalBlocks === 0) { stability = 100; } else { // Calculate stability percentage var supportRatio = baseSupport / totalBlocks; var connectionRatio = connectedBlocks / totalBlocks; stability = Math.max(10, Math.min(100, (supportRatio * 60 + connectionRatio * 40) * 100)); } } 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 matches for (var row = 0; row < gridHeight; row++) { var rowBlocks = []; var colors = {}; var colorCount = 0; // Collect all blocks in this row for (var col = 0; col < gridWidth; col++) { if (grid[row][col]) { rowBlocks.push({ col: col, type: grid[row][col] }); var color = shapeColors[grid[row][col]] || 0xffffff; if (!colors[color]) { colors[color] = 0; colorCount++; } colors[color]++; } } // Rule 1: 4+ blocks of same color horizontally for (var color in colors) { if (colors[color] >= 4) { linesToClear.push({ row: row, type: 'same_color', points: colors[color] * 25 }); break; } } // Rule 2: 8+ blocks with exactly 2 colors if (linesToClear.length === 0 && colorCount === 2 && rowBlocks.length >= 8) { linesToClear.push({ row: row, type: 'two_color', points: 200 }); } // Rule 3: Full row (24 blocks) with 3+ colors if (linesToClear.length === 0 && colorCount >= 3 && rowBlocks.length === 24) { linesToClear.push({ row: row, type: 'full_multicolor', points: 500 }); } } // Clear matched lines if (linesToClear.length > 0) { clearLines(linesToClear); } } function clearLines(lines) { LK.getSound('lineClear').play(); var totalPoints = 0; for (var i = 0; i < lines.length; i++) { totalPoints += lines[i].points || 100; } score += totalPoints; updateScore(); // Remove visual blocks and clear grid for (var i = 0; i < lines.length; i++) { var row = lines[i].row; // 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 LK.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 detectHoveredShape(x, y) { var newHoveredShape = null; // Check falling shapes first (priority for interaction) for (var i = 0; i < fallingShapes.length; i++) { var shape = fallingShapes[i]; var dx = x - shape.x; var dy = y - shape.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < blockSize / 2) { newHoveredShape = shape; break; } } // Update hover state and cursor appearance if (newHoveredShape !== hoveredShape) { // Reset previous hovered shape if (hoveredShape) { tween.stop(hoveredShape, { alpha: true }); tween(hoveredShape, { alpha: 1 }, { duration: 200 }); } // Highlight new hovered shape if (newHoveredShape) { tween.stop(newHoveredShape, { alpha: true }); tween(newHoveredShape, { alpha: 0.7 }, { duration: 200 }); // Change cursor to indicate draggable tween(cursor, { scaleX: 1.2, scaleY: 1.2, tint: 0x00ff00 }, { duration: 150 }); } else { // Reset cursor to normal tween(cursor, { scaleX: 1, scaleY: 1, tint: 0xffffff }, { duration: 150 }); } hoveredShape = newHoveredShape; } } 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) { // Only select the shape under the cursor for dragging if (hoveredShape && !selectedShape) { selectedShape = hoveredShape; selectedShape.isDragging = true; // Visual feedback for selection tween(selectedShape, { scaleX: 1.1, scaleY: 1.1 }, { duration: 150 }); tween(cursor, { tint: 0xff0000 }, { duration: 100 }); } }; game.move = function (x, y, obj) { // Update cursor position smoothly tween.stop(cursor, { x: true, y: true }); tween(cursor, { x: x, y: y }, { duration: 50, easing: tween.easeOut }); // Detect what shape is under cursor detectHoveredShape(x, y); // Handle dragging of selected shape if (selectedShape && selectedShape.isDragging) { var gameAreaLeft = (2048 - gridWidth * blockSize) / 2; var gameAreaRight = gameAreaLeft + gridWidth * blockSize; // Only allow movement within game area if (x >= gameAreaLeft && x <= gameAreaRight) { var targetGridX = Math.floor((x - gameAreaLeft) / blockSize); targetGridX = Math.max(0, Math.min(gridWidth - 1, targetGridX)); // Only move if the target position is different and valid if (targetGridX !== selectedShape.gridX) { selectedShape.moveToPosition(targetGridX); } } } }; game.up = function (x, y, obj) { // Release the selected shape with smooth transition if (selectedShape && selectedShape.isDragging) { selectedShape.isDragging = false; // Reset visual feedback tween(selectedShape, { scaleX: 1, scaleY: 1 }, { duration: 200 }); tween(cursor, { tint: 0xffffff }, { duration: 200 }); selectedShape = null; } // Re-detect hover state immediately after release detectHoveredShape(x, y); }; 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(); } }; // Create cursor cursor = game.addChild(LK.getAsset('cursor', { anchorX: 0.5, anchorY: 0, x: 1024, y: 1000 })); cursor.rotation = Math.PI / 4; // Make it look like an arrow cursor.alpha = 0.8; // Initialize display updateScore(); updateStability(); updateInventoryDisplay(); updateHeight();
===================================================================
--- original.js
+++ change.js
@@ -149,14 +149,18 @@
if (!self.checkCollision(newGridX, self.gridY)) {
self.gridX = newGridX;
self.targetX = self.gridX * blockSize + blockSize / 2 + (2048 - gridWidth * blockSize) / 2;
self.isMoving = true;
- // Fast smooth movement with tween
+ // Stop any existing movement tween
+ tween.stop(self, {
+ x: true
+ });
+ // Ultra-fast responsive movement
tween(self, {
x: self.targetX
}, {
- duration: 150,
- // Faster movement
+ duration: 80,
+ // Much faster for responsive feel
easing: tween.easeOut,
onFinish: function onFinish() {
self.isMoving = false;
}
@@ -248,8 +252,10 @@
var grid = [];
var stability = 100;
var maxHeight = 0;
var selectedShape = null;
+var hoveredShape = null;
+var cursor = null;
var inventory = [];
// Initialize empty grid
for (var row = 0; row < gridHeight; row++) {
grid[row] = [];
@@ -576,8 +582,65 @@
});
}
}
}
+function detectHoveredShape(x, y) {
+ var newHoveredShape = null;
+ // Check falling shapes first (priority for interaction)
+ for (var i = 0; i < fallingShapes.length; i++) {
+ var shape = fallingShapes[i];
+ var dx = x - shape.x;
+ var dy = y - shape.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < blockSize / 2) {
+ newHoveredShape = shape;
+ break;
+ }
+ }
+ // Update hover state and cursor appearance
+ if (newHoveredShape !== hoveredShape) {
+ // Reset previous hovered shape
+ if (hoveredShape) {
+ tween.stop(hoveredShape, {
+ alpha: true
+ });
+ tween(hoveredShape, {
+ alpha: 1
+ }, {
+ duration: 200
+ });
+ }
+ // Highlight new hovered shape
+ if (newHoveredShape) {
+ tween.stop(newHoveredShape, {
+ alpha: true
+ });
+ tween(newHoveredShape, {
+ alpha: 0.7
+ }, {
+ duration: 200
+ });
+ // Change cursor to indicate draggable
+ tween(cursor, {
+ scaleX: 1.2,
+ scaleY: 1.2,
+ tint: 0x00ff00
+ }, {
+ duration: 150
+ });
+ } else {
+ // Reset cursor to normal
+ tween(cursor, {
+ scaleX: 1,
+ scaleY: 1,
+ tint: 0xffffff
+ }, {
+ duration: 150
+ });
+ }
+ hoveredShape = newHoveredShape;
+ }
+}
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++) {
@@ -665,32 +728,42 @@
inventoryShape.destroy();
}
// Event handlers
game.down = function (x, y, obj) {
- // Check if touch hit a specific falling shape
- var touchedShape = null;
- for (var i = 0; i < fallingShapes.length; i++) {
- var shape = fallingShapes[i];
- var dx = x - shape.x;
- var dy = y - shape.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance < blockSize / 2) {
- touchedShape = shape;
- break;
- }
- }
- // Only move the specifically touched shape
- if (touchedShape) {
- var gameAreaLeft = (2048 - gridWidth * blockSize) / 2;
- var targetGridX = Math.floor((x - gameAreaLeft) / blockSize);
- targetGridX = Math.max(0, Math.min(gridWidth - 1, targetGridX));
- touchedShape.moveToPosition(targetGridX);
- selectedShape = touchedShape;
+ // Only select the shape under the cursor for dragging
+ if (hoveredShape && !selectedShape) {
+ selectedShape = hoveredShape;
selectedShape.isDragging = true;
+ // Visual feedback for selection
+ tween(selectedShape, {
+ scaleX: 1.1,
+ scaleY: 1.1
+ }, {
+ duration: 150
+ });
+ tween(cursor, {
+ tint: 0xff0000
+ }, {
+ duration: 100
+ });
}
};
game.move = function (x, y, obj) {
- // Only move the specifically selected shape that is being dragged
+ // Update cursor position smoothly
+ tween.stop(cursor, {
+ x: true,
+ y: true
+ });
+ tween(cursor, {
+ x: x,
+ y: y
+ }, {
+ duration: 50,
+ easing: tween.easeOut
+ });
+ // Detect what shape is under cursor
+ detectHoveredShape(x, y);
+ // Handle dragging of selected shape
if (selectedShape && selectedShape.isDragging) {
var gameAreaLeft = (2048 - gridWidth * blockSize) / 2;
var gameAreaRight = gameAreaLeft + gridWidth * blockSize;
// Only allow movement within game area
@@ -701,17 +774,30 @@
if (targetGridX !== selectedShape.gridX) {
selectedShape.moveToPosition(targetGridX);
}
}
- return; // Exit early to prevent other movement handling
}
};
game.up = function (x, y, obj) {
- // Release the selected shape
+ // Release the selected shape with smooth transition
if (selectedShape && selectedShape.isDragging) {
selectedShape.isDragging = false;
+ // Reset visual feedback
+ tween(selectedShape, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ tween(cursor, {
+ tint: 0xffffff
+ }, {
+ duration: 200
+ });
selectedShape = null;
}
+ // Re-detect hover state immediately after release
+ detectHoveredShape(x, y);
};
game.update = function () {
// Spawn new shapes
nextShapeTimer++;
@@ -737,8 +823,17 @@
if (stability < 10 && placedShapes.length > 0) {
LK.showGameOver();
}
};
+// Create cursor
+cursor = game.addChild(LK.getAsset('cursor', {
+ anchorX: 0.5,
+ anchorY: 0,
+ x: 1024,
+ y: 1000
+}));
+cursor.rotation = Math.PI / 4; // Make it look like an arrow
+cursor.alpha = 0.8;
// Initialize display
updateScore();
updateStability();
updateInventoryDisplay();