diff --git a/src/engine/runtime.js b/src/engine/runtime.js index d634dbc83..ad295dfec 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -112,6 +112,12 @@ class Runtime extends EventEmitter { */ this.targets = []; + /** + * Targets in reverse order of execution. Shares its order with drawables. + * @type {Array.<!Target>} + */ + this.executableTargets = []; + /** * A list of threads that are currently running in the VM. * Threads are added when execution starts and pruned when execution ends. @@ -1245,7 +1251,7 @@ class Runtime extends EventEmitter { * @param {Target=} optTarget Optionally, a target to restrict to. */ allScriptsDo (f, optTarget) { - let targets = this.targets; + let targets = this.executableTargets; if (optTarget) { targets = [optTarget]; } @@ -1362,6 +1368,68 @@ class Runtime extends EventEmitter { // @todo clear out extensions? turboMode? etc. } + /** + * Add a target to the execution order. + * @param {Target} executableTarget target to add + */ + addExecutable (executableTarget) { + this.executableTargets.push(executableTarget); + } + + /** + * Move a target in the execution order by a relative amount. + * + * A positve number will make the target execute earlier. A negative number + * will make the target execute later in the order. + * + * @param {Target} executableTarget target to move + * @param {number} delta number of positions to move target by + * @returns {number} new position in execution order + */ + moveExecutable (executableTarget, delta) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + this.executableTargets.splice(oldIndex, 1); + let newIndex = oldIndex + delta; + if (newIndex > this.executableTargets.length) { + newIndex = this.executableTargets.length; + } + if (newIndex <= 0) { + if (this.executableTargets.length > 0 && this.executableTargets[0].isStage) { + newIndex = 1; + } else { + newIndex = 0; + } + } + this.executableTargets.splice(newIndex, 0, executableTarget); + return newIndex; + } + + /** + * Set a target to execute at a specific position in the execution order. + * + * Infinity will set the target to execute first. 0 will set the target to + * execute last (before the stage). + * + * @param {Target} executableTarget target to move + * @param {number} newIndex position in execution order to place the target + * @returns {number} new position in the execution order + */ + setExecutablePosition (executableTarget, newIndex) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + return this.moveExecutable(executableTarget, newIndex - oldIndex); + } + + /** + * Remove a target from the execution set. + * @param {Target} executableTarget target to remove + */ + removeExecutable (executableTarget) { + const oldIndex = this.executableTargets.indexOf(executableTarget); + if (oldIndex > -1) { + this.executableTargets.splice(oldIndex, 1); + } + } + /** * Dispose of a target. * @param {!Target} disposingTarget Target to dispose of. diff --git a/src/engine/target.js b/src/engine/target.js index a5e344df5..a25d77da1 100644 --- a/src/engine/target.js +++ b/src/engine/target.js @@ -62,6 +62,10 @@ class Target extends EventEmitter { * @type {Object.<string,*>} */ this._customState = {}; + + if (this.runtime) { + this.runtime.addExecutable(this); + } } /** @@ -394,6 +398,10 @@ class Target extends EventEmitter { */ dispose () { this._customState = {}; + + if (this.runtime) { + this.runtime.removeExecutable(this); + } } // Variable Conflict Resolution Helpers diff --git a/src/sprites/rendered-target.js b/src/sprites/rendered-target.js index ebfe80a6d..88588eae0 100644 --- a/src/sprites/rendered-target.js +++ b/src/sprites/rendered-target.js @@ -943,6 +943,9 @@ class RenderedTarget extends Target { other.drawableID, 0, StageLayering.SPRITE_LAYER, true); this.renderer.setDrawableOrder(this.drawableID, otherLayer, StageLayering.SPRITE_LAYER); } + + const executionPosition = this.runtime.executableTargets.indexOf(other); + this.runtime.setExecutablePosition(this, executionPosition); } /** @@ -1142,6 +1145,7 @@ class RenderedTarget extends Target { dispose () { this.runtime.changeCloneCounter(-1); this.runtime.stopForTarget(this); + this.runtime.removeExecutable(this); this.sprite.removeClone(this); if (this.renderer && this.drawableID !== null) { this.renderer.destroyDrawable(this.drawableID, this.isStage ? diff --git a/test/fixtures/execute/order-clones-2.sb2 b/test/fixtures/execute/order-clones-2.sb2 new file mode 100644 index 000000000..2c0d9f05c Binary files /dev/null and b/test/fixtures/execute/order-clones-2.sb2 differ diff --git a/test/fixtures/execute/order-library-reverse.sb2 b/test/fixtures/execute/order-library-reverse.sb2 new file mode 100644 index 000000000..a48b8606b Binary files /dev/null and b/test/fixtures/execute/order-library-reverse.sb2 differ diff --git a/test/fixtures/execute/order-library-reverse.sb3 b/test/fixtures/execute/order-library-reverse.sb3 new file mode 100644 index 000000000..f818a47fe Binary files /dev/null and b/test/fixtures/execute/order-library-reverse.sb3 differ diff --git a/test/fixtures/execute/order-library.sb2 b/test/fixtures/execute/order-library.sb2 new file mode 100644 index 000000000..b8d340800 Binary files /dev/null and b/test/fixtures/execute/order-library.sb2 differ diff --git a/test/fixtures/execute/order-library.sb3 b/test/fixtures/execute/order-library.sb3 new file mode 100644 index 000000000..f3c6dbc46 Binary files /dev/null and b/test/fixtures/execute/order-library.sb3 differ