From b5be1211c574e550f75f4d92e8976406c9248c2d Mon Sep 17 00:00:00 2001 From: lego7set <84934297+lego7set@users.noreply.github.com> Date: Fri, 2 May 2025 17:32:25 -0400 Subject: [PATCH 1/4] add shape for sprite blocks --- src/patches/blockly/renderer.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/patches/blockly/renderer.js b/src/patches/blockly/renderer.js index a491ee5..2061a97 100644 --- a/src/patches/blockly/renderer.js +++ b/src/patches/blockly/renderer.js @@ -18,6 +18,33 @@ class CustomConstantProvider extends Blockly.zelos.ConstantProvider { this.ADD_START_HATS = true this.PLUS = this.makePlus() + this.ARROW = this.makeArrow(); + } + + /** @returns {import("blockly/core/renderers/common/constants").Shape} */ + makeArrow() { + const maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + return { + type: this.SHAPES.HEXAGONAL, + isDynamic: true, + width(height) { + const halfHeight = height / 2; + return halfHeight > maxWidth ? maxWidth : halfHeight + }, + height(height) { + return height; + }, + connectionOffsetY(connectionHeight) { + return connectionHeight / 2; + }, + connectionOffsetX(connectionWidth) { + return -connectionWidth; + }, + pathDown: this.SQUARED.pathDown, + pathUp: this.SQUARED.pathUp, + pathRightDown: this.HEXAGONAL.pathRightDown, + pathRightUp: this.HEXAGONAL.pathRightUp + } } /** @returns {import("blockly/core/renderers/common/constants").Shape} */ @@ -103,6 +130,8 @@ class CustomConstantProvider extends Blockly.zelos.ConstantProvider { if (connection.type == Blockly.ConnectionType.INPUT_VALUE || connection.type == Blockly.ConnectionType.OUTPUT_VALUE) { if (checks && checks.length > 1) { return this.ROUNDED; + } else if (checks && checks.includes('Sprite')) { + return this.ARROW; } else if (checks && checks.includes('List')) { return this.PLUS; } else if (checks && checks.includes('String')) { From 9f9eb3409c58d967a5548a696d4d1e8633c26cce Mon Sep 17 00:00:00 2001 From: lego7set <84934297+lego7set@users.noreply.github.com> Date: Fri, 2 May 2025 18:16:18 -0400 Subject: [PATCH 2/4] add sprites blocks & fix an error i found --- src/lib/Toolbox/Toolbox.xml | 4 +++ src/resources/blocks/index.js | 2 ++ src/resources/blocks/lists.js | 2 +- src/resources/blocks/sprites.js | 57 +++++++++++++++++++++++++++++++++ src/resources/compiler/index.js | 2 +- 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/resources/blocks/sprites.js diff --git a/src/lib/Toolbox/Toolbox.xml b/src/lib/Toolbox/Toolbox.xml index c2327e2..58c7a91 100644 --- a/src/lib/Toolbox/Toolbox.xml +++ b/src/lib/Toolbox/Toolbox.xml @@ -114,6 +114,10 @@ + + + + diff --git a/src/resources/blocks/index.js b/src/resources/blocks/index.js index 9466bbf..b26ca7c 100644 --- a/src/resources/blocks/index.js +++ b/src/resources/blocks/index.js @@ -6,6 +6,7 @@ import registerStrings from "./strings"; import registerInputs from "./inputs"; import registerVariables from "./variables"; import registerLists from "./lists"; +import registerSprites from "./sprites"; import registerBlocks from "./blocks"; import registerRuntime from "./runtime"; @@ -20,6 +21,7 @@ export default () => { registerInputs(); registerVariables(); registerLists(); + registerSprites(); registerBlocks(); registerRuntime(); diff --git a/src/resources/blocks/lists.js b/src/resources/blocks/lists.js index 825069d..e3e14d5 100644 --- a/src/resources/blocks/lists.js +++ b/src/resources/blocks/lists.js @@ -96,7 +96,7 @@ function register() { }, (block) => { const INDEX = javascriptGenerator.valueToCode(block, 'INDEX') const INPUT = javascriptGenerator.valueToCode(block, 'INPUT') - const VALUE = javascriptGenerator.valueToCode(block, 'VALUE') + const VALUE = javascriptGenerator.valueToCode(block, 'TEXT') const code = `ExtForge.Utils.setList(${INPUT}, ${INDEX}, ${VALUE})`; return [`${code}`, 0]; }) diff --git a/src/resources/blocks/sprites.js b/src/resources/blocks/sprites.js new file mode 100644 index 0000000..577f49d --- /dev/null +++ b/src/resources/blocks/sprites.js @@ -0,0 +1,57 @@ +import javascriptGenerator from '../javascriptGenerator'; +import { registerBlock } from '../register'; + +const categoryPrefix = 'sprites_'; +const categoryColor = '#18f'; + +function register() { + /*registerBlock(`${categoryPrefix}evalb`, { + message0: 'eval %1', + args0: [ + { + "type": "field_input", + "name": "INPUT", + "check": "String", + "text": "alert(\"hi\")", + "acceptsBlocks": true + }, + ], + previousStatement: null, + nextStatement: null, + inputsInline: true, + colour: categoryColor + }, (block) => { + const INPUT = javascriptGenerator.valueToCode(block, 'INPUT'); + const code = `eval(${INPUT})`; + return `${code}\n`; + })*/ + registerBlock(`${categoryPrefix}stage`, { + message0: 'stage sprite', + args0: [], + output: "Sprite", + inputsInline: true, + colour: categoryColor + }, (block) => { + return ["Scratch.vm.runtime.getTargetForStage()", 0]; + }) + registerBlock(`${categoryPrefix}getSpriteFromName`, { + message0: "get sprite named %1", + args0: [ + { + "type": "field_input", + "name": "NAME", + "check": "String", + "text": "Sprite1", + "acceptsBlocks": true + } + ], + output: "Sprite", + inputsInline: true, + colour: categoryColor + }, (block) => { + const INPUT = javascriptGenerator.valueToCode(block, 'NAME'); + return [`(()=>{const name = String(${INPUT}); const res = Scratch.vm.runtime.getSpriteTargetByName(name); if (!res) throw "Unable to find sprite named " + name; return res;})()`, 0] + }) +} + +export default register; \ No newline at end of file diff --git a/src/resources/compiler/index.js b/src/resources/compiler/index.js index 8eb8ba1..fedd9bb 100644 --- a/src/resources/compiler/index.js +++ b/src/resources/compiler/index.js @@ -135,7 +135,7 @@ class Compiler { workspace.getTopBlocks().find(v => v.type == "blocks_define" && v.blockId_ == id), "BLOCKS" ) - return `async block_${id}(args) { ${blockCode} }` + return `async block_${id}(args, blockUtils) { ${blockCode} }` }), classRegistry.bottom, code, footerCode).join('\n'); } } From a85e440d43270674435051d9de64e7fb687dbb61 Mon Sep 17 00:00:00 2001 From: lego7set <84934297+lego7set@users.noreply.github.com> Date: Fri, 2 May 2025 20:06:30 -0400 Subject: [PATCH 3/4] add more sprite blocks + fix sprite shape --- src/lib/Toolbox/Toolbox.xml | 18 +++ src/patches/blockly/renderer.js | 18 ++- src/resources/blocks/sprites.js | 227 +++++++++++++++++++++++++++++++- src/resources/compiler/index.js | 3 + 4 files changed, 263 insertions(+), 3 deletions(-) diff --git a/src/lib/Toolbox/Toolbox.xml b/src/lib/Toolbox/Toolbox.xml index 58c7a91..d0a7730 100644 --- a/src/lib/Toolbox/Toolbox.xml +++ b/src/lib/Toolbox/Toolbox.xml @@ -117,6 +117,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/patches/blockly/renderer.js b/src/patches/blockly/renderer.js index 2061a97..4bfa9e1 100644 --- a/src/patches/blockly/renderer.js +++ b/src/patches/blockly/renderer.js @@ -24,6 +24,20 @@ class CustomConstantProvider extends Blockly.zelos.ConstantProvider { /** @returns {import("blockly/core/renderers/common/constants").Shape} */ makeArrow() { const maxWidth = this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH; + // try to get rid of the VERY noticeable gap + function makeLeftPath(height, up) { + const halfHeight = height / 2; + const width = halfHeight > maxWidth ? maxWidth : halfHeight; + const forward = up ? -1 : 1; + const direction = 1; + const dy = (forward * height) / 2; + return ( + svgPaths.lineOnAxis("h", -width) + + svgPaths.lineOnAxis("v", dy * 2) + //svgPaths.lineTo(-width, up ? (forward * height) : 0) + + //svgPaths.lineOnAxis("v", dy) + ); + } return { type: this.SHAPES.HEXAGONAL, isDynamic: true, @@ -40,8 +54,8 @@ class CustomConstantProvider extends Blockly.zelos.ConstantProvider { connectionOffsetX(connectionWidth) { return -connectionWidth; }, - pathDown: this.SQUARED.pathDown, - pathUp: this.SQUARED.pathUp, + pathDown: (height) => makeLeftPath(height, false), //this.SQUARED.pathDown, + pathUp: (height) => makeLeftPath(height, true), //this.SQUARED.pathUp, pathRightDown: this.HEXAGONAL.pathRightDown, pathRightUp: this.HEXAGONAL.pathRightUp } diff --git a/src/resources/blocks/sprites.js b/src/resources/blocks/sprites.js index 577f49d..6c38607 100644 --- a/src/resources/blocks/sprites.js +++ b/src/resources/blocks/sprites.js @@ -1,5 +1,6 @@ import javascriptGenerator from '../javascriptGenerator'; import { registerBlock } from '../register'; +import util from "../util"; const categoryPrefix = 'sprites_'; const categoryColor = '#18f'; @@ -35,7 +36,7 @@ function register() { return ["Scratch.vm.runtime.getTargetForStage()", 0]; }) registerBlock(`${categoryPrefix}getSpriteFromName`, { - message0: "get sprite named %1", + message0: "sprite named %1", args0: [ { "type": "field_input", @@ -52,6 +53,230 @@ function register() { const INPUT = javascriptGenerator.valueToCode(block, 'NAME'); return [`(()=>{const name = String(${INPUT}); const res = Scratch.vm.runtime.getSpriteTargetByName(name); if (!res) throw "Unable to find sprite named " + name; return res;})()`, 0] }) + registerBlock(`${categoryPrefix}getSpriteThatRanBlock`, { + message0: "sprite that ran this block", + args0: [], + output: "Sprite", + inputsInline: true, + colour: categoryColor + }, (block) => { + return [`(()=>{if(typeof blockUtils === "undefined")throw "This only works inside blocks ran by sprites";return blockUtils.target})()`, 0] + }) + registerBlock(`${categoryPrefix}getListOfSprites`, { + message0: "get list of all sprites/clones", + args0: [], + output: "List", + inputsInline: true, + colour: categoryColor + }, (block) => { + return [`(new Proxy(Scratch.vm.runtime.executableTargets, {set: ()=>{throw "The list of all sprites is not mutable";}}))`, 0] + }) + registerBlock(`${categoryPrefix}getClonesOfSprite`, { + message0: "clones of %1 (including itself)", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "List", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`(new Proxy(${SPRITE}.sprite.clones, {set: ()=>{throw "The list of clones of a sprite is not mutable";}}))`, 0] + }) + registerBlock(`${categoryPrefix}getNameOfSprite`, { + message0: "get name of %1", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "Number", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`${SPRITE}.getName()`, 0] + }) + registerBlock(`${categoryPrefix}getIdOfSprite`, { + message0: "get id of %1", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "Number", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`${SPRITE}.id`, 0] + }) + registerBlock(`${categoryPrefix}getXposOfSprite`, { + message0: "get x position of %1", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "Number", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`${SPRITE}.x`, 0] + }) + registerBlock(`${categoryPrefix}getYposOfSprite`, { + message0: "get y position of %1", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "Number", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`${SPRITE}.y`, 0] + }) + registerBlock(`${categoryPrefix}getDirectionOfSprite`, { + message0: "get direction of %1", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "Number", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`${SPRITE}.direction`, 0] + }) + registerBlock(`${categoryPrefix}setXposOfSprite`, { + message0: 'set x position of %1 to %2', + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + }, + { + "type": "field_number", + "name": "INPUT", + "check": null, + "text": "0", + "acceptsBlocks": true + } + ], + previousStatement: null, + nextStatement: null, + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + const VALUE = javascriptGenerator.valueToCode(block, 'INPUT'); + const code = `((spriteVariable)=>spriteVariable.setXY(Scratch.Cast.toNumber(${VALUE}), spriteVariable.y))(${SPRITE})`; + return `${code}\n`; + }) + registerBlock(`${categoryPrefix}setYposOfSprite`, { + message0: 'set y position of %1 to %2', + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + }, + { + "type": "field_number", + "name": "INPUT", + "check": null, + "text": "0", + "acceptsBlocks": true + } + ], + previousStatement: null, + nextStatement: null, + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + const VALUE = javascriptGenerator.valueToCode(block, 'INPUT'); + const code = `((spriteVariable)=>spriteVariable.setXY(spriteVariable.x, Scratch.Cast.toNumber(${VALUE})))(${SPRITE})`; + return `${code}\n`; + }) + registerBlock(`${categoryPrefix}setDirectionOfSprite`, { + message0: 'set direction of %1 to %2', + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + }, + { + "type": "field_number", + "name": "INPUT", + "check": null, + "text": "90", + "acceptsBlocks": true + } + ], + previousStatement: null, + nextStatement: null, + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + const VALUE = javascriptGenerator.valueToCode(block, 'INPUT'); + const code = `((spriteVariable)=>spriteVariable.setDirection(Scratch.Cast.toNumber(${VALUE})))(${SPRITE})`; + return `${code}\n`; + }) + registerBlock(`${categoryPrefix}hasSpriteBeenDeleted`, { + message0: "has %1 been deleted?", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "Boolean", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`(Scratch.vm.runtime.executableTargets.indexOf(${SPRITE}) !== -1)`, 0] + }) + registerBlock(`${categoryPrefix}isSpriteAClone`, { + message0: "is %1 a clone?", + args0: [ + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + } + ], + output: "Boolean", + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + return [`!(${SPRITE}.isOriginal)`, 0] + }) } export default register; \ No newline at end of file diff --git a/src/resources/compiler/index.js b/src/resources/compiler/index.js index fedd9bb..cc8c261 100644 --- a/src/resources/compiler/index.js +++ b/src/resources/compiler/index.js @@ -41,6 +41,9 @@ const ExtForge = { }, countString: (x, y) => { return y.length == 0 ? 0 : x.split(y).length - 1 + }, + throw: function(err) { + throw err; } } } From d81b42e91a3091f9f8753dcdd03b1c665a7680a9 Mon Sep 17 00:00:00 2001 From: lego7set <84934297+lego7set@users.noreply.github.com> Date: Fri, 2 May 2025 20:26:27 -0400 Subject: [PATCH 4/4] add get/set variable of sprite blocks --- src/lib/Toolbox/Toolbox.xml | 3 ++ src/resources/blocks/sprites.js | 77 ++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/lib/Toolbox/Toolbox.xml b/src/lib/Toolbox/Toolbox.xml index d0a7730..06fc358 100644 --- a/src/lib/Toolbox/Toolbox.xml +++ b/src/lib/Toolbox/Toolbox.xml @@ -135,6 +135,9 @@ + + + diff --git a/src/resources/blocks/sprites.js b/src/resources/blocks/sprites.js index 6c38607..726e4e0 100644 --- a/src/resources/blocks/sprites.js +++ b/src/resources/blocks/sprites.js @@ -6,26 +6,6 @@ const categoryPrefix = 'sprites_'; const categoryColor = '#18f'; function register() { - /*registerBlock(`${categoryPrefix}evalb`, { - message0: 'eval %1', - args0: [ - { - "type": "field_input", - "name": "INPUT", - "check": "String", - "text": "alert(\"hi\")", - "acceptsBlocks": true - }, - ], - previousStatement: null, - nextStatement: null, - inputsInline: true, - colour: categoryColor - }, (block) => { - const INPUT = javascriptGenerator.valueToCode(block, 'INPUT'); - const code = `eval(${INPUT})`; - return `${code}\n`; - })*/ registerBlock(`${categoryPrefix}stage`, { message0: 'stage sprite', args0: [], @@ -277,6 +257,63 @@ function register() { const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; return [`!(${SPRITE}.isOriginal)`, 0] }) + registerBlock(`${categoryPrefix}getVarOfSprite`, { + message0: 'get variable %1 of %2', + args0: [ + { + "type": "field_input", + "name": "INPUT", + "check": null, + "text": "my sprite-only variable", + "acceptsBlocks": true + }, + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + }, + ], + output: null, + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + const VALUE = javascriptGenerator.valueToCode(block, 'INPUT'); + return [`((${SPRITE}.lookupVariableByNameAndType(String(${VALUE})) || {value: 0}).value)`, 0] + }) + registerBlock(`${categoryPrefix}setVarOfSprite`, { + message0: 'set variable %1 of %2 to %3', + args0: [ + { + "type": "field_input", + "name": "INPUT", + "check": null, + "text": "my sprite-only variable", + "acceptsBlocks": true + }, + { + "type": "input_value", + "name": "SPRITE", + "check": "Sprite", + }, + { + "type": "field_input", + "name": "VALUE", + "check": null, + "text": "0", + "acceptsBlocks": true + }, + ], + previousStatement: null, + nextStatement: null, + inputsInline: true, + colour: categoryColor + }, (block) => { + const SPRITE = javascriptGenerator.valueToCode(block, 'SPRITE') || "ExtForge.Utils.throw('Sprite inputs MUST have a sprite inside them')"; + const INPUT = javascriptGenerator.valueToCode(block, 'INPUT'); + const VALUE = javascriptGenerator.valueToCode(block, 'VALUE') + return `((${SPRITE}.lookupVariableByNameAndType(String(${INPUT})) || {value: 0}).value = String(${VALUE}))` + }) } export default register; \ No newline at end of file