2016-09-12 13:14:16 -04:00
|
|
|
var Cast = require('../util/cast');
|
2016-06-30 17:06:50 -04:00
|
|
|
var Promise = require('promise');
|
|
|
|
|
2016-06-09 11:45:58 -04:00
|
|
|
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,
|
2016-07-01 11:25:26 -04:00
|
|
|
'control_repeat_until': this.repeatUntil,
|
2016-06-09 11:45:58 -04:00
|
|
|
'control_forever': this.forever,
|
|
|
|
'control_wait': this.wait,
|
2016-06-10 10:36:05 -04:00
|
|
|
'control_if': this.if,
|
2016-06-10 10:40:15 -04:00
|
|
|
'control_if_else': this.ifElse,
|
2016-09-15 19:37:12 -04:00
|
|
|
'control_stop': this.stop,
|
|
|
|
'control_create_clone_of_menu': this.createCloneMenu,
|
|
|
|
'control_create_clone_of': this.createClone,
|
|
|
|
'control_delete_this_clone': this.deleteClone
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
Scratch3ControlBlocks.prototype.getHats = function () {
|
|
|
|
return {
|
|
|
|
'control_start_as_clone': {
|
|
|
|
restartExistingThreads: false
|
|
|
|
}
|
2016-06-09 11:45:58 -04:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2016-06-10 10:36:05 -04:00
|
|
|
Scratch3ControlBlocks.prototype.repeat = function(args, util) {
|
2016-09-12 13:14:16 -04:00
|
|
|
var times = Math.floor(Cast.toNumber(args.TIMES));
|
2016-06-09 11:45:58 -04:00
|
|
|
// Initialize loop
|
|
|
|
if (util.stackFrame.loopCounter === undefined) {
|
2016-09-12 13:14:16 -04:00
|
|
|
util.stackFrame.loopCounter = times;
|
2016-06-09 11:45:58 -04:00
|
|
|
}
|
2016-07-01 10:50:31 -04:00
|
|
|
// Only execute once per frame.
|
2016-08-10 11:27:21 -04:00
|
|
|
// When the branch finishes, `repeat` will be executed again and
|
2016-07-01 10:50:31 -04:00
|
|
|
// the second branch will be taken, yielding for the rest of the frame.
|
2016-07-01 11:29:32 -04:00
|
|
|
if (!util.stackFrame.executedInFrame) {
|
|
|
|
util.stackFrame.executedInFrame = true;
|
2016-07-01 10:50:31 -04:00
|
|
|
// Decrease counter
|
|
|
|
util.stackFrame.loopCounter--;
|
2016-08-10 11:27:21 -04:00
|
|
|
// If we still have some left, start the branch.
|
2016-07-01 10:50:31 -04:00
|
|
|
if (util.stackFrame.loopCounter >= 0) {
|
2016-08-10 11:27:21 -04:00
|
|
|
util.startBranch();
|
2016-07-01 10:50:31 -04:00
|
|
|
}
|
|
|
|
} else {
|
2016-07-01 11:41:52 -04:00
|
|
|
util.stackFrame.executedInFrame = false;
|
2016-07-01 10:50:31 -04:00
|
|
|
util.yieldFrame();
|
2016-06-09 11:45:58 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-07-01 11:25:26 -04:00
|
|
|
Scratch3ControlBlocks.prototype.repeatUntil = function(args, util) {
|
2016-09-12 13:14:16 -04:00
|
|
|
var condition = Cast.toBoolean(args.CONDITION);
|
2016-07-01 11:25:26 -04:00
|
|
|
// Only execute once per frame.
|
2016-08-10 11:27:21 -04:00
|
|
|
// When the branch finishes, `repeat` will be executed again and
|
2016-07-01 11:25:26 -04:00
|
|
|
// the second branch will be taken, yielding for the rest of the frame.
|
|
|
|
if (!util.stackFrame.executedInFrame) {
|
|
|
|
util.stackFrame.executedInFrame = true;
|
2016-08-10 11:27:21 -04:00
|
|
|
// If the condition is true, start the branch.
|
2016-09-12 13:14:16 -04:00
|
|
|
if (!condition) {
|
2016-08-10 11:27:21 -04:00
|
|
|
util.startBranch();
|
2016-07-01 11:25:26 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
util.stackFrame.executedInFrame = false;
|
|
|
|
util.yieldFrame();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-06-10 10:36:05 -04:00
|
|
|
Scratch3ControlBlocks.prototype.forever = function(args, util) {
|
2016-07-01 10:50:31 -04:00
|
|
|
// Only execute once per frame.
|
2016-08-10 11:27:21 -04:00
|
|
|
// When the branch finishes, `forever` will be executed again and
|
2016-07-01 10:50:31 -04:00
|
|
|
// the second branch will be taken, yielding for the rest of the frame.
|
2016-07-01 11:25:26 -04:00
|
|
|
if (!util.stackFrame.executedInFrame) {
|
|
|
|
util.stackFrame.executedInFrame = true;
|
2016-08-10 11:27:21 -04:00
|
|
|
util.startBranch();
|
2016-07-01 10:44:43 -04:00
|
|
|
} else {
|
2016-07-01 11:25:26 -04:00
|
|
|
util.stackFrame.executedInFrame = false;
|
2016-07-01 10:44:43 -04:00
|
|
|
util.yieldFrame();
|
|
|
|
}
|
2016-06-09 11:45:58 -04:00
|
|
|
};
|
|
|
|
|
2016-06-30 17:06:50 -04:00
|
|
|
Scratch3ControlBlocks.prototype.wait = function(args) {
|
2016-09-12 13:14:16 -04:00
|
|
|
var duration = Cast.toNumber(args.DURATION);
|
2016-06-30 17:06:50 -04:00
|
|
|
return new Promise(function(resolve) {
|
|
|
|
setTimeout(function() {
|
|
|
|
resolve();
|
2016-09-12 13:14:16 -04:00
|
|
|
}, 1000 * duration);
|
2016-06-30 17:06:50 -04:00
|
|
|
});
|
2016-06-10 10:36:05 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
Scratch3ControlBlocks.prototype.if = function(args, util) {
|
2016-09-12 13:14:16 -04:00
|
|
|
var condition = Cast.toBoolean(args.CONDITION);
|
2016-06-10 10:36:05 -04:00
|
|
|
// Only execute one time. `if` will be returned to
|
2016-08-10 11:27:21 -04:00
|
|
|
// when the branch finishes, but it shouldn't execute again.
|
2016-07-01 11:25:26 -04:00
|
|
|
if (util.stackFrame.executedInFrame === undefined) {
|
|
|
|
util.stackFrame.executedInFrame = true;
|
2016-09-12 13:14:16 -04:00
|
|
|
if (condition) {
|
2016-08-10 11:27:21 -04:00
|
|
|
util.startBranch();
|
2016-06-10 10:36:05 -04:00
|
|
|
}
|
|
|
|
}
|
2016-06-09 11:45:58 -04:00
|
|
|
};
|
|
|
|
|
2016-06-10 10:40:15 -04:00
|
|
|
Scratch3ControlBlocks.prototype.ifElse = function(args, util) {
|
2016-09-12 13:14:16 -04:00
|
|
|
var condition = Cast.toBoolean(args.CONDITION);
|
2016-06-10 10:40:15 -04:00
|
|
|
// Only execute one time. `ifElse` will be returned to
|
2016-08-10 11:27:21 -04:00
|
|
|
// when the branch finishes, but it shouldn't execute again.
|
2016-07-01 11:29:32 -04:00
|
|
|
if (util.stackFrame.executedInFrame === undefined) {
|
|
|
|
util.stackFrame.executedInFrame = true;
|
2016-09-12 13:14:16 -04:00
|
|
|
if (condition) {
|
2016-08-10 11:27:21 -04:00
|
|
|
util.startBranch(1);
|
2016-06-10 10:40:15 -04:00
|
|
|
} else {
|
2016-08-10 11:27:21 -04:00
|
|
|
util.startBranch(2);
|
2016-06-10 10:40:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-06-09 11:45:58 -04:00
|
|
|
Scratch3ControlBlocks.prototype.stop = function() {
|
|
|
|
// @todo - don't use this.runtime
|
|
|
|
this.runtime.stopAll();
|
|
|
|
};
|
|
|
|
|
2016-09-15 19:37:12 -04:00
|
|
|
// @todo (GH-146): remove.
|
|
|
|
Scratch3ControlBlocks.prototype.createCloneMenu = function (args) {
|
|
|
|
return args.CLONE_OPTION;
|
|
|
|
};
|
|
|
|
|
|
|
|
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) {
|
|
|
|
this.runtime.disposeTarget(util.target);
|
|
|
|
this.runtime.stopForTarget(util.target);
|
|
|
|
};
|
|
|
|
|
2016-06-09 11:45:58 -04:00
|
|
|
module.exports = Scratch3ControlBlocks;
|