// NOTICE: GOTO is evil. (function(Scratch) { 'use strict'; if (!Scratch.extensions.unsandboxed) { throw new Error('This Hello World example must run unsandboxed'); } let gotoLabels = new Map(); let resetVars = () => gotoLabels = new Map(); Scratch.vm.on("PROJECT_START", resetVars); Scratch.vm.on("PROJECT_STOP", resetVars); class GOTO { getInfo() { return { id: 'helloworldunsandboxed', name: 'Unsandboxed Hello World', blocks: [ { opcode: 'createGotoLabel', blockType: Scratch.BlockType.COMMAND, text: "create goto label with name [name] for this script", arguments: { name: { type: Scratch.ArgumentType.STRING, defaultValue: "label" } } }, { opcode: "gotoLabel", blockType: Scratch.BlockType.COMMAND, text: "skip to label with [name] in this script", arguments: { name: { type: Scratch.ArgumentType.STRING, defaultValue: "label", } } } ] }; } createGotoLabel({name}, util) { if (!gotoLabels.get(util.thread)) { gotoLabels.set(util.thread, {}) } // set current if (!gotoLabels.get(util.thread)[name]) { // util.thread.peekStack gives either opcode or the block id. gotoLabels.get(util.thread)[name] = util.thread.peekStack(); } else { console.log("imagine creating the same goto label twice, as if goto wasnt already evil enough") } } gotoLabel({name}, util) { // if this block fails it doesnt goto let blockId = gotoLabels.get(util.thread)[name]; console.log(util.thread.stack) if (blockId && util.target.blocks._blocks[blockId].id) { //console.log(util.target.blocks._blocks[blockId]); //util.thread.pushStack(util.target.blocks._blocks[blockId].id); util.thread.stack[util.thread.stack.length - 1] = blockId } console.log(util.thread.stack) } } Scratch.extensions.register(new GOTO()); })(Scratch);