diff --git a/README.md b/README.md
index 809c871d..51902d44 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
#### Scratch VM is a library for representing, running, and maintaining the state of computer programs written using [Scratch Blocks](https://github.com/LLK/scratch-blocks).
[](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://david-dm.org/LLK/scratch-vm#info=devDependencies)
diff --git a/playground/index.html b/playground/index.html
index 94b455e1..4c0a454b 100644
--- a/playground/index.html
+++ b/playground/index.html
@@ -31,6 +31,12 @@
Renderer
+ x:
+ y:
+ dir:
+ rotation style:
+ visible:
+
Thread explorer
diff --git a/playground/playground.js b/playground/playground.js
index 5a88de76..75027dc3 100644
--- a/playground/playground.js
+++ b/playground/playground.js
@@ -153,6 +153,24 @@ window.onload = function() {
workspace.reportValue(data.id, data.value);
});
+ vm.on('SPRITE_INFO_REPORT', function(data) {
+ document.getElementById('sinfo-x').value = data.x;
+ document.getElementById('sinfo-y').value = data.y;
+ document.getElementById('sinfo-direction').value = data.direction;
+ document.getElementById('sinfo-rotationstyle').value = data.rotationStyle;
+ document.getElementById('sinfo-visible').value = data.visible;
+ });
+
+ document.getElementById('sinfo-post').addEventListener('click', function () {
+ var data = {};
+ data.x = document.getElementById('sinfo-x').value;
+ data.y = document.getElementById('sinfo-y').value;
+ data.direction = document.getElementById('sinfo-direction').value;
+ data.rotationStyle = document.getElementById('sinfo-rotationstyle').value;
+ data.visible = document.getElementById('sinfo-visible').value === 'true';
+ vm.postSpriteInfo(data);
+ });
+
// Feed mouse events as VM I/O events.
document.addEventListener('mousemove', function (e) {
var rect = canvas.getBoundingClientRect();
@@ -219,9 +237,7 @@ window.onload = function() {
// Inform VM of animation frames.
var animate = function() {
- stats.end();
- stats.begin();
- window.vm.animationFrame();
+ stats.update();
requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index 0a8930a1..4fe871a6 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -101,13 +101,6 @@ var Runtime = function () {
*/
this.compatibilityMode = false;
- /**
- * How fast in ms "single stepping mode" should run, in ms.
- * Can be updated dynamically.
- * @type {!number}
- */
- this.singleStepInterval = 1000 / 10;
-
/**
* A reference to the current runtime stepping interval, set
* by a `setInterval`.
@@ -192,6 +185,12 @@ Runtime.BLOCK_GLOW_OFF = 'BLOCK_GLOW_OFF';
*/
Runtime.VISUAL_REPORT = 'VISUAL_REPORT';
+/**
+ * Event name for sprite info report.
+ * @const {string}
+ */
+Runtime.SPRITE_INFO_REPORT = 'SPRITE_INFO_REPORT';
+
/**
* How rapidly we try to step threads by default, in ms.
*/
@@ -534,6 +533,10 @@ Runtime.prototype._step = function () {
this.redrawRequested = false;
var inactiveThreads = this.sequencer.stepThreads();
this._updateGlows(inactiveThreads);
+ if (this.renderer) {
+ // @todo: Only render when this.redrawRequested or clones rendered.
+ this.renderer.draw();
+ }
};
/**
@@ -545,6 +548,7 @@ Runtime.prototype.setEditingTarget = function (editingTarget) {
// Script glows must be cleared.
this._scriptGlowsPreviousFrame = [];
this._updateGlows();
+ this.spriteInfoReport(editingTarget);
};
/**
@@ -664,6 +668,23 @@ Runtime.prototype.visualReport = function (blockId, value) {
this.emit(Runtime.VISUAL_REPORT, blockId, String(value));
};
+/**
+ * Emit a sprite info report if the provided target is the editing target.
+ * @param {!Target} target Target to report sprite info for.
+ */
+Runtime.prototype.spriteInfoReport = function (target) {
+ if (target !== this._editingTarget) {
+ return;
+ }
+ this.emit(Runtime.SPRITE_INFO_REPORT, {
+ x: target.x,
+ y: target.y,
+ direction: target.direction,
+ visible: target.visible,
+ rotationStyle: target.rotationStyle
+ });
+};
+
/**
* Get a target by its id.
* @param {string} targetId Id of target to find.
@@ -729,24 +750,12 @@ Runtime.prototype.requestRedraw = function () {
this.redrawRequested = true;
};
-/**
- * Handle an animation frame from the main thread.
- */
-Runtime.prototype.animationFrame = function () {
- if (this.renderer) {
- // @todo: Only render when this.redrawRequested or clones rendered.
- this.renderer.draw();
- }
-};
-
/**
* Set up timers to repeatedly step in a browser.
*/
Runtime.prototype.start = function () {
var interval = Runtime.THREAD_STEP_INTERVAL;
- if (this.singleStepping) {
- interval = this.singleStepInterval;
- } else if (this.compatibilityMode) {
+ if (this.compatibilityMode) {
interval = Runtime.THREAD_STEP_INTERVAL_COMPATIBILITY;
}
this.currentStepTime = interval;
diff --git a/src/engine/target.js b/src/engine/target.js
index f8f99f47..e36790b9 100644
--- a/src/engine/target.js
+++ b/src/engine/target.js
@@ -105,6 +105,13 @@ Target.prototype.lookupOrCreateList = function (name) {
return newList;
};
+/**
+ * Post/edit sprite info.
+ * @param {object} data An object with sprite info data to set.
+ * @abstract
+ */
+Target.prototype.postSpriteInfo = function () {};
+
/**
* Call to destroy a target.
* @abstract
diff --git a/src/import/sb2import.js b/src/import/sb2import.js
index dd7e9a99..aed61b69 100644
--- a/src/import/sb2import.js
+++ b/src/import/sb2import.js
@@ -6,7 +6,7 @@
*/
var Blocks = require('../engine/blocks');
-var Clone = require('../sprites/clone');
+var RenderedTarget = require('../sprites/rendered-target');
var Sprite = require('../sprites/sprite');
var Color = require('../util/color.js');
var log = require('../util/log');
@@ -101,11 +101,11 @@ var parseScratchObject = function (object, runtime, topLevel) {
}
if (object.hasOwnProperty('rotationStyle')) {
if (object.rotationStyle === 'none') {
- target.rotationStyle = Clone.ROTATION_STYLE_NONE;
+ target.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE;
} else if (object.rotationStyle === 'leftRight') {
- target.rotationStyle = Clone.ROTATION_STYLE_LEFT_RIGHT;
+ target.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT;
} else if (object.rotationStyle === 'normal') {
- target.rotationStyle = Clone.ROTATION_STYLE_ALL_AROUND;
+ target.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND;
}
}
target.isStage = topLevel;
diff --git a/src/index.js b/src/index.js
index 56962277..a275cfac 100644
--- a/src/index.js
+++ b/src/index.js
@@ -40,6 +40,9 @@ var VirtualMachine = function () {
instance.runtime.on(Runtime.VISUAL_REPORT, function (id, value) {
instance.emit(Runtime.VISUAL_REPORT, {id: id, value: value});
});
+ instance.runtime.on(Runtime.SPRITE_INFO_REPORT, function (data) {
+ instance.emit(Runtime.SPRITE_INFO_REPORT, data);
+ });
this.blockListener = this.blockListener.bind(this);
this.flyoutBlockListener = this.flyoutBlockListener.bind(this);
@@ -118,13 +121,6 @@ VirtualMachine.prototype.getPlaygroundData = function () {
});
};
-/**
- * Handle an animation frame.
- */
-VirtualMachine.prototype.animationFrame = function () {
- this.runtime.animationFrame();
-};
-
/**
* Post I/O data to the virtual devices.
* @param {?string} device Name of virtual I/O device.
@@ -188,6 +184,59 @@ VirtualMachine.prototype.addBackdrop = function (backdropObject) {
stage.setCostume(stage.sprite.costumes.length - 1);
};
+/**
+ * Rename a sprite.
+ * @param {string} targetId ID of a target whose sprite to rename.
+ * @param {string} newName New name of the sprite.
+ */
+VirtualMachine.prototype.renameSprite = function (targetId, newName) {
+ var target = this.runtime.getTargetById(targetId);
+ if (target) {
+ if (!target.isSprite()) {
+ throw new Error('Cannot rename non-sprite targets.');
+ }
+ var sprite = target.sprite;
+ if (!sprite) {
+ throw new Error('No sprite associated with this target.');
+ }
+ sprite.name = newName;
+ this.emitTargetsUpdate();
+ } else {
+ throw new Error('No target with the provided id.');
+ }
+};
+
+/**
+ * Delete a sprite and all its clones.
+ * @param {string} targetId ID of a target whose sprite to delete.
+ */
+VirtualMachine.prototype.deleteSprite = function (targetId) {
+ var target = this.runtime.getTargetById(targetId);
+ if (target) {
+ if (!target.isSprite()) {
+ throw new Error('Cannot delete non-sprite targets.');
+ }
+ var sprite = target.sprite;
+ if (!sprite) {
+ throw new Error('No sprite associated with this target.');
+ }
+ var currentEditingTarget = this.editingTarget;
+ for (var i = 0; i < sprite.clones.length; i++) {
+ var clone = sprite.clones[i];
+ this.runtime.stopForTarget(sprite.clones[i]);
+ this.runtime.disposeTarget(sprite.clones[i]);
+ // Ensure editing target is switched if we are deleting it.
+ if (clone === currentEditingTarget) {
+ this.setEditingTarget(this.runtime.targets[0].id);
+ }
+ }
+ // Sprite object should be deleted by GC.
+ this.emitTargetsUpdate();
+ } else {
+ throw new Error('No target with the provided id.');
+ }
+};
+
/**
* Set the renderer for the VM/runtime
* @param {!RenderWebGL} renderer The renderer to attach
@@ -266,4 +315,12 @@ VirtualMachine.prototype.emitWorkspaceUpdate = function () {
});
};
+/**
+ * Post/edit sprite info for the current editing target.
+ * @param {object} data An object with sprite info data to set.
+ */
+VirtualMachine.prototype.postSpriteInfo = function (data) {
+ this.editingTarget.postSpriteInfo(data);
+};
+
module.exports = VirtualMachine;
diff --git a/src/sprites/clone.js b/src/sprites/rendered-target.js
similarity index 63%
rename from src/sprites/clone.js
rename to src/sprites/rendered-target.js
index 7d91d30d..89270397 100644
--- a/src/sprites/clone.js
+++ b/src/sprites/rendered-target.js
@@ -5,16 +5,16 @@ var MathUtil = require('../util/math-util');
var Target = require('../engine/target');
/**
- * Clone (instance) of a sprite.
- * @param {!Sprite} sprite Reference to the sprite.
+ * Rendered target: instance of a sprite (clone), or the stage.
+ * @param {!Sprite} sprite Reference to the parent sprite.
* @param {Runtime} runtime Reference to the runtime.
* @constructor
*/
-var Clone = function (sprite, runtime) {
+var RenderedTarget = function (sprite, runtime) {
Target.call(this, sprite.blocks);
this.runtime = runtime;
/**
- * Reference to the sprite that this is a clone of.
+ * Reference to the sprite that this is a render of.
* @type {!Sprite}
*/
this.sprite = sprite;
@@ -27,7 +27,8 @@ var Clone = function (sprite, runtime) {
this.renderer = this.runtime.renderer;
}
/**
- * ID of the drawable for this clone returned by the renderer, if rendered.
+ * ID of the drawable for this rendered target,
+ * returned by the renderer, if rendered.
* @type {?Number}
*/
this.drawableID = null;
@@ -46,12 +47,12 @@ var Clone = function (sprite, runtime) {
ghost: 0
};
};
-util.inherits(Clone, Target);
+util.inherits(RenderedTarget, Target);
/**
- * Create a clone's drawable with the this.renderer.
+ * Create a drawable with the this.renderer.
*/
-Clone.prototype.initDrawable = function () {
+RenderedTarget.prototype.initDrawable = function () {
if (this.renderer) {
this.drawableID = this.renderer.createDrawable();
}
@@ -63,88 +64,87 @@ Clone.prototype.initDrawable = function () {
}
};
-// Clone-level properties.
/**
- * Whether this represents an "original" clone, i.e., created by the editor
- * and not clone blocks. In interface terms, this true for a "sprite."
+ * Whether this represents an "original" non-clone rendered-target for a sprite,
+ * i.e., created by the editor and not clone blocks.
* @type {boolean}
*/
-Clone.prototype.isOriginal = true;
+RenderedTarget.prototype.isOriginal = true;
/**
- * Whether this clone represents the Scratch stage.
+ * Whether this rendered target represents the Scratch stage.
* @type {boolean}
*/
-Clone.prototype.isStage = false;
+RenderedTarget.prototype.isStage = false;
/**
* Scratch X coordinate. Currently should range from -240 to 240.
* @type {Number}
*/
-Clone.prototype.x = 0;
+RenderedTarget.prototype.x = 0;
/**
* Scratch Y coordinate. Currently should range from -180 to 180.
* @type {number}
*/
-Clone.prototype.y = 0;
+RenderedTarget.prototype.y = 0;
/**
* Scratch direction. Currently should range from -179 to 180.
* @type {number}
*/
-Clone.prototype.direction = 90;
+RenderedTarget.prototype.direction = 90;
/**
- * Whether the clone is currently visible.
+ * Whether the rendered target is currently visible.
* @type {boolean}
*/
-Clone.prototype.visible = true;
+RenderedTarget.prototype.visible = true;
/**
- * Size of clone as a percent of costume size. Ranges from 5% to 535%.
+ * Size of rendered target as a percent of costume size.
* @type {number}
*/
-Clone.prototype.size = 100;
+RenderedTarget.prototype.size = 100;
/**
* Currently selected costume index.
* @type {number}
*/
-Clone.prototype.currentCostume = 0;
+RenderedTarget.prototype.currentCostume = 0;
/**
* Rotation style for "all around"/spinning.
* @enum
*/
-Clone.ROTATION_STYLE_ALL_AROUND = 'all around';
+RenderedTarget.ROTATION_STYLE_ALL_AROUND = 'all around';
/**
* Rotation style for "left-right"/flipping.
* @enum
*/
-Clone.ROTATION_STYLE_LEFT_RIGHT = 'left-right';
+RenderedTarget.ROTATION_STYLE_LEFT_RIGHT = 'left-right';
/**
* Rotation style for "no rotation."
* @enum
*/
-Clone.ROTATION_STYLE_NONE = 'don\'t rotate';
+RenderedTarget.ROTATION_STYLE_NONE = 'don\'t rotate';
/**
* Current rotation style.
* @type {!string}
*/
-Clone.prototype.rotationStyle = Clone.ROTATION_STYLE_ALL_AROUND;
-
-// End clone-level properties.
+RenderedTarget.prototype.rotationStyle = (
+ RenderedTarget.ROTATION_STYLE_ALL_AROUND
+);
/**
- * Set the X and Y coordinates of a clone.
- * @param {!number} x New X coordinate of clone, in Scratch coordinates.
- * @param {!number} y New Y coordinate of clone, in Scratch coordinates.
+ * Set the X and Y coordinates.
+ * @param {!number} x New X coordinate, in Scratch coordinates.
+ * @param {!number} y New Y coordinate, in Scratch coordinates.
*/
-Clone.prototype.setXY = function (x, y) {
+RenderedTarget.prototype.setXY = function (x, y) {
if (this.isStage) {
return;
}
@@ -158,20 +158,21 @@ Clone.prototype.setXY = function (x, y) {
this.runtime.requestRedraw();
}
}
+ this.runtime.spriteInfoReport(this);
};
/**
* Get the rendered direction and scale, after applying rotation style.
* @return {Object} Direction and scale to render.
*/
-Clone.prototype._getRenderedDirectionAndScale = function () {
+RenderedTarget.prototype._getRenderedDirectionAndScale = function () {
// Default: no changes to `this.direction` or `this.scale`.
var finalDirection = this.direction;
var finalScale = [this.size, this.size];
- if (this.rotationStyle === Clone.ROTATION_STYLE_NONE) {
+ if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) {
// Force rendered direction to be 90.
finalDirection = 90;
- } else if (this.rotationStyle === Clone.ROTATION_STYLE_LEFT_RIGHT) {
+ } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) {
// Force rendered direction to be 90, and flip drawable if needed.
finalDirection = 90;
var scaleFlip = (this.direction < 0) ? -1 : 1;
@@ -181,10 +182,10 @@ Clone.prototype._getRenderedDirectionAndScale = function () {
};
/**
- * Set the direction of a clone.
- * @param {!number} direction New direction of clone.
+ * Set the direction.
+ * @param {!number} direction New direction.
*/
-Clone.prototype.setDirection = function (direction) {
+RenderedTarget.prototype.setDirection = function (direction) {
if (this.isStage) {
return;
}
@@ -200,14 +201,15 @@ Clone.prototype.setDirection = function (direction) {
this.runtime.requestRedraw();
}
}
+ this.runtime.spriteInfoReport(this);
};
/**
- * Set a say bubble on this clone.
+ * Set a say bubble.
* @param {?string} type Type of say bubble: "say", "think", or null.
* @param {?string} message Message to put in say bubble.
*/
-Clone.prototype.setSay = function (type, message) {
+RenderedTarget.prototype.setSay = function (type, message) {
if (this.isStage) {
return;
}
@@ -220,14 +222,14 @@ Clone.prototype.setSay = function (type, message) {
};
/**
- * Set visibility of the clone; i.e., whether it's shown or hidden.
- * @param {!boolean} visible True if the sprite should be shown.
+ * Set visibility; i.e., whether it's shown or hidden.
+ * @param {!boolean} visible True if should be shown.
*/
-Clone.prototype.setVisible = function (visible) {
+RenderedTarget.prototype.setVisible = function (visible) {
if (this.isStage) {
return;
}
- this.visible = visible;
+ this.visible = !!visible;
if (this.renderer) {
this.renderer.updateDrawableProperties(this.drawableID, {
visible: this.visible
@@ -236,13 +238,14 @@ Clone.prototype.setVisible = function (visible) {
this.runtime.requestRedraw();
}
}
+ this.runtime.spriteInfoReport(this);
};
/**
- * Set size of the clone, as a percentage of the costume size.
- * @param {!number} size Size of clone, from 5 to 535.
+ * Set size, as a percentage of the costume size.
+ * @param {!number} size Size of rendered target, as % of costume size.
*/
-Clone.prototype.setSize = function (size) {
+RenderedTarget.prototype.setSize = function (size) {
if (this.isStage) {
return;
}
@@ -261,11 +264,11 @@ Clone.prototype.setSize = function (size) {
};
/**
- * Set a particular graphic effect on this clone.
- * @param {!string} effectName Name of effect (see `Clone.prototype.effects`).
+ * Set a particular graphic effect value.
+ * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`).
* @param {!number} value Numerical magnitude of effect.
*/
-Clone.prototype.setEffect = function (effectName, value) {
+RenderedTarget.prototype.setEffect = function (effectName, value) {
if (!this.effects.hasOwnProperty(effectName)) return;
this.effects[effectName] = value;
if (this.renderer) {
@@ -279,9 +282,9 @@ Clone.prototype.setEffect = function (effectName, value) {
};
/**
- * Clear all graphic effects on this clone.
+ * Clear all graphic effects on this rendered target.
*/
-Clone.prototype.clearEffects = function () {
+RenderedTarget.prototype.clearEffects = function () {
for (var effectName in this.effects) {
this.effects[effectName] = 0;
}
@@ -294,10 +297,10 @@ Clone.prototype.clearEffects = function () {
};
/**
- * Set the current costume of this clone.
+ * Set the current costume.
* @param {number} index New index of costume.
*/
-Clone.prototype.setCostume = function (index) {
+RenderedTarget.prototype.setCostume = function (index) {
// Keep the costume index within possible values.
index = Math.round(index);
this.currentCostume = MathUtil.wrapClamp(
@@ -308,7 +311,10 @@ Clone.prototype.setCostume = function (index) {
this.renderer.updateDrawableProperties(this.drawableID, {
skin: costume.skin,
costumeResolution: costume.bitmapResolution,
- rotationCenter: [costume.rotationCenterX, costume.rotationCenterY]
+ rotationCenter: [
+ costume.rotationCenterX / costume.bitmapResolution,
+ costume.rotationCenterY / costume.bitmapResolution
+ ]
});
if (this.visible) {
this.runtime.requestRedraw();
@@ -317,16 +323,16 @@ Clone.prototype.setCostume = function (index) {
};
/**
- * Update the rotation style for this clone.
+ * Update the rotation style.
* @param {!string} rotationStyle New rotation style.
*/
-Clone.prototype.setRotationStyle = function (rotationStyle) {
- if (rotationStyle === Clone.ROTATION_STYLE_NONE) {
- this.rotationStyle = Clone.ROTATION_STYLE_NONE;
- } else if (rotationStyle === Clone.ROTATION_STYLE_ALL_AROUND) {
- this.rotationStyle = Clone.ROTATION_STYLE_ALL_AROUND;
- } else if (rotationStyle === Clone.ROTATION_STYLE_LEFT_RIGHT) {
- this.rotationStyle = Clone.ROTATION_STYLE_LEFT_RIGHT;
+RenderedTarget.prototype.setRotationStyle = function (rotationStyle) {
+ if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) {
+ this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE;
+ } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) {
+ this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND;
+ } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) {
+ this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT;
}
if (this.renderer) {
var renderedDirectionScale = this._getRenderedDirectionAndScale();
@@ -338,14 +344,15 @@ Clone.prototype.setRotationStyle = function (rotationStyle) {
this.runtime.requestRedraw();
}
}
+ this.runtime.spriteInfoReport(this);
};
/**
- * Get a costume index of this clone, by name of the costume.
+ * Get a costume index of this rendered target, by name of the costume.
* @param {?string} costumeName Name of a costume.
* @return {number} Index of the named costume, or -1 if not present.
*/
-Clone.prototype.getCostumeIndexByName = function (costumeName) {
+RenderedTarget.prototype.getCostumeIndexByName = function (costumeName) {
for (var i = 0; i < this.sprite.costumes.length; i++) {
if (this.sprite.costumes[i].name === costumeName) {
return i;
@@ -355,10 +362,10 @@ Clone.prototype.getCostumeIndexByName = function (costumeName) {
};
/**
- * Update all drawable properties for this clone.
+ * Update all drawable properties for this rendered target.
* Use when a batch has changed, e.g., when the drawable is first created.
*/
-Clone.prototype.updateAllDrawableProperties = function () {
+RenderedTarget.prototype.updateAllDrawableProperties = function () {
if (this.renderer) {
var renderedDirectionScale = this._getRenderedDirectionAndScale();
var costume = this.sprite.costumes[this.currentCostume];
@@ -369,29 +376,41 @@ Clone.prototype.updateAllDrawableProperties = function () {
visible: this.visible,
skin: costume.skin,
costumeResolution: costume.bitmapResolution,
- rotationCenter: [costume.rotationCenterX, costume.rotationCenterY]
+ rotationCenter: [
+ costume.rotationCenterX / costume.bitmapResolution,
+ costume.rotationCenterY / costume.bitmapResolution
+ ]
});
if (this.visible) {
this.runtime.requestRedraw();
}
}
+ this.runtime.spriteInfoReport(this);
};
/**
- * Return the human-readable name for this clone, i.e., the sprite's name.
+ * Return the human-readable name for this rendered target, e.g., the sprite's name.
* @override
- * @returns {string} Human-readable name for the clone.
+ * @returns {string} Human-readable name.
*/
-Clone.prototype.getName = function () {
+RenderedTarget.prototype.getName = function () {
return this.sprite.name;
};
/**
- * Return the clone's tight bounding box.
- * Includes top, left, bottom, right attributes in Scratch coordinates.
- * @return {?Object} Tight bounding box of clone, or null.
+ * Return whether this rendered target is a sprite (not a clone, not the stage).
+ * @return {boolean} True if not a clone and not the stage.
*/
-Clone.prototype.getBounds = function () {
+RenderedTarget.prototype.isSprite = function () {
+ return !this.isStage && this.isOriginal;
+};
+
+/**
+ * Return the rendered target's tight bounding box.
+ * Includes top, left, bottom, right attributes in Scratch coordinates.
+ * @return {?Object} Tight bounding box, or null.
+ */
+RenderedTarget.prototype.getBounds = function () {
if (this.renderer) {
return this.runtime.renderer.getBounds(this.drawableID);
}
@@ -399,12 +418,12 @@ Clone.prototype.getBounds = function () {
};
/**
- * Return whether the clone is touching a point.
+ * Return whether touching a point.
* @param {number} x X coordinate of test point.
* @param {number} y Y coordinate of test point.
- * @return {Boolean} True iff the clone is touching the point.
+ * @return {Boolean} True iff the rendered target is touching the point.
*/
-Clone.prototype.isTouchingPoint = function (x, y) {
+RenderedTarget.prototype.isTouchingPoint = function (x, y) {
if (this.renderer) {
// @todo: Update once pick is in Scratch coordinates.
// Limits test to this Drawable, so this will return true
@@ -421,10 +440,10 @@ Clone.prototype.isTouchingPoint = function (x, y) {
};
/**
- * Return whether the clone is touching a stage edge.
- * @return {Boolean} True iff the clone is touching the stage edge.
+ * Return whether touching a stage edge.
+ * @return {Boolean} True iff the rendered target is touching the stage edge.
*/
-Clone.prototype.isTouchingEdge = function () {
+RenderedTarget.prototype.isTouchingEdge = function () {
if (this.renderer) {
var stageWidth = this.runtime.constructor.STAGE_WIDTH;
var stageHeight = this.runtime.constructor.STAGE_HEIGHT;
@@ -440,11 +459,11 @@ Clone.prototype.isTouchingEdge = function () {
};
/**
- * Return whether the clone is touching a named sprite.
- * @param {string} spriteName Name fo the sprite.
- * @return {Boolean} True iff the clone is touching a clone of the sprite.
+ * Return whether touching any of a named sprite's clones.
+ * @param {string} spriteName Name of the sprite.
+ * @return {Boolean} True iff touching a clone of the sprite.
*/
-Clone.prototype.isTouchingSprite = function (spriteName) {
+RenderedTarget.prototype.isTouchingSprite = function (spriteName) {
var firstClone = this.runtime.getSpriteTargetByName(spriteName);
if (!firstClone || !this.renderer) {
return false;
@@ -457,11 +476,11 @@ Clone.prototype.isTouchingSprite = function (spriteName) {
};
/**
- * Return whether the clone is touching a color.
+ * Return whether touching a color.
* @param {Array.} rgb [r,g,b], values between 0-255.
- * @return {Promise.} True iff the clone is touching the color.
+ * @return {Promise.} True iff the rendered target is touching the color.
*/
-Clone.prototype.isTouchingColor = function (rgb) {
+RenderedTarget.prototype.isTouchingColor = function (rgb) {
if (this.renderer) {
return this.renderer.isTouchingColor(this.drawableID, rgb);
}
@@ -469,12 +488,12 @@ Clone.prototype.isTouchingColor = function (rgb) {
};
/**
- * Return whether the clone's color is touching a color.
+ * Return whether rendered target's color is touching a color.
* @param {Object} targetRgb {Array.} [r,g,b], values between 0-255.
* @param {Object} maskRgb {Array.} [r,g,b], values between 0-255.
- * @return {Promise.} True iff the clone's color is touching the color.
+ * @return {Promise.} True iff the color is touching the color.
*/
-Clone.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) {
+RenderedTarget.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) {
if (this.renderer) {
return this.renderer.isTouchingColor(
this.drawableID,
@@ -486,32 +505,32 @@ Clone.prototype.colorIsTouchingColor = function (targetRgb, maskRgb) {
};
/**
- * Move clone to the front layer.
+ * Move to the front layer.
*/
-Clone.prototype.goToFront = function () {
+RenderedTarget.prototype.goToFront = function () {
if (this.renderer) {
this.renderer.setDrawableOrder(this.drawableID, Infinity);
}
};
/**
- * Move clone back a number of layers.
+ * Move back a number of layers.
* @param {number} nLayers How many layers to go back.
*/
-Clone.prototype.goBackLayers = function (nLayers) {
+RenderedTarget.prototype.goBackLayers = function (nLayers) {
if (this.renderer) {
this.renderer.setDrawableOrder(this.drawableID, -nLayers, true, 1);
}
};
/**
- * Move behind some other clone.
- * @param {!Clone} otherClone Other clone to move behind.
+ * Move behind some other rendered target.
+ * @param {!Clone} other Other rendered target to move behind.
*/
-Clone.prototype.goBehindOtherClone = function (otherClone) {
+RenderedTarget.prototype.goBehindOther = function (other) {
if (this.renderer) {
var otherLayer = this.renderer.setDrawableOrder(
- otherClone.drawableID, 0, true);
+ other.drawableID, 0, true);
this.renderer.setDrawableOrder(this.drawableID, otherLayer);
}
};
@@ -523,7 +542,7 @@ Clone.prototype.goBehindOtherClone = function (otherClone) {
* @param {Object=} optFence Optional fence with left, right, top bottom.
* @return {Array.} Fenced X and Y coordinates.
*/
-Clone.prototype.keepInFence = function (newX, newY, optFence) {
+RenderedTarget.prototype.keepInFence = function (newX, newY, optFence) {
var fence = optFence;
if (!fence) {
fence = {
@@ -559,11 +578,11 @@ Clone.prototype.keepInFence = function (newX, newY, optFence) {
};
/**
- * Make a clone of this clone, copying any run-time properties.
+ * Make a clone, copying any run-time properties.
* If we've hit the global clone limit, returns null.
- * @return {!Clone} New clone object.
+ * @return {!RenderedTarget} New clone.
*/
-Clone.prototype.makeClone = function () {
+RenderedTarget.prototype.makeClone = function () {
if (!this.runtime.clonesAvailable() || this.isStage) {
return; // Hit max clone limit, or this is the stage.
}
@@ -582,23 +601,45 @@ Clone.prototype.makeClone = function () {
newClone.lists = JSON.parse(JSON.stringify(this.lists));
newClone.initDrawable();
newClone.updateAllDrawableProperties();
- // Place clone behind the current target.
- newClone.goBehindOtherClone(this);
+ // Place behind the current target.
+ newClone.goBehindOther(this);
return newClone;
};
/**
* Called when the project receives a "green flag."
- * For a clone, this clears graphic effects.
+ * For a rendered target, this clears graphic effects.
*/
-Clone.prototype.onGreenFlag = function () {
+RenderedTarget.prototype.onGreenFlag = function () {
this.clearEffects();
};
/**
- * Dispose of this clone, destroying any run-time properties.
+ * Post/edit sprite info.
+ * @param {object} data An object with sprite info data to set.
*/
-Clone.prototype.dispose = function () {
+RenderedTarget.prototype.postSpriteInfo = function (data) {
+ if (data.hasOwnProperty('x')) {
+ this.setXY(data.x, this.y);
+ }
+ if (data.hasOwnProperty('y')) {
+ this.setXY(this.x, data.y);
+ }
+ if (data.hasOwnProperty('direction')) {
+ this.setDirection(data.direction);
+ }
+ if (data.hasOwnProperty('rotationStyle')) {
+ this.setRotationStyle(data.rotationStyle);
+ }
+ if (data.hasOwnProperty('visible')) {
+ this.setVisible(data.visible);
+ }
+};
+
+/**
+ * Dispose, destroying any run-time properties.
+ */
+RenderedTarget.prototype.dispose = function () {
this.runtime.changeCloneCounter(-1);
if (this.renderer && this.drawableID !== null) {
this.renderer.destroyDrawable(this.drawableID);
@@ -608,4 +649,4 @@ Clone.prototype.dispose = function () {
}
};
-module.exports = Clone;
+module.exports = RenderedTarget;
diff --git a/src/sprites/sprite.js b/src/sprites/sprite.js
index 351d40a3..0f189206 100644
--- a/src/sprites/sprite.js
+++ b/src/sprites/sprite.js
@@ -1,4 +1,4 @@
-var Clone = require('./clone');
+var RenderedTarget = require('./rendered-target');
var Blocks = require('../engine/blocks');
/**
@@ -35,7 +35,7 @@ var Sprite = function (blocks, runtime) {
this.costumes = [];
/**
* List of clones for this sprite, including the original.
- * @type {Array.}
+ * @type {Array.}
*/
this.clones = [];
};
@@ -45,7 +45,7 @@ var Sprite = function (blocks, runtime) {
* @returns {!Clone} Newly created clone.
*/
Sprite.prototype.createClone = function () {
- var newClone = new Clone(this, this.runtime);
+ var newClone = new RenderedTarget(this, this.runtime);
newClone.isOriginal = this.clones.length === 0;
this.clones.push(newClone);
if (newClone.isOriginal) {
diff --git a/test/unit/import_sb2.js b/test/unit/import_sb2.js
index be4d6286..5072b208 100644
--- a/test/unit/import_sb2.js
+++ b/test/unit/import_sb2.js
@@ -2,7 +2,7 @@ var fs = require('fs');
var path = require('path');
var test = require('tap').test;
-var clone = require('../../src/sprites/clone');
+var renderedTarget = require('../../src/sprites/rendered-target');
var runtime = require('../../src/engine/runtime');
var sb2 = require('../../src/import/sb2import');
@@ -25,7 +25,7 @@ test('default', function (t) {
t.type(rt, 'object');
t.type(rt.targets, 'object');
- t.ok(rt.targets[0] instanceof clone);
+ t.ok(rt.targets[0] instanceof renderedTarget);
t.type(rt.targets[0].id, 'string');
t.type(rt.targets[0].blocks, 'object');
t.type(rt.targets[0].variables, 'object');
@@ -36,7 +36,7 @@ test('default', function (t) {
t.equal(rt.targets[0].isOriginal, true);
t.equal(rt.targets[0].isStage, true);
- t.ok(rt.targets[1] instanceof clone);
+ t.ok(rt.targets[1] instanceof renderedTarget);
t.type(rt.targets[1].id, 'string');
t.type(rt.targets[1].blocks, 'object');
t.type(rt.targets[1].variables, 'object');
@@ -63,7 +63,7 @@ test('demo', function (t) {
t.type(rt, 'object');
t.type(rt.targets, 'object');
- t.ok(rt.targets[0] instanceof clone);
+ t.ok(rt.targets[0] instanceof renderedTarget);
t.type(rt.targets[0].id, 'string');
t.type(rt.targets[0].blocks, 'object');
t.type(rt.targets[0].variables, 'object');
@@ -74,7 +74,7 @@ test('demo', function (t) {
t.equal(rt.targets[0].isOriginal, true);
t.equal(rt.targets[0].isStage, true);
- t.ok(rt.targets[1] instanceof clone);
+ t.ok(rt.targets[1] instanceof renderedTarget);
t.type(rt.targets[1].id, 'string');
t.type(rt.targets[1].blocks, 'object');
t.type(rt.targets[1].variables, 'object');
diff --git a/test/unit/sprites_clone.js b/test/unit/sprites_rendered-target.js
similarity index 67%
rename from test/unit/sprites_clone.js
rename to test/unit/sprites_rendered-target.js
index 246e1b95..58c61bdf 100644
--- a/test/unit/sprites_clone.js
+++ b/test/unit/sprites_rendered-target.js
@@ -1,13 +1,13 @@
var test = require('tap').test;
-var Clone = require('../../src/sprites/clone');
+var RenderedTarget = require('../../src/sprites/rendered-target');
var Sprite = require('../../src/sprites/sprite');
test('clone effects', function (t) {
// Create two clones and ensure they have different graphic effect objects.
// Regression test for Github issue #224
var spr = new Sprite();
- var a = new Clone(spr, null);
- var b = new Clone(spr, null);
+ var a = new RenderedTarget(spr, null);
+ var b = new RenderedTarget(spr, null);
t.ok(a.effects !== b.effects);
t.end();
});