@awayjs/scene
Version:
AwayJS scene classes
734 lines (731 loc) • 28.4 kB
JavaScript
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 };