Merge pull request #784 from fsih/perSpriteMonitors

Execute monitors on a given target ID when block is sprite-specific
This commit is contained in:
DD Liu 2017-11-21 10:23:53 -05:00 committed by GitHub
commit 58dd57fe48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 78 additions and 6 deletions

View file

@ -242,6 +242,15 @@ class Scratch3LooksBlocks {
}; };
} }
getMonitored () {
return {
looks_size: {isSpriteSpecific: true},
looks_costumeorder: {isSpriteSpecific: true},
looks_backdroporder: {},
looks_backdropname: {}
};
}
say (args, util) { say (args, util) {
// @TODO in 2.0 calling say/think resets the right/left bias of the bubble // @TODO in 2.0 calling say/think resets the right/left bias of the bubble
this._updateBubble(util.target, 'say', String(args.MESSAGE)); this._updateBubble(util.target, 'say', String(args.MESSAGE));

View file

@ -38,6 +38,14 @@ class Scratch3MotionBlocks {
}; };
} }
getMonitored () {
return {
motion_xposition: {isSpriteSpecific: true},
motion_yposition: {isSpriteSpecific: true},
motion_direction: {isSpriteSpecific: true}
};
}
moveSteps (args, util) { moveSteps (args, util) {
const steps = Cast.toNumber(args.STEPS); const steps = Cast.toNumber(args.STEPS);
const radians = MathUtil.degToRad(90 - util.target.direction); const radians = MathUtil.degToRad(90 - util.target.direction);

View file

@ -49,6 +49,16 @@ class Scratch3SensingBlocks {
}; };
} }
getMonitored () {
return {
sensing_answer: {},
sensing_loudness: {},
sensing_timer: {},
sensing_of: {},
sensing_current: {}
};
}
_onAnswer (answer) { _onAnswer (answer) {
this._answer = answer; this._answer = answer;
const questionObj = this._questionList.shift(); const questionObj = this._questionList.shift();

View file

@ -103,6 +103,12 @@ class Scratch3SoundBlocks {
}; };
} }
getMonitored () {
return {
sound_volume: {}
};
}
playSound (args, util) { playSound (args, util) {
const index = this._getSoundIndex(args.SOUND_MENU, util); const index = this._getSoundIndex(args.SOUND_MENU, util);
if (index >= 0) { if (index >= 0) {

View file

@ -381,12 +381,19 @@ class Blocks {
break; break;
case 'checkbox': case 'checkbox':
block.isMonitored = args.value; block.isMonitored = args.value;
if (optRuntime) {
const isSpriteSpecific = optRuntime.monitorBlockInfo.hasOwnProperty(block.opcode) &&
optRuntime.monitorBlockInfo[block.opcode].isSpriteSpecific;
block.targetId = isSpriteSpecific ? optRuntime.getEditingTarget().id : null;
}
if (optRuntime && wasMonitored && !block.isMonitored) { if (optRuntime && wasMonitored && !block.isMonitored) {
optRuntime.requestRemoveMonitor(block.id); optRuntime.requestRemoveMonitor(block.id);
} else if (optRuntime && !wasMonitored && block.isMonitored) { } else if (optRuntime && !wasMonitored && block.isMonitored) {
optRuntime.requestAddMonitor(MonitorRecord({ optRuntime.requestAddMonitor(MonitorRecord({
// @todo(vm#564) this will collide if multiple sprites use same block // @todo(vm#564) this will collide if multiple sprites use same block
id: block.id, id: block.id,
targetId: block.targetId,
spriteName: block.targetId ? optRuntime.getTargetById(block.targetId).getName() : null,
opcode: block.opcode, opcode: block.opcode,
params: this._getBlockParams(block), params: this._getBlockParams(block),
// @todo(vm#565) for numerical values with decimals, some countries use comma // @todo(vm#565) for numerical values with decimals, some countries use comma
@ -464,8 +471,8 @@ class Blocks {
runAllMonitored (runtime) { runAllMonitored (runtime) {
Object.keys(this._blocks).forEach(blockId => { Object.keys(this._blocks).forEach(blockId => {
if (this.getBlock(blockId).isMonitored) { if (this.getBlock(blockId).isMonitored) {
// @todo handle specific targets (e.g. apple x position) const targetId = this.getBlock(blockId).targetId;
runtime.addMonitorScript(blockId); runtime.addMonitorScript(blockId, targetId ? runtime.getTargetById(targetId) : null);
} }
}); });
} }

View file

@ -80,8 +80,14 @@ const handleReport = function (
sequencer.runtime.visualReport(currentBlockId, resolvedValue); sequencer.runtime.visualReport(currentBlockId, resolvedValue);
} }
if (thread.updateMonitor) { if (thread.updateMonitor) {
const targetId = sequencer.runtime.monitorBlocks.getBlock(currentBlockId).targetId;
if (targetId && !sequencer.runtime.getTargetById(targetId)) {
// Target no longer exists
return;
}
sequencer.runtime.requestUpdateMonitor(Map({ sequencer.runtime.requestUpdateMonitor(Map({
id: currentBlockId, id: currentBlockId,
spriteName: targetId ? sequencer.runtime.getTargetById(targetId).getName() : null,
value: String(resolvedValue) value: String(resolvedValue)
})); }));
} }

View file

@ -2,6 +2,10 @@ const {Record} = require('immutable');
const MonitorRecord = Record({ const MonitorRecord = Record({
id: null, id: null,
/** Present only if the monitor is sprite-specific, such as x position */
spriteName: null,
/** Present only if the monitor is sprite-specific, such as x position */
targetId: null,
opcode: null, opcode: null,
value: null, value: null,
params: null params: null

View file

@ -195,6 +195,13 @@ class Runtime extends EventEmitter {
*/ */
this._refreshTargets = false; this._refreshTargets = false;
/**
* Map to look up all monitor block information by opcode.
* @type {object}
* @private
*/
this.monitorBlockInfo = {};
/** /**
* Ordered map of all monitors, which are MonitorReporter objects. * Ordered map of all monitors, which are MonitorReporter objects.
*/ */
@ -423,6 +430,10 @@ class Runtime extends EventEmitter {
} }
} }
} }
// Collect monitored from package.
if (packageObject.getMonitored) {
this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored());
}
} }
} }
} }
@ -870,7 +881,7 @@ class Runtime extends EventEmitter {
/** /**
* Enqueue a script that when finished will update the monitor for the block. * Enqueue a script that when finished will update the monitor for the block.
* @param {!string} topBlockId ID of block that starts the script. * @param {!string} topBlockId ID of block that starts the script.
* @param {?string} optTarget target ID for target to run script on. If not supplied, uses editing target. * @param {?Target} optTarget target Target to run script on. If not supplied, uses editing target.
*/ */
addMonitorScript (topBlockId, optTarget) { addMonitorScript (topBlockId, optTarget) {
if (!optTarget) optTarget = this._editingTarget; if (!optTarget) optTarget = this._editingTarget;
@ -1321,7 +1332,7 @@ class Runtime extends EventEmitter {
* @param {!MonitorRecord} monitor Monitor to add. * @param {!MonitorRecord} monitor Monitor to add.
*/ */
requestAddMonitor (monitor) { requestAddMonitor (monitor) {
this._monitorState = this._monitorState.set(monitor.id, monitor); this._monitorState = this._monitorState.set(monitor.get('id'), monitor);
} }
/** /**
@ -1332,9 +1343,10 @@ class Runtime extends EventEmitter {
* the old monitor will keep its old value. * the old monitor will keep its old value.
*/ */
requestUpdateMonitor (monitor) { requestUpdateMonitor (monitor) {
if (this._monitorState.has(monitor.get('id'))) { const id = monitor.get('id');
if (this._monitorState.has(id)) {
this._monitorState = this._monitorState =
this._monitorState.set(monitor.get('id'), this._monitorState.get(monitor.get('id')).merge(monitor)); this._monitorState.set(id, this._monitorState.get(id).merge(monitor));
} }
} }
@ -1347,6 +1359,15 @@ class Runtime extends EventEmitter {
this._monitorState = this._monitorState.delete(monitorId); this._monitorState = this._monitorState.delete(monitorId);
} }
/**
* Removes all monitors with the given target ID from the state. Does nothing if
* the monitor already does not exist in the state.
* @param {!string} targetId Remove all monitors with given target ID.
*/
requestRemoveMonitorByTargetId (targetId) {
this._monitorState = this._monitorState.filterNot(value => value.targetId === targetId);
}
/** /**
* Get a target by its id. * Get a target by its id.
* @param {string} targetId Id of target to find. * @param {string} targetId Id of target to find.

View file

@ -484,6 +484,7 @@ class VirtualMachine extends EventEmitter {
if (!sprite) { if (!sprite) {
throw new Error('No sprite associated with this target.'); throw new Error('No sprite associated with this target.');
} }
this.runtime.requestRemoveMonitorByTargetId(targetId);
const currentEditingTarget = this.editingTarget; const currentEditingTarget = this.editingTarget;
for (let i = 0; i < sprite.clones.length; i++) { for (let i = 0; i < sprite.clones.length; i++) {
const clone = sprite.clones[i]; const clone = sprite.clones[i];