From bf44720d7164ba5dffb886650089568b8c7f1cc8 Mon Sep 17 00:00:00 2001 From: Joalor64 Date: Tue, 14 Nov 2023 07:32:55 -0500 Subject: [PATCH] umm flixel stuff --- .../animation/PsychAnimationController.hx | 18 + .../animation/FlxAnimationController.hx | 966 ------------------ source/flixel/sound/FlxSound.hx | 808 +++++++++++++++ source/flixel/system/FlxSound.hx | 798 +-------------- source/flixel/system/FlxSoundGroup.hx | 87 -- source/meta/state/PlayState.hx | 6 +- source/objects/Character.hx | 3 + source/objects/userinterface/note/Note.hx | 4 + .../objects/userinterface/note/NoteSplash.hx | 4 + .../objects/userinterface/note/StrumNote.hx | 4 + 10 files changed, 844 insertions(+), 1854 deletions(-) create mode 100644 source/backend/animation/PsychAnimationController.hx delete mode 100644 source/flixel/animation/FlxAnimationController.hx create mode 100644 source/flixel/sound/FlxSound.hx delete mode 100644 source/flixel/system/FlxSoundGroup.hx diff --git a/source/backend/animation/PsychAnimationController.hx b/source/backend/animation/PsychAnimationController.hx new file mode 100644 index 00000000..f8b3dd90 --- /dev/null +++ b/source/backend/animation/PsychAnimationController.hx @@ -0,0 +1,18 @@ +package backend.animation; + +import flixel.animation.FlxAnimationController; + +class PsychAnimationController extends FlxAnimationController +{ + public var followGlobalSpeed:Bool = true; + + public override function update(elapsed:Float):Void { + if (_curAnim != null) { + var speed:Float = timeScale; + if (followGlobalSpeed) speed *= FlxG.animationTimeScale; + _curAnim.update(elapsed * speed); + } else if (_prerotated != null) { + _prerotated.angle = _sprite.angle; + } + } +} \ No newline at end of file diff --git a/source/flixel/animation/FlxAnimationController.hx b/source/flixel/animation/FlxAnimationController.hx deleted file mode 100644 index ef900884..00000000 --- a/source/flixel/animation/FlxAnimationController.hx +++ /dev/null @@ -1,966 +0,0 @@ -package flixel.animation; - -import flixel.FlxG; -import flixel.FlxSprite; -import flixel.graphics.frames.FlxFrame; -import flixel.util.FlxDestroyUtil.IFlxDestroyable; - -class FlxAnimationController implements IFlxDestroyable -{ - /** - * Property access for currently playing animation (warning: can be `null`). - */ - public var curAnim(get, set):FlxAnimation; - - /** - * The frame index of the current animation. Can be changed manually. - */ - public var frameIndex(default, set):Int = -1; - - /** - * Tell the sprite to change to a frame with specific name. - * Useful for sprites with loaded TexturePacker atlas. - */ - public var frameName(get, set):String; - - /** - * Gets or sets the currently playing animation (warning: can be `null`). - */ - public var name(get, set):String; - - /** - * Pause or resume the current animation. - */ - public var paused(get, set):Bool; - - /** - * Whether the current animation has finished playing. - */ - public var finished(get, set):Bool; - - /** - * The total number of frames in this image. - * WARNING: assumes each row in the sprite sheet is full! - */ - public var numFrames(get, never):Int; - - /** - * The total number of frames in this image. - * WARNING: assumes each row in the sprite sheet is full! - */ - @:deprecated("frames is deprecated, use numFrames") - public var frames(get, never):Int; - - /** - * If assigned, will be called each time the current animation's frame changes. - * A function that has 3 parameters: a string name, a frame number, and a frame index. - */ - #if haxe4 - public var callback:(name:String, frameNumber:Int, frameIndex:Int) -> Void; - #else - public var callback:String->Int->Int->Void; - #end - - /** - * If assigned, will be called each time the current animation finishes. - * A function that has 1 parameter: a string name - animation name. - */ - #if haxe4 - public var finishCallback:(name:String) -> Void; - #else - public var finishCallback:String->Void; - #end - - /** - * Internal, reference to owner sprite. - */ - var _sprite:FlxSprite; - - /** - * Internal, currently playing animation. - */ - @:allow(flixel.animation) - var _curAnim:FlxAnimation; - - /** - * Internal, stores all the animation that were added to this sprite. - */ - var _animations(default, null) = new Map(); - - var _prerotated:FlxPrerotatedAnimation; - - public function new(sprite:FlxSprite) - { - _sprite = sprite; - } - - public static var globalSpeed:Float = 1; - - public var followGlobalSpeed:Bool = true; - - public function update(elapsed:Float):Void - { - if (_curAnim != null) - { - var e:Float = elapsed; - if (followGlobalSpeed) - e *= globalSpeed; - - _curAnim.update(e); - } - else if (_prerotated != null) - { - _prerotated.angle = _sprite.angle; - } - } - - public function copyFrom(controller:FlxAnimationController):FlxAnimationController - { - destroyAnimations(); - - for (anim in controller._animations) - { - add(anim.name, anim.frames, anim.frameRate, anim.looped, anim.flipX, anim.flipY); - } - - if (controller._prerotated != null) - { - createPrerotated(); - } - - if (controller.name != null) - { - name = controller.name; - } - - frameIndex = controller.frameIndex; - - return this; - } - - public function createPrerotated(?Controller:FlxAnimationController):Void - { - destroyAnimations(); - Controller = (Controller != null) ? Controller : this; - _prerotated = new FlxPrerotatedAnimation(Controller, Controller._sprite.bakedRotationAngle); - _prerotated.angle = _sprite.angle; - } - - public function destroyAnimations():Void - { - clearAnimations(); - clearPrerotated(); - } - - public function destroy():Void - { - destroyAnimations(); - _animations = null; - callback = null; - _sprite = null; - } - - #if (flixel >= "5.3.0") - @:allow(flixel.animation.FlxAnimation) - function getFrameDuration(index:Int) - { - return _sprite.frames.frames[index].duration; - } - #end - - function clearPrerotated():Void - { - if (_prerotated != null) - { - _prerotated.destroy(); - } - _prerotated = null; - } - - function clearAnimations():Void - { - if (_animations != null) - { - var anim:FlxAnimation; - for (key in _animations.keys()) - { - anim = _animations.get(key); - if (anim != null) - { - anim.destroy(); - } - } - } - - _animations = new Map(); - _curAnim = null; - } - - /** - * Adds a new animation to the sprite. - * - * @param name What this animation should be called (e.g. `"run"`). - * @param frames An array of indices indicating what frames to play in what order (e.g. `[0, 1, 2]`). - * @param frameRate The speed in frames per second that the animation should play at (e.g. `40` fps). - * @param looped Whether or not the animation is looped or just plays once. - * @param flipX Whether the frames should be flipped horizontally. - * @param flipY Whether the frames should be flipped vertically. - */ - public function add(name:String, frames:Array, frameRate = 30.0, looped = true, flipX = false, flipY = false):Void - { - if (numFrames == 0) - { - FlxG.log.warn('Could not create animation: "$name", this sprite has no frames'); - return; - } - - // Check _animations frames - var framesToAdd:Array = frames; - var hasInvalidFrames = false; - var i = framesToAdd.length; - while (i-- >= 0) - { - final frame = framesToAdd[i]; - if (frame >= numFrames) - { - // log if frames are excluded - hasInvalidFrames = true; - - // Splicing original Frames array could lead to unexpected results - // So we are cloning it (only once) and will use its copy - if (framesToAdd == frames) - framesToAdd = frames.copy(); - - framesToAdd.splice(i, 1); - } - } - - if (framesToAdd.length > 0) - { - var anim = new FlxAnimation(this, name, framesToAdd, frameRate, looped, flipX, flipY); - _animations.set(name, anim); - - if (hasInvalidFrames) - FlxG.log.warn('Could not add frames above ${numFrames - 1} to animation: "$name"'); - } - else - FlxG.log.warn('Could not create animation: "$name", no valid frames were given'); - } - - /** - * Removes (and destroys) an animation. - * - * @param Name The name of animation to remove. - */ - public function remove(Name:String):Void - { - var anim:FlxAnimation = _animations.get(Name); - if (anim != null) - { - _animations.remove(Name); - anim.destroy(); - } - } - - /** - * Adds to an existing animation in the sprite by appending the specified frames to the existing frames. - * Use this method when the indices of the frames in the atlas are already known. - * The animation must already exist in order to append frames to it. - * - * @param Name What the existing animation is called (e.g. `"run"`). - * @param Frames An array of indices indicating what frames to append (e.g. `[0, 1, 2]`). - */ - public function append(Name:String, Frames:Array):Void - { - var anim:FlxAnimation = _animations.get(Name); - if (anim == null) - { - // anim must already exist - FlxG.log.warn("No animation called \"" + Name + "\""); - return; - } - - var hasInvalidFrames = false; - - // Check _animations frames - for (frame in Frames) - { - if (frame < numFrames) - anim.frames.push(frame); - else - hasInvalidFrames = true; - } - - if (hasInvalidFrames) - FlxG.log.warn('Could not append frames above ${numFrames - 1} to animation: "$Name"'); - } - - /** - * Adds a new animation to the sprite. - * - * @param Name What this animation should be called (e.g. `"run"`). - * @param FrameNames An array of image names from the atlas indicating what frames to play in what order. - * @param FrameRate The speed in frames per second that the animation should play at (e.g. `40` fps). - * @param Looped Whether or not the animation is looped or just plays once. - * @param FlipX Whether the frames should be flipped horizontally. - * @param FlipY Whether the frames should be flipped vertically. - */ - public function addByNames(Name:String, FrameNames:Array, FrameRate:Int = 30, Looped:Bool = true, FlipX:Bool = false, FlipY:Bool = false):Void - { - if (_sprite.frames != null) - { - var indices:Array = new Array(); - byNamesHelper(indices, FrameNames); // finds frames and appends them to the blank array - - if (indices.length > 0) - { - var anim = new FlxAnimation(this, Name, indices, FrameRate, Looped, FlipX, FlipY); - _animations.set(Name, anim); - } - } - } - - /** - * Adds to an existing animation in the sprite by appending the specified frames to the existing frames. - * Use this method when the exact name of each frame from the atlas is known (e.g. `"walk00.png"`, `"walk01.png"`). - * The animation must already exist in order to append frames to it. - * - * @param Name What the existing animation is called (e.g. `"run"`). - * @param FrameNames An array of image names from atlas indicating what frames to append. - */ - public function appendByNames(Name:String, FrameNames:Array):Void - { - var anim:FlxAnimation = _animations.get(Name); - if (anim == null) - { - FlxG.log.warn("No animation called \"" + Name + "\""); - return; - } - - if (_sprite.frames != null) - { - byNamesHelper(anim.frames, FrameNames); // finds frames and appends them to the existing array - } - } - - /** - * Adds a new animation to the sprite. Should be slightly faster than `addByIndices()`. - * - * @param Name What this animation should be called (e.g. `"run"`). - * @param Prefix Common beginning of image names in the atlas (e.g. `"tiles-"`). - * @param Indices An array of strings indicating what frames to play in what order - * (e.g. `["01", "02", "03"]`). - * @param Postfix Common ending of image names in atlas (e.g. `".png"`). - * @param FrameRate The speed in frames per second that the animation should play at (e.g. `40` fps). - * @param Looped Whether or not the animation is looped or just plays once. - * @param FlipX Whether the frames should be flipped horizontally. - * @param FlipY Whether the frames should be flipped vertically. - */ - public function addByStringIndices(Name:String, Prefix:String, Indices:Array, Postfix:String, FrameRate:Int = 30, Looped:Bool = true, - FlipX:Bool = false, FlipY:Bool = false):Void - { - if (_sprite.frames != null) - { - var frameIndices:Array = new Array(); - // finds frames and appends them to the blank array - byStringIndicesHelper(frameIndices, Prefix, Indices, Postfix); - - if (frameIndices.length > 0) - { - var anim:FlxAnimation = new FlxAnimation(this, Name, frameIndices, FrameRate, Looped, FlipX, FlipY); - _animations.set(Name, anim); - } - } - } - - /** - * Adds to an existing animation in the sprite by appending the specified frames to the existing frames. - * Should be slightly faster than `appendByIndices()`. Use this method when the names of each frame from - * the atlas share a common prefix and postfix (e.g. `"walk00.png"`, `"walk01.png"`). - * The animation must already exist in order to append frames to it. FrameRate and Looped are unchanged. - * - * @param Name What the existing animation is called (e.g. `"run"`). - * @param Prefix Common beginning of image names in the atlas (e.g. `"tiles-"`). - * @param Indices An array of strings indicating what frames to append (e.g. `["01", "02", "03"]`). - * @param Postfix Common ending of image names in atlas (e.g. `".png"`). - */ - public function appendByStringIndices(Name:String, Prefix:String, Indices:Array, Postfix:String):Void - { - var anim:FlxAnimation = _animations.get(Name); - if (anim == null) - { - FlxG.log.warn("No animation called \"" + Name + "\""); - return; - } - - if (_sprite.frames != null) - { - // finds frames and appends them to the existing array - byStringIndicesHelper(anim.frames, Prefix, Indices, Postfix); - } - } - - /** - * Adds a new animation to the sprite. - * - * @param Name What this animation should be called (e.g. `"run"`). - * @param Prefix Common beginning of image names in the atlas (e.g. "tiles-"). - * @param Indices An array of numbers indicating what frames to play in what order (e.g. `[0, 1, 2]`). - * @param Postfix Common ending of image names in the atlas (e.g. `".png"`). - * @param FrameRate The speed in frames per second that the animation should play at (e.g. `40` fps). - * @param Looped Whether or not the animation is looped or just plays once. - * @param FlipX Whether the frames should be flipped horizontally. - * @param FlipY Whether the frames should be flipped vertically. - */ - public function addByIndices(Name:String, Prefix:String, Indices:Array, Postfix:String, FrameRate:Int = 30, Looped:Bool = true, FlipX:Bool = false, - FlipY:Bool = false):Void - { - if (_sprite.frames != null) - { - var frameIndices:Array = new Array(); - // finds frames and appends them to the blank array - byIndicesHelper(frameIndices, Prefix, Indices, Postfix); - - if (frameIndices.length > 0) - { - var anim:FlxAnimation = new FlxAnimation(this, Name, frameIndices, FrameRate, Looped, FlipX, FlipY); - _animations.set(Name, anim); - } - } - } - - /** - * Adds to an existing animation in the sprite by appending the specified frames to the existing frames. - * Use this method when the names of each frame from the atlas share a common prefix - * and postfix (e.g. `"walk00.png"`, `"walk01.png"`). - * Leading zeroes are ignored for matching indices (`5` will match `"5"` and `"005"`). - * The animation must already exist in order to append frames to it. - * - * @param Name What the existing animation is called (e.g. `"run"`). - * @param Prefix Common beginning of image names in atlas (e.g. `"tiles-"`). - * @param Indices An array of numbers indicating what frames to append (e.g. `[0, 1, 2]`). - * @param Postfix Common ending of image names in atlas (e.g. `".png"`). - */ - public function appendByIndices(Name:String, Prefix:String, Indices:Array, Postfix:String):Void - { - var anim:FlxAnimation = _animations.get(Name); - if (anim == null) - { - FlxG.log.warn("No animation called \"" + Name + "\""); - return; - } - - if (_sprite.frames != null) - { - // finds frames and appends them to the existing array - byIndicesHelper(anim.frames, Prefix, Indices, Postfix); - } - } - - /** - * Find a sprite frame so that for `Prefix = "file"; Index = 5; Postfix = ".png"` - * It will find frame with name `"file5.png"`. If it doesn't exist it will try - * to find `"file05.png"`, allowing 99 frames per animation. - * Returns the found frame or `-1` on failure. - */ - function findSpriteFrame(prefix:String, index:Int, postfix:String):Int - { - final frames = _sprite.frames.frames; - for (i in 0...frames.length) - { - final frame = frames[i]; - final name = frame.name; - if (StringTools.startsWith(name, prefix) && StringTools.endsWith(name, postfix)) - { - final frameIndex:Null = Std.parseInt(name.substring(prefix.length, name.length - postfix.length)); - if (frameIndex == index) - return i; - } - } - - return -1; - } - - /** - * Adds a new animation to the sprite. - * - * @param name What this animation should be called (e.g. `"run"`). - * @param prefix Common beginning of image names in atlas (e.g. `"tiles-"`). - * @param frameRate The animation speed in frames per second. - * Note: individual frames have their own duration, which overides this value. - * @param looped Whether or not the animation is looped or just plays once. - * @param flipX Whether the frames should be flipped horizontally. - * @param flipY Whether the frames should be flipped vertically. - */ - public function addByPrefix(name:String, prefix:String, frameRate = 30, looped = true, flipX = false, flipY = false):Void - { - if (_sprite.frames != null) - { - var animFrames:Array = new Array(); - findByPrefix(animFrames, prefix); // adds valid frames to animFrames - - if (animFrames.length > 0) - { - var frameIndices:Array = new Array(); - byPrefixHelper(frameIndices, animFrames, prefix); // finds frames and appends them to the blank array - - if (frameIndices.length > 0) - { - var anim:FlxAnimation = new FlxAnimation(this, name, frameIndices, frameRate, looped, flipX, flipY); - _animations.set(name, anim); - } - } - } - } - - /** - * Adds to an existing animation in the sprite by appending the specified frames to the existing frames. - * Use this method when the names of each frame from the atlas share a common prefix - * (e.g. `"walk00.png"`, `"walk01.png"`). - * Frames are sorted numerically while ignoring postfixes (e.g. `".png"`, `".gif"`). - * The animation must already exist in order to append frames to it. - * - * @param name What the existing animation is called (e.g. `"run"`). - * @param prefix Common beginning of image names in atlas (e.g. `"tiles-"`) - */ - public function appendByPrefix(name:String, prefix:String):Void - { - var anim:FlxAnimation = _animations.get(name); - if (anim == null) - { - FlxG.log.warn("No animation called \"" + name + "\""); - return; - } - - if (_sprite.frames != null) - { - var animFrames:Array = new Array(); - findByPrefix(animFrames, prefix); // adds valid frames to animFrames - - if (animFrames.length > 0) - { - // finds frames and appends them to the existing array - byPrefixHelper(anim.frames, animFrames, prefix); - } - } - } - - /** - * Plays an existing animation (e.g. `"run"`). - * If you call an animation that is already playing, it will be ignored. - * - * @param AnimName The string name of the animation you want to play. - * @param Force Whether to force the animation to restart. - * @param Reversed Whether to play animation backwards or not. - * @param Frame The frame number in the animation you want to start from. - * If a negative value is passed, a random frame is used. - */ - public function play(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void - { - if (AnimName == null) - { - if (_curAnim != null) - { - _curAnim.stop(); - } - _curAnim = null; - } - - if (AnimName == null || _animations.get(AnimName) == null) - { - FlxG.log.warn("No animation called \"" + AnimName + "\""); - return; - } - - var oldFlipX:Bool = false; - var oldFlipY:Bool = false; - - if (_curAnim != null && AnimName != _curAnim.name) - { - oldFlipX = _curAnim.flipX; - oldFlipY = _curAnim.flipY; - _curAnim.stop(); - } - _curAnim = _animations.get(AnimName); - _curAnim.play(Force, Reversed, Frame); - - if (oldFlipX != _curAnim.flipX || oldFlipY != _curAnim.flipY) - { - _sprite.dirty = true; - } - } - - /** - * Stops current animation and resets its frame index to zero. - */ - public inline function reset():Void - { - if (_curAnim != null) - { - _curAnim.reset(); - } - } - - /** - * Stops current animation and sets its frame to the last one. - */ - public function finish():Void - { - if (_curAnim != null) - { - _curAnim.finish(); - } - } - - /** - * Just stops current animation. - */ - public function stop():Void - { - if (_curAnim != null) - { - _curAnim.stop(); - } - } - - /** - * Pauses the current animation. - */ - public inline function pause():Void - { - if (_curAnim != null) - { - _curAnim.pause(); - } - } - - /** - * Resumes the current animation if it exists. - */ - public inline function resume():Void - { - if (_curAnim != null) - { - _curAnim.resume(); - } - } - - /** - * Reverses current animation if it exists. - */ - public inline function reverse():Void - { - if (_curAnim != null) - { - _curAnim.reverse(); - } - } - - /** - * Gets the FlxAnimation object with the specified name. - */ - public inline function getByName(name:String):FlxAnimation - { - return _animations.get(name); - } - - /** - * Changes to a random animation frame. - * Useful for instantiating particles or other weird things. - */ - public function randomFrame():Void - { - if (_curAnim != null) - { - _curAnim.stop(); - _curAnim = null; - } - frameIndex = FlxG.random.int(0, numFrames - 1); - } - - inline function fireCallback():Void - { - if (callback != null) - { - var name:String = (_curAnim != null) ? (_curAnim.name) : null; - var number:Int = (_curAnim != null) ? (_curAnim.curFrame) : frameIndex; - callback(name, number, frameIndex); - } - } - - @:allow(flixel.animation) - inline function fireFinishCallback(?name:String):Void - { - if (finishCallback != null) - { - finishCallback(name); - } - } - - function byNamesHelper(AddTo:Array, FrameNames:Array):Void - { - for (frameName in FrameNames) - { - if (_sprite.frames.framesHash.exists(frameName)) - { - var frameToAdd:FlxFrame = _sprite.frames.framesHash.get(frameName); - AddTo.push(getFrameIndex(frameToAdd)); - } - } - } - - function byStringIndicesHelper(AddTo:Array, Prefix:String, Indices:Array, Postfix:String):Void - { - for (index in Indices) - { - var name:String = Prefix + index + Postfix; - if (_sprite.frames.framesHash.exists(name)) - { - var frameToAdd:FlxFrame = _sprite.frames.framesHash.get(name); - AddTo.push(getFrameIndex(frameToAdd)); - } - } - } - - function byIndicesHelper(AddTo:Array, Prefix:String, Indices:Array, Postfix:String):Void - { - for (index in Indices) - { - var indexToAdd:Int = findSpriteFrame(Prefix, index, Postfix); - if (indexToAdd != -1) - { - AddTo.push(indexToAdd); - } - } - } - - function byPrefixHelper(AddTo:Array, AnimFrames:Array, Prefix:String):Void - { - var name:String = AnimFrames[0].name; - var postIndex:Int = name.indexOf(".", Prefix.length); - var postFix:String = name.substring(postIndex == -1 ? name.length : postIndex, name.length); - FlxFrame.sort(AnimFrames, Prefix.length, postFix.length); - - for (animFrame in AnimFrames) - { - AddTo.push(getFrameIndex(animFrame)); - } - } - - function findByPrefix(AnimFrames:Array, Prefix:String):Void - { - for (frame in _sprite.frames.frames) - { - if (frame.name != null && StringTools.startsWith(frame.name, Prefix)) - { - AnimFrames.push(frame); - } - } - } - - function set_frameIndex(Frame:Int):Int - { - if (_sprite.frames != null && numFrames > 0) - { - Frame = Frame % numFrames; - _sprite.frame = _sprite.frames.frames[Frame]; - frameIndex = Frame; - fireCallback(); - } - - return frameIndex; - } - - inline function get_frameName():String - { - return _sprite.frame.name; - } - - function set_frameName(Value:String):String - { - if (_sprite.frames != null && _sprite.frames.framesHash.exists(Value)) - { - if (_curAnim != null) - { - _curAnim.stop(); - _curAnim = null; - } - - var frame = _sprite.frames.framesHash.get(Value); - if (frame != null) - { - frameIndex = getFrameIndex(frame); - } - } - - return Value; - } - - function get_name():String - { - var animName:String = null; - if (_curAnim != null) - { - animName = _curAnim.name; - } - return animName; - } - - function set_name(AnimName:String):String - { - play(AnimName); - return AnimName; - } - - /** - * Gets a list with all the animations that are added in a sprite. - * WARNING: Do not confuse with `getNameList`, this function returns the animation instances - * @return an array with all the animations. - * @since 4.11.0 - */ - public function getAnimationList():Array - { - var animList:Array = []; - - for (anims in _animations) - { - animList.push(anims); - } - - return animList; - } - - /** - * Gets a list with all the name animations that are added in a sprite - * WARNING: Do not confuse with `getAnimationList`, this function returns the animation names - * @return an array with all the animation names in it. - * @since 4.11.0 - */ - public function getNameList():Array - { - var namesList:Array = []; - for (names in _animations.keys()) - { - namesList.push(names); - } - - return namesList; - } - - /** - * Checks if an animation exists by it's name. - * @param name The animation name. - * @since 4.11.0 - */ - public function exists(name:String):Bool - { - return _animations.exists(name); - } - - /** - * Renames the animation with a new name. - * @param oldName the name that is replaced. - * @param newName the name that replaces the old one. - * @since 4.11.0 - */ - public function rename(oldName:String, newName:String) - { - var anim = _animations.get(oldName); - if (anim == null) - { - FlxG.log.warn('No animation called "$oldName"'); - return; - } - _animations.set(newName, anim); - anim.name = newName; - _animations.remove(oldName); - } - - inline function get_curAnim():FlxAnimation - { - return _curAnim; - } - - inline function set_curAnim(anim:FlxAnimation):FlxAnimation - { - if (anim != _curAnim) - { - if (_curAnim != null) - { - _curAnim.stop(); - } - - if (anim != null) - { - anim.play(); - } - } - return _curAnim = anim; - } - - inline function get_paused():Bool - { - var paused = false; - if (_curAnim != null) - { - paused = _curAnim.paused; - } - return paused; - } - - inline function set_paused(value:Bool):Bool - { - if (_curAnim != null) - { - if (value) - { - _curAnim.pause(); - } - else - { - _curAnim.resume(); - } - } - return value; - } - - function get_finished():Bool - { - var finished = true; - if (_curAnim != null) - { - finished = _curAnim.finished; - } - return finished; - } - - inline function set_finished(value:Bool):Bool - { - if (value && _curAnim != null) - { - _curAnim.finish(); - } - return value; - } - - inline function get_frames():Int - { - return _sprite.numFrames; - } - - inline function get_numFrames():Int - { - return _sprite.numFrames; - } - - /** - * Helper function used for finding index of `FlxFrame` in `_framesData`'s frames array - * - * @param Frame `FlxFrame` to find - * @return position of specified `FlxFrame` object. - */ - public inline function getFrameIndex(frame:FlxFrame):Int - { - return _sprite.frames.frames.indexOf(frame); - } -} diff --git a/source/flixel/sound/FlxSound.hx b/source/flixel/sound/FlxSound.hx new file mode 100644 index 00000000..6d1afaa0 --- /dev/null +++ b/source/flixel/sound/FlxSound.hx @@ -0,0 +1,808 @@ +package flixel.sound; + +import flash.events.Event; +import flash.events.IEventDispatcher; +import flash.media.Sound; +import flash.media.SoundChannel; +import flash.media.SoundTransform; +import flash.net.URLRequest; +import flixel.FlxBasic; + +import flixel.math.FlxMath; + +import flixel.tweens.FlxTween; +import flixel.tweens.FlxEase; + +import flixel.math.FlxPoint; +import flixel.system.FlxAssets.FlxSoundAsset; +import flixel.util.FlxStringUtil; + +#if (flixel >= "5.3.0") +import flixel.sound.FlxSoundGroup; +#else +import flixel.system.FlxSoundGroup; +#end + +import openfl.Assets; +#if flash11 +import flash.utils.ByteArray; +#end +#if (openfl >= "8.0.0") +import openfl.utils.AssetType; +#end + +/** + * This is the universal flixel sound object, used for streaming, music, and sound effects. + */ +class FlxSound extends FlxBasic +{ + /** + * The x position of this sound in world coordinates. + * Only really matters if you are doing proximity/panning stuff. + */ + public var x:Float; + + /** + * The y position of this sound in world coordinates. + * Only really matters if you are doing proximity/panning stuff. + */ + public var y:Float; + + /** + * Whether or not this sound should be automatically destroyed when you switch states. + */ + public var persist:Bool; + + /** + * The ID3 song name. Defaults to null. Currently only works for streamed sounds. + */ + public var name(default, null):String; + + /** + * The ID3 artist name. Defaults to null. Currently only works for streamed sounds. + */ + public var artist(default, null):String; + + /** + * Stores the average wave amplitude of both stereo channels + */ + public var amplitude(default, null):Float; + + /** + * Just the amplitude of the left stereo channel + */ + public var amplitudeLeft(default, null):Float; + + /** + * Just the amplitude of the left stereo channel + */ + public var amplitudeRight(default, null):Float; + + /** + * Whether to call `destroy()` when the sound has finished playing. + */ + public var autoDestroy:Bool; + + /** + * Tracker for sound complete callback. If assigned, will be called + * each time when sound reaches its end. + */ + public var onComplete:Void->Void; + + /** + * Pan amount. -1 = full left, 1 = full right. Proximity based panning overrides this. + */ + public var pan(get, set):Float; + + /** + * Whether or not the sound is currently playing. + */ + public var playing(get, never):Bool; + + /** + * Set volume to a value between 0 and 1 to change how this sound is. + */ + public var volume(get, set):Float; + + /** + * Set pitch, which also alters the playback speed. Default is 1. + */ + public var pitch(get, set):Float; + + /** + * The position in runtime of the music playback in milliseconds. + * If set while paused, changes only come into effect after a `resume()` call. + */ + public var time(get, set):Float; + + /** + * The length of the sound in milliseconds. + * @since 4.2.0 + */ + public var length(get, never):Float; + + /** + * The sound group this sound belongs to + */ + public var group(default, set):FlxSoundGroup; + + /** + * Whether or not this sound should loop. + */ + public var looped:Bool; + + /** + * In case of looping, the point (in milliseconds) from where to restart the sound when it loops back + * @since 4.1.0 + */ + public var loopTime:Float = 0; + + /** + * At which point to stop playing the sound, in milliseconds. + * If not set / `null`, the sound completes normally. + * @since 4.2.0 + */ + public var endTime:Null; + + /** + * The tween used to fade this sound's volume in and out (set via `fadeIn()` and `fadeOut()`) + * @since 4.1.0 + */ + public var fadeTween:FlxTween; + + /** + * Internal tracker for a Flash sound object. + */ + @:allow(flixel.system.frontEnds.SoundFrontEnd.load) + var _sound:Sound; + + /** + * Internal tracker for a Flash sound channel object. + */ + var _channel:SoundChannel; + + /** + * Internal tracker for a Flash sound transform object. + */ + var _transform:SoundTransform; + + /** + * Internal tracker for whether the sound is paused or not (not the same as stopped). + */ + var _paused:Bool; + + /** + * Internal tracker for volume. + */ + var _volume:Float; + + /** + * Internal tracker for sound channel position. + */ + var _time:Float = 0; + + /** + * Internal tracker for sound length, so that length can still be obtained while a sound is paused, because _sound becomes null. + */ + var _length:Float = 0; + + /** + * Internal tracker for pitch. + */ + var _pitch:Float = 1.0; + + /** + * Internal tracker for total volume adjustment. + */ + var _volumeAdjust:Float = 1.0; + + /** + * Internal tracker for the sound's "target" (for proximity and panning). + */ + var _target:FlxObject; + + /** + * Internal tracker for the maximum effective radius of this sound (for proximity and panning). + */ + var _radius:Float; + + /** + * Internal tracker for whether to pan the sound left and right. Default is false. + */ + var _proximityPan:Bool; + + /** + * Helper var to prevent the sound from playing after focus was regained when it was already paused. + */ + var _alreadyPaused:Bool = false; + + /** + * The FlxSound constructor gets all the variables initialized, but NOT ready to play a sound yet. + */ + public function new() + { + super(); + reset(); + } + + /** + * An internal function for clearing all the variables used by sounds. + */ + function reset():Void + { + destroy(); + + x = 0; + y = 0; + + _time = 0; + _paused = false; + _volume = 1.0; + _pitch = 1.0; + _volumeAdjust = 1.0; + looped = false; + loopTime = 0.0; + endTime = 0.0; + _target = null; + _radius = 0; + _proximityPan = false; + visible = false; + amplitude = 0; + amplitudeLeft = 0; + amplitudeRight = 0; + autoDestroy = false; + + if (_transform == null) + _transform = new SoundTransform(); + _transform.pan = 0; + } + + override public function destroy():Void + { + _transform = null; + exists = false; + active = false; + _target = null; + name = null; + artist = null; + + if (_channel != null) + { + _channel.removeEventListener(Event.SOUND_COMPLETE, stopped); + _channel.stop(); + _channel = null; + } + + if (_sound != null) + { + _sound.removeEventListener(Event.ID3, gotID3); + _sound = null; + } + + onComplete = null; + + super.destroy(); + } + + /** + * Handles fade out, fade in, panning, proximity, and amplitude operations each frame. + */ + override public function update(elapsed:Float):Void + { + if (!playing) + return; + + _time = _channel.position; + + var radialMultiplier:Float = 1.0; + + // Distance-based volume control + if (_target != null) + { + var targetPosition = _target.getPosition(); + radialMultiplier = targetPosition.distanceTo(FlxPoint.weak(x, y)) / _radius; + targetPosition.put(); + radialMultiplier = 1 - FlxMath.bound(radialMultiplier, 0, 1); + + if (_proximityPan) + { + var d:Float = (x - _target.x) / _radius; + _transform.pan = FlxMath.bound(d, -1, 1); + } + } + + _volumeAdjust = radialMultiplier; + updateTransform(); + + if (_transform.volume > 0) + { + amplitudeLeft = _channel.leftPeak / _transform.volume; + amplitudeRight = _channel.rightPeak / _transform.volume; + amplitude = (amplitudeLeft + amplitudeRight) * 0.5; + } + else + { + amplitudeLeft = 0; + amplitudeRight = 0; + amplitude = 0; + } + + if (endTime != null && _time >= endTime) + stopped(); + } + + override public function kill():Void + { + super.kill(); + cleanup(false); + } + + /** + * One of the main setup functions for sounds, this function loads a sound from an embedded MP3. + * + * @param EmbeddedSound An embedded Class object representing an MP3 file. + * @param Looped Whether or not this sound should loop endlessly. + * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. + * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default. + * @param OnComplete Called when the sound finished playing + * @return This FlxSound instance (nice for chaining stuff together, if you're into that). + */ + public function loadEmbedded(EmbeddedSound:FlxSoundAsset, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound + { + if (EmbeddedSound == null) + return this; + + cleanup(true); + + if ((EmbeddedSound is Sound)) + { + _sound = EmbeddedSound; + } + else if ((EmbeddedSound is Class)) + { + _sound = Type.createInstance(EmbeddedSound, []); + } + else if ((EmbeddedSound is String)) + { + if (Assets.exists(EmbeddedSound, AssetType.SOUND) || Assets.exists(EmbeddedSound, AssetType.MUSIC)) + _sound = Assets.getSound(EmbeddedSound); + else + FlxG.log.error('Could not find a Sound asset with an ID of \'$EmbeddedSound\'.'); + } + + // NOTE: can't pull ID3 info from embedded sound currently + return init(Looped, AutoDestroy, OnComplete); + } + + /** + * One of the main setup functions for sounds, this function loads a sound from a URL. + * + * @param SoundURL A string representing the URL of the MP3 file you want to play. + * @param Looped Whether or not this sound should loop endlessly. + * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. + * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default. + * @param OnComplete Called when the sound finished playing + * @param OnLoad Called when the sound finished loading. + * @return This FlxSound instance (nice for chaining stuff together, if you're into that). + */ + public function loadStream(SoundURL:String, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void, ?OnLoad:Void->Void):FlxSound + { + cleanup(true); + + _sound = new Sound(); + _sound.addEventListener(Event.ID3, gotID3); + var loadCallback:Event->Void = null; + loadCallback = function(e:Event) + { + (e.target : IEventDispatcher).removeEventListener(e.type, loadCallback); + // Check if the sound was destroyed before calling. Weak ref doesn't guarantee GC. + if (_sound == e.target) + { + _length = _sound.length; + if (OnLoad != null) + OnLoad(); + } + } + // Use a weak reference so this can be garbage collected if destroyed before loading. + _sound.addEventListener(Event.COMPLETE, loadCallback, false, 0, true); + _sound.load(new URLRequest(SoundURL)); + + return init(Looped, AutoDestroy, OnComplete); + } + + #if flash11 + /** + * One of the main setup functions for sounds, this function loads a sound from a ByteArray. + * + * @param Bytes A ByteArray object. + * @param Looped Whether or not this sound should loop endlessly. + * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. + * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default. + * @return This FlxSound instance (nice for chaining stuff together, if you're into that). + */ + public function loadByteArray(Bytes:ByteArray, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound + { + cleanup(true); + + _sound = new Sound(); + _sound.addEventListener(Event.ID3, gotID3); + _sound.loadCompressedDataFromByteArray(Bytes, Bytes.length); + + return init(Looped, AutoDestroy, OnComplete); + } + #end + + function init(Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound + { + looped = Looped; + autoDestroy = AutoDestroy; + updateTransform(); + exists = true; + onComplete = OnComplete; + _length = (_sound == null) ? 0 : _sound.length; + endTime = _length; + return this; + } + + /** + * Call this function if you want this sound's volume to change + * based on distance from a particular FlxObject. + * + * @param X The X position of the sound. + * @param Y The Y position of the sound. + * @param TargetObject The object you want to track. + * @param Radius The maximum distance this sound can travel. + * @param Pan Whether panning should be used in addition to the volume changes. + * @return This FlxSound instance (nice for chaining stuff together, if you're into that). + */ + public function proximity(X:Float, Y:Float, TargetObject:FlxObject, Radius:Float, Pan:Bool = true):FlxSound + { + x = X; + y = Y; + _target = TargetObject; + _radius = Radius; + _proximityPan = Pan; + return this; + } + + /** + * Call this function to play the sound - also works on paused sounds. + * + * @param ForceRestart Whether to start the sound over or not. + * Default value is false, meaning if the sound is already playing or was + * paused when you call play(), it will continue playing from its current + * position, NOT start again from the beginning. + * @param StartTime At which point to start playing the sound, in milliseconds. + * @param EndTime At which point to stop playing the sound, in milliseconds. + * If not set / `null`, the sound completes normally. + */ + public function play(ForceRestart:Bool = false, StartTime:Float = 0.0, ?EndTime:Float):FlxSound + { + if (!exists) + return this; + + if (ForceRestart) + cleanup(false, true); + else if (playing) // Already playing sound + return this; + + if (_paused) + resume(); + else + startSound(StartTime); + + endTime = EndTime; + return this; + } + + /** + * Unpause a sound. Only works on sounds that have been paused. + */ + public function resume():FlxSound + { + if (_paused) + startSound(_time); + return this; + } + + /** + * Call this function to pause this sound. + */ + public function pause():FlxSound + { + if (!playing) + return this; + + _time = _channel.position; + _paused = true; + cleanup(false, false); + return this; + } + + /** + * Call this function to stop this sound. + */ + public inline function stop():FlxSound + { + cleanup(autoDestroy, true); + return this; + } + + /** + * Helper function that tweens this sound's volume. + * + * @param Duration The amount of time the fade-out operation should take. + * @param To The volume to tween to, 0 by default. + */ + public inline function fadeOut(Duration:Float = 1, ?To:Float = 0, ?onComplete:FlxTween->Void):FlxSound + { + if (fadeTween != null) + fadeTween.cancel(); + fadeTween = FlxTween.num(volume, To, Duration, {onComplete: onComplete}, volumeTween); + + return this; + } + + /** + * Helper function that tweens this sound's volume. + * + * @param Duration The amount of time the fade-in operation should take. + * @param From The volume to tween from, 0 by default. + * @param To The volume to tween to, 1 by default. + */ + public inline function fadeIn(Duration:Float = 1, From:Float = 0, To:Float = 1, ?onComplete:FlxTween->Void):FlxSound + { + if (!playing) + play(); + + if (fadeTween != null) + fadeTween.cancel(); + + fadeTween = FlxTween.num(From, To, Duration, {onComplete: onComplete}, volumeTween); + return this; + } + + function volumeTween(f:Float):Void + { + volume = f; + } + + /** + * Returns the currently selected "real" volume of the sound (takes fades and proximity into account). + * + * @return The adjusted volume of the sound. + */ + public inline function getActualVolume():Float + { + return _volume * _volumeAdjust; + } + + /** + * Helper function to set the coordinates of this object. + * Sound positioning is used in conjunction with proximity/panning. + * + * @param X The new x position + * @param Y The new y position + */ + public inline function setPosition(X:Float = 0, Y:Float = 0):Void + { + x = X; + y = Y; + } + + /** + * Call after adjusting the volume to update the sound channel's settings. + */ + public function updateTransform():Void + { + _transform.volume = #if FLX_SOUND_SYSTEM (FlxG.sound.muted ? 0 : 1) * FlxG.sound.volume * #end + (group != null ? group.volume : 1) * _volume * _volumeAdjust; + + if (_channel != null) + { + _channel.soundTransform = _transform; + + @:privateAccess + if(_channel.__source != null) + { + #if cpp + @:privateAccess + this._channel.__source.__backend.setPitch(_pitch); + // trace('changing $name pitch new $_pitch'); + #end + } + } + } + + /** + * An internal helper function used to attempt to start playing + * the sound and populate the _channel variable. + */ + function startSound(StartTime:Float):Void + { + if (_sound == null) + return; + + _time = StartTime; + _paused = false; + _channel = _sound.play(_time, 0, _transform); + if (_channel != null) + { + #if (sys && openfl_legacy) + pitch = _pitch; + #end + _channel.addEventListener(Event.SOUND_COMPLETE, stopped); + active = true; + } + else + { + exists = false; + active = false; + } + } + + /** + * An internal helper function used to help Flash + * clean up finished sounds or restart looped sounds. + */ + function stopped(?_):Void + { + if (onComplete != null) + onComplete(); + + if (looped) + { + cleanup(false); + play(false, loopTime, endTime); + } + else + cleanup(autoDestroy); + } + + /** + * An internal helper function used to help Flash clean up (and potentially re-use) finished sounds. + * Will stop the current sound and destroy the associated SoundChannel, plus, + * any other commands ordered by the passed in parameters. + * + * @param destroySound Whether or not to destroy the sound. If this is true, + * the position and fading will be reset as well. + * @param resetPosition Whether or not to reset the position of the sound. + */ + function cleanup(destroySound:Bool, resetPosition:Bool = true):Void + { + if (destroySound) + { + reset(); + return; + } + + if (_channel != null) + { + _channel.removeEventListener(Event.SOUND_COMPLETE, stopped); + _channel.stop(); + _channel = null; + } + + active = false; + + if (resetPosition) + { + _time = 0; + _paused = false; + } + } + + /** + * Internal event handler for ID3 info (i.e. fetching the song name). + */ + function gotID3(_):Void + { + name = _sound.id3.songName; + artist = _sound.id3.artist; + _sound.removeEventListener(Event.ID3, gotID3); + } + + #if FLX_SOUND_SYSTEM + @:allow(flixel.system.frontEnds.SoundFrontEnd) + function onFocus():Void + { + if (!_alreadyPaused) + resume(); + } + + @:allow(flixel.system.frontEnds.SoundFrontEnd) + function onFocusLost():Void + { + _alreadyPaused = _paused; + pause(); + } + #end + + function set_group(group:FlxSoundGroup):FlxSoundGroup + { + if (this.group != group) + { + var oldGroup:FlxSoundGroup = this.group; + + // New group must be set before removing sound to prevent infinite recursion + this.group = group; + + if (oldGroup != null) + oldGroup.remove(this); + + if (group != null) + group.add(this); + + updateTransform(); + } + return group; + } + + inline function get_playing():Bool + { + return _channel != null; + } + + inline function get_volume():Float + { + return _volume; + } + + function set_volume(Volume:Float):Float + { + _volume = FlxMath.bound(Volume, 0, 1); + updateTransform(); + return Volume; + } + + inline function get_pitch():Float + { + return _pitch; + } + + function set_pitch(v:Float):Float + { + return _pitch = v; + } + + inline function get_pan():Float + { + return _transform.pan; + } + + inline function set_pan(pan:Float):Float + { + return _transform.pan = pan; + } + + inline function get_time():Float + { + return _time; + } + + function set_time(time:Float):Float + { + if (playing) + { + cleanup(false, true); + startSound(time); + } + return _time = time; + } + + inline function get_length():Float + { + return _length; + } + + override public function toString():String + { + return FlxStringUtil.getDebugString([ + LabelValuePair.weak("playing", playing), + LabelValuePair.weak("time", time), + LabelValuePair.weak("length", length), + LabelValuePair.weak("volume", volume), + LabelValuePair.weak("pitch", pitch) + ]); + } +} \ No newline at end of file diff --git a/source/flixel/system/FlxSound.hx b/source/flixel/system/FlxSound.hx index b87018c6..526d07c4 100644 --- a/source/flixel/system/FlxSound.hx +++ b/source/flixel/system/FlxSound.hx @@ -1,799 +1,3 @@ package flixel.system; -import flash.events.IEventDispatcher; -import flash.events.Event; -import flash.media.Sound; -import flash.media.SoundChannel; -import flash.media.SoundTransform; -import flash.net.URLRequest; -import flixel.FlxBasic; -import flixel.FlxG; -import flixel.math.FlxMath; -import flixel.math.FlxPoint; -import flixel.system.FlxAssets.FlxSoundAsset; -import flixel.tweens.FlxTween; -import flixel.util.FlxStringUtil; -import openfl.Assets; -#if flash11 -import flash.utils.ByteArray; -#end -#if (openfl >= "8.0.0") -import openfl.utils.AssetType; -#end - -/** - * This is the universal flixel sound object, used for streaming, music, and sound effects. - */ -class FlxSound extends FlxBasic -{ - /** - * The x position of this sound in world coordinates. - * Only really matters if you are doing proximity/panning stuff. - */ - public var x:Float; - - /** - * The y position of this sound in world coordinates. - * Only really matters if you are doing proximity/panning stuff. - */ - public var y:Float; - - /** - * Whether or not this sound should be automatically destroyed when you switch states. - */ - public var persist:Bool; - - /** - * The ID3 song name. Defaults to null. Currently only works for streamed sounds. - */ - public var name(default, null):String; - - /** - * The ID3 artist name. Defaults to null. Currently only works for streamed sounds. - */ - public var artist(default, null):String; - - /** - * Stores the average wave amplitude of both stereo channels - */ - public var amplitude(default, null):Float; - - /** - * Just the amplitude of the left stereo channel - */ - public var amplitudeLeft(default, null):Float; - - /** - * Just the amplitude of the left stereo channel - */ - public var amplitudeRight(default, null):Float; - - /** - * Whether to call `destroy()` when the sound has finished playing. - */ - public var autoDestroy:Bool; - - /** - * Tracker for sound complete callback. If assigned, will be called - * each time when sound reaches its end. - */ - public var onComplete:Void->Void; - - /** - * Pan amount. -1 = full left, 1 = full right. Proximity based panning overrides this. - */ - public var pan(get, set):Float; - - /** - * Whether or not the sound is currently playing. - */ - public var playing(get, never):Bool; - - /** - * Set volume to a value between 0 and 1 to change how this sound is. - */ - public var volume(get, set):Float; - #if FLX_PITCH - /** - * Set pitch, which also alters the playback speed. Default is 1. - */ - public var pitch(get, set):Float; - #end - /** - * The position in runtime of the music playback in milliseconds. - * If set while paused, changes only come into effect after a `resume()` call. - */ - public var time(get, set):Float; - - /** - * The length of the sound in milliseconds. - * @since 4.2.0 - */ - public var length(get, never):Float; - - /** - * The sound group this sound belongs to - */ - public var group(default, set):FlxSoundGroup; - - /** - * Whether or not this sound should loop. - */ - public var looped:Bool; - - /** - * In case of looping, the point (in milliseconds) from where to restart the sound when it loops back - * @since 4.1.0 - */ - public var loopTime:Float = 0; - - /** - * At which point to stop playing the sound, in milliseconds. - * If not set / `null`, the sound completes normally. - * @since 4.2.0 - */ - public var endTime:Null; - - /** - * The tween used to fade this sound's volume in and out (set via `fadeIn()` and `fadeOut()`) - * @since 4.1.0 - */ - public var fadeTween:FlxTween; - - /** - * Internal tracker for a Flash sound object. - */ - @:allow(flixel.system.frontEnds.SoundFrontEnd.load) - var _sound:Sound; - - /** - * Internal tracker for a Flash sound channel object. - */ - var _channel:SoundChannel; - - /** - * Internal tracker for a Flash sound transform object. - */ - var _transform:SoundTransform; - - /** - * Internal tracker for whether the sound is paused or not (not the same as stopped). - */ - var _paused:Bool; - - /** - * Internal tracker for volume. - */ - var _volume:Float; - - /** - * Internal tracker for sound channel position. - */ - var _time:Float = 0; - - /** - * Internal tracker for sound length, so that length can still be obtained while a sound is paused, because _sound becomes null. - */ - var _length:Float = 0; - #if FLX_PITCH - /** - * Internal tracker for pitch. - */ - var _pitch:Float = 1.0; - #end - /** - * Internal tracker for total volume adjustment. - */ - var _volumeAdjust:Float = 1.0; - - /** - * Internal tracker for the sound's "target" (for proximity and panning). - */ - var _target:FlxObject; - - /** - * Internal tracker for the maximum effective radius of this sound (for proximity and panning). - */ - var _radius:Float; - - /** - * Internal tracker for whether to pan the sound left and right. Default is false. - */ - var _proximityPan:Bool; - - /** - * Helper var to prevent the sound from playing after focus was regained when it was already paused. - */ - var _alreadyPaused:Bool = false; - - /** - * The FlxSound constructor gets all the variables initialized, but NOT ready to play a sound yet. - */ - public function new() - { - super(); - reset(); - } - - /** - * An internal function for clearing all the variables used by sounds. - */ - function reset():Void - { - destroy(); - - x = 0; - y = 0; - - _time = 0; - _paused = false; - _volume = 1.0; - _volumeAdjust = 1.0; - looped = false; - loopTime = 0.0; - endTime = 0.0; - _target = null; - _radius = 0; - _proximityPan = false; - visible = false; - amplitude = 0; - amplitudeLeft = 0; - amplitudeRight = 0; - autoDestroy = false; - - if (_transform == null) - _transform = new SoundTransform(); - _transform.pan = 0; - } - - override public function destroy():Void - { - _transform = null; - exists = false; - active = false; - _target = null; - name = null; - artist = null; - - if (_channel != null) - { - _channel.removeEventListener(Event.SOUND_COMPLETE, stopped); - _channel.stop(); - _channel = null; - } - - if (_sound != null) - { - _sound.removeEventListener(Event.ID3, gotID3); - _sound = null; - } - - onComplete = null; - - super.destroy(); - } - - /** - * Handles fade out, fade in, panning, proximity, and amplitude operations each frame. - */ - override public function update(elapsed:Float):Void - { - if (!playing) - return; - - _time = _channel.position; - - var radialMultiplier:Float = 1.0; - - // Distance-based volume control - if (_target != null) - { - var targetPosition = _target.getPosition(); - radialMultiplier = targetPosition.distanceTo(FlxPoint.weak(x, y)) / _radius; - targetPosition.put(); - radialMultiplier = 1 - FlxMath.bound(radialMultiplier, 0, 1); - - if (_proximityPan) - { - var d:Float = (x - _target.x) / _radius; - _transform.pan = FlxMath.bound(d, -1, 1); - } - } - - _volumeAdjust = radialMultiplier; - updateTransform(); - - if (_transform.volume > 0) - { - amplitudeLeft = _channel.leftPeak / _transform.volume; - amplitudeRight = _channel.rightPeak / _transform.volume; - amplitude = (amplitudeLeft + amplitudeRight) * 0.5; - } - else - { - amplitudeLeft = 0; - amplitudeRight = 0; - amplitude = 0; - } - - if (endTime != null && _time >= endTime) - stopped(); - } - - override public function kill():Void - { - super.kill(); - cleanup(false); - } - - /** - * One of the main setup functions for sounds, this function loads a sound from an embedded MP3. - * - * @param EmbeddedSound An embedded Class object representing an MP3 file. - * @param Looped Whether or not this sound should loop endlessly. - * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. - * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default. - * @param OnComplete Called when the sound finished playing - * @return This FlxSound instance (nice for chaining stuff together, if you're into that). - */ - public function loadEmbedded(EmbeddedSound:FlxSoundAsset, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound - { - if (EmbeddedSound == null) - return this; - - cleanup(true); - - if ((EmbeddedSound is Sound)) - { - _sound = EmbeddedSound; - } - else if ((EmbeddedSound is Class)) - { - _sound = Type.createInstance(EmbeddedSound, []); - } - else if ((EmbeddedSound is String)) - { - if (Assets.exists(EmbeddedSound, AssetType.SOUND) || Assets.exists(EmbeddedSound, AssetType.MUSIC)) - _sound = Assets.getSound(EmbeddedSound); - else - FlxG.log.error('Could not find a Sound asset with an ID of \'$EmbeddedSound\'.'); - } - - // NOTE: can't pull ID3 info from embedded sound currently - return init(Looped, AutoDestroy, OnComplete); - } - - /** - * One of the main setup functions for sounds, this function loads a sound from a URL. - * - * @param SoundURL A string representing the URL of the MP3 file you want to play. - * @param Looped Whether or not this sound should loop endlessly. - * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. - * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default. - * @param OnComplete Called when the sound finished playing - * @param OnLoad Called when the sound finished loading. - * @return This FlxSound instance (nice for chaining stuff together, if you're into that). - */ - public function loadStream(SoundURL:String, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void, ?OnLoad:Void->Void):FlxSound - { - cleanup(true); - - _sound = new Sound(); - _sound.addEventListener(Event.ID3, gotID3); - var loadCallback:Event->Void = null; - loadCallback = function(e:Event) - { - (e.target : IEventDispatcher).removeEventListener(e.type, loadCallback); - // Check if the sound was destroyed before calling. Weak ref doesn't guarantee GC. - if (_sound == e.target) - { - _length = _sound.length; - if (OnLoad != null) - OnLoad(); - } - } - // Use a weak reference so this can be garbage collected if destroyed before loading. - _sound.addEventListener(Event.COMPLETE, loadCallback, false, 0, true); - _sound.load(new URLRequest(SoundURL)); - - return init(Looped, AutoDestroy, OnComplete); - } - - #if flash11 - /** - * One of the main setup functions for sounds, this function loads a sound from a ByteArray. - * - * @param Bytes A ByteArray object. - * @param Looped Whether or not this sound should loop endlessly. - * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. - * Default value is false, but `FlxG.sound.play()` and `FlxG.sound.stream()` will set it to true by default. - * @return This FlxSound instance (nice for chaining stuff together, if you're into that). - */ - public function loadByteArray(Bytes:ByteArray, Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound - { - cleanup(true); - - _sound = new Sound(); - _sound.addEventListener(Event.ID3, gotID3); - _sound.loadCompressedDataFromByteArray(Bytes, Bytes.length); - - return init(Looped, AutoDestroy, OnComplete); - } - #end - - function init(Looped:Bool = false, AutoDestroy:Bool = false, ?OnComplete:Void->Void):FlxSound - { - looped = Looped; - autoDestroy = AutoDestroy; - updateTransform(); - exists = true; - onComplete = OnComplete; - #if FLX_PITCH - pitch = 1; - #end - _length = (_sound == null) ? 0 : _sound.length; - endTime = _length; - return this; - } - - /** - * Call this function if you want this sound's volume to change - * based on distance from a particular FlxObject. - * - * @param X The X position of the sound. - * @param Y The Y position of the sound. - * @param TargetObject The object you want to track. - * @param Radius The maximum distance this sound can travel. - * @param Pan Whether panning should be used in addition to the volume changes. - * @return This FlxSound instance (nice for chaining stuff together, if you're into that). - */ - public function proximity(X:Float, Y:Float, TargetObject:FlxObject, Radius:Float, Pan:Bool = true):FlxSound - { - x = X; - y = Y; - _target = TargetObject; - _radius = Radius; - _proximityPan = Pan; - return this; - } - - /** - * Call this function to play the sound - also works on paused sounds. - * - * @param ForceRestart Whether to start the sound over or not. - * Default value is false, meaning if the sound is already playing or was - * paused when you call play(), it will continue playing from its current - * position, NOT start again from the beginning. - * @param StartTime At which point to start playing the sound, in milliseconds. - * @param EndTime At which point to stop playing the sound, in milliseconds. - * If not set / `null`, the sound completes normally. - */ - public function play(ForceRestart:Bool = false, StartTime:Float = 0.0, ?EndTime:Float):FlxSound - { - if (!exists) - return this; - - if (ForceRestart) - cleanup(false, true); - else if (playing) // Already playing sound - return this; - - if (_paused) - resume(); - else - startSound(StartTime); - - endTime = EndTime; - return this; - } - - /** - * Unpause a sound. Only works on sounds that have been paused. - */ - public function resume():FlxSound - { - if (_paused) - startSound(_time); - return this; - } - - /** - * Call this function to pause this sound. - */ - public function pause():FlxSound - { - if (!playing) - return this; - - _time = _channel.position; - _paused = true; - cleanup(false, false); - return this; - } - - /** - * Call this function to stop this sound. - */ - public inline function stop():FlxSound - { - cleanup(autoDestroy, true); - return this; - } - - /** - * Helper function that tweens this sound's volume. - * - * @param Duration The amount of time the fade-out operation should take. - * @param To The volume to tween to, 0 by default. - */ - public inline function fadeOut(Duration:Float = 1, ?To:Float = 0, ?onComplete:FlxTween->Void):FlxSound - { - if (fadeTween != null) - fadeTween.cancel(); - fadeTween = FlxTween.num(volume, To, Duration, {onComplete: onComplete}, volumeTween); - - return this; - } - - /** - * Helper function that tweens this sound's volume. - * - * @param Duration The amount of time the fade-in operation should take. - * @param From The volume to tween from, 0 by default. - * @param To The volume to tween to, 1 by default. - */ - public inline function fadeIn(Duration:Float = 1, From:Float = 0, To:Float = 1, ?onComplete:FlxTween->Void):FlxSound - { - if (!playing) - play(); - - if (fadeTween != null) - fadeTween.cancel(); - - fadeTween = FlxTween.num(From, To, Duration, {onComplete: onComplete}, volumeTween); - return this; - } - - function volumeTween(f:Float):Void - { - volume = f; - } - - /** - * Returns the currently selected "real" volume of the sound (takes fades and proximity into account). - * - * @return The adjusted volume of the sound. - */ - public inline function getActualVolume():Float - { - return _volume * _volumeAdjust; - } - - /** - * Helper function to set the coordinates of this object. - * Sound positioning is used in conjunction with proximity/panning. - * - * @param X The new x position - * @param Y The new y position - */ - public inline function setPosition(X:Float = 0, Y:Float = 0):Void - { - x = X; - y = Y; - } - - /** - * Call after adjusting the volume to update the sound channel's settings. - */ - @:allow(flixel.system.FlxSoundGroup) - function updateTransform():Void - { - _transform.volume = #if FLX_SOUND_SYSTEM (FlxG.sound.muted ? 0 : 1) * FlxG.sound.volume * #end - (group != null ? group.volume : 1) * _volume * _volumeAdjust; - - if (_channel != null) - _channel.soundTransform = _transform; - } - - /** - * An internal helper function used to attempt to start playing - * the sound and populate the _channel variable. - */ - function startSound(StartTime:Float):Void - { - if (_sound == null) - return; - - _time = StartTime; - _paused = false; - _channel = _sound.play(_time, 0, _transform); - if (_channel != null) - { - #if FLX_PITCH - pitch = _pitch; - #end - _channel.addEventListener(Event.SOUND_COMPLETE, stopped); - active = true; - } - else - { - exists = false; - active = false; - } - } - - /** - * An internal helper function used to help Flash - * clean up finished sounds or restart looped sounds. - */ - function stopped(?_):Void - { - if (onComplete != null) - onComplete(); - - if (looped) - { - cleanup(false); - play(false, loopTime, endTime); - } - else - cleanup(autoDestroy); - } - - /** - * An internal helper function used to help Flash clean up (and potentially re-use) finished sounds. - * Will stop the current sound and destroy the associated SoundChannel, plus, - * any other commands ordered by the passed in parameters. - * - * @param destroySound Whether or not to destroy the sound. If this is true, - * the position and fading will be reset as well. - * @param resetPosition Whether or not to reset the position of the sound. - */ - function cleanup(destroySound:Bool, resetPosition:Bool = true):Void - { - if (destroySound) - { - reset(); - return; - } - - if (_channel != null) - { - _channel.removeEventListener(Event.SOUND_COMPLETE, stopped); - _channel.stop(); - _channel = null; - } - - active = false; - - if (resetPosition) - { - _time = 0; - _paused = false; - } - } - - /** - * Internal event handler for ID3 info (i.e. fetching the song name). - */ - function gotID3(_):Void - { - name = _sound.id3.songName; - artist = _sound.id3.artist; - _sound.removeEventListener(Event.ID3, gotID3); - } - - #if FLX_SOUND_SYSTEM - @:allow(flixel.system.frontEnds.SoundFrontEnd) - function onFocus():Void - { - if (!_alreadyPaused) - resume(); - } - - @:allow(flixel.system.frontEnds.SoundFrontEnd) - function onFocusLost():Void - { - _alreadyPaused = _paused; - pause(); - } - #end - - function set_group(group:FlxSoundGroup):FlxSoundGroup - { - if (this.group != group) - { - var oldGroup:FlxSoundGroup = this.group; - - // New group must be set before removing sound to prevent infinite recursion - this.group = group; - - if (oldGroup != null) - oldGroup.remove(this); - - if (group != null) - group.add(this); - - updateTransform(); - } - return group; - } - - inline function get_playing():Bool - { - return _channel != null; - } - - inline function get_volume():Float - { - return _volume; - } - - function set_volume(Volume:Float):Float - { - _volume = FlxMath.bound(Volume, 0, 1); - updateTransform(); - return Volume; - } - #if FLX_PITCH - inline function get_pitch():Float - { - return _pitch; - } - - - function set_pitch(v:Float):Float - { - if (_channel != null) - #if openfl_legacy - _channel.pitch = v; - #else - @:privateAccess - if (_channel.__source != null) - _channel.__source.pitch = v; - #end - - return _pitch = v; - } - #end - - inline function get_pan():Float - { - return _transform.pan; - } - - inline function set_pan(pan:Float):Float - { - return _transform.pan = pan; - } - - inline function get_time():Float - { - return _time; - } - - function set_time(time:Float):Float - { - if (playing) - { - cleanup(false, true); - startSound(time); - } - return _time = time; - } - - inline function get_length():Float - { - return _length; - } - - override public function toString():String - { - return FlxStringUtil.getDebugString([ - LabelValuePair.weak("playing", playing), - LabelValuePair.weak("time", time), - LabelValuePair.weak("length", length), - LabelValuePair.weak("volume", volume) - ]); - } -} \ No newline at end of file +typedef FlxSound = flixel.sound.FlxSound; \ No newline at end of file diff --git a/source/flixel/system/FlxSoundGroup.hx b/source/flixel/system/FlxSoundGroup.hx deleted file mode 100644 index af49477a..00000000 --- a/source/flixel/system/FlxSoundGroup.hx +++ /dev/null @@ -1,87 +0,0 @@ -package flixel.system; - -/** - * A way of grouping sounds for things such as collective volume control - */ -class FlxSoundGroup -{ - /** - * The sounds in this group - */ - public var sounds:Array = []; - - /** - * The volume of this group - */ - public var volume(default, set):Float; - - /** - * Create a new sound group - * @param volume The initial volume of this group - */ - public function new(volume:Float = 1) - { - this.volume = volume; - } - - /** - * Add a sound to this group - * @param sound The sound to add to this group - * @return True if sound was successfully added, false otherwise - */ - public function add(sound:FlxSound):Bool - { - if (sounds.indexOf(sound) < 0) - { - sounds.push(sound); - sound.group = this; - return true; - } - return false; - } - - /** - * Remove a sound from this group - * @param sound The sound to remove - * @return True if sound was successfully removed, false otherwise - */ - public function remove(sound:FlxSound):Bool - { - if (sounds.indexOf(sound) >= 0) - { - sound.group = null; - return sounds.remove(sound); - } - return false; - } - - /** - * Call this function to pause all sounds in this group. - * @since 4.3.0 - */ - public function pause():Void - { - for (sound in sounds) - sound.pause(); - } - - /** - * Unpauses all sounds in this group. Only works on sounds that have been paused. - * @since 4.3.0 - */ - public function resume():Void - { - for (sound in sounds) - sound.resume(); - } - - function set_volume(volume:Float):Float - { - this.volume = volume; - for (sound in sounds) - { - sound.updateTransform(); - } - return volume; - } -} \ No newline at end of file diff --git a/source/meta/state/PlayState.hx b/source/meta/state/PlayState.hx index b5857302..a1b7545b 100644 --- a/source/meta/state/PlayState.hx +++ b/source/meta/state/PlayState.hx @@ -45,7 +45,6 @@ import flixel.group.FlxSpriteGroup; import flixel.input.keyboard.FlxKey; import openfl.events.KeyboardEvent; import flixel.util.FlxSave; -import flixel.animation.FlxAnimationController; import animateatlas.AtlasFrameMaker; import modcharting.ModchartFuncs; import modcharting.NoteMovement; @@ -1806,8 +1805,7 @@ class PlayState extends MusicBeatState FlxG.sound.music.pitch = value; } playbackRate = value; - FlxAnimationController.globalSpeed = value; - trace('Anim speed: ' + FlxAnimationController.globalSpeed); + FlxG.animationTimeScale = value; Conductor.safeZoneOffset = (ClientPrefs.safeFrames / 60) * 1000 * value; setOnLuas('playbackRate', playbackRate); return value; @@ -5919,7 +5917,7 @@ class PlayState extends MusicBeatState FlxG.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyPress); FlxG.stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyRelease); - FlxAnimationController.globalSpeed = 1; + FlxG.animationTimeScale = 1; FlxG.sound.music.pitch = 1; super.destroy(); } diff --git a/source/objects/Character.hx b/source/objects/Character.hx index ca91137c..73090bba 100644 --- a/source/objects/Character.hx +++ b/source/objects/Character.hx @@ -8,6 +8,7 @@ import meta.*; import meta.data.*; import meta.state.*; import objects.background.*; +import backend.animation.PsychAnimationController; #if (MODS_ALLOWED && FUTURE_POLYMOD) import sys.io.File; import sys.FileSystem; @@ -86,6 +87,8 @@ class Character extends FlxSprite { super(x, y); + animation = new PsychAnimationController(this); + animOffsets = new Map(); curCharacter = character; this.isPlayer = isPlayer; diff --git a/source/objects/userinterface/note/Note.hx b/source/objects/userinterface/note/Note.hx index 3f7b2035..ceb4383e 100644 --- a/source/objects/userinterface/note/Note.hx +++ b/source/objects/userinterface/note/Note.hx @@ -13,6 +13,8 @@ import meta.data.*; import objects.shaders.*; +import backend.animation.PsychAnimationController; + using StringTools; typedef EventNote = { @@ -179,6 +181,8 @@ class Note extends FlxSprite { super(); + animation = new PsychAnimationController(this); + if (prevNote == null) prevNote = this; diff --git a/source/objects/userinterface/note/NoteSplash.hx b/source/objects/userinterface/note/NoteSplash.hx index de7c7b3b..c34cd65b 100644 --- a/source/objects/userinterface/note/NoteSplash.hx +++ b/source/objects/userinterface/note/NoteSplash.hx @@ -10,6 +10,8 @@ import meta.state.*; import objects.shaders.*; import objects.userinterface.note.*; +import backend.animation.PsychAnimationController; + class NoteSplash extends FlxSprite { public var colorMask:ColorMask = null; @@ -19,6 +21,8 @@ class NoteSplash extends FlxSprite public function new(x:Float = 0, y:Float = 0, ?note:Int = 0) { super(x, y); + animation = new PsychAnimationController(this); + var skin:String = 'noteSplashes'; if(PlayState.SONG.splashSkin != null && PlayState.SONG.splashSkin.length > 0) skin = PlayState.SONG.splashSkin; diff --git a/source/objects/userinterface/note/StrumNote.hx b/source/objects/userinterface/note/StrumNote.hx index 280582e3..a76dba4e 100644 --- a/source/objects/userinterface/note/StrumNote.hx +++ b/source/objects/userinterface/note/StrumNote.hx @@ -10,6 +10,8 @@ import meta.state.*; import objects.shaders.*; import objects.userinterface.note.*; +import backend.animation.PsychAnimationController; + using StringTools; class StrumNote extends FlxSprite @@ -39,6 +41,8 @@ class StrumNote extends FlxSprite this.noteData = leData; super(x, y); + animation = new PsychAnimationController(this); + var skin:String = 'NOTE_assets'; if(PlayState.SONG.arrowSkin != null && PlayState.SONG.arrowSkin.length > 1) skin = PlayState.SONG.arrowSkin; shader = colorMask.shader;