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). [![Build Status](https://travis-ci.org/LLK/scratch-vm.svg?branch=develop)](https://travis-ci.org/LLK/scratch-vm) -[![Coverage Status](https://coveralls.io/repos/github/LLK/scratch-vm/badge.svg?branch=develop&20161025)](https://coveralls.io/github/LLK/scratch-vm?branch=develop) +[![Coverage Status](https://coveralls.io/repos/github/LLK/scratch-vm/badge.svg?branch=develop)](https://coveralls.io/github/LLK/scratch-vm?branch=develop) [![Dependency Status](https://david-dm.org/LLK/scratch-vm.svg)](https://david-dm.org/LLK/scratch-vm) [![devDependency Status](https://david-dm.org/LLK/scratch-vm/dev-status.svg)](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(); });