scratch-vm/src/blocks/scratch3_control.js
Tim Mickel e49f076fa1 Interpreter fixes, enhancements, features (#280)
* Thread stepping rework; interp.redraw equivalent

* Add turbo mode and pause mode

* Yielding behavior to match Scratch 2.0

* Implement warp-mode procedure threads

* Add check for recursive call

* Inline wait block timer

* Revert to setInterval and always drawing

* Restore yielding in glide

* 30TPS compatibility mode

* 5-call count recursion limit

* Removing dead primitive code

* To simplify, access runtime.threads inline in `stepThreads`.

* Warp mode/timer fixes; recursive check fixes; clean-up

* Add basic single-stepping

* Add single-stepping speed slider

* Allow yielding threads to run in single-stepping

* Restore inactive threads tracking for block glows

* Add clock pausing during pause mode

* Documentation and clean-up throughout

* Don't look for block glows in `thread.topBlock`.

* Add null check for block glows; rename `_updateScriptGlows` to reflect block glowing

* Use the current executed block for glow, instead of stack

* Add more comments to `stepToProcedure`, and re-arrange to match 2.0

* Tweak to Blocks.prototype.getTopLevelScript

* Revert previous

* Fix threads array to be resilient to changes during `stepThreads`

* Restore inactive threads filtering

* Fix typo in "procedure"

* !! instead of == true
2016-10-17 23:23:16 -04:00

139 lines
4 KiB
JavaScript

var Cast = require('../util/cast');
var Timer = require('../util/timer');
function Scratch3ControlBlocks(runtime) {
/**
* The runtime instantiating this block package.
* @type {Runtime}
*/
this.runtime = runtime;
}
/**
* Retrieve the block primitives implemented by this package.
* @return {Object.<string, Function>} Mapping of opcode to Function.
*/
Scratch3ControlBlocks.prototype.getPrimitives = function() {
return {
'control_repeat': this.repeat,
'control_repeat_until': this.repeatUntil,
'control_forever': this.forever,
'control_wait': this.wait,
'control_wait_until': this.waitUntil,
'control_if': this.if,
'control_if_else': this.ifElse,
'control_stop': this.stop,
'control_create_clone_of': this.createClone,
'control_delete_this_clone': this.deleteClone
};
};
Scratch3ControlBlocks.prototype.getHats = function () {
return {
'control_start_as_clone': {
restartExistingThreads: false
}
};
};
Scratch3ControlBlocks.prototype.repeat = function(args, util) {
var times = Math.floor(Cast.toNumber(args.TIMES));
// Initialize loop
if (util.stackFrame.loopCounter === undefined) {
util.stackFrame.loopCounter = times;
}
// Only execute once per frame.
// When the branch finishes, `repeat` will be executed again and
// the second branch will be taken, yielding for the rest of the frame.
// Decrease counter
util.stackFrame.loopCounter--;
// If we still have some left, start the branch.
if (util.stackFrame.loopCounter >= 0) {
util.startBranch(1, true);
}
};
Scratch3ControlBlocks.prototype.repeatUntil = function(args, util) {
var condition = Cast.toBoolean(args.CONDITION);
// If the condition is true, start the branch.
if (!condition) {
util.startBranch(1, true);
}
};
Scratch3ControlBlocks.prototype.waitUntil = function(args, util) {
var condition = Cast.toBoolean(args.CONDITION);
if (!condition) {
util.yield();
}
};
Scratch3ControlBlocks.prototype.forever = function(args, util) {
util.startBranch(1, true);
};
Scratch3ControlBlocks.prototype.wait = function(args, util) {
if (!util.stackFrame.timer) {
util.stackFrame.timer = new Timer();
util.stackFrame.timer.start();
util.yield();
this.runtime.requestRedraw();
} else {
var duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION));
if (util.stackFrame.timer.timeElapsed() < duration) {
util.yield();
}
}
};
Scratch3ControlBlocks.prototype.if = function(args, util) {
var condition = Cast.toBoolean(args.CONDITION);
if (condition) {
util.startBranch(1, false);
}
};
Scratch3ControlBlocks.prototype.ifElse = function(args, util) {
var condition = Cast.toBoolean(args.CONDITION);
if (condition) {
util.startBranch(1, false);
} else {
util.startBranch(2, false);
}
};
Scratch3ControlBlocks.prototype.stop = function(args, util) {
var option = args.STOP_OPTION;
if (option == 'all') {
util.stopAll();
} else if (option == 'other scripts in sprite' ||
option == 'other scripts in stage') {
util.stopOtherTargetThreads();
} else if (option == 'this script') {
util.stopThread();
}
};
Scratch3ControlBlocks.prototype.createClone = function (args, util) {
var cloneTarget;
if (args.CLONE_OPTION == '_myself_') {
cloneTarget = util.target;
} else {
cloneTarget = this.runtime.getSpriteTargetByName(args.CLONE_OPTION);
}
if (!cloneTarget) {
return;
}
var newClone = cloneTarget.makeClone();
if (newClone) {
this.runtime.targets.push(newClone);
}
};
Scratch3ControlBlocks.prototype.deleteClone = function (args, util) {
if (util.target.isOriginal) return;
this.runtime.disposeTarget(util.target);
this.runtime.stopForTarget(util.target);
};
module.exports = Scratch3ControlBlocks;