UNPKG

@awayjs/scene

Version:
734 lines (731 loc) 28.4 kB
import { __extends } from "tslib"; import { BaseAudioChannel } from '@awayjs/core'; import { MouseEvent } from '../events/MouseEvent'; import { FrameScriptManager } from '../managers/FrameScriptManager'; import { Sprite } from './Sprite'; var MovieClip = /** @class */ (function (_super) { __extends(MovieClip, _super); function MovieClip(timeline, spriteMode) { if (spriteMode === void 0) { spriteMode = false; } var _this = _super.call(this) || this; _this.symbolID = 0; _this.preventScript = false; _this._scenes = []; // buttonMode specifies if the mc has any mouse-listeners attached that should trigger showing the hand-cursor // if this is set once to true; it will never get set back to false again. _this._buttonMode = false; // isButton specifies if the mc-timeline is actually considered a button-timeline _this._isButton = false; _this._time = 0; // the current time inside the animation _this._currentFrameIndex = -1; // the current frame _this._isPlaying = true; // false if paused or stopped _this._currentSceneIndex = 0; _this._sceneDirty = false; _this._isInit = true; _this._sessionID_childs = {}; _this._sounds = {}; _this._skipFramesForStream = 0; /** * Mark that operation provided by timeline except a script, some operation not allowed from script * `removeChild` should not remove a child name * @private */ _this._isTimelinePass = false; /** * Some symbols can be a sprite. This means that when we spawn it, we should track only first frame. * @private */ _this._isSprite = false; _this.buttonEnabled = true; /** * */ _this.loop = true; /** * the current index of the current active frame */ _this.constructedKeyFrameIndex = -1; _this._soundVolume = 1; _this._isButton = false; _this._buttonMode = false; _this._useHandCursor = true; _this.doingSwap = false; _this.cursorType = 'pointer'; //this.debugVisible=true; _this._onMouseOver = function (_event) { if (_this.buttonEnabled) _this.currentFrameIndex = (_event.buttons & 1 /* MouseButtons.PRIMARY_BUTTON */) ? _this.currentFrameIndex == 0 ? 0 : 2 : 1; else _this.currentFrameIndex = 0; }; _this._onMouseOut = function (_event) { _this.currentFrameIndex = (_event.buttons & 1 /* MouseButtons.PRIMARY_BUTTON */ && _this.currentFrameIndex != 0) ? 1 : 0; }; _this._onMouseDown = function (_event) { if (_this.buttonEnabled) _this.currentFrameIndex = 2; else _this.currentFrameIndex = 0; }; _this._onMouseUp = function (_event) { _this.currentFrameIndex = 1; }; _this._onMouseUpOutside = function (_event) { _this.currentFrameIndex = 0; }; _this._timeline = timeline; if (spriteMode) { _this.transformToSprite(); } _this._onChannelCompleteStopError = _this._onChannelCompleteStopError.bind(_this); return _this; } MovieClip.stopSounds = function (sound) { if (sound) { if (MovieClip._activeSounds[sound.id]) MovieClip._activeSounds[sound.id].stop(); } else { for (var key in MovieClip._activeSounds) MovieClip._activeSounds[key].stop(); } }; MovieClip.getNewMovieClip = function (timeline) { if (MovieClip._movieClips.length) { var movieClip = MovieClip._movieClips.pop(); movieClip.timeline = timeline; return movieClip; } return new MovieClip(timeline); }; MovieClip.clearPool = function () { MovieClip._movieClips = []; }; Object.defineProperty(MovieClip.prototype, "isSprite", { get: function () { return this._isSprite; }, enumerable: false, configurable: true }); MovieClip.prototype.initSoundStream = function (streamInfo, maxFrameNum) { if (!this._soundStreams) { this._soundStreams = new MovieClip.movieClipSoundsManagerClass(this); } this._soundStreams.initSoundStream(streamInfo, maxFrameNum); }; MovieClip.prototype.addSoundStreamBlock = function (frameNum, streamBlock) { if (!this._soundStreams) { this._soundStreams = new MovieClip.movieClipSoundsManagerClass(this); } this._soundStreams.addSoundStreamBlock(frameNum, streamBlock); }; MovieClip.prototype.stopCurrentStream = function (frameNum) { if (this._soundStreams) { //console.log("sync sounds for mc: ", this.numFrames); return this._soundStreams.stopStream(frameNum); } }; MovieClip.prototype.resetStreamStopped = function () { if (this._soundStreams) { //console.log("sync sounds for mc: ", this.numFrames); this._soundStreams.resetStreamStopped(); } }; MovieClip.prototype._syncSounds = function (frameNum) { if (this._soundStreams) { //console.log("sync sounds for mc: ", this.numFrames); return this._soundStreams.syncSounds(frameNum, this._isPlaying, this.parent); } return 0; }; /** * Reduce frames of timeline to sprite mode. * Used for UIComponents, where timeline store more that 1 frame */ MovieClip.prototype.transformToSprite = function () { if (this._isSprite) { return; } this._isSprite = true; var timeline = this._timeline; // regular MC or frames already is not more 1 if (timeline.numFrames <= 1) { return; } timeline.frame_command_indices = [timeline.frame_command_indices[0]]; timeline.frame_recipe = [timeline.frame_recipe[0]]; timeline.keyframe_constructframes = [timeline.keyframe_constructframes[0]]; timeline.keyframe_durations = [timeline.keyframe_durations[0]]; timeline.keyframe_firstframes = [timeline.keyframe_firstframes[0]]; timeline.keyframe_indices = [timeline.keyframe_indices[0]]; }; MovieClip.prototype.startSound = function (sound, loopsToPlay, onSoundComplete) { var channel = sound.play(0, loopsToPlay); channel.volume = this._soundVolume; if (onSoundComplete) channel.addEventListener(BaseAudioChannel.COMPLETE, onSoundComplete); //internal listener to clear channel.addEventListener(BaseAudioChannel.COMPLETE, this._onChannelCompleteStopError); channel.addEventListener(BaseAudioChannel.STOP, this._onChannelCompleteStopError); channel.addEventListener(BaseAudioChannel.ERROR, this._onChannelCompleteStopError); var id = sound.id; if (!this._sounds[id]) this._sounds[id] = []; // store channels, stop it instead of sounds this._sounds[id].push(channel); //store active sound MovieClip._activeSounds[id] = sound; }; MovieClip.prototype.stopSounds = function (sound) { if (sound === void 0) { sound = null; } if (sound) { if (this._sounds[sound.id]) this._stopChannels(sound); } else { for (var key in this._sounds) for (var _i = 0, _a = this._sounds[key]; _i < _a.length; _i++) { var c = _a[_i]; c.stop(); } } var len = this._children.length; var child; for (var i = 0; i < len; ++i) { child = this._children[i]; if (child.isAsset(MovieClip)) child.stopSounds(sound); } this.stopCurrentStream(this._currentFrameIndex); if (this._soundStreams) this._soundStreams.syncSounds(0, false, this.parent); }; /** * Compute scene index by global frame index * @param frameIndex * @private */ MovieClip.prototype.getSceneIndexByFrame = function (frameIndex) { var scenes = this.scenes; if (scenes.length <= 1) { return 0; } for (var i = 0; i < scenes.length; i++) { if (scenes[i].offset > frameIndex) { return i - 1; } } return scenes.length - 1; }; MovieClip.prototype.getSceneIndex = function (scene) { var scenes = this.scenes; for (var i = 0; i < scenes.length && scene; i++) { if (scenes[i].name === scene) { return i; } } return 0; }; Object.defineProperty(MovieClip.prototype, "currentSceneName", { get: function () { return this.scenes[this._currentSceneIndex].name; }, set: function (scene) { var index = this.getSceneIndex(scene); this._sceneDirty = this._currentSceneIndex !== index; this._currentSceneIndex = index; this._currentFrameIndex = this._scenes[index].offset; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "currentScene", { get: function () { var currentScene = this.scenes[this._currentSceneIndex]; if (currentScene.numFrames === -1) { currentScene.numFrames = this.timeline.numFrames - currentScene.offset; } return currentScene; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "scenes", { get: function () { if (this._scenes.length === 0) { this._scenes[0] = { name: 'Scene1', offset: 0, labels: [], numFrames: this.timeline.numFrames, }; } return this._scenes; }, set: function (value) { this._scenes = value; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "isPlaying", { get: function () { return this._isPlaying; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "soundVolume", { get: function () { return this._soundVolume; }, set: function (value) { if (this._soundVolume == value) return; this._soundVolume = value; var channels; for (var key in this._sounds) if ((channels = this._sounds[key])) for (var _i = 0, channels_1 = channels; _i < channels_1.length; _i++) { var c = channels_1[_i]; c.volume = value; } }, enumerable: false, configurable: true }); MovieClip.prototype.stopSound = function (sound) { MovieClip.stopSounds(sound); }; MovieClip.prototype.buttonReset = function () { if (this._isButton && !this.buttonEnabled) { this.currentFrameIndex = 0; } }; MovieClip.prototype.getMouseCursor = function () { if (this.name == 'scene') return 'initial'; if (this._useHandCursor && this.buttonMode) { return this.cursorType; } return 'initial'; /* var cursorName:string; var parent:DisplayObject=this.parent; while(parent){ if(parent.isAsset(MovieClip)){ cursorName=(<MovieClip>parent).getMouseCursor(); if(cursorName!="initial"){ return cursorName; } } parent=parent.parent; if(parent && parent.name=="scene"){ return "initial"; } } return "initial"; */ }; MovieClip.prototype.queueFrameScripts = function (timeline, frame_idx, scriptPass1) { console.warn('[MovieClip] - queueFrameScripts should only be called on AVM-Adapters'); }; MovieClip.prototype.registerScriptObject = function (child) { this[child.name] = child; if (child.isAsset(MovieClip)) child.removeButtonListeners(); }; MovieClip.prototype.unregisterScriptObject = function (child) { delete this[child.name]; if (child.isAsset(MovieClip)) child.removeButtonListeners(); }; MovieClip.prototype.dispose = function () { this.disposeValues(); MovieClip._movieClips.push(this); }; MovieClip.prototype.disposeValues = function () { _super.prototype.disposeValues.call(this); this._sessionID_childs = {}; this._timeline = null; }; Object.defineProperty(MovieClip.prototype, "useHandCursor", { get: function () { return this._useHandCursor; }, set: function (value) { this._useHandCursor = value; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "buttonMode", { get: function () { return this._buttonMode; }, set: function (value) { this._buttonMode = value; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "isButton", { get: function () { return this._isButton; }, set: function (value) { this._isButton = value; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "isInit", { get: function () { return this._isInit; }, set: function (value) { this._isInit = value; }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "timeline", { get: function () { return this._timeline; }, set: function (value) { if (this._timeline == value) return; this._timeline = value; this.reset(false, false); }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "numFrames", { get: function () { return this._timeline.numFrames; }, enumerable: false, configurable: true }); MovieClip.prototype.jumpToLabel = function (label, offset) { if (offset === void 0) { offset = 0; } // the timeline.jumpTolabel will set currentFrameIndex var index = this._currentFrameIndex; this._timeline.jumpToLabel(this, label, offset); return index !== this._currentFrameIndex; }; MovieClip.prototype.reset = function (fireScripts, resetSelf) { if (fireScripts === void 0) { fireScripts = true; } if (resetSelf === void 0) { resetSelf = true; } this._isTimelinePass = true; if (resetSelf) _super.prototype.reset.call(this); this.resetStreamStopped(); // time only is relevant for the root mc, as it is the only one that executes the update function this._time = 0; //this.stopSounds(); if (resetSelf && this._adapter) this.adapter.freeFromScript(); this.constructedKeyFrameIndex = -1; for (var i = this.numChildren - 1; i >= 0; i--) this.removeChildAt(i); if (this._graphics) this._graphics.clear(); if (fireScripts) { var numFrames = this._timeline.keyframe_indices.length; this._isPlaying = Boolean(numFrames > 1); this._currentSceneIndex = 0; if (numFrames) { this._currentFrameIndex = 0; // contruct the timeline and queue the script. //if(fireScripts){ this._timeline.constructNextFrame(this, fireScripts && !this.doingSwap && !this.preventScript, true); //} } else { this._currentFrameIndex = -1; } } // prevents the playhead to get moved in the advance frame again: this._skipAdvance = true; this._isTimelinePass = false; }; Object.defineProperty(MovieClip.prototype, "currentFrameIndex", { get: function () { return this._currentFrameIndex; }, set: function (value) { this.jumpToIndex(value); }, enumerable: false, configurable: true }); Object.defineProperty(MovieClip.prototype, "currentFrameIndexRelative", { /* * Setting the currentFrameIndex will move the playhead for this movieclip to the new position */ get: function () { return this._currentFrameIndex - this.currentScene.offset; }, set: function (value) { this.jumpToIndex(value, this._currentSceneIndex); }, enumerable: false, configurable: true }); MovieClip.prototype.jumpToIndex = function (value, sceneIndex) { var queue_script = true; var offset = 0; var numFrames = this._timeline.keyframe_indices.length; this.resetStreamStopped(); if (!numFrames) return false; var scenes = this.scenes; // scene not presented - global navigation // we should compute scene index and shift frame if (typeof sceneIndex === 'undefined') { sceneIndex = this.getSceneIndexByFrame(value); } else { sceneIndex = typeof sceneIndex === 'string' ? this.getSceneIndex(sceneIndex) : sceneIndex; var scene = scenes[sceneIndex]; // fix negative frames size on latest scene. // this is because we don't know how many frames in instance time if (scene.numFrames === -1) { scene.numFrames = numFrames - scene.offset; } offset = scene.offset; numFrames = scene.numFrames; } this._currentSceneIndex = sceneIndex; if (value < 0) { value = 0; } else if (value >= numFrames) { // if value is greater than the available number of // frames, the playhead is moved to the last frame in the timeline. // In this case the frame specified is not considered a keyframe, // no scripts should be executed in this case value = numFrames - 1; queue_script = false; } value += offset; this._skipAdvance = false; if (this._currentFrameIndex === value && !this._sceneDirty) return false; this._sceneDirty = false; this._currentFrameIndex = value; //console.log("_currentFrameIndex ", this.name, this._currentFrameIndex); //changing current frame will ignore advance command for that //update's advanceFrame function, unless advanceFrame has //already been executed this._isTimelinePass = true; this._timeline.gotoFrame(this, value, queue_script, false, false); this._isTimelinePass = false; return true; }; MovieClip.prototype.addButtonListeners = function () { this._isButton = true; this.stop(); this.addEventListener(MouseEvent.MOUSE_OVER, this._onMouseOver); this.addEventListener(MouseEvent.MOUSE_OUT, this._onMouseOut); this.addEventListener(MouseEvent.MOUSE_DOWN, this._onMouseDown); this.addEventListener(MouseEvent.MOUSE_UP, this._onMouseUp); this.addEventListener(MouseEvent.MOUSE_UP_OUTSIDE, this._onMouseUpOutside); this.mouseChildren = false; }; MovieClip.prototype.removeButtonListeners = function () { this.removeEventListener(MouseEvent.MOUSE_OVER, this._onMouseOver); this.removeEventListener(MouseEvent.MOUSE_OUT, this._onMouseOut); this.removeEventListener(MouseEvent.MOUSE_DOWN, this._onMouseDown); this.removeEventListener(MouseEvent.MOUSE_UP, this._onMouseUp); this.removeEventListener(MouseEvent.MOUSE_UP_OUTSIDE, this._onMouseUpOutside); }; MovieClip.prototype.getTimelineChildAtSessionID = function (sessionID) { return this._sessionID_childs[sessionID]; }; // should only be called from timeline when navigating frames MovieClip.prototype.constructFrame = function (timeline, start_construct_idx, target_keyframe_idx, jump_forward, frame_idx, queue_pass2, queue_script) { console.warn('[scene/MovieClip] - constructFrame not implemented'); }; MovieClip.prototype.addTimelineChildAtDepth = function (child, depth, sessionID) { console.warn('[scene/MovieClip] - addTimelineChildAtDepth not implemented'); return null; }; MovieClip.prototype.removeTimelineChildAtDepth = function (depth) { console.warn('[scene/MovieClip] - removeTimelineChildAtDepth not implemented'); }; MovieClip.prototype.removeChildAtInternal = function (index) { var child = this._children[index]; if (child._adapter) child.adapter.freeFromScript(); // only timeline can do this if (this._isTimelinePass) { this.adapter.unregisterScriptObject(child); } delete this._sessionID_childs[child._sessionID]; child._sessionID = -1; return _super.prototype.removeChildAtInternal.call(this, index); }; Object.defineProperty(MovieClip.prototype, "assetType", { get: function () { return MovieClip.assetType; }, enumerable: false, configurable: true }); /** * Starts playback of animation from current position */ MovieClip.prototype.play = function () { if (this._timeline.keyframe_indices.length > 1) this._isPlaying = true; }; /** * Stop playback of animation and hold current position */ MovieClip.prototype.stop = function () { //this.stopSounds(); this.resetStreamStopped(); this._isPlaying = false; }; MovieClip.prototype.clone = function () { var newInstance = MovieClip.getNewMovieClip(this._timeline); this.copyTo(newInstance); return newInstance; }; MovieClip.prototype.copyTo = function (movieClip) { _super.prototype.copyTo.call(this, movieClip); movieClip.buttonMode = this.buttonMode; movieClip.symbolID = this.symbolID; movieClip.loop = this.loop; movieClip._soundStreams = this._soundStreams; movieClip._scenes = this._scenes; // move to sprite mode too if (this._isSprite) { movieClip.transformToSprite(); } }; MovieClip.prototype.advanceFrameInternal = function () { // if this._skipadvance is true, the mc has already been moving on its timeline this frame // this happens for objects that have been newly added to parent // they still need to queue their scripts //if(this._timeline && this._timeline.numFrames>0) if (this._timeline && this._timeline.numFrames > 0 && this._isPlaying && !this._skipAdvance) { if (this._currentFrameIndex === this._timeline.numFrames - 1) { if (this.loop) { if (this._currentFrameIndex !== 0) { this._currentFrameIndex = 0; this._currentSceneIndex = 0; this.resetStreamStopped(); this._timeline.gotoFrame(this, 0, true, true, true); } // end of loop - jump to first frame. } else //end of timeline, stop playing this._isPlaying = false; } else { // not end - construct next frame this._currentFrameIndex++; this._currentSceneIndex = this.getSceneIndexByFrame(this._currentFrameIndex); this._timeline.constructNextFrame(this); } //console.log("advancedFrame ", this.name, this._currentFrameIndex); } _super.prototype.advanceFrame.call(this); this._skipAdvance = false; }; MovieClip.prototype.advanceFrame = function () { this._isTimelinePass = true; if (this._skipFramesForStream == 0) { this.advanceFrameInternal(); } /*if(this._skipFramesForStream<0){ console.log("wait for audio to catch up"); }*/ this._skipFramesForStream = this._syncSounds(this._currentFrameIndex); while (this._skipFramesForStream > 0) { //console.log("skip frame for keeping audio stream synced"); FrameScriptManager.execute_queue(); this.advanceFrameInternal(); this._skipFramesForStream = this._syncSounds(this._currentFrameIndex); } this._isTimelinePass = false; }; // DEBUG CODE: MovieClip.prototype.logHierarchy = function (depth) { if (depth === void 0) { depth = 0; } this.printHierarchyName(depth, this); var len = this._children.length; var child; for (var i = 0; i < len; i++) { child = this._children[i]; if (child.isAsset(MovieClip)) child.logHierarchy(depth + 1); else this.printHierarchyName(depth + 1, child); } }; MovieClip.prototype.printHierarchyName = function (depth, target) { var str = ''; for (var i = 0; i < depth; ++i) str += '--'; str += ' ' + target.name + ' = ' + target.id; console.log(str); }; MovieClip.prototype.clear = function () { //clear out potential instances this.resetStreamStopped(); /* check memory disposal with new approach of child-instancing for (var key in this._potentialInstances) { var instance: IAsset = this._potentialInstances[key]; //only dispose instances that are not used in script ie. do not have an instance name if (instance && !instance.name) { if (!instance.isAsset(Sprite)) { FrameScriptManager.add_child_to_dispose(<DisplayObject>instance); } delete this._potentialInstances[key]; } } */ _super.prototype.clear.call(this); }; MovieClip.prototype._onChannelCompleteStopError = function (event) { var channel = event.target; var sound = channel.owner; var channels = this._sounds[sound.id]; var index = channels ? channels.indexOf(channel) : -1; channel.removeEventListener(BaseAudioChannel.COMPLETE, this._onChannelCompleteStopError); channel.removeEventListener(BaseAudioChannel.STOP, this._onChannelCompleteStopError); channel.removeEventListener(BaseAudioChannel.ERROR, this._onChannelCompleteStopError); if (index != -1) { channels.splice(index, 1); if (channels.length === 0) this._removeSound(sound); } }; MovieClip.prototype._stopChannels = function (sound) { var id = sound.id; var channels = this._sounds[id]; for (var _i = 0, channels_2 = channels; _i < channels_2.length; _i++) { var c = channels_2[_i]; c.stop(); } }; MovieClip.prototype._removeSound = function (sound) { //remove sound from local sounds delete this._sounds[sound.id]; //remove sound from active sounds if (!sound.isPlaying) delete MovieClip._activeSounds[sound.id]; }; MovieClip.movieClipSoundsManagerClass = null; MovieClip._movieClips = []; MovieClip._activeSounds = {}; MovieClip.assetType = '[asset MovieClip]'; return MovieClip; }(Sprite)); export { MovieClip };