From c4a50908d44bf08622f725ce121ff593d8a8c3ee Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Fri, 13 Jan 2023 04:22:00 +0100 Subject: [PATCH 1/2] Support for 50 minute songs (html5 untested)) Experimental support for 3+ hour long songs comes later --- buildnumber.txt | 2 +- .../backend/html5/HTML5AudioSource.hx | 253 ++++++++++++++++++ .../backend/native/NativeAudioSource.hx | 69 ++--- source/lime/media/AudioSource.hx | 12 +- source/openfl/media/Sound.hx | 36 +-- source/openfl/media/SoundChannel.hx | 238 ++++++++++++++++ 6 files changed, 544 insertions(+), 66 deletions(-) create mode 100644 source/lime/_internal/backend/html5/HTML5AudioSource.hx create mode 100644 source/openfl/media/SoundChannel.hx diff --git a/buildnumber.txt b/buildnumber.txt index abf9e63a..4d2b2566 100644 --- a/buildnumber.txt +++ b/buildnumber.txt @@ -1 +1 @@ -2036 \ No newline at end of file +2042 \ No newline at end of file diff --git a/source/lime/_internal/backend/html5/HTML5AudioSource.hx b/source/lime/_internal/backend/html5/HTML5AudioSource.hx new file mode 100644 index 00000000..bfca4198 --- /dev/null +++ b/source/lime/_internal/backend/html5/HTML5AudioSource.hx @@ -0,0 +1,253 @@ +package lime._internal.backend.html5; + +import lime.math.Vector4; +import lime.media.AudioSource; + +@:access(lime.media.AudioBuffer) +class HTML5AudioSource +{ + private var completed:Bool; + private var gain:Float; + private var id:Int; + private var length:Float; + private var loops:Int; + private var parent:AudioSource; + private var playing:Bool; + private var position:Vector4; + + public function new(parent:AudioSource) + { + this.parent = parent; + + id = -1; + gain = 1; + position = new Vector4(); + } + + public function dispose():Void {} + + public function init():Void {} + + public function play():Void + { + #if lime_howlerjs + if (playing || parent.buffer == null || parent.buffer.__srcHowl == null) + { + return; + } + + playing = true; + + var time = getCurrentTime(); + + completed = false; + + var cacheVolume = untyped parent.buffer.__srcHowl._volume; + untyped parent.buffer.__srcHowl._volume = parent.gain; + + id = parent.buffer.__srcHowl.play(); + + untyped parent.buffer.__srcHowl._volume = cacheVolume; + // setGain (parent.gain); + + setPosition(parent.position); + + parent.buffer.__srcHowl.on("end", howl_onEnd, id); + + // Calling setCurrentTime causes html5 audio to replay from this position on next frame + #if force_html5_audio + if (time == 0) setCurrentTime(time); + #else + setCurrentTime(time); + #end + #end + } + + public function pause():Void + { + #if lime_howlerjs + playing = false; + + if (parent.buffer != null && parent.buffer.__srcHowl != null) + { + parent.buffer.__srcHowl.pause(id); + } + #end + } + + public function stop():Void + { + #if lime_howlerjs + playing = false; + + if (parent.buffer != null && parent.buffer.__srcHowl != null) + { + parent.buffer.__srcHowl.stop(id); + parent.buffer.__srcHowl.off("end", howl_onEnd, id); + } + #end + } + + // Event Handlers + private function howl_onEnd() + { + #if lime_howlerjs + playing = false; + + if (loops > 0) + { + loops--; + stop(); + // currentTime = 0; + play(); + return; + } + else if (parent.buffer != null && parent.buffer.__srcHowl != null) + { + parent.buffer.__srcHowl.stop(id); + parent.buffer.__srcHowl.off("end", howl_onEnd, id); + } + + completed = true; + parent.onComplete.dispatch(); + #end + } + + // Get & Set Methods + public function getCurrentTime():Float + { + if (id == -1) + { + return 0; + } + + #if lime_howlerjs + if (completed) + { + return getLength(); + } + else if (parent.buffer != null && parent.buffer.__srcHowl != null) + { + var time = (parent.buffer.__srcHowl.seek(id) * 1000) - parent.offset; + if (time < 0) return 0; + return time; + } + #end + + return 0; + } + + public function setCurrentTime(value:Float):Float + { + #if lime_howlerjs + if (parent.buffer != null && parent.buffer.__srcHowl != null) + { + // if (playing) buffer.__srcHowl.play (id); + var pos = (value + parent.offset) / 1000; + if (pos < 0) pos = 0; + parent.buffer.__srcHowl.seek(pos, id); + } + #end + + return value; + } + + public function getGain():Float + { + return gain; + } + + public function setGain(value:Float):Float + { + #if lime_howlerjs + // set howler volume only if we have an active id. + // Passing -1 might create issues in future play()'s. + + if (parent.buffer != null && parent.buffer.__srcHowl != null && id != -1) + { + parent.buffer.__srcHowl.volume(value, id); + } + #end + + return gain = value; + } + + public function getLength():Float + { + if (length != 0) + { + return length; + } + + #if lime_howlerjs + if (parent.buffer != null && parent.buffer.__srcHowl != null) + { + return parent.buffer.__srcHowl.duration() * 1000; + } + #end + + return 0; + } + + public function setLength(value:Float):Float + { + return length = value; + } + + public function getLoops():Int + { + return loops; + } + + public function setLoops(value:Int):Int + { + return loops = value; + } + + public function getPitch():Float + { + #if lime_howlerjs + return parent.buffer.__srcHowl.rate(); + #else + return 1; + #end + } + + public function setPitch(value:Float):Float + { + #if lime_howlerjs + parent.buffer.__srcHowl.rate(value); + #end + return getPitch(); + } + + public function getPosition():Vector4 + { + #if lime_howlerjs + // This should work, but it returns null (But checking the inside of the howl, the _pos is actually null... so ¯\_(ツ)_/¯) + /* + var arr = parent.buffer.__srcHowl.pos()) + position.x = arr[0]; + position.y = arr[1]; + position.z = arr[2]; + */ + #end + + return position; + } + + public function setPosition(value:Vector4):Vector4 + { + position.x = value.x; + position.y = value.y; + position.z = value.z; + position.w = value.w; + + #if lime_howlerjs + if (parent.buffer != null && parent.buffer.__srcHowl != null && parent.buffer.__srcHowl.pos != null) parent.buffer.__srcHowl.pos(position.x, position.y, position.z, id); + // There are more settings to the position of the sound on the "pannerAttr()" function of howler. Maybe somebody who understands sound should look into it? + #end + + return position; + } +} \ No newline at end of file diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx index a4c6fdff..e7cbd821 100644 --- a/source/lime/_internal/backend/native/NativeAudioSource.hx +++ b/source/lime/_internal/backend/native/NativeAudioSource.hx @@ -34,16 +34,19 @@ class NativeAudioSource private var dataLength:Int; private var format:Int; private var handle:ALSource; - private var length:Null; + private var length:Null; private var loops:Int; private var parent:AudioSource; private var playing:Bool; private var position:Vector4; - private var samples:Int; + private var samples:Float; private var stream:Bool; private var streamTimer:Timer; private var timer:Timer; + private var pcmTotal:Int64; + private var sampleRate:Int; + var disposed:Bool = false; public function new(parent:AudioSource) @@ -107,7 +110,11 @@ class NativeAudioSource stream = true; var vorbisFile = parent.buffer.__srcVorbisFile; - dataLength = Std.int(Int64.toInt(vorbisFile.pcmTotal()) * parent.buffer.channels * (parent.buffer.bitsPerSample / 8)); + pcmTotal = vorbisFile.pcmTotal(); + var info = vorbisFile.info(); + sampleRate = info.rate; + var _dataLength = pcmTotal * Int64.ofInt(parent.buffer.channels) * (Int64.ofInt(parent.buffer.bitsPerSample) / Int64.ofInt(8)); + dataLength = Int64.toInt(_dataLength); buffers = new Array(); bufferTimeBlocks = new Array(); @@ -119,6 +126,8 @@ class NativeAudioSource } handle = AL.createSource(); + + samples = Int64.toInt(pcmTotal); } else { @@ -140,43 +149,15 @@ class NativeAudioSource { AL.sourcei(handle, AL.BUFFER, parent.buffer.__srcBuffer); } - } - samples = Std.int((dataLength * 8) / (parent.buffer.channels * parent.buffer.bitsPerSample)); + samples = Int64.toInt((Int64.make(0, dataLength) * Int64.ofInt(8)) / Int64.ofInt(parent.buffer.channels * parent.buffer.bitsPerSample)); + } initBuffers.push(this); } public function play():Void { - /*var pitch:Float = AL.getSourcef (handle, AL.PITCH); - trace(pitch); - AL.sourcef (handle, AL.PITCH, pitch*0.9); - pitch = AL.getSourcef (handle, AL.PITCH); - trace(pitch); */ - /*var pos = getPosition(); - trace(AL.DISTANCE_MODEL); - AL.distanceModel(AL.INVERSE_DISTANCE); - trace(AL.DISTANCE_MODEL); - AL.sourcef(handle, AL.ROLLOFF_FACTOR, 5); - setPosition(new Vector4(10, 10, -100)); - pos = getPosition(); - trace(pos); */ - /*var filter = AL.createFilter(); - trace(AL.getErrorString()); - - AL.filteri(filter, AL.FILTER_TYPE, AL.FILTER_LOWPASS); - trace(AL.getErrorString()); - - AL.filterf(filter, AL.LOWPASS_GAIN, 0.5); - trace(AL.getErrorString()); - - AL.filterf(filter, AL.LOWPASS_GAINHF, 0.5); - trace(AL.getErrorString()); - - AL.sourcei(handle, AL.DIRECT_FILTER, filter); - trace(AL.getErrorString()); */ - if (disposed) { trace("TRIED TO PLAY DISPOSED?? WTFFF"); trace(haxe.CallStack.toString(haxe.CallStack.callStack())); @@ -373,7 +354,7 @@ class NativeAudioSource } // Get & Set Methods - public function getCurrentTime():Int + public function getCurrentTime():Float { if (completed) { @@ -389,11 +370,11 @@ class NativeAudioSource } else { - var offset = AL.getSourcei(handle, AL.BYTE_OFFSET); + var offset:Int = AL.getSourcei(handle, AL.BYTE_OFFSET); var ratio = (offset / dataLength); var totalSeconds = samples / parent.buffer.sampleRate; - var time = Std.int(totalSeconds * ratio * 1000) - parent.offset; + var time = totalSeconds * ratio * 1000 - parent.offset; // var time = Std.int (AL.getSourcef (handle, AL.SEC_OFFSET) * 1000) - parent.offset; if (time < 0) return 0; @@ -404,7 +385,7 @@ class NativeAudioSource return 0; } - public function setCurrentTime(value:Int):Int + public function setCurrentTime(value:Float):Float { // `setCurrentTime()` has side effects and is never safe to skip. /* if (value == getCurrentTime()) @@ -434,7 +415,7 @@ class NativeAudioSource var totalSeconds = samples / parent.buffer.sampleRate; if (secondOffset < 0) secondOffset = 0; - if (secondOffset > totalSeconds) secondOffset = totalSeconds; + else if (secondOffset > totalSeconds) secondOffset = totalSeconds; var ratio = (secondOffset / totalSeconds); var totalOffset = Std.int(dataLength * ratio); @@ -451,7 +432,7 @@ class NativeAudioSource timer.stop(); } - var timeRemaining = Std.int((getLength() - value) / getPitch()); + var timeRemaining = (getLength() - value) / getPitch(); if (timeRemaining > 0) { @@ -491,17 +472,17 @@ class NativeAudioSource return value; } - public function getLength():Int + public function getLength():Float { if (length != null) { return length; } - return Std.int(samples / parent.buffer.sampleRate * 1000) - parent.offset; + return (samples / parent.buffer.sampleRate * 1000) - parent.offset; } - public function setLength(value:Int):Int + public function setLength(value:Float):Float { if (playing && length != value) { @@ -510,7 +491,7 @@ class NativeAudioSource timer.stop(); } - var timeRemaining = Std.int((value - getCurrentTime()) / getPitch()); + var timeRemaining = (value - getCurrentTime()) / getPitch(); if (timeRemaining > 0) { @@ -553,7 +534,7 @@ class NativeAudioSource timer.stop(); } - var timeRemaining = Std.int((getLength() - getCurrentTime()) / value); + var timeRemaining = (getLength() - getCurrentTime()) / value; if (timeRemaining > 0) { diff --git a/source/lime/media/AudioSource.hx b/source/lime/media/AudioSource.hx index ab0992e4..bc5948df 100644 --- a/source/lime/media/AudioSource.hx +++ b/source/lime/media/AudioSource.hx @@ -13,9 +13,9 @@ class AudioSource { public var onComplete = new EventVoid>(); public var buffer:AudioBuffer; - public var currentTime(get, set):Int; + public var currentTime(get, set):Float; public var gain(get, set):Float; - public var length(get, set):Int; + public var length(get, set):Float; public var loops(get, set):Int; public var pitch(get, set):Float; public var offset:Int; @@ -69,12 +69,12 @@ class AudioSource } // Get & Set Methods - @:noCompletion private function get_currentTime():Int + @:noCompletion private function get_currentTime():Float { return __backend.getCurrentTime(); } - @:noCompletion private function set_currentTime(value:Int):Int + @:noCompletion private function set_currentTime(value:Float):Float { return __backend.setCurrentTime(value); } @@ -89,12 +89,12 @@ class AudioSource return __backend.setGain(value); } - @:noCompletion private function get_length():Int + @:noCompletion private function get_length():Float { return __backend.getLength(); } - @:noCompletion private function set_length(value:Int):Int + @:noCompletion private function set_length(value:Float):Float { return __backend.setLength(value); } diff --git a/source/openfl/media/Sound.hx b/source/openfl/media/Sound.hx index bc073477..dfc475c1 100644 --- a/source/openfl/media/Sound.hx +++ b/source/openfl/media/Sound.hx @@ -599,16 +599,16 @@ class Sound extends EventDispatcher #end } - var changeID:Int = 0; - public function regen() { - var audioBuffer = new AudioBuffer(); + var changeID:Int = 0; + public function regen() { + var audioBuffer = new AudioBuffer(); audioBuffer.bitsPerSample = __buffer.bitsPerSample; audioBuffer.channels = __buffer.channels; audioBuffer.data = __buffer.data; audioBuffer.sampleRate = __buffer.sampleRate; __buffer = audioBuffer; - } + } /** Generates a new SoundChannel object to play back the sound. This method @@ -636,10 +636,10 @@ class Sound extends EventDispatcher return null; } - if (changeID < funkin.system.Main.changeID) { - changeID = funkin.system.Main.changeID; - regen(); - } + if (changeID < funkin.system.Main.changeID) { + changeID = funkin.system.Main.changeID; + regen(); + } if (sndTransform == null) { @@ -653,7 +653,7 @@ class Sound extends EventDispatcher var pan = SoundMixer.__soundTransform.pan + sndTransform.pan; if (pan > 1) pan = 1; - if (pan < -1) pan = -1; + else if (pan < -1) pan = -1; var volume = SoundMixer.__soundTransform.volume * sndTransform.volume; @@ -680,23 +680,29 @@ class Sound extends EventDispatcher return new ID3Info(); } - @:noCompletion private function get_length():Int + @:noCompletion private function get_length():Float { #if lime if (__buffer != null) { #if (js && html5 && howlerjs) - return Std.int(__buffer.src.duration() * 1000); + return __buffer.src.duration() * 1000; #else if (__buffer.data != null) { - var samples = (__buffer.data.length * 8) / (__buffer.channels * __buffer.bitsPerSample); - return Std.int(samples / __buffer.sampleRate * 1000); + // var samples = (__buffer.data.length * 8) / (__buffer.channels * __buffer.bitsPerSample); + // return Std.int(samples / __buffer.sampleRate * 1000); + var samples = (Int64.make(0, __buffer.data.length) * Int64.ofInt(8)) / Int64.ofInt(__buffer.channels * __buffer.bitsPerSample); + var value = samples / Int64.ofInt(__buffer.sampleRate) * Int64.ofInt(1000); + return Int64.toInt(value); } else if (__buffer.__srcVorbisFile != null) { - var samples = Int64.toInt(__buffer.__srcVorbisFile.pcmTotal()); - return Std.int(samples / __buffer.sampleRate * 1000); + //var samples = Int64.toInt(__buffer.__srcVorbisFile.pcmTotal()); + //return Std.int(samples / __buffer.sampleRate * 1000); + var samples = __buffer.__srcVorbisFile.pcmTotal(); + var value = Int64.fromFloat(__buffer.__srcVorbisFile.timeTotal()) * 1000; + return Int64.toInt(value); } else { diff --git a/source/openfl/media/SoundChannel.hx b/source/openfl/media/SoundChannel.hx new file mode 100644 index 00000000..f74d89b6 --- /dev/null +++ b/source/openfl/media/SoundChannel.hx @@ -0,0 +1,238 @@ +package openfl.media; + +#if !flash +import openfl.events.Event; +import openfl.events.EventDispatcher; +#if lime +import lime.media.AudioSource; +#end + +/** + The SoundChannel class controls a sound in an application. Every sound is + assigned to a sound channel, and the application can have multiple sound + channels that are mixed together. The SoundChannel class contains a + `stop()` method, properties for monitoring the amplitude + (volume) of the channel, and a property for assigning a SoundTransform + object to the channel. + + @event soundComplete Dispatched when a sound has finished playing. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(openfl.media.SoundMixer) +@:final @:keep class SoundChannel extends EventDispatcher +{ + /** + The current amplitude(volume) of the left channel, from 0(silent) to 1 + (full amplitude). + **/ + public var leftPeak(default, null):Float; + + /** + When the sound is playing, the `position` property indicates in + milliseconds the current point that is being played in the sound file. + When the sound is stopped or paused, the `position` property + indicates the last point that was played in the sound file. + + A common use case is to save the value of the `position` + property when the sound is stopped. You can resume the sound later by + restarting it from that saved position. + + If the sound is looped, `position` is reset to 0 at the + beginning of each loop. + **/ + public var position(get, set):Float; + + /** + The current amplitude(volume) of the right channel, from 0(silent) to 1 + (full amplitude). + **/ + public var rightPeak(default, null):Float; + + /** + The SoundTransform object assigned to the sound channel. A SoundTransform + object includes properties for setting volume, panning, left speaker + assignment, and right speaker assignment. + **/ + public var soundTransform(get, set):SoundTransform; + + public var pitch(get, set):Float; + + @:noCompletion private var __isValid:Bool; + @:noCompletion private var __soundTransform:SoundTransform; + #if lime + @:noCompletion private var __source:AudioSource; + #end + + #if openfljs + @:noCompletion private static function __init__() + { + untyped Object.defineProperties(SoundChannel.prototype, { + "position": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_position (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_position (v); }") + }, + "soundTransform": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_soundTransform (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_soundTransform (v); }") + }, + }); + } + #end + + @:noCompletion private function new(source:#if lime AudioSource #else Dynamic #end = null, soundTransform:SoundTransform = null):Void + { + super(this); + + leftPeak = 1; + rightPeak = 1; + + if (soundTransform != null) + { + __soundTransform = soundTransform; + } + else + { + __soundTransform = new SoundTransform(); + } + + #if lime + if (source != null) + { + __source = source; + __source.onComplete.add(source_onComplete); + __isValid = true; + + __source.play(); + } + #end + + SoundMixer.__registerSoundChannel(this); + } + + /** + Stops the sound playing in the channel. + **/ + public function stop():Void + { + SoundMixer.__unregisterSoundChannel(this); + + if (!__isValid) return; + + #if lime + __source.stop(); + #end + __dispose(); + } + + @:noCompletion private function __dispose():Void + { + if (!__isValid) return; + + #if lime + __source.onComplete.remove(source_onComplete); + __source.dispose(); + __source = null; + #end + __isValid = false; + } + + @:noCompletion private function __updateTransform():Void + { + this.soundTransform = soundTransform; + } + + // Get & Set Methods + @:noCompletion private function get_position():Float + { + if (!__isValid) return 0; + + #if lime + return __source.currentTime + __source.offset; + #else + return 0; + #end + } + + @:noCompletion private function set_position(value:Float):Float + { + if (!__isValid) return 0; + + #if lime + __source.currentTime = value - __source.offset; + #end + return value; + } + + @:noCompletion private function get_pitch():Float + { + if (!__isValid) return 1; + + #if lime + return __source.pitch; + #else + return 1; + #end + } + + @:noCompletion private function set_pitch(value:Float):Float + { + if (!__isValid) return 1; + + #if lime + __source.pitch = value; + #end + return value; + } + + @:noCompletion private function get_soundTransform():SoundTransform + { + return __soundTransform.clone(); + } + + @:noCompletion private function set_soundTransform(value:SoundTransform):SoundTransform + { + if (value != null) + { + __soundTransform.pan = value.pan; + __soundTransform.volume = value.volume; + + var pan = SoundMixer.__soundTransform.pan + __soundTransform.pan; + + if (pan < -1) pan = -1; + else if (pan > 1) pan = 1; + + var volume = SoundMixer.__soundTransform.volume * __soundTransform.volume; + + if (__isValid) + { + #if lime + __source.gain = volume; + + var position = __source.position; + position.x = pan; + position.z = -1 * Math.sqrt(1 - Math.pow(pan, 2)); + __source.position = position; + + return value; + #end + } + } + + return value; + } + + // Event Handlers + @:noCompletion private function source_onComplete():Void + { + SoundMixer.__unregisterSoundChannel(this); + + __dispose(); + dispatchEvent(new Event(Event.SOUND_COMPLETE)); + } +} +#else +typedef SoundChannel = flash.media.SoundChannel; +#end \ No newline at end of file From 66a6283167804fbf16ed3e2947044e2906b3254a Mon Sep 17 00:00:00 2001 From: NeeEoo Date: Fri, 13 Jan 2023 06:04:58 +0100 Subject: [PATCH 2/2] Fixed some bugs + Typo fixes Fixed newgrounds logo not showing and causing errors Small optimizations --- Project.xml | 26 +++++++------- assets/data/titlescreen/titlescreen.xml | 8 ++--- buildnumber.txt | 2 +- source/flx3d/Flx3DUtil.hx | 4 +-- source/flx3d/Flx3DView.hx | 4 +-- source/flx3d/FlxView3D.hx | 19 +++++------ .../funkin/desktop/editors/CharacterEditor.hx | 22 ++++++------ source/funkin/game/Note.hx | 3 -- source/funkin/menus/TitleState.hx | 19 +++++++++-- source/funkin/scripting/Script.hx | 14 ++++---- .../scripting/events/CancellableEvent.hx | 2 +- .../funkin/scripting/events/NoteHitEvent.hx | 18 +++++----- source/funkin/system/Controls.hx | 2 +- source/funkin/system/MusicBeatState.hx | 10 +++--- source/funkin/system/MusicBeatSubstate.hx | 34 +++++++++---------- source/funkin/system/Paths.hx | 2 +- source/funkin/system/Song.hx | 6 +--- source/funkin/ui/Alphabet.hx | 2 +- source/funkin/utils/CoolUtil.hx | 18 +++++----- 19 files changed, 110 insertions(+), 105 deletions(-) diff --git a/Project.xml b/Project.xml index b24a6fc7..ecc9439e 100644 --- a/Project.xml +++ b/Project.xml @@ -43,16 +43,16 @@ - + - + - + - + @@ -60,13 +60,13 @@ - + - + @@ -127,7 +127,7 @@ - + @@ -135,12 +135,12 @@ - + - + @@ -148,11 +148,11 @@ - + - + - +
@@ -160,7 +160,7 @@
- + diff --git a/assets/data/titlescreen/titlescreen.xml b/assets/data/titlescreen/titlescreen.xml index f6a3fc0b..763db8e4 100644 --- a/assets/data/titlescreen/titlescreen.xml +++ b/assets/data/titlescreen/titlescreen.xml @@ -17,7 +17,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -37,7 +37,7 @@ - + @@ -61,7 +61,7 @@ - + diff --git a/buildnumber.txt b/buildnumber.txt index 4d2b2566..37b767d4 100644 --- a/buildnumber.txt +++ b/buildnumber.txt @@ -1 +1 @@ -2042 \ No newline at end of file +2044 \ No newline at end of file diff --git a/source/flx3d/Flx3DUtil.hx b/source/flx3d/Flx3DUtil.hx index b4749cc1..53d6bf44 100644 --- a/source/flx3d/Flx3DUtil.hx +++ b/source/flx3d/Flx3DUtil.hx @@ -14,7 +14,7 @@ import flixel.FlxG; class Flx3DUtil { /** - * Returns the total amount of 3D stages (not exculding the ones in use) + * Returns the total amount of 3D stages (not excluding the ones in use) * @return Int */ public static inline function getTotal3D():Int @@ -38,7 +38,7 @@ class Flx3DUtil /** * Calls dispose() (destroy) on a Away3D asset - * @param obj + * @param obj * @return null */ public static inline function dispose(obj:Null):T diff --git a/source/flx3d/Flx3DView.hx b/source/flx3d/Flx3DView.hx index 3d414184..18bed7c1 100644 --- a/source/flx3d/Flx3DView.hx +++ b/source/flx3d/Flx3DView.hx @@ -44,7 +44,7 @@ class Flx3DView extends FlxView3D { var model = Assets.getBytes(assetPath); if (model == null) throw 'Model at ${assetPath} was not found.'; - + var context = new AssetLoaderContext(); var noExt = Path.withoutExtension(assetPath); trace(noExt); @@ -113,7 +113,7 @@ class Flx3DView extends FlxView3D { if (onAssetCallback != null) onAssetCallback(event); }); - + token.addEventListener(LoaderEvent.RESOURCE_COMPLETE, (_) -> { trace("Loader Finished..."); diff --git a/source/flx3d/FlxView3D.hx b/source/flx3d/FlxView3D.hx index 681ff900..416a87f8 100644 --- a/source/flx3d/FlxView3D.hx +++ b/source/flx3d/FlxView3D.hx @@ -7,10 +7,10 @@ import flixel.FlxSprite; import openfl.display.BitmapData; /** - * @author Ne_Eo + * @author Ne_Eo * @see https://twitter.com/Ne_Eo_Twitch - * - * @author lunarclient + * + * @author lunarcleint * @see https://twitter.com/lunarcleint */ class FlxView3D extends FlxSprite @@ -18,20 +18,20 @@ class FlxView3D extends FlxSprite @:noCompletion private var bmp:BitmapData; /** - * The Away3D View + * The Away3D View */ public var view:View3D; /** * Set this flag to true to force the View3D to update during the `draw()` call. */ - public var dirty3D:Bool = true; + public var dirty3D:Bool = true; /** * Creates a new instance of a View3D from Away3D and renders it as a FlxSprite * ! Call Flx3DUtil.is3DAvailable(); to make sure a 3D stage is usable - * @param x - * @param y + * @param x + * @param y * @param width Leave as -1 for screen width * @param height Leave as -1 for screen height */ @@ -54,7 +54,7 @@ class FlxView3D extends FlxSprite /** * Disposes (destroys) the asset and returns null - * @param obj + * @param obj * @return T null */ public static function dispose(obj:Null):T @@ -76,12 +76,11 @@ class FlxView3D extends FlxSprite bmp = null; } - if (view != null) + if (view != null) { view.dispose(); view = null; } - } @:noCompletion override function draw() diff --git a/source/funkin/desktop/editors/CharacterEditor.hx b/source/funkin/desktop/editors/CharacterEditor.hx index c986eb4a..e62b8aff 100644 --- a/source/funkin/desktop/editors/CharacterEditor.hx +++ b/source/funkin/desktop/editors/CharacterEditor.hx @@ -75,7 +75,7 @@ class CharacterEditor extends WindowContent { add(bg); add(stageFront); add(char); - + title = 'Character Editor - ${char.curCharacter}.xml'; // character setup & following @@ -143,13 +143,13 @@ class CharacterEditor extends WindowContent { callback: resetCamZoom }, { - name: "Show a Boyfriend Silouhette" + name: "Show a Boyfriend Silhouette" }, { - name: "Show a Dad Silouhette" + name: "Show a Dad Silhouette" }, { - name: "Show a Girlfriend Silouhette" + name: "Show a Girlfriend Silhouette" } ] }, @@ -197,7 +197,7 @@ class CharacterEditor extends WindowContent { content.push(animPrefixInput = new InputBox(10, label.y + label.height, 380, "")); content.push(animLoopCheckbox = new Checkbox(10, animPrefixInput.y + animPrefixInput.height + 10, 380, "Loop")); - + content.push(label = new WindowText(10, animLoopCheckbox.y + animLoopCheckbox.height + 10, 0, "Animation FPS (Frames per second)")); content.push(animFpsStepper = new NumericStepper(10, label.y + label.height, 380, 0)); @@ -207,7 +207,7 @@ class CharacterEditor extends WindowContent { for(e in [animOffsetX, animOffsetY]) e.increment = 10; - + for(spr in content) tabView.tabs[0].add(spr); } @@ -221,18 +221,18 @@ class CharacterEditor extends WindowContent { content.push(label = new WindowText(10, content.last().y + content.last().height, 0, "Icon Name (images/icons/)")); content.push(charIconInput = new InputBox(10, label.y + label.height, 380, char.icon.getDefault(""))); - + content.push(charIsPlayerCheckbox = new Checkbox(10, content.last().y + content.last().height + 10, 380, "Playable character")); content.push(label = new WindowText(10, content.last().y + content.last().height, 380, "(Offsets are automatically fixed when playing as a non-playable character and vice versa)")); charIsPlayerCheckbox.checked = char.xml.getAtt("isPlayer") == "true"; - + content.push(charIsGFCheckbox = new Checkbox(10, content.last().y + content.last().height + 10, 380, "Is Girlfriend")); content.push(label = new WindowText(10, content.last().y + content.last().height, 380, "(Characters marked as Girlfriend will replace GF when used as opponent)")); charIsGFCheckbox.checked = char.isGF; - + content.push(charFlipXCheckbox = new Checkbox(10, content.last().y + content.last().height + 10, 380, "Flip Horizontally")); charFlipXCheckbox.checked = char.__baseFlipped != char.isPlayer; - + for(spr in content) tabView.tabs[1].add(spr); } @@ -306,7 +306,7 @@ class CharacterEditor extends WindowContent { /** * =========== CONTEXT MENU OPTIONS =========== */ - + /** * View */ diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx index 5336849d..cfb7be14 100644 --- a/source/funkin/game/Note.hx +++ b/source/funkin/game/Note.hx @@ -11,9 +11,6 @@ import flixel.math.FlxRect; import flixel.math.FlxPoint; import funkin.system.Conductor; import funkin.scripting.events.*; -#if polymod -import polymod.format.ParseRules.TargetSignatureElement; -#end using StringTools; diff --git a/source/funkin/menus/TitleState.hx b/source/funkin/menus/TitleState.hx index b3766a10..67fa72c6 100644 --- a/source/funkin/menus/TitleState.hx +++ b/source/funkin/menus/TitleState.hx @@ -92,6 +92,7 @@ class TitleState extends MusicBeatState blackScreen = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); add(blackScreen); + #if !TITLESCREEN_XML ngSpr = new FlxSprite(0, FlxG.height * 0.52).loadAnimatedGraphic(Paths.image('newgrounds_logo')); add(ngSpr); ngSpr.visible = false; @@ -99,6 +100,7 @@ class TitleState extends MusicBeatState ngSpr.updateHitbox(); ngSpr.screenCenter(X); ngSpr.antialiasing = true; + #end FlxG.mouse.visible = false; @@ -213,7 +215,10 @@ class TitleState extends MusicBeatState public function deleteCoolText() { - while (textGroup.members.length > 0) textGroup.remove(textGroup.members[0], true); + while (textGroup.members.length > 0) { + textGroup.members[0].destroy(); + textGroup.remove(textGroup.members[0], true); + } } override function beatHit(curBeat:Int) @@ -258,7 +263,7 @@ class TitleState extends MusicBeatState 5 => new IntroText(['In association', 'with']), 7 => new IntroText(['In association', 'with', 'newgrounds', { name: "newgroundsLogo", - path: "titlescreen/newgrounds_logo", + path: "menus/titlescreen/newgrounds_logo", scale: 0.8 }]), 8 => new IntroText(), @@ -330,7 +335,9 @@ class TitleState extends MusicBeatState { if (!skippedIntro) { + #if !TITLESCREEN_XML remove(ngSpr); + #end FlxG.camera.flash(FlxColor.WHITE, 4); remove(blackScreen); @@ -363,7 +370,13 @@ class IntroText { var scale:Float = CoolUtil.getDefault(image.scale, 1); - var sprite = new FlxSprite(0, 200).loadAnimatedGraphic(Paths.image(image.path)); + var yPos:Float = 200; + if(state.textGroup.members.length > 0) { + var lastLine:FlxSprite = cast state.textGroup.members[state.textGroup.members.length-1]; + yPos = lastLine.y + lastLine.height + 10; + } + + var sprite = new FlxSprite(0, yPos).loadAnimatedGraphic(Paths.image(image.path)); sprite.flipX = CoolUtil.getDefault(image.flipX, false); sprite.flipY = CoolUtil.getDefault(image.flipY, false); sprite.scale.set(scale, scale); diff --git a/source/funkin/scripting/Script.hx b/source/funkin/scripting/Script.hx index 9a39a702..843b2dc4 100644 --- a/source/funkin/scripting/Script.hx +++ b/source/funkin/scripting/Script.hx @@ -12,7 +12,7 @@ import openfl.utils.Assets; class Script extends FlxBasic implements IFlxDestroyable { /** * Use "static var thing = true;" in hscript to use those!! - * are reset every mod switch so once youre done with them make sure to make them null!! + * are reset every mod switch so once you're done with them make sure to make them null!! */ public static var staticVariables:Map = []; @@ -155,7 +155,7 @@ class Script extends FlxBasic implements IFlxDestroyable { /** * Creates a new instance of the script class. - * @param path + * @param path */ public function new(path:String) { super(); @@ -184,14 +184,14 @@ class Script extends FlxBasic implements IFlxDestroyable { * Sets the "public" variables map for ScriptPack */ public function setPublicMap(map:Map) { - + } /** * Hot-reloads the script, if possible */ public function reload() { - + } /** @@ -204,7 +204,7 @@ class Script extends FlxBasic implements IFlxDestroyable { ], TRACE); } - + /** * Calls the function `func` defined in the script. * @param func Name of the function @@ -216,13 +216,13 @@ class Script extends FlxBasic implements IFlxDestroyable { curScript = this; var result = onCall(func, parameters == null ? [] : parameters); - + curScript = oldScript; return result; } /** - * Sets a script's parent object so that its properties can be accessed easily. Ex: Passing `PlayState.instace` will allow `boyfriend` to be typed instead of `PlayState.instance.boyfriend`. + * Sets a script's parent object so that its properties can be accessed easily. Ex: Passing `PlayState.instance` will allow `boyfriend` to be typed instead of `PlayState.instance.boyfriend`. * @param variable Parent variable. */ public function setParent(variable:Dynamic) {} diff --git a/source/funkin/scripting/events/CancellableEvent.hx b/source/funkin/scripting/events/CancellableEvent.hx index 5c0f651e..ecb6f84c 100644 --- a/source/funkin/scripting/events/CancellableEvent.hx +++ b/source/funkin/scripting/events/CancellableEvent.hx @@ -14,7 +14,7 @@ class CancellableEvent implements IFlxDestroyable { public var data:Dynamic = {}; /** - * Prevents default action from occuring. + * Prevents default action from occurring. * @param c Whenever the scripts following this one should be called or not. (Defaults to `true`) */ public function preventDefault(c:Bool = false) { diff --git a/source/funkin/scripting/events/NoteHitEvent.hx b/source/funkin/scripting/events/NoteHitEvent.hx index 951383bd..e7aafb7e 100644 --- a/source/funkin/scripting/events/NoteHitEvent.hx +++ b/source/funkin/scripting/events/NoteHitEvent.hx @@ -12,16 +12,16 @@ class NoteHitEvent extends CancellableEvent { @:dox(hide) public var autoHitLastSustain:Bool = true; /** - Whenever a miss should be added. - **/ + * Whenever a miss should be added. + */ public var misses:Bool = true; /** - Whenever a miss should be added. - **/ + * Whenever a miss should be added. + */ public var countAsCombo:Bool = true; /** - Whenever ratings should be shown or not. - **/ + * Whenever ratings should be shown or not. + */ public var showRating:Null = null; /** * Note that has been pressed @@ -141,7 +141,7 @@ class NoteHitEvent extends CancellableEvent { } @:dox(hide) public function cancelCamZooming() {preventCamZooming();} - + /** * Prevents the sustain tail (the last one) from being automatically hit when the sustain before it is hit. */ @@ -150,7 +150,7 @@ class NoteHitEvent extends CancellableEvent { } @:dox(hide) public function cancelLastSustainHit() {preventLastSustainHit();} - + /** * Prevents the strum from glowing after this note has been pressed. */ @@ -160,7 +160,7 @@ class NoteHitEvent extends CancellableEvent { @:dox(hide) public function cancelStrumGlow() {preventStrumGlow();} - private inline function get_character() + private inline function get_character() return characters[0]; private function set_character(char:Character) { characters = [char]; diff --git a/source/funkin/system/Controls.hx b/source/funkin/system/Controls.hx index 25d6fc9b..2466146d 100644 --- a/source/funkin/system/Controls.hx +++ b/source/funkin/system/Controls.hx @@ -55,7 +55,7 @@ enum Device /** * Since, in many cases multiple actions should use similar keys, we don't want the - * rebinding UI to list every action. ActionBinders are what the user percieves as + * rebinding UI to list every action. ActionBinders are what the user perceives as * an input so, for instance, they can't set jump-press and jump-release to different keys. */ enum Control diff --git a/source/funkin/system/MusicBeatState.hx b/source/funkin/system/MusicBeatState.hx index 64d572b7..fb618d09 100644 --- a/source/funkin/system/MusicBeatState.hx +++ b/source/funkin/system/MusicBeatState.hx @@ -86,7 +86,7 @@ class MusicBeatState extends FlxUIState implements IBeatReceiver if (stateScript == null || stateScript is DummyScript) { var className = Type.getClassName(Type.getClass(this)); var scriptName = this.scriptName != null ? this.scriptName : className.substr(className.lastIndexOf(".")+1); - + stateScript = Script.create(Paths.script('data/states/${scriptName}')); stateScript.setParent(this); stateScript.load(); @@ -144,7 +144,7 @@ class MusicBeatState extends FlxUIState implements IBeatReceiver Logs.trace('State script successfully reloaded', WARNING, GREEN); } call("update", [elapsed]); - + super.update(elapsed); } @@ -165,7 +165,7 @@ class MusicBeatState extends FlxUIState implements IBeatReceiver * @param v1 Value 1 * @param v2 Value 2 * @param ratio Ratio - * @param fpsSensitive Whenever the ratio should not be adjusted to run at the same speed independant of framerate. + * @param fpsSensitive Whenever the ratio should not be adjusted to run at the same speed independent of framerate. */ public function lerp(v1:Float, v2:Float, ratio:Float, fpsSensitive:Bool = false) { if (fpsSensitive) @@ -175,7 +175,7 @@ class MusicBeatState extends FlxUIState implements IBeatReceiver } /** - * SCRIPTING STUFF + * SCRIPTING STUFF */ public override function openSubState(subState:FlxSubState) { var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); @@ -191,7 +191,7 @@ class MusicBeatState extends FlxUIState implements IBeatReceiver public override function destroy() { if (parentWindow == null) super.destroy(); - + call("onDestroy"); if (stateScript != null) stateScript.destroy(); diff --git a/source/funkin/system/MusicBeatSubstate.hx b/source/funkin/system/MusicBeatSubstate.hx index 06de0add..714bb0d0 100644 --- a/source/funkin/system/MusicBeatSubstate.hx +++ b/source/funkin/system/MusicBeatSubstate.hx @@ -17,7 +17,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver private var lastBeat:Float = 0; private var lastStep:Float = 0; - + /** * Current step */ @@ -37,19 +37,19 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver /** * Current song position (in milliseconds). */ - public var songPos(get, never):Float; - - inline function get_curStep():Int - return Conductor.curStep; - inline function get_curBeat():Int - return Conductor.curBeat; - inline function get_curStepFloat():Float - return Conductor.curStepFloat; - inline function get_curBeatFloat():Float - return Conductor.curBeatFloat; - inline function get_songPos():Float - return Conductor.songPosition; - + public var songPos(get, never):Float; + + inline function get_curStep():Int + return Conductor.curStep; + inline function get_curBeat():Int + return Conductor.curBeat; + inline function get_curStepFloat():Float + return Conductor.curStepFloat; + inline function get_curBeatFloat():Float + return Conductor.curBeatFloat; + inline function get_songPos():Float + return Conductor.songPosition; + /** * Current injected script attached to the state. To add one, create a file at path "data/states/stateName" (ex: "data/states/PauseMenuSubstate.hx") @@ -81,7 +81,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver if (stateScript == null || stateScript is DummyScript) { var className = Type.getClassName(Type.getClass(this)); var scriptName = this.scriptName != null ? this.scriptName : className.substr(className.lastIndexOf(".")+1); - + stateScript = Script.create(Paths.script('data/states/${scriptName}')); stateScript.setParent(this); stateScript.load(); @@ -168,7 +168,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver * @param v1 Value 1 * @param v2 Value 2 * @param ratio Ratio - * @param fpsSensitive Whenever the ratio should not be adjusted to run at the same speed independant of framerate. + * @param fpsSensitive Whenever the ratio should not be adjusted to run at the same speed independent of framerate. */ public function lerp(v1:Float, v2:Float, ratio:Float, fpsSensitive:Bool = false) { if (fpsSensitive) @@ -178,7 +178,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver } /** - * SCRIPTING STUFF + * SCRIPTING STUFF */ public override function openSubState(subState:FlxSubState) { var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); diff --git a/source/funkin/system/Paths.hx b/source/funkin/system/Paths.hx index a0c2779a..274c0cbd 100644 --- a/source/funkin/system/Paths.hx +++ b/source/funkin/system/Paths.hx @@ -254,7 +254,7 @@ class Paths var content:Array = []; #if MOD_SUPPORT if (library is funkin.mods.ModsAssetLibrary) { - // easy task, can immediatly scan for files! + // easy task, can immediately scan for files! var lib = cast(library, funkin.mods.ModsAssetLibrary); content = lib.getFiles(libThing.symbolName); if (addPath) diff --git a/source/funkin/system/Song.hx b/source/funkin/system/Song.hx index 82d1d65b..0f06360b 100644 --- a/source/funkin/system/Song.hx +++ b/source/funkin/system/Song.hx @@ -34,11 +34,7 @@ class Song PlayState.fromMods = Paths.assetsTree.existsSpecific(assetPath, "TEXT", MODS); var rawJson = Assets.getText(assetPath).trim(); - while (!rawJson.endsWith("}")) - { - rawJson = rawJson.substr(0, rawJson.length - 1); - // LOL GOING THROUGH THE BULLSHIT TO CLEAN IDK WHATS STRANGE - } + rawJson = rawJson.substr(0, rawJson.lastIndexOf('}') + 1); return parseJSONshit(rawJson); } diff --git a/source/funkin/ui/Alphabet.hx b/source/funkin/ui/Alphabet.hx index b52376a9..da67c575 100644 --- a/source/funkin/ui/Alphabet.hx +++ b/source/funkin/ui/Alphabet.hx @@ -16,7 +16,7 @@ import flixel.util.FlxColor; using StringTools; /** - * Loosley based on FlxTypeText lolol + * Loosely based on FlxTypeText lolol */ class Alphabet extends FlxSpriteGroup { diff --git a/source/funkin/utils/CoolUtil.hx b/source/funkin/utils/CoolUtil.hx index 7bf5f982..c2d6f8af 100644 --- a/source/funkin/utils/CoolUtil.hx +++ b/source/funkin/utils/CoolUtil.hx @@ -61,7 +61,7 @@ class CoolUtil var f:Null = Reflect.field(v, name); if (f != null) return cast f; - } + } Reflect.setField(v, name, defaultValue); return defaultValue; } @@ -90,7 +90,7 @@ class CoolUtil * @param color1 Color 1 * @param color2 Color 2 * @param ratio Ratio - * @param fpsSensitive Whenever the ratio should be case sentivive (adapted when game is running at 120 instead of 60) + * @param fpsSensitive Whenever the ratio should be fps sensitive (adapted when game is running at 120 instead of 60) */ public static inline function lerpColor(color1:FlxColor, color2:FlxColor, ratio:Float, fpsSensitive:Bool = false) { if (!fpsSensitive) @@ -114,7 +114,7 @@ class CoolUtil public static function getColorFromDynamic(c:Dynamic):Null { // -1 if (c is Int) return c; - + // -1.0 if (c is Float) return Std.int(c); @@ -263,9 +263,9 @@ class CoolUtil } public static function loadWeek(weekData:WeekData, difficulty:String = "normal") { - PlayState.storyWeek = weekData; - PlayState.storyPlaylist = [for(e in weekData.songs) e.name]; - PlayState.isStoryMode = true; + PlayState.storyWeek = weekData; + PlayState.storyPlaylist = [for(e in weekData.songs) e.name]; + PlayState.isStoryMode = true; PlayState.campaignScore = 0; __loadSong(PlayState.storyPlaylist[0], difficulty); } @@ -331,10 +331,10 @@ class CoolUtil return null; return graph.imageFrame; } - + public static function loadAnimatedGraphic(spr:FlxSprite, path:String) { spr.frames = loadFrames(path); - + if (spr.frames != null && spr.frames.frames != null) { spr.animation.add("idle", [for(i in 0...spr.frames.frames.length) i], 24, true); spr.animation.play("idle"); @@ -376,7 +376,7 @@ class CoolUtil public static function clear(array:Array):Array { while(array.length > 0) array.shift(); - return array; + return array; } public static function pushGroup(array:Array, ...args:T):Array {