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