const BlockType = require('../../extension-support/block-type'); const ArgumentType = require('../../extension-support/argument-type'); const Cast = require('../../util/cast'); class JgScriptsBlocks { /** * Class for Script blocks * @constructor */ constructor(runtime) { /** * The runtime instantiating this block package. * @type {Runtime} */ this.runtime = runtime; this.scripts = {}; this.runtime.on("PROJECT_START", () => { this.scripts = {} }); this.runtime.on("PROJECT_STOP_ALL", () => { this.scripts = {} }); } /** * @returns {object} metadata for this extension and its blocks. */ getInfo() { return { id: "jgScripts", name: "Scripts", color1: "#8c8c8c", color2: "#7a7a7a", blocks: [ { opcode: "createScript", blockType: BlockType.COMMAND, text: "create script named [NAME]", arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" } }, }, { opcode: "deleteScript", blockType: BlockType.COMMAND, text: "delete script named [NAME]", arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" } }, }, { opcode: "deleteAll", blockType: BlockType.COMMAND, text: "delete all scripts" }, { opcode: "allScripts", blockType: BlockType.REPORTER, text: "all scripts" }, { opcode: "scriptExists", blockType: BlockType.BOOLEAN, text: "script named [NAME] exists?", arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" } }, }, "---", { opcode: "addBlocksTo", blockType: BlockType.COMMAND, text: ["add blocks", "to script [NAME]"], branchCount: 1, arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" } }, }, { opcode: "JGreturn", text: "return [THING]", blockType: BlockType.COMMAND, isTerminal: true, arguments: { THING: { type: ArgumentType.STRING, defaultValue: "1" } }, }, "---", { opcode: "scriptData", text: "script data", blockType: BlockType.REPORTER, allowDropAnywhere: true, disableMonitor: true }, "---", { opcode: "runBlocks", text: "run script [NAME] in [SPRITE]", blockType: BlockType.LOOP, branchCount: 0, branchIconURI: "", arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" }, SPRITE: { type: ArgumentType.STRING, menu: "TARGETS" } }, }, { opcode: "runBlocksData", text: "run script [NAME] in [SPRITE] with data [DATA]", blockType: BlockType.LOOP, branchCount: 0, branchIconURI: "", arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" }, SPRITE: { type: ArgumentType.STRING, menu: "TARGETS" }, DATA: { type: ArgumentType.STRING, defaultValue: "data" } }, }, "---", { opcode: "reportBlocks", text: "run script [NAME] in [SPRITE]", blockType: BlockType.REPORTER, allowDropAnywhere: true, arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" }, SPRITE: { type: ArgumentType.STRING, menu: "TARGETS" } }, }, { opcode: "reportBlocksData", text: "run script [NAME] in [SPRITE] with data [DATA]", blockType: BlockType.REPORTER, allowDropAnywhere: true, arguments: { NAME: { type: ArgumentType.STRING, defaultValue: "Script1" }, SPRITE: { type: ArgumentType.STRING, menu: "TARGETS" }, DATA: { type: ArgumentType.STRING, defaultValue: "data" } }, } ], menus: { TARGETS: { acceptReporters: true, items: "getTargets" } }, }; } getTargets() { const spriteNames = [ { text: "myself", value: "_myself_" }, { text: "Stage", value: "_stage_" } ]; const targets = this.runtime.targets; for (let index = 1; index < targets.length; index++) { const target = targets[index]; if (target.isOriginal) spriteNames.push({ text: target.getName(), value: target.getName() }); } return spriteNames.length > 0 ? spriteNames : [""]; } createScript(args) { this.scripts[Cast.toString(args.NAME)] = { blocks: [] } } deleteScript(args) { delete this.scripts[Cast.toString(args.NAME)] } deleteAll() { this.scripts = {} } allScripts() { return JSON.stringify(Object.keys(this.scripts)) } scriptExists(args) { return Cast.toBoolean(this.scripts[args.NAME]) } addBlocksTo(args, util) { const name = Cast.toString(args.NAME); const branch = util.thread.target.blocks.getBranch(util.thread.peekStack(), 1); if (branch && this.scripts[name] !== undefined) { this.scripts[name].blocks.push({ stack : branch, target : util.target }); } } JGreturn(args, util) { util.thread.report = Cast.toString(args.THING) } scriptData(args, util) { const data = util.thread.scriptData; return data ? data : ""; } runBlocksData(args, util) { this.runBlocks(args, util) } runBlocks(args, util) { const target = args.SPRITE === "_myself_" ? util.target : args.SPRITE === "_stage_" ? this.runtime.getTargetForStage() : this.runtime.getSpriteTargetByName(args.SPRITE); const name = Cast.toString(args.NAME); const data = args.DATA ? Cast.toString(args.DATA) : ""; if (this.scripts[name] === undefined || !target) return; if (util.stackFrame.JGindex === undefined) util.stackFrame.JGindex = 0; if (util.stackFrame.JGthread === undefined) util.stackFrame.JGthread = ""; const blocks = this.scripts[name].blocks; const index = util.stackFrame.JGindex; const thread = util.stackFrame.JGthread; if (!thread && index < blocks.length) { const thisStack = blocks[index]; if (thisStack.target.blocks.getBlock(thisStack.stack) !== undefined) { util.stackFrame.JGthread = this.runtime._pushThread(thisStack.stack, thisStack.target, { stackClick: false }); util.stackFrame.JGthread.scriptData = data; util.stackFrame.JGthread.target = target; util.stackFrame.JGthread.tryCompile(); // update thread } util.stackFrame.JGindex = util.stackFrame.JGindex + 1; } if (util.stackFrame.JGthread && this.runtime.isActiveThread(util.stackFrame.JGthread)) util.startBranch(1, true); else util.stackFrame.JGthread = ""; if (util.stackFrame.JGindex < blocks.length) util.startBranch(1, true); } reportBlocksData(args, util) { return this.reportBlocks(args, util) || "" } reportBlocks(args, util) { const target = args.SPRITE === "_myself_" ? util.target : args.SPRITE === "_stage_" ? this.runtime.getTargetForStage() : this.runtime.getSpriteTargetByName(args.SPRITE); const name = Cast.toString(args.NAME); const data = args.DATA ? Cast.toString(args.DATA) : ""; if (this.scripts[name] === undefined || !target) return; if (util.stackFrame.JGindex === undefined) util.stackFrame.JGindex = 0; if (util.stackFrame.JGthread === undefined) util.stackFrame.JGthread = ""; const blocks = this.scripts[name].blocks; const index = util.stackFrame.JGindex; const thread = util.stackFrame.JGthread; if (!thread && index < blocks.length) { const thisStack = blocks[index]; if (thisStack.target.blocks.getBlock(thisStack.stack) !== undefined) { util.stackFrame.JGthread = this.runtime._pushThread(thisStack.stack, thisStack.target, { stackClick: false }); util.stackFrame.JGthread.scriptData = data; util.stackFrame.JGthread.target = target; util.stackFrame.JGthread.tryCompile(); // update thread } util.stackFrame.JGindex = util.stackFrame.JGindex + 1; } if (util.stackFrame.JGthread && this.runtime.isActiveThread(util.stackFrame.JGthread)) util.yield(); else { if (util.stackFrame.JGthread.report !== undefined) { util.stackFrame.JGreport = util.stackFrame.JGthread.report; util.stackFrame.JGindex = blocks.length + 1; } util.stackFrame.JGthread = ""; } if (util.stackFrame.JGindex < blocks.length) util.yield(); return util.stackFrame.JGreport || ""; } } module.exports = JgScriptsBlocks;