User prompt
what we have put "brick programs" should not just start one but all of them
User prompt
there should be a white cube at the bottom where can see the finished game
User prompt
there should be no levels
Code edit (1 edits merged)
Please save this source code
User prompt
BlockCode Builder
Initial prompt
Make a game that has infinite "brick programs" and can make a game with these "brick programs"
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BlockPalette = Container.expand(function (width, height) { var self = Container.call(this); self.paletteWidth = width || 400; self.paletteHeight = height || 2732; // Create background self.background = self.addChild(LK.getAsset('codeBlock', { anchorX: 0, anchorY: 0, width: self.paletteWidth, height: self.paletteHeight, alpha: 0.3, tint: 0x333333 })); // Add title self.title = new Text2("Block Palette", { size: 40, fill: 0xFFFFFF }); self.title.anchor.set(0.5, 0); self.title.x = self.paletteWidth / 2; self.title.y = 30; self.addChild(self.title); // Add blocks to palette self.addBlockToPalette = function (type, text, x, y) { var block = new CodeBlock(type, text); block.x = x; block.y = y; // This is a template block - when dragged, it creates a new instance block.down = function (x, y, obj) { // Create a new block when this template is clicked var newBlock = new CodeBlock(type, text); newBlock.x = block.x; newBlock.y = block.y; // Convert to global position var globalPos = self.parent.toGlobal({ x: block.x, y: block.y }); newBlock.x = globalPos.x; newBlock.y = globalPos.y; // Add to game's blocks array blocks.push(newBlock); // Add to game game.addChild(newBlock); // Start dragging the new block newBlock.startDragX = newBlock.x; newBlock.startDragY = newBlock.y; newBlock.isDragging = true; draggedBlock = newBlock; }; self.addChild(block); return block; }; return self; }); var Button = Container.expand(function (text, width, height, color) { var self = Container.call(this); self.buttonWidth = width || 200; self.buttonHeight = height || 80; self.buttonColor = color || 0x4287f5; // Create button background self.background = self.addChild(LK.getAsset('codeBlock', { anchorX: 0.5, anchorY: 0.5, width: self.buttonWidth, height: self.buttonHeight, tint: self.buttonColor })); // Add text self.label = new Text2(text, { size: 30, fill: 0xFFFFFF }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); // Handle down event self.down = function (x, y, obj) { // Visual feedback tween(self.background, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); }; // Handle up event self.up = function (x, y, obj) { // Reset scale tween(self.background, { scaleX: 1, scaleY: 1 }, { duration: 100, onFinish: function onFinish() { // Call the button's action if (self.action) { self.action(); } } }); }; return self; }); var CodeArea = Container.expand(function (width, height) { var self = Container.call(this); self.areaWidth = width || 1200; self.areaHeight = height || 1500; // Create background self.background = self.addChild(LK.getAsset('codeBlock', { anchorX: 0, anchorY: 0, width: self.areaWidth, height: self.areaHeight, alpha: 0.2, tint: 0x222222 })); // Method to check if a point is inside the code area self.isPointInside = function (x, y) { var globalPos = game.toLocal({ x: x, y: y }, self.parent); return globalPos.x >= self.x && globalPos.x <= self.x + self.areaWidth && globalPos.y >= self.y && globalPos.y <= self.y + self.areaHeight; }; return self; }); var CodeBlock = Container.expand(function (type, text) { var self = Container.call(this); self.type = type || 'default'; self.text = text || ''; self.connections = { top: null, bottom: null, left: null, right: null }; self.canConnect = true; self.isExecuting = false; // Select the correct asset based on type var assetId = 'codeBlock'; if (self.type === 'variable') { assetId = 'codeBlockVar'; } else if (self.type === 'loop') { assetId = 'codeBlockLoop'; } else if (self.type === 'condition') { assetId = 'codeBlockCondition'; } else if (self.type === 'function') { assetId = 'codeBlockFunction'; } // Create main block self.blockGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add connection points self.connectionPoints = {}; // Top connection self.connectionPoints.top = self.addChild(LK.getAsset('connectionPoint', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -self.blockGraphics.height / 2 })); // Bottom connection self.connectionPoints.bottom = self.addChild(LK.getAsset('connectionPoint', { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.blockGraphics.height / 2 })); // Left connection (only for condition and loop types) if (self.type === 'condition' || self.type === 'loop') { self.connectionPoints.left = self.addChild(LK.getAsset('connectionPoint', { anchorX: 0.5, anchorY: 0.5, x: -self.blockGraphics.width / 2, y: 0 })); } // Right connection (only for condition type) if (self.type === 'condition') { self.connectionPoints.right = self.addChild(LK.getAsset('connectionPoint', { anchorX: 0.5, anchorY: 0.5, x: self.blockGraphics.width / 2, y: 0 })); } // Add text label self.label = new Text2(self.text, { size: 24, fill: 0xFFFFFF }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); // Execution highlight (initially invisible) self.highlight = self.addChild(LK.getAsset('executionHighlight', { anchorX: 0.5, anchorY: 0.5, alpha: 0 })); // Handle interaction events self.down = function (x, y, obj) { if (!programIsRunning) { self.startDragX = self.x; self.startDragY = self.y; self.isDragging = true; draggedBlock = self; // Bring to front if (self.parent) { var parent = self.parent; parent.removeChild(self); parent.addChild(self); } } }; self.move = function (x, y, obj) { // Movement handled by game's move handler }; self.up = function (x, y, obj) { if (self.isDragging) { self.isDragging = false; draggedBlock = null; // Check if we're close to another block's connection point var connected = false; for (var i = 0; i < blocks.length; i++) { var otherBlock = blocks[i]; if (otherBlock !== self && otherBlock.canConnect) { connected = self.tryConnectTo(otherBlock); if (connected) { break; } } } if (!connected && codeArea.isPointInside(self.x, self.y)) { // Snap to grid if in code area self.x = Math.round(self.x / grid.cellSize) * grid.cellSize; self.y = Math.round(self.y / grid.cellSize) * grid.cellSize; } else if (!connected && !codeArea.isPointInside(self.x, self.y)) { // Return to palette area if not connected and outside code area self.x = self.startDragX; self.y = self.startDragY; } } }; self.tryConnectTo = function (otherBlock) { if (!otherBlock || otherBlock === self) { return false; } // Check proximity to connection points var connectionThreshold = 50; var myPoints = self.connectionPoints; var otherPoints = otherBlock.connectionPoints; // Check all possible connections for (var myPos in myPoints) { if (!myPoints[myPos]) { continue; } var myGlobalPos = self.parent.toGlobal(myPoints[myPos].position); for (var otherPos in otherPoints) { if (!otherPoints[otherPos]) { continue; } // Skip if this connection is already occupied if (otherBlock.connections[otherPos]) { continue; } var otherGlobalPos = otherBlock.parent.toGlobal(otherPoints[otherPos].position); var distance = Math.sqrt(Math.pow(myGlobalPos.x - otherGlobalPos.x, 2) + Math.pow(myGlobalPos.y - otherGlobalPos.y, 2)); // If close enough, connect if (distance < connectionThreshold) { // Connect based on valid combinations var validConnection = false; // Top to bottom connections if (myPos === 'bottom' && otherPos === 'top' || myPos === 'top' && otherPos === 'bottom') { validConnection = true; } // Left/right connections for condition/loop blocks if ((self.type === 'condition' || self.type === 'loop') && otherBlock.type !== 'condition' && otherBlock.type !== 'loop') { if (myPos === 'left' && otherPos === 'top' || myPos === 'right' && otherPos === 'top') { validConnection = true; } } if (validConnection) { self.connectTo(otherBlock, myPos, otherPos); return true; } } } } return false; }; self.connectTo = function (otherBlock, myPos, otherPos) { // Position calculation if (myPos === 'bottom' && otherPos === 'top') { // Align other block below this one otherBlock.x = self.x; otherBlock.y = self.y + self.blockGraphics.height; // Update connections self.connections.bottom = otherBlock; otherBlock.connections.top = self; } else if (myPos === 'top' && otherPos === 'bottom') { // Align this block below other one self.x = otherBlock.x; self.y = otherBlock.y + otherBlock.blockGraphics.height; // Update connections self.connections.top = otherBlock; otherBlock.connections.bottom = self; } else if (myPos === 'left' && otherPos === 'top') { // Align other block to the left of this one otherBlock.x = self.x - self.blockGraphics.width / 2; otherBlock.y = self.y; // Update connections self.connections.left = otherBlock; otherBlock.connections.top = self; } else if (myPos === 'right' && otherPos === 'top') { // Align other block to the right of this one otherBlock.x = self.x + self.blockGraphics.width / 2; otherBlock.y = self.y; // Update connections self.connections.right = otherBlock; otherBlock.connections.top = self; } // Play connection sound LK.getSound('connect').play(); }; self.execute = function (callback) { self.isExecuting = true; // Show execution highlight self.highlight.alpha = 0.5; // Play execution sound LK.getSound('execute').play(); // Execute block action based on type var executionTime = 800; // Default execution time switch (self.type) { case 'variable': // Variable assignment executionLog.push("➡️ Set variable: " + self.text); // Visual effect for variable assignment tween(self.blockGraphics, { alpha: 0.5 }, { duration: 300, onFinish: function onFinish() { tween(self.blockGraphics, { alpha: 1 }, { duration: 300 }); } }); break; case 'loop': // Loop block execution executionLog.push("🔄 Loop: " + self.text); // Visual effect for loops - make it pulse tween(self.blockGraphics, { scaleX: 1.1, scaleY: 1.1 }, { duration: 400, onFinish: function onFinish() { tween(self.blockGraphics, { scaleX: 1, scaleY: 1 }, { duration: 400 }); } }); // For loops, we need to execute differently executionTime = 1000; break; case 'condition': // Condition evaluation executionLog.push("⚖️ If condition: " + self.text); // Visual effect for conditions - flash slightly tween(self.blockGraphics, { alpha: 0.7 }, { duration: 200, onFinish: function onFinish() { tween(self.blockGraphics, { alpha: 1 }, { duration: 200 }); } }); break; case 'function': // Function call executionLog.push("🧩 Call function: " + self.text); // Visual effect for functions - rotate slightly tween(self.blockGraphics, { rotation: 0.1 }, { duration: 300, onFinish: function onFinish() { tween(self.blockGraphics, { rotation: 0 }, { duration: 300 }); } }); break; default: // Default block executionLog.push("▶️ Execute: " + self.text); // Visual effect for standard blocks tween(self.blockGraphics, { alpha: 0.8 }, { duration: 200, onFinish: function onFinish() { tween(self.blockGraphics, { alpha: 1 }, { duration: 200 }); } }); break; } // Update execution log display updateExecutionLog(); // After execution, continue to next block LK.setTimeout(function () { self.highlight.alpha = 0; self.isExecuting = false; if (callback) { callback(); } }, executionTime); }; return self; }); var PreviewDisplay = Container.expand(function () { var self = Container.call(this); // Create the white cube display area self.display = self.addChild(LK.getAsset('codeBlock', { anchorX: 0, anchorY: 0, width: 500, height: 500, tint: 0xffffff })); // Title for the preview area self.title = new Text2("Program Output", { size: 30, fill: 0x000000 }); self.title.anchor.set(0.5, 0); self.title.x = self.display.width / 2; self.title.y = 20; self.addChild(self.title); // Program output text self.outputText = new Text2("Your program will run here", { size: 24, fill: 0x000000, wordWrap: true, wordWrapWidth: 460 }); self.outputText.anchor.set(0.5, 0); self.outputText.x = self.display.width / 2; self.outputText.y = 80; self.addChild(self.outputText); // Update the output display self.updateOutput = function (results) { if (!results || results.length === 0) { self.outputText.setText("Your program will run here"); return; } var displayText = ""; // Take the last 5 operations or fewer var startIndex = Math.max(0, results.length - 5); for (var i = startIndex; i < results.length; i++) { displayText += results[i] + "\n\n"; } self.outputText.setText(displayText); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a24 }); /**** * Game Code ****/ // Grid settings var grid = { cellSize: 100 }; // Game state var programIsRunning = false; var draggedBlock = null; var blocks = []; var executionLog = []; var creationScore = 0; // Score based on program complexity // Function to update the execution log display function updateExecutionLog() { var logText = "Execution Log:\n"; for (var i = 0; i < executionLog.length; i++) { logText += "- " + executionLog[i] + "\n"; } executionLogText.setText(logText); } // Create the code area var codeArea = new CodeArea(1200, 1500); codeArea.x = 424; // Offset from left edge codeArea.y = 150; // Offset from top game.addChild(codeArea); // Create block palette var blockPalette = new BlockPalette(400, 2732); blockPalette.x = 20; // Left edge blockPalette.y = 0; // Top edge game.addChild(blockPalette); // Add blocks to palette var yOffset = 100; var blockSpacing = 120; // Basic blocks blockPalette.addBlockToPalette('default', "Print 'Hello World'", blockPalette.paletteWidth / 2, yOffset + blockSpacing); blockPalette.addBlockToPalette('default', "Display Message", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 2); blockPalette.addBlockToPalette('default', "Play Sound", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 3); // Variable blocks blockPalette.addBlockToPalette('variable', "x = 5", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 4); blockPalette.addBlockToPalette('variable', "y = 10", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 5); blockPalette.addBlockToPalette('variable', "counter = 0", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 6); blockPalette.addBlockToPalette('variable', "name = 'Player'", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 7); // Loop blocks blockPalette.addBlockToPalette('loop', "Repeat 3 times", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 8); blockPalette.addBlockToPalette('loop', "While true", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 9); blockPalette.addBlockToPalette('loop', "For each item", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 10); // Condition blocks blockPalette.addBlockToPalette('condition', "If x > 0", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 11); blockPalette.addBlockToPalette('condition', "If x == y", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 12); blockPalette.addBlockToPalette('condition', "If counter > 10", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 13); // Function blocks blockPalette.addBlockToPalette('function', "Calculate Sum", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 14); blockPalette.addBlockToPalette('function', "Get Random Number", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 15); blockPalette.addBlockToPalette('function', "Convert to String", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 16); // Action blocks blockPalette.addBlockToPalette('default', "Draw Shape", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 17); blockPalette.addBlockToPalette('default', "Move Forward", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 18); blockPalette.addBlockToPalette('default', "Turn Right", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 19); blockPalette.addBlockToPalette('default', "Turn Left", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 20); // Create program builder title var titleText = new Text2("BlockCode Builder", { size: 50, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.x = codeArea.x + codeArea.areaWidth / 2; titleText.y = 50; game.addChild(titleText); // Create instructions text var instructionsText = new Text2("Create and run your own programs by connecting blocks from the palette", { size: 30, fill: 0xFFFFFF, wordWrap: true, wordWrapWidth: 1100 }); instructionsText.anchor.set(0.5, 0); instructionsText.x = codeArea.x + codeArea.areaWidth / 2; instructionsText.y = 110; game.addChild(instructionsText); // Execution log var executionLogText = new Text2("Execution Log:\n", { size: 24, fill: 0xFFFFFF, wordWrap: true, wordWrapWidth: 1100 }); executionLogText.anchor.set(0, 0); executionLogText.x = codeArea.x + 50; executionLogText.y = codeArea.y + codeArea.areaHeight + 20; game.addChild(executionLogText); // Create run button var runButton = new Button("Execute Program", 220, 80, 0x4CAF50); runButton.x = codeArea.x + codeArea.areaWidth - 150; runButton.y = codeArea.y + codeArea.areaHeight + 50; runButton.action = function () { if (!programIsRunning) { runProgram(); } }; game.addChild(runButton); // Create reset button var resetButton = new Button("Clear Canvas", 180, 80, 0xF44336); resetButton.x = codeArea.x + 100; resetButton.y = codeArea.y + codeArea.areaHeight + 50; resetButton.action = function () { resetProgram(); }; game.addChild(resetButton); // Create preview display area var previewDisplay = new PreviewDisplay(); previewDisplay.x = codeArea.x + codeArea.areaWidth / 2 - 250; // Center horizontally under code area previewDisplay.y = 2732 - 550; // Position at bottom of screen game.addChild(previewDisplay); // Create score display var scoreText = new Text2("Score: 0", { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(1, 0); scoreText.x = 2048 - 50; scoreText.y = 50; game.addChild(scoreText); // Function to find all start blocks (blocks without a top connection in code area) function findStartBlocks() { var startBlocks = []; for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; if (codeArea.isPointInside(block.x, block.y) && !block.connections.top) { startBlocks.push(block); } } return startBlocks; } // Function to run the program function runProgram() { // Clear execution log executionLog = []; updateExecutionLog(); // Find all starting blocks var startingBlocks = []; for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; if (codeArea.isPointInside(block.x, block.y) && !block.connections.top) { startingBlocks.push(block); } } if (startingBlocks.length === 0) { executionLog.push("No starting blocks found!"); updateExecutionLog(); // Update preview display previewDisplay.updateOutput(["No starting blocks found!"]); return; } programIsRunning = true; // Calculate program complexity before execution calculateProgramComplexity(); // Execute all starting blocks in sequence var currentBlockIndex = 0; function executeNextBlock() { if (currentBlockIndex < startingBlocks.length) { executionLog.push("--- Running Program " + (currentBlockIndex + 1) + " ---"); updateExecutionLog(); executeBlock(startingBlocks[currentBlockIndex], function () { currentBlockIndex++; executeNextBlock(); }); } else { // All programs executed programIsRunning = false; // Reward the player for successfully running programs LK.setScore(creationScore); scoreText.setText("Score: " + LK.getScore()); // Update the preview display with the execution results previewDisplay.updateOutput(executionLog); // Play completion sound for visual feedback if (executionLog.length > 0) { LK.getSound('complete').play(); } } } // Start executing blocks executeNextBlock(); } // Function to execute a block and its connected blocks function executeBlock(block, callback) { if (!block) { if (callback) { callback(); } return; } block.execute(function () { // After execution, continue to next block based on type if (block.type === 'condition') { // For condition, randomly go left or right branch var randomBranch = Math.random() > 0.5 ? 'left' : 'right'; if (block.connections[randomBranch]) { executeBlock(block.connections[randomBranch], function () { // After branch execution, continue with bottom block if any executeBlock(block.connections.bottom, callback); }); } else { // No branch, go to bottom executeBlock(block.connections.bottom, callback); } } else if (block.type === 'loop') { // For loop, execute left branch twice, then continue if (block.connections.left) { executeBlock(block.connections.left, function () { // Execute loop content again if (block.connections.left) { executeBlock(block.connections.left, function () { // Then continue with bottom block executeBlock(block.connections.bottom, callback); }); } else { executeBlock(block.connections.bottom, callback); } }); } else { // No loop content, go to bottom executeBlock(block.connections.bottom, callback); } } else { // For other blocks, just go to bottom executeBlock(block.connections.bottom, callback); } }); } // Function to reset the program function resetProgram() { // Clear blocks in the code area var blocksRemoved = 0; for (var i = blocks.length - 1; i >= 0; i--) { var block = blocks[i]; if (codeArea.isPointInside(block.x, block.y)) { block.destroy(); blocks.splice(i, 1); blocksRemoved++; } } // Clear execution log executionLog = []; executionLog.push("Program reset. " + blocksRemoved + " blocks removed."); updateExecutionLog(); // Reset preview display previewDisplay.updateOutput(["Program has been reset. Add blocks to create a new program."]); // Reset score creationScore = 0; } // Function to calculate program complexity and score function calculateProgramComplexity() { // Reset score for this program creationScore = 0; // Count total blocks in the program var programBlocks = 0; var variableCount = 0; var loopCount = 0; var conditionCount = 0; var functionCount = 0; var defaultBlockCount = 0; // Count different types of blocks in the code area for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; if (codeArea.isPointInside(block.x, block.y)) { programBlocks++; switch (block.type) { case 'variable': variableCount++; break; case 'loop': loopCount++; break; case 'condition': conditionCount++; break; case 'function': functionCount++; break; default: defaultBlockCount++; break; } } } // Base score for having a program if (programBlocks > 0) { creationScore += 10; } // Award points for program complexity creationScore += variableCount * 15; creationScore += loopCount * 25; creationScore += conditionCount * 20; creationScore += functionCount * 20; creationScore += defaultBlockCount * 10; // Bonus for more complex programs if (programBlocks >= 5) { creationScore += 50; } if (variableCount > 0 && loopCount > 0 && conditionCount > 0) { creationScore += 100; // Bonus for using all major programming concepts } // Play success sound if we have a reasonably complex program if (programBlocks >= 3 && (loopCount > 0 || conditionCount > 0)) { LK.getSound('complete').play(); } } // Handle game events game.move = function (x, y, obj) { if (draggedBlock && draggedBlock.isDragging) { draggedBlock.x = x; draggedBlock.y = y; } }; game.down = function (x, y, obj) { // Default handler, individual objects have their own handlers }; game.up = function (x, y, obj) { // Default handler, individual objects have their own handlers }; // Start background music LK.playMusic('bgmusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BlockPalette = Container.expand(function (width, height) {
var self = Container.call(this);
self.paletteWidth = width || 400;
self.paletteHeight = height || 2732;
// Create background
self.background = self.addChild(LK.getAsset('codeBlock', {
anchorX: 0,
anchorY: 0,
width: self.paletteWidth,
height: self.paletteHeight,
alpha: 0.3,
tint: 0x333333
}));
// Add title
self.title = new Text2("Block Palette", {
size: 40,
fill: 0xFFFFFF
});
self.title.anchor.set(0.5, 0);
self.title.x = self.paletteWidth / 2;
self.title.y = 30;
self.addChild(self.title);
// Add blocks to palette
self.addBlockToPalette = function (type, text, x, y) {
var block = new CodeBlock(type, text);
block.x = x;
block.y = y;
// This is a template block - when dragged, it creates a new instance
block.down = function (x, y, obj) {
// Create a new block when this template is clicked
var newBlock = new CodeBlock(type, text);
newBlock.x = block.x;
newBlock.y = block.y;
// Convert to global position
var globalPos = self.parent.toGlobal({
x: block.x,
y: block.y
});
newBlock.x = globalPos.x;
newBlock.y = globalPos.y;
// Add to game's blocks array
blocks.push(newBlock);
// Add to game
game.addChild(newBlock);
// Start dragging the new block
newBlock.startDragX = newBlock.x;
newBlock.startDragY = newBlock.y;
newBlock.isDragging = true;
draggedBlock = newBlock;
};
self.addChild(block);
return block;
};
return self;
});
var Button = Container.expand(function (text, width, height, color) {
var self = Container.call(this);
self.buttonWidth = width || 200;
self.buttonHeight = height || 80;
self.buttonColor = color || 0x4287f5;
// Create button background
self.background = self.addChild(LK.getAsset('codeBlock', {
anchorX: 0.5,
anchorY: 0.5,
width: self.buttonWidth,
height: self.buttonHeight,
tint: self.buttonColor
}));
// Add text
self.label = new Text2(text, {
size: 30,
fill: 0xFFFFFF
});
self.label.anchor.set(0.5, 0.5);
self.addChild(self.label);
// Handle down event
self.down = function (x, y, obj) {
// Visual feedback
tween(self.background, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
// Handle up event
self.up = function (x, y, obj) {
// Reset scale
tween(self.background, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
onFinish: function onFinish() {
// Call the button's action
if (self.action) {
self.action();
}
}
});
};
return self;
});
var CodeArea = Container.expand(function (width, height) {
var self = Container.call(this);
self.areaWidth = width || 1200;
self.areaHeight = height || 1500;
// Create background
self.background = self.addChild(LK.getAsset('codeBlock', {
anchorX: 0,
anchorY: 0,
width: self.areaWidth,
height: self.areaHeight,
alpha: 0.2,
tint: 0x222222
}));
// Method to check if a point is inside the code area
self.isPointInside = function (x, y) {
var globalPos = game.toLocal({
x: x,
y: y
}, self.parent);
return globalPos.x >= self.x && globalPos.x <= self.x + self.areaWidth && globalPos.y >= self.y && globalPos.y <= self.y + self.areaHeight;
};
return self;
});
var CodeBlock = Container.expand(function (type, text) {
var self = Container.call(this);
self.type = type || 'default';
self.text = text || '';
self.connections = {
top: null,
bottom: null,
left: null,
right: null
};
self.canConnect = true;
self.isExecuting = false;
// Select the correct asset based on type
var assetId = 'codeBlock';
if (self.type === 'variable') {
assetId = 'codeBlockVar';
} else if (self.type === 'loop') {
assetId = 'codeBlockLoop';
} else if (self.type === 'condition') {
assetId = 'codeBlockCondition';
} else if (self.type === 'function') {
assetId = 'codeBlockFunction';
}
// Create main block
self.blockGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Add connection points
self.connectionPoints = {};
// Top connection
self.connectionPoints.top = self.addChild(LK.getAsset('connectionPoint', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -self.blockGraphics.height / 2
}));
// Bottom connection
self.connectionPoints.bottom = self.addChild(LK.getAsset('connectionPoint', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.blockGraphics.height / 2
}));
// Left connection (only for condition and loop types)
if (self.type === 'condition' || self.type === 'loop') {
self.connectionPoints.left = self.addChild(LK.getAsset('connectionPoint', {
anchorX: 0.5,
anchorY: 0.5,
x: -self.blockGraphics.width / 2,
y: 0
}));
}
// Right connection (only for condition type)
if (self.type === 'condition') {
self.connectionPoints.right = self.addChild(LK.getAsset('connectionPoint', {
anchorX: 0.5,
anchorY: 0.5,
x: self.blockGraphics.width / 2,
y: 0
}));
}
// Add text label
self.label = new Text2(self.text, {
size: 24,
fill: 0xFFFFFF
});
self.label.anchor.set(0.5, 0.5);
self.addChild(self.label);
// Execution highlight (initially invisible)
self.highlight = self.addChild(LK.getAsset('executionHighlight', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
}));
// Handle interaction events
self.down = function (x, y, obj) {
if (!programIsRunning) {
self.startDragX = self.x;
self.startDragY = self.y;
self.isDragging = true;
draggedBlock = self;
// Bring to front
if (self.parent) {
var parent = self.parent;
parent.removeChild(self);
parent.addChild(self);
}
}
};
self.move = function (x, y, obj) {
// Movement handled by game's move handler
};
self.up = function (x, y, obj) {
if (self.isDragging) {
self.isDragging = false;
draggedBlock = null;
// Check if we're close to another block's connection point
var connected = false;
for (var i = 0; i < blocks.length; i++) {
var otherBlock = blocks[i];
if (otherBlock !== self && otherBlock.canConnect) {
connected = self.tryConnectTo(otherBlock);
if (connected) {
break;
}
}
}
if (!connected && codeArea.isPointInside(self.x, self.y)) {
// Snap to grid if in code area
self.x = Math.round(self.x / grid.cellSize) * grid.cellSize;
self.y = Math.round(self.y / grid.cellSize) * grid.cellSize;
} else if (!connected && !codeArea.isPointInside(self.x, self.y)) {
// Return to palette area if not connected and outside code area
self.x = self.startDragX;
self.y = self.startDragY;
}
}
};
self.tryConnectTo = function (otherBlock) {
if (!otherBlock || otherBlock === self) {
return false;
}
// Check proximity to connection points
var connectionThreshold = 50;
var myPoints = self.connectionPoints;
var otherPoints = otherBlock.connectionPoints;
// Check all possible connections
for (var myPos in myPoints) {
if (!myPoints[myPos]) {
continue;
}
var myGlobalPos = self.parent.toGlobal(myPoints[myPos].position);
for (var otherPos in otherPoints) {
if (!otherPoints[otherPos]) {
continue;
}
// Skip if this connection is already occupied
if (otherBlock.connections[otherPos]) {
continue;
}
var otherGlobalPos = otherBlock.parent.toGlobal(otherPoints[otherPos].position);
var distance = Math.sqrt(Math.pow(myGlobalPos.x - otherGlobalPos.x, 2) + Math.pow(myGlobalPos.y - otherGlobalPos.y, 2));
// If close enough, connect
if (distance < connectionThreshold) {
// Connect based on valid combinations
var validConnection = false;
// Top to bottom connections
if (myPos === 'bottom' && otherPos === 'top' || myPos === 'top' && otherPos === 'bottom') {
validConnection = true;
}
// Left/right connections for condition/loop blocks
if ((self.type === 'condition' || self.type === 'loop') && otherBlock.type !== 'condition' && otherBlock.type !== 'loop') {
if (myPos === 'left' && otherPos === 'top' || myPos === 'right' && otherPos === 'top') {
validConnection = true;
}
}
if (validConnection) {
self.connectTo(otherBlock, myPos, otherPos);
return true;
}
}
}
}
return false;
};
self.connectTo = function (otherBlock, myPos, otherPos) {
// Position calculation
if (myPos === 'bottom' && otherPos === 'top') {
// Align other block below this one
otherBlock.x = self.x;
otherBlock.y = self.y + self.blockGraphics.height;
// Update connections
self.connections.bottom = otherBlock;
otherBlock.connections.top = self;
} else if (myPos === 'top' && otherPos === 'bottom') {
// Align this block below other one
self.x = otherBlock.x;
self.y = otherBlock.y + otherBlock.blockGraphics.height;
// Update connections
self.connections.top = otherBlock;
otherBlock.connections.bottom = self;
} else if (myPos === 'left' && otherPos === 'top') {
// Align other block to the left of this one
otherBlock.x = self.x - self.blockGraphics.width / 2;
otherBlock.y = self.y;
// Update connections
self.connections.left = otherBlock;
otherBlock.connections.top = self;
} else if (myPos === 'right' && otherPos === 'top') {
// Align other block to the right of this one
otherBlock.x = self.x + self.blockGraphics.width / 2;
otherBlock.y = self.y;
// Update connections
self.connections.right = otherBlock;
otherBlock.connections.top = self;
}
// Play connection sound
LK.getSound('connect').play();
};
self.execute = function (callback) {
self.isExecuting = true;
// Show execution highlight
self.highlight.alpha = 0.5;
// Play execution sound
LK.getSound('execute').play();
// Execute block action based on type
var executionTime = 800; // Default execution time
switch (self.type) {
case 'variable':
// Variable assignment
executionLog.push("➡️ Set variable: " + self.text);
// Visual effect for variable assignment
tween(self.blockGraphics, {
alpha: 0.5
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.blockGraphics, {
alpha: 1
}, {
duration: 300
});
}
});
break;
case 'loop':
// Loop block execution
executionLog.push("🔄 Loop: " + self.text);
// Visual effect for loops - make it pulse
tween(self.blockGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 400,
onFinish: function onFinish() {
tween(self.blockGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 400
});
}
});
// For loops, we need to execute differently
executionTime = 1000;
break;
case 'condition':
// Condition evaluation
executionLog.push("⚖️ If condition: " + self.text);
// Visual effect for conditions - flash slightly
tween(self.blockGraphics, {
alpha: 0.7
}, {
duration: 200,
onFinish: function onFinish() {
tween(self.blockGraphics, {
alpha: 1
}, {
duration: 200
});
}
});
break;
case 'function':
// Function call
executionLog.push("🧩 Call function: " + self.text);
// Visual effect for functions - rotate slightly
tween(self.blockGraphics, {
rotation: 0.1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.blockGraphics, {
rotation: 0
}, {
duration: 300
});
}
});
break;
default:
// Default block
executionLog.push("▶️ Execute: " + self.text);
// Visual effect for standard blocks
tween(self.blockGraphics, {
alpha: 0.8
}, {
duration: 200,
onFinish: function onFinish() {
tween(self.blockGraphics, {
alpha: 1
}, {
duration: 200
});
}
});
break;
}
// Update execution log display
updateExecutionLog();
// After execution, continue to next block
LK.setTimeout(function () {
self.highlight.alpha = 0;
self.isExecuting = false;
if (callback) {
callback();
}
}, executionTime);
};
return self;
});
var PreviewDisplay = Container.expand(function () {
var self = Container.call(this);
// Create the white cube display area
self.display = self.addChild(LK.getAsset('codeBlock', {
anchorX: 0,
anchorY: 0,
width: 500,
height: 500,
tint: 0xffffff
}));
// Title for the preview area
self.title = new Text2("Program Output", {
size: 30,
fill: 0x000000
});
self.title.anchor.set(0.5, 0);
self.title.x = self.display.width / 2;
self.title.y = 20;
self.addChild(self.title);
// Program output text
self.outputText = new Text2("Your program will run here", {
size: 24,
fill: 0x000000,
wordWrap: true,
wordWrapWidth: 460
});
self.outputText.anchor.set(0.5, 0);
self.outputText.x = self.display.width / 2;
self.outputText.y = 80;
self.addChild(self.outputText);
// Update the output display
self.updateOutput = function (results) {
if (!results || results.length === 0) {
self.outputText.setText("Your program will run here");
return;
}
var displayText = "";
// Take the last 5 operations or fewer
var startIndex = Math.max(0, results.length - 5);
for (var i = startIndex; i < results.length; i++) {
displayText += results[i] + "\n\n";
}
self.outputText.setText(displayText);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a24
});
/****
* Game Code
****/
// Grid settings
var grid = {
cellSize: 100
};
// Game state
var programIsRunning = false;
var draggedBlock = null;
var blocks = [];
var executionLog = [];
var creationScore = 0; // Score based on program complexity
// Function to update the execution log display
function updateExecutionLog() {
var logText = "Execution Log:\n";
for (var i = 0; i < executionLog.length; i++) {
logText += "- " + executionLog[i] + "\n";
}
executionLogText.setText(logText);
}
// Create the code area
var codeArea = new CodeArea(1200, 1500);
codeArea.x = 424; // Offset from left edge
codeArea.y = 150; // Offset from top
game.addChild(codeArea);
// Create block palette
var blockPalette = new BlockPalette(400, 2732);
blockPalette.x = 20; // Left edge
blockPalette.y = 0; // Top edge
game.addChild(blockPalette);
// Add blocks to palette
var yOffset = 100;
var blockSpacing = 120;
// Basic blocks
blockPalette.addBlockToPalette('default', "Print 'Hello World'", blockPalette.paletteWidth / 2, yOffset + blockSpacing);
blockPalette.addBlockToPalette('default', "Display Message", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 2);
blockPalette.addBlockToPalette('default', "Play Sound", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 3);
// Variable blocks
blockPalette.addBlockToPalette('variable', "x = 5", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 4);
blockPalette.addBlockToPalette('variable', "y = 10", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 5);
blockPalette.addBlockToPalette('variable', "counter = 0", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 6);
blockPalette.addBlockToPalette('variable', "name = 'Player'", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 7);
// Loop blocks
blockPalette.addBlockToPalette('loop', "Repeat 3 times", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 8);
blockPalette.addBlockToPalette('loop', "While true", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 9);
blockPalette.addBlockToPalette('loop', "For each item", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 10);
// Condition blocks
blockPalette.addBlockToPalette('condition', "If x > 0", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 11);
blockPalette.addBlockToPalette('condition', "If x == y", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 12);
blockPalette.addBlockToPalette('condition', "If counter > 10", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 13);
// Function blocks
blockPalette.addBlockToPalette('function', "Calculate Sum", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 14);
blockPalette.addBlockToPalette('function', "Get Random Number", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 15);
blockPalette.addBlockToPalette('function', "Convert to String", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 16);
// Action blocks
blockPalette.addBlockToPalette('default', "Draw Shape", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 17);
blockPalette.addBlockToPalette('default', "Move Forward", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 18);
blockPalette.addBlockToPalette('default', "Turn Right", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 19);
blockPalette.addBlockToPalette('default', "Turn Left", blockPalette.paletteWidth / 2, yOffset + blockSpacing * 20);
// Create program builder title
var titleText = new Text2("BlockCode Builder", {
size: 50,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = codeArea.x + codeArea.areaWidth / 2;
titleText.y = 50;
game.addChild(titleText);
// Create instructions text
var instructionsText = new Text2("Create and run your own programs by connecting blocks from the palette", {
size: 30,
fill: 0xFFFFFF,
wordWrap: true,
wordWrapWidth: 1100
});
instructionsText.anchor.set(0.5, 0);
instructionsText.x = codeArea.x + codeArea.areaWidth / 2;
instructionsText.y = 110;
game.addChild(instructionsText);
// Execution log
var executionLogText = new Text2("Execution Log:\n", {
size: 24,
fill: 0xFFFFFF,
wordWrap: true,
wordWrapWidth: 1100
});
executionLogText.anchor.set(0, 0);
executionLogText.x = codeArea.x + 50;
executionLogText.y = codeArea.y + codeArea.areaHeight + 20;
game.addChild(executionLogText);
// Create run button
var runButton = new Button("Execute Program", 220, 80, 0x4CAF50);
runButton.x = codeArea.x + codeArea.areaWidth - 150;
runButton.y = codeArea.y + codeArea.areaHeight + 50;
runButton.action = function () {
if (!programIsRunning) {
runProgram();
}
};
game.addChild(runButton);
// Create reset button
var resetButton = new Button("Clear Canvas", 180, 80, 0xF44336);
resetButton.x = codeArea.x + 100;
resetButton.y = codeArea.y + codeArea.areaHeight + 50;
resetButton.action = function () {
resetProgram();
};
game.addChild(resetButton);
// Create preview display area
var previewDisplay = new PreviewDisplay();
previewDisplay.x = codeArea.x + codeArea.areaWidth / 2 - 250; // Center horizontally under code area
previewDisplay.y = 2732 - 550; // Position at bottom of screen
game.addChild(previewDisplay);
// Create score display
var scoreText = new Text2("Score: 0", {
size: 40,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = 2048 - 50;
scoreText.y = 50;
game.addChild(scoreText);
// Function to find all start blocks (blocks without a top connection in code area)
function findStartBlocks() {
var startBlocks = [];
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
if (codeArea.isPointInside(block.x, block.y) && !block.connections.top) {
startBlocks.push(block);
}
}
return startBlocks;
}
// Function to run the program
function runProgram() {
// Clear execution log
executionLog = [];
updateExecutionLog();
// Find all starting blocks
var startingBlocks = [];
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
if (codeArea.isPointInside(block.x, block.y) && !block.connections.top) {
startingBlocks.push(block);
}
}
if (startingBlocks.length === 0) {
executionLog.push("No starting blocks found!");
updateExecutionLog();
// Update preview display
previewDisplay.updateOutput(["No starting blocks found!"]);
return;
}
programIsRunning = true;
// Calculate program complexity before execution
calculateProgramComplexity();
// Execute all starting blocks in sequence
var currentBlockIndex = 0;
function executeNextBlock() {
if (currentBlockIndex < startingBlocks.length) {
executionLog.push("--- Running Program " + (currentBlockIndex + 1) + " ---");
updateExecutionLog();
executeBlock(startingBlocks[currentBlockIndex], function () {
currentBlockIndex++;
executeNextBlock();
});
} else {
// All programs executed
programIsRunning = false;
// Reward the player for successfully running programs
LK.setScore(creationScore);
scoreText.setText("Score: " + LK.getScore());
// Update the preview display with the execution results
previewDisplay.updateOutput(executionLog);
// Play completion sound for visual feedback
if (executionLog.length > 0) {
LK.getSound('complete').play();
}
}
}
// Start executing blocks
executeNextBlock();
}
// Function to execute a block and its connected blocks
function executeBlock(block, callback) {
if (!block) {
if (callback) {
callback();
}
return;
}
block.execute(function () {
// After execution, continue to next block based on type
if (block.type === 'condition') {
// For condition, randomly go left or right branch
var randomBranch = Math.random() > 0.5 ? 'left' : 'right';
if (block.connections[randomBranch]) {
executeBlock(block.connections[randomBranch], function () {
// After branch execution, continue with bottom block if any
executeBlock(block.connections.bottom, callback);
});
} else {
// No branch, go to bottom
executeBlock(block.connections.bottom, callback);
}
} else if (block.type === 'loop') {
// For loop, execute left branch twice, then continue
if (block.connections.left) {
executeBlock(block.connections.left, function () {
// Execute loop content again
if (block.connections.left) {
executeBlock(block.connections.left, function () {
// Then continue with bottom block
executeBlock(block.connections.bottom, callback);
});
} else {
executeBlock(block.connections.bottom, callback);
}
});
} else {
// No loop content, go to bottom
executeBlock(block.connections.bottom, callback);
}
} else {
// For other blocks, just go to bottom
executeBlock(block.connections.bottom, callback);
}
});
}
// Function to reset the program
function resetProgram() {
// Clear blocks in the code area
var blocksRemoved = 0;
for (var i = blocks.length - 1; i >= 0; i--) {
var block = blocks[i];
if (codeArea.isPointInside(block.x, block.y)) {
block.destroy();
blocks.splice(i, 1);
blocksRemoved++;
}
}
// Clear execution log
executionLog = [];
executionLog.push("Program reset. " + blocksRemoved + " blocks removed.");
updateExecutionLog();
// Reset preview display
previewDisplay.updateOutput(["Program has been reset. Add blocks to create a new program."]);
// Reset score
creationScore = 0;
}
// Function to calculate program complexity and score
function calculateProgramComplexity() {
// Reset score for this program
creationScore = 0;
// Count total blocks in the program
var programBlocks = 0;
var variableCount = 0;
var loopCount = 0;
var conditionCount = 0;
var functionCount = 0;
var defaultBlockCount = 0;
// Count different types of blocks in the code area
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
if (codeArea.isPointInside(block.x, block.y)) {
programBlocks++;
switch (block.type) {
case 'variable':
variableCount++;
break;
case 'loop':
loopCount++;
break;
case 'condition':
conditionCount++;
break;
case 'function':
functionCount++;
break;
default:
defaultBlockCount++;
break;
}
}
}
// Base score for having a program
if (programBlocks > 0) {
creationScore += 10;
}
// Award points for program complexity
creationScore += variableCount * 15;
creationScore += loopCount * 25;
creationScore += conditionCount * 20;
creationScore += functionCount * 20;
creationScore += defaultBlockCount * 10;
// Bonus for more complex programs
if (programBlocks >= 5) {
creationScore += 50;
}
if (variableCount > 0 && loopCount > 0 && conditionCount > 0) {
creationScore += 100; // Bonus for using all major programming concepts
}
// Play success sound if we have a reasonably complex program
if (programBlocks >= 3 && (loopCount > 0 || conditionCount > 0)) {
LK.getSound('complete').play();
}
}
// Handle game events
game.move = function (x, y, obj) {
if (draggedBlock && draggedBlock.isDragging) {
draggedBlock.x = x;
draggedBlock.y = y;
}
};
game.down = function (x, y, obj) {
// Default handler, individual objects have their own handlers
};
game.up = function (x, y, obj) {
// Default handler, individual objects have their own handlers
};
// Start background music
LK.playMusic('bgmusic');