Spaces:
Runtime error
Runtime error
| const Cast = require('../util/cast'); | |
| const SandboxRunner = require('../util/sandboxed-javascript-runner.js'); | |
| class Scratch3EventBlocks { | |
| constructor (runtime) { | |
| /** | |
| * The runtime instantiating this block package. | |
| * @type {Runtime} | |
| */ | |
| this.runtime = runtime; | |
| this.runtime.on('KEY_PRESSED', key => { | |
| this.runtime.startHats('event_whenkeypressed', { | |
| KEY_OPTION: key | |
| }); | |
| this.runtime.startHats('event_whenkeypressed', { | |
| KEY_OPTION: 'any' | |
| }); | |
| }); | |
| this.runtime.on('KEY_HIT', key => { | |
| this.runtime.startHats('event_whenkeyhit', { | |
| KEY_OPTION: key | |
| }); | |
| this.runtime.startHats('event_whenkeyhit', { | |
| KEY_OPTION: 'any' | |
| }); | |
| }); | |
| this.isStarting = false; | |
| this.runtime.on('PROJECT_START_BEFORE_RESET', () => { | |
| // we need to remember that the project is starting | |
| // otherwise the stop block will run when flag is clicked | |
| this.isStarting = true; | |
| }); | |
| this.runtime.on('PROJECT_STOP_ALL', () => { | |
| // if green flag is clicked, dont bother starting the hat | |
| if (this.isStarting) { | |
| this.isStarting = false; | |
| return; | |
| } | |
| // we need to wait for runtime to step once | |
| // otherwise the hat will be stopped as soon as it starts | |
| this.runtime.once('RUNTIME_STEP_START', () => { | |
| this.runtime.startHats('event_whenstopclicked'); | |
| }) | |
| this.isStarting = false; | |
| }); | |
| this.runtime.on('RUNTIME_STEP_START', () => { | |
| this.runtime.startHats('event_always'); | |
| }); | |
| this.runtime.on("AFTER_EXECUTE", () => { | |
| // Use a timeout as regular Block Threads and Events Blocks dont run at the Same Speed | |
| setTimeout(() => { | |
| const stage = this.runtime.getTargetForStage(); | |
| if (!stage) return; // happens when project is loading | |
| const stageVars = stage.variables; | |
| for (const key in stageVars) { | |
| if (stageVars[key].isSent !== undefined) stageVars[key].isSent = false; | |
| } | |
| }, 10); | |
| }); | |
| } | |
| /** | |
| * Retrieve the block primitives implemented by this package. | |
| * @return {object.<string, Function>} Mapping of opcode to Function. | |
| */ | |
| getPrimitives () { | |
| return { | |
| event_whenanything: this.whenanything, | |
| event_whenjavascript: this.whenjavascript, | |
| event_whentouchingobject: this.touchingObject, | |
| event_broadcast: this.broadcast, | |
| event_broadcastandwait: this.broadcastAndWait, | |
| event_whengreaterthan: this.hatGreaterThanPredicate | |
| }; | |
| } | |
| whenanything (args) { | |
| return Cast.toBoolean(args.ANYTHING); | |
| } | |
| whenjavascript (args) { | |
| return new Promise((resolve) => { | |
| const js = Cast.toString(args.JS); | |
| SandboxRunner.execute(js).then(result => { | |
| resolve(result.value === true); | |
| }) | |
| }) | |
| } | |
| getHats () { | |
| return { | |
| event_whenflagclicked: { | |
| restartExistingThreads: true | |
| }, | |
| event_whenstopclicked: { | |
| restartExistingThreads: true | |
| }, | |
| event_always: { | |
| restartExistingThreads: false | |
| }, | |
| event_whenkeypressed: { | |
| restartExistingThreads: false | |
| }, | |
| event_whenkeyhit: { | |
| restartExistingThreads: false | |
| }, | |
| event_whenmousescrolled: { | |
| restartExistingThreads: false | |
| }, | |
| event_whenanything: { | |
| restartExistingThreads: false, | |
| edgeActivated: true | |
| }, | |
| event_whenjavascript: { | |
| restartExistingThreads: false, | |
| edgeActivated: true | |
| }, | |
| event_whenthisspriteclicked: { | |
| restartExistingThreads: true | |
| }, | |
| event_whentouchingobject: { | |
| restartExistingThreads: false, | |
| edgeActivated: true | |
| }, | |
| event_whenstageclicked: { | |
| restartExistingThreads: true | |
| }, | |
| event_whenbackdropswitchesto: { | |
| restartExistingThreads: true | |
| }, | |
| event_whengreaterthan: { | |
| restartExistingThreads: false, | |
| edgeActivated: true | |
| }, | |
| event_whenbroadcastreceived: { | |
| restartExistingThreads: true | |
| } | |
| }; | |
| } | |
| touchingObject (args, util) { | |
| return util.target.isTouchingObject(args.TOUCHINGOBJECTMENU); | |
| } | |
| hatGreaterThanPredicate (args, util) { | |
| const option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase(); | |
| const value = Cast.toNumber(args.VALUE); | |
| switch (option) { | |
| case 'timer': | |
| return util.ioQuery('clock', 'projectTimer') > value; | |
| case 'loudness': | |
| return this.runtime.audioEngine && this.runtime.audioEngine.getLoudness() > value; | |
| } | |
| return false; | |
| } | |
| broadcast (args, util) { | |
| const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( | |
| args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); | |
| if (broadcastVar) { | |
| const broadcastOption = broadcastVar.name; | |
| broadcastVar.isSent = true; | |
| util.startHats('event_whenbroadcastreceived', { | |
| BROADCAST_OPTION: broadcastOption | |
| }); | |
| } | |
| } | |
| broadcastAndWait (args, util) { | |
| if (!util.stackFrame.broadcastVar) { | |
| util.stackFrame.broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg( | |
| args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name); | |
| } | |
| if (util.stackFrame.broadcastVar) { | |
| const broadcastOption = util.stackFrame.broadcastVar.name; | |
| // Have we run before, starting threads? | |
| if (!util.stackFrame.startedThreads) { | |
| broadcastVar.isSent = true; | |
| // No - start hats for this broadcast. | |
| util.stackFrame.startedThreads = util.startHats( | |
| 'event_whenbroadcastreceived', { | |
| BROADCAST_OPTION: broadcastOption | |
| } | |
| ); | |
| if (util.stackFrame.startedThreads.length === 0) { | |
| // Nothing was started. | |
| return; | |
| } | |
| } | |
| // We've run before; check if the wait is still going on. | |
| const instance = this; | |
| // Scratch 2 considers threads to be waiting if they are still in | |
| // runtime.threads. Threads that have run all their blocks, or are | |
| // marked done but still in runtime.threads are still considered to | |
| // be waiting. | |
| const waiting = util.stackFrame.startedThreads | |
| .some(thread => instance.runtime.threads.indexOf(thread) !== -1); | |
| if (waiting) { | |
| // If all threads are waiting for the next tick or later yield | |
| // for a tick as well. Otherwise yield until the next loop of | |
| // the threads. | |
| if ( | |
| util.stackFrame.startedThreads | |
| .every(thread => instance.runtime.isWaitingThread(thread)) | |
| ) { | |
| util.yieldTick(); | |
| } else { | |
| util.yield(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| module.exports = Scratch3EventBlocks; | |