mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-07-27 06:29:25 -04:00
Merge remote-tracking branch 'refs/remotes/LLK/develop' into develop
This commit is contained in:
commit
aecc3bf893
31 changed files with 351 additions and 188 deletions
|
@ -3,8 +3,7 @@
|
||||||
|
|
||||||
[](https://travis-ci.org/LLK/scratch-vm)
|
[](https://travis-ci.org/LLK/scratch-vm)
|
||||||
[](https://coveralls.io/github/LLK/scratch-vm?branch=develop)
|
[](https://coveralls.io/github/LLK/scratch-vm?branch=develop)
|
||||||
[](https://david-dm.org/LLK/scratch-vm)
|
[](https://greenkeeper.io/)
|
||||||
[](https://david-dm.org/LLK/scratch-vm#info=devDependencies)
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
This requires you to have Git and Node.js installed.
|
This requires you to have Git and Node.js installed.
|
||||||
|
|
25
package.json
25
package.json
|
@ -25,27 +25,26 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"adm-zip": "0.4.7",
|
"adm-zip": "0.4.7",
|
||||||
"babel-eslint": "7.0.0",
|
"babel-eslint": "7.1.1",
|
||||||
"copy-webpack-plugin": "3.0.1",
|
"copy-webpack-plugin": "4.0.1",
|
||||||
"eslint": "3.8.1",
|
"eslint": "3.15.0",
|
||||||
"eslint-config-scratch": "^2.0.0",
|
"eslint-config-scratch": "^3.1.0",
|
||||||
"expose-loader": "0.7.1",
|
"expose-loader": "0.7.1",
|
||||||
"gh-pages": "0.11.0",
|
"gh-pages": "0.12.0",
|
||||||
"highlightjs": "8.7.0",
|
"highlightjs": "9.8.0",
|
||||||
"htmlparser2": "3.9.0",
|
"htmlparser2": "3.9.2",
|
||||||
"json": "9.0.4",
|
"json": "9.0.4",
|
||||||
"json-loader": "0.5.4",
|
|
||||||
"lodash.defaultsdeep": "4.6.0",
|
"lodash.defaultsdeep": "4.6.0",
|
||||||
"minilog": "3.0.1",
|
"minilog": "3.1.0",
|
||||||
"promise": "7.1.1",
|
"promise": "7.1.1",
|
||||||
"scratch-audio": "latest",
|
"scratch-audio": "latest",
|
||||||
"scratch-blocks": "latest",
|
"scratch-blocks": "latest",
|
||||||
"scratch-render": "latest",
|
"scratch-render": "latest",
|
||||||
"script-loader": "0.7.0",
|
"script-loader": "0.7.0",
|
||||||
"stats.js": "0.16.0",
|
"stats.js": "0.17.0",
|
||||||
"tap": "5.7.1",
|
"tap": "10.0.2",
|
||||||
"travis-after-all": "1.4.4",
|
"travis-after-all": "1.4.4",
|
||||||
"webpack": "1.13.0",
|
"webpack": "2.2.1",
|
||||||
"webpack-dev-server": "1.14.1"
|
"webpack-dev-server": "1.16.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ var Scratch3ControlBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3ControlBlocks.prototype.getPrimitives = function () {
|
Scratch3ControlBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -10,7 +10,7 @@ var Scratch3DataBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3DataBlocks.prototype.getPrimitives = function () {
|
Scratch3DataBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -10,7 +10,7 @@ var Scratch3EventBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3EventBlocks.prototype.getPrimitives = function () {
|
Scratch3EventBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -10,7 +10,7 @@ var Scratch3LooksBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3LooksBlocks.prototype.getPrimitives = function () {
|
Scratch3LooksBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -12,7 +12,7 @@ var Scratch3MotionBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3MotionBlocks.prototype.getPrimitives = function () {
|
Scratch3MotionBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var Cast = require('../util/cast.js');
|
var Cast = require('../util/cast.js');
|
||||||
|
var MathUtil = require('../util/math-util.js');
|
||||||
|
|
||||||
var Scratch3OperatorsBlocks = function (runtime) {
|
var Scratch3OperatorsBlocks = function (runtime) {
|
||||||
/**
|
/**
|
||||||
|
@ -10,7 +11,7 @@ var Scratch3OperatorsBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3OperatorsBlocks.prototype.getPrimitives = function () {
|
Scratch3OperatorsBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
@ -126,9 +127,9 @@ Scratch3OperatorsBlocks.prototype.mathop = function (args) {
|
||||||
case 'floor': return Math.floor(n);
|
case 'floor': return Math.floor(n);
|
||||||
case 'ceiling': return Math.ceil(n);
|
case 'ceiling': return Math.ceil(n);
|
||||||
case 'sqrt': return Math.sqrt(n);
|
case 'sqrt': return Math.sqrt(n);
|
||||||
case 'sin': return Math.sin((Math.PI * n) / 180);
|
case 'sin': return parseFloat(Math.sin((Math.PI * n) / 180).toFixed(10));
|
||||||
case 'cos': return Math.cos((Math.PI * n) / 180);
|
case 'cos': return parseFloat(Math.cos((Math.PI * n) / 180).toFixed(10));
|
||||||
case 'tan': return Math.tan((Math.PI * n) / 180);
|
case 'tan': return MathUtil.tan(n);
|
||||||
case 'asin': return (Math.asin(n) * 180) / Math.PI;
|
case 'asin': return (Math.asin(n) * 180) / Math.PI;
|
||||||
case 'acos': return (Math.acos(n) * 180) / Math.PI;
|
case 'acos': return (Math.acos(n) * 180) / Math.PI;
|
||||||
case 'atan': return (Math.atan(n) * 180) / Math.PI;
|
case 'atan': return (Math.atan(n) * 180) / Math.PI;
|
||||||
|
|
|
@ -163,7 +163,7 @@ Scratch3PenBlocks.prototype._wrapHueOrShade = function (value) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3PenBlocks.prototype.getPrimitives = function () {
|
Scratch3PenBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -8,7 +8,7 @@ var Scratch3ProcedureBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3ProcedureBlocks.prototype.getPrimitives = function () {
|
Scratch3ProcedureBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -10,7 +10,7 @@ var Scratch3SensingBlocks = function (runtime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3SensingBlocks.prototype.getPrimitives = function () {
|
Scratch3SensingBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
var MathUtil = require('../util/math-util');
|
var MathUtil = require('../util/math-util');
|
||||||
var Cast = require('../util/cast');
|
var Cast = require('../util/cast');
|
||||||
|
var Clone = require('../util/clone');
|
||||||
|
|
||||||
var Scratch3SoundBlocks = function (runtime) {
|
var Scratch3SoundBlocks = function (runtime) {
|
||||||
/**
|
/**
|
||||||
|
@ -9,9 +10,46 @@ var Scratch3SoundBlocks = function (runtime) {
|
||||||
this.runtime = runtime;
|
this.runtime = runtime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key to load & store a target's sound-related state.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
Scratch3SoundBlocks.STATE_KEY = 'Scratch.sound';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default sound-related state, to be used when a target has no existing sound state.
|
||||||
|
* @type {SoundState}
|
||||||
|
*/
|
||||||
|
Scratch3SoundBlocks.DEFAULT_SOUND_STATE = {
|
||||||
|
volume: 100,
|
||||||
|
currentInstrument: 0,
|
||||||
|
effects: {
|
||||||
|
pitch: 0,
|
||||||
|
pan: 0,
|
||||||
|
echo: 0,
|
||||||
|
reverb: 0,
|
||||||
|
fuzz: 0,
|
||||||
|
robot: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Target} target - collect sound state for this target.
|
||||||
|
* @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Scratch3SoundBlocks.prototype._getSoundState = function (target) {
|
||||||
|
var soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY);
|
||||||
|
if (!soundState) {
|
||||||
|
soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE);
|
||||||
|
target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState);
|
||||||
|
}
|
||||||
|
return soundState;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the block primitives implemented by this package.
|
* Retrieve the block primitives implemented by this package.
|
||||||
* @return {Object.<string, Function>} Mapping of opcode to Function.
|
* @return {object.<string, Function>} Mapping of opcode to Function.
|
||||||
*/
|
*/
|
||||||
Scratch3SoundBlocks.prototype.getPrimitives = function () {
|
Scratch3SoundBlocks.prototype.getPrimitives = function () {
|
||||||
return {
|
return {
|
||||||
|
@ -30,6 +68,7 @@ Scratch3SoundBlocks.prototype.getPrimitives = function () {
|
||||||
sound_effects_menu: this.effectsMenu,
|
sound_effects_menu: this.effectsMenu,
|
||||||
sound_setvolumeto: this.setVolume,
|
sound_setvolumeto: this.setVolume,
|
||||||
sound_changevolumeby: this.changeVolume,
|
sound_changevolumeby: this.changeVolume,
|
||||||
|
sound_volume: this.getVolume,
|
||||||
sound_settempotobpm: this.setTempo,
|
sound_settempotobpm: this.setTempo,
|
||||||
sound_changetempoby: this.changeTempo,
|
sound_changetempoby: this.changeTempo,
|
||||||
sound_tempo: this.getTempo
|
sound_tempo: this.getTempo
|
||||||
|
@ -38,94 +77,166 @@ Scratch3SoundBlocks.prototype.getPrimitives = function () {
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.playSound = function (args, util) {
|
Scratch3SoundBlocks.prototype.playSound = function (args, util) {
|
||||||
var index = this._getSoundIndex(args.SOUND_MENU, util);
|
var index = this._getSoundIndex(args.SOUND_MENU, util);
|
||||||
util.target.audioPlayer.playSound(index);
|
if (index >= 0) {
|
||||||
|
var md5 = util.target.sprite.sounds[index].md5;
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
|
util.target.audioPlayer.playSound(md5);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) {
|
Scratch3SoundBlocks.prototype.playSoundAndWait = function (args, util) {
|
||||||
var index = this._getSoundIndex(args.SOUND_MENU, util);
|
var index = this._getSoundIndex(args.SOUND_MENU, util);
|
||||||
return util.target.audioPlayer.playSound(index);
|
if (index >= 0) {
|
||||||
|
var md5 = util.target.sprite.sounds[index].md5;
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
|
return util.target.audioPlayer.playSound(md5);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype._getSoundIndex = function (soundName, util) {
|
Scratch3SoundBlocks.prototype._getSoundIndex = function (soundName, util) {
|
||||||
if (util.target.sprite.sounds.length === 0) {
|
// if the sprite has no sounds, return -1
|
||||||
return 0;
|
var len = util.target.sprite.sounds.length;
|
||||||
|
if (len === 0) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var index;
|
var index;
|
||||||
|
|
||||||
if (Number(soundName)) {
|
// try to convert to a number and use that as an index
|
||||||
soundName = Number(soundName);
|
var num = parseInt(soundName, 10);
|
||||||
var len = util.target.sprite.sounds.length;
|
if (!isNaN(num)) {
|
||||||
index = MathUtil.wrapClamp(soundName, 1, len) - 1;
|
index = MathUtil.wrapClamp(num, 0, len - 1);
|
||||||
} else {
|
return index;
|
||||||
index = util.target.getSoundIndexByName(soundName);
|
|
||||||
if (index === -1) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the index for the sound of that name
|
||||||
|
index = this.getSoundIndexByName(soundName, util);
|
||||||
return index;
|
return index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Scratch3SoundBlocks.prototype.getSoundIndexByName = function (soundName, util) {
|
||||||
|
var sounds = util.target.sprite.sounds;
|
||||||
|
for (var i = 0; i < sounds.length; i++) {
|
||||||
|
if (sounds[i].name === soundName) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if there is no sound by that name, return -1
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) {
|
Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) {
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
util.target.audioPlayer.stopAllSounds();
|
util.target.audioPlayer.stopAllSounds();
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) {
|
Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) {
|
||||||
return util.target.audioPlayer.playNoteForBeats(args.NOTE, args.BEATS);
|
var note = Cast.toNumber(args.NOTE);
|
||||||
|
var beats = Cast.toNumber(args.BEATS);
|
||||||
|
var soundState = this._getSoundState(util.target);
|
||||||
|
var inst = soundState.currentInstrument;
|
||||||
|
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||||
|
return this.runtime.audioEngine.playNoteForBeatsWithInst(note, beats, inst);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) {
|
Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) {
|
||||||
return util.target.audioPlayer.playDrumForBeats(args.DRUM, args.BEATS);
|
var drum = Cast.toNumber(args.DRUM);
|
||||||
|
drum -= 1; // drums are one-indexed
|
||||||
|
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||||
|
drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums);
|
||||||
|
var beats = Cast.toNumber(args.BEATS);
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
|
return util.target.audioPlayer.playDrumForBeats(drum, beats);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.restForBeats = function (args, util) {
|
Scratch3SoundBlocks.prototype.restForBeats = function (args) {
|
||||||
return util.target.audioPlayer.waitForBeats(args.BEATS);
|
var beats = Cast.toNumber(args.BEATS);
|
||||||
|
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||||
|
return this.runtime.audioEngine.waitForBeats(beats);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.setInstrument = function (args, util) {
|
Scratch3SoundBlocks.prototype.setInstrument = function (args, util) {
|
||||||
|
var soundState = this._getSoundState(util.target);
|
||||||
var instNum = Cast.toNumber(args.INSTRUMENT);
|
var instNum = Cast.toNumber(args.INSTRUMENT);
|
||||||
return util.target.audioPlayer.setInstrument(instNum);
|
instNum -= 1; // instruments are one-indexed
|
||||||
|
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||||
|
instNum = MathUtil.wrapClamp(instNum, 0, this.runtime.audioEngine.numInstruments);
|
||||||
|
soundState.currentInstrument = instNum;
|
||||||
|
return this.runtime.audioEngine.instrumentPlayer.loadInstrument(soundState.currentInstrument);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.setEffect = function (args, util) {
|
Scratch3SoundBlocks.prototype.setEffect = function (args, util) {
|
||||||
|
var effect = Cast.toString(args.EFFECT).toLowerCase();
|
||||||
var value = Cast.toNumber(args.VALUE);
|
var value = Cast.toNumber(args.VALUE);
|
||||||
util.target.audioPlayer.setEffect(args.EFFECT, value);
|
|
||||||
|
var soundState = this._getSoundState(util.target);
|
||||||
|
if (!soundState.effects.hasOwnProperty(effect)) return;
|
||||||
|
|
||||||
|
soundState.effects[effect] = value;
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
|
util.target.audioPlayer.setEffect(effect, soundState.effects[effect]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.changeEffect = function (args, util) {
|
Scratch3SoundBlocks.prototype.changeEffect = function (args, util) {
|
||||||
|
var effect = Cast.toString(args.EFFECT).toLowerCase();
|
||||||
var value = Cast.toNumber(args.VALUE);
|
var value = Cast.toNumber(args.VALUE);
|
||||||
util.target.audioPlayer.changeEffect(args.EFFECT, value);
|
|
||||||
|
var soundState = this._getSoundState(util.target);
|
||||||
|
if (!soundState.effects.hasOwnProperty(effect)) return;
|
||||||
|
|
||||||
|
soundState.effects[effect] += value;
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
|
util.target.audioPlayer.setEffect(effect, soundState.effects[effect]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.clearEffects = function (args, util) {
|
Scratch3SoundBlocks.prototype.clearEffects = function (args, util) {
|
||||||
|
var soundState = this._getSoundState(util.target);
|
||||||
|
for (var effect in soundState.effects) {
|
||||||
|
soundState.effects[effect] = 0;
|
||||||
|
}
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
util.target.audioPlayer.clearEffects();
|
util.target.audioPlayer.clearEffects();
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.setVolume = function (args, util) {
|
Scratch3SoundBlocks.prototype.setVolume = function (args, util) {
|
||||||
var value = Cast.toNumber(args.VOLUME);
|
var volume = Cast.toNumber(args.VOLUME);
|
||||||
util.target.audioPlayer.setVolume(value);
|
this._updateVolume(volume, util);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.changeVolume = function (args, util) {
|
Scratch3SoundBlocks.prototype.changeVolume = function (args, util) {
|
||||||
var value = Cast.toNumber(args.VOLUME);
|
var soundState = this._getSoundState(util.target);
|
||||||
util.target.audioPlayer.changeVolume(value);
|
var volume = Cast.toNumber(args.VOLUME) + soundState.volume;
|
||||||
|
this._updateVolume(volume, util);
|
||||||
|
};
|
||||||
|
|
||||||
|
Scratch3SoundBlocks.prototype._updateVolume = function (volume, util) {
|
||||||
|
var soundState = this._getSoundState(util.target);
|
||||||
|
volume = MathUtil.clamp(volume, 0, 100);
|
||||||
|
soundState.volume = volume;
|
||||||
|
if (util.target.audioPlayer === null) return;
|
||||||
|
util.target.audioPlayer.setVolume(soundState.volume);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.getVolume = function (args, util) {
|
Scratch3SoundBlocks.prototype.getVolume = function (args, util) {
|
||||||
return util.target.audioPlayer.currentVolume;
|
var soundState = this._getSoundState(util.target);
|
||||||
|
return soundState.volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.setTempo = function (args, util) {
|
Scratch3SoundBlocks.prototype.setTempo = function (args) {
|
||||||
var value = Cast.toNumber(args.TEMPO);
|
var value = Cast.toNumber(args.TEMPO);
|
||||||
util.target.audioPlayer.setTempo(value);
|
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||||
|
this.runtime.audioEngine.setTempo(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.changeTempo = function (args, util) {
|
Scratch3SoundBlocks.prototype.changeTempo = function (args) {
|
||||||
var value = Cast.toNumber(args.TEMPO);
|
var value = Cast.toNumber(args.TEMPO);
|
||||||
util.target.audioPlayer.changeTempo(value);
|
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||||
|
this.runtime.audioEngine.changeTempo(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.getTempo = function (args, util) {
|
Scratch3SoundBlocks.prototype.getTempo = function () {
|
||||||
return util.target.audioPlayer.currentTempo;
|
if (typeof this.runtime.audioEngine === 'undefined') return;
|
||||||
|
return this.runtime.audioEngine.currentTempo;
|
||||||
};
|
};
|
||||||
|
|
||||||
Scratch3SoundBlocks.prototype.soundsMenu = function (args) {
|
Scratch3SoundBlocks.prototype.soundsMenu = function (args) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ var html = require('htmlparser2');
|
||||||
* to a usable form for the Scratch runtime.
|
* to a usable form for the Scratch runtime.
|
||||||
* This structure is based on Blockly xml.js:`domToWorkspace` and `domToBlock`.
|
* This structure is based on Blockly xml.js:`domToWorkspace` and `domToBlock`.
|
||||||
* @param {Element} blocksDOM DOM tree for this event.
|
* @param {Element} blocksDOM DOM tree for this event.
|
||||||
* @return {Array.<Object>} Usable list of blocks from this CREATE event.
|
* @return {Array.<object>} Usable list of blocks from this CREATE event.
|
||||||
*/
|
*/
|
||||||
var domToBlocks = function (blocksDOM) {
|
var domToBlocks = function (blocksDOM) {
|
||||||
// At this level, there could be multiple blocks adjacent in the DOM tree.
|
// At this level, there could be multiple blocks adjacent in the DOM tree.
|
||||||
|
@ -32,8 +32,8 @@ var domToBlocks = function (blocksDOM) {
|
||||||
/**
|
/**
|
||||||
* Adapter between block creation events and block representation which can be
|
* Adapter between block creation events and block representation which can be
|
||||||
* used by the Scratch runtime.
|
* used by the Scratch runtime.
|
||||||
* @param {Object} e `Blockly.events.create`
|
* @param {object} e `Blockly.events.create`
|
||||||
* @return {Array.<Object>} List of blocks from this CREATE event.
|
* @return {Array.<object>} List of blocks from this CREATE event.
|
||||||
*/
|
*/
|
||||||
var adapter = function (e) {
|
var adapter = function (e) {
|
||||||
// Validate input
|
// Validate input
|
||||||
|
@ -47,8 +47,8 @@ var adapter = function (e) {
|
||||||
* Convert and an individual block DOM to the representation tree.
|
* Convert and an individual block DOM to the representation tree.
|
||||||
* Based on Blockly's `domToBlockHeadless_`.
|
* Based on Blockly's `domToBlockHeadless_`.
|
||||||
* @param {Element} blockDOM DOM tree for an individual block.
|
* @param {Element} blockDOM DOM tree for an individual block.
|
||||||
* @param {Object} blocks Collection of blocks to add to.
|
* @param {object} blocks Collection of blocks to add to.
|
||||||
* @param {Boolean} isTopBlock Whether blocks at this level are "top blocks."
|
* @param {boolean} isTopBlock Whether blocks at this level are "top blocks."
|
||||||
* @param {?string} parent Parent block ID.
|
* @param {?string} parent Parent block ID.
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -34,7 +34,7 @@ Blocks.BRANCH_INPUT_PREFIX = 'SUBSTACK';
|
||||||
/**
|
/**
|
||||||
* Provide an object with metadata for the requested block ID.
|
* Provide an object with metadata for the requested block ID.
|
||||||
* @param {!string} blockId ID of block we have stored.
|
* @param {!string} blockId ID of block we have stored.
|
||||||
* @return {?Object} Metadata about the block, if it exists.
|
* @return {?object} Metadata about the block, if it exists.
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.getBlock = function (blockId) {
|
Blocks.prototype.getBlock = function (blockId) {
|
||||||
return this._blocks[blockId];
|
return this._blocks[blockId];
|
||||||
|
@ -54,8 +54,8 @@ Blocks.prototype.getScripts = function () {
|
||||||
* @return {?string} ID of next block in the sequence
|
* @return {?string} ID of next block in the sequence
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.getNextBlock = function (id) {
|
Blocks.prototype.getNextBlock = function (id) {
|
||||||
if (typeof this._blocks[id] === 'undefined') return null;
|
var block = this._blocks[id];
|
||||||
return this._blocks[id].next;
|
return (typeof block === 'undefined') ? null : block.next;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,33 +85,34 @@ Blocks.prototype.getBranch = function (id, branchNum) {
|
||||||
* @return {?string} the opcode corresponding to that block
|
* @return {?string} the opcode corresponding to that block
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.getOpcode = function (id) {
|
Blocks.prototype.getOpcode = function (id) {
|
||||||
if (typeof this._blocks[id] === 'undefined') return null;
|
var block = this._blocks[id];
|
||||||
return this._blocks[id].opcode;
|
return (typeof block === 'undefined') ? null : block.opcode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all fields and their values for a block.
|
* Get all fields and their values for a block.
|
||||||
* @param {?string} id ID of block to query.
|
* @param {?string} id ID of block to query.
|
||||||
* @return {!Object} All fields and their values.
|
* @return {!object} All fields and their values.
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.getFields = function (id) {
|
Blocks.prototype.getFields = function (id) {
|
||||||
if (typeof this._blocks[id] === 'undefined') return null;
|
var block = this._blocks[id];
|
||||||
return this._blocks[id].fields;
|
return (typeof block === 'undefined') ? null : block.fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all non-branch inputs for a block.
|
* Get all non-branch inputs for a block.
|
||||||
* @param {?string} id ID of block to query.
|
* @param {?string} id ID of block to query.
|
||||||
* @return {!Object} All non-branch inputs and their associated blocks.
|
* @return {!object} All non-branch inputs and their associated blocks.
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.getInputs = function (id) {
|
Blocks.prototype.getInputs = function (id) {
|
||||||
if (typeof this._blocks[id] === 'undefined') return null;
|
var block = this._blocks[id];
|
||||||
|
if (typeof block === 'undefined') return null;
|
||||||
var inputs = {};
|
var inputs = {};
|
||||||
for (var input in this._blocks[id].inputs) {
|
for (var input in block.inputs) {
|
||||||
// Ignore blocks prefixed with branch prefix.
|
// Ignore blocks prefixed with branch prefix.
|
||||||
if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !==
|
if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !==
|
||||||
Blocks.BRANCH_INPUT_PREFIX) {
|
Blocks.BRANCH_INPUT_PREFIX) {
|
||||||
inputs[input] = this._blocks[id].inputs[input];
|
inputs[input] = block.inputs[input];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return inputs;
|
return inputs;
|
||||||
|
@ -120,11 +121,11 @@ Blocks.prototype.getInputs = function (id) {
|
||||||
/**
|
/**
|
||||||
* Get mutation data for a block.
|
* Get mutation data for a block.
|
||||||
* @param {?string} id ID of block to query.
|
* @param {?string} id ID of block to query.
|
||||||
* @return {!Object} Mutation for the block.
|
* @return {!object} Mutation for the block.
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.getMutation = function (id) {
|
Blocks.prototype.getMutation = function (id) {
|
||||||
if (typeof this._blocks[id] === 'undefined') return null;
|
var block = this._blocks[id];
|
||||||
return this._blocks[id].mutation;
|
return (typeof block === 'undefined') ? null : block.mutation;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,8 +134,8 @@ Blocks.prototype.getMutation = function (id) {
|
||||||
* @return {?string} ID of top-level script block.
|
* @return {?string} ID of top-level script block.
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.getTopLevelScript = function (id) {
|
Blocks.prototype.getTopLevelScript = function (id) {
|
||||||
if (typeof this._blocks[id] === 'undefined') return null;
|
|
||||||
var block = this._blocks[id];
|
var block = this._blocks[id];
|
||||||
|
if (typeof block === 'undefined') return null;
|
||||||
while (block.parent !== null) {
|
while (block.parent !== null) {
|
||||||
block = this._blocks[block.parent];
|
block = this._blocks[block.parent];
|
||||||
}
|
}
|
||||||
|
@ -246,7 +247,7 @@ Blocks.prototype.blocklyListen = function (e, optRuntime) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block management: create blocks and scripts from a `create` event
|
* Block management: create blocks and scripts from a `create` event
|
||||||
* @param {!Object} block Blockly create event to be processed
|
* @param {!object} block Blockly create event to be processed
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.createBlock = function (block) {
|
Blocks.prototype.createBlock = function (block) {
|
||||||
// Does the block already exist?
|
// Does the block already exist?
|
||||||
|
@ -266,25 +267,26 @@ Blocks.prototype.createBlock = function (block) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block management: change block field values
|
* Block management: change block field values
|
||||||
* @param {!Object} args Blockly change event to be processed
|
* @param {!object} args Blockly change event to be processed
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.changeBlock = function (args) {
|
Blocks.prototype.changeBlock = function (args) {
|
||||||
// Validate
|
// Validate
|
||||||
if (args.element !== 'field' && args.element !== 'mutation') return;
|
if (args.element !== 'field' && args.element !== 'mutation') return;
|
||||||
if (typeof this._blocks[args.id] === 'undefined') return;
|
var block = this._blocks[args.id];
|
||||||
|
if (typeof block === 'undefined') return;
|
||||||
|
|
||||||
if (args.element === 'field') {
|
if (args.element === 'field') {
|
||||||
// Update block value
|
// Update block value
|
||||||
if (!this._blocks[args.id].fields[args.name]) return;
|
if (!block.fields[args.name]) return;
|
||||||
this._blocks[args.id].fields[args.name].value = args.value;
|
block.fields[args.name].value = args.value;
|
||||||
} else if (args.element === 'mutation') {
|
} else if (args.element === 'mutation') {
|
||||||
this._blocks[args.id].mutation = mutationAdapter(args.value);
|
block.mutation = mutationAdapter(args.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block management: move blocks from parent to parent
|
* Block management: move blocks from parent to parent
|
||||||
* @param {!Object} e Blockly move event to be processed
|
* @param {!object} e Blockly move event to be processed
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.moveBlock = function (e) {
|
Blocks.prototype.moveBlock = function (e) {
|
||||||
if (!this._blocks.hasOwnProperty(e.id)) {
|
if (!this._blocks.hasOwnProperty(e.id)) {
|
||||||
|
@ -340,7 +342,7 @@ Blocks.prototype.moveBlock = function (e) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block management: delete blocks and their associated scripts.
|
* Block management: delete blocks and their associated scripts.
|
||||||
* @param {!Object} e Blockly delete event to be processed.
|
* @param {!object} e Blockly delete event to be processed.
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.deleteBlock = function (e) {
|
Blocks.prototype.deleteBlock = function (e) {
|
||||||
// @todo In runtime, stop threads running on this script.
|
// @todo In runtime, stop threads running on this script.
|
||||||
|
@ -447,7 +449,7 @@ Blocks.prototype.blockToXML = function (blockId) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively encode a mutation object to XML.
|
* Recursively encode a mutation object to XML.
|
||||||
* @param {!Object} mutation Object representing a mutation.
|
* @param {!object} mutation Object representing a mutation.
|
||||||
* @return {string} XML string representing a mutation.
|
* @return {string} XML string representing a mutation.
|
||||||
*/
|
*/
|
||||||
Blocks.prototype.mutationToXML = function (mutation) {
|
Blocks.prototype.mutationToXML = function (mutation) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ var Thread = require('./thread');
|
||||||
/**
|
/**
|
||||||
* Utility function to determine if a value is a Promise.
|
* Utility function to determine if a value is a Promise.
|
||||||
* @param {*} value Value to check for a Promise.
|
* @param {*} value Value to check for a Promise.
|
||||||
* @return {Boolean} True if the value appears to be a Promise.
|
* @return {boolean} True if the value appears to be a Promise.
|
||||||
*/
|
*/
|
||||||
var isPromise = function (value) {
|
var isPromise = function (value) {
|
||||||
return value && value.then && typeof value.then === 'function';
|
return value && value.then && typeof value.then === 'function';
|
||||||
|
|
|
@ -2,8 +2,8 @@ var html = require('htmlparser2');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a part of a mutation DOM to a mutation VM object, recursively.
|
* Convert a part of a mutation DOM to a mutation VM object, recursively.
|
||||||
* @param {Object} dom DOM object for mutation tag.
|
* @param {object} dom DOM object for mutation tag.
|
||||||
* @return {Object} Object representing useful parts of this mutation.
|
* @return {object} Object representing useful parts of this mutation.
|
||||||
*/
|
*/
|
||||||
var mutatorTagToObject = function (dom) {
|
var mutatorTagToObject = function (dom) {
|
||||||
var obj = Object.create(null);
|
var obj = Object.create(null);
|
||||||
|
@ -24,8 +24,8 @@ var mutatorTagToObject = function (dom) {
|
||||||
/**
|
/**
|
||||||
* Adapter between mutator XML or DOM and block representation which can be
|
* Adapter between mutator XML or DOM and block representation which can be
|
||||||
* used by the Scratch runtime.
|
* used by the Scratch runtime.
|
||||||
* @param {(Object|string)} mutation Mutation XML string or DOM.
|
* @param {(object|string)} mutation Mutation XML string or DOM.
|
||||||
* @return {Object} Object representing the mutation.
|
* @return {object} Object representing the mutation.
|
||||||
*/
|
*/
|
||||||
var mutationAdpater = function (mutation) {
|
var mutationAdpater = function (mutation) {
|
||||||
var mutationParsed;
|
var mutationParsed;
|
||||||
|
|
|
@ -276,7 +276,7 @@ Runtime.prototype.getOpcodeFunction = function (opcode) {
|
||||||
/**
|
/**
|
||||||
* Return whether an opcode represents a hat block.
|
* Return whether an opcode represents a hat block.
|
||||||
* @param {!string} opcode The opcode to look up.
|
* @param {!string} opcode The opcode to look up.
|
||||||
* @return {Boolean} True if the op is known to be a hat.
|
* @return {boolean} True if the op is known to be a hat.
|
||||||
*/
|
*/
|
||||||
Runtime.prototype.getIsHat = function (opcode) {
|
Runtime.prototype.getIsHat = function (opcode) {
|
||||||
return this._hats.hasOwnProperty(opcode);
|
return this._hats.hasOwnProperty(opcode);
|
||||||
|
@ -285,7 +285,7 @@ Runtime.prototype.getIsHat = function (opcode) {
|
||||||
/**
|
/**
|
||||||
* Return whether an opcode represents an edge-activated hat block.
|
* Return whether an opcode represents an edge-activated hat block.
|
||||||
* @param {!string} opcode The opcode to look up.
|
* @param {!string} opcode The opcode to look up.
|
||||||
* @return {Boolean} True if the op is known to be a edge-activated hat.
|
* @return {boolean} True if the op is known to be a edge-activated hat.
|
||||||
*/
|
*/
|
||||||
Runtime.prototype.getIsEdgeActivatedHat = function (opcode) {
|
Runtime.prototype.getIsEdgeActivatedHat = function (opcode) {
|
||||||
return this._hats.hasOwnProperty(opcode) &&
|
return this._hats.hasOwnProperty(opcode) &&
|
||||||
|
@ -379,7 +379,7 @@ Runtime.prototype._restartThread = function (thread) {
|
||||||
/**
|
/**
|
||||||
* Return whether a thread is currently active/running.
|
* Return whether a thread is currently active/running.
|
||||||
* @param {?Thread} thread Thread object to check.
|
* @param {?Thread} thread Thread object to check.
|
||||||
* @return {Boolean} True if the thread is active/running.
|
* @return {boolean} True if the thread is active/running.
|
||||||
*/
|
*/
|
||||||
Runtime.prototype.isActiveThread = function (thread) {
|
Runtime.prototype.isActiveThread = function (thread) {
|
||||||
return this.threads.indexOf(thread) > -1;
|
return this.threads.indexOf(thread) > -1;
|
||||||
|
@ -427,7 +427,7 @@ Runtime.prototype.allScriptsDo = function (f, optTarget) {
|
||||||
/**
|
/**
|
||||||
* Start all relevant hats.
|
* Start all relevant hats.
|
||||||
* @param {!string} requestedHatOpcode Opcode of hats to start.
|
* @param {!string} requestedHatOpcode Opcode of hats to start.
|
||||||
* @param {Object=} optMatchFields Optionally, fields to match on the hat.
|
* @param {object=} optMatchFields Optionally, fields to match on the hat.
|
||||||
* @param {Target=} optTarget Optionally, a target to restrict to.
|
* @param {Target=} optTarget Optionally, a target to restrict to.
|
||||||
* @return {Array.<Thread>} List of threads started by this function.
|
* @return {Array.<Thread>} List of threads started by this function.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -171,8 +171,8 @@ Sequencer.prototype.stepThread = function (thread) {
|
||||||
/**
|
/**
|
||||||
* Step a thread into a block's branch.
|
* Step a thread into a block's branch.
|
||||||
* @param {!Thread} thread Thread object to step to branch.
|
* @param {!Thread} thread Thread object to step to branch.
|
||||||
* @param {Number} branchNum Which branch to step to (i.e., 1, 2).
|
* @param {number} branchNum Which branch to step to (i.e., 1, 2).
|
||||||
* @param {Boolean} isLoop Whether this block is a loop.
|
* @param {boolean} isLoop Whether this block is a loop.
|
||||||
*/
|
*/
|
||||||
Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) {
|
Sequencer.prototype.stepToBranch = function (thread, branchNum, isLoop) {
|
||||||
if (!branchNum) {
|
if (!branchNum) {
|
||||||
|
|
|
@ -101,7 +101,7 @@ Thread.prototype.pushStack = function (blockId) {
|
||||||
if (this.stack.length > this.stackFrames.length) {
|
if (this.stack.length > this.stackFrames.length) {
|
||||||
// Copy warp mode from any higher level.
|
// Copy warp mode from any higher level.
|
||||||
var warpMode = false;
|
var warpMode = false;
|
||||||
if (this.stackFrames[this.stackFrames.length - 1]) {
|
if (this.stackFrames.length > 0 && this.stackFrames[this.stackFrames.length - 1]) {
|
||||||
warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode;
|
warpMode = this.stackFrames[this.stackFrames.length - 1].warpMode;
|
||||||
}
|
}
|
||||||
this.stackFrames.push({
|
this.stackFrames.push({
|
||||||
|
@ -115,6 +115,22 @@ Thread.prototype.pushStack = function (blockId) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the stack frame for use by the next block.
|
||||||
|
* (avoids popping and re-pushing a new stack frame - keeps the warpmode the same
|
||||||
|
* @param {string} blockId Block ID to push to stack.
|
||||||
|
*/
|
||||||
|
Thread.prototype.reuseStackForNextBlock = function (blockId) {
|
||||||
|
this.stack[this.stack.length - 1] = blockId;
|
||||||
|
var frame = this.stackFrames[this.stackFrames.length - 1];
|
||||||
|
frame.isLoop = false;
|
||||||
|
// frame.warpMode = warpMode; // warp mode stays the same when reusing the stack frame.
|
||||||
|
frame.reported = {};
|
||||||
|
frame.waitingReporter = null;
|
||||||
|
frame.params = {};
|
||||||
|
frame.executionContext = {};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop last block on the stack and its stack frame.
|
* Pop last block on the stack and its stack frame.
|
||||||
* @return {string} Block ID popped from the stack.
|
* @return {string} Block ID popped from the stack.
|
||||||
|
@ -129,24 +145,24 @@ Thread.prototype.popStack = function () {
|
||||||
* @return {?string} Block ID on top of stack.
|
* @return {?string} Block ID on top of stack.
|
||||||
*/
|
*/
|
||||||
Thread.prototype.peekStack = function () {
|
Thread.prototype.peekStack = function () {
|
||||||
return this.stack[this.stack.length - 1];
|
return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get top stack frame.
|
* Get top stack frame.
|
||||||
* @return {?Object} Last stack frame stored on this thread.
|
* @return {?object} Last stack frame stored on this thread.
|
||||||
*/
|
*/
|
||||||
Thread.prototype.peekStackFrame = function () {
|
Thread.prototype.peekStackFrame = function () {
|
||||||
return this.stackFrames[this.stackFrames.length - 1];
|
return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get stack frame above the current top.
|
* Get stack frame above the current top.
|
||||||
* @return {?Object} Second to last stack frame stored on this thread.
|
* @return {?object} Second to last stack frame stored on this thread.
|
||||||
*/
|
*/
|
||||||
Thread.prototype.peekParentStackFrame = function () {
|
Thread.prototype.peekParentStackFrame = function () {
|
||||||
return this.stackFrames[this.stackFrames.length - 2];
|
return this.stackFrames.length > 1 ? this.stackFrames[this.stackFrames.length - 2] : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -189,7 +205,7 @@ Thread.prototype.getParam = function (paramName) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the current execution of a thread is at the top of the stack.
|
* Whether the current execution of a thread is at the top of the stack.
|
||||||
* @return {Boolean} True if execution is at top of the stack.
|
* @return {boolean} True if execution is at top of the stack.
|
||||||
*/
|
*/
|
||||||
Thread.prototype.atStackTop = function () {
|
Thread.prototype.atStackTop = function () {
|
||||||
return this.peekStack() === this.topBlock;
|
return this.peekStack() === this.topBlock;
|
||||||
|
@ -203,15 +219,7 @@ Thread.prototype.atStackTop = function () {
|
||||||
*/
|
*/
|
||||||
Thread.prototype.goToNextBlock = function () {
|
Thread.prototype.goToNextBlock = function () {
|
||||||
var nextBlockId = this.target.blocks.getNextBlock(this.peekStack());
|
var nextBlockId = this.target.blocks.getNextBlock(this.peekStack());
|
||||||
// Copy warp mode to next block.
|
this.reuseStackForNextBlock(nextBlockId);
|
||||||
var warpMode = this.peekStackFrame().warpMode;
|
|
||||||
// The current block is on the stack - pop it and push the next.
|
|
||||||
// Note that this could push `null` - that is handled by the sequencer.
|
|
||||||
this.popStack();
|
|
||||||
this.pushStack(nextBlockId);
|
|
||||||
if (this.peekStackFrame()) {
|
|
||||||
this.peekStackFrame().warpMode = warpMode;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!string} name Name of the variable.
|
* @param {!string} name Name of the variable.
|
||||||
* @param {(string|Number)} value Value of the variable.
|
* @param {(string|number)} value Value of the variable.
|
||||||
* @param {boolean} isCloud Whether the variable is stored in the cloud.
|
* @param {boolean} isCloud Whether the variable is stored in the cloud.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,7 +17,7 @@ var List = require('../engine/list');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
||||||
* @param {!Object} object From-JSON "Scratch object:" sprite, stage, watcher.
|
* @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
|
||||||
* @param {!Runtime} runtime Runtime object to load all structures into.
|
* @param {!Runtime} runtime Runtime object to load all structures into.
|
||||||
* @param {boolean} topLevel Whether this is the top-level object (stage).
|
* @param {boolean} topLevel Whether this is the top-level object (stage).
|
||||||
* @return {?Target} Target created (stage or sprite).
|
* @return {?Target} Target created (stage or sprite).
|
||||||
|
@ -61,7 +61,8 @@ var parseScratchObject = function (object, runtime, topLevel) {
|
||||||
rate: sound.rate,
|
rate: sound.rate,
|
||||||
sampleCount: sound.sampleCount,
|
sampleCount: sound.sampleCount,
|
||||||
soundID: sound.soundID,
|
soundID: sound.soundID,
|
||||||
name: sound.soundName
|
name: sound.soundName,
|
||||||
|
md5: sound.md5
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +139,7 @@ var parseScratchObject = function (object, runtime, topLevel) {
|
||||||
* and process the top-level object (the stage object).
|
* and process the top-level object (the stage object).
|
||||||
* @param {!string} json SB2-format JSON to load.
|
* @param {!string} json SB2-format JSON to load.
|
||||||
* @param {!Runtime} runtime Runtime object to load all structures into.
|
* @param {!Runtime} runtime Runtime object to load all structures into.
|
||||||
* @param {Boolean=} optForceSprite If set, treat as sprite (Sprite2).
|
* @param {boolean=} optForceSprite If set, treat as sprite (Sprite2).
|
||||||
* @return {?Target} Top-level target created (stage or sprite).
|
* @return {?Target} Top-level target created (stage or sprite).
|
||||||
*/
|
*/
|
||||||
var sb2import = function (json, runtime, optForceSprite) {
|
var sb2import = function (json, runtime, optForceSprite) {
|
||||||
|
@ -152,7 +153,7 @@ var sb2import = function (json, runtime, optForceSprite) {
|
||||||
/**
|
/**
|
||||||
* Parse a Scratch object's scripts into VM blocks.
|
* Parse a Scratch object's scripts into VM blocks.
|
||||||
* This should only handle top-level scripts that include X, Y coordinates.
|
* This should only handle top-level scripts that include X, Y coordinates.
|
||||||
* @param {!Object} scripts Scripts object from SB2 JSON.
|
* @param {!object} scripts Scripts object from SB2 JSON.
|
||||||
* @param {!Blocks} blocks Blocks object to load parsed blocks into.
|
* @param {!Blocks} blocks Blocks object to load parsed blocks into.
|
||||||
*/
|
*/
|
||||||
var parseScripts = function (scripts, blocks) {
|
var parseScripts = function (scripts, blocks) {
|
||||||
|
@ -184,8 +185,8 @@ var parseScripts = function (scripts, blocks) {
|
||||||
* Could be used to parse a top-level script,
|
* Could be used to parse a top-level script,
|
||||||
* a list of blocks in a branch (e.g., in forever),
|
* a list of blocks in a branch (e.g., in forever),
|
||||||
* or a list of blocks in an argument (e.g., move [pick random...]).
|
* or a list of blocks in an argument (e.g., move [pick random...]).
|
||||||
* @param {Array.<Object>} blockList SB2 JSON-format block list.
|
* @param {Array.<object>} blockList SB2 JSON-format block list.
|
||||||
* @return {Array.<Object>} Scratch VM-format block list.
|
* @return {Array.<object>} Scratch VM-format block list.
|
||||||
*/
|
*/
|
||||||
var parseBlockList = function (blockList) {
|
var parseBlockList = function (blockList) {
|
||||||
var resultingList = [];
|
var resultingList = [];
|
||||||
|
@ -193,6 +194,7 @@ var parseBlockList = function (blockList) {
|
||||||
for (var i = 0; i < blockList.length; i++) {
|
for (var i = 0; i < blockList.length; i++) {
|
||||||
var block = blockList[i];
|
var block = blockList[i];
|
||||||
var parsedBlock = parseBlock(block);
|
var parsedBlock = parseBlock(block);
|
||||||
|
if (typeof parsedBlock === 'undefined') continue;
|
||||||
if (previousBlock) {
|
if (previousBlock) {
|
||||||
parsedBlock.parent = previousBlock.id;
|
parsedBlock.parent = previousBlock.id;
|
||||||
previousBlock.next = parsedBlock.id;
|
previousBlock.next = parsedBlock.id;
|
||||||
|
@ -206,8 +208,8 @@ var parseBlockList = function (blockList) {
|
||||||
/**
|
/**
|
||||||
* Flatten a block tree into a block list.
|
* Flatten a block tree into a block list.
|
||||||
* Children are temporarily stored on the `block.children` property.
|
* Children are temporarily stored on the `block.children` property.
|
||||||
* @param {Array.<Object>} blocks list generated by `parseBlockList`.
|
* @param {Array.<object>} blocks list generated by `parseBlockList`.
|
||||||
* @return {Array.<Object>} Flattened list to be passed to `blocks.createBlock`.
|
* @return {Array.<object>} Flattened list to be passed to `blocks.createBlock`.
|
||||||
*/
|
*/
|
||||||
var flatten = function (blocks) {
|
var flatten = function (blocks) {
|
||||||
var finalBlocks = [];
|
var finalBlocks = [];
|
||||||
|
@ -227,7 +229,7 @@ var flatten = function (blocks) {
|
||||||
* into an argument map. This allows us to provide the expected inputs
|
* into an argument map. This allows us to provide the expected inputs
|
||||||
* to a mutated procedure call.
|
* to a mutated procedure call.
|
||||||
* @param {string} procCode Scratch 2.0 procedure string.
|
* @param {string} procCode Scratch 2.0 procedure string.
|
||||||
* @return {Object} Argument map compatible with those in sb2specmap.
|
* @return {object} Argument map compatible with those in sb2specmap.
|
||||||
*/
|
*/
|
||||||
var parseProcedureArgMap = function (procCode) {
|
var parseProcedureArgMap = function (procCode) {
|
||||||
var argMap = [
|
var argMap = [
|
||||||
|
@ -258,8 +260,8 @@ var parseProcedureArgMap = function (procCode) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a single SB2 JSON-formatted block and its children.
|
* Parse a single SB2 JSON-formatted block and its children.
|
||||||
* @param {!Object} sb2block SB2 JSON-formatted block.
|
* @param {!object} sb2block SB2 JSON-formatted block.
|
||||||
* @return {Object} Scratch VM format block.
|
* @return {object} Scratch VM format block.
|
||||||
*/
|
*/
|
||||||
var parseBlock = function (sb2block) {
|
var parseBlock = function (sb2block) {
|
||||||
// First item in block object is the old opcode (e.g., 'forward:').
|
// First item in block object is the old opcode (e.g., 'forward:').
|
||||||
|
|
|
@ -408,7 +408,7 @@ var specMap = {
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
inputOp: 'math_number',
|
inputOp: 'math_number',
|
||||||
inputName: 'DRUMTYPE'
|
inputName: 'DRUM'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
|
|
@ -69,10 +69,9 @@ RenderedTarget.prototype.initDrawable = function () {
|
||||||
this.audioPlayer = null;
|
this.audioPlayer = null;
|
||||||
if (this.runtime && this.runtime.audioEngine) {
|
if (this.runtime && this.runtime.audioEngine) {
|
||||||
if (this.isOriginal) {
|
if (this.isOriginal) {
|
||||||
this.sprite.audioPlayer = this.runtime.audioEngine.createPlayer();
|
this.runtime.audioEngine.loadSounds(this.sprite.sounds);
|
||||||
this.sprite.audioPlayer.loadSounds(this.sprite.sounds);
|
|
||||||
}
|
}
|
||||||
this.audioPlayer = this.sprite.audioPlayer;
|
this.audioPlayer = this.runtime.audioEngine.createPlayer();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -184,7 +183,7 @@ RenderedTarget.prototype.setXY = function (x, y) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the rendered direction and scale, after applying rotation style.
|
* Get the rendered direction and scale, after applying rotation style.
|
||||||
* @return {Object<string, number>} Direction and scale to render.
|
* @return {object<string, number>} Direction and scale to render.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype._getRenderedDirectionAndScale = function () {
|
RenderedTarget.prototype._getRenderedDirectionAndScale = function () {
|
||||||
// Default: no changes to `this.direction` or `this.scale`.
|
// Default: no changes to `this.direction` or `this.scale`.
|
||||||
|
@ -399,20 +398,6 @@ RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) {
|
||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a sound index of this rendered target, by name of the sound.
|
|
||||||
* @param {?string} soundName Name of a sound.
|
|
||||||
* @return {number} Index of the named sound, or -1 if not present.
|
|
||||||
*/
|
|
||||||
RenderedTarget.prototype.getSoundIndexByName = function (soundName) {
|
|
||||||
for (var i = 0; i < this.sprite.sounds.length; i++) {
|
|
||||||
if (this.sprite.sounds[i].name === soundName) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a costume of this rendered target by id.
|
* Get a costume of this rendered target by id.
|
||||||
* @return {object} current costume
|
* @return {object} current costume
|
||||||
|
@ -480,7 +465,7 @@ RenderedTarget.prototype.isSprite = function () {
|
||||||
/**
|
/**
|
||||||
* Return the rendered target's tight bounding box.
|
* Return the rendered target's tight bounding box.
|
||||||
* Includes top, left, bottom, right attributes in Scratch coordinates.
|
* Includes top, left, bottom, right attributes in Scratch coordinates.
|
||||||
* @return {?Object} Tight bounding box, or null.
|
* @return {?object} Tight bounding box, or null.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.getBounds = function () {
|
RenderedTarget.prototype.getBounds = function () {
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
|
@ -493,7 +478,7 @@ RenderedTarget.prototype.getBounds = function () {
|
||||||
* Return whether touching a point.
|
* Return whether touching a point.
|
||||||
* @param {number} x X coordinate of test point.
|
* @param {number} x X coordinate of test point.
|
||||||
* @param {number} y Y coordinate of test point.
|
* @param {number} y Y coordinate of test point.
|
||||||
* @return {Boolean} True iff the rendered target is touching the point.
|
* @return {boolean} True iff the rendered target is touching the point.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.isTouchingPoint = function (x, y) {
|
RenderedTarget.prototype.isTouchingPoint = function (x, y) {
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
|
@ -513,7 +498,7 @@ RenderedTarget.prototype.isTouchingPoint = function (x, y) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether touching a stage edge.
|
* Return whether touching a stage edge.
|
||||||
* @return {Boolean} True iff the rendered target is touching the stage edge.
|
* @return {boolean} True iff the rendered target is touching the stage edge.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.isTouchingEdge = function () {
|
RenderedTarget.prototype.isTouchingEdge = function () {
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
|
@ -533,7 +518,7 @@ RenderedTarget.prototype.isTouchingEdge = function () {
|
||||||
/**
|
/**
|
||||||
* Return whether touching any of a named sprite's clones.
|
* Return whether touching any of a named sprite's clones.
|
||||||
* @param {string} spriteName Name of the sprite.
|
* @param {string} spriteName Name of the sprite.
|
||||||
* @return {Boolean} True iff touching a clone of the sprite.
|
* @return {boolean} True iff touching a clone of the sprite.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.isTouchingSprite = function (spriteName) {
|
RenderedTarget.prototype.isTouchingSprite = function (spriteName) {
|
||||||
var firstClone = this.runtime.getSpriteTargetByName(spriteName);
|
var firstClone = this.runtime.getSpriteTargetByName(spriteName);
|
||||||
|
@ -550,7 +535,7 @@ RenderedTarget.prototype.isTouchingSprite = function (spriteName) {
|
||||||
/**
|
/**
|
||||||
* Return whether touching a color.
|
* Return whether touching a color.
|
||||||
* @param {Array.<number>} rgb [r,g,b], values between 0-255.
|
* @param {Array.<number>} rgb [r,g,b], values between 0-255.
|
||||||
* @return {Promise.<Boolean>} True iff the rendered target is touching the color.
|
* @return {Promise.<boolean>} True iff the rendered target is touching the color.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.isTouchingColor = function (rgb) {
|
RenderedTarget.prototype.isTouchingColor = function (rgb) {
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
|
@ -561,9 +546,9 @@ RenderedTarget.prototype.isTouchingColor = function (rgb) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether rendered target's color is touching a color.
|
* Return whether rendered target's color is touching a color.
|
||||||
* @param {Object} targetRgb {Array.<number>} [r,g,b], values between 0-255.
|
* @param {object} targetRgb {Array.<number>} [r,g,b], values between 0-255.
|
||||||
* @param {Object} maskRgb {Array.<number>} [r,g,b], values between 0-255.
|
* @param {object} maskRgb {Array.<number>} [r,g,b], values between 0-255.
|
||||||
* @return {Promise.<Boolean>} True iff the color is touching the color.
|
* @return {Promise.<boolean>} True iff the color is touching the color.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) {
|
RenderedTarget.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) {
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
|
@ -611,7 +596,7 @@ RenderedTarget.prototype.goBehindOther = function (other) {
|
||||||
* Keep a desired position within a fence.
|
* Keep a desired position within a fence.
|
||||||
* @param {number} newX New desired X position.
|
* @param {number} newX New desired X position.
|
||||||
* @param {number} newY New desired Y position.
|
* @param {number} newY New desired Y position.
|
||||||
* @param {Object=} optFence Optional fence with left, right, top bottom.
|
* @param {object=} optFence Optional fence with left, right, top bottom.
|
||||||
* @return {Array.<number>} Fenced X and Y coordinates.
|
* @return {Array.<number>} Fenced X and Y coordinates.
|
||||||
*/
|
*/
|
||||||
RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) {
|
RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) {
|
||||||
|
|
|
@ -95,7 +95,7 @@ Cast.toRgbColorObject = function (value) {
|
||||||
* In Scratch 2.0, this is captured by `interp.compare.`
|
* In Scratch 2.0, this is captured by `interp.compare.`
|
||||||
* @param {*} v1 First value to compare.
|
* @param {*} v1 First value to compare.
|
||||||
* @param {*} v2 Second value to compare.
|
* @param {*} v2 Second value to compare.
|
||||||
* @returns {Number} Negative number if v1 < v2; 0 if equal; positive otherwise.
|
* @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise.
|
||||||
*/
|
*/
|
||||||
Cast.compare = function (v1, v2) {
|
Cast.compare = function (v1, v2) {
|
||||||
var n1 = Number(v1);
|
var n1 = Number(v1);
|
||||||
|
|
|
@ -45,4 +45,24 @@ MathUtil.wrapClamp = function (n, min, max) {
|
||||||
return n - (Math.floor((n - min) / range) * range);
|
return n - (Math.floor((n - min) / range) * range);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a value from tan function in degrees.
|
||||||
|
* @param {!number} angle in degrees
|
||||||
|
* @return {!number} Correct tan value
|
||||||
|
*/
|
||||||
|
MathUtil.tan = function (angle) {
|
||||||
|
angle = angle % 360;
|
||||||
|
switch (angle) {
|
||||||
|
case -270:
|
||||||
|
case 90:
|
||||||
|
return Infinity;
|
||||||
|
case -90:
|
||||||
|
case 270:
|
||||||
|
return -Infinity;
|
||||||
|
default:
|
||||||
|
return parseFloat(Math.tan((Math.PI * angle) / 180).toFixed(10));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = MathUtil;
|
module.exports = MathUtil;
|
||||||
|
|
|
@ -75,7 +75,7 @@ VirtualMachine.prototype.greenFlag = function () {
|
||||||
/**
|
/**
|
||||||
* Set whether the VM is in "turbo mode."
|
* Set whether the VM is in "turbo mode."
|
||||||
* When true, loops don't yield to redraw.
|
* When true, loops don't yield to redraw.
|
||||||
* @param {Boolean} turboModeOn Whether turbo mode should be set.
|
* @param {boolean} turboModeOn Whether turbo mode should be set.
|
||||||
*/
|
*/
|
||||||
VirtualMachine.prototype.setTurboMode = function (turboModeOn) {
|
VirtualMachine.prototype.setTurboMode = function (turboModeOn) {
|
||||||
this.runtime.turboMode = !!turboModeOn;
|
this.runtime.turboMode = !!turboModeOn;
|
||||||
|
@ -84,7 +84,7 @@ VirtualMachine.prototype.setTurboMode = function (turboModeOn) {
|
||||||
/**
|
/**
|
||||||
* Set whether the VM is in 2.0 "compatibility mode."
|
* Set whether the VM is in 2.0 "compatibility mode."
|
||||||
* When true, ticks go at 2.0 speed (30 TPS).
|
* When true, ticks go at 2.0 speed (30 TPS).
|
||||||
* @param {Boolean} compatibilityModeOn Whether compatibility mode is set.
|
* @param {boolean} compatibilityModeOn Whether compatibility mode is set.
|
||||||
*/
|
*/
|
||||||
VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) {
|
VirtualMachine.prototype.setCompatibilityMode = function (compatibilityModeOn) {
|
||||||
this.runtime.setCompatibilityMode(!!compatibilityModeOn);
|
this.runtime.setCompatibilityMode(!!compatibilityModeOn);
|
||||||
|
@ -129,7 +129,7 @@ VirtualMachine.prototype.getPlaygroundData = function () {
|
||||||
/**
|
/**
|
||||||
* Post I/O data to the virtual devices.
|
* Post I/O data to the virtual devices.
|
||||||
* @param {?string} device Name of virtual I/O device.
|
* @param {?string} device Name of virtual I/O device.
|
||||||
* @param {Object} data Any data object to post to the I/O device.
|
* @param {object} data Any data object to post to the I/O device.
|
||||||
*/
|
*/
|
||||||
VirtualMachine.prototype.postIOData = function (device, data) {
|
VirtualMachine.prototype.postIOData = function (device, data) {
|
||||||
if (this.runtime.ioDevices[device]) {
|
if (this.runtime.ioDevices[device]) {
|
||||||
|
@ -168,7 +168,7 @@ VirtualMachine.prototype.addSprite2 = function (json) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a costume to the current editing target.
|
* Add a costume to the current editing target.
|
||||||
* @param {!Object} costumeObject Object representing the costume.
|
* @param {!object} costumeObject Object representing the costume.
|
||||||
*/
|
*/
|
||||||
VirtualMachine.prototype.addCostume = function (costumeObject) {
|
VirtualMachine.prototype.addCostume = function (costumeObject) {
|
||||||
this.editingTarget.sprite.costumes.push(costumeObject);
|
this.editingTarget.sprite.costumes.push(costumeObject);
|
||||||
|
@ -180,7 +180,7 @@ VirtualMachine.prototype.addCostume = function (costumeObject) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a backdrop to the stage.
|
* Add a backdrop to the stage.
|
||||||
* @param {!Object} backdropObject Object representing the backdrop.
|
* @param {!object} backdropObject Object representing the backdrop.
|
||||||
*/
|
*/
|
||||||
VirtualMachine.prototype.addBackdrop = function (backdropObject) {
|
VirtualMachine.prototype.addBackdrop = function (backdropObject) {
|
||||||
var stage = this.runtime.getTargetForStage();
|
var stage = this.runtime.getTargetForStage();
|
||||||
|
|
BIN
test/fixtures/sound.sb2
vendored
Normal file
BIN
test/fixtures/sound.sb2
vendored
Normal file
Binary file not shown.
35
test/integration/sound.js
Normal file
35
test/integration/sound.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
var path = require('path');
|
||||||
|
var test = require('tap').test;
|
||||||
|
var extract = require('../fixtures/extract');
|
||||||
|
var VirtualMachine = require('../../src/index');
|
||||||
|
|
||||||
|
var uri = path.resolve(__dirname, '../fixtures/sound.sb2');
|
||||||
|
var project = extract(uri);
|
||||||
|
|
||||||
|
test('sound', function (t) {
|
||||||
|
var vm = new VirtualMachine();
|
||||||
|
|
||||||
|
// Evaluate playground data and exit
|
||||||
|
vm.on('playgroundData', function (e) {
|
||||||
|
var threads = JSON.parse(e.threads);
|
||||||
|
t.ok(threads.length > 0);
|
||||||
|
t.end();
|
||||||
|
process.nextTick(process.exit);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start VM, load project, and run
|
||||||
|
t.doesNotThrow(function () {
|
||||||
|
vm.start();
|
||||||
|
vm.clear();
|
||||||
|
vm.setCompatibilityMode(false);
|
||||||
|
vm.setTurboMode(false);
|
||||||
|
vm.loadProject(project);
|
||||||
|
vm.greenFlag();
|
||||||
|
});
|
||||||
|
|
||||||
|
// After two seconds, get playground data and stop
|
||||||
|
setTimeout(function () {
|
||||||
|
vm.getPlaygroundData();
|
||||||
|
vm.stopAll();
|
||||||
|
}, 2000);
|
||||||
|
});
|
|
@ -160,9 +160,13 @@ test('mathop', function (t) {
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'floor', NUM: 1.5}), 1);
|
t.strictEqual(blocks.mathop({OPERATOR: 'floor', NUM: 1.5}), 1);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'ceiling', NUM: 0.1}), 1);
|
t.strictEqual(blocks.mathop({OPERATOR: 'ceiling', NUM: 0.1}), 1);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'sqrt', NUM: 1}), 1);
|
t.strictEqual(blocks.mathop({OPERATOR: 'sqrt', NUM: 1}), 1);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'sin', NUM: 1}), 0.01745240643728351);
|
t.strictEqual(blocks.mathop({OPERATOR: 'sin', NUM: 1}), 0.0174524064);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'cos', NUM: 1}), 0.9998476951563913);
|
t.strictEqual(blocks.mathop({OPERATOR: 'sin', NUM: 90}), 1);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 1}), 0.017455064928217585);
|
t.strictEqual(blocks.mathop({OPERATOR: 'cos', NUM: 1}), 0.9998476952);
|
||||||
|
t.strictEqual(blocks.mathop({OPERATOR: 'cos', NUM: 180}), -1);
|
||||||
|
t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 1}), 0.0174550649);
|
||||||
|
t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 90}), Infinity);
|
||||||
|
t.strictEqual(blocks.mathop({OPERATOR: 'tan', NUM: 180}), 0);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'asin', NUM: 1}), 90);
|
t.strictEqual(blocks.mathop({OPERATOR: 'asin', NUM: 1}), 90);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'acos', NUM: 1}), 0);
|
t.strictEqual(blocks.mathop({OPERATOR: 'acos', NUM: 1}), 0);
|
||||||
t.strictEqual(blocks.mathop({OPERATOR: 'atan', NUM: 1}), 45);
|
t.strictEqual(blocks.mathop({OPERATOR: 'atan', NUM: 1}), 45);
|
||||||
|
|
|
@ -34,3 +34,11 @@ test('wrapClamp', function (t) {
|
||||||
t.strictEqual(math.wrapClamp(100, 0, 10), 1);
|
t.strictEqual(math.wrapClamp(100, 0, 10), 1);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('tan', function (t) {
|
||||||
|
t.strictEqual(math.tan(90), Infinity);
|
||||||
|
t.strictEqual(math.tan(180), 0);
|
||||||
|
t.strictEqual(math.tan(-90), -Infinity);
|
||||||
|
t.strictEqual(math.tan(33), 0.6494075932);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
@ -10,21 +10,10 @@ var base = {
|
||||||
port: process.env.PORT || 8073
|
port: process.env.PORT || 8073
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
module: {
|
|
||||||
loaders: [
|
|
||||||
{
|
|
||||||
test: /\.json$/,
|
|
||||||
loader: 'json-loader'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.optimize.UglifyJsPlugin({
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
include: /\.min\.js$/,
|
include: /\.min\.js$/,
|
||||||
minimize: true,
|
minimize: true
|
||||||
compress: {
|
|
||||||
warnings: false
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -42,12 +31,12 @@ module.exports = [
|
||||||
filename: '[name].js'
|
filename: '[name].js'
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
loaders: base.module.loaders.concat([
|
rules: [
|
||||||
{
|
{
|
||||||
test: require.resolve('./src/index.js'),
|
test: require.resolve('./src/index.js'),
|
||||||
loader: 'expose?VirtualMachine'
|
loader: 'expose-loader?VirtualMachine'
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
// Node-compatible
|
// Node-compatible
|
||||||
|
@ -86,32 +75,32 @@ module.exports = [
|
||||||
filename: '[name].js'
|
filename: '[name].js'
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
loaders: base.module.loaders.concat([
|
loaders: [
|
||||||
{
|
{
|
||||||
test: require.resolve('./src/index.js'),
|
test: require.resolve('./src/index.js'),
|
||||||
loader: 'expose?VirtualMachine'
|
loader: 'expose-loader?VirtualMachine'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: require.resolve('stats.js/build/stats.min.js'),
|
test: require.resolve('stats.js/build/stats.min.js'),
|
||||||
loader: 'script'
|
loader: 'script-loader'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: require.resolve('highlightjs/highlight.pack.min.js'),
|
test: require.resolve('highlightjs/highlight.pack.min.js'),
|
||||||
loader: 'script'
|
loader: 'script-loader'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: require.resolve('scratch-blocks/dist/vertical.js'),
|
test: require.resolve('scratch-blocks/dist/vertical.js'),
|
||||||
loader: 'expose?Blockly'
|
loader: 'expose-loader?Blockly'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: require.resolve('scratch-render'),
|
test: require.resolve('scratch-render'),
|
||||||
loader: 'expose?RenderWebGL'
|
loader: 'expose-loader?RenderWebGL'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: require.resolve('scratch-audio'),
|
test: require.resolve('scratch-audio'),
|
||||||
loader: 'expose?AudioEngine'
|
loader: 'expose-loader?AudioEngine'
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
},
|
},
|
||||||
plugins: base.plugins.concat([
|
plugins: base.plugins.concat([
|
||||||
new CopyWebpackPlugin([{
|
new CopyWebpackPlugin([{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue