diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml index 11cecd138..7115f2f6d 100644 --- a/.github/workflows/build-shit.yml +++ b/.github/workflows/build-shit.yml @@ -27,6 +27,7 @@ jobs: - uses: ./.github/actions/setup-haxeshit - name: Build game? run: | + sudo apt-get install -y libx11-dev libxinerama-dev libxrandr-dev libgl1-mesa-dev libgl-dev libxi-dev libxext-dev libasound2-dev haxelib run lime build html5 -debug --times ls - uses: ./.github/actions/upload-itch diff --git a/.vscode/settings.json b/.vscode/settings.json index d7433698d..dd4cd7aef 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,84 +1,120 @@ { - "[haxe]": { - // Automatically keep Haxe files formatted. - "editor.formatOnSave": true, - "editor.formatOnPaste": true, - "editor.codeActionsOnSave": { - // Compilation server issues can cause auto-cleanup to remove valid imports. - "source.organizeImports": false - }, - "editor.defaultFormatter": "nadako.vshaxe", - "editor.tabSize": 2 + "[haxe]": { + // Automatically keep Haxe files formatted. + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.codeActionsOnSave": { + // Compilation server issues can cause auto-cleanup to remove valid imports. + "source.organizeImports": false }, - - "[json]": { - // Automatically keep JSON files formatted. - "editor.formatOnSave": true, - "editor.formatOnPaste": true, - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "nadako.vshaxe", + "editor.tabSize": 2 + }, + + "[json]": { + // Automatically keep JSON files formatted. + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + + "[jsonc]": { + // Automatically keep JSONC files formatted. + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "prettier.tabWidth": 2, + + // XML formatting style configuration + "xml.format.enabled": true, + "xml.format.legacy": false, + "xml.format.emptyElements": "collapse", + "xml.preferences.quoteStyle": "double", + "xml.format.enforceQuoteStyle": "preferred", + "xml.format.preserveAttributeLineBreaks": false, + "xml.format.preservedNewlines": 0, + "xml.format.splitAttributes": false, + "xml.format.joinCDATALines": true, + "xml.format.preserveEmptyContent": false, + "xml.format.joinCommentLines": false, + "xml.format.joinContentLines": false, + "xml.format.spaceBeforeEmptyCloseTag": true, + "xml.format.xsiSchemaLocationSplit": "onPair", + "xml.format.splitAttributesIndentSize": 2, + "xml.format.closingBracketNewLine": false, + "xml.format.preserveSpace": [ + "xsl:text", + "xsl:comment", + "xsl:processing-instruction", + "literallayout", + "programlisting", + "screen", + "synopsis", + "pre", + "xd:pre" + ], + "xml.format.maxLineWidth": 0, + "xml.format.grammarAwareFormatting": true, + + // Generic file formatting style configuration + "files.insertFinalNewline": true, + "files.trimFinalNewlines": false, + "files.trimTrailingWhitespace": true, + + // Automatically detect indentation. + "editor.detectIndentation": true, + "editor.insertSpaces": true, + "editor.tabSize": 2, + + // Automatically enforce Linux style line endings. + "files.eol": "\n", + + "haxe.displayPort": "auto", + "haxe.enableCompilationServer": true, + "haxe.displayServer": { + "arguments": ["-v"] + }, + // Fix file associations for HScript. + "files.associations": { + "*.hxp": "haxe", + "*.hscript": "haxe", + "*.haxe": "haxe", + "*.hxs": "haxe", + "*.hxc": "haxe" + }, + "projectManager.git.baseFolders": ["./"], + + "haxecheckstyle.sourceFolders": ["src", "Source"], + "haxecheckstyle.externalSourceRoots": [], + "haxecheckstyle.configurationFile": "checkstyle.json", + "haxecheckstyle.codeSimilarityBufferSize": 100, + + "lime.targetConfigurations": [ + { + "label": "Windows / Debug", + "target": "windows", + "args": ["-debug"] }, - - "[jsonc]": { - // Automatically keep JSONC files formatted. - "editor.formatOnSave": true, - "editor.formatOnPaste": true, - "editor.defaultFormatter": "esbenp.prettier-vscode" + { + "label": "Windows / Debug (DEBUG ASSETS)", + "target": "windows", + "args": ["-debug", "-DDEBUG_ASSETS"] }, - "prettier.tabWidth": 2, - - // Automatically detect indentation. - "editor.detectIndentation": true, - "editor.insertSpaces": true, - "editor.tabSize": 2, - - // Automatically enforce Linux style line endings. - "files.eol": "\n", - - "haxe.displayPort": "auto", - "haxe.enableCompilationServer": true, - "haxe.displayServer": { - "arguments": ["-v"] + { + "label": "Windows / Debug (ANIMATE)", + "target": "windows", + "args": ["-debug", "-DANIMATE"] }, - // Fix file associations for HScript. - "files.associations": { - "*.hxp": "haxe", - "*.hscript": "haxe", - "*.haxe": "haxe", - "*.hxs": "haxe", - "*.hxc": "haxe" + { + "label": "HTML5 / Debug", + "target": "html5", + "args": ["-debug"] }, - "projectManager.git.baseFolders": ["./"], - - "haxecheckstyle.sourceFolders": ["src", "Source"], - "haxecheckstyle.externalSourceRoots": [], - "haxecheckstyle.configurationFile": "checkstyle.json", - "haxecheckstyle.codeSimilarityBufferSize": 100, - - "lime.targetConfigurations": [ - { - "label": "Windows / Debug", - "target": "windows", - "args": ["-debug"] - }, - { - "label": "Windows / Debug (DEBUG ASSETS)", - "target": "windows", - "args": ["-debug", "-DDEBUG_ASSETS"] - }, - { - "label": "Windows / Debug (ANIMATE)", - "target": "windows", - "args": ["-debug", "-DANIMATE"] - }, - { - "label": "HTML5 / Debug", - "target": "html5", - "args": ["-debug"] - }, - { - "label": "HTML5 / Debug (Watch)", - "target": "html5", - "args": ["-debug", "-watch"] - } - ] - } \ No newline at end of file + { + "label": "HTML5 / Debug (Watch)", + "target": "html5", + "args": ["-debug", "-watch"] + } + ] +} diff --git a/Project.xml b/Project.xml index 972749939..2d9dd802b 100644 --- a/Project.xml +++ b/Project.xml @@ -1,48 +1,33 @@ - - - - - - - - - - - - - - -
@@ -56,7 +41,6 @@
-
@@ -70,7 +54,6 @@
- @@ -93,9 +76,7 @@ - - - - - - - - + - - - + - - - + + - + - - - - - - + - + + + - - - - - - + + - - - - - - - - - - + + + - - -
- -
- -
- -
- - -
@@ -247,14 +200,12 @@
- -
+
- - - + +
- \ No newline at end of file + diff --git a/example_mods/introMod/_polymod_meta.json b/example_mods/introMod/_polymod_meta.json index 4c7fd742e..e0b03f1cd 100644 --- a/example_mods/introMod/_polymod_meta.json +++ b/example_mods/introMod/_polymod_meta.json @@ -1,9 +1,11 @@ { "title": "Intro Mod", "description": "An introductory mod.", - "contributors": [{ - "name": "MasterEric" - }], + "contributors": [ + { + "name": "MasterEric" + } + ], "api_version": "0.1.0", "mod_version": "1.0.0", "license": "Apache-2.0" diff --git a/example_mods/testing123/_polymod_meta.json b/example_mods/testing123/_polymod_meta.json index 1a7766820..4c0f177f9 100644 --- a/example_mods/testing123/_polymod_meta.json +++ b/example_mods/testing123/_polymod_meta.json @@ -1,9 +1,11 @@ { "title": "Testing123", "description": "Newgrounds? More like OLDGROUNDS lol.", - "contributors": [{ - "name": "MasterEric" - }], + "contributors": [ + { + "name": "MasterEric" + } + ], "api_version": "0.1.0", "mod_version": "1.0.0", "license": "Apache-2.0" diff --git a/hmm.json b/hmm.json index e92868545..f45a94b08 100644 --- a/hmm.json +++ b/hmm.json @@ -65,10 +65,10 @@ "version": "2.5.0" }, { - "name": "hxcodec", + "name": "hxCodec", "type": "git", "dir": null, - "ref": "91adeec", + "ref": "c42ab99", "url": "https://github.com/polybiusproxy/hxCodec" }, { @@ -95,7 +95,7 @@ "name": "lime", "type": "git", "dir": null, - "ref": "deecd6c", + "ref": "5634ad7", "url": "https://github.com/openfl/lime" }, { @@ -118,4 +118,4 @@ "version": "0.2.2" } ] -} \ No newline at end of file +} diff --git a/source/funkin/Alphabet.hx b/source/funkin/Alphabet.hx index adf97933a..a501707be 100644 --- a/source/funkin/Alphabet.hx +++ b/source/funkin/Alphabet.hx @@ -125,8 +125,7 @@ class Alphabet extends FlxSpriteGroup var xPos:Float = 0; var curRow:Int = 0; - new FlxTimer().start(0.05, function(tmr:FlxTimer) - { + new FlxTimer().start(0.05, function(tmr:FlxTimer) { // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); if (_finalText.fastCodeAt(loopNum) == "\n".code) { diff --git a/source/funkin/ButtonRemapSubstate.hx b/source/funkin/ButtonRemapSubstate.hx index 376cc4d54..8905ec8ba 100644 --- a/source/funkin/ButtonRemapSubstate.hx +++ b/source/funkin/ButtonRemapSubstate.hx @@ -2,7 +2,7 @@ package funkin; import flixel.FlxSubState; -class ButtonRemapSubstate extends FlxSubState +class ButtonRemapSubState extends FlxSubState { public function new() { diff --git a/source/funkin/ComboCounter.hx b/source/funkin/ComboCounter.hx index ac8606691..28aed4560 100644 --- a/source/funkin/ComboCounter.hx +++ b/source/funkin/ComboCounter.hx @@ -27,8 +27,7 @@ class ComboCounter extends FlxTypedSpriteGroup effectStuff.animation.addByPrefix('funny', 'NOTE COMBO animation', 24, false); effectStuff.animation.play('funny'); effectStuff.antialiasing = true; - effectStuff.animation.finishCallback = function(nameThing) - { + effectStuff.animation.finishCallback = function(nameThing) { kill(); }; effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7)); @@ -42,8 +41,7 @@ class ComboCounter extends FlxTypedSpriteGroup { if (onScreenTime < 0.9) { - new FlxTimer().start((Conductor.crochet / 1000) * 0.25, function(tmr) - { + new FlxTimer().start((Conductor.crochet / 1000) * 0.25, function(tmr) { forceFinish(); }); } @@ -64,16 +62,14 @@ class ComboCounter extends FlxTypedSpriteGroup if (effectStuff.animation.curAnim.curFrame == 18) { - grpNumbers.forEach(function(spr:ComboNumber) - { + grpNumbers.forEach(function(spr:ComboNumber) { spr.animation.reset(); }); } if (effectStuff.animation.curAnim.curFrame == 20) { - grpNumbers.forEach(function(spr:ComboNumber) - { + grpNumbers.forEach(function(spr:ComboNumber) { spr.kill(); }); } diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx index a0493869b..e31b6d501 100644 --- a/source/funkin/Conductor.hx +++ b/source/funkin/Conductor.hx @@ -50,6 +50,16 @@ class Conductor // OLD, replaced with timeChanges. public static var bpmChangeMap:Array = []; + /** + * Duration of a measure in milliseconds. Calculated based on bpm. + */ + public static var measureLengthMs(get, null):Float; + + static function get_measureLengthMs():Float + { + return crochet * timeSignatureNumerator; + } + /** * Duration of a beat in millisecond. Calculated based on bpm. */ @@ -149,9 +159,9 @@ class Conductor /** * Forcibly defines the current BPM of the song. * Useful for things like the chart editor that need to manipulate BPM in real time. - * + * * Set to null to reset to the BPM defined by the timeChanges. - * + * * WARNING: Avoid this for things like setting the BPM of the title screen music, * you should have a metadata file for it instead. */ @@ -166,7 +176,7 @@ class Conductor /** * Update the conductor with the current song position. * BPM, current step, etc. will be re-calculated based on the song position. - * + * * @param songPosition The current position in the song in milliseconds. * Leave blank to use the FlxG.sound.music position. */ diff --git a/source/funkin/CoolUtil.hx b/source/funkin/CoolUtil.hx index c7d3f7dab..93fa937da 100644 --- a/source/funkin/CoolUtil.hx +++ b/source/funkin/CoolUtil.hx @@ -20,13 +20,6 @@ import openfl.filters.ShaderFilter; class CoolUtil { - public static var difficultyArray:Array = ['EASY', "NORMAL", "HARD"]; - - public static function difficultyString():String - { - return difficultyArray[PlayState.storyDifficulty]; - } - public static function coolBaseLog(base:Float, fin:Float):Float { return Math.log(fin) / Math.log(base); @@ -119,8 +112,7 @@ class CoolUtil FlxTween.tween(screenWipeShit, {daAlphaShit: 1}, time, { ease: FlxEase.quadInOut, - onComplete: function(twn) - { + onComplete: function(twn) { screenShit.destroy(); FlxG.switchState(new MainMenuState()); } @@ -130,7 +122,7 @@ class CoolUtil /** * Just saves the json with some default values hehe - * @param json + * @param json * @return String */ public static inline function jsonStringify(data:Dynamic):String diff --git a/source/funkin/CutsceneAnimTestState.hx b/source/funkin/CutsceneAnimTestState.hx deleted file mode 100644 index 7d6369c12..000000000 --- a/source/funkin/CutsceneAnimTestState.hx +++ /dev/null @@ -1,80 +0,0 @@ -package funkin; - -import flixel.FlxSprite; -import flixel.FlxState; -import flixel.addons.display.FlxGridOverlay; -import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.math.FlxPoint; -import flixel.text.FlxText; -import flixel.util.FlxColor; -import openfl.Assets; -import openfl.display.BitmapData; -import openfl.display.MovieClip; -import openfl.display.Timeline; -import openfl.geom.Matrix; -import openfl.geom.Rectangle; - -class CutsceneAnimTestState extends FlxState -{ - var cutsceneGroup:CutsceneCharacter; - - var curSelected:Int = 0; - var debugTxt:FlxText; - - var funnySprite:FlxSprite = new FlxSprite(); - var clip:MovieClip; - - public function new() - { - super(); - - var gridBG:FlxSprite = FlxGridOverlay.create(10, 10); - gridBG.scrollFactor.set(0.5, 0.5); - add(gridBG); - - debugTxt = new FlxText(900, 20, 0, "", 20); - debugTxt.color = FlxColor.BLUE; - add(debugTxt); - - clip = Assets.getMovieClip("tanky:"); - // clip.x = FlxG.width/2; - // clip.y = FlxG.height/2; - FlxG.stage.addChild(clip); - - var swagShit:MovieClip = Assets.getMovieClip('tankBG:'); - // swagShit.scaleX = 5; - - FlxG.stage.addChild(swagShit); - swagShit.gotoAndStop(13); - - var swfMountain = new BitmapData(FlxG.width, FlxG.height, true, 0x00000000); - swfMountain.draw(swagShit, swagShit.transform.matrix); - - var mountains:FlxSprite = new FlxSprite().loadGraphic(swfMountain); - // add(mountains); - - FlxG.stage.removeChild(swagShit); - - funnySprite.x = FlxG.width / 2; - funnySprite.y = FlxG.height / 2; - add(funnySprite); - } - - override function update(elapsed:Float) - { - super.update(elapsed); - - // jam sprite into top left corner - var drawMatrix:Matrix = clip.transform.matrix; - var bounds:Rectangle = clip.getBounds(null); - drawMatrix.tx = -bounds.x; - drawMatrix.ty = -bounds.y; - // make bitmapdata only as big as it needs to be - var funnyBmp:BitmapData = new BitmapData(Math.ceil(bounds.width), Math.ceil(bounds.height), true, 0x00000000); - funnyBmp.draw(clip, drawMatrix, true); - funnySprite.loadGraphic(funnyBmp); - // jam sprite back into place lol - funnySprite.offset.x = -bounds.x; - funnySprite.offset.y = -bounds.y; - } -} diff --git a/source/funkin/CutsceneCharacter.hx b/source/funkin/CutsceneCharacter.hx deleted file mode 100644 index 67810bc30..000000000 --- a/source/funkin/CutsceneCharacter.hx +++ /dev/null @@ -1,75 +0,0 @@ -package funkin; - -import flixel.FlxSprite; -import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.math.FlxPoint; - -class CutsceneCharacter extends FlxTypedGroup -{ - public var coolPos:FlxPoint = FlxPoint.get(); - public var animShit:Map = new Map(); - - var imageShit:String; - - public function new(x:Float, y:Float, imageShit:String) - { - super(); - - coolPos.set(x, y); - - this.imageShit = imageShit; - parseOffsets(); - createCutscene(0); - } - - // shitshow, oh well - var arrayLMFAOOOO:Array = []; - - function parseOffsets() - { - var splitShit:Array = CoolUtil.coolTextFile(Paths.file('images/cutsceneStuff/' + imageShit + "CutsceneOffsets.txt")); - - for (i in splitShit) - { - var xAndY:FlxPoint = FlxPoint.get(); - var dumbSplit:Array = i.split('---')[1].trim().split(' '); - trace('cool split: ' + i.split('---')[1]); - trace(dumbSplit); - xAndY.set(Std.parseFloat(dumbSplit[0]), Std.parseFloat(dumbSplit[1])); - - animShit.set(i.split('---')[0].trim(), xAndY); - arrayLMFAOOOO.push(i.split('---')[0].trim()); - } - - trace(animShit); - } - - public function createCutscene(daNum:Int = 0) - { - var cutScene:FlxSprite = new FlxSprite(coolPos.x + animShit.get(arrayLMFAOOOO[daNum]).x, coolPos.y + animShit.get(arrayLMFAOOOO[daNum]).y); - cutScene.frames = Paths.getSparrowAtlas('cutsceneStuff/' + imageShit + "-" + daNum); - cutScene.animation.addByPrefix('weed', arrayLMFAOOOO[daNum], 24, false); - cutScene.animation.play('weed'); - cutScene.antialiasing = true; - - cutScene.animation.finishCallback = function(anim:String) - { - cutScene.kill(); - cutScene.destroy(); - cutScene = null; - - if (daNum + 1 < arrayLMFAOOOO.length) createCutscene(daNum + 1); - else - ended(); - }; - - add(cutScene); - } - - public var onFinish:Void->Void; - - public function ended():Void - { - if (onFinish != null) onFinish(); - } -} diff --git a/source/funkin/DialogueBox.hx b/source/funkin/DialogueBox.hx index e9c3587ad..4258f71ce 100644 --- a/source/funkin/DialogueBox.hx +++ b/source/funkin/DialogueBox.hx @@ -38,7 +38,7 @@ class DialogueBox extends FlxSpriteGroup { super(); - switch (PlayState.currentSong.song.toLowerCase()) + switch (PlayState.instance.currentSong.songId.toLowerCase()) { case 'senpai': FlxG.sound.playMusic(Paths.music('Lunchbox'), 0); @@ -53,8 +53,7 @@ class DialogueBox extends FlxSpriteGroup bgFade.alpha = 0; add(bgFade); - new FlxTimer().start(0.83, function(tmr:FlxTimer) - { + new FlxTimer().start(0.83, function(tmr:FlxTimer) { bgFade.alpha += (1 / 5) * 0.7; if (bgFade.alpha > 0.7) bgFade.alpha = 0.7; }, 5); @@ -80,7 +79,7 @@ class DialogueBox extends FlxSpriteGroup box = new FlxSprite(-20, 45); var hasDialog:Bool = false; - switch (PlayState.currentSong.song.toLowerCase()) + switch (PlayState.instance.currentSong.songId.toLowerCase()) { case 'senpai': hasDialog = true; @@ -152,8 +151,8 @@ class DialogueBox extends FlxSpriteGroup override function update(elapsed:Float):Void { // HARD CODING CUZ IM STUPDI - if (PlayState.currentSong.song.toLowerCase() == 'roses') portraitLeft.visible = false; - if (PlayState.currentSong.song.toLowerCase() == 'thorns') + if (PlayState.instance.currentSong.songId.toLowerCase() == 'roses') portraitLeft.visible = false; + if (PlayState.instance.currentSong.songId.toLowerCase() == 'thorns') { portraitLeft.color = FlxColor.BLACK; swagDialogue.color = FlxColor.WHITE; @@ -189,11 +188,10 @@ class DialogueBox extends FlxSpriteGroup { isEnding = true; - if (PlayState.currentSong.song.toLowerCase() == 'senpai' - || PlayState.currentSong.song.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0); + if (PlayState.instance.currentSong.songId.toLowerCase() == 'senpai' + || PlayState.instance.currentSong.songId.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0); - new FlxTimer().start(0.2, function(tmr:FlxTimer) - { + new FlxTimer().start(0.2, function(tmr:FlxTimer) { box.alpha -= 1 / 5; bgFade.alpha -= 1 / 5 * 0.7; portraitLeft.visible = false; @@ -203,8 +201,7 @@ class DialogueBox extends FlxSpriteGroup dropText.alpha = swagDialogue.alpha; }, 5); - new FlxTimer().start(1.2, function(tmr:FlxTimer) - { + new FlxTimer().start(1.2, function(tmr:FlxTimer) { finishThing(); kill(); }); @@ -233,8 +230,7 @@ class DialogueBox extends FlxSpriteGroup // swagDialogue.text = ; swagDialogue.resetText(dialogueList[0]); swagDialogue.start(0.04); - swagDialogue.completeCallback = function() - { + swagDialogue.completeCallback = function() { trace('dialogue finish'); handSelect.visible = true; dialogueEnded = true; diff --git a/source/funkin/Discord.hx b/source/funkin/Discord.hx index 26a96ab52..4fb6e9dcf 100644 --- a/source/funkin/Discord.hx +++ b/source/funkin/Discord.hx @@ -58,8 +58,7 @@ class DiscordClient public static function initialize() { - var DiscordDaemon = sys.thread.Thread.create(() -> - { + var DiscordDaemon = sys.thread.Thread.create(() -> { new DiscordClient(); }); trace("Discord Client initialized"); diff --git a/source/funkin/FreeplayState.hx b/source/funkin/FreeplayState.hx index fb2119c55..322e79e31 100644 --- a/source/funkin/FreeplayState.hx +++ b/source/funkin/FreeplayState.hx @@ -34,12 +34,14 @@ import funkin.play.song.SongData.SongDataParser; import funkin.shaderslmfao.AngleMask; import funkin.shaderslmfao.PureColor; import funkin.shaderslmfao.StrokeShader; +import funkin.play.PlayStatePlaylist; +import funkin.play.song.Song; import lime.app.Future; import lime.utils.Assets; -class FreeplayState extends MusicBeatSubstate +class FreeplayState extends MusicBeatSubState { - var songs:Array = []; + var songs:Array = []; // var selector:FlxText; var curSelected:Int = 0; @@ -112,15 +114,15 @@ class FreeplayState extends MusicBeatSubstate #if debug isDebug = true; - addSong('Test', 1, 'bf-pixel'); - addSong('Pyro', 8, 'darnell'); + addSong('Test', 'tutorial', 'bf-pixel'); + addSong('Pyro', 'weekend1', 'darnell'); #end var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist')); for (i in 0...initSonglist.length) { - songs.push(new SongMetadata(initSonglist[i], 1, 'gf')); + songs.push(new FreeplaySongData(initSonglist[i], 'tutorial', 'gf')); } if (FlxG.sound.music != null) @@ -128,22 +130,28 @@ class FreeplayState extends MusicBeatSubstate if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu')); } - if (StoryMenuState.weekUnlocked[2] || isDebug) addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 1, ['dad']); + // if (StoryMenuState.weekUnlocked[2] || isDebug) + addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 'week1', ['dad']); - if (StoryMenuState.weekUnlocked[2] || isDebug) addWeek(['Spookeez', 'South', 'Monster'], 2, ['spooky', 'spooky', 'monster']); + // if (StoryMenuState.weekUnlocked[2] || isDebug) + addWeek(['Spookeez', 'South', 'Monster'], 'week2', ['spooky', 'spooky', 'monster']); - if (StoryMenuState.weekUnlocked[3] || isDebug) addWeek(['Pico', 'Philly', 'Blammed'], 3, ['pico']); + // if (StoryMenuState.weekUnlocked[3] || isDebug) + addWeek(['Pico', 'Philly-Nice', 'Blammed'], 'week3', ['pico']); - if (StoryMenuState.weekUnlocked[4] || isDebug) addWeek(['Satin-Panties', 'High', 'Milf'], 4, ['mom']); + // if (StoryMenuState.weekUnlocked[4] || isDebug) + addWeek(['Satin-Panties', 'High', 'MILF'], 'week4', ['mom']); - if (StoryMenuState.weekUnlocked[5] || isDebug) addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 5, - ['parents-christmas', 'parents-christmas', 'monster-christmas']); + // if (StoryMenuState.weekUnlocked[5] || isDebug) + addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 'week5', ['parents-christmas', 'parents-christmas', 'monster-christmas']); - if (StoryMenuState.weekUnlocked[6] || isDebug) addWeek(['Senpai', 'Roses', 'Thorns'], 6, ['senpai', 'senpai', 'spirit']); + // if (StoryMenuState.weekUnlocked[6] || isDebug) + addWeek(['Senpai', 'Roses', 'Thorns'], 'week6', ['senpai', 'senpai', 'spirit']); - if (StoryMenuState.weekUnlocked[7] || isDebug) addWeek(['Ugh', 'Guns', 'Stress'], 7, ['tankman']); + // if (StoryMenuState.weekUnlocked[7] || isDebug) + addWeek(['Ugh', 'Guns', 'Stress'], 'week7', ['tankman']); - addWeek(["Darnell", "lit-up", "2hot", "blazin"], 8, ['darnell']); + addWeek(["Darnell", "lit-up", "2hot", "blazin"], 'weekend1', ['darnell']); // LOAD MUSIC @@ -426,7 +434,7 @@ class FreeplayState extends MusicBeatSubstate var swag:Alphabet = new Alphabet(1, 0, "swag"); // JUST DOIN THIS SHIT FOR TESTING!!! - /* + /* var md:String = Markdown.markdownToHtml(Assets.getText('CHANGELOG.md')); var texFel:TextField = new TextField(); @@ -464,7 +472,7 @@ class FreeplayState extends MusicBeatSubstate grpCapsules.clear(); // var regexp:EReg = regexp; - var tempSongs:Array = songs; + var tempSongs:Array = songs; if (filterStuff != null) { @@ -553,19 +561,19 @@ class FreeplayState extends MusicBeatSubstate changeDiff(); } - public function addSong(songName:String, weekNum:Int, songCharacter:String) + public function addSong(songName:String, levelId:String, songCharacter:String) { - songs.push(new SongMetadata(songName, weekNum, songCharacter)); + songs.push(new FreeplaySongData(songName, levelId, songCharacter)); } - public function addWeek(songs:Array, weekNum:Int, ?songCharacters:Array) + public function addWeek(songs:Array, levelId:String, ?songCharacters:Array) { if (songCharacters == null) songCharacters = ['bf']; var num:Int = 0; for (song in songs) { - addSong(song, weekNum, songCharacters[num]); + addSong(song, levelId, songCharacters[num]); if (songCharacters.length != 1) num++; } @@ -851,11 +859,9 @@ class FreeplayState extends MusicBeatSubstate curDifficulty = 1; }*/ - PlayState.currentSong = SongLoad.loadFromJson(poop, songs[curSelected].songName.toLowerCase()); - PlayState.currentSong_NEW = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase()); - PlayState.isStoryMode = false; - PlayState.storyDifficulty = curDifficulty; - PlayState.storyDifficulty_NEW = switch (curDifficulty) + PlayStatePlaylist.isStoryMode = false; + var targetSong:Song = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase()); + var targetDifficulty:String = switch (curDifficulty) { case 0: 'easy'; @@ -865,27 +871,41 @@ class FreeplayState extends MusicBeatSubstate 'hard'; default: 'normal'; }; - // SongLoad.curDiff = Highscore.formatSong() - SongLoad.curDiff = PlayState.storyDifficulty_NEW; + // TODO: Implement additional difficulties into the interface properly. + if (FlxG.keys.pressed.E) + { + targetDifficulty = 'erect'; + } - PlayState.storyWeek = songs[curSelected].week; - // trace(' CUR WEEK ' + PlayState.storyWeek); + // TODO: Implement Pico into the interface properly. + var targetCharacter:String = 'bf'; + if (FlxG.keys.pressed.P) + { + targetCharacter = 'pico'; + } + + PlayStatePlaylist.campaignId = songs[curSelected].levelId; // Visual and audio effects. FlxG.sound.play(Paths.sound('confirmMenu')); dj.confirm(); new FlxTimer().start(1, function(tmr:FlxTimer) { - LoadingState.loadAndSwitchState(new PlayState(), true); + LoadingState.loadAndSwitchState(new PlayState( + { + targetSong: targetSong, + targetDifficulty: targetDifficulty, + targetCharacter: targetCharacter, + }), true); }); } } - override function startOutro(onComplete:() -> Void):Void + override function switchTo(nextState:FlxState):Bool { clearDaCache(songs[curSelected].songName); - super.startOutro(onComplete); + return super.switchTo(nextState); } function changeDiff(change:Int = 0) @@ -901,19 +921,6 @@ class FreeplayState extends MusicBeatSubstate intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty); intendedCompletion = Highscore.getCompletion(songs[curSelected].songName, curDifficulty); - PlayState.storyDifficulty = curDifficulty; - PlayState.storyDifficulty_NEW = switch (curDifficulty) - { - case 0: - 'easy'; - case 1: - 'normal'; - case 2: - 'hard'; - default: - 'normal'; - }; - grpDifficulties.group.forEach(function(spr) { spr.visible = false; }); @@ -1040,17 +1047,17 @@ enum abstract FilterType(String) var ALL; } -class SongMetadata +class FreeplaySongData { public var songName:String = ""; - public var week:Int = 0; + public var levelId:String = ""; public var songCharacter:String = ""; public var isFav:Bool = false; - public function new(song:String, week:Int, songCharacter:String, ?isFav:Bool = false) + public function new(song:String, levelId:String, songCharacter:String, ?isFav:Bool = false) { this.songName = song; - this.week = week; + this.levelId = levelId; this.songCharacter = songCharacter; this.isFav = isFav; } diff --git a/source/funkin/GitarooPause.hx b/source/funkin/GitarooPause.hx index fda809548..5747de5e5 100644 --- a/source/funkin/GitarooPause.hx +++ b/source/funkin/GitarooPause.hx @@ -11,12 +11,16 @@ class GitarooPause extends MusicBeatState var replaySelect:Bool = false; - public function new():Void + var previousParams:PlayStateParams; + + public function new(previousParams:PlayStateParams):Void { super(); + + this.previousParams = previousParams; } - override function create() + override function create():Void { if (FlxG.sound.music != null) FlxG.sound.music.stop(); @@ -49,7 +53,7 @@ class GitarooPause extends MusicBeatState super.create(); } - override function update(elapsed:Float) + override function update(elapsed:Float):Void { if (controls.UI_LEFT_P || controls.UI_RIGHT_P) changeThing(); @@ -57,7 +61,7 @@ class GitarooPause extends MusicBeatState { if (replaySelect) { - FlxG.switchState(new PlayState()); + FlxG.switchState(new PlayState(previousParams)); } else { diff --git a/source/funkin/Highscore.hx b/source/funkin/Highscore.hx index 08ad7dcba..904d2cb45 100644 --- a/source/funkin/Highscore.hx +++ b/source/funkin/Highscore.hx @@ -39,7 +39,17 @@ class Highscore return false; } - public static function saveCompletion(song:String, completion:Float, ?diff:Int = 0):Bool + public static function saveScoreForDifficulty(song:String, score:Int = 0, diff:String = 'normal'):Bool + { + var diffInt:Int = 1; + + if (diff == 'easy') diffInt = 0; + else if (diff == 'hard') diffInt = 2; + + return saveScore(song, score, diffInt); + } + + public static function saveCompletion(song:String, completion:Float, diff:Int = 0):Bool { var formattedSong:String = formatSong(song, diff); @@ -57,20 +67,42 @@ class Highscore return false; } - public static function saveWeekScore(week:Int = 1, score:Int = 0, ?diff:Int = 0):Void + public static function saveCompletionForDifficulty(song:String, completion:Float, diff:String = 'normal'):Bool + { + var diffInt:Int = 1; + + if (diff == 'easy') diffInt = 0; + else if (diff == 'hard') diffInt = 2; + + return saveCompletion(song, completion, diffInt); + } + + public static function saveWeekScore(week:String, score:Int = 0, diff:Int = 0):Void { #if newgrounds - NGio.postScore(score, "Week " + week); + NGio.postScore(score, 'Campaign ID $week'); #end - var formattedSong:String = formatSong('week' + week, diff); + var formattedSong:String = formatSong(week, diff); if (songScores.exists(formattedSong)) { if (songScores.get(formattedSong) < score) setScore(formattedSong, score); } else + { setScore(formattedSong, score); + } + } + + public static function saveWeekScoreForDifficulty(week:String, score:Int = 0, diff:String = 'normal'):Void + { + var diffInt:Int = 1; + + if (diff == 'easy') diffInt = 0; + else if (diff == 'hard') diffInt = 2; + + saveWeekScore(week, score, diffInt); } static function setCompletion(formattedSong:String, completion:Float):Void @@ -122,7 +154,7 @@ class Highscore return songCompletion.get(formatSong(song, diff)); } - public static function getAllScores() + public static function getAllScores():Void { trace(songScores.toString()); } diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index 45c2645df..8d7d2d550 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -12,7 +12,7 @@ import flixel.util.FlxColor; import funkin.modding.module.ModuleHandler; import funkin.play.PlayState; import funkin.play.character.CharacterData.CharacterDataParser; -import funkin.play.event.SongEvent.SongEventParser; +import funkin.play.event.SongEventData.SongEventParser; import funkin.play.song.SongData.SongDataParser; import funkin.ui.PreferencesMenu; import funkin.util.WindowUtil; @@ -140,10 +140,10 @@ class InitState extends FlxTransitionableState // WEEK UNLOCK PROGRESSION!! // StoryMenuState.weekUnlocked = FlxG.save.data.weekUnlocked; - if (StoryMenuState.weekUnlocked.length < 4) StoryMenuState.weekUnlocked.insert(0, true); + // if (StoryMenuState.weekUnlocked.length < 4) StoryMenuState.weekUnlocked.insert(0, true); // QUICK PATCH OOPS! - if (!StoryMenuState.weekUnlocked[0]) StoryMenuState.weekUnlocked[0] = true; + // if (!StoryMenuState.weekUnlocked[0]) StoryMenuState.weekUnlocked[0] = true; } if (FlxG.save.data.seenVideo != null) VideoState.seenVideo = FlxG.save.data.seenVideo; @@ -237,20 +237,18 @@ class InitState extends FlxTransitionableState { var dif:Int = getDif(); - PlayState.currentSong = SongLoad.loadFromJson(song, song); - PlayState.currentSong_NEW = SongDataParser.fetchSong(song); - PlayState.isStoryMode = isStoryMode; - PlayState.storyDifficulty = dif; - PlayState.storyDifficulty_NEW = switch (dif) + var targetDifficulty = switch (dif) { case 0: 'easy'; case 1: 'normal'; case 2: 'hard'; default: 'normal'; }; - SongLoad.curDiff = PlayState.storyDifficulty_NEW; - PlayState.storyWeek = week; - LoadingState.loadAndSwitchState(new PlayState()); + LoadingState.loadAndSwitchState(new PlayState( + { + targetSong: SongDataParser.fetchSong(song), + targetDifficulty: targetDifficulty, + })); } } diff --git a/source/funkin/LatencyState.hx b/source/funkin/LatencyState.hx index ad803b963..694e9c3e5 100644 --- a/source/funkin/LatencyState.hx +++ b/source/funkin/LatencyState.hx @@ -14,7 +14,7 @@ import funkin.ui.CoolStatsGraph; import haxe.Timer; import openfl.events.KeyboardEvent; -class LatencyState extends MusicBeatSubstate +class LatencyState extends MusicBeatSubState { var offsetText:FlxText; var noteGrp:FlxTypedGroup; diff --git a/source/funkin/LoadingState.hx b/source/funkin/LoadingState.hx index 316041fd4..604e78f79 100644 --- a/source/funkin/LoadingState.hx +++ b/source/funkin/LoadingState.hx @@ -1,5 +1,6 @@ package funkin; +import funkin.play.PlayStatePlaylist; import flixel.FlxSprite; import flixel.FlxState; import flixel.math.FlxMath; @@ -32,7 +33,7 @@ class LoadingState extends MusicBeatState this.stopMusic = stopMusic; } - override function create() + override function create():Void { var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, 0xFFcaff4d); add(bg); @@ -50,63 +51,59 @@ class LoadingState extends MusicBeatState loadBar.screenCenter(X); add(loadBar); - initSongsManifest().onComplete(function(lib) - { + initSongsManifest().onComplete(function(lib) { callbacks = new MultiCallback(onLoad); - var introComplete = callbacks.add("introComplete"); - checkLoadSong(getSongPath()); - if (PlayState.currentSong.needsVoices) - { - var files = PlayState.currentSong.voiceList; + var introComplete = callbacks.add('introComplete'); + // checkLoadSong(getSongPath()); + // if (PlayState.currentSong.needsVoices) + // { + // var files = PlayState.currentSong.voiceList; + // + // if (files == null) files = ['']; // loads with no file name assumption, to load 'Voices.ogg' or whatev normally + // + // for (sndFile in files) + // { + // checkLoadSong(getVocalPath(sndFile)); + // } + // } - if (files == null) files = [""]; // loads with no file name assumption, to load "Voices.ogg" or whatev normally + checkLibrary('shared'); + checkLibrary(PlayStatePlaylist.campaignId); + checkLibrary('tutorial'); - for (sndFile in files) - { - checkLoadSong(getVocalPath(sndFile)); - } - } - - checkLibrary("shared"); - if (PlayState.storyWeek > 0) checkLibrary("week" + PlayState.storyWeek); - else - checkLibrary("tutorial"); - - var fadeTime = 0.5; + var fadeTime:Float = 0.5; FlxG.camera.fade(FlxG.camera.bgColor, fadeTime, true); new FlxTimer().start(fadeTime + MIN_TIME, function(_) introComplete()); }); } - function checkLoadSong(path:String) + function checkLoadSong(path:String):Void { if (!Assets.cache.hasSound(path)) { - var library = Assets.getLibrary("songs"); - var symbolPath = path.split(":").pop(); + var library = Assets.getLibrary('songs'); + var symbolPath = path.split(':').pop(); // @:privateAccess // library.types.set(symbolPath, SOUND); // @:privateAccess // library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]); - var callback = callbacks.add("song:" + path); - Assets.loadSound(path).onComplete(function(_) - { + var callback = callbacks.add('song:' + path); + Assets.loadSound(path).onComplete(function(_) { callback(); }); } } - function checkLibrary(library:String) + function checkLibrary(library:String):Void { trace(Assets.hasLibrary(library)); if (Assets.getLibrary(library) == null) { @:privateAccess - if (!LimeAssets.libraryPaths.exists(library)) throw "Missing library: " + library; + if (!LimeAssets.libraryPaths.exists(library)) throw 'Missing library: ' + library; - var callback = callbacks.add("library:" + library); - Assets.loadLibrary(library).onComplete(function(_) - { + var callback = callbacks.add('library:' + library); + Assets.loadLibrary(library).onComplete(function(_) { callback(); }); } @@ -124,7 +121,7 @@ class LoadingState extends MusicBeatState var targetShit:Float = 0; - override function update(elapsed:Float) + override function update(elapsed:Float):Void { super.update(elapsed); @@ -150,57 +147,41 @@ class LoadingState extends MusicBeatState } #if debug - if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + " unfired:" + callbacks.getUnfired()); + if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + ' unfired:' + callbacks.getUnfired()); #end } - function onLoad() + function onLoad():Void { if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); FlxG.switchState(target); } - static function getSongPath() + static function getSongPath():String { - return Paths.inst(PlayState.currentSong.song); + return Paths.inst(PlayState.instance.currentSong.songId); } - static function getVocalPath(?suffix:String) + inline static public function loadAndSwitchState(nextState:FlxState, shouldStopMusic = false):Void { - return Paths.voices(PlayState.currentSong.song, suffix); + FlxG.switchState(getNextState(nextState, shouldStopMusic)); } - inline static public function loadAndSwitchState(target:FlxState, stopMusic = false) + static function getNextState(nextState:FlxState, shouldStopMusic = false):FlxState { - FlxG.switchState(getNextState(target, stopMusic)); - } + Paths.setCurrentLevel(PlayStatePlaylist.campaignId); - static function getNextState(target:FlxState, stopMusic = false):FlxState - { - if (PlayState.storyWeek == 0) - { - Paths.setCurrentLevel('tutorial'); - } - else if (PlayState.storyWeek == 8) - { - // TODO: Refactor this code. - Paths.setCurrentLevel("weekend1"); - } - else - { - Paths.setCurrentLevel("week" + PlayState.storyWeek); - } #if NO_PRELOAD_ALL - var loaded = isSoundLoaded(getSongPath()) - && (!PlayState.currentSong.needsVoices || isSoundLoaded(getVocalPath())) - && isLibraryLoaded("shared"); - - if (!loaded) return new LoadingState(target, stopMusic); + // var loaded = isSoundLoaded(getSongPath()) + // && (!PlayState.currentSong.needsVoices || isSoundLoaded(getVocalPath())) + // && isLibraryLoaded('shared'); + // + if (true) return new LoadingState(nextState, shouldStopMusic); #end - if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); + if (shouldStopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); - return target; + return nextState; } #if NO_PRELOAD_ALL @@ -215,16 +196,16 @@ class LoadingState extends MusicBeatState } #end - override function destroy() + override function destroy():Void { super.destroy(); callbacks = null; } - static function initSongsManifest() + static function initSongsManifest():Future { - var id = "songs"; + var id = 'songs'; var promise = new Promise(); var library = LimeAssets.getLibrary(id); @@ -246,10 +227,10 @@ class LoadingState extends MusicBeatState } else { - if (path.endsWith(".bundle")) + if (path.endsWith('.bundle')) { rootPath = path; - path += "/library.json"; + path += '/library.json'; } else { @@ -259,11 +240,10 @@ class LoadingState extends MusicBeatState path = LimeAssets.__cacheBreak(path); } - AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest) - { + AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest) { if (manifest == null) { - promise.error("Cannot parse asset manifest for library \"" + id + "\""); + promise.error('Cannot parse asset manifest for library \'' + id + '\''); return; } @@ -271,7 +251,7 @@ class LoadingState extends MusicBeatState if (library == null) { - promise.error("Cannot open library \"" + id + "\""); + promise.error('Cannot open library \'' + id + '\''); } else { @@ -280,9 +260,8 @@ class LoadingState extends MusicBeatState library.onChange.add(LimeAssets.onChange.dispatch); promise.completeWith(Future.withValue(library)); } - }).onError(function(_) - { - promise.error("There is no asset library with an ID of \"" + id + "\""); + }).onError(function(_) { + promise.error('There is no asset library with an ID of \'' + id + '\''); }); return promise.future; @@ -305,14 +284,13 @@ class MultiCallback this.logId = logId; } - public function add(id = "untitled") + public function add(id = 'untitled'):Void->Void { id = '$length:$id'; length++; numRemaining++; var func:Void->Void = null; - func = function() - { + func = function() { if (unfired.exists(id)) { unfired.remove(id); @@ -339,9 +317,9 @@ class MultiCallback if (logId != null) trace('$logId: $msg'); } - public function getFired() + public function getFired():Array return fired.copy(); - public function getUnfired() + public function getUnfired():ArrayVoid> return unfired.array(); } diff --git a/source/funkin/MainMenuState.hx b/source/funkin/MainMenuState.hx index e4bdfbe35..57640bffa 100644 --- a/source/funkin/MainMenuState.hx +++ b/source/funkin/MainMenuState.hx @@ -223,8 +223,8 @@ class MainMenuState extends MusicBeatState /** * Calls openPrompt and redraws the login/logout button - * @param prompt - * @param onClose + * @param prompt + * @param onClose */ public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void) { diff --git a/source/funkin/MemoryCounter.hx b/source/funkin/MemoryCounter.hx index 5ef09b332..658febe59 100644 --- a/source/funkin/MemoryCounter.hx +++ b/source/funkin/MemoryCounter.hx @@ -24,8 +24,7 @@ class MemoryCounter extends TextField text = "RAM: "; #if flash - addEventListener(Event.ENTER_FRAME, function(e) - { + addEventListener(Event.ENTER_FRAME, function(e) { var time = Lib.getTimer(); __enterFrame(time - currentTime); }); diff --git a/source/funkin/MusicBeatState.hx b/source/funkin/MusicBeatState.hx index 3614695c0..6c6591c62 100644 --- a/source/funkin/MusicBeatState.hx +++ b/source/funkin/MusicBeatState.hx @@ -35,8 +35,8 @@ class MusicBeatState extends FlxUIState function initCallbacks() { - subStateOpened.add(onOpenSubstateComplete); - subStateClosed.add(onCloseSubstateComplete); + subStateOpened.add(onOpenSubStateComplete); + subStateClosed.add(onCloseSubStateComplete); } override function create() @@ -162,18 +162,18 @@ class MusicBeatState extends FlxUIState } } - public override function openSubState(targetSubstate:FlxSubState):Void + public override function openSubState(targetSubState:FlxSubState):Void { - var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_BEGIN, targetSubstate, true); + var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_BEGIN, targetSubState, true); dispatchEvent(event); if (event.eventCanceled) return; - super.openSubState(targetSubstate); + super.openSubState(targetSubState); } - function onOpenSubstateComplete(targetState:FlxSubState):Void + function onOpenSubStateComplete(targetState:FlxSubState):Void { dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_END, targetState, true)); } @@ -189,7 +189,7 @@ class MusicBeatState extends FlxUIState super.closeSubState(); } - function onCloseSubstateComplete(targetState:FlxSubState):Void + function onCloseSubStateComplete(targetState:FlxSubState):Void { dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_CLOSE_END, targetState, true)); } diff --git a/source/funkin/MusicBeatSubstate.hx b/source/funkin/MusicBeatSubState.hx similarity index 94% rename from source/funkin/MusicBeatSubstate.hx rename to source/funkin/MusicBeatSubState.hx index 802022a67..440e25c96 100644 --- a/source/funkin/MusicBeatSubstate.hx +++ b/source/funkin/MusicBeatSubState.hx @@ -7,9 +7,9 @@ import funkin.modding.events.ScriptEvent; import funkin.modding.module.ModuleHandler; /** - * MusicBeatSubstate reincorporates the functionality of MusicBeatState into an FlxSubState. + * MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState. */ -class MusicBeatSubstate extends FlxSubState +class MusicBeatSubState extends FlxSubState { public function new(bgColor:FlxColor = FlxColor.TRANSPARENT) { diff --git a/source/funkin/NGio.hx b/source/funkin/NGio.hx index f538c9656..f2afe84db 100644 --- a/source/funkin/NGio.hx +++ b/source/funkin/NGio.hx @@ -49,8 +49,7 @@ class NGio trace('checking NG.io version'); GAME_VER = "v" + Application.current.meta.get('version'); - NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response) - { + NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response) { GAME_VER = response.result.data.currentVersion; trace('CURRENT NG VERSION: ' + GAME_VER); callback(GAME_VER); @@ -141,8 +140,7 @@ class NGio var onCancel:Void->Void = null; if (onComplete != null) { - onSuccess = function() - { + onSuccess = function() { onNGLogin(); onComplete(Success); } @@ -228,7 +226,7 @@ class NGio scoreboardsLoaded = true; ngScoresLoaded.dispatch(); - /* + /* for (score in NG.core.scoreBoards.get(8737).scores) { trace('score loaded user:${score.user.name}, score:${score.formatted_value}'); diff --git a/source/funkin/Note.hx b/source/funkin/Note.hx index 7fc49717e..71c63a94e 100644 --- a/source/funkin/Note.hx +++ b/source/funkin/Note.hx @@ -1,5 +1,6 @@ package funkin; +import funkin.play.Strumline.StrumlineArrow; import flixel.FlxSprite; import flixel.math.FlxMath; import funkin.noteStuff.NoteBasic.NoteData; @@ -15,9 +16,9 @@ class Note extends FlxSprite public var data = new NoteData(); /** - * code colors for.... code.... + * code colors for.... code.... * i think goes in order of left to right - * + * * left 0 * down 1 * up 2 @@ -215,6 +216,24 @@ class Note extends FlxSprite } } + public function alignToSturmlineArrow(arrow:StrumlineArrow):Void + { + x = arrow.x; + + if (isSustainNote && prevNote != null) + { + if (prevNote.isSustainNote) + { + x = prevNote.x; + } + else + { + x += prevNote.width / 2; + x -= width / 2; + } + } + } + override function destroy() { prevNote = null; diff --git a/source/funkin/NoteSplash.hx b/source/funkin/NoteSplash.hx index ea4242679..7f3a8c5e4 100644 --- a/source/funkin/NoteSplash.hx +++ b/source/funkin/NoteSplash.hx @@ -34,8 +34,7 @@ class NoteSplash extends FlxSprite animation.play('note' + noteData + '-' + FlxG.random.int(0, 1), true); animation.curAnim.frameRate = 24 + FlxG.random.int(-2, 2); - animation.finishCallback = function(name) - { + animation.finishCallback = function(name) { kill(); }; updateHitbox(); diff --git a/source/funkin/PauseSubState.hx b/source/funkin/PauseSubState.hx index 67b58f41d..77fdfabf1 100644 --- a/source/funkin/PauseSubState.hx +++ b/source/funkin/PauseSubState.hx @@ -1,9 +1,10 @@ package funkin; +import funkin.play.PlayStatePlaylist; import flixel.FlxSprite; import flixel.addons.transition.FlxTransitionableState; import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.sound.FlxSound; +import flixel.system.FlxSound; import flixel.text.FlxText; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; @@ -11,7 +12,7 @@ import flixel.util.FlxColor; import funkin.play.PlayState; import funkin.play.song.SongData.SongDataParser; -class PauseSubState extends MusicBeatSubstate +class PauseSubState extends MusicBeatSubState { var grpMenuShit:FlxTypedGroup; @@ -20,9 +21,9 @@ class PauseSubState extends MusicBeatSubstate 'Restart Song', 'Change Difficulty', 'Toggle Practice Mode', - 'Exit to menu' + 'Exit to Menu' ]; - var difficultyChoices:Array = ['EASY', 'NORMAL', 'HARD', 'BACK']; + var difficultyChoices:Array = ['EASY', 'NORMAL', 'HARD', 'ERECT', 'BACK']; var menuItems:Array = []; var curSelected:Int = 0; @@ -41,10 +42,14 @@ class PauseSubState extends MusicBeatSubstate menuItems = pauseOG; - if (PlayState.storyWeek == 6) // consistent with logic that decides asset lib!! + if (PlayStatePlaylist.campaignId == 'week6') + { pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast-pixel'), true, true); + } else + { pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast'), true, true); + } pauseMusic.volume = 0; pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2))); @@ -58,43 +63,38 @@ class PauseSubState extends MusicBeatSubstate metaDataGrp = new FlxTypedGroup(); add(metaDataGrp); - var levelInfo:FlxText = new FlxText(20, 15, 0, "", 32); + var levelInfo:FlxText = new FlxText(20, 15, 0, '', 32); if (PlayState.instance.currentChart != null) { levelInfo.text += '${PlayState.instance.currentChart.songName} - ${PlayState.instance.currentChart.songArtist}'; } - else - { - levelInfo.text += PlayState.currentSong.song; - } levelInfo.scrollFactor.set(); - levelInfo.setFormat(Paths.font("vcr.ttf"), 32); + levelInfo.setFormat(Paths.font('vcr.ttf'), 32); levelInfo.updateHitbox(); metaDataGrp.add(levelInfo); - var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, "", 32); - levelDifficulty.text += CoolUtil.difficultyString(); + var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, '', 32); + levelDifficulty.text += PlayState.instance.currentDifficulty.toTitleCase(); levelDifficulty.scrollFactor.set(); levelDifficulty.setFormat(Paths.font('vcr.ttf'), 32); levelDifficulty.updateHitbox(); metaDataGrp.add(levelDifficulty); - var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, "", 32); - deathCounter.text = "Blue balled: " + PlayState.deathCounter; - deathCounter.text += "\n" + Highscore.tallies.totalNotesHit; - deathCounter.text += "\n" + Highscore.tallies.totalNotes; - deathCounter.text += "\n" + Std.string(Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes); + var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, '', 32); + deathCounter.text = 'Blue balled: ${PlayState.instance.deathCounter}'; + FlxG.watch.addQuick('totalNotesHit', Highscore.tallies.totalNotesHit); + FlxG.watch.addQuick('totalNotes', Highscore.tallies.totalNotes); deathCounter.scrollFactor.set(); deathCounter.setFormat(Paths.font('vcr.ttf'), 32); deathCounter.updateHitbox(); metaDataGrp.add(deathCounter); - practiceText = new FlxText(20, 15 + 64 + 32, 0, "PRACTICE MODE", 32); + practiceText = new FlxText(20, 15 + 64 + 32, 0, 'PRACTICE MODE', 32); practiceText.scrollFactor.set(); practiceText.setFormat(Paths.font('vcr.ttf'), 32); practiceText.updateHitbox(); practiceText.x = FlxG.width - (practiceText.width + 20); - practiceText.visible = PlayState.isPracticeMode; + practiceText.visible = PlayState.instance.isPracticeMode; metaDataGrp.add(practiceText); levelDifficulty.alpha = 0; @@ -137,7 +137,7 @@ class PauseSubState extends MusicBeatSubstate changeSelection(); } - override function update(elapsed:Float) + override function update(elapsed:Float):Void { if (pauseMusic.volume < 0.5) pauseMusic.volume += 0.01 * elapsed; @@ -180,41 +180,39 @@ class PauseSubState extends MusicBeatSubstate { var daSelected:String = menuItems[curSelected]; + // TODO: Why is this based on the menu item's name? Make this an enum or something. switch (daSelected) { - case "Resume": + case 'Resume': close(); - case "EASY" | 'NORMAL' | "HARD": - PlayState.currentSong = SongLoad.loadFromJson(PlayState.currentSong.song.toLowerCase(), PlayState.currentSong.song.toLowerCase()); - PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.currentSong.song.toLowerCase()); - SongLoad.curDiff = daSelected.toLowerCase(); - - PlayState.storyDifficulty = curSelected; - PlayState.storyDifficulty_NEW = daSelected.toLowerCase(); - - PlayState.needsReset = true; - - close(); - - case 'Toggle Practice Mode': - PlayState.isPracticeMode = !PlayState.isPracticeMode; - practiceText.visible = PlayState.isPracticeMode; case 'Change Difficulty': menuItems = difficultyChoices; regenMenu(); + + case 'EASY' | 'NORMAL' | 'HARD' | 'ERECT': + PlayState.instance.currentSong = SongDataParser.fetchSong(PlayState.instance.currentSong.songId.toLowerCase()); + + PlayState.instance.currentDifficulty = daSelected.toLowerCase(); + + PlayState.instance.needsReset = true; + + close(); case 'BACK': menuItems = pauseOG; regenMenu(); - case "Restart Song": - PlayState.needsReset = true; + case 'Toggle Practice Mode': + PlayState.instance.isPracticeMode = true; + practiceText.visible = PlayState.instance.isPracticeMode; + + case 'Restart Song': + PlayState.instance.needsReset = true; close(); - // FlxG.resetState(); - case "Exit to menu": + + case 'Exit to Menu': exitingToMenu = true; - PlayState.seenCutscene = false; - PlayState.deathCounter = 0; + PlayState.instance.deathCounter = 0; for (item in grpMenuShit.members) { @@ -225,9 +223,9 @@ class PauseSubState extends MusicBeatSubstate FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; - if (PlayState.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY)); + if (PlayStatePlaylist.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY)); else - openSubState(new funkin.ui.StickerSubState()); + openSubState(new funkin.ui.StickerSubState(null, FREEPLAY)); } } @@ -239,7 +237,7 @@ class PauseSubState extends MusicBeatSubstate } } - override function destroy() + override function destroy():Void { pauseMusic.destroy(); @@ -260,12 +258,10 @@ class PauseSubState extends MusicBeatSubstate item.targetY = index - curSelected; item.alpha = 0.6; - // item.setGraphicSize(Std.int(item.width * 0.8)); if (item.targetY == 0) { item.alpha = 1; - // item.setGraphicSize(Std.int(item.width)); } } } diff --git a/source/funkin/SongLoad.hx b/source/funkin/SongLoad.hx index d4ad0023e..ca3bc72d0 100644 --- a/source/funkin/SongLoad.hx +++ b/source/funkin/SongLoad.hx @@ -281,7 +281,7 @@ class SongLoad // castNoteDataToNoteData(swagShit.noteMap[diff]); - /* + /* switch (diff) { case "easy": diff --git a/source/funkin/StoryMenuState.hx b/source/funkin/StoryMenuState.hx deleted file mode 100644 index 89d59de1f..000000000 --- a/source/funkin/StoryMenuState.hx +++ /dev/null @@ -1,497 +0,0 @@ -package funkin; - -import flixel.FlxSprite; -import flixel.addons.transition.FlxTransitionableState; -import flixel.graphics.frames.FlxAtlasFrames; -import flixel.group.FlxGroup.FlxTypedGroup; -import flixel.group.FlxGroup; -import flixel.math.FlxMath; -import flixel.text.FlxText; -import flixel.tweens.FlxTween; -import flixel.util.FlxColor; -import flixel.util.FlxTimer; -import funkin.MenuItem.WeekType; -import funkin.play.PlayState; -import funkin.play.song.SongData.SongDataParser; -import lime.net.curl.CURLCode; -import openfl.Assets; -import funkin.ui.StickerSubState; -#if discord_rpc -import Discord.DiscordClient; -#end - -class StoryMenuState extends MusicBeatState -{ - var scoreText:FlxText; - - var weekData:Array> = [ - ['Tutorial'], - ['Bopeebo', 'Fresh', 'Dadbattle'], - ['Spookeez', 'South', "Monster"], - ['Pico', 'Philly', "Blammed"], - ['Satin-Panties', "High", "Milf"], - ['Cocoa', 'Eggnog', 'Winter-Horrorland'], - ['Senpai', 'Roses', 'Thorns'], - ['Ugh', 'Guns', 'Stress'], - ['Darnell', "lit-up", "2hot", "blazin"] - ]; - var curDifficulty:Int = 1; - - // TODO: This info is just hardcoded right now. - // We should probably make it so that weeks must be completed in order to unlock the next week. - public static var weekUnlocked:Array = [true, true, true, true, true, true, true, true, true]; - - var weekCharacters:Array = [ - ['dad', 'bf', 'gf'], - ['dad', 'bf', 'gf'], - ['spooky', 'bf', 'gf'], - ['pico', 'bf', 'gf'], - ['mom', 'bf', 'gf'], - ['parents-christmas', 'bf', 'gf'], - ['senpai', 'bf', 'gf'], - ['tankman', 'bf', 'gf'], - ['darnell', 'pico', 'nene'] - ]; - - var weekNames:Array = [ - "", - "Daddy Dearest", - "Spooky Month", - "PICO", - "MOMMY MUST MURDER", - "RED SNOW", - "hating simulator ft. moawling", - "TANKMAN", - "Due Debts" - ]; - - var weekType:Array = [WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEKEND]; - var weekTypeInc:Map = new Map(); - - var txtWeekTitle:FlxText; - - var curWeek:Int = 0; - - var txtTracklist:FlxText; - - var grpWeekText:FlxTypedGroup; - var grpWeekCharacters:Array>; - - // var grpWeekCharacters:FlxTypedGroup; - var grpLocks:FlxTypedGroup; - - var difficultySelectors:FlxGroup; - var sprDifficulty:FlxSprite; - var leftArrow:FlxSprite; - var rightArrow:FlxSprite; - var yellowBG:FlxSprite; // not actually, yellow, lol! - var targetColor:Int = 0xFFF9CF51; - - var stickerSubState:StickerSubState; - - public function new(?stickers:StickerSubState = null) - { - if (stickers != null) - { - stickerSubState = stickers; - } - - super(); - } - - override function create() - { - transIn = FlxTransitionableState.defaultTransIn; - transOut = FlxTransitionableState.defaultTransOut; - - if (FlxG.sound.music != null) - { - if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu')); - } - - if (stickerSubState != null) - { - this.persistentUpdate = true; - this.persistentDraw = true; - - openSubState(stickerSubState); - stickerSubState.degenStickers(); - - // resetSubState(); - } - - persistentUpdate = persistentDraw = true; - - scoreText = new FlxText(10, 10, 0, "SCORE: 49324858"); - scoreText.setFormat("VCR OSD Mono", 32); - - txtWeekTitle = new FlxText(FlxG.width * 0.7, 10, 0, ""); - txtWeekTitle.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT); - txtWeekTitle.alpha = 0.7; - - var rankText:FlxText = new FlxText(0, 10); - rankText.text = 'RANK: GREAT'; - rankText.setFormat(Paths.font("vcr.ttf"), 32); - rankText.size = scoreText.size; - rankText.screenCenter(X); - - var ui_tex = Paths.getSparrowAtlas('campaign_menu_UI_assets'); - yellowBG = new FlxSprite(0, 56).makeGraphic(FlxG.width, 400, FlxColor.WHITE); - yellowBG.color = 0xFFF9CF51; - // 0xFF413CAE blue - // 0xFFF9CF51 yello - - grpWeekText = new FlxTypedGroup(); - add(grpWeekText); - - var blackBarThingie:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 56, FlxColor.BLACK); - add(blackBarThingie); - - // grpWeekCharacters = new FlxTypedGroup(); - grpWeekCharacters = []; - - grpLocks = new FlxTypedGroup(); - add(grpLocks); - - #if discord_rpc - // Updating Discord Rich Presence - DiscordClient.changePresence("In the Menus", null); - #end - - for (i in 0...weekData.length) - { - if (!weekTypeInc.exists(weekType[i])) weekTypeInc[weekType[i]] = 1; - - if (i == 0 && weekType[i] == WEEK) weekTypeInc[weekType[i]] = 0; // set week to 0 by default? - - var weekThing:MenuItem = new MenuItem(0, yellowBG.y + yellowBG.height + 10, weekTypeInc[weekType[i]], weekType[i]); - weekThing.y += ((weekThing.height + 20) * i); - weekThing.targetY = i; - grpWeekText.add(weekThing); - - weekTypeInc[weekType[i]] += 1; - - weekThing.screenCenter(X); - weekThing.antialiasing = true; - // weekThing.updateHitbox(); - - // Needs an offset thingie - if (!weekUnlocked[i]) - { - var lock:FlxSprite = new FlxSprite(weekThing.width + 10 + weekThing.x); - lock.frames = ui_tex; - lock.animation.addByPrefix('lock', 'lock'); - lock.animation.play('lock'); - lock.ID = i; - lock.antialiasing = true; - grpLocks.add(lock); - } - } - - var sizeChart:Map> = new Map(); - - var sizeTxt:Array = Assets.getText(Paths.file("data/storychardata.txt")).split("\n"); - - for (item in sizeTxt) - { - var items:Array = item.split(" "); - - var stuf:Array = []; - var name:String = items.shift(); - - for (num in items) - stuf.push(Std.parseFloat(num)); - - sizeChart.set(name, stuf); - } - - for (index => week in weekCharacters) - { - grpWeekCharacters.push(new FlxTypedGroup()); - - for (char in 0...week.length) - { - var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, weekCharacters[index][char]); - weekCharacterThing.y += 70; - weekCharacterThing.antialiasing = true; - - var size:Float = 0.9; - - switch (char) - { - case 0 | 2: - size = 0.5; - if (char == 0 && weekCharacterThing.character == "pico") weekCharacterThing.flipX = true; - case 1: - size = 0.9; - weekCharacterThing.x -= 80; - } - - if (sizeChart.exists(weekCharacterThing.character)) - { - var nums:Array = sizeChart[weekCharacterThing.character]; - size = nums[char]; - - // IDK, this might be busted ass null shit? - if (char != 1) - { - weekCharacterThing.x += nums[3]; - weekCharacterThing.y += nums[4]; - } - } - - weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * size)); - weekCharacterThing.updateHitbox(); - - grpWeekCharacters[index].add(weekCharacterThing); - trace("ADD CHARACTER"); - } - - trace(grpWeekCharacters[index].toString()); - } - - difficultySelectors = new FlxGroup(); - add(difficultySelectors); - - leftArrow = new FlxSprite(grpWeekText.members[0].x + grpWeekText.members[0].width + 10, grpWeekText.members[0].y + 10); - leftArrow.frames = ui_tex; - leftArrow.animation.addByPrefix('idle', "arrow left"); - leftArrow.animation.addByPrefix('press', "arrow push left"); - leftArrow.animation.play('idle'); - difficultySelectors.add(leftArrow); - - sprDifficulty = new FlxSprite(leftArrow.x + 130, leftArrow.y); - sprDifficulty.frames = ui_tex; - sprDifficulty.animation.addByPrefix('easy', 'EASY'); - sprDifficulty.animation.addByPrefix('normal', 'NORMAL'); - sprDifficulty.animation.addByPrefix('hard', 'HARD'); - sprDifficulty.animation.play('easy'); - changeDifficulty(); - - difficultySelectors.add(sprDifficulty); - - rightArrow = new FlxSprite(sprDifficulty.x + sprDifficulty.width + 50, leftArrow.y); - rightArrow.frames = ui_tex; - rightArrow.animation.addByPrefix('idle', 'arrow right'); - rightArrow.animation.addByPrefix('press', "arrow push right", 24, false); - rightArrow.animation.play('idle'); - difficultySelectors.add(rightArrow); - - add(yellowBG); - for (grp in grpWeekCharacters) - { - add(grp); - // trace("ADDED GRP"); - } - - // add(grpWeekCharacters); - - txtTracklist = new FlxText(FlxG.width * 0.05, yellowBG.x + yellowBG.height + 100, 0, "Tracks", 32); - txtTracklist.alignment = CENTER; - txtTracklist.font = rankText.font; - txtTracklist.color = 0xFFe55777; - add(txtTracklist); - // add(rankText); - add(scoreText); - add(txtWeekTitle); - - updateText(); - - super.create(); - } - - override function update(elapsed:Float) - { - // scoreText.setFormat('VCR OSD Mono', 32); - - yellowBG.color = FlxColor.interpolate(yellowBG.color, targetColor, 0.06); - - lerpScore = CoolUtil.coolLerp(lerpScore, intendedScore, 0.5); - - scoreText.text = "WEEK SCORE:" + Math.round(lerpScore); - - txtWeekTitle.text = weekNames[curWeek].toUpperCase(); - txtWeekTitle.x = FlxG.width - (txtWeekTitle.width + 10); - - // FlxG.watch.addQuick('font', scoreText.font); - - difficultySelectors.visible = weekUnlocked[curWeek]; - - grpLocks.forEach(function(lock:FlxSprite) { - lock.y = grpWeekText.members[lock.ID].y; - }); - - if (!movedBack) - { - if (!selectedWeek) - { - if (controls.UI_UP_P) - { - changeWeek(-1); - } - - if (controls.UI_DOWN_P) - { - changeWeek(1); - } - - if (controls.UI_RIGHT) rightArrow.animation.play('press') - else - rightArrow.animation.play('idle'); - - if (controls.UI_LEFT) leftArrow.animation.play('press'); - else - leftArrow.animation.play('idle'); - - if (controls.UI_RIGHT_P) changeDifficulty(1); - if (controls.UI_LEFT_P) changeDifficulty(-1); - } - - if (controls.ACCEPT) - { - selectWeek(); - } - } - - if (controls.BACK && !movedBack && !selectedWeek) - { - FlxG.sound.play(Paths.sound('cancelMenu')); - movedBack = true; - FlxG.switchState(new MainMenuState()); - } - - super.update(elapsed); - } - - var movedBack:Bool = false; - var selectedWeek:Bool = false; - var stopspamming:Bool = false; - - function selectWeek() - { - if (weekUnlocked[curWeek]) - { - if (stopspamming == false) - { - FlxG.sound.play(Paths.sound('confirmMenu')); - - grpWeekText.members[curWeek].startFlashing(); - grpWeekCharacters[curWeek].members[1].animation.play('bfConfirm'); - stopspamming = true; - } - - PlayState.storyPlaylist = weekData[curWeek]; - PlayState.isStoryMode = true; - selectedWeek = true; - - PlayState.currentSong = SongLoad.loadFromJson(PlayState.storyPlaylist[0].toLowerCase(), PlayState.storyPlaylist[0].toLowerCase()); - PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.storyPlaylist[0].toLowerCase()); - PlayState.storyWeek = curWeek; - PlayState.campaignScore = 0; - - PlayState.storyDifficulty = curDifficulty; - PlayState.storyDifficulty_NEW = switch (curDifficulty) - { - case 0: - 'easy'; - case 1: - 'normal'; - case 2: - 'hard'; - default: - 'normal'; - }; - SongLoad.curDiff = PlayState.storyDifficulty_NEW; - - new FlxTimer().start(1, function(tmr:FlxTimer) { - LoadingState.loadAndSwitchState(new PlayState(), true); - }); - } - } - - function changeDifficulty(change:Int = 0):Void - { - curDifficulty += change; - - if (curDifficulty < 0) curDifficulty = 2; - if (curDifficulty > 2) curDifficulty = 0; - - sprDifficulty.offset.x = 0; - - switch (curDifficulty) - { - case 0: - sprDifficulty.animation.play('easy'); - sprDifficulty.offset.x = 20; - case 1: - sprDifficulty.animation.play('normal'); - sprDifficulty.offset.x = 70; - case 2: - sprDifficulty.animation.play('hard'); - sprDifficulty.offset.x = 20; - } - - sprDifficulty.alpha = 0; - - // USING THESE WEIRD VALUES SO THAT IT DOESNT FLOAT UP - sprDifficulty.y = leftArrow.y - 15; - intendedScore = Highscore.getWeekScore(curWeek, curDifficulty); - - FlxTween.tween(sprDifficulty, {y: leftArrow.y + 15, alpha: 1}, 0.07); - } - - var lerpScore:Float = 0; - var intendedScore:Int = 0; - - function changeWeek(change:Int = 0):Void - { - curWeek += change; - - if (curWeek >= weekData.length) curWeek = 0; - if (curWeek < 0) curWeek = weekData.length - 1; - - var bullShit:Int = 0; - - for (item in grpWeekText.members) - { - item.targetY = bullShit - curWeek; - if (item.targetY == Std.int(0) && weekUnlocked[curWeek]) item.alpha = 1; - else - item.alpha = 0.6; - bullShit++; - } - - FlxG.sound.play(Paths.sound('scrollMenu')); - - updateText(); - } - - function updateText() - { - switch (weekType[curWeek]) - { - case WEEK: - targetColor = 0xFFF9CF51; - case WEEKEND: - targetColor = 0xFF413CAE; - } - - for (ind => grp in grpWeekCharacters) - grp.visible = ind == curWeek; - - txtTracklist.text = "Tracks\n"; - - var trackNames:Array = weekData[curWeek]; - for (i in trackNames) - { - txtTracklist.text += '\n${i}'; - } - - txtTracklist.text = txtTracklist.text.toUpperCase(); - - txtTracklist.screenCenter(X); - txtTracklist.x -= FlxG.width * 0.35; - - intendedScore = Highscore.getWeekScore(curWeek, curDifficulty); - } -} diff --git a/source/funkin/TitleState.hx b/source/funkin/TitleState.hx index e8e96e54d..bc6ef571d 100644 --- a/source/funkin/TitleState.hx +++ b/source/funkin/TitleState.hx @@ -53,7 +53,7 @@ class TitleState extends MusicBeatState super.create(); - /* + /* #elseif web @@ -84,8 +84,7 @@ class TitleState extends MusicBeatState */ // netConnection.addEventListener(MouseEvent.MOUSE_DOWN, overlay_onMouseDown); - new FlxTimer().start(1, function(tmr:FlxTimer) - { + new FlxTimer().start(1, function(tmr:FlxTimer) { startIntro(); }); } @@ -284,44 +283,6 @@ class TitleState extends MusicBeatState FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG}); } - /* - FlxG.watch.addQuick('cur display', FlxG.stage.window.display.id); - if (FlxG.keys.justPressed.Y) - { - // trace(FlxG.stage.window.display.name); - - if (FlxG.gamepads.firstActive != null) - { - trace(FlxG.gamepads.firstActive.model); - FlxG.gamepads.firstActive.id - } - else - trace('gamepad null'); - - // FlxG.stage.window.title = Std.string(FlxG.random.int(0, 20000)); - // FlxG.stage.window.setIcon(Image.fromFile('assets/images/icon16.png')); - // FlxG.stage.window.readPixels; - - if (FlxG.stage.window.width == Std.int(FlxG.stage.window.display.bounds.width)) - { - FlxG.stage.window.width = 1280; - FlxG.stage.window.height = 720; - FlxG.stage.window.y = 30; - } - else - { - FlxG.stage.window.width = Std.int(FlxG.stage.window.display.bounds.width); - FlxG.stage.window.height = Std.int(FlxG.stage.window.display.bounds.height); - FlxG.stage.window.x = Std.int(FlxG.stage.window.display.bounds.x); - FlxG.stage.window.y = Std.int(FlxG.stage.window.display.bounds.y); - } - } - */ - - #if debug - if (FlxG.keys.justPressed.EIGHT) FlxG.switchState(new CutsceneAnimTestState()); - #end - if (FlxG.sound.music != null) Conductor.songPosition = FlxG.sound.music.time; if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen; @@ -373,8 +334,7 @@ class TitleState extends MusicBeatState #if newgrounds if (!OutdatedSubState.leftState) { - NGio.checkVersion(function(version) - { + NGio.checkVersion(function(version) { // Check if version is outdated var localVersion:String = "v" + Application.current.meta.get('version'); var onlineVersion = version.split(" ")[0].trim(); @@ -391,8 +351,7 @@ class TitleState extends MusicBeatState }); } #end - new FlxTimer().start(2, function(tmr:FlxTimer) - { + new FlxTimer().start(2, function(tmr:FlxTimer) { // These assets are very unlikely to be used for the rest of gameplay, so it unloads them from cache/memory // Saves about 50mb of RAM or so??? Assets.cache.clear(Paths.image('gfDanceTitle')); @@ -404,7 +363,7 @@ class TitleState extends MusicBeatState // FlxG.sound.play(Paths.music('titleShoot'), 0.7); } if (pressedEnter && !skippedIntro && initialized) skipIntro(); - /* + /* #if web if (!initialized && controls.ACCEPT) { diff --git a/source/funkin/api/newgrounds/NGUtil.hx b/source/funkin/api/newgrounds/NGUtil.hx index 88db899d1..773e2f98f 100644 --- a/source/funkin/api/newgrounds/NGUtil.hx +++ b/source/funkin/api/newgrounds/NGUtil.hx @@ -226,7 +226,7 @@ class NGUtil scoreboardsLoaded = true; ngScoresLoaded.dispatch(); - /* + /* for (score in NG.core.scoreBoards.get(8737).scores) { trace('score loaded user:${score.user.name}, score:${score.formatted_value}'); diff --git a/source/funkin/api/newgrounds/NgPrompt.hx b/source/funkin/api/newgrounds/NgPrompt.hx index 7f7d677ef..133132ba4 100644 --- a/source/funkin/api/newgrounds/NgPrompt.hx +++ b/source/funkin/api/newgrounds/NgPrompt.hx @@ -36,14 +36,12 @@ class NgPrompt extends Prompt #if web prompt.buttons.getItem("yes").fireInstantly = true; #end - prompt.onYes = function() - { + prompt.onYes = function() { prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end); prompt.setButtons(None); openPassportUrl(); }; - prompt.onNo = function() - { + prompt.onNo = function() { prompt.close(); prompt = null; NGio.cancelLogin(); @@ -92,8 +90,7 @@ class NgPrompt extends Prompt { var user = io.newgrounds.NG.core.user.name; var prompt = new NgPrompt('Log out of $user?', Yes_No); - prompt.onYes = function() - { + prompt.onYes = function() { NGio.logout(); prompt.close(); }; diff --git a/source/funkin/audio/SoundGroup.hx b/source/funkin/audio/SoundGroup.hx index 69c52c3d0..ae755e231 100644 --- a/source/funkin/audio/SoundGroup.hx +++ b/source/funkin/audio/SoundGroup.hx @@ -44,7 +44,7 @@ class SoundGroup extends FlxTypedGroup /** * Finds the largest deviation from the desired time inside this SoundGroup. - * + * * @param targetTime The time to check against. * If none is provided, it checks the time of all members against the first member of this SoundGroup. * @return The largest deviation from the target time found. diff --git a/source/funkin/charting/ChartingState.hx b/source/funkin/charting/ChartingState.hx deleted file mode 100644 index 3cfac53be..000000000 --- a/source/funkin/charting/ChartingState.hx +++ /dev/null @@ -1,1541 +0,0 @@ -package funkin.charting; - -import flixel.addons.display.FlxGridOverlay; -import flixel.addons.transition.FlxTransitionableState; -import flixel.addons.ui.FlxInputText; -import flixel.addons.ui.FlxUI; -import flixel.addons.ui.FlxUICheckBox; -import flixel.addons.ui.FlxUIDropDownMenu; -import flixel.addons.ui.FlxUIInputText; -import flixel.addons.ui.FlxUINumericStepper; -import flixel.addons.ui.FlxUITabMenu; -import flixel.FlxSprite; -import flixel.group.FlxGroup; -import flixel.math.FlxMath; -import flixel.sound.FlxSound; -import flixel.text.FlxText; -import flixel.ui.FlxButton; -import flixel.util.FlxColor; -import funkin.audio.visualize.PolygonSpectogram; -import funkin.audiovis.ABotVis; -import funkin.audiovis.SpectogramSprite; -import funkin.Conductor.BPMChangeEvent; -import funkin.graphics.rendering.MeshRender; -import funkin.noteStuff.NoteBasic.NoteData; -import funkin.play.HealthIcon; -import funkin.play.PlayState; -import funkin.Section.SwagSection; -import funkin.SongLoad.SwagSong; -import funkin.audio.VoicesGroup; -import haxe.Json; -import lime.media.AudioBuffer; -import lime.utils.Assets; -import openfl.events.Event; -import openfl.events.IOErrorEvent; -import openfl.net.FileReference; - -using Lambda; -using flixel.util.FlxSpriteUtil; // add in "compiler save" that saves the JSON directly to the debug json using File.write() stuff on windows / sys - -class ChartingState extends MusicBeatState -{ - var _file:FileReference; - - var UI_box:FlxUITabMenu; - var sidePreview:FlxSprite; - - /** - * Array of notes showing when each section STARTS in STEPS - * Usually rounded up?? - */ - var curSection:Int = 0; - - public static var lastSection:Int = 0; - - var bpmTxt:FlxText; - - var strumLine:FlxSprite; - var curSong:String = 'Dadbattle'; - var amountSteps:Int = 0; - var bullshitUI:FlxGroup; - - var highlight:FlxSprite; - - var GRID_SIZE:Int = 40; - - var dummyArrow:FlxSprite; - - var curRenderedNotes:FlxTypedGroup; - var curRenderedSustains:FlxTypedGroup; - - var gridBG:FlxSprite; - - var _song:SwagSong; - - var typingShit:FlxInputText; - /* - * WILL BE THE CURRENT / LAST PLACED NOTE - **/ - var curSelectedNote:NoteData; - - var tempBpm:Float = 0; - - var vocals:VoicesGroup; - - var leftIcon:HealthIcon; - var rightIcon:HealthIcon; - - var audioBuf:AudioBuffer = new AudioBuffer(); - - var playheadTest:FlxSprite; - - var staticSpecGrp:FlxTypedGroup; - - override function create() - { - curSection = lastSection; - - // sys.io.File.saveContent('./bitShit.txt', "swag"); - - // trace(audioBuf.sampleRate); - - gridBG = FlxGridOverlay.create(GRID_SIZE, GRID_SIZE, GRID_SIZE * 8, GRID_SIZE * 16); - trace("GRD BG: " + gridBG.height); - add(gridBG); - - leftIcon = new HealthIcon('bf'); - rightIcon = new HealthIcon('dad'); - leftIcon.scrollFactor.set(1, 1); - rightIcon.scrollFactor.set(1, 1); - - leftIcon.setGraphicSize(0, 45); - rightIcon.setGraphicSize(0, 45); - - leftIcon.autoUpdate = false; - rightIcon.autoUpdate = false; - - add(leftIcon); - add(rightIcon); - - leftIcon.setPosition(0, -100); - rightIcon.setPosition(gridBG.width / 2, -100); - - var gridBlackLine:FlxSprite = new FlxSprite(gridBG.x + gridBG.width / 2).makeGraphic(2, Std.int(gridBG.height), FlxColor.BLACK); - add(gridBlackLine); - - curRenderedNotes = new FlxTypedGroup(); - curRenderedSustains = new FlxTypedGroup(); - - if (PlayState.currentSong != null) - { - _song = SongLoad.songData = PlayState.currentSong; - trace("LOADED A PLAYSTATE SONGFILE"); - } - else - { - _song = SongLoad.songData = SongLoad.getDefaultSwagSong(); - } - - FlxG.mouse.visible = true; - FlxG.save.bind('funkin', 'ninjamuffin99'); - - tempBpm = _song.bpm; - - addSection(); - - // sections = SongLoad.getSong(); - - updateGrid(); - - loadSong(_song.song); - // Conductor.bpm = _song.bpm; - Conductor.mapBPMChanges(_song); - - bpmTxt = new FlxText(1000, 50, 0, "", 16); - bpmTxt.scrollFactor.set(); - add(bpmTxt); - - strumLine = new FlxSprite(0, 50).makeGraphic(Std.int(GRID_SIZE * 8), 4); - add(strumLine); - - dummyArrow = new FlxSprite().makeGraphic(GRID_SIZE, GRID_SIZE, 0xFFCC2288); - dummyArrow.alpha = 0.3; - add(dummyArrow); - - var tabs = [ - {name: "Song", label: 'Song'}, - {name: "Section", label: 'Section'}, - {name: "Note", label: 'Note'} - ]; - - UI_box = new FlxUITabMenu(null, tabs, true); - - UI_box.resize(300, 400); - UI_box.x = (FlxG.width / 4) * 3; - UI_box.y = 120; - add(UI_box); - - addSongUI(); - addSectionUI(); - addNoteUI(); - - add(curRenderedNotes); - add(curRenderedSustains); - - changeSection(); - super.create(); - } - - function addSongUI():Void - { - var UI_songTitle = new FlxUIInputText(10, 10, 70, _song.song, 8); - typingShit = UI_songTitle; - - var check_voices = new FlxUICheckBox(10, 25, null, null, "Has voice track", 100); - check_voices.checked = _song.needsVoices; - // _song.needsVoices = check_voices.checked; - check_voices.callback = function() { - _song.needsVoices = check_voices.checked; - trace('CHECKED!'); - }; - - var check_mute_inst = new FlxUICheckBox(10, 200, null, null, "Mute Instrumental (in editor)", 100); - check_mute_inst.checked = false; - check_mute_inst.callback = function() { - var vol:Float = 1; - - if (check_mute_inst.checked) vol = 0; - - FlxG.sound.music.volume = vol; - }; - - var saveButton:FlxButton = new FlxButton(110, 8, "Save", function() { - saveLevel(); - }); - - var saveCompiler:FlxButton = new FlxButton(110, 30, "Save compile", function() { - saveLevel(true); - }); - - var reloadSong:FlxButton = new FlxButton(saveButton.x + saveButton.width + 10, saveButton.y, "Reload Audio", function() { - loadSong(_song.song); - }); - - var reloadSongJson:FlxButton = new FlxButton(reloadSong.x, saveButton.y + 30, "Reload JSON", function() { - FlxTransitionableState.skipNextTransIn = true; - FlxTransitionableState.skipNextTransOut = true; - loadJson(_song.song.toLowerCase()); - }); - - var loadAutosaveBtn:FlxButton = new FlxButton(reloadSongJson.x, reloadSongJson.y + 30, 'load autosave', loadAutosave); - - var stepperSpeed:FlxUINumericStepper = new FlxUINumericStepper(10, 80, 0.1, 1, 0.1, 10, 2); - stepperSpeed.value = SongLoad.getSpeed(); - // stepperSpeed.value = _song.speed[SongLoad.curDiff]; - stepperSpeed.name = 'song_speed'; - - var stepperBPM:FlxUINumericStepper = new FlxUINumericStepper(10, 65, 1, 100, 1, 999, 3); - stepperBPM.value = Conductor.bpm; - stepperBPM.name = 'song_bpm'; - - var characters:Array = CoolUtil.coolTextFile(Paths.txt('characterList')); - - var player1DropDown = new FlxUIDropDownMenu(10, 100, FlxUIDropDownMenu.makeStrIdLabelArray(characters, true), function(character:String) { - _song.player1 = characters[Std.parseInt(character)]; - updateHeads(); - }); - player1DropDown.selectedLabel = _song.player1; - - var player2DropDown = new FlxUIDropDownMenu(140, 100, FlxUIDropDownMenu.makeStrIdLabelArray(characters, true), function(character:String) { - _song.player2 = characters[Std.parseInt(character)]; - updateHeads(); - }); - player2DropDown.selectedLabel = _song.player2; - - var difficultyDropDown = new FlxUIDropDownMenu(10, 230, FlxUIDropDownMenu.makeStrIdLabelArray(_song.difficulties, true), function(diff:String) { - SongLoad.curDiff = _song.difficulties[Std.parseInt(diff)]; - SongLoad.checkAndCreateNotemap(SongLoad.curDiff); - - while (SongLoad.getSong()[curSection] == null) - addSection(); - - updateGrid(); - }); - difficultyDropDown.selectedLabel = SongLoad.curDiff; - - var difficultyAdder = new FlxUIInputText(130, 230, 100, "", 12); - - var addDiff:FlxButton = new FlxButton(130, 250, "Add Difficulty", function() { - difficultyAdder.text = ""; - // something to regenerate difficulties - }); - - var tab_group_song = new FlxUI(null, UI_box); - tab_group_song.name = "Song"; - tab_group_song.add(UI_songTitle); - - tab_group_song.add(check_voices); - tab_group_song.add(check_mute_inst); - tab_group_song.add(saveButton); - tab_group_song.add(saveCompiler); - tab_group_song.add(reloadSong); - tab_group_song.add(reloadSongJson); - tab_group_song.add(loadAutosaveBtn); - tab_group_song.add(stepperBPM); - tab_group_song.add(stepperSpeed); - tab_group_song.add(player1DropDown); - tab_group_song.add(player2DropDown); - tab_group_song.add(difficultyDropDown); - tab_group_song.add(difficultyAdder); - tab_group_song.add(addDiff); - - UI_box.addGroup(tab_group_song); - UI_box.scrollFactor.set(); - - FlxG.camera.focusOn(gridBG.getGraphicMidpoint()); - } - - var stepperLength:FlxUINumericStepper; - var check_mustHitSection:FlxUICheckBox; - var check_changeBPM:FlxUICheckBox; - var stepperSectionBPM:FlxUINumericStepper; - var check_altAnim:FlxUICheckBox; - - function addSectionUI():Void - { - var tab_group_section = new FlxUI(null, UI_box); - tab_group_section.name = 'Section'; - - stepperLength = new FlxUINumericStepper(10, 10, 4, 0, 0, 999, 0); - stepperLength.value = SongLoad.getSong()[curSection].lengthInSteps; - stepperLength.name = "section_length"; - - stepperSectionBPM = new FlxUINumericStepper(10, 80, 1, Conductor.bpm, 1, 999, 3); - stepperSectionBPM.value = Conductor.bpm; - stepperSectionBPM.name = 'section_bpm'; - - var stepperCopy:FlxUINumericStepper = new FlxUINumericStepper(110, 130, 1, 1, -999, 999, 0); - - var copyButton:FlxButton = new FlxButton(10, 130, "Copy last section", function() { - copySection(Std.int(stepperCopy.value)); - }); - - var clearSectionButton:FlxButton = new FlxButton(10, 150, "Clear", clearSection); - - var swapSection:FlxButton = new FlxButton(10, 170, "Swap section", function() { - for (i in 0...SongLoad.getSong()[curSection].sectionNotes.length) - { - var note:Note = new Note(0, 0); - note.data = SongLoad.getSong()[curSection].sectionNotes[i]; - note.data.noteData = (note.data.noteData + 4) % 8; - SongLoad.getSong()[curSection].sectionNotes[i] = note.data; - updateGrid(); - } - }); - - check_mustHitSection = new FlxUICheckBox(10, 30, null, null, "Must hit section", 100); - check_mustHitSection.name = 'check_mustHit'; - check_mustHitSection.checked = true; - // _song.needsVoices = check_mustHit.checked; - - check_altAnim = new FlxUICheckBox(10, 400, null, null, "Alt Animation", 100); - check_altAnim.name = 'check_altAnim'; - - check_changeBPM = new FlxUICheckBox(10, 60, null, null, 'Change BPM', 100); - check_changeBPM.name = 'check_changeBPM'; - - tab_group_section.add(stepperLength); - tab_group_section.add(stepperSectionBPM); - tab_group_section.add(stepperCopy); - tab_group_section.add(check_mustHitSection); - tab_group_section.add(check_altAnim); - tab_group_section.add(check_changeBPM); - tab_group_section.add(copyButton); - tab_group_section.add(clearSectionButton); - tab_group_section.add(swapSection); - - UI_box.addGroup(tab_group_section); - } - - var stepperSusLength:FlxUINumericStepper; - var stepperPerNoteSpeed:FlxUINumericStepper; - - function addNoteUI():Void - { - var tab_group_note = new FlxUI(null, UI_box); - tab_group_note.name = 'Note'; - - stepperSusLength = new FlxUINumericStepper(10, 10, Conductor.stepCrochet / 2, 0, 0, Conductor.stepCrochet * 16); - stepperSusLength.value = 0; - stepperSusLength.name = 'note_susLength'; - - stepperPerNoteSpeed = new FlxUINumericStepper(10, 40, 0.1, 1, 0.01, 100, 2); - stepperPerNoteSpeed.value = 1; - stepperPerNoteSpeed.name = "note_PerNoteSpeed"; - - var noteSpeedName:FlxText = new FlxText(40, stepperPerNoteSpeed.y, 0, "Note Speed Multiplier"); - - var applyLength:FlxButton = new FlxButton(100, 10, 'Apply'); - - tab_group_note.add(stepperSusLength); - tab_group_note.add(stepperPerNoteSpeed); - tab_group_note.add(noteSpeedName); - tab_group_note.add(applyLength); - - UI_box.addGroup(tab_group_note); - } - - // var spec:SpectogramSprite; - - function loadSong(daSong:String):Void - { - if (FlxG.sound.music != null) - { - FlxG.sound.music.stop(); - // vocals.stop(); - } - - var pathShit = Paths.inst(daSong); - - if (!openfl.utils.Assets.cache.hasSound(pathShit)) - { - var library = Assets.getLibrary("songs"); - var symbolPath = pathShit.split(":").pop(); - // @:privateAccess - // library.types.set(symbolPath, SOUND); - // @:privateAccess - // library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]); - // var callback = callbacks.add("song:" + pathShit); - openfl.utils.Assets.loadSound(pathShit).onComplete(function(_) { - // callback(); - }); - } - - FlxG.sound.playMusic(Paths.inst(daSong), 0.6); - - var musSpec:PolygonSpectogram = new PolygonSpectogram(FlxG.sound.music, FlxColor.RED, FlxG.height / 2, Math.floor(FlxG.height / 2)); - musSpec.x += 170; - musSpec.scrollFactor.set(); - musSpec.waveAmplitude = 50; - // musSpec.visType = FREQUENCIES; - add(musSpec); - - sidePreview = new FlxSprite(0, 0).makeGraphic(40, FlxG.height, FlxColor.GRAY); - sidePreview.scrollFactor.set(); - add(sidePreview); - - // trace(audioBuf.data.length); - playheadTest = new FlxSprite(0, 0).makeGraphic(30, 2, FlxColor.RED); - playheadTest.scrollFactor.set(); - playheadTest.alpha = 0.5; - add(playheadTest); - - // WONT WORK FOR TUTORIAL OR TEST SONG!!! REDO LATER - vocals = new VoicesGroup(); - // vocals = new FlxSound().loadEmbedded(Paths.voices(daSong)); - // FlxG.sound.list.add(vocals); - - staticSpecGrp = new FlxTypedGroup(); - add(staticSpecGrp); - - var aBoy:ABotVis = new ABotVis(FlxG.sound.music); - // add(aBoy); - - for (index => voc in vocals.members) - { - var vocalSpec:SpectogramSprite = new SpectogramSprite(voc, FlxG.random.color(0xFFAAAAAA, FlxColor.WHITE, 100), musSpec.daHeight, - Math.floor(FlxG.height / 2)); - vocalSpec.x = 70 - (50 * index); - // vocalSpec.visType = FREQUENCIES; - vocalSpec.daHeight = musSpec.daHeight; - vocalSpec.y = vocalSpec.daHeight; - vocalSpec.scrollFactor.set(); - add(vocalSpec); - - var staticVocal:SpectogramSprite = new SpectogramSprite(voc, FlxG.random.color(0xFFAAAAAA, FlxColor.WHITE, 100), GRID_SIZE * 16, GRID_SIZE * 8); - if (index == 0) staticVocal.x -= 150; - - if (index == 1) staticVocal.x = gridBG.width; - - staticVocal.visType = STATIC; - staticSpecGrp.add(staticVocal); - } - - FlxG.sound.music.pause(); - - vocals.pause(); - - FlxG.sound.music.onComplete = function() { - vocals.pause(); - vocals.time = 0; - FlxG.sound.music.pause(); - FlxG.sound.music.time = 0; - changeSection(); - }; - } - - function generateUI():Void - { - while (bullshitUI.members.length > 0) - { - bullshitUI.remove(bullshitUI.members[0], true); - } - - // general shit - var title:FlxText = new FlxText(UI_box.x + 20, UI_box.y + 20, 0); - bullshitUI.add(title); - /* - var loopCheck = new FlxUICheckBox(UI_box.x + 10, UI_box.y + 50, null, null, "Loops", 100, ['loop check']); - loopCheck.checked = notes[0]elected.doesLoop; - tooltips.add(loopCheck, {title: 'Section looping', body: "Whether or not it's a simon says style section", style: tooltipType}); - bullshitUI.add(loopCheck); - - */ - } - - override function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array) - { - if (id == FlxUICheckBox.CLICK_EVENT) - { - var check:FlxUICheckBox = cast sender; - var label = check.getLabel().text; - switch (label) - { - case 'Must hit section': - SongLoad.getSong()[curSection].mustHitSection = check.checked; - - updateHeads(); - - case 'Change BPM': - SongLoad.getSong()[curSection].changeBPM = check.checked; - FlxG.log.add('changed bpm shit'); - case "Alt Animation": - SongLoad.getSong()[curSection].altAnim = check.checked; - } - } - else if (id == FlxUINumericStepper.CHANGE_EVENT && (sender is FlxUINumericStepper)) - { - var nums:FlxUINumericStepper = cast sender; - var wname = nums.name; - FlxG.log.add(wname); - if (wname == 'section_length') - { - SongLoad.getSong()[curSection].lengthInSteps = Std.int(nums.value); - updateGrid(); - } - else if (wname == 'song_speed') - { - // _song.speed[SongLoad.curDiff] = nums.value; - _song.speed.normal = nums.value; - } - else if (wname == 'song_bpm') - { - tempBpm = nums.value; - Conductor.mapBPMChanges(_song); - Conductor.forceBPM(nums.value); - } - else if (wname == 'note_susLength') - { - curSelectedNote.sustainLength = nums.value; - updateGrid(); - } - else if (wname == 'section_bpm') - { - SongLoad.getSong()[curSection].bpm = nums.value; - updateGrid(); - } - } - - // FlxG.log.add(id + " WEED " + sender + " WEED " + data + " WEED " + params); - } - - var updatedSection:Bool = false; - - /* this function got owned LOL - function lengthBpmBullshit():Float - { - if (SongLoad.getSong()[curSection].changeBPM) - return SongLoad.getSong()[curSection].lengthInSteps * (SongLoad.getSong()[curSection].bpm / _song.bpm); - else - return SongLoad.getSong()[curSection].lengthInSteps; - }*/ - /** - * Gets the start time of section, defaults to the curSection - * @param section - * @return position of the song in... either seconds or milliseconds.... woops - */ - function sectionStartTime(?funnySection:Int):Float - { - if (funnySection == null) funnySection = curSection; - - var daBPM:Float = _song.bpm; - var daPos:Float = 0; - for (i in 0...funnySection) - { - if (SongLoad.getSong()[i].changeBPM) - { - daBPM = SongLoad.getSong()[i].bpm; - } - daPos += 4 * sectionCalc(daBPM); - } - return daPos; - } - - function measureStartTime():Float - { - var daBPM:Float = _song.bpm; - var daPos:Float = sectionStartTime(); - - daPos = Math.floor(FlxG.sound.music.time / sectionCalc(daBPM)) * sectionCalc(daBPM); - return daPos; - } - - function sectionCalc(bpm:Float) - { - return (1000 * 60 / bpm); - } - - var p1Muted:Bool = false; - var p2Muted:Bool = false; - - override function update(elapsed:Float) - { - // FlxG.camera.followLerp = CoolUtil.camLerpShit(0.05); - - FlxG.sound.music.pan = FlxMath.remapToRange(FlxG.mouse.screenX, 0, FlxG.width, -1, 1) * 10; - - // curStep = recalculateSteps(); - - Conductor.songPosition = FlxG.sound.music.time; - _song.song = typingShit.text; - - playheadTest.y = CoolUtil.coolLerp(playheadTest.y, FlxMath.remapToRange(Conductor.songPosition, 0, FlxG.sound.music.length, 0, FlxG.height), 0.5); - - var strumLinePos:Float = getYfromStrum((Conductor.songPosition - sectionStartTime()) % (Conductor.stepCrochet * SongLoad.getSong()[curSection].lengthInSteps)); - - if (FlxG.sound.music != null) - { - if (FlxG.sound.music.playing) strumLine.y = strumLinePos; - else - strumLine.y = CoolUtil.coolLerp(strumLine.y, strumLinePos, 0.5); - } - - /* if (FlxG.sound.music.playing) - { - var normalizedShitIDK:Int = Std.int(FlxMath.remapToRange(Conductor.songPosition, 0, FlxG.sound.music.length, 0, audioBuf.data.length)); - FlxG.watch.addQuick('WEIRD AUDIO SHIT LOL', audioBuf.data[normalizedShitIDK]); - // leftIcon.scale.x = FlxMath.remapToRange(audioBuf.data[normalizedShitIDK], 0, 255, 1, 2); - }*/ - - if (FlxG.keys.justPressed.X) toggleAltAnimNote(); - - if (false) // (curBeat % 4 == 0 && curStep >= 16 * (curSection + 1)) - { - // trace(curStep); - // trace((SongLoad.getSong()[curSection].lengthInSteps) * (curSection + 1)); - trace('DUMBSHIT'); - - if (SongLoad.getSong()[curSection + 1] == null) - { - addSection(); - } - - changeSection(curSection + 1, false); - } - - FlxG.watch.addQuick('daBeat', Conductor.currentBeat); - FlxG.watch.addQuick('daStep', Conductor.currentStep); - - if (FlxG.mouse.pressedMiddle && FlxG.mouse.overlaps(gridBG)) - { - if (FlxG.sound.music.playing) - { - FlxG.sound.music.pause(); - vocals.pause(); - } - - FlxG.sound.music.time = CoolUtil.coolLerp(FlxG.sound.music.time, getStrumTime(FlxG.mouse.y) + sectionStartTime(), 0.5); - vocals.time = FlxG.sound.music.time; - } - - if (FlxG.mouse.pressed) - { - if (FlxG.keys.pressed.ALT && FlxG.mouse.overlaps(gridBG)) // same shit as middle click / hold on grid - { - if (FlxG.sound.music.playing) - { - FlxG.sound.music.pause(); - vocals.pause(); - } - - FlxG.sound.music.time = getStrumTime(FlxG.mouse.y) + sectionStartTime(); - vocals.time = FlxG.sound.music.time; - } - else - { - if (FlxG.mouse.screenX <= 30 && FlxMath.inBounds(FlxG.mouse.screenY, 0, FlxG.height)) - { - if (FlxG.sound.music.playing) - { - FlxG.sound.music.pause(); - vocals.pause(); - } - - FlxG.sound.music.time = CoolUtil.coolLerp(FlxG.sound.music.time, - FlxMath.remapToRange(FlxG.mouse.screenY, 0, FlxG.height, 0, FlxG.sound.music.length), 0.5); - vocals.time = FlxG.sound.music.time; - } - if (FlxG.mouse.justPressed) - { - if (FlxG.mouse.overlaps(leftIcon)) - { - if (leftIcon.characterId == _song.player1) - { - p1Muted = !p1Muted; - leftIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0; - } - else - { - p2Muted = !p2Muted; - - leftIcon.animation.curAnim.curFrame = p2Muted ? 1 : 0; - } - - vocals.members[0].volume = p1Muted ? 0 : 1; - - // null check jus in case using old shit? - if (vocals.members[1] != null) vocals.members[1].volume = p2Muted ? 0 : 1; - } - - // sloppy copypaste lol deal with it! - if (FlxG.mouse.overlaps(rightIcon)) - { - if (rightIcon.characterId == _song.player1) - { - p1Muted = !p1Muted; - rightIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0; - } - else - { - rightIcon.animation.curAnim.curFrame = p2Muted ? 1 : 0; - p2Muted = !p2Muted; - } - - vocals.members[0].volume = p1Muted ? 0 : 1; - - // null check jus in case using old shit? - if (vocals.members[1] != null) vocals.members[1].volume = p2Muted ? 0 : 1; - } - - if (FlxG.mouse.overlaps(curRenderedNotes)) - { - curRenderedNotes.forEach(function(note:Note) { - if (FlxG.mouse.overlaps(note)) - { - selectNote(note); - } - }); - } - else - { - if (FlxG.mouse.overlaps(gridBG)) - { - FlxG.log.add('added note'); - addNote(); - } - } - } - } - } - - if (FlxG.mouse.pressedRight) - { - if (FlxG.mouse.overlaps(curRenderedNotes)) - { - curRenderedNotes.forEach(function(note:Note) { - if (FlxG.mouse.overlaps(note)) - { - trace('tryin to delete note...'); - deleteNote(note); - } - }); - } - } - - if (FlxG.mouse.justReleased) justPlacedNote = false; - - if (FlxG.mouse.overlaps(gridBG)) - { - if (justPlacedNote && FlxG.mouse.pressed && FlxG.mouse.y > getYfromStrum(curSelectedNote.strumTime)) - { - var minusStuff:Float = FlxG.mouse.y - getYfromStrum(curSelectedNote.strumTime); - minusStuff -= GRID_SIZE; - minusStuff += GRID_SIZE / 2; - minusStuff = Math.floor(minusStuff / GRID_SIZE) * GRID_SIZE; - minusStuff = FlxMath.remapToRange(minusStuff, 0, 40, 0, Conductor.stepCrochet); - - curSelectedNote.sustainLength = minusStuff; - - updateNoteUI(); - updateGrid(); - } - - dummyArrow.x = Math.floor(FlxG.mouse.x / GRID_SIZE) * GRID_SIZE; - if (FlxG.keys.pressed.SHIFT) dummyArrow.y = FlxG.mouse.y; - else - dummyArrow.y = Math.floor(FlxG.mouse.y / GRID_SIZE) * GRID_SIZE; - } - - if (FlxG.keys.justPressed.ENTER) - { - autosaveSong(); - - lastSection = curSection; - - PlayState.currentSong = _song; - - // JUST FOR DEBUG DARNELL STUFF, GENERALIZE THIS FOR BETTER LOADING ELSEWHERE TOO! - PlayState.storyWeek = 8; - - FlxG.sound.music.stop(); - vocals.stop(); - LoadingState.loadAndSwitchState(new PlayState()); - // FlxG.switchState(new PlayState()); - } - - if (FlxG.keys.justPressed.E) - { - changeNoteSustain(Conductor.stepCrochet); - } - if (FlxG.keys.justPressed.Q) - { - changeNoteSustain(-Conductor.stepCrochet); - } - - if (FlxG.keys.justPressed.TAB) - { - if (FlxG.keys.pressed.SHIFT) - { - UI_box.selected_tab -= 1; - if (UI_box.selected_tab < 0) UI_box.selected_tab = 2; - } - else - { - UI_box.selected_tab += 1; - if (UI_box.selected_tab >= 3) UI_box.selected_tab = 0; - } - } - - if (!typingShit.hasFocus) - { - if (FlxG.keys.justPressed.SPACE) - { - if (FlxG.sound.music.playing) - { - FlxG.sound.music.pause(); - vocals.pause(); - } - else - { - vocals.play(); - FlxG.sound.music.play(); - } - } - - if (FlxG.keys.justPressed.R) - { - if (FlxG.keys.pressed.CONTROL) resetSection(BEGINNING); - else if (FlxG.keys.pressed.SHIFT) resetSection(MEASURE); - else - resetSection(SECTION); - } - - if (FlxG.mouse.wheel != 0) - { - FlxG.sound.music.pause(); - vocals.pause(); - - var ctrlMod:Float = FlxG.keys.pressed.CONTROL ? 0.1 : 1; - var shiftMod:Float = FlxG.keys.pressed.SHIFT ? 2 : 1; - - FlxG.sound.music.time -= (FlxG.mouse.wheel * Conductor.stepCrochet * 0.4 * ctrlMod * shiftMod); - vocals.time = FlxG.sound.music.time; - } - - if (FlxG.keys.justReleased.S) - { - FlxG.sound.music.pause(); - vocals.pause(); - - #if HAS_PITCH - FlxG.sound.music.pitch = 1; - vocals.pitch = 1; - #end - } - - if (!FlxG.keys.pressed.SHIFT) - { - if (FlxG.keys.pressed.W || FlxG.keys.pressed.S) - { - var daTime:Float = 700 * elapsed; - - if (FlxG.keys.pressed.CONTROL) daTime *= 0.2; - - if (FlxG.keys.pressed.W) - { - FlxG.sound.music.pause(); - vocals.pause(); - FlxG.sound.music.time -= daTime; - vocals.time = FlxG.sound.music.time; - } - else - { - if (FlxG.keys.justPressed.S) - { - FlxG.sound.music.play(); - vocals.play(); - - #if HAS_PITCH - FlxG.sound.music.pitch = 0.5; - vocals.pitch = 0.5; - #end - } - } - // FlxG.sound.music.time += daTime; - - // vocals.time = FlxG.sound.music.time; - } - } - else - { - if (FlxG.keys.justPressed.W || FlxG.keys.justPressed.S) - { - var daTime:Float = Conductor.stepCrochet * 2; - - if (FlxG.keys.justPressed.W) - { - FlxG.sound.music.pause(); - vocals.pause(); - - FlxG.sound.music.time -= daTime; - vocals.time = FlxG.sound.music.time; - } - else - { - if (FlxG.keys.justPressed.S) - { - // FlxG.sound.music.time += daTime; - - FlxG.sound.music.pause(); - vocals.pause(); - - FlxG.sound.music.time += daTime; - vocals.time = FlxG.sound.music.time; - - // FlxG.sound.music.play(); - // vocals.play(); - - #if HAS_PITCH - FlxG.sound.music.pitch = 0.2; - vocals.pitch = 0.2; - #end - } - } - } - } - } - - _song.bpm = tempBpm; - - /* if (FlxG.keys.justPressed.UP) - Conductor.bpm = Conductor.bpm + 1; - if (FlxG.keys.justPressed.DOWN) - Conductor.bpm = Conductor.bpm - 1; */ - - var shiftThing:Int = 1; - if (FlxG.keys.pressed.SHIFT) shiftThing = 4; - if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.D) changeSection(curSection + shiftThing); - if (FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.A) changeSection(curSection - shiftThing); - - bpmTxt.text = bpmTxt.text = Std.string(FlxMath.roundDecimal(Conductor.songPosition / 1000, 3)) - + " / " - + Std.string(FlxMath.roundDecimal(FlxG.sound.music.length / 1000, 3)) - + "\nSection: " - + curSection; - super.update(elapsed); - } - - function changeNoteSustain(value:Float):Void - { - if (curSelectedNote != null) - { - curSelectedNote.sustainLength += value; - curSelectedNote.sustainLength = Math.max(curSelectedNote.sustainLength, 0); - } - - updateNoteUI(); - updateGrid(); - } - - function toggleAltAnimNote():Void - { - if (curSelectedNote != null) - { - trace('ALT NOTE SHIT'); - curSelectedNote.noteKind = (curSelectedNote.noteKind == "alt") ? "" : "alt"; - trace(curSelectedNote.noteKind); - } - } - - function recalculateSteps():Float - { - var lastChange:BPMChangeEvent = - { - stepTime: 0, - songTime: 0, - bpm: 0 - } - for (i in 0...Conductor.bpmChangeMap.length) - { - if (FlxG.sound.music.time > Conductor.bpmChangeMap[i].songTime) lastChange = Conductor.bpmChangeMap[i]; - } - - // curStep = lastChange.stepTime + Math.floor((FlxG.sound.music.time - lastChange.songTime) / Conductor.stepCrochet); - // updateBeat(); - - return Conductor.currentStep; - } - - function resetSection(songBeginning:SongResetType = SECTION):Void - { - updateGrid(); - - FlxG.sound.music.pause(); - vocals.pause(); - - switch (songBeginning) - { - case SECTION: - // Basically old shit from changeSection??? - FlxG.sound.music.time = sectionStartTime(); - case BEGINNING: - FlxG.sound.music.time = 0; - curSection = 0; - case MEASURE: - FlxG.sound.music.time = measureStartTime(); // Math.floor(FlxG.mouse.y / GRID_SIZE) * GRID_SIZE - default: - } - - vocals.time = FlxG.sound.music.time; - // updateCurStep(); - - updateGrid(); - updateSectionUI(); - } - - function changeSection(sec:Int = 0, ?updateMusic:Bool = true):Void - { - // trace('changing section' + sec); - - if (SongLoad.getSong()[sec] != null) - { - curSection = sec; - - updateGrid(); - - if (updateMusic) - { - FlxG.sound.music.pause(); - vocals.pause(); - - /*var daNum:Int = 0; - var daLength:Float = 0; - while (daNum <= sec) - { - daLength += lengthBpmBullshit(); - daNum++; - }*/ - - FlxG.sound.music.time = sectionStartTime(); - vocals.time = FlxG.sound.music.time; - // updateCurStep(); - } - - updateGrid(); - updateSectionUI(); - } - } - - function copySection(?sectionNum:Int = 1) - { - var daSec = FlxMath.maxInt(curSection, sectionNum); - - for (noteShit in SongLoad.getSong()[daSec - sectionNum].sectionNotes) - { - var strum = noteShit.strumTime + Conductor.stepCrochet * (SongLoad.getSong()[daSec].lengthInSteps * sectionNum); - - var copiedNote:Note = new Note(strum, noteShit.noteData); - copiedNote.data.sustainLength = noteShit.sustainLength; - SongLoad.getSong()[daSec].sectionNotes.push(copiedNote.data); - } - - updateGrid(); - } - - function updateSectionUI():Void - { - var sec = SongLoad.getSong()[curSection]; - - stepperLength.value = sec.lengthInSteps; - check_mustHitSection.checked = sec.mustHitSection; - check_altAnim.checked = sec.altAnim; - check_changeBPM.checked = sec.changeBPM; - stepperSectionBPM.value = sec.bpm; - - updateHeads(); - } - - function updateHeads():Void - { - if (check_mustHitSection.checked) - { - leftIcon.characterId = (_song.player1); - rightIcon.characterId = (_song.player2); - - // leftIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0; - // rightIcon.animation.curAnim.curFrame = p2Muted ? 1 : 0; - // - // leftIcon.playAnimation(p1Muted ? LOSING : IDLE); - // rightIcon.playAnimation(p2Muted ? LOSING : IDLE); - } - else - { - leftIcon.characterId = (_song.player2); - rightIcon.characterId = (_song.player1); - - // leftIcon.playAnimation(p2Muted ? LOSING : IDLE); - // rightIcon.playAnimation(p1Muted ? LOSING : IDLE); - - // leftIcon.animation.curAnim.curFrame = p2Muted ? 1 : 0; - // rightIcon.animation.curAnim.curFrame = p1Muted ? 1 : 0; - } - leftIcon.setGraphicSize(0, 45); - rightIcon.setGraphicSize(0, 45); - - leftIcon.height *= 0.6; - rightIcon.height *= 0.6; - } - - function updateNoteUI():Void - { - if (curSelectedNote != null) stepperSusLength.value = curSelectedNote.sustainLength; - } - - function updateGrid():Void - { - // null if checks jus cuz i put updateGrid() in some weird places! - if (staticSpecGrp != null) - { - staticSpecGrp.forEach(function(spec) { - if (spec != null) spec.generateSection(sectionStartTime(), (Conductor.stepCrochet * 32) / 1000); - }); - } - - while (curRenderedNotes.members.length > 0) - { - curRenderedNotes.remove(curRenderedNotes.members[0], true); - } - - while (curRenderedSustains.members.length > 0) - { - curRenderedSustains.remove(curRenderedSustains.members[0], true); - } - - // generates the cool sidebar shit - if (sidePreview != null && sidePreview.active) - { - sidePreview.drawRect(0, 0, 40, FlxG.height, 0xFF444444); - - /* - var sectionsNeeded:Int = Std.int(FlxG.sound.music.length / (sectionCalc(_song.bpm) * 4)); - - while (sectionsNeeded > 0) - { - sidePreview.drawRect(0, sectionsNeeded * (FlxG.height / sectionsNeeded), 40, FlxG.height / sectionsNeeded, - (sectionsNeeded % 2 == 0 ? 0xFF000000 : 0xFFFFFFFF)); - - sectionsNeeded--; - } - */ - - for (secIndex => sideSection in SongLoad.getSong()) - { - for (notes in sideSection.sectionNotes) - { - var col:Int = Note.codeColors[notes.noteData % 4]; - - var noteFlip:Int = (sideSection.mustHitSection ? 1 : -1); - var noteX:Float = 5 * (((notes.noteData - 4) * noteFlip) + 4); - - sidePreview.drawRect(noteX, FlxMath.remapToRange(notes.strumTime, 0, FlxG.sound.music.length, 0, FlxG.height), 5, 1, col); - } - } - } - - var sectionInfo:Array = SongLoad.getSong()[curSection].sectionNotes; - - if (SongLoad.getSong()[curSection].changeBPM && SongLoad.getSong()[curSection].bpm > 0) - { - Conductor.forceBPM(SongLoad.getSong()[curSection].bpm); - FlxG.log.add('CHANGED BPM!'); - } - else - { - // get last bpm - var daBPM:Float = _song.bpm; - for (i in 0...curSection) - if (SongLoad.getSong()[i].changeBPM) daBPM = SongLoad.getSong()[i].bpm; - Conductor.forceBPM(daBPM); - } - - /* // PORT BULLSHIT, INCASE THERE'S NO SUSTAIN DATA FOR A NOTE - for (sec in 0...SongLoad.getSong().length) - { - for (notesse in 0...SongLoad.getSong()[sec].sectionNotes.length) - { - if (SongLoad.getSong()[sec].sectionNotes[notesse][2] == null) - { - trace('SUS NULL'); - SongLoad.getSong()[sec].sectionNotes[notesse][2] = 0; - } - } - } - */ - - for (i in sectionInfo) - { - var daNoteInfo = i.noteData; - var daStrumTime = i.strumTime; - var daSus = i.sustainLength; - - var note:Note = new Note(daStrumTime, daNoteInfo % 4); - note.data.sustainLength = daSus; - note.setGraphicSize(GRID_SIZE, GRID_SIZE); - note.updateHitbox(); - note.x = Math.floor(daNoteInfo * GRID_SIZE); - note.y = Math.floor(getYfromStrum((daStrumTime - sectionStartTime()) % (Conductor.stepCrochet * SongLoad.getSong()[curSection].lengthInSteps))); - - curRenderedNotes.add(note); - - if (daSus > 0) - { - var sustainVis:FlxSprite = new FlxSprite(note.x + (GRID_SIZE / 2), - note.y + GRID_SIZE).makeGraphic(8, Math.floor(FlxMath.remapToRange(daSus, 0, Conductor.stepCrochet * 16, 0, gridBG.height))); - sustainVis.x -= sustainVis.width / 2; - sustainVis.color = Note.codeColors[note.data.noteData % 4]; - curRenderedSustains.add(sustainVis); - } - } - } - - function addSection(lengthInSteps:Int = 16):Void - { - var sec:SwagSection = - { - lengthInSteps: lengthInSteps, - bpm: _song.bpm, - changeBPM: false, - mustHitSection: true, - sectionNotes: [], - typeOfSection: 0, - altAnim: false - }; - - SongLoad.getSong().push(sec); - } - - function selectNote(note:Note):Void - { - var swagNum:Int = 0; - - for (i in SongLoad.getSong()[curSection].sectionNotes) - { - if (i.strumTime == note.data.strumTime && i.noteData % 4 == note.data.noteData) - { - curSelectedNote = SongLoad.getSong()[curSection].sectionNotes[swagNum]; - } - - swagNum += 1; - } - - updateGrid(); - updateNoteUI(); - } - - function deleteNote(note:Note):Void - { - for (i in SongLoad.getSong()[curSection].sectionNotes) - { - if (i.strumTime == note.data.strumTime && i.noteData % 4 == note.data.noteData) - { - var placeIDK:Int = Std.int(((Math.floor(dummyArrow.y / GRID_SIZE) * GRID_SIZE)) / 40); - - placeIDK = Std.int(Math.min(placeIDK, 15)); - placeIDK = Std.int(Math.max(placeIDK, 1)); - - trace(placeIDK); - FlxG.sound.play(Paths.sound('funnyNoise/funnyNoise-0' + placeIDK)); - - FlxG.log.add('FOUND EVIL NUMBER'); - SongLoad.getSong()[curSection].sectionNotes.remove(i); - } - } - - updateGrid(); - } - - function clearSection():Void - { - SongLoad.getSong()[curSection].sectionNotes = []; - - updateGrid(); - } - - function clearSong():Void - { - for (daSection in 0...SongLoad.getSong().length) - { - SongLoad.getSong()[daSection].sectionNotes = []; - } - - updateGrid(); - } - - /** - * Is true if clicked and placed a note, set reset to false when releasing mouse button! - */ - var justPlacedNote:Bool = false; - - function addNote():Void - { - var noteStrum = getStrumTime(dummyArrow.y) + sectionStartTime(); - var noteData = Math.floor(FlxG.mouse.x / GRID_SIZE); - var noteSus = 0; - var noteKind = ""; - - justPlacedNote = true; - - // FlxG.sound.play(Paths.sound('pianoStuff/piano-00' + FlxG.random.int(1, 9)), FlxG.random.float(0.01, 0.3)); - - function makeAndPlayChord(soundsToPlay:Array) - { - var bullshit:Int = Std.int((Math.floor(dummyArrow.y / GRID_SIZE) * GRID_SIZE) / 40); - soundsToPlay.push('00' + Std.string((bullshit % 8) + 1)); - - for (key in soundsToPlay) - { - var snd:FlxSound = FlxG.sound.list.recycle(FlxSound).loadEmbedded(FlxG.sound.cache(Paths.sound("pianoStuff/piano-" + key))); - snd.autoDestroy = true; - FlxG.sound.list.add(snd); - snd.volume = FlxG.random.float(0.05, 0.7); - snd.pan = noteData - 2; // .... idk why tf panning doesnt work? (as of 2022/01/25) busted ass bullshit. I only went thru this fuss of creating FlxSound just for the panning! - - // snd.proximity(FlxG.mouse.x, FlxG.mouse.y, gridBG, gridBG.width / 2); - - snd.play(); - } - } - - switch (noteData) - { - case 0: - makeAndPlayChord(["015", "013", "009"]); - case 1: - makeAndPlayChord(["015", "012", "009"]); - case 2: - makeAndPlayChord(["015", "011", "009"]); - case 3: - makeAndPlayChord(["014", "011", "010"]); - } - - // trace('bullshit $bullshit'); // trace(Math.floor(dummyArrow.y / GRID_SIZE) * GRID_SIZE); - - var daNewNote:Note = new Note(noteStrum, noteData); - daNewNote.data.sustainLength = noteSus; - daNewNote.data.noteKind = noteKind; - SongLoad.getSong()[curSection].sectionNotes.push(daNewNote.data); - - curSelectedNote = SongLoad.getSong()[curSection].sectionNotes[SongLoad.getSong()[curSection].sectionNotes.length - 1]; - - if (FlxG.keys.pressed.CONTROL) - { - // SongLoad.getSong()[curSection].sectionNotes.push([noteStrum, (noteData + 4) % 8, noteSus, noteAlt]); - } - - trace(noteStrum); - trace(curSection); - - updateGrid(); - updateNoteUI(); - - autosaveSong(); - } - - function getStrumTime(yPos:Float):Float - { - return FlxMath.remapToRange(yPos, gridBG.y, gridBG.y + gridBG.height, 0, 16 * Conductor.stepCrochet); - } - - function getYfromStrum(strumTime:Float):Float - { - return FlxMath.remapToRange(strumTime, 0, 16 * Conductor.stepCrochet, gridBG.y, gridBG.y + gridBG.height); - } - - /* - function calculateSectionLengths(?sec:SwagSection):Int - { - var daLength:Int = 0; - - for (i in SongLoad.getSong()) - { - var swagLength = i.lengthInSteps; - - if (i.typeOfSection == Section.COPYCAT) - swagLength * 2; - - daLength += swagLength; - - if (sec != null && sec == i) - { - trace('swag loop??'); - break; - } - } - - return daLength; - }*/ - var daSpacing:Float = 0.3; - - function loadLevel():Void - { - trace(SongLoad.getSong()); - } - - function getNotes():Array - { - var noteData:Array = []; - - for (i in SongLoad.getSong()) - { - noteData.push(i.sectionNotes); - } - - return noteData; - } - - function loadJson(song:String):Void - { - PlayState.currentSong = SongLoad.loadFromJson(song.toLowerCase(), song.toLowerCase()); - LoadingState.loadAndSwitchState(new ChartingState()); - } - - function loadAutosave():Void - { - PlayState.currentSong = FlxG.save.data.autosave; - FlxG.resetState(); - } - - function autosaveSong():Void - { - FlxG.save.data.autosave = _song; - // trace(FlxG.save.data.autosave); - FlxG.save.flush(); - } - - function saveLevel(?debugSavepath:Bool = false) - { - // Right now the note data is saved as a Note.NoteData typedef / object or whatev - // we want to format it to an ARRAY. We turn it back into the typedef / object at the end of this function hehe - - for (key in _song.noteMap.keys()) - SongLoad.castNoteDataToArray(_song.noteMap[key]); - - // SongLoad.castNoteDataToArray(_song.notes.easy); - // SongLoad.castNoteDataToArray(_song.notes.normal); - // SongLoad.castNoteDataToArray(_song.notes.hard); - - var json = {"song": _song}; - var data:String = Json.stringify(json, null, "\t"); - - #if sys - // quick workaround, since it easier to load into hashlink, thus quicker/nicer to test? - // should get this auto-saved into a file or somethin - var filename = _song.song.toLowerCase(); - - if (debugSavepath) - { - // file path to assumingly your assets folder in your SOURCE CODE assets folder!!! - // update this later so the save button ONLY appears when you compile in debug mode! - sys.io.File.saveContent('../../../../assets/preload/data/$filename/$filename.json', data); - } - else - sys.io.File.saveContent('./$filename.json', data); - #else - if ((data != null) && (data.length > 0)) - { - _file = new FileReference(); - _file.addEventListener(Event.COMPLETE, onSaveComplete); - _file.addEventListener(Event.CANCEL, onSaveCancel); - _file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file.save(data.trim(), _song.song.toLowerCase() + ".json"); - } - #end - - for (key in _song.noteMap.keys()) - SongLoad.castArrayToNoteData(_song.noteMap[key]); - - // turn the array data back to Note.NoteData typedef - // SongLoad.castArrayToNoteData(_song.notes.easy); - // SongLoad.castArrayToNoteData(_song.notes.normal); - // SongLoad.castArrayToNoteData(_song.notes.hard); - } - - function onSaveComplete(_):Void - { - _file.removeEventListener(Event.COMPLETE, onSaveComplete); - _file.removeEventListener(Event.CANCEL, onSaveCancel); - _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file = null; - FlxG.log.notice("Successfully saved LEVEL DATA."); - } - - /** - * Called when the save file dialog is cancelled. - */ - function onSaveCancel(_):Void - { - _file.removeEventListener(Event.COMPLETE, onSaveComplete); - _file.removeEventListener(Event.CANCEL, onSaveCancel); - _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file = null; - } - - /** - * Called if there is an error while saving the gameplay recording. - */ - function onSaveError(_):Void - { - _file.removeEventListener(Event.COMPLETE, onSaveComplete); - _file.removeEventListener(Event.CANCEL, onSaveCancel); - _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file = null; - FlxG.log.error("Problem saving Level data"); - } -} - -enum SongResetType -{ - BEGINNING; - MEASURE; // not sure if measure is 1/4 of a "SECTION" which is definitely a... bar.. right? its nerd shit whatever - SECTION; -} diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx index 0864fddd9..b30c311a3 100644 --- a/source/funkin/data/BaseRegistry.hx +++ b/source/funkin/data/BaseRegistry.hx @@ -11,7 +11,7 @@ typedef EntryConstructorFunction = String->Void; /** * A base type for a Registry, which is an object which handles loading scriptable objects. - * + * * @param T The type to construct. Must implement `IRegistryEntry`. * @param J The type of the JSON data used when constructing. */ @@ -139,8 +139,8 @@ abstract class BaseRegistry & Constructible; @@ -161,7 +161,7 @@ abstract class BaseRegistry & Constructible; } diff --git a/source/funkin/freeplayStuff/BGScrollingText.hx b/source/funkin/freeplayStuff/BGScrollingText.hx index 1091afbe9..9fa6dd49b 100644 --- a/source/funkin/freeplayStuff/BGScrollingText.hx +++ b/source/funkin/freeplayStuff/BGScrollingText.hx @@ -48,8 +48,7 @@ class BGScrollingText extends FlxSpriteGroup function set_funnyColor(col:Int):Int { - grpTexts.forEach(function(txt) - { + grpTexts.forEach(function(txt) { txt.color = col; }); @@ -85,8 +84,7 @@ class BGScrollingText extends FlxSpriteGroup function sortTextShit():Void { - grpTexts.sort(function(Order:Int, Obj1:FlxObject, Obj2:FlxObject) - { + grpTexts.sort(function(Order:Int, Obj1:FlxObject, Obj2:FlxObject) { return FlxSort.byValues(Order, Obj1.x, Obj2.x); }); } diff --git a/source/funkin/graphics/rendering/SustainTrail.hx b/source/funkin/graphics/rendering/SustainTrail.hx index 80625b419..d9f43584e 100644 --- a/source/funkin/graphics/rendering/SustainTrail.hx +++ b/source/funkin/graphics/rendering/SustainTrail.hx @@ -77,9 +77,9 @@ class SustainTrail extends FlxSprite /** * Normally you would take strumTime:Float, noteData:Int, sustainLength:Float, parentNote:Note (?) - * @param NoteData - * @param SustainLength - * @param FileName + * @param NoteData + * @param SustainLength + * @param FileName */ public function new(NoteData:Int, SustainLength:Float, Path:String, ?Alpha:Float = 0.6, ?Pixel:Bool = false) { diff --git a/source/funkin/graphics/video/FlxVideo.hx b/source/funkin/graphics/video/FlxVideo.hx index db95c3d95..34b806f7e 100644 --- a/source/funkin/graphics/video/FlxVideo.hx +++ b/source/funkin/graphics/video/FlxVideo.hx @@ -18,7 +18,7 @@ class FlxVideo extends FlxBasic public var finishCallback:Void->Void; /** - * Doesn't actually interact with Flixel shit, only just a pleasant to use class + * Doesn't actually interact with Flixel shit, only just a pleasant to use class */ public function new(vidSrc:String) { diff --git a/source/funkin/input/Cursor.hx b/source/funkin/input/Cursor.hx index e965165f3..37e819469 100644 --- a/source/funkin/input/Cursor.hx +++ b/source/funkin/input/Cursor.hx @@ -123,8 +123,7 @@ class Cursor if (assetCursorDefault == null) { var future:Future = Assets.loadBitmapData(CURSOR_DEFAULT_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorDefault = bitmapData; applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS); }); @@ -138,8 +137,7 @@ class Cursor if (assetCursorCross == null) { var future:Future = Assets.loadBitmapData(CURSOR_CROSS_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorCross = bitmapData; applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS); }); @@ -153,8 +151,7 @@ class Cursor if (assetCursorEraser == null) { var future:Future = Assets.loadBitmapData(CURSOR_ERASER_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorEraser = bitmapData; applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS); }); @@ -168,8 +165,7 @@ class Cursor if (assetCursorGrabbing == null) { var future:Future = Assets.loadBitmapData(CURSOR_GRABBING_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorGrabbing = bitmapData; applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS); }); @@ -183,8 +179,7 @@ class Cursor if (assetCursorHourglass == null) { var future:Future = Assets.loadBitmapData(CURSOR_HOURGLASS_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorHourglass = bitmapData; applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS); }); @@ -198,8 +193,7 @@ class Cursor if (assetCursorPointer == null) { var future:Future = Assets.loadBitmapData(CURSOR_POINTER_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorPointer = bitmapData; applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS); }); @@ -213,8 +207,7 @@ class Cursor if (assetCursorText == null) { var future:Future = Assets.loadBitmapData(CURSOR_TEXT_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorText = bitmapData; applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS); }); @@ -228,8 +221,7 @@ class Cursor if (assetCursorZoomIn == null) { var future:Future = Assets.loadBitmapData(CURSOR_ZOOM_IN_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorZoomIn = bitmapData; applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS); }); @@ -243,8 +235,7 @@ class Cursor if (assetCursorZoomOut == null) { var future:Future = Assets.loadBitmapData(CURSOR_ZOOM_OUT_PARAMS.graphic); - future.onComplete(function(bitmapData:BitmapData) - { + future.onComplete(function(bitmapData:BitmapData) { assetCursorZoomOut = bitmapData; applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); }); diff --git a/source/funkin/input/TurboKeyHandler.hx b/source/funkin/input/TurboKeyHandler.hx index 9181f836b..3719ff7cc 100644 --- a/source/funkin/input/TurboKeyHandler.hx +++ b/source/funkin/input/TurboKeyHandler.hx @@ -5,10 +5,10 @@ import flixel.FlxBasic; /** * Handles repeating behavior when holding down a key or key combination. - * + * * When the `keys` are pressed, `activated` will be true for the first frame, * then wait `delay` seconds before becoming true for one frame every `interval` seconds. - * + * * Example: Pressing Ctrl+Z will undo, while holding Ctrl+Z will start to undo repeatedly. */ class TurboKeyHandler extends FlxBasic diff --git a/source/funkin/modding/IScriptedClass.hx b/source/funkin/modding/IScriptedClass.hx index 1450f8045..abcce483f 100644 --- a/source/funkin/modding/IScriptedClass.hx +++ b/source/funkin/modding/IScriptedClass.hx @@ -4,7 +4,7 @@ import funkin.modding.events.ScriptEvent; /** * Defines a set of callbacks available to all scripted classes. - * + * * Includes events handling basic life cycle relevant to all scripted classes. */ interface IScriptedClass @@ -24,10 +24,10 @@ interface IStateChangingScriptedClass extends IScriptedClass public function onStateChangeBegin(event:StateChangeScriptEvent):Void; public function onStateChangeEnd(event:StateChangeScriptEvent):Void; - public function onSubstateOpenBegin(event:SubStateScriptEvent):Void; - public function onSubstateOpenEnd(event:SubStateScriptEvent):Void; - public function onSubstateCloseBegin(event:SubStateScriptEvent):Void; - public function onSubstateCloseEnd(event:SubStateScriptEvent):Void; + public function onSubStateOpenBegin(event:SubStateScriptEvent):Void; + public function onSubStateOpenEnd(event:SubStateScriptEvent):Void; + public function onSubStateCloseBegin(event:SubStateScriptEvent):Void; + public function onSubStateCloseEnd(event:SubStateScriptEvent):Void; } /** @@ -53,7 +53,7 @@ interface INoteScriptedClass extends IScriptedClass /** * Developer note: - * + * * I previously considered adding events for onKeyDown, onKeyUp, mouse events, etc. * However, I realized that you can simply call something like the following within a module: * `FlxG.state.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);` diff --git a/source/funkin/modding/PolymodErrorHandler.hx b/source/funkin/modding/PolymodErrorHandler.hx index 70a716821..dc387900b 100644 --- a/source/funkin/modding/PolymodErrorHandler.hx +++ b/source/funkin/modding/PolymodErrorHandler.hx @@ -8,7 +8,7 @@ class PolymodErrorHandler * Show a popup with the given text. * This displays a system popup, it WILL interrupt the game. * Make sure to only use this when it's important, like when there's a script error. - * + * * @param name The name at the top of the popup. * @param desc The body text of the popup. */ diff --git a/source/funkin/modding/base/ScriptedMusicBeatSubState.hx b/source/funkin/modding/base/ScriptedMusicBeatSubState.hx new file mode 100644 index 000000000..7dab3d7dd --- /dev/null +++ b/source/funkin/modding/base/ScriptedMusicBeatSubState.hx @@ -0,0 +1,8 @@ +package funkin.modding.base; + +/** + * A script that can be tied to a MusicBeatSubState. + * Create a scripted class that extends MusicBeatSubState to use this. + */ +@:hscriptClass +class ScriptedMusicBeatSubState extends funkin.MusicBeatSubState implements HScriptedClass {} diff --git a/source/funkin/modding/base/ScriptedMusicBeatSubstate.hx b/source/funkin/modding/base/ScriptedMusicBeatSubstate.hx deleted file mode 100644 index d0d764aeb..000000000 --- a/source/funkin/modding/base/ScriptedMusicBeatSubstate.hx +++ /dev/null @@ -1,8 +0,0 @@ -package funkin.modding.base; - -/** - * A script that can be tied to a MusicBeatSubstate. - * Create a scripted class that extends MusicBeatSubstate to use this. - */ -@:hscriptClass -class ScriptedMusicBeatSubstate extends funkin.MusicBeatSubstate implements HScriptedClass {} diff --git a/source/funkin/modding/events/ScriptEvent.hx b/source/funkin/modding/events/ScriptEvent.hx index c99db1d0f..ef67ba64a 100644 --- a/source/funkin/modding/events/ScriptEvent.hx +++ b/source/funkin/modding/events/ScriptEvent.hx @@ -19,23 +19,23 @@ class ScriptEvent * Called when the relevant object is created. * Keep in mind that the constructor may be called before the object is needed, * for the purposes of caching data or otherwise. - * + * * This event is not cancelable. */ - public static inline final CREATE:ScriptEventType = "CREATE"; + public static inline final CREATE:ScriptEventType = 'CREATE'; /** * Called when the relevant object is destroyed. * This should perform relevant cleanup to ensure good performance. - * + * * This event is not cancelable. */ - public static inline final DESTROY:ScriptEventType = "DESTROY"; + public static inline final DESTROY:ScriptEventType = 'DESTROY'; /** * Called when the relevent object is added to the game state. * This assumes all data is loaded and ready to go. - * + * * This event is not cancelable. */ public static inline final ADDED:ScriptEventType = 'ADDED'; @@ -43,38 +43,38 @@ class ScriptEvent /** * Called during the update function. * This is called every frame, so be careful! - * + * * This event is not cancelable. */ - public static inline final UPDATE:ScriptEventType = "UPDATE"; + public static inline final UPDATE:ScriptEventType = 'UPDATE'; /** * Called when the player moves to pause the game. - * + * * This event IS cancelable! Canceling the event will prevent the game from pausing. */ - public static inline final PAUSE:ScriptEventType = "PAUSE"; + public static inline final PAUSE:ScriptEventType = 'PAUSE'; /** * Called when the player moves to unpause the game while paused. - * + * * This event IS cancelable! Canceling the event will prevent the game from resuming. */ - public static inline final RESUME:ScriptEventType = "RESUME"; + public static inline final RESUME:ScriptEventType = 'RESUME'; /** * Called once per step in the song. This happens 4 times per measure. - * + * * This event is not cancelable. */ - public static inline final SONG_BEAT_HIT:ScriptEventType = "BEAT_HIT"; + public static inline final SONG_BEAT_HIT:ScriptEventType = 'BEAT_HIT'; /** * Called once per step in the song. This happens 16 times per measure. - * + * * This event is not cancelable. */ - public static inline final SONG_STEP_HIT:ScriptEventType = "STEP_HIT"; + public static inline final SONG_STEP_HIT:ScriptEventType = 'STEP_HIT'; /** * Called when a character hits a note. @@ -83,7 +83,7 @@ class ScriptEvent * This event IS cancelable! Canceling this event prevents the note from being hit, * and will likely result in a miss later. */ - public static inline final NOTE_HIT:ScriptEventType = "NOTE_HIT"; + public static inline final NOTE_HIT:ScriptEventType = 'NOTE_HIT'; /** * Called when a character misses a note. @@ -92,7 +92,7 @@ class ScriptEvent * This event IS cancelable! Canceling this event prevents the note from being considered missed, * avoiding a combo break and lost health. */ - public static inline final NOTE_MISS:ScriptEventType = "NOTE_MISS"; + public static inline final NOTE_MISS:ScriptEventType = 'NOTE_MISS'; /** * Called when a character presses a note when there was none there, causing them to lose health. @@ -101,137 +101,137 @@ class ScriptEvent * This event IS cancelable! Canceling this event prevents the note from being considered missed, * avoiding lost health/score and preventing the miss animation. */ - public static inline final NOTE_GHOST_MISS:ScriptEventType = "NOTE_GHOST_MISS"; + public static inline final NOTE_GHOST_MISS:ScriptEventType = 'NOTE_GHOST_MISS'; /** * Called when a song event is reached in the chart. - * + * * This event IS cancelable! Cancelling this event prevents the event from being triggered, * thus blocking its normal functionality. */ - public static inline final SONG_EVENT:ScriptEventType = "SONG_EVENT"; + public static inline final SONG_EVENT:ScriptEventType = 'SONG_EVENT'; /** * Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin. - * + * * This event is not cancelable. */ - public static inline final SONG_START:ScriptEventType = "SONG_START"; + public static inline final SONG_START:ScriptEventType = 'SONG_START'; /** * Called when the song ends. This happens as the instrumental and vocals end. - * + * * This event is not cancelable. */ - public static inline final SONG_END:ScriptEventType = "SONG_END"; + public static inline final SONG_END:ScriptEventType = 'SONG_END'; /** * Called when the countdown begins. This occurs before the song starts. - * + * * This event IS cancelable! Canceling this event will prevent the countdown from starting. * - The song will not start until you call Countdown.performCountdown() later. * - Note that calling performCountdown() will trigger this event again, so be sure to add logic to ignore it. */ - public static inline final COUNTDOWN_START:ScriptEventType = "COUNTDOWN_START"; + public static inline final COUNTDOWN_START:ScriptEventType = 'COUNTDOWN_START'; /** * Called when a step of the countdown happens. * Includes information about what step of the countdown was hit. - * + * * This event IS cancelable! Canceling this event will pause the countdown. * - The countdown will not resume until you call PlayState.resumeCountdown(). */ - public static inline final COUNTDOWN_STEP:ScriptEventType = "COUNTDOWN_STEP"; + public static inline final COUNTDOWN_STEP:ScriptEventType = 'COUNTDOWN_STEP'; /** * Called when the countdown is done but just before the song starts. - * + * * This event is not cancelable. */ - public static inline final COUNTDOWN_END:ScriptEventType = "COUNTDOWN_END"; + public static inline final COUNTDOWN_END:ScriptEventType = 'COUNTDOWN_END'; /** * Called before the game over screen triggers and the death animation plays. - * + * * This event is not cancelable. */ - public static inline final GAME_OVER:ScriptEventType = "GAME_OVER"; + public static inline final GAME_OVER:ScriptEventType = 'GAME_OVER'; /** * Called after the player presses a key to restart the game. * This can happen from the pause menu or the game over screen. - * + * * This event IS cancelable! Canceling this event will prevent the game from restarting. */ - public static inline final SONG_RETRY:ScriptEventType = "SONG_RETRY"; + public static inline final SONG_RETRY:ScriptEventType = 'SONG_RETRY'; /** * Called when the player pushes down any key on the keyboard. - * + * * This event is not cancelable. */ - public static inline final KEY_DOWN:ScriptEventType = "KEY_DOWN"; + public static inline final KEY_DOWN:ScriptEventType = 'KEY_DOWN'; /** * Called when the player releases a key on the keyboard. - * + * * This event is not cancelable. */ - public static inline final KEY_UP:ScriptEventType = "KEY_UP"; + public static inline final KEY_UP:ScriptEventType = 'KEY_UP'; /** * Called when the game has finished loading the notes from JSON. * This allows modders to mutate the notes before they are used in the song. - * + * * This event is not cancelable. */ - public static inline final SONG_LOADED:ScriptEventType = "SONG_LOADED"; + public static inline final SONG_LOADED:ScriptEventType = 'SONG_LOADED'; /** * Called when the game is about to switch the current FlxState. - * + * * This event is not cancelable. */ - public static inline final STATE_CHANGE_BEGIN:ScriptEventType = "STATE_CHANGE_BEGIN"; + public static inline final STATE_CHANGE_BEGIN:ScriptEventType = 'STATE_CHANGE_BEGIN'; /** * Called when the game has finished switching the current FlxState. - * + * * This event is not cancelable. */ - public static inline final STATE_CHANGE_END:ScriptEventType = "STATE_CHANGE_END"; + public static inline final STATE_CHANGE_END:ScriptEventType = 'STATE_CHANGE_END'; /** * Called when the game is about to open a new FlxSubState. - * + * * This event is not cancelable. */ - public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = "SUBSTATE_OPEN_BEGIN"; + public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = 'SUBSTATE_OPEN_BEGIN'; /** * Called when the game has finished opening a new FlxSubState. - * + * * This event is not cancelable. */ - public static inline final SUBSTATE_OPEN_END:ScriptEventType = "SUBSTATE_OPEN_END"; + public static inline final SUBSTATE_OPEN_END:ScriptEventType = 'SUBSTATE_OPEN_END'; /** * Called when the game is about to close the current FlxSubState. - * + * * This event is not cancelable. */ - public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = "SUBSTATE_CLOSE_BEGIN"; + public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = 'SUBSTATE_CLOSE_BEGIN'; /** * Called when the game has finished closing the current FlxSubState. - * + * * This event is not cancelable. */ - public static inline final SUBSTATE_CLOSE_END:ScriptEventType = "SUBSTATE_CLOSE_END"; + public static inline final SUBSTATE_CLOSE_END:ScriptEventType = 'SUBSTATE_CLOSE_END'; /** * Called when the game is exiting the current FlxState. - * + * * This event is not cancelable. */ /** @@ -276,9 +276,12 @@ class ScriptEvent } } + /** + * Cancel this event. + * This is an alias for cancelEvent() but I make this typo all the time. + */ public function cancel():Void { - // This typo happens enough that I just added this. cancelEvent(); } @@ -316,11 +319,17 @@ class NoteScriptEvent extends ScriptEvent */ public var comboCount(default, null):Int; + /** + * Whether to play the record scratch sound (if this eventn type is `NOTE_MISS`). + */ + public var playSound(default, default):Bool; + public function new(type:ScriptEventType, note:Note, comboCount:Int = 0, cancelable:Bool = false):Void { super(type, cancelable); this.note = note; this.comboCount = comboCount; + this.playSound = true; } public override function toString():String @@ -468,7 +477,7 @@ class CountdownScriptEvent extends ScriptEvent */ public var step(default, null):CountdownStep; - public function new(type:ScriptEventType, step:CountdownStep, cancelable = true):Void + public function new(type:ScriptEventType, step:CountdownStep, cancelable:Bool = true):Void { super(type, cancelable); this.step = step; diff --git a/source/funkin/modding/events/ScriptEventDispatcher.hx b/source/funkin/modding/events/ScriptEventDispatcher.hx index 18181a1d1..a816d748a 100644 --- a/source/funkin/modding/events/ScriptEventDispatcher.hx +++ b/source/funkin/modding/events/ScriptEventDispatcher.hx @@ -113,16 +113,16 @@ class ScriptEventDispatcher t.onStateChangeEnd(cast event); return; case ScriptEvent.SUBSTATE_OPEN_BEGIN: - t.onSubstateOpenBegin(cast event); + t.onSubStateOpenBegin(cast event); return; case ScriptEvent.SUBSTATE_OPEN_END: - t.onSubstateOpenEnd(cast event); + t.onSubStateOpenEnd(cast event); return; case ScriptEvent.SUBSTATE_CLOSE_BEGIN: - t.onSubstateCloseBegin(cast event); + t.onSubStateCloseBegin(cast event); return; case ScriptEvent.SUBSTATE_CLOSE_END: - t.onSubstateCloseEnd(cast event); + t.onSubStateCloseEnd(cast event); return; } } diff --git a/source/funkin/modding/module/Module.hx b/source/funkin/modding/module/Module.hx index d1a54b619..f50c9936a 100644 --- a/source/funkin/modding/module/Module.hx +++ b/source/funkin/modding/module/Module.hx @@ -25,7 +25,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte /** * Determines the order in which modules receive events. * You can modify this to change the order in which a given module receives events. - * + * * Priority 1 is processed before Priority 1000, etc. */ public var priority(default, set):Int; @@ -41,7 +41,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte /** * Called when the module is initialized. * It may not be safe to reference other modules here since they may not be loaded yet. - * + * * NOTE: To make the module start inactive, call `this.active = false` in the constructor. */ public function new(moduleId:String, priority:Int = 1000):Void @@ -107,13 +107,13 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte public function onStateChangeEnd(event:StateChangeScriptEvent) {} - public function onSubstateOpenBegin(event:SubStateScriptEvent) {} + public function onSubStateOpenBegin(event:SubStateScriptEvent) {} - public function onSubstateOpenEnd(event:SubStateScriptEvent) {} + public function onSubStateOpenEnd(event:SubStateScriptEvent) {} - public function onSubstateCloseBegin(event:SubStateScriptEvent) {} + public function onSubStateCloseBegin(event:SubStateScriptEvent) {} - public function onSubstateCloseEnd(event:SubStateScriptEvent) {} + public function onSubStateCloseEnd(event:SubStateScriptEvent) {} public function onSongRetry(event:ScriptEvent) {} } diff --git a/source/funkin/modding/module/ModuleHandler.hx b/source/funkin/modding/module/ModuleHandler.hx index 51d723d0d..20bd6425c 100644 --- a/source/funkin/modding/module/ModuleHandler.hx +++ b/source/funkin/modding/module/ModuleHandler.hx @@ -16,7 +16,7 @@ class ModuleHandler /** * Parses and preloads the game's stage data and scripts when the game starts. - * + * * If you want to force stages to be reloaded, you can just call this function again. */ public static function loadModuleCache():Void @@ -66,8 +66,7 @@ class ModuleHandler { modulePriorityOrder = moduleCache.keys().array(); - modulePriorityOrder.sort(function(a:String, b:String):Int - { + modulePriorityOrder.sort(function(a:String, b:String):Int { var aModule:Module = moduleCache.get(a); var bModule:Module = moduleCache.get(b); diff --git a/source/funkin/noteStuff/NoteUtil.hx b/source/funkin/noteStuff/NoteUtil.hx index 610f82a09..a36c32482 100644 --- a/source/funkin/noteStuff/NoteUtil.hx +++ b/source/funkin/noteStuff/NoteUtil.hx @@ -7,7 +7,7 @@ import openfl.Assets; * Just various functions that IDK where to put em!!! * Semi-temp for now? the note stuff is super clutter-y right now * so I am putting this new stuff here right now XDD - * + * * A lot of this stuff can probably be moved to where appropriate! * i dont care about NoteUtil.hx at all!!! */ @@ -15,7 +15,7 @@ class NoteUtil { /** * IDK THING FOR BOTH LOL! DIS SHIT HACK-Y - * @param jsonPath + * @param jsonPath * @return Map> */ public static function loadSongEvents(jsonPath:String):Map> @@ -34,7 +34,7 @@ class NoteUtil /** * Parses song event json stuff into a neater lil map grouping? - * @param songEvents + * @param songEvents */ public static function parseSongEvents(songEvents:Array):Map> { diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx index 0af098dbd..016b21c6c 100644 --- a/source/funkin/play/Countdown.hx +++ b/source/funkin/play/Countdown.hx @@ -37,7 +37,7 @@ class Countdown // Stop any existing countdown. stopCountdown(); - PlayState.isInCountdown = true; + PlayState.instance.isInCountdown = true; Conductor.songPosition = Conductor.crochet * -5; // Handle onBeatHit events manually @:privateAccess @@ -46,8 +46,7 @@ class Countdown // The timer function gets called based on the beat of the song. countdownTimer = new FlxTimer(); - countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer) - { + countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer) { countdownStep = decrement(countdownStep); // Handle onBeatHit events manually @@ -102,7 +101,7 @@ class Countdown /** * Pauses the countdown at the current step. You can start it up again later by calling resumeCountdown(). - * + * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ public static function pauseCountdown() @@ -115,7 +114,7 @@ class Countdown /** * Resumes the countdown at the current step. Only makes sense if you called pauseCountdown() first. - * + * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. */ public static function resumeCountdown() @@ -128,7 +127,7 @@ class Countdown /** * Stops the countdown at the current step. You will have to restart it again later. - * + * * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStart event. */ public static function stopCountdown() @@ -166,7 +165,7 @@ class Countdown /** * Retrieves the graphic to use for this step of the countdown. * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? - * + * * This is public so modules can do lol funny shit. */ public static function showCountdownGraphic(index:CountdownStep, isPixelStyle:Bool):Void @@ -216,8 +215,7 @@ class Countdown FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.crochet / 1000, { ease: FlxEase.cubeInOut, - onComplete: function(twn:FlxTween) - { + onComplete: function(twn:FlxTween) { countdownSprite.destroy(); } }); @@ -228,7 +226,7 @@ class Countdown /** * Retrieves the sound file to use for this step of the countdown. * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? - * + * * This is public so modules can do lol funny shit. */ public static function playCountdownSound(index:CountdownStep, isPixelStyle:Bool):Void diff --git a/source/funkin/play/GameOverSubstate.hx b/source/funkin/play/GameOverSubState.hx similarity index 94% rename from source/funkin/play/GameOverSubstate.hx rename to source/funkin/play/GameOverSubState.hx index a2048149f..1b79d024b 100644 --- a/source/funkin/play/GameOverSubstate.hx +++ b/source/funkin/play/GameOverSubState.hx @@ -16,10 +16,10 @@ import funkin.ui.PreferencesMenu; /** * A substate which renders over the PlayState when the player dies. * Displays the player death animation, plays the music, and handles restarting the song. - * + * * The newest implementation uses a substate, which prevents having to reload the song and stage each reset. */ -class GameOverSubstate extends MusicBeatSubstate +class GameOverSubState extends MusicBeatSubState { /** * Which alternate animation on the character to use. @@ -92,7 +92,7 @@ class GameOverSubstate extends MusicBeatSubstate bg.scrollFactor.set(); add(bg); - // Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubstate. + // Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubState. // We can then play the character's `firstDeath` animation. boyfriend = PlayState.instance.currentStage.getBoyfriend(true); boyfriend.isDead = true; @@ -160,19 +160,21 @@ class GameOverSubstate extends MusicBeatSubstate } // KEYBOARD ONLY: Restart the level when pressing the assigned key. - if (controls.ACCEPT) + if (controls.ACCEPT && blueballed) { + blueballed = false; confirmDeath(); } // KEYBOARD ONLY: Return to the menu when pressing the assigned key. if (controls.BACK) { + blueballed = false; PlayState.deathCounter = 0; PlayState.seenCutscene = false; gameOverMusic.stop(); - if (PlayState.isStoryMode) FlxG.switchState(new StoryMenuState()); + if (PlayStatePlaylist.isStoryMode) FlxG.switchState(new StoryMenuState()); else FlxG.switchState(new FreeplayState()); } @@ -186,11 +188,11 @@ class GameOverSubstate extends MusicBeatSubstate else { // Music hasn't started yet. - switch (PlayState.storyWeek) + switch (PlayStatePlaylist.campaignId) { // TODO: Make the behavior for playing Jeff's voicelines generic or un-hardcoded. // This will simplify the class and make it easier for mods to add death quotes. - case 7: + case "week7": if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote) { playingJeffQuote = true; @@ -227,9 +229,9 @@ class GameOverSubstate extends MusicBeatSubstate new FlxTimer().start(0.7, function(tmr:FlxTimer) { // ...fade out the graphics. Then after that happens... FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { - // ...close the GameOverSubstate. + // ...close the GameOverSubState. FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); - PlayState.needsReset = true; + PlayState.instance.needsReset = true; // Readd Boyfriend to the stage. boyfriend.isDead = false; @@ -252,7 +254,7 @@ class GameOverSubstate extends MusicBeatSubstate /** * Starts the death music at the appropriate volume. - * @param startingVolume + * @param startingVolume */ function startDeathMusic(?startingVolume:Float = 1, ?force:Bool = false):Void { @@ -269,12 +271,15 @@ class GameOverSubstate extends MusicBeatSubstate } } + static var blueballed:Bool = false; + /** * Play the sound effect that occurs when * boyfriend's testicles get utterly annihilated. */ public static function playBlueBalledSFX() { + blueballed = true; FlxG.sound.play(Paths.sound('fnf_loss_sfx' + blueBallSuffix)); } diff --git a/source/funkin/play/HealthIcon.hx b/source/funkin/play/HealthIcon.hx index f2bf431bc..a15c63060 100644 --- a/source/funkin/play/HealthIcon.hx +++ b/source/funkin/play/HealthIcon.hx @@ -317,7 +317,7 @@ class HealthIcon extends FlxSprite /** * Load health icon animations from a Sparrow XML file (the kind used by characters) * Note that this is looking for SPECIFIC animation names, so you may need to modify the XML. - * @param charId + * @param charId */ function loadAnimationNew():Void { @@ -333,7 +333,7 @@ class HealthIcon extends FlxSprite /** * Load health icon animations using the legacy format. * Simply assumes two icons, the idle and losing icons. - * @param charId + * @param charId */ function loadAnimationOld():Void { diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 047a1b5fe..e8cfb1402 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1,17 +1,18 @@ package funkin.play; -import funkin.play.song.SongData.SongDataParser; import flixel.sound.FlxSound; +import funkin.ui.story.StoryMenuState; +import flixel.addons.display.FlxPieDial; import flixel.addons.transition.FlxTransitionableState; import flixel.FlxCamera; import flixel.FlxObject; -import funkin.ui.story.StoryMenuState; import flixel.FlxSprite; import flixel.FlxState; import flixel.FlxSubState; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.input.keyboard.FlxKey; import flixel.math.FlxMath; +import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.text.FlxText; import flixel.tweens.FlxEase; @@ -20,18 +21,19 @@ import flixel.ui.FlxBar; import flixel.util.FlxColor; import flixel.util.FlxSort; import flixel.util.FlxTimer; -import funkin.charting.ChartingState; +import funkin.audio.VoicesGroup; import funkin.Highscore.Tallies; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; import funkin.Note; import funkin.play.character.BaseCharacter; -import funkin.play.character.CharacterData; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.cutscene.VanillaCutscenes; -import funkin.play.event.SongEvent.SongEventParser; +import funkin.play.cutscene.VideoCutscene; +import funkin.play.event.SongEventData.SongEventParser; import funkin.play.scoring.Scoring; import funkin.play.song.Song; +import funkin.play.song.SongData.SongDataParser; import funkin.play.song.SongData.SongEventData; import funkin.play.song.SongData.SongNoteData; import funkin.play.song.SongData.SongPlayableChar; @@ -40,12 +42,9 @@ import funkin.play.stage.Stage; import funkin.play.stage.StageData.StageDataParser; import funkin.play.Strumline.StrumlineArrow; import funkin.play.Strumline.StrumlineStyle; -import funkin.Section.SwagSection; -import funkin.SongLoad.SwagSong; -import funkin.audio.VoicesGroup; import funkin.ui.PopUpStuff; import funkin.ui.PreferencesMenu; -import funkin.ui.stageBuildShit.StageOffsetSubstate; +import funkin.ui.stageBuildShit.StageOffsetSubState; import funkin.util.Constants; import funkin.util.SerializerUtil; import funkin.util.SortUtil; @@ -54,6 +53,28 @@ import lime.ui.Haptic; import Discord.DiscordClient; #end +/** + * Parameters used to initialize the PlayState. + */ +typedef PlayStateParams = +{ + /** + * The song to play. + */ + targetSong:Song, + + /** + * The difficulty to play the song on. + * @default `Constants.DEFAULT_DIFFICULTY` + */ + ?targetDifficulty:String, + /** + * The character to play as. + * @default `bf`, or the first character in the song's character list. + */ + ?targetCharacter:String, +} + /** * The gameplay state, where all the rhythm gaming happens. */ @@ -66,89 +87,58 @@ class PlayState extends MusicBeatState */ /** * The currently active PlayState. - * Since there is only one PlayState in existance at a time, we can use a singleton. + * There should be only one PlayState in existance at a time, we can use a singleton. */ public static var instance:PlayState = null; - /** - * The currently active song. Includes data about what stage should be used, what characters, - * and the notes to be played. - */ - public static var currentSong:SwagSong = null; - - public static var currentSong_NEW:Song = null; - - /** - * Whether the game is currently in Story Mode. If false, we are in Free Play Mode. - */ - public static var isStoryMode:Bool = false; - - /** - * Whether the game is currently in Practice Mode. - * If true, player will not lose gain or lose score from notes. - */ - public static var isPracticeMode:Bool = false; - - /** - * Whether the game is currently in an animated cutscene, and gameplay should be stopped. - */ - public static var isInCutscene:Bool = false; - - /** - * Whether the inputs should be disabled for whatever reason... used for the stage edit lol! - */ - public static var disableKeys:Bool = false; - - /* - * Whether the game is currently in dialog, and gameplay should be stopped. - */ - public static var isInDialog:Bool = false; - - /** - * Whether the game is currently in the countdown before the song resumes. - */ - public static var isInCountdown:Bool = false; - - /** - * Gets set to true when the PlayState needs to reset (player opted to restart or died). - * Gets disabled once resetting happens. - */ - public static var needsReset:Bool = false; - - /** - * The current "Blueball Counter" to display in the pause menu. - * Resets when you beat a song or go back to the main menu. - */ - public static var deathCounter:Int = 0; - - /** - * The default camera zoom level. The camera lerps back to this after zooming in. - * Defaults to 1.05 but may be larger or smaller depending on the current stage. - */ - public static var defaultCameraZoom:Float = 1.05; - - /** - * Used to persist the position of the `cameraFollowPosition` between resets. - */ - static var previousCameraFollowPoint:FlxObject = null; - /** * PUBLIC INSTANCE VARIABLES * Public instance variables should be used for information that must be reset or dereferenced - * every time the state is reset, such as the currently active stage, but may need to be accessed externally. + * every time the state is changed, but may need to be accessed externally. */ + /** + * The currently selected stage. + */ + public var currentSong:Song = null; + + /** + * The currently selected difficulty. + */ + public var currentDifficulty:String = Constants.DEFAULT_DIFFICULTY; + + /** + * The player character being used for this level, as a character ID. + */ + public var currentPlayerId:String = 'bf'; + /** * The currently active Stage. This is the object containing all the props. */ public var currentStage:Stage = null; + /** + * Data for the current difficulty for the current song. + * Includes chart data, scroll speed, and other information. + */ public var currentChart(get, null):SongDifficulty; /** * The internal ID of the currently active Stage. * Used to retrieve the data required to build the `currentStage`. */ - public var currentStageId:String = ''; + public var currentStageId(get, null):String; + + /** + * Gets set to true when the PlayState needs to reset (player opted to restart or died). + * Gets disabled once resetting happens. + */ + public var needsReset:Bool = false; + + /** + * The current 'Blueball Counter' to display in the pause menu. + * Resets when you beat a song or go back to the main menu. + */ + public var deathCounter:Int = 0; /** * The player's current health. @@ -164,13 +154,74 @@ class PlayState extends MusicBeatState /** * An empty FlxObject contained in the scene. * The current gameplay camera will be centered on this object. Tween its position to move the camera smoothly. - * + * * This is an FlxSprite for two reasons: * 1. It needs to be an object in the scene for the camera to be configured to follow it. * 2. It needs to be an FlxSprite to allow a graphic (optionally, for debug purposes) to be drawn on it. */ public var cameraFollowPoint:FlxSprite = new FlxSprite(0, 0); + /** + * The camera follow point from the last stage. + * Used to persist the position of the `cameraFollowPosition` between levels. + */ + public var previousCameraFollowPoint:FlxSprite = null; + + /** + * The current camera zoom level. + * + * The camera zoom is increased every beat, and lerped back to this value every frame, creating a smooth 'zoom-in' effect. + * Defaults to 1.05 but may be larger or smaller depending on the current stage, + * and may be changed by the `ZoomCamera` song event. + */ + public var defaultCameraZoom:Float = FlxCamera.defaultZoom * 1.05; + + /** + * The current HUD camera zoom level. + * + * The camera zoom is increased every beat, and lerped back to this value every frame, creating a smooth 'zoom-in' effect. + */ + public var defaultHUDCameraZoom:Float = FlxCamera.defaultZoom * 1.0; + + /** + * Intensity of the gameplay camera zoom. + * @default `1.5%` + */ + public var cameraZoomIntensity:Float = Constants.DEFAULT_ZOOM_INTENSITY; + + /** + * Intensity of the HUD camera zoom. + * @default `3.0%` + */ + public var hudCameraZoomIntensity:Float = Constants.DEFAULT_ZOOM_INTENSITY * 2.0; + + /** + * How many beats (quarter notes) between camera zooms. + * @default One camera zoom per measure (four beats). + */ + public var cameraZoomRate:Int = Constants.DEFAULT_ZOOM_RATE; + + /** + * Whether the game is currently in the countdown before the song resumes. + */ + public var isInCountdown:Bool = false; + + /** + * Whether the game is currently in Practice Mode. + * If true, player will not lose gain or lose score from notes. + */ + public var isPracticeMode:Bool = false; + + /** + * Whether the game is currently in an animated cutscene, and gameplay should be stopped. + */ + public var isInCutscene:Bool = false; + + /** + * Whether the inputs should be disabled for whatever reason... used for the stage edit lol! + */ + public var disableKeys:Bool = false; + /** * PRIVATE INSTANCE VARIABLES * Private instance variables should be used for information that must be reset or dereferenced @@ -182,6 +233,10 @@ class PlayState extends MusicBeatState */ var inactiveNotes:Array; + /** + * The Array containing the upcoming song events. + * The `update()` function regularly shifts these out to trigger events. + */ var songEvents:Array; /** @@ -196,17 +251,26 @@ class PlayState extends MusicBeatState */ var healthLerp:Float = 1; + /** + * How long the user has held the "Skip Video Cutscene" button for. + */ + var skipHeldTimer:Float = 0; + /** * Forcibly disables all update logic while the game moves back to the Menu state. - * This is used only when a critical error occurs and the game cannot continue. + * This is used only when a critical error occurs and the game absolutely cannot continue. */ var criticalFailure:Bool = false; /** - * How many beats between camera zooms. - * @default One camera zoom per four beats. + * False as long as the countdown has not finished yet. */ - var camZoomRate:Int = 4; + var startingSong:Bool = false; + + /** + * A group of audio tracks, used to play the song's vocals. + */ + var vocals:VoicesGroup; /** * RENDER OBJECTS @@ -268,6 +332,8 @@ class PlayState extends MusicBeatState */ public var camCutscene:FlxCamera; + var skipTimer:FlxPieDial; + /** * PROPERTIES */ @@ -287,24 +353,9 @@ class PlayState extends MusicBeatState return this.subState != null; } - // TODO: Reorganize these variables (maybe there should be a separate class like Conductor just to hold them?) - public static var storyWeek:Int = 0; - public static var storyPlaylist:Array = []; - public static var storyDifficulty:Int = 1; - public static var storyDifficulty_NEW:String = "normal"; - public static var seenCutscene:Bool = false; - public static var campaignScore:Int = 0; - - var vocals:VoicesGroup; - var vocalsFinished:Bool = false; - var gfSpeed:Int = 1; var generatedMusic:Bool = false; - var startingSong:Bool = false; - var dialogue:Array; - var talking:Bool = true; - var doof:DialogueBox; var grpNoteSplashes:FlxTypedGroup; var comboPopUps:PopUpStuff; var perfectMode:Bool = false; @@ -320,28 +371,76 @@ class PlayState extends MusicBeatState var detailsPausedText:String = ''; #end - override public function create():Void + /** + * This sucks. We need this because FlxG.resetState(); assumes the constructor has no arguments. + * @see https://github.com/HaxeFlixel/flixel/issues/2541 + */ + static var lastParams:PlayStateParams = null; + + public function new(params:PlayStateParams) + { + super(); + + if (params == null && lastParams == null) + { + throw 'PlayState constructor called with no available parameters.'; + } + else if (params == null) + { + trace('WARNING: PlayState constructor called with no parameters. Reusing previous parameters.'); + params = lastParams; + } + else + { + lastParams = params; + } + + currentSong = params.targetSong; + if (params.targetDifficulty != null) currentDifficulty = params.targetDifficulty; + if (params.targetCharacter != null) currentPlayerId = params.targetCharacter; + } + + public override function create():Void { super.create(); - if (currentSong == null && currentSong_NEW == null) + if (instance != null) + { + trace('WARNING: PlayState instance already exists. This should not happen.'); + } + instance = this; + + if (currentSong != null) + { + // TODO: Do this in the loading state. + currentSong.cacheCharts(true); + } + + // Returns null if the song failed to load or doesn't have the selected difficulty. + if (currentChart == null) { criticalFailure = true; - lime.app.Application.current.window.alert("There was a critical error while accessing the selected song. Click OK to return to the main menu.", - "Error loading PlayState"); + var message:String = 'There was a critical error. Click OK to return to the main menu.'; + + if (currentSong == null) + { + message = 'The was a critical error loading this song\'s chart. Click OK to return to the main menu.'; + } + else if (currentDifficulty == null) + { + message = 'The was a critical error selecting a difficulty for this song. Click OK to return to the main menu.'; + } + else if (currentSong.getDifficulty(currentDifficulty) == null) + { + message = 'The was a critical error retrieving data for this song on "$currentDifficulty" difficulty. Click OK to return to the main menu.'; + } + + lime.app.Application.current.window.alert(message, 'Error loading PlayState'); FlxG.switchState(new MainMenuState()); return; } - instance = this; - - if (currentSong_NEW != null) - { - // TODO: Do this in the loading state. - currentSong_NEW.cacheCharts(true); - } - // Displays the camera follow point as a sprite for debug purposes. // TODO: Put this on a toggle? cameraFollowPoint.makeGraphic(8, 8, 0xFF00FF00); @@ -363,55 +462,16 @@ class PlayState extends MusicBeatState if (currentChart != null) { currentChart.cacheInst(); - currentChart.cacheVocals('bf'); - } - else - { - FlxG.sound.cache(Paths.inst(currentSong.song)); - FlxG.sound.cache(Paths.voices(currentSong.song)); + currentChart.cacheVocals(currentPlayerId); } // Initialize stage stuff. initCameras(); - if (currentSong == null && currentSong_NEW == null) - { - currentSong = SongLoad.loadFromJson('tutorial'); - } - - if (currentSong_NEW != null) - { - Conductor.mapTimeChanges(currentChart.timeChanges); - // Conductor.bpm = currentChart.getStartingBPM(); - - // TODO: Support for dialog. - } - else - { - Conductor.mapBPMChanges(currentSong); - // Conductor.bpm = currentSong.bpm; - - switch (currentSong.song.toLowerCase()) - { - case 'senpai': - dialogue = CoolUtil.coolTextFile(Paths.txt('songs/senpai/senpaiDialogue')); - case 'roses': - dialogue = CoolUtil.coolTextFile(Paths.txt('songs/roses/rosesDialogue')); - case 'thorns': - dialogue = CoolUtil.coolTextFile(Paths.txt('songs/thorns/thornsDialogue')); - } - } + Conductor.mapTimeChanges(currentChart.timeChanges); Conductor.update(-5000); - if (dialogue != null) - { - doof = new DialogueBox(false, dialogue); - doof.scrollFactor.set(); - doof.finishThing = startCountdown; - doof.cameras = [camHUD]; - } - // Once the song is loaded, we can continue and initialize the stage. var healthBarYPos:Float = PreferencesMenu.getPref('downscroll') ? FlxG.height * 0.1 : FlxG.height * 0.9; @@ -444,6 +504,8 @@ class PlayState extends MusicBeatState comboPopUps.cameras = [camHUD]; add(comboPopUps); + buildStrumlines(); + grpNoteSplashes = new FlxTypedGroup(); var noteSplash:NoteSplash = new NoteSplash(100, 100, 0); @@ -452,24 +514,25 @@ class PlayState extends MusicBeatState add(grpNoteSplashes); - if (currentSong_NEW != null) - { - generateSong_NEW(); - } - else - { - generateSong(); - } + generateSong(); resetCamera(); FlxG.worldBounds.set(0, 0, FlxG.width, FlxG.height); - scoreText = new FlxText(healthBarBG.x + healthBarBG.width - 190, healthBarBG.y + 30, 0, "", 20); - scoreText.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + scoreText = new FlxText(healthBarBG.x + healthBarBG.width - 190, healthBarBG.y + 30, 0, '', 20); + scoreText.setFormat(Paths.font('vcr.ttf'), 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); scoreText.scrollFactor.set(); add(scoreText); + // Skip Video Cutscene + skipTimer = new FlxPieDial(16, 16, 32, FlxColor.WHITE, 36, CIRCLE, true, 24); + skipTimer.amount = 0; + skipTimer.zIndex = 1000; + // Renders only in video cutscene mode. + skipTimer.cameras = [camCutscene]; + add(skipTimer); + // Attach the groups to the HUD camera so they are rendered independent of the stage. grpNoteSplashes.cameras = [camHUD]; activeNotes.cameras = [camHUD]; @@ -488,22 +551,21 @@ class PlayState extends MusicBeatState // TODO: Alternatively: make a song script that allows startCountdown to be called, // then cancels the countdown, hides the UI, plays the cutscene, // then calls PlayState.startCountdown later? - if (isStoryMode && !seenCutscene) + if (currentSong != null) { - seenCutscene = true; - - switch (currentSong_NEW.songId.toLowerCase()) + switch (currentSong.songId.toLowerCase()) { - case "winter-horrorland": + case 'winter-horrorland': VanillaCutscenes.playHorrorStartCutscene(); - case 'senpai' | 'roses' | 'thorns': - schoolIntro(doof); // doof is assumed to be non-null, lol! - case 'ugh': - VanillaCutscenes.playUghCutscene(); - case 'stress': - VanillaCutscenes.playStressCutscene(); - case 'guns': - VanillaCutscenes.playGunsCutscene(); + // This one is softcoded now WOOOO! + // case 'senpai' | 'roses' | 'thorns': + // schoolIntro(doof); + // case 'ugh': + // VanillaCutscenes.playUghCutscene(); + // case 'stress': + // VanillaCutscenes.playStressCutscene(); + // case 'guns': + // VanillaCutscenes.playGunsCutscene(); default: // VanillaCutscenes will call startCountdown later. startCountdown(); @@ -525,8 +587,14 @@ class PlayState extends MusicBeatState function get_currentChart():SongDifficulty { - if (currentSong_NEW == null || storyDifficulty_NEW == null) return null; - return currentSong_NEW.getDifficulty(storyDifficulty_NEW); + if (currentSong == null || currentDifficulty == null) return null; + return currentSong.getDifficulty(currentDifficulty); + } + + function get_currentStageId():String + { + if (currentChart == null || currentChart.stage == null || currentChart.stage == '') return Constants.DEFAULT_STAGE; + return currentChart.stage; } /** @@ -534,8 +602,8 @@ class PlayState extends MusicBeatState */ function initCameras():Void { - // Configure the default camera zoom level. - defaultCameraZoom = FlxCamera.defaultZoom * 1.05; + // Set the camera zoom. This gets overridden by the value in the stage data. + // defaultCameraZoom = FlxCamera.defaultZoom * 1.05; camGame = new SwagCamera(); camHUD = new FlxCamera(); @@ -550,187 +618,25 @@ class PlayState extends MusicBeatState function initStage():Void { - if (currentSong_NEW != null) + if (currentSong != null) { - initStage_NEW(); - return; - } + if (currentChart == null) + { + trace('Song difficulty could not be loaded.'); + } - // TODO: Move stageId to the song file. - switch (currentSong.song.toLowerCase()) - { - case 'spookeez' | 'monster' | 'south': - currentStageId = 'spookyMansion'; - case 'pico' | 'blammed' | 'philly': - currentStageId = 'phillyTrain'; - case 'milf' | 'satin-panties' | 'high': - currentStageId = 'limoRide'; - case 'cocoa' | 'eggnog': - currentStageId = 'mallXmas'; - case 'winter-horrorland': - currentStageId = 'mallEvil'; - case 'senpai' | 'roses': - currentStageId = 'school'; - case 'darnell' | 'lit-up' | '2hot': - currentStageId = 'phillyStreets'; - case 'blazin': - currentStageId = 'phillyBlazin'; - case 'pyro': - currentStageId = 'pyro'; - case 'thorns': - currentStageId = 'schoolEvil'; - case 'guns' | 'stress' | 'ugh': - currentStageId = 'tankmanBattlefield'; - default: - currentStageId = 'mainStage'; - } - // Loads the relevant stage based on its ID. - loadStage(currentStageId); - } - - function initStage_NEW():Void - { - if (currentChart == null) - { - trace('Song difficulty could not be loaded.'); - } - - if (currentChart.stage != null && currentChart.stage != '') - { - currentStageId = currentChart.stage; + loadStage(currentStageId); } else { - currentStageId = SongValidator.DEFAULT_STAGE; - } - - loadStage(currentStageId); - } - - function initCharacters() - { - if (currentSong_NEW != null) - { - initCharacters_NEW(); - return; - } - - iconP1 = new HealthIcon(currentSong.player1, 0); - iconP1.y = healthBar.y - (iconP1.height / 2); - add(iconP1); - - iconP2 = new HealthIcon(currentSong.player2, 1); - iconP2.y = healthBar.y - (iconP2.height / 2); - add(iconP2); - - // - // GIRLFRIEND - // - - // TODO: Tie the GF version to the song data, not the stage ID or the current player. - var gfVersion:String = 'gf'; - - switch (currentStageId) - { - case 'pyro' | 'phillyStreets': - gfVersion = 'nene'; - case 'blazin': - gfVersion = ''; - case 'limoRide': - gfVersion = 'gf-car'; - case 'mallXmas' | 'mallEvil': - gfVersion = 'gf-christmas'; - case 'school' | 'schoolEvil': - gfVersion = 'gf-pixel'; - case 'tankmanBattlefield': - gfVersion = 'gf-tankmen'; - } - - if (currentSong.player1 == "pico") gfVersion = "nene"; - - if (currentSong.song.toLowerCase() == 'stress') gfVersion = 'pico-speaker'; - - if (currentSong.song.toLowerCase() == 'tutorial') gfVersion = ''; - - // - // GIRLFRIEND - // - var girlfriend:BaseCharacter = CharacterDataParser.fetchCharacter(gfVersion); - - if (girlfriend != null) - { - girlfriend.characterType = CharacterType.GF; - girlfriend.scrollFactor.set(0.95, 0.95); - if (gfVersion == 'pico-speaker') - { - girlfriend.x -= 50; - girlfriend.y -= 200; - } - } - else if (gfVersion != '') - { - trace('WARNING: Could not load girlfriend character with ID ${gfVersion}, skipping...'); - } - - // - // DAD - // - var dad:BaseCharacter = CharacterDataParser.fetchCharacter(currentSong.player2); - - if (dad != null) - { - dad.characterType = CharacterType.DAD; - } - - switch (currentSong.player2) - { - case 'gf': - if (isStoryMode) - { - cameraFollowPoint.x += 600; - tweenCamIn(); - } - } - - // - // BOYFRIEND - // - var boyfriend:BaseCharacter = CharacterDataParser.fetchCharacter(currentSong.player1); - - if (boyfriend != null) - { - boyfriend.characterType = CharacterType.BF; - } - - if (currentStage != null) - { - // We're using Eric's stage handler. - // Characters get added to the stage, not the main scene. - if (girlfriend != null) - { - currentStage.addCharacter(girlfriend, GF); - } - - if (boyfriend != null) - { - currentStage.addCharacter(boyfriend, BF); - } - - if (dad != null) - { - currentStage.addCharacter(dad, DAD); - // Camera starts at dad. - cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y); - } - - // Redo z-indexes. - currentStage.refresh(); + // Fallback. + loadStage('mainStage'); } } - function initCharacters_NEW() + function initCharacters():Void { - if (currentSong_NEW == null || currentChart == null) + if (currentSong == null || currentChart == null) { trace('Song difficulty could not be loaded.'); } @@ -738,19 +644,18 @@ class PlayState extends MusicBeatState // TODO: Switch playable character by manipulating this value. // TODO: How to choose which one to use for story mode? - var playableChars = currentChart.getPlayableChars(); - var currentPlayer = 'bf'; + var playableChars:Array = currentChart.getPlayableChars(); if (playableChars.length == 0) { trace('WARNING: No playable characters found for this song.'); } - else if (playableChars.indexOf(currentPlayer) == -1) + else if (playableChars.indexOf(currentPlayerId) == -1) { - currentPlayer = playableChars[0]; + currentPlayerId = playableChars[0]; } - var currentCharData:SongPlayableChar = currentChart.getPlayableChar(currentPlayer); + var currentCharData:SongPlayableChar = currentChart.getPlayableChar(currentPlayerId); // // GIRLFRIEND @@ -780,28 +685,18 @@ class PlayState extends MusicBeatState dad.characterType = CharacterType.DAD; } - // TODO: Cut out this code/make it generic. - switch (currentCharData.opponent) - { - case 'gf': - if (isStoryMode) - { - cameraFollowPoint.x += 600; - tweenCamIn(); - } - } - // // OPPONENT HEALTH ICON // iconP2 = new HealthIcon(currentCharData.opponent, 1); iconP2.y = healthBar.y - (iconP2.height / 2); + dad.initHealthIcon(true); add(iconP2); // // BOYFRIEND // - var boyfriend:BaseCharacter = CharacterDataParser.fetchCharacter(currentPlayer); + var boyfriend:BaseCharacter = CharacterDataParser.fetchCharacter(currentPlayerId); if (boyfriend != null) { @@ -811,8 +706,9 @@ class PlayState extends MusicBeatState // // PLAYER HEALTH ICON // - iconP1 = new HealthIcon(currentPlayer, 0); + iconP1 = new HealthIcon(currentPlayerId, 0); iconP1.y = healthBar.y - (iconP1.height / 2); + boyfriend.initHealthIcon(false); add(iconP1); // @@ -859,13 +755,13 @@ class PlayState extends MusicBeatState /** * Removes any references to the current stage, then clears the stage cache, * then reloads all the stages. - * + * * This is useful for when you want to edit a stage without reloading the whole game. * Reloading works on both the JSON and the HXC, if applicable. - * + * * Call this by pressing F5 on a debug build. */ - override function debug_refreshModules() + override function debug_refreshModules():Void { // Remove the current stage. If the stage gets deleted while it's still in use, // it'll probably crash the game or something. @@ -877,13 +773,22 @@ class PlayState extends MusicBeatState currentStage = null; } + // Stop the vocals. + if (vocals != null) + { + vocals.stop(); + } + super.debug_refreshModules(); + + var event:ScriptEvent = new ScriptEvent(ScriptEvent.CREATE, false); + ScriptEventDispatcher.callEvent(currentSong, event); } /** * Pauses music and vocals easily. */ - public function pauseMusic() + public function pauseMusic():Void { FlxG.sound.music.pause(); vocals.pause(); @@ -892,9 +797,9 @@ class PlayState extends MusicBeatState /** * Loads stage data from cache, assembles the props, * and adds it to the state. - * @param id + * @param id */ - function loadStage(id:String) + function loadStage(id:String):Void { currentStage = StageDataParser.fetchStage(id); @@ -904,7 +809,7 @@ class PlayState extends MusicBeatState var event:ScriptEvent = new ScriptEvent(ScriptEvent.CREATE, false); ScriptEventDispatcher.callEvent(currentStage, event); - // Apply camera zoom. + // Apply camera zoom level from stage data. defaultCameraZoom = currentStage.camZoom; // Add the stage to the scene. @@ -914,6 +819,12 @@ class PlayState extends MusicBeatState FlxG.console.registerObject('stage', currentStage); #end } + else + { + // lolol + lime.app.Application.current.window.alert('Nice job, you ignoramus. $id isn\'t a real stage.\nI\'m falling back to the default so the game doesn\'t shit itself.', + 'Stage Error'); + } } function initDiscord():Void @@ -934,91 +845,14 @@ class PlayState extends MusicBeatState } // String that contains the mode defined here so it isn't necessary to call changePresence for each mode - detailsText = isStoryMode ? "Story Mode: Week " + storyWeek : "Freeplay"; - detailsPausedText = "Paused - " + detailsText; + detailsText = isStoryMode ? 'Story Mode: Week $storyWeek' : 'Freeplay'; + detailsPausedText = 'Paused - $detailsText'; // Updating Discord Rich Presence. - DiscordClient.changePresence(detailsText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC); + DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC); #end } - function schoolIntro(?dialogueBox:DialogueBox):Void - { - var black:FlxSprite = new FlxSprite(-100, -100).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); - black.scrollFactor.set(); - add(black); - - var red:FlxSprite = new FlxSprite(-100, -100).makeGraphic(FlxG.width * 2, FlxG.height * 2, 0xFFff1b31); - red.scrollFactor.set(); - - var senpaiEvil:FlxSprite = new FlxSprite(); - senpaiEvil.frames = Paths.getSparrowAtlas('weeb/senpaiCrazy'); - senpaiEvil.animation.addByPrefix('idle', 'Senpai Pre Explosion', 24, false); - senpaiEvil.setGraphicSize(Std.int(senpaiEvil.width * Constants.PIXEL_ART_SCALE)); - senpaiEvil.scrollFactor.set(); - senpaiEvil.updateHitbox(); - senpaiEvil.screenCenter(); - senpaiEvil.x += senpaiEvil.width / 5; - - if (currentSong.song.toLowerCase() == 'roses' || currentSong.song.toLowerCase() == 'thorns') - { - remove(black); - - if (currentSong.song.toLowerCase() == 'thorns') - { - add(red); - camHUD.visible = false; - } - else - FlxG.sound.play(Paths.sound('ANGRY')); - // moved senpai angry noise in here to clean up cutscene switch case lol - } - - new FlxTimer().start(0.3, function(tmr:FlxTimer) { - black.alpha -= 0.15; - - if (black.alpha > 0) tmr.reset(0.3); - else - { - if (dialogueBox != null) - { - isInDialog = true; - - if (currentSong.song.toLowerCase() == 'thorns') - { - add(senpaiEvil); - senpaiEvil.alpha = 0; - new FlxTimer().start(0.3, function(swagTimer:FlxTimer) { - senpaiEvil.alpha += 0.15; - if (senpaiEvil.alpha < 1) swagTimer.reset(); - else - { - senpaiEvil.animation.play('idle'); - FlxG.sound.play(Paths.sound('Senpai_Dies'), 1, false, null, true, function() { - remove(senpaiEvil); - remove(red); - FlxG.camera.fade(FlxColor.WHITE, 0.01, true, function() { - add(dialogueBox); - camHUD.visible = true; - }, true); - }); - new FlxTimer().start(3.2, function(deadTime:FlxTimer) { - FlxG.camera.fade(FlxColor.WHITE, 1.6, false); - }); - } - }); - } - else - add(dialogueBox); - } - else - startCountdown(); - - remove(black); - } - }); - } - function startSong():Void { dispatchEvent(new ScriptEvent(ScriptEvent.SONG_START)); @@ -1027,23 +861,14 @@ class PlayState extends MusicBeatState previousFrameTime = FlxG.game.ticks; - if (!isGamePaused) + if (!isGamePaused && currentChart != null) { - // if (FlxG.sound.music != null) - // FlxG.sound.music.play(true); - // else - if (currentChart != null) - { - currentChart.playInst(1.0, false); - } - else - { - FlxG.sound.playMusic(Paths.inst(currentSong.song), 1, false); - } + currentChart.playInst(1.0, false); } FlxG.sound.music.onComplete = endSong; trace('Playing vocals...'); + add(vocals); vocals.play(); #if discord_rpc @@ -1051,28 +876,26 @@ class PlayState extends MusicBeatState songLength = FlxG.sound.music.length; // Updating Discord Rich Presence (with Time Left) - DiscordClient.changePresence(detailsText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC, true, songLength); + DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, songLength); #end } function generateSong():Void { - trace('===WARNING=== Song uses old chart format!!!!!'); + if (currentChart == null) + { + trace('Song difficulty could not be loaded.'); + } - Conductor.forceBPM(currentSong.bpm); + Conductor.forceBPM(currentChart.getStartingBPM()); - currentSong.song = currentSong.song; - - vocals = new VoicesGroup(); - var playerVocals:FlxSound = FlxG.sound.load(Paths.voices(currentSong.song, 'bf'), 1.0, false); - vocals.addPlayerVoice(playerVocals); - var opponentVocals:FlxSound = FlxG.sound.load(Paths.voices(currentSong.song, 'dad'), 1.0, false); - vocals.addOpponentVoice(opponentVocals); - - vocals.members[0].onComplete = function() { - vocalsFinished = true; - }; + vocals = currentChart.buildVocals(currentPlayerId); + if (vocals.members.length == 0) + { + trace('WARNING: No vocals found for this song.'); + } + // Create the rendered note group. activeNotes = new FlxTypedGroup(); activeNotes.zIndex = 1000; add(activeNotes); @@ -1082,144 +905,7 @@ class PlayState extends MusicBeatState generatedMusic = true; } - function generateSong_NEW():Void - { - if (currentChart == null) - { - trace('Song difficulty could not be loaded.'); - } - - Conductor.forceBPM(currentChart.getStartingBPM()); - - // TODO: Fix grouped vocals - vocals = currentChart.buildVocals(); - vocals.onComplete = function() { - vocalsFinished = true; - } - - // Create the rendered note group. - activeNotes = new FlxTypedGroup(); - activeNotes.zIndex = 1000; - add(activeNotes); - - regenNoteData_NEW(); - - generatedMusic = true; - } - function regenNoteData():Void - { - // resets combo, should prob put somewhere else! - Highscore.tallies.combo = 0; - Highscore.tallies = new Tallies(); - // make unspawn notes shit def empty - inactiveNotes = []; - - activeNotes.forEach(function(nt) { - nt.followsTime = false; - FlxTween.tween(nt, {y: FlxG.height + nt.y}, 0.5, - { - ease: FlxEase.expoIn, - onComplete: function(twn) { - nt.kill(); - activeNotes.remove(nt, true); - nt.destroy(); - } - }); - }); - - var noteData:Array; - - // NEW SHIT - noteData = SongLoad.getSong(); - - for (section in noteData) - { - for (songNotes in section.sectionNotes) - { - var daStrumTime:Float = songNotes.strumTime; - // TODO: Replace 4 with strumlineSize - var daNoteData:Int = Std.int(songNotes.noteData % 4); - var gottaHitNote:Bool = section.mustHitSection; - - if (songNotes.highStakes) // noteData > 3 - gottaHitNote = !section.mustHitSection; - - var oldNote:Note; - if (inactiveNotes.length > 0) oldNote = inactiveNotes[Std.int(inactiveNotes.length - 1)]; - else - oldNote = null; - - var strumlineStyle:StrumlineStyle = NORMAL; - - // TODO: Put this in the chart or something? - switch (currentStageId) - { - case 'school': - strumlineStyle = PIXEL; - case 'schoolEvil': - strumlineStyle = PIXEL; - } - - var swagNote:Note = new Note(daStrumTime, daNoteData, oldNote, false, strumlineStyle); - // swagNote.data = songNotes; - swagNote.data.sustainLength = songNotes.sustainLength; - swagNote.data.noteKind = songNotes.noteKind; - swagNote.scrollFactor.set(0, 0); - - var susLength:Float = swagNote.data.sustainLength; - - susLength = susLength / Conductor.stepCrochet; - inactiveNotes.push(swagNote); - - for (susNote in 0...Math.round(susLength)) - { - oldNote = inactiveNotes[Std.int(inactiveNotes.length - 1)]; - - var sustainNote:Note = new Note(daStrumTime + (Conductor.stepCrochet * susNote) + Conductor.stepCrochet, daNoteData, oldNote, true, strumlineStyle); - sustainNote.data.noteKind = songNotes.noteKind; - sustainNote.scrollFactor.set(); - inactiveNotes.push(sustainNote); - - sustainNote.mustPress = gottaHitNote; - - if (sustainNote.mustPress) sustainNote.x += FlxG.width / 2; // general offset - } - - // TODO: Replace 4 with strumlineSize - swagNote.mustPress = gottaHitNote; - - if (swagNote.mustPress) - { - if (playerStrumline != null) - { - swagNote.x = playerStrumline.getArrow(swagNote.data.noteData).x; - } - else - { - swagNote.x += FlxG.width / 2; // general offset - } - } - else - { - if (enemyStrumline != null) - { - swagNote.x = enemyStrumline.getArrow(swagNote.data.noteData).x; - } - else - { - // swagNote.x += FlxG.width / 2; // general offset - } - } - } - } - - inactiveNotes.sort(function(a:Note, b:Note):Int { - return SortUtil.byStrumtime(FlxSort.ASCENDING, a, b); - }); - } - - function regenNoteData_NEW():Void { Highscore.tallies.combo = 0; Highscore.tallies = new Tallies(); @@ -1274,27 +960,11 @@ class PlayState extends MusicBeatState // TODO: Make this more robust. if (newNote.mustPress) { - if (playerStrumline != null) - { - // Align with the strumline arrow. - newNote.x = playerStrumline.getArrow(songNote.getDirection()).x; - } - else - { - // Assume strumline position. - newNote.x += FlxG.width / 2; - } + newNote.alignToSturmlineArrow(playerStrumline.getArrow(songNote.getDirection())); } else { - if (enemyStrumline != null) - { - newNote.x = enemyStrumline.getArrow(songNote.getDirection()).x; - } - else - { - // newNote.x += 0; - } + newNote.alignToSturmlineArrow(enemyStrumline.getArrow(songNote.getDirection())); } inactiveNotes.push(newNote); @@ -1313,27 +983,12 @@ class PlayState extends MusicBeatState if (sustainNote.mustPress) { - if (playerStrumline != null) - { - // Align with the strumline arrow. - sustainNote.x = playerStrumline.getArrow(songNote.getDirection()).x; - } - else - { - // Assume strumline position. - sustainNote.x += FlxG.width / 2; - } + // Align with the strumline arrow. + sustainNote.alignToSturmlineArrow(playerStrumline.getArrow(songNote.getDirection())); } else { - if (enemyStrumline != null) - { - sustainNote.x = enemyStrumline.getArrow(songNote.getDirection()).x; - } - else - { - // newNote.x += 0; - } + sustainNote.alignToSturmlineArrow(enemyStrumline.getArrow(songNote.getDirection())); } inactiveNotes.push(sustainNote); @@ -1343,29 +998,24 @@ class PlayState extends MusicBeatState } // Sorting is an expensive operation. - // Assume it was done in the chart file. + // TODO: Make this more efficient. + // DO NOT assume it was done in the chart file. Notes created artificially by sustains are in here too. + inactiveNotes.sort(function(a:Note, b:Note):Int { + return SortUtil.byStrumtime(FlxSort.ASCENDING, a, b); + }); /** - inactiveNotes.sort(function(a:Note, b:Note):Int - { - return SortUtil.byStrumtime(FlxSort.ASCENDING, a, b); - }); **/ } - function tweenCamIn():Void - { - FlxTween.tween(FlxG.camera, {zoom: 1.3 * FlxCamera.defaultZoom}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut}); - } - #if discord_rpc override public function onFocus():Void { if (health > 0 && !paused && FlxG.autoPause) { - if (Conductor.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC, true, + if (Conductor.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC, true, songLength - Conductor.songPosition); else - DiscordClient.changePresence(detailsText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC); + DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); } super.onFocus(); @@ -1373,7 +1023,7 @@ class PlayState extends MusicBeatState override public function onFocusLost():Void { - if (health > 0 && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC); + if (health > 0 && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); super.onFocusLost(); } @@ -1386,31 +1036,30 @@ class PlayState extends MusicBeatState vocals.pause(); FlxG.sound.music.play(); - Conductor.update(FlxG.sound.music.time + Conductor.offset); - - if (vocalsFinished) return; + Conductor.update(); vocals.time = FlxG.sound.music.time; vocals.play(); } - override public function update(elapsed:Float) + public override function update(elapsed:Float):Void { - super.update(elapsed); - if (criticalFailure) return; + super.update(elapsed); + if (FlxG.keys.justPressed.U) { // hack for HaxeUI generation, doesn't work unless persistentUpdate is false at state creation!! disableKeys = true; persistentUpdate = false; - openSubState(new StageOffsetSubstate()); + openSubState(new StageOffsetSubState()); } updateHealthBar(); updateScoreText(); + // Handle restarting the song when needed (player death or pressing Retry) if (needsReset) { dispatchEvent(new ScriptEvent(ScriptEvent.SONG_RETRY)); @@ -1439,14 +1088,12 @@ class PlayState extends MusicBeatState currentStage.resetStage(); // Delete all notes and reset the arrays. - if (currentChart != null) - { - regenNoteData_NEW(); - } - else - { - regenNoteData(); - } + regenNoteData(); + + // Reset camera zooming + cameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY; + hudCameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * 2.0; + cameraZoomRate = Constants.DEFAULT_ZOOM_RATE; health = 1; songScore = 0; @@ -1456,13 +1103,7 @@ class PlayState extends MusicBeatState needsReset = false; } - #if !debug - perfectMode = false; - #else - if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible; - #end - - // do this BEFORE super.update() so songPosition is accurate + // Update the conductor. if (startingSong) { if (isInCountdown) @@ -1473,9 +1114,12 @@ class PlayState extends MusicBeatState } else { - if (Paths.SOUND_EXT == 'mp3') Conductor.offset = -13; // DO NOT FORGET TO REMOVE THE HARDCODE! WHEN I MAKE BETTER OFFSET SYSTEM! + // DO NOT FORGET TO REMOVE THE HARDCODE! WHEN I MAKE BETTER OFFSET SYSTEM! - Conductor.update(FlxG.sound.music.time + Conductor.offset); + // :nerd: um ackshually it's not 13 it's 11.97278911564 + if (Paths.SOUND_EXT == 'mp3') Conductor.offset = Constants.MP3_DELAY_MS; + + Conductor.update(); if (!isGamePaused) { @@ -1497,6 +1141,7 @@ class PlayState extends MusicBeatState androidPause = FlxG.android.justPressed.BACK; #end + // Attempt to pause the game. if ((controls.PAUSE || androidPause) && isInCountdown && mayPauseGame) { var event = new PauseScriptEvent(FlxG.random.bool(1 / 1000)); @@ -1515,77 +1160,58 @@ class PlayState extends MusicBeatState // It's a reference to Gitaroo Man, which doesn't let you pause the game. if (event.gitaroo) { - FlxG.switchState(new GitarooPause()); + FlxG.switchState(new GitarooPause( + { + targetSong: currentSong, + targetDifficulty: currentDifficulty, + targetCharacter: currentPlayerId, + })); } else { - var boyfriendPos = currentStage.getBoyfriend().getScreenPosition(); - var pauseSubState = new PauseSubState(); + var boyfriendPos:FlxPoint = new FlxPoint(0, 0); + + // Prevent the game from crashing if Boyfriend isn't present. + if (currentStage != null && currentStage.getBoyfriend() != null) + { + boyfriendPos = currentStage.getBoyfriend().getScreenPosition(); + } + + var pauseSubState:FlxSubState = new PauseSubState(); + openSubState(pauseSubState); pauseSubState.camera = camHUD; - boyfriendPos.put(); + // boyfriendPos.put(); // TODO: Why is this here? } #if discord_rpc - DiscordClient.changePresence(detailsPausedText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC); + DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); #end } } - #if debug - // 1: End the song immediately. - if (FlxG.keys.justPressed.ONE) endSong(); - - // 2: Gain 10% health. - if (FlxG.keys.justPressed.TWO) health += 0.1 * 2.0; - - // 3: Lose 5% health. - if (FlxG.keys.justPressed.THREE) health -= 0.05 * 2.0; - #end - - // 7: Move to the charter. - if (FlxG.keys.justPressed.SEVEN) - { - FlxG.switchState(new ChartingState()); - - #if discord_rpc - DiscordClient.changePresence("Chart Editor", null, null, true); - #end - } - - // 8: Move to the offset editor. - if (FlxG.keys.justPressed.EIGHT) FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState()); - - // 9: Toggle the old icon. - if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon(); - - #if debug - // PAGEUP: Skip forward one section. - // SHIFT+PAGEUP: Skip forward ten sections. - if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 10 : 1); - // PAGEDOWN: Skip backward one section. Doesn't replace notes. - // SHIFT+PAGEDOWN: Skip backward ten sections. - if (FlxG.keys.justPressed.PAGEDOWN) changeSection(FlxG.keys.pressed.SHIFT ? -10 : -1); - #end - + // Cap health. if (health > 2.0) health = 2.0; if (health < 0.0) health = 0.0; + // Lerp the camera zoom towards the target level. if (subState == null) { FlxG.camera.zoom = FlxMath.lerp(defaultCameraZoom, FlxG.camera.zoom, 0.95); - camHUD.zoom = FlxMath.lerp(1 * FlxCamera.defaultZoom, camHUD.zoom, 0.95); + camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95); } - FlxG.watch.addQuick("beatShit", Conductor.currentBeat); - FlxG.watch.addQuick("stepShit", Conductor.currentStep); + FlxG.watch.addQuick('beatShit', Conductor.currentBeat); + FlxG.watch.addQuick('stepShit', Conductor.currentStep); if (currentStage != null) { - FlxG.watch.addQuick("bfAnim", currentStage.getBoyfriend().getCurrentAnimation()); + FlxG.watch.addQuick('bfAnim', currentStage.getBoyfriend().getCurrentAnimation()); } - FlxG.watch.addQuick("songPos", Conductor.songPosition); + FlxG.watch.addQuick('songPos', Conductor.songPosition); - if (currentSong != null && currentSong.song == 'Fresh') + // Handle GF dance speed. + // TODO: Add a song event for this. + if (currentSong.songId == 'fresh') { switch (Conductor.currentBeat) { @@ -1600,20 +1226,21 @@ class PlayState extends MusicBeatState } } - if (!isInCutscene && !isInDialog && !disableKeys && !_exiting) + // Handle player death. + if (!isInCutscene && !disableKeys && !_exiting) { // RESET = Quick Game Over Screen if (controls.RESET) { health = 0; - trace("RESET = True"); + trace('RESET = True'); } #if CAN_CHEAT // brandon's a pussy if (controls.CHEAT) { health += 1; - trace("User is cheating!"); + trace('User is cheating!'); } #end @@ -1643,17 +1270,18 @@ class PlayState extends MusicBeatState } #end - var gameOverSubstate = new GameOverSubstate(); - openSubState(gameOverSubstate); + var gameOverSubState = new GameOverSubState(); + openSubState(gameOverSubState); #if discord_rpc // Game Over doesn't get his own variable because it's only used here - DiscordClient.changePresence("Game Over - " + detailsText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC); + DiscordClient.changePresence('Game Over - ' + detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); #end } } - while (inactiveNotes[0] != null && inactiveNotes[0].data.strumTime - Conductor.songPosition < 1800 / SongLoad.getSpeed()) + // Iterate over inactive notes. + while (inactiveNotes[0] != null && inactiveNotes[0].data.strumTime - Conductor.songPosition < 1800 / currentChart.scrollSpeed) { var dunceNote:Note = inactiveNotes[0]; @@ -1664,6 +1292,7 @@ class PlayState extends MusicBeatState inactiveNotes.shift(); } + // Iterate over active notes. if (generatedMusic && playerStrumline != null) { activeNotes.forEachAlive(function(daNote:Note) { @@ -1679,19 +1308,26 @@ class PlayState extends MusicBeatState daNote.active = true; } - var strumLineMid = playerStrumline.y + Note.swagWidth / 2; + var strumLineMid:Float = playerStrumline.y + Note.swagWidth / 2; - if (daNote.followsTime) daNote.y = (Conductor.songPosition - daNote.data.strumTime) * (0.45 * FlxMath.roundDecimal(SongLoad.getSpeed(), - 2) * daNote.noteSpeedMulti); + if (daNote.followsTime) + { + daNote.y = (Conductor.songPosition - daNote.data.strumTime) * (0.45 * FlxMath.roundDecimal(currentChart.scrollSpeed, 2) * daNote.noteSpeedMulti); + } if (PreferencesMenu.getPref('downscroll')) { daNote.y += playerStrumline.y; if (daNote.isSustainNote) { - if (daNote.animation.curAnim.name.endsWith("end") && daNote.prevNote != null) daNote.y += daNote.prevNote.height; + if (daNote.animation.curAnim.name.endsWith('end') && daNote.prevNote != null) + { + daNote.y += daNote.prevNote.height; + } else + { daNote.y += daNote.height / 2; + } if ((!daNote.mustPress || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit))) && daNote.y - daNote.offset.y * daNote.scale.y + daNote.height >= strumLineMid) @@ -1724,15 +1360,10 @@ class PlayState extends MusicBeatState { daNote.tooLate = true; } - else - { - // Volume of DAD. - if (currentSong != null && currentSong.needsVoices) vocals.opponentVolume = 1; - } } // WIP interpolation shit? Need to fix the pause issue - // daNote.y = (strumLine.y - (songTime - daNote.strumTime) * (0.45 * SONG.speed[SongLoad.curDiff])); + // daNote.y = (strumLine.y - (songTime - daNote.strumTime) * (0.45 * SONG.speed[.curDiff])); // removing this so whether the note misses or not is entirely up to Note class // var noteMiss:Bool = daNote.y < -daNote.height; @@ -1770,6 +1401,7 @@ class PlayState extends MusicBeatState }); } + // Query and activate song events. if (songEvents != null && songEvents.length > 0) { var songEventsToActivate:Array = SongEventParser.queryEvents(songEvents, Conductor.songPosition); @@ -1790,17 +1422,35 @@ class PlayState extends MusicBeatState } } - if (!isInCutscene && !isInDialog && !disableKeys) keyShit(true); - if (isInCutscene && !disableKeys) handleCutsceneKeys(); + // Handle keybinds. + if (!isInCutscene && !disableKeys) keyShit(true); + if (!isInCutscene && !disableKeys) debugKeyShit(); + + // Dispatch the onUpdate event to scripted elements. + dispatchEvent(new UpdateScriptEvent(elapsed)); } static final CUTSCENE_KEYS:Array = [SPACE, ESCAPE, ENTER]; - function handleCutsceneKeys():Void + public function trySkipVideoCutscene(elapsed:Float):Void { - if (FlxG.keys.anyJustPressed(CUTSCENE_KEYS)) + if (skipTimer == null || skipTimer.animation == null) return; + + if (elapsed < 0) { - VanillaCutscenes.finishCutscene(); + skipHeldTimer = 0.0; + } + else + { + skipHeldTimer += elapsed; + } + + skipTimer.visible = skipHeldTimer >= 0.05; + skipTimer.amount = Math.min(skipHeldTimer / 1.5, 1.0); + + if (skipHeldTimer >= 1.5) + { + VideoCutscene.finishVideo(); } } @@ -1827,8 +1477,13 @@ class PlayState extends MusicBeatState function killCombo():Void { // Girlfriend gets sad if you combo break after hitting 5 notes. - if (currentStage != null && currentStage.getGirlfriend() != null) if (Highscore.tallies.combo > 5 - && currentStage.getGirlfriend().hasAnimation('sad')) currentStage.getGirlfriend().playAnimation('sad'); + if (currentStage != null && currentStage.getGirlfriend() != null) + { + if (Highscore.tallies.combo > 5 && currentStage.getGirlfriend().hasAnimation('sad')) + { + currentStage.getGirlfriend().playAnimation('sad'); + } + } if (Highscore.tallies.combo != 0) { @@ -1840,26 +1495,36 @@ class PlayState extends MusicBeatState /** * Jumps forward or backward a number of sections in the song. * Accounts for BPM changes, does not prevent death from skipped notes. - * @param sec + * @param sections The number of sections to jump, negative to go backwards. */ - function changeSection(sec:Int):Void + function changeSection(sections:Int):Void { FlxG.sound.music.pause(); - var daBPM:Float = currentSong.bpm; - var daPos:Float = 0; - for (i in 0...(Std.int(Conductor.currentStep / 16 + sec))) - { - var section = SongLoad.getSong()[i]; - if (section == null) continue; - if (section.changeBPM) + FlxG.sound.music.time += sections * Conductor.measureLengthMs; + + Conductor.update(FlxG.sound.music.time); + + /** + * + // TODO: Redo this for the new conductor. + var daBPM:Float = Conductor.bpm; + var daPos:Float = 0; + for (i in 0...(Std.int(Conductor.currentStep / 16 + sec))) { - daBPM = SongLoad.getSong()[i].bpm; + var section = .getSong()[i]; + if (section == null) continue; + if (section.changeBPM) + { + daBPM = .getSong()[i].bpm; + } + daPos += 4 * (1000 * 60 / daBPM); } - daPos += 4 * (1000 * 60 / daBPM); - } - Conductor.songPosition = FlxG.sound.music.time = daPos; - Conductor.songPosition += Conductor.offset; + Conductor.songPosition = FlxG.sound.music.time = daPos; + Conductor.songPosition += Conductor.offset; + + */ + resyncVocals(); } #end @@ -1870,11 +1535,11 @@ class PlayState extends MusicBeatState #if sys // spitter for ravy, teehee!! + var output = SerializerUtil.toJSON(inputSpitter); sys.io.File.saveContent("./scores.json", output); #end - seenCutscene = false; deathCounter = 0; mayPauseGame = false; FlxG.sound.music.volume = 0; @@ -1882,54 +1547,45 @@ class PlayState extends MusicBeatState if (currentSong != null && currentSong.validScore) { // crackhead double thingie, sets whether was new highscore, AND saves the song! - Highscore.tallies.isNewHighscore = Highscore.saveScore(currentSong.song, songScore, storyDifficulty); + Highscore.tallies.isNewHighscore = Highscore.saveScoreForDifficulty(currentSong.songId, songScore, currentDifficulty); - Highscore.saveCompletion(currentSong.song, Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes, storyDifficulty); + Highscore.saveCompletionForDifficulty(currentSong.songId, Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes, currentDifficulty); } - if (isStoryMode) + if (PlayStatePlaylist.isStoryMode) { - campaignScore += songScore; + PlayStatePlaylist.campaignScore += songScore; - storyPlaylist.remove(storyPlaylist[0]); + // Pop the next song ID from the list. + // Returns null if the list is empty. + var targetSongId:String = PlayStatePlaylist.playlistSongIds.shift(); - if (storyPlaylist.length <= 0) + if (targetSongId == null) { FlxG.sound.playMusic(Paths.music('freakyMenu')); transIn = FlxTransitionableState.defaultTransIn; transOut = FlxTransitionableState.defaultTransOut; - switch (storyWeek) - { - case 7: - FlxG.switchState(new VideoState()); - default: - FlxG.switchState(new StoryMenuState()); - } + // TODO: Rework week unlock logic. + // StoryMenuState.weekUnlocked[Std.int(Math.min(storyWeek + 1, StoryMenuState.weekUnlocked.length - 1))] = true; - // if () - StoryMenuState.weekUnlocked[Std.int(Math.min(storyWeek + 1, StoryMenuState.weekUnlocked.length - 1))] = true; - - if (currentSong?.validScore) + if (currentSong.validScore) { NGio.unlockMedal(60961); - Highscore.saveWeekScore(storyWeek, campaignScore, storyDifficulty); + Highscore.saveWeekScoreForDifficulty(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignScore, currentDifficulty); } - FlxG.save.data.weekUnlocked = StoryMenuState.weekUnlocked; + // FlxG.save.data.weekUnlocked = StoryMenuState.weekUnlocked; FlxG.save.flush(); + + moveToResultsScreen(); } else { - var difficulty:String = ""; + var difficulty:String = ''; - if (storyDifficulty == 0) difficulty = '-easy'; - - if (storyDifficulty == 2) difficulty = '-hard'; - - trace('LOADING NEXT SONG'); - trace(storyPlaylist[0].toLowerCase() + difficulty); + trace('Loading next song ($targetSongId : $difficulty)'); FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransOut = true; @@ -1937,7 +1593,8 @@ class PlayState extends MusicBeatState FlxG.sound.music.stop(); vocals.stop(); - if ((currentSong?.song ?? '').toLowerCase() == 'eggnog') + // TODO: Softcode this cutscene. + if (currentSong.songId == 'eggnog') { var blackShit:FlxSprite = new FlxSprite(-FlxG.width * FlxG.camera.zoom, -FlxG.height * FlxG.camera.zoom).makeGraphic(FlxG.width * 3, FlxG.height * 3, FlxColor.BLACK); @@ -1948,51 +1605,104 @@ class PlayState extends MusicBeatState FlxG.sound.play(Paths.sound('Lights_Shut_off'), function() { // no camFollow so it centers on horror tree - currentSong = SongLoad.loadFromJson(storyPlaylist[0].toLowerCase() + difficulty, storyPlaylist[0]); - LoadingState.loadAndSwitchState(new PlayState()); + var targetSong:Song = SongDataParser.fetchSong(targetSongId); + + var nextPlayState:PlayState = new PlayState( + { + targetSong: targetSong, + targetDifficulty: currentDifficulty, + targetCharacter: currentPlayerId, + }); + nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); + LoadingState.loadAndSwitchState(nextPlayState); }); } else { - previousCameraFollowPoint = cameraFollowPoint; - - currentSong_NEW = SongDataParser.fetchSong(PlayState.storyPlaylist[0].toLowerCase()); - LoadingState.loadAndSwitchState(new PlayState()); + var targetSong:Song = SongDataParser.fetchSong(targetSongId); + var nextPlayState:PlayState = new PlayState( + { + targetSong: targetSong, + targetDifficulty: currentDifficulty, + targetCharacter: currentPlayerId, + }); + nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y); + LoadingState.loadAndSwitchState(nextPlayState); } } } else { - trace('WENT TO RESULTS SCREEN!'); - trace(songScore); - // unloadAssets(); + moveToResultsScreen(); + } + } - camZoomRate = 0; + /** + * Play the camera zoom animation and move to the results screen. + */ + function moveToResultsScreen():Void + { + trace('WENT TO RESULTS SCREEN!'); + // Stop camera zooming on beat. + cameraZoomRate = 0; + + // If the opponent is GF, zoom in on the opponent. + // Else, if there is no GF, zoom in on BF. + // Else, zoom in on GF. + var targetDad:Bool = PlayState.instance.currentStage.getDad() != null && PlayState.instance.currentStage.getDad().characterId == 'gf'; + var targetBF:Bool = PlayState.instance.currentStage.getGirlfriend() == null && !targetDad; + + if (targetBF) + { + FlxG.camera.follow(PlayState.instance.currentStage.getBoyfriend(), null, 0.05); + FlxG.camera.targetOffset.y -= 350; + FlxG.camera.targetOffset.x += 20; + } + else if (targetDad) + { + FlxG.camera.follow(PlayState.instance.currentStage.getDad(), null, 0.05); + FlxG.camera.targetOffset.y -= 350; + FlxG.camera.targetOffset.x += 20; + } + else + { FlxG.camera.follow(PlayState.instance.currentStage.getGirlfriend(), null, 0.05); FlxG.camera.targetOffset.y -= 350; FlxG.camera.targetOffset.x += 20; - - FlxTween.tween(camHUD, {alpha: 0}, 0.6); - - new FlxTimer().start(0.8, _ -> { - currentStage.getGirlfriend().animation.play("cheer"); - - FlxTween.tween(FlxG.camera, {zoom: 1200}, 1.1, - { - ease: FlxEase.expoIn, - onComplete: _ -> { - persistentUpdate = false; - vocals.stop(); - camHUD.alpha = 1; - var res:ResultState = new ResultState(); - res.camera = camHUD; - openSubState(res); - } - }); - }); - // FlxG.switchState(new FreeplayState()); } + + FlxTween.tween(camHUD, {alpha: 0}, 0.6); + + // Zoom in on Girlfriend (or BF if no GF) + new FlxTimer().start(0.8, function(_) { + if (targetBF) + { + currentStage.getBoyfriend().animation.play('hey'); + } + else if (targetDad) + { + currentStage.getDad().animation.play('cheer'); + } + else + { + currentStage.getGirlfriend().animation.play('cheer'); + } + + // Zoom over to the Results screen. + FlxTween.tween(FlxG.camera, {zoom: 1200}, 1.1, + { + ease: FlxEase.expoIn, + onComplete: function(_) { + persistentUpdate = false; + vocals.stop(); + camHUD.alpha = 1; + var res:ResultState = new ResultState(); + res.camera = camHUD; + openSubState(res); + } + }); + }); } // gives score and pops up rating @@ -2052,6 +1762,7 @@ class PlayState extends MusicBeatState controls.NOTE_UP_P, controls.NOTE_RIGHT_P ]; + var indices:Array = []; for (i in 0...pressArray.length) { @@ -2083,63 +1794,6 @@ class PlayState extends MusicBeatState if (Highscore.tallies.combo >= 10 || Highscore.tallies.combo == 0) comboPopUps.displayCombo(Highscore.tallies.combo); } - /* - function controlCamera() - { - if (currentStage == null) - return; - - switch (cameraFocusCharacter) - { - default: // null = No change - break; - case 0: // Boyfriend - var isFocusedOnBF = cameraFollowPoint.x == currentStage.getBoyfriend().cameraFocusPoint.x; - if (!isFocusedOnBF) - { - // Focus the camera on the player. - cameraFollowPoint.setPosition(currentStage.getBoyfriend().cameraFocusPoint.x, currentStage.getBoyfriend().cameraFocusPoint.y); - } - case 1: // Dad - var isFocusedOnDad = cameraFollowPoint.x == currentStage.getDad().cameraFocusPoint.x; - if (!isFocusedOnDad) - { - cameraFollowPoint.setPosition(currentStage.getDad().cameraFocusPoint.x, currentStage.getDad().cameraFocusPoint.y); - } - case 2: // Girlfriend - var isFocusedOnGF = cameraFollowPoint.x == currentStage.getGirlfriend().cameraFocusPoint.x; - if (!isFocusedOnGF) - { - cameraFollowPoint.setPosition(currentStage.getGirlfriend().cameraFocusPoint.x, currentStage.getGirlfriend().cameraFocusPoint.y); - } - } - - /* - if (cameraRightSide && !isFocusedOnBF) - { - // Focus the camera on the player. - cameraFollowPoint.setPosition(currentStage.getBoyfriend().cameraFocusPoint.x, currentStage.getBoyfriend().cameraFocusPoint.y); - - // TODO: Un-hardcode this. - if (currentSong.song.toLowerCase() == 'tutorial') - FlxTween.tween(FlxG.camera, {zoom: 1 * FlxCamera.defaultZoom}, (Conductor.stepCrochet * 4 / 1000), {ease: FlxEase.elasticInOut}); - } - else if (!cameraRightSide && !isFocusedOnDad) - { - // Focus the camera on the opponent. - cameraFollowPoint.setPosition(currentStage.getDad().cameraFocusPoint.x, currentStage.getDad().cameraFocusPoint.y); - - // TODO: Un-hardcode this stuff. - if (currentStage.getDad().characterId == 'mom') - { - } - - if (currentSong.song.toLowerCase() == 'tutorial') - tweenCamIn(); - } - */ - // } - /** * Spitting out the input for ravy 🙇‍♂️!! */ @@ -2167,11 +1821,9 @@ class PlayState extends MusicBeatState // if (pressArray.contains(true)) // { // var lol:Array = cast pressArray; - // inputSpitter.push(Std.int(Conductor.songPosition) + " " + lol.join(" ")); + // inputSpitter.push(Std.int(Conductor.songPosition) + ' ' + lol.join(' ')); // } - if (FlxG.keys.justPressed.B) trace(inputSpitter.join("\n")); - // HOLDS, check for sustain notes if (holdArray.contains(true) && PlayState.instance.generatedMusic) { @@ -2185,7 +1837,10 @@ class PlayState extends MusicBeatState { Haptic.vibrate(100, 100); - PlayState.instance.currentStage.getBoyfriend().holdTimer = 0; + if (currentStage != null && currentStage.getBoyfriend() != null) + { + currentStage.getBoyfriend().holdTimer = 0; + } var possibleNotes:Array = []; // notes that can be hit var directionList:Array = []; // directions that can be hit @@ -2222,7 +1877,7 @@ class PlayState extends MusicBeatState for (note in dumbNotes) { - FlxG.log.add("killing dumb ass note at " + note.data.strumTime); + FlxG.log.add('killing dumb ass note at ' + note.data.strumTime); note.kill(); PlayState.instance.activeNotes.remove(note, true); note.destroy(); @@ -2269,13 +1924,72 @@ class PlayState extends MusicBeatState } } + /** + * Debug keys. Disabled while in cutscenes. + */ + public function debugKeyShit():Void + { + #if !debug + perfectMode = false; + #else + if (FlxG.keys.justPressed.H) camHUD.visible = !camHUD.visible; + #end + + if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState()); + + if (FlxG.keys.justPressed.F5) debug_refreshModules(); + + // Press U to open stage ditor. + if (FlxG.keys.justPressed.U) + { + // hack for HaxeUI generation, doesn't work unless persistentUpdate is false at state creation!! + disableKeys = true; + persistentUpdate = false; + openSubState(new StageOffsetSubState()); + } + + #if debug + // 1: End the song immediately. + if (FlxG.keys.justPressed.ONE) endSong(); + + // 2: Gain 10% health. + if (FlxG.keys.justPressed.TWO) health += 0.1 * 2.0; + + // 3: Lose 5% health. + if (FlxG.keys.justPressed.THREE) health -= 0.05 * 2.0; + #end + + // 7: Move to the charter. + if (FlxG.keys.justPressed.SEVEN) + { + lime.app.Application.current.window.alert("Press ~ on the main menu to get to the editor", 'LOL'); + } + + // 8: Move to the offset editor. + if (FlxG.keys.justPressed.EIGHT) FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState()); + + // 9: Toggle the old icon. + if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon(); + + #if debug + // PAGEUP: Skip forward one section. + // SHIFT+PAGEUP: Skip forward ten sections. + if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 10 : 1); + // PAGEDOWN: Skip backward one section. Doesn't replace notes. + // SHIFT+PAGEDOWN: Skip backward ten sections. + if (FlxG.keys.justPressed.PAGEDOWN) changeSection(FlxG.keys.pressed.SHIFT ? -10 : -1); + #end + + if (FlxG.keys.justPressed.B) trace(inputSpitter.join('\n')); + } + /** * Called when a player presses a key with no note present. * Scripts can modify the amount of health/score lost, whether player animations or sounds are used, * or even cancel the event entirely. - * - * @param direction - * @param hasPossibleNotes + * + * @param direction + * @param hasPossibleNotes */ function ghostNoteMiss(direction:funkin.noteStuff.NoteBasic.NoteType = 1, hasPossibleNotes:Bool = true):Void { @@ -2301,6 +2015,7 @@ class PlayState extends MusicBeatState controls.NOTE_UP_P, controls.NOTE_RIGHT_P ]; + var indices:Array = []; for (i in 0...pressArray.length) { @@ -2347,6 +2062,7 @@ class PlayState extends MusicBeatState controls.NOTE_UP_P, controls.NOTE_RIGHT_P ]; + var indices:Array = []; for (i in 0...pressArray.length) { @@ -2381,6 +2097,12 @@ class PlayState extends MusicBeatState Highscore.tallies.combo = comboPopUps.displayCombo(0); } + if (event.playSound) + { + vocals.playerVolume = 0; + FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); + } + note.active = false; note.visible = false; @@ -2425,14 +2147,15 @@ class PlayState extends MusicBeatState override function stepHit():Bool { - if (SongLoad.songData == null) return false; - // super.stepHit() returns false if a module cancelled the event. if (!super.stepHit()) return false; - if (Math.abs(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset)) > 20 - || Math.abs(vocals.checkSyncError(Conductor.songPosition - Conductor.offset)) > 20) + if (Math.abs(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset)) > 200 + || Math.abs(vocals.checkSyncError(Conductor.songPosition - Conductor.offset)) > 200) { + trace("VOCALS NEED RESYNC"); + if (vocals != null) trace(vocals.checkSyncError(Conductor.songPosition - Conductor.offset)); + trace(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset)); resyncVocals(); } @@ -2453,47 +2176,17 @@ class PlayState extends MusicBeatState activeNotes.sort(SortUtil.byStrumtime, FlxSort.DESCENDING); } - // Moving this code into the `beatHit` function allows for scripts and modules to control the camera better. - if (currentSong != null) + // Only zoom camera if we are zoomed by less than 35%. + if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.currentBeat % cameraZoomRate == 0) { - if (generatedMusic && SongLoad.getSong()[Std.int(Conductor.currentStep / 16)] != null) - { - // cameraRightSide = SongLoad.getSong()[Std.int(Conductor.currentStep / 16)].mustHitSection; - } - - if (SongLoad.getSong()[Math.floor(Conductor.currentStep / 16)] != null) - { - if (SongLoad.getSong()[Math.floor(Conductor.currentStep / 16)].changeBPM) - { - Conductor.forceBPM(SongLoad.getSong()[Math.floor(Conductor.currentStep / 16)].bpm); - FlxG.log.add('CHANGED BPM!'); - } - } + // Zoom camera in (1.5%) + FlxG.camera.zoom += cameraZoomIntensity * defaultCameraZoom; + // Hud zooms double (3%) + camHUD.zoom += hudCameraZoomIntensity * defaultHUDCameraZoom; } + // trace('Not bopping camera: ${FlxG.camera.zoom} < ${(1.35 * defaultCameraZoom)} && ${cameraZoomRate} > 0 && ${Conductor.currentBeat} % ${cameraZoomRate} == ${Conductor.currentBeat % cameraZoomRate}}'); - if (PreferencesMenu.getPref('camera-zoom')) - { - // TODO: Move this into a song script. - if (currentSong != null - && currentSong.song.toLowerCase() == 'milf' - && Conductor.currentBeat >= 168 - && Conductor.currentBeat < 200) - { - camZoomRate = 1; - } - if (currentSong != null && currentSong.song.toLowerCase() == 'milf' && Conductor.currentBeat >= 200) - { - camZoomRate = 4; - } - - if (FlxG.camera.zoom < (1.35 * FlxCamera.defaultZoom) && camZoomRate > 0 && Conductor.currentBeat % camZoomRate == 0) - { - FlxG.camera.zoom += 0.015 * FlxCamera.defaultZoom; - camHUD.zoom += 0.03; - } - } - - // That combo counter that got spoiled that one time. + // That combo milestones that got spoiled that one time. // Comes with NEAT visual and audio effects. // bruh this var is bonkers i thot it was a function lmfaooo @@ -2501,17 +2194,18 @@ class PlayState extends MusicBeatState // Break up into individual lines to aid debugging. var shouldShowComboText:Bool = false; - if (currentSong != null) - { - shouldShowComboText = (Conductor.currentBeat % 8 == 7); - var daSection = SongLoad.getSong()[Std.int(Conductor.currentBeat / 16)]; - shouldShowComboText = shouldShowComboText && (daSection != null && daSection.mustHitSection); - shouldShowComboText = shouldShowComboText && (Highscore.tallies.combo > 5); - - var daNextSection = SongLoad.getSong()[Std.int(Conductor.currentBeat / 16) + 1]; - var isEndOfSong = SongLoad.getSong().length < Std.int(Conductor.currentBeat / 16); - shouldShowComboText = shouldShowComboText && (isEndOfSong || (daNextSection != null && !daNextSection.mustHitSection)); - } + // TODO: Re-enable combo text (how to do this without sections?). + // if (currentSong != null) + // { + // shouldShowComboText = (Conductor.currentBeat % 8 == 7); + // var daSection = .getSong()[Std.int(Conductor.currentBeat / 16)]; + // shouldShowComboText = shouldShowComboText && (daSection != null && daSection.mustHitSection); + // shouldShowComboText = shouldShowComboText && (Highscore.tallies.combo > 5); + // + // var daNextSection = .getSong()[Std.int(Conductor.currentBeat / 16) + 1]; + // var isEndOfSong = .getSong().length < Std.int(Conductor.currentBeat / 16); + // shouldShowComboText = shouldShowComboText && (isEndOfSong || (daNextSection != null && !daNextSection.mustHitSection)); + // } if (shouldShowComboText) { @@ -2535,15 +2229,15 @@ class PlayState extends MusicBeatState /** * Handles characters dancing to the beat of the current song. - * + * * TODO: Move some of this logic into `Bopper.hx` */ - public function danceOnBeat() + public function danceOnBeat():Void { if (currentStage == null) return; - // TODO: Move this to a song event. - if (Conductor.currentBeat % 16 == 15 // && currentSong.song == 'Tutorial' + // TODO: Add HEY! song events to Tutorial. + if (Conductor.currentBeat % 16 == 15 && currentStage.getDad().characterId == 'gf' && Conductor.currentBeat > 16 && Conductor.currentBeat < 48) @@ -2579,7 +2273,7 @@ class PlayState extends MusicBeatState add(playerStrumline); playerStrumline.cameras = [camHUD]; - if (!isStoryMode) + if (!PlayStatePlaylist.isStoryMode) { playerStrumline.fadeInArrows(); } @@ -2592,7 +2286,7 @@ class PlayState extends MusicBeatState add(enemyStrumline); enemyStrumline.cameras = [camHUD]; - if (!isStoryMode) + if (!PlayStatePlaylist.isStoryMode) { enemyStrumline.fadeInArrows(); } @@ -2604,7 +2298,7 @@ class PlayState extends MusicBeatState * Function called before opening a new substate. * @param subState The substate to open. */ - public override function openSubState(subState:FlxSubState) + public override function openSubState(subState:FlxSubState):Void { // If there is a substate which requires the game to continue, // then make this a condition. @@ -2628,9 +2322,9 @@ class PlayState extends MusicBeatState /** * Function called before closing the current substate. - * @param subState + * @param subState */ - public override function closeSubState() + public override function closeSubState():Void { if (isGamePaused) { @@ -2640,16 +2334,20 @@ class PlayState extends MusicBeatState if (event.eventCanceled) return; - if (FlxG.sound.music != null && !startingSong && !isInCutscene && !isInDialog) resyncVocals(); + if (FlxG.sound.music != null && !startingSong && !isInCutscene) resyncVocals(); // Resume the countdown. Countdown.resumeCountdown(); #if discord_rpc - if (startTimer.finished) DiscordClient.changePresence(detailsText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC, true, - songLength - Conductor.songPosition); + if (startTimer.finished) + { + DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, songLength - Conductor.songPosition); + } else - DiscordClient.changePresence(detailsText, currentSong.song + " (" + storyDifficultyText + ")", iconRPC); + { + DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC); + } #end } @@ -2667,11 +2365,8 @@ class PlayState extends MusicBeatState if (!result) return; isInCutscene = false; - isInDialog = false; + camCutscene.visible = false; camHUD.visible = true; - talking = false; - - buildStrumlines(); } override function dispatchEvent(event:ScriptEvent):Void @@ -2687,6 +2382,10 @@ class PlayState extends MusicBeatState // Dispatch event to character script(s). if (currentStage != null) currentStage.dispatchToCharacters(event); + + ScriptEventDispatcher.callEvent(currentSong, event); + + // TODO: Dispatch event to note scripts } /** @@ -2695,7 +2394,7 @@ class PlayState extends MusicBeatState function updateScoreText():Void { // TODO: Add functionality for modules to update the score text. - scoreText.text = "Score:" + songScore; + scoreText.text = 'Score:' + songScore; } /** @@ -2720,14 +2419,11 @@ class PlayState extends MusicBeatState /** * Perform necessary cleanup before leaving the PlayState. */ - function performCleanup() + function performCleanup():Void { - // Uncache the song. - if (currentChart != null) {} - else if (currentSong != null) + if (currentChart != null) { - openfl.utils.Assets.cache.clear(Paths.inst(currentSong.song)); - openfl.utils.Assets.cache.clear(Paths.voices(currentSong.song)); + // TODO: Uncache the song. } // Remove reference to stage and remove sprites from it to save memory. @@ -2739,7 +2435,7 @@ class PlayState extends MusicBeatState currentStage = null; } - GameOverSubstate.reset(); + GameOverSubState.reset(); // Clear the static reference to this state. instance = null; @@ -2749,10 +2445,15 @@ class PlayState extends MusicBeatState * This function is called whenever Flixel switches switching to a new FlxState. * @return Whether to actually switch to the new state. */ - override function startOutro(onComplete:() -> Void):Void + override function switchTo(nextState:FlxState):Bool { - performCleanup(); + var result:Bool = super.switchTo(nextState); - onComplete(); + if (result) + { + performCleanup(); + } + + return result; } } diff --git a/source/funkin/play/PlayStatePlaylist.hx b/source/funkin/play/PlayStatePlaylist.hx new file mode 100644 index 000000000..acfd26752 --- /dev/null +++ b/source/funkin/play/PlayStatePlaylist.hx @@ -0,0 +1,56 @@ +package funkin.play; + +import funkin.util.Constants; + +/** + * Manages playback of multiple songs in a row. + * + * TODO: Add getters/setters for all these properties to validate them. + */ +class PlayStatePlaylist +{ + /** + * Whether the game is currently in Story Mode. If false, we are in Free Play Mode. + */ + public static var isStoryMode(default, default):Bool = false; + + /** + * The loist of upcoming songs to be played. + * When the user completes a song in Story Mode, the first entry in this list is played. + * When this list is empty, move to the Results screen instead. + */ + public static var playlistSongIds:Array = []; + + /** + * The cumulative score for all the songs in the playlist. + */ + public static var campaignScore:Int = 0; + + /** + * The title of this playlist, for example `Week 4` or `Weekend 1` + */ + public static var campaignTitle:String = 'UNKNOWN'; + + /** + * The internal ID of the current playlist, for example `week4` or `weekend-1`. + */ + public static var campaignId:String = 'unknown'; + + /** + * The current difficulty selected for this level (as a named ID). + */ + public static var currentDifficulty(default, default):String = Constants.DEFAULT_DIFFICULTY; + + /** + * Resets the playlist to its default state. + */ + public static function reset():Void + { + isStoryMode = false; + playlistSongIds = []; + campaignScore = 0; + campaignTitle = 'UNKNOWN'; + campaignId = 'unknown'; + currentDifficulty = Constants.DEFAULT_DIFFICULTY; + } +} diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index bdf4c19c9..12be46fc8 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -19,7 +19,7 @@ import funkin.shaderslmfao.LeftMaskShader; import funkin.ui.TallyCounter; import flxanimate.FlxAnimate.Settings; -class ResultState extends MusicBeatSubstate +class ResultState extends MusicBeatSubState { var resultsVariation:ResultVariations; var songName:FlxBitmapText; @@ -118,16 +118,16 @@ class ResultState extends MusicBeatSubstate difficulty = new FlxSprite(555); - var diffSpr:String = switch (CoolUtil.difficultyString()) + var diffSpr:String = switch (PlayState.instance.currentDifficulty) { - case "EASY": - "difEasy"; - case "NORMAL": - "difNormal"; - case "HARD": - "difHard"; + case 'EASY': + 'difEasy'; + case 'NORMAL': + 'difNormal'; + case 'HARD': + 'difHard'; case _: - "difNormal"; + 'difNormal'; } difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr)); @@ -144,7 +144,7 @@ class ResultState extends MusicBeatSubstate } else { - songName.text += PlayState.currentSong.song; + songName.text += PlayState.instance.currentSong.songId; } songName.antialiasing = true; diff --git a/source/funkin/play/Strumline.hx b/source/funkin/play/Strumline.hx index f45de5cac..4bbcc720a 100644 --- a/source/funkin/play/Strumline.hx +++ b/source/funkin/play/Strumline.hx @@ -13,7 +13,7 @@ import funkin.util.Constants; /** * A group controlling the individual notes of the strumline for a given player. - * + * * FUN FACT: Setting the X and Y of a FlxSpriteGroup will move all the sprites in the group. */ class Strumline extends FlxTypedSpriteGroup @@ -62,8 +62,8 @@ class Strumline extends FlxTypedSpriteGroup /** * Apply a small animation which moves the arrow down and fades it in. * Only plays at the start of Free Play songs. - * - * Note that modifying the offset of the whole strumline won't have the + * + * Note that modifying the offset of the whole strumline won't have the * @param arrow The arrow to animate. * @param index The index of the arrow in the strumline. */ diff --git a/source/funkin/play/character/AnimateAtlasCharacter.hx b/source/funkin/play/character/AnimateAtlasCharacter.hx index f0ce48a32..4f4b3f8f7 100644 --- a/source/funkin/play/character/AnimateAtlasCharacter.hx +++ b/source/funkin/play/character/AnimateAtlasCharacter.hx @@ -32,7 +32,7 @@ typedef AnimateAtlasAnimation = /** * An AnimateAtlasCharacter is a Character which is rendered by * displaying an animation derived from an Adobe Animate texture atlas spritesheet file. - * + * * BaseCharacter has game logic, AnimateAtlasCharacter has only rendering logic. * KEEP THEM SEPARATE! */ @@ -537,7 +537,7 @@ class AnimateAtlasCharacter extends BaseCharacter /** * Returns the left-most position of the left-most member. * If there are no members, x is returned. - * + * * @since 5.0.0 * @return the left-most position of the left-most member */ @@ -554,7 +554,7 @@ class AnimateAtlasCharacter extends BaseCharacter /** * Returns the right-most position of the right-most member. * If there are no members, x is returned. - * + * * @since 5.0.0 * @return the right-most position of the right-most member */ @@ -586,7 +586,7 @@ class AnimateAtlasCharacter extends BaseCharacter /** * Returns the top-most position of the top-most member. * If there are no members, y is returned. - * + * * @since 5.0.0 * @return the top-most position of the top-most member */ @@ -603,7 +603,7 @@ class AnimateAtlasCharacter extends BaseCharacter /** * Returns the top-most position of the top-most member. * If there are no members, y is returned. - * + * * @since 5.0.0 * @return the bottom-most position of the bottom-most member */ diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index f47f384a3..f4db7f66f 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -9,7 +9,7 @@ import funkin.play.stage.Bopper; /** * A Character is a stage prop which bops to the music as well as controlled by the strumlines. - * + * * Remember: The character's origin is at its FEET. (horizontal center, vertical bottom) */ class BaseCharacter extends Bopper @@ -43,7 +43,7 @@ class BaseCharacter extends Bopper /** * Set to true when the character being used in a special way. * This includes the Chart Editor and the Animation Editor. - * + * * Used by scripts to ensure that they don't try to run code to interact with the stage when the stage doesn't actually exist. */ public var debug:Bool = false; @@ -76,7 +76,7 @@ class BaseCharacter extends Bopper /** * The absolute position of the top-left of the character. - * @return + * @return */ public var cornerPosition(get, set):FlxPoint; @@ -112,7 +112,7 @@ class BaseCharacter extends Bopper /** * Returns the point the camera should focus on. * Should be approximately centered on the character, and should not move based on the current animation. - * + * * Set the position of this rather than reassigning it, so that anything referencing it will not be affected. */ public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0); @@ -242,7 +242,7 @@ class BaseCharacter extends Bopper /** * Set the sprite scale to the appropriate value. - * @param scale + * @param scale */ public function setScale(scale:Null):Void { @@ -394,14 +394,14 @@ class BaseCharacter extends Bopper /** * Since no `onBeatHit` or `dance` calls happen in GameOverSubState, * this regularly gets called instead. - * + * * @param force Force the deathLoop animation to play, even if `firstDeath` is still playing. */ public function playDeathAnimation(force:Bool = false):Void { if (force || (getCurrentAnimation().startsWith('firstDeath') && isAnimationFinished())) { - playAnimation('deathLoop' + GameOverSubstate.animationSuffix); + playAnimation('deathLoop' + GameOverSubState.animationSuffix); } } @@ -582,6 +582,13 @@ class BaseCharacter extends Bopper // restart even if already playing, because the character might sing the same note twice. playAnimation(anim, true); } + + public override function playAnimation(name:String, restart:Bool = false, ?ignoreOther:Bool = false, ?reversed:Bool = false):Void + { + FlxG.watch.addQuick('playAnim(${characterName})', name); + trace('playAnim(${characterName}): ${name}'); + super.playAnimation(name, restart, ignoreOther, reversed); + } } /** diff --git a/source/funkin/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx index 886047ec2..16bf3a8a6 100644 --- a/source/funkin/play/character/CharacterData.hx +++ b/source/funkin/play/character/CharacterData.hx @@ -33,7 +33,7 @@ class CharacterDataParser /** * Parses and preloads the game's stage data and scripts when the game starts. - * + * * If you want to force stages to be reloaded, you can just call this function again. */ public static function loadCharacterCache():Void @@ -294,7 +294,7 @@ class CharacterDataParser /** * Load a character's JSON file and parse its data. - * + * * @param charId The character to load. * @return The character data, or null if validation failed. */ @@ -363,8 +363,8 @@ class CharacterDataParser /** * Set unspecified parameters to their defaults. * If the parameter is mandatory, print an error message. - * @param id - * @param input + * @param id + * @param input * @return The validated character data */ static function validateCharacterData(id:String, input:CharacterData):Null @@ -624,7 +624,7 @@ typedef CharacterData = /** * The frequency at which the character will play its idle animation, in beats. * Increasing this number will make the character dance less often. - * + * * @default 1 */ var danceEvery:Null; @@ -633,7 +633,7 @@ typedef CharacterData = * The minimum duration that a character will play a note animation for, in beats. * If this number is too low, you may see the character start playing the idle animation between notes. * If this number is too high, you may see the the character play the sing animation for too long after the notes are gone. - * + * * Examples: * - Daddy Dearest uses a value of `1.525`. * @default 1.0 @@ -654,7 +654,7 @@ typedef CharacterData = /** * Whether or not the whole ass sprite is flipped by default. * Useful for characters that could also be played (Pico) - * + * * @default false */ var flipX:Null; diff --git a/source/funkin/play/character/MultiSparrowCharacter.hx b/source/funkin/play/character/MultiSparrowCharacter.hx index 5d4deee5d..45934c99b 100644 --- a/source/funkin/play/character/MultiSparrowCharacter.hx +++ b/source/funkin/play/character/MultiSparrowCharacter.hx @@ -8,11 +8,11 @@ import funkin.play.character.CharacterData.CharacterRenderType; /** * For some characters which use Sparrow atlases, the spritesheets need to be split * into multiple files. This character renderer handles by showing the appropriate sprite. - * + * * Examples in base game include BF Holding GF (most of the sprites are in one file * but the death animation is in a separate file). * Only example I can think of in mods is Tricky (which has a separate file for each animation). - * + * * BaseCharacter has game logic, SparrowCharacter has only rendering logic. * KEEP THEM SEPARATE! * diff --git a/source/funkin/play/character/SparrowCharacter.hx b/source/funkin/play/character/SparrowCharacter.hx index 1db14fe23..4bd17e7e6 100644 --- a/source/funkin/play/character/SparrowCharacter.hx +++ b/source/funkin/play/character/SparrowCharacter.hx @@ -8,7 +8,7 @@ import funkin.play.character.CharacterData.CharacterRenderType; /** * A SparrowCharacter is a Character which is rendered by * displaying an animation derived from a SparrowV2 atlas spritesheet file. - * + * * BaseCharacter has game logic, SparrowCharacter has only rendering logic. * KEEP THEM SEPARATE! */ diff --git a/source/funkin/play/cutscene/VanillaCutscenes.hx b/source/funkin/play/cutscene/VanillaCutscenes.hx index a0cbd965b..a332d0795 100644 --- a/source/funkin/play/cutscene/VanillaCutscenes.hx +++ b/source/funkin/play/cutscene/VanillaCutscenes.hx @@ -1,13 +1,10 @@ package funkin.play.cutscene; -// import hxcodec.flixel.FlxVideoSprite; -// import hxcodec.flixel.FlxCutsceneState; import flixel.FlxSprite; import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.util.FlxColor; import flixel.util.FlxTimer; -import funkin.graphics.video.FlxVideo; /** * Static methods for playing cutscenes in the PlayState. @@ -15,112 +12,46 @@ import funkin.graphics.video.FlxVideo; */ class VanillaCutscenes { - /** - * Well, well, well, what have we got here? - */ - public static function playUghCutscene():Void - { - playVideoCutscene('music/ughCutscene.mp4'); - } - - /** - * Nice bars for an ugly, boring teenager! - */ - public static function playGunsCutscene():Void - { - playVideoCutscene('music/gunsCutscene.mp4'); - } - - /** - * Don't you have a school to shoot up? - */ - public static function playStressCutscene():Void - { - playVideoCutscene('music/stressCutscene.mp4'); - } - static var blackScreen:FlxSprite; - /** - * Plays a cutscene from a video file, then starts the countdown once the video is done. - * TODO: Cutscene is currently skipped on native platforms. - */ - static function playVideoCutscene(path:String):Void - { - // Tell PlayState to stop the song until the video is done. - PlayState.isInCutscene = true; - PlayState.instance.camHUD.visible = false; - - // Display a black screen to hide the game while the video is playing. - blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); - blackScreen.scrollFactor.set(0, 0); - blackScreen.cameras = [PlayState.instance.camCutscene]; - PlayState.instance.add(blackScreen); - - #if html5 - // Video displays OVER the FlxState. - vid = new FlxVideo(path); - vid.finishCallback = finishCutscene.bind(0.5); - #else - // Video displays OVER the FlxState. - // vid = new FlxVideoSprite(0, 0); - - vid.cameras = [PlayState.instance.camCutscene]; - - PlayState.instance.add(vid); - - vid.playVideo(Paths.file(path), false); - vid.onEndReached.add(finishCutscene.bind(0.5)); - #end - } - - static var vid:#if html5 FlxVideo #else Dynamic /**FlxVideoSprite **/ #end; + static final TWEEN_DURATION:Float = 2.0; /** - * Does the cleanup to start the countdown after the video is done. - * Gets called immediately if the video can't be played. - */ - public static function finishCutscene(?transitionTime:Float = 2.5):Void - { - trace('ALERT: Finish cutscene called!'); - - #if html5 - #else - vid.stop(); - PlayState.instance.remove(vid); - #end - - PlayState.instance.camHUD.visible = true; - - FlxTween.tween(blackScreen, {alpha: 0}, transitionTime, - { - ease: FlxEase.quadInOut, - onComplete: function(twn:FlxTween) { - PlayState.instance.remove(blackScreen); - blackScreen = null; - } - }); - FlxTween.tween(FlxG.camera, {zoom: PlayState.defaultCameraZoom}, transitionTime, - { - ease: FlxEase.quadInOut, - onComplete: function(twn:FlxTween) { - PlayState.instance.startCountdown(); - } - }); - } - - /** - * FNF corruption mod??? + * Plays the cutscene that appears at the start of Winter Horrorland. + * TODO: Move this to `winter-horrorland.hxc` */ public static function playHorrorStartCutscene():Void { - PlayState.isInCutscene = true; + PlayState.instance.isInCutscene = true; PlayState.instance.camHUD.visible = false; blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); blackScreen.scrollFactor.set(0, 0); + blackScreen.zIndex = 1000000; PlayState.instance.add(blackScreen); - new FlxTimer().start(0.1, _ -> finishCutscene(2.5)); + new FlxTimer().start(0.1, function(_) { + trace('Playing horrorland cutscene...'); + PlayState.instance.remove(blackScreen); + + // Force set the camera position and zoom. + PlayState.instance.cameraFollowPoint.setPosition(400, -2050); + PlayState.instance.resetCamera(); + FlxG.camera.zoom = 2.5; + + // Play the Sound effect. + FlxG.sound.play(Paths.sound('Lights_Turn_On'), function() { + // Fade in the HUD. + trace('SFX done...'); + PlayState.instance.camHUD.visible = true; + PlayState.instance.camHUD.alpha = 0.0; // Use alpha rather than visible to let us fade it in. + FlxTween.tween(PlayState.instance.camHUD, {alpha: 1.0}, TWEEN_DURATION, {ease: FlxEase.quadInOut}); + + // Start the countdown. + trace('Zoom out done...'); + trace('Begin countdown (ends cutscene)'); + PlayState.instance.startCountdown(); + }); + }); } } diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx new file mode 100644 index 000000000..652ca0287 --- /dev/null +++ b/source/funkin/play/cutscene/VideoCutscene.hx @@ -0,0 +1,155 @@ +package funkin.play.cutscene; + +import funkin.play.PlayState; +import flixel.FlxSprite; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxColor; +import flixel.util.FlxTimer; +#if html5 +import funkin.graphics.video.FlxVideo; +#else +import hxcodec.flixel.FlxVideoSprite; +#end + +/** + * Assumes you are in the PlayState. + */ +class VideoCutscene +{ + static var blackScreen:FlxSprite; + + /** + * Play a video cutscene. + * TODO: Currently this is hardcoded to start the countdown after the video is done. + * @param path The path to the video file. Use Paths.file(path) to get the correct path. + */ + public static function play(filePath:String):Void + { + if (PlayState.instance == null) return; + + if (!openfl.Assets.exists(filePath)) + { + trace('ERROR: Video file does not exist: ${filePath}'); + return; + } + + // Trigger the cutscene. Don't play the song in the background. + PlayState.instance.isInCutscene = true; + PlayState.instance.camHUD.visible = false; + PlayState.instance.camCutscene.visible = true; + + // Display a black screen to hide the game while the video is playing. + blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); + blackScreen.scrollFactor.set(0, 0); + blackScreen.cameras = [PlayState.instance.camCutscene]; + PlayState.instance.add(blackScreen); + + #if html5 + playVideoHTML5(filePath); + #else + playVideoNative(filePath); + #end + } + + public static function isPlaying():Bool + { + return vid != null; + } + + #if html5 + static var vid:FlxVideo; + + static function playVideoHTML5(filePath:String):Void + { + // Video displays OVER the FlxState. + vid = new FlxVideo(filePath); + if (vid != null) + { + vid.zIndex = 0; + + vid.finishCallback = finishVideo.bind(0.5); + + vid.cameras = [PlayState.instance.camCutscene]; + + PlayState.instance.add(vid); + + PlayState.instance.refresh(); + } + else + { + trace('ALERT: Video is null! Could not play cutscene!'); + } + } + #else + static var vid:FlxVideoSprite; + + static function playVideoNative(filePath:String):Void + { + // Video displays OVER the FlxState. + vid = new FlxVideoSprite(0, 0); + + if (vid != null) + { + vid.zIndex = 0; + vid.bitmap.onEndReached.add(finishVideo.bind(0.5)); + + vid.cameras = [PlayState.instance.camCutscene]; + + PlayState.instance.add(vid); + + PlayState.instance.refresh(); + vid.play(filePath, false); + } + else + { + trace('ALERT: Video is null! Could not play cutscene!'); + } + } + #end + + public static function finishVideo(?transitionTime:Float = 0.5):Void + { + trace('ALERT: Finish video cutscene called!'); + + #if html5 + if (vid != null) + { + PlayState.instance.remove(vid); + } + #else + if (vid != null) + { + vid.stop(); + PlayState.instance.remove(vid); + } + #end + vid.destroy(); + vid = null; + + PlayState.instance.camCutscene.visible = true; + PlayState.instance.camHUD.visible = true; + + FlxTween.tween(blackScreen, {alpha: 0}, transitionTime, + { + ease: FlxEase.quadInOut, + onComplete: function(twn:FlxTween) { + PlayState.instance.remove(blackScreen); + blackScreen = null; + } + }); + FlxTween.tween(FlxG.camera, {zoom: PlayState.instance.defaultCameraZoom}, transitionTime, + { + ease: FlxEase.quadInOut, + onComplete: function(twn:FlxTween) { + PlayState.instance.startCountdown(); + } + }); + } +} + +/* + trace('Video playback failed (${e})'); + vid = null; + finishCutscene(0.5); + */ diff --git a/source/funkin/play/event/FocusCameraSongEvent.hx b/source/funkin/play/event/FocusCameraSongEvent.hx index 29d2e80e0..8d677118b 100644 --- a/source/funkin/play/event/FocusCameraSongEvent.hx +++ b/source/funkin/play/event/FocusCameraSongEvent.hx @@ -1,12 +1,14 @@ package funkin.play.event; -import funkin.play.event.SongEvent; import funkin.play.song.SongData; +import funkin.play.event.SongEvent; +import funkin.play.event.SongEventData.SongEventFieldType; +import funkin.play.event.SongEventData.SongEventSchema; /** * This class represents a handler for a type of song event. * It is used by the ScriptedSongEvent class to handle user-defined events. - * + * * Example: Focus on Boyfriend: * ``` * { @@ -16,7 +18,7 @@ import funkin.play.song.SongData; * } * } * ``` - * + * * Example: Focus on 10px above Girlfriend: * ``` * { @@ -27,7 +29,7 @@ import funkin.play.song.SongData; * } * } * ``` - * + * * Example: Focus on (100, 100): * ``` * { diff --git a/source/funkin/play/event/PlayAnimationSongEvent.hx b/source/funkin/play/event/PlayAnimationSongEvent.hx index 73937e61a..a187ca285 100644 --- a/source/funkin/play/event/PlayAnimationSongEvent.hx +++ b/source/funkin/play/event/PlayAnimationSongEvent.hx @@ -3,6 +3,8 @@ package funkin.play.event; import flixel.FlxSprite; import funkin.play.character.BaseCharacter; import funkin.play.event.SongEvent; +import funkin.play.event.SongEventData.SongEventFieldType; +import funkin.play.event.SongEventData.SongEventSchema; import funkin.play.song.SongData; class PlayAnimationSongEvent extends SongEvent diff --git a/source/funkin/play/event/ScriptedSongEvent.hx b/source/funkin/play/event/ScriptedSongEvent.hx index 6f5e1c9bc..d85a1c838 100644 --- a/source/funkin/play/event/ScriptedSongEvent.hx +++ b/source/funkin/play/event/ScriptedSongEvent.hx @@ -7,7 +7,7 @@ import polymod.hscript.HScriptedClass; * A script that can be tied to a SongEvent. * Create a scripted class that extends SongEvent, * then call `super('SongEventType')` to use this. - * + * * - Remember to override `handleEvent(data:SongEventData)` to perform your actions when the event is hit. * - Remember to override `getTitle()` to return an event name that will be displayed in the editor. * - Remember to override `getEventSchema()` to return a schema for the event data, used to build a form in the chart editor. diff --git a/source/funkin/play/event/SetCameraBopSongEvent.hx b/source/funkin/play/event/SetCameraBopSongEvent.hx new file mode 100644 index 000000000..6f8a0645d --- /dev/null +++ b/source/funkin/play/event/SetCameraBopSongEvent.hx @@ -0,0 +1,90 @@ +package funkin.play.event; + +import funkin.util.Constants; +import flixel.tweens.FlxTween; +import flixel.FlxCamera; +import flixel.tweens.FlxEase; +import funkin.play.event.SongEvent; +import funkin.play.song.SongData; +import funkin.play.event.SongEventData; +import funkin.play.event.SongEventData.SongEventFieldType; + +/** + * This class represents a handler for configuring camera bop intensity and rate. + * + * Example: Bop the camera twice as hard, once per beat (rather than once every four beats). + * ``` + * { + * 'e': 'SetCameraBop', + * 'v': { + * 'intensity': 2.0, + * 'rate': 1, + * } + * } + * ``` + * + * Example: Reset the camera bop to default values. + * ``` + * { + * 'e': 'SetCameraBop', + * 'v': {} + * } + * ``` + */ +class SetCameraBopSongEvent extends SongEvent +{ + public function new() + { + super('SetCameraBop'); + } + + public override function handleEvent(data:SongEventData):Void + { + // Does nothing if there is no PlayState camera or stage. + if (PlayState.instance == null) return; + + var rate:Null = data.getInt('rate'); + if (rate == null) rate = Constants.DEFAULT_ZOOM_RATE; + var intensity:Null = data.getFloat('intensity'); + if (intensity == null) intensity = 1.0; + + PlayState.instance.cameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity; + PlayState.instance.hudCameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity * 2.0; + PlayState.instance.cameraZoomRate = rate; + trace('Set camera zoom rate to ${PlayState.instance.cameraZoomRate}'); + } + + public override function getTitle():String + { + return 'Set Camera Bop'; + } + + /** + * ``` + * { + * 'intensity': FLOAT, // Zoom amount + * 'rate': INT, // Zoom rate (beats/zoom) + * } + * ``` + * @return SongEventSchema + */ + public override function getEventSchema():SongEventSchema + { + return [ + { + name: 'intensity', + title: 'Intensity', + defaultValue: 1.0, + step: 0.1, + type: SongEventFieldType.FLOAT + }, + { + name: 'rate', + title: 'Rate (beats/zoom)', + defaultValue: 4, + step: 1, + type: SongEventFieldType.INTEGER, + } + ]; + } +} diff --git a/source/funkin/play/event/SongEvent.hx b/source/funkin/play/event/SongEvent.hx index 4071f9a34..098a84e12 100644 --- a/source/funkin/play/event/SongEvent.hx +++ b/source/funkin/play/event/SongEvent.hx @@ -1,7 +1,7 @@ package funkin.play.event; -import funkin.util.macro.ClassMacro; import funkin.play.song.SongData.SongEventData; +import funkin.play.event.SongEventData.SongEventSchema; /** * This class represents a handler for a type of song event. @@ -52,233 +52,3 @@ class SongEvent return 'SongEvent(${this.id})'; } } - -/** - * This class statically handles the parsing of internal and scripted song event handlers. - */ -class SongEventParser -{ - /** - * Every built-in event class must be added to this list. - * Thankfully, with the power of `SongEventMacro`, this is done automatically. - */ - static final BUILTIN_EVENTS:List> = ClassMacro.listSubclassesOf(SongEvent); - - /** - * Map of internal handlers for song events. - * These may be either `ScriptedSongEvents` or built-in classes extending `SongEvent`. - */ - static final eventCache:Map = new Map(); - - public static function loadEventCache():Void - { - clearEventCache(); - - // - // BASE GAME EVENTS - // - registerBaseEvents(); - registerScriptedEvents(); - } - - static function registerBaseEvents() - { - trace('Instantiating ${BUILTIN_EVENTS.length} built-in song events...'); - for (eventCls in BUILTIN_EVENTS) - { - var eventClsName:String = Type.getClassName(eventCls); - if (eventClsName == 'funkin.play.event.SongEvent' || eventClsName == 'funkin.play.event.ScriptedSongEvent') continue; - - var event:SongEvent = Type.createInstance(eventCls, ["UNKNOWN"]); - - if (event != null) - { - trace(' Loaded built-in song event: (${event.id})'); - eventCache.set(event.id, event); - } - else - { - trace(' Failed to load built-in song event: ${Type.getClassName(eventCls)}'); - } - } - } - - static function registerScriptedEvents() - { - var scriptedEventClassNames:Array = ScriptedSongEvent.listScriptClasses(); - if (scriptedEventClassNames == null || scriptedEventClassNames.length == 0) return; - - trace('Instantiating ${scriptedEventClassNames.length} scripted song events...'); - for (eventCls in scriptedEventClassNames) - { - var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN"); - - if (event != null) - { - trace(' Loaded scripted song event: ${event.id}'); - eventCache.set(event.id, event); - } - else - { - trace(' Failed to instantiate scripted song event class: ${eventCls}'); - } - } - } - - public static function listEventIds():Array - { - return eventCache.keys().array(); - } - - public static function listEvents():Array - { - return eventCache.values(); - } - - public static function getEvent(id:String):SongEvent - { - return eventCache.get(id); - } - - public static function getEventSchema(id:String):SongEventSchema - { - var event:SongEvent = getEvent(id); - if (event == null) return null; - - return event.getEventSchema(); - } - - static function clearEventCache() - { - eventCache.clear(); - } - - public static function handleEvent(data:SongEventData):Void - { - var eventType:String = data.event; - var eventHandler:SongEvent = eventCache.get(eventType); - - if (eventHandler != null) - { - eventHandler.handleEvent(data); - } - else - { - trace('WARNING: No event handler for event with id: ${eventType}'); - } - - data.activated = true; - } - - public static inline function handleEvents(events:Array):Void - { - for (event in events) - { - handleEvent(event); - } - } - - /** - * Given a list of song events and the current timestamp, - * return a list of events that should be handled. - */ - public static function queryEvents(events:Array, currentTime:Float):Array - { - return events.filter(function(event:SongEventData):Bool - { - // If the event is already activated, don't activate it again. - if (event.activated) return false; - - // If the event is in the future, don't activate it. - if (event.time > currentTime) return false; - - return true; - }); - } - - /** - * Reset activation of all the provided events. - */ - public static function resetEvents(events:Array):Void - { - for (event in events) - { - event.activated = false; - // TODO: Add an onReset() method to SongEvent? - } - } -} - -enum abstract SongEventFieldType(String) from String to String -{ - /** - * The STRING type will display as a text field. - */ - var STRING = "string"; - - /** - * The INTEGER type will display as a text field that only accepts numbers. - */ - var INTEGER = "integer"; - - /** - * The FLOAT type will display as a text field that only accepts numbers. - */ - var FLOAT = "float"; - - /** - * The BOOL type will display as a checkbox. - */ - var BOOL = "bool"; - - /** - * The ENUM type will display as a dropdown. - * Make sure to specify the `keys` field in the schema. - */ - var ENUM = "enum"; -} - -typedef SongEventSchemaField = -{ - /** - * The name of the property as it should be saved in the event data. - */ - name:String, - - /** - * The title of the field to display in the UI. - */ - title:String, - - /** - * The type of the field. - */ - type:SongEventFieldType, - - /** - * Used for ENUM values. - * The key is the display name and the value is the actual value. - */ - ?keys:Map, - /** - * Used for INTEGER and FLOAT values. - * The minimum value that can be entered. - */ - ?min:Float, - /** - * Used for INTEGER and FLOAT values. - * The maximum value that can be entered. - */ - ?max:Float, - /** - * Used for INTEGER and FLOAT values. - * The step value that will be used when incrementing/decrementing the value. - */ - ?step:Float, - /** - * An optional default value for the field. - */ - ?defaultValue:Dynamic, -} - -typedef SongEventSchema = Array; diff --git a/source/funkin/play/event/SongEventData.hx b/source/funkin/play/event/SongEventData.hx new file mode 100644 index 000000000..8c157b52a --- /dev/null +++ b/source/funkin/play/event/SongEventData.hx @@ -0,0 +1,235 @@ +package funkin.play.event; + +import funkin.play.event.SongEventData.SongEventSchema; +import funkin.play.song.SongData.SongEventData; +import funkin.util.macro.ClassMacro; +import funkin.play.event.ScriptedSongEvent; + +/** + * This class statically handles the parsing of internal and scripted song event handlers. + */ +class SongEventParser +{ + /** + * Every built-in event class must be added to this list. + * Thankfully, with the power of `SongEventMacro`, this is done automatically. + */ + static final BUILTIN_EVENTS:List> = ClassMacro.listSubclassesOf(SongEvent); + + /** + * Map of internal handlers for song events. + * These may be either `ScriptedSongEvents` or built-in classes extending `SongEvent`. + */ + static final eventCache:Map = new Map(); + + public static function loadEventCache():Void + { + clearEventCache(); + + // + // BASE GAME EVENTS + // + registerBaseEvents(); + registerScriptedEvents(); + } + + static function registerBaseEvents() + { + trace('Instantiating ${BUILTIN_EVENTS.length} built-in song events...'); + for (eventCls in BUILTIN_EVENTS) + { + var eventClsName:String = Type.getClassName(eventCls); + if (eventClsName == 'funkin.play.event.SongEvent' || eventClsName == 'funkin.play.event.ScriptedSongEvent') continue; + + var event:SongEvent = Type.createInstance(eventCls, ["UNKNOWN"]); + + if (event != null) + { + trace(' Loaded built-in song event: (${event.id})'); + eventCache.set(event.id, event); + } + else + { + trace(' Failed to load built-in song event: ${Type.getClassName(eventCls)}'); + } + } + } + + static function registerScriptedEvents() + { + var scriptedEventClassNames:Array = ScriptedSongEvent.listScriptClasses(); + if (scriptedEventClassNames == null || scriptedEventClassNames.length == 0) return; + + trace('Instantiating ${scriptedEventClassNames.length} scripted song events...'); + for (eventCls in scriptedEventClassNames) + { + var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN"); + + if (event != null) + { + trace(' Loaded scripted song event: ${event.id}'); + eventCache.set(event.id, event); + } + else + { + trace(' Failed to instantiate scripted song event class: ${eventCls}'); + } + } + } + + public static function listEventIds():Array + { + return eventCache.keys().array(); + } + + public static function listEvents():Array + { + return eventCache.values(); + } + + public static function getEvent(id:String):SongEvent + { + return eventCache.get(id); + } + + public static function getEventSchema(id:String):SongEventSchema + { + var event:SongEvent = getEvent(id); + if (event == null) return null; + + return event.getEventSchema(); + } + + static function clearEventCache() + { + eventCache.clear(); + } + + public static function handleEvent(data:SongEventData):Void + { + var eventType:String = data.event; + var eventHandler:SongEvent = eventCache.get(eventType); + + if (eventHandler != null) + { + eventHandler.handleEvent(data); + } + else + { + trace('WARNING: No event handler for event with id: ${eventType}'); + } + + data.activated = true; + } + + public static inline function handleEvents(events:Array):Void + { + for (event in events) + { + handleEvent(event); + } + } + + /** + * Given a list of song events and the current timestamp, + * return a list of events that should be handled. + */ + public static function queryEvents(events:Array, currentTime:Float):Array + { + return events.filter(function(event:SongEventData):Bool { + // If the event is already activated, don't activate it again. + if (event.activated) return false; + + // If the event is in the future, don't activate it. + if (event.time > currentTime) return false; + + return true; + }); + } + + /** + * Reset activation of all the provided events. + */ + public static function resetEvents(events:Array):Void + { + for (event in events) + { + event.activated = false; + // TODO: Add an onReset() method to SongEvent? + } + } +} + +enum abstract SongEventFieldType(String) from String to String +{ + /** + * The STRING type will display as a text field. + */ + var STRING = "string"; + + /** + * The INTEGER type will display as a text field that only accepts numbers. + */ + var INTEGER = "integer"; + + /** + * The FLOAT type will display as a text field that only accepts numbers. + */ + var FLOAT = "float"; + + /** + * The BOOL type will display as a checkbox. + */ + var BOOL = "bool"; + + /** + * The ENUM type will display as a dropdown. + * Make sure to specify the `keys` field in the schema. + */ + var ENUM = "enum"; +} + +typedef SongEventSchemaField = +{ + /** + * The name of the property as it should be saved in the event data. + */ + name:String, + + /** + * The title of the field to display in the UI. + */ + title:String, + + /** + * The type of the field. + */ + type:SongEventFieldType, + + /** + * Used for ENUM values. + * The key is the display name and the value is the actual value. + */ + ?keys:Map, + /** + * Used for INTEGER and FLOAT values. + * The minimum value that can be entered. + */ + ?min:Float, + /** + * Used for INTEGER and FLOAT values. + * The maximum value that can be entered. + */ + ?max:Float, + /** + * Used for INTEGER and FLOAT values. + * The step value that will be used when incrementing/decrementing the value. + */ + ?step:Float, + /** + * An optional default value for the field. + */ + ?defaultValue:Dynamic, +} + +typedef SongEventSchema = Array; diff --git a/source/funkin/play/event/ZoomCameraSongEvent.hx b/source/funkin/play/event/ZoomCameraSongEvent.hx new file mode 100644 index 000000000..e6e9c843d --- /dev/null +++ b/source/funkin/play/event/ZoomCameraSongEvent.hx @@ -0,0 +1,147 @@ +package funkin.play.event; + +import flixel.tweens.FlxTween; +import flixel.FlxCamera; +import flixel.tweens.FlxEase; +import funkin.play.event.SongEvent; +import funkin.play.song.SongData; +import funkin.play.event.SongEventData; +import funkin.play.event.SongEventData.SongEventFieldType; + +/** + * This class represents a handler for camera zoom events. + * + * Example: Zoom to 1.3x: + * ``` + * { + * 'e': 'ZoomCamera', + * 'v': 1.3 + * } + * ``` + * + * Example: Zoom to 1.3x + * ``` + * { + * 'e': 'FocusCamera', + * 'v': { + * 'char': 2, + * 'y': -10, + * } + * } + * ``` + * + * Example: Focus on (100, 100): + * ``` + * { + * 'e': 'FocusCamera', + * 'v': { + * 'char': -1, + * 'x': 100, + * 'y': 100, + * } + * } + * ``` + */ +class ZoomCameraSongEvent extends SongEvent +{ + public function new() + { + super('ZoomCamera'); + } + + public override function handleEvent(data:SongEventData):Void + { + // Does nothing if there is no PlayState camera or stage. + if (PlayState.instance == null) return; + + var zoom:Null = data.getFloat('zoom'); + if (zoom == null) zoom = 1.0; + var duration:Null = data.getFloat('duration'); + if (duration == null) duration = 4.0; + + var ease:Null = data.getString('ease'); + if (ease == null) ease = 'linear'; + + // If it's a string, check the value. + switch (ease) + { + case 'INSTANT': + // Set the zoom. Use defaultCameraZoom to prevent breaking camera bops. + PlayState.instance.defaultCameraZoom = zoom * FlxCamera.defaultZoom; + default: + var easeFunction:NullFloat> = Reflect.field(FlxEase, ease); + if (easeFunction == null) + { + trace('Invalid ease function: $ease'); + return; + } + + FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.stepCrochet * duration / 1000), {ease: easeFunction}); + } + } + + public override function getTitle():String + { + return 'Zoom Camera'; + } + + /** + * ``` + * { + * 'zoom': FLOAT, // Target zoom level. + * 'duration': FLOAT, // Optional duration in steps + * 'ease': ENUM, // Optional easing function + * } + * @return SongEventSchema + */ + public override function getEventSchema():SongEventSchema + { + return [ + { + name: 'zoom', + title: 'Zoom Level', + defaultValue: 1.0, + step: 0.1, + type: SongEventFieldType.FLOAT + }, + { + name: 'duration', + title: 'Duration (in steps)', + defaultValue: 4.0, + step: 0.5, + type: SongEventFieldType.FLOAT, + }, + { + name: 'ease', + title: 'Easing Type', + defaultValue: 'linear', + type: SongEventFieldType.ENUM, + keys: [ + 'Linear' => 'linear', + 'Instant' => 'INSTANT', + 'Quad In' => 'quadIn', + 'Quad Out' => 'quadOut', + 'Quad In/Out' => 'quadInOut', + 'Cube In' => 'cubeIn', + 'Cube Out' => 'cubeOut', + 'Cube In/Out' => 'cubeInOut', + 'Quart In' => 'quartIn', + 'Quart Out' => 'quartOut', + 'Quart In/Out' => 'quartInOut', + 'Quint In' => 'quintIn', + 'Quint Out' => 'quintOut', + 'Quint In/Out' => 'quintInOut', + 'Smooth Step In' => 'smoothStepIn', + 'Smooth Step Out' => 'smoothStepOut', + 'Smooth Step In/Out' => 'smoothStepInOut', + 'Sine In' => 'sineIn', + 'Sine Out' => 'sineOut', + 'Sine In/Out' => 'sineInOut', + 'Elastic In' => 'elasticIn', + 'Elastic Out' => 'elasticOut', + 'Elastic In/Out' => 'elasticInOut', + ] + } + ]; + } +} diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index a712de1cc..7de005cb0 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -324,7 +324,7 @@ class SongDifficulty /** * Build a list of vocal files for the given character. * Automatically resolves suffixed character IDs (so bf-car will resolve to bf if needed). - * + * * @param id The character we are about to play. */ public function buildVoiceList(?id:String = 'bf'):Array diff --git a/source/funkin/play/song/SongData.hx b/source/funkin/play/song/SongData.hx index 982ccb402..78593c6c0 100644 --- a/source/funkin/play/song/SongData.hx +++ b/source/funkin/play/song/SongData.hx @@ -24,7 +24,7 @@ class SongDataParser /** * Parses and preloads the game's song metadata and scripts when the game starts. - * + * * If you want to force song metadata to be reloaded, you can just call this function again. */ public static function loadSongCache():Void @@ -217,7 +217,7 @@ typedef RawSongMetadata = { /** * A semantic versioning string for the song data format. - * + * */ var version:Version; @@ -414,7 +414,7 @@ abstract SongNoteData(RawSongNoteData) /** * The strumline index of the note, if applicable. * Strips the direction from the data. - * + * * 0 = player, 1 = opponent, etc. */ public inline function getStrumlineIndex(strumlineSize:Int = 4):Int diff --git a/source/funkin/play/song/SongDataUtils.hx b/source/funkin/play/song/SongDataUtils.hx index 27625e846..f75972ee7 100644 --- a/source/funkin/play/song/SongDataUtils.hx +++ b/source/funkin/play/song/SongDataUtils.hx @@ -14,14 +14,13 @@ class SongDataUtils * Given an array of SongNoteData objects, return a new array of SongNoteData objects * whose timestamps are shifted by the given amount. * Does not mutate the original array. - * + * * @param notes The notes to modify. * @param offset The time difference to apply in milliseconds. */ public static function offsetSongNoteData(notes:Array, offset:Int):Array { - return notes.map(function(note:SongNoteData):SongNoteData - { + return notes.map(function(note:SongNoteData):SongNoteData { return new SongNoteData(note.time + offset, note.data, note.length, note.kind); }); } @@ -30,14 +29,13 @@ class SongDataUtils * Given an array of SongEventData objects, return a new array of SongEventData objects * whose timestamps are shifted by the given amount. * Does not mutate the original array. - * + * * @param events The events to modify. * @param offset The time difference to apply in milliseconds. */ public static function offsetSongEventData(events:Array, offset:Int):Array { - return events.map(function(event:SongEventData):SongEventData - { + return events.map(function(event:SongEventData):SongEventData { return new SongEventData(event.time + offset, event.event, event.value); }); } @@ -45,7 +43,7 @@ class SongDataUtils /** * Return a new array without a certain subset of notes from an array of SongNoteData objects. * Does not mutate the original array. - * + * * @param notes The array of notes to be subtracted from. * @param subtrahend The notes to remove from the `notes` array. Yes, subtrahend is a real word. */ @@ -53,8 +51,7 @@ class SongDataUtils { if (notes.length == 0 || subtrahend.length == 0) return notes; - var result = notes.filter(function(note:SongNoteData):Bool - { + var result = notes.filter(function(note:SongNoteData):Bool { for (x in subtrahend) // SongNoteData's == operation has been overridden so that this will work. if (x == note) return false; @@ -68,7 +65,7 @@ class SongDataUtils /** * Return a new array without a certain subset of events from an array of SongEventData objects. * Does not mutate the original array. - * + * * @param events The array of events to be subtracted from. * @param subtrahend The events to remove from the `events` array. Yes, subtrahend is a real word. */ @@ -76,8 +73,7 @@ class SongDataUtils { if (events.length == 0 || subtrahend.length == 0) return events; - return events.filter(function(event:SongEventData):Bool - { + return events.filter(function(event:SongEventData):Bool { // SongEventData's == operation has been overridden so that this will work. return !subtrahend.has(event); }); @@ -89,8 +85,7 @@ class SongDataUtils */ public static function flipNotes(notes:Array, ?strumlineSize:Int = 4):Array { - return notes.map(function(note:SongNoteData):SongNoteData - { + return notes.map(function(note:SongNoteData):SongNoteData { var newData = note.data; if (newData < strumlineSize) newData += strumlineSize; @@ -103,7 +98,7 @@ class SongDataUtils /** * Prepare an array of notes to be used as the clipboard data. - * + * * Offset the provided array of notes such that the first note is at 0 milliseconds. */ public static function buildNoteClipboard(notes:Array):Array @@ -113,7 +108,7 @@ class SongDataUtils /** * Prepare an array of events to be used as the clipboard data. - * + * * Offset the provided array of events such that the first event is at 0 milliseconds. */ public static function buildEventClipboard(events:Array):Array @@ -127,8 +122,7 @@ class SongDataUtils public static function sortNotes(notes:Array, ?desc:Bool = false):Array { // TODO: Modifies the array in place. Is this okay? - notes.sort(function(a:SongNoteData, b:SongNoteData):Int - { + notes.sort(function(a:SongNoteData, b:SongNoteData):Int { return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time); }); return notes; @@ -140,8 +134,7 @@ class SongDataUtils public static function sortEvents(events:Array, ?desc:Bool = false):Array { // TODO: Modifies the array in place. Is this okay? - events.sort(function(a:SongEventData, b:SongEventData):Int - { + events.sort(function(a:SongEventData, b:SongEventData):Int { return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time); }); return events; @@ -192,8 +185,7 @@ class SongDataUtils */ public static function getNotesInTimeRange(notes:Array, start:Float, end:Float):Array { - return notes.filter(function(note:SongNoteData):Bool - { + return notes.filter(function(note:SongNoteData):Bool { return note.time >= start && note.time <= end; }); } @@ -203,8 +195,7 @@ class SongDataUtils */ public static function getEventsInTimeRange(events:Array, start:Float, end:Float):Array { - return events.filter(function(event:SongEventData):Bool - { + return events.filter(function(event:SongEventData):Bool { return event.time >= start && event.time <= end; }); } @@ -214,8 +205,7 @@ class SongDataUtils */ public static function getNotesInDataRange(notes:Array, start:Int, end:Int):Array { - return notes.filter(function(note:SongNoteData):Bool - { + return notes.filter(function(note:SongNoteData):Bool { return note.data >= start && note.data <= end; }); } @@ -225,8 +215,7 @@ class SongDataUtils */ public static function getNotesWithData(notes:Array, data:Array):Array { - return notes.filter(function(note:SongNoteData):Bool - { + return notes.filter(function(note:SongNoteData):Bool { return data.indexOf(note.data) != -1; }); } diff --git a/source/funkin/play/song/SongSerializer.hx b/source/funkin/play/song/SongSerializer.hx index c7933665a..968a7a1f5 100644 --- a/source/funkin/play/song/SongSerializer.hx +++ b/source/funkin/play/song/SongSerializer.hx @@ -50,8 +50,7 @@ class SongSerializer */ public static function importSongChartDataAsync(callback:SongChartData->Void):Void { - browseFileReference(function(fileReference:FileReference) - { + browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; @@ -68,8 +67,7 @@ class SongSerializer */ public static function importSongMetadataAsync(callback:SongMetadata->Void):Void { - browseFileReference(function(fileReference:FileReference) - { + browseFileReference(function(fileReference:FileReference) { var data = fileReference.data.toString(); if (data == null) return; @@ -103,7 +101,7 @@ class SongSerializer /** * Save a SongChartData object as a JSON file to a specified path. * Works great on HTML5 and desktop. - * + * * @param path The file path to save to. */ public static function exportSongChartDataAs(path:String, data:SongChartData) @@ -116,7 +114,7 @@ class SongSerializer /** * Save a SongMetadata object as a JSON file to a specified path. * Works great on HTML5 and desktop. - * + * * @param path The file path to save to. */ public static function exportSongMetadataAs(path:String, data:SongMetadata) @@ -163,19 +161,17 @@ class SongSerializer /** * Browse for a file to read and execute a callback once we have a file reference. * Works great on HTML5 or desktop. - * + * * @param callback The function to call when the file is loaded. */ static function browseFileReference(callback:FileReference->Void) { var file = new FileReference(); - file.addEventListener(Event.SELECT, function(e) - { + file.addEventListener(Event.SELECT, function(e) { var selectedFileRef:FileReference = e.target; trace('Selected file: ' + selectedFileRef.name); - selectedFileRef.addEventListener(Event.COMPLETE, function(e) - { + selectedFileRef.addEventListener(Event.COMPLETE, function(e) { var loadedFileRef:FileReference = e.target; trace('Loaded file: ' + loadedFileRef.name); callback(loadedFileRef); @@ -192,16 +188,13 @@ class SongSerializer static function writeFileReference(path:String, data:String) { var file = new FileReference(); - file.addEventListener(Event.COMPLETE, function(e:Event) - { + file.addEventListener(Event.COMPLETE, function(e:Event) { trace('Successfully wrote file.'); }); - file.addEventListener(Event.CANCEL, function(e:Event) - { + file.addEventListener(Event.CANCEL, function(e:Event) { trace('Cancelled writing file.'); }); - file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) - { + file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) { trace('IO error writing file.'); }); file.save(data, path); diff --git a/source/funkin/play/song/SongValidator.hx b/source/funkin/play/song/SongValidator.hx index 950113bcf..d393c11eb 100644 --- a/source/funkin/play/song/SongValidator.hx +++ b/source/funkin/play/song/SongValidator.hx @@ -30,7 +30,7 @@ class SongValidator /** * Validates the fields of a SongMetadata object (excluding the version field). - * + * * @param input The SongMetadata object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated SongMetadata object. @@ -73,7 +73,7 @@ class SongValidator /** * Validates the fields of a SongPlayData object. - * + * * @param input The SongPlayData object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated SongPlayData object. @@ -85,7 +85,7 @@ class SongValidator /** * Validates the fields of a TimeChange object. - * + * * @param input The TimeChange object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated TimeChange object. @@ -113,7 +113,7 @@ class SongValidator /** * Validates the fields of a SongChartData object (excluding the version field). - * + * * @param input The SongChartData object to validate. * @param songId The ID of the song being validated. Only used for error messages. * @return The validated SongChartData object. diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx index 9b5d1f314..26c3a0ff2 100644 --- a/source/funkin/play/stage/Bopper.hx +++ b/source/funkin/play/stage/Bopper.hx @@ -26,7 +26,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass * Whether the bopper should dance left and right. * - If true, alternate playing `danceLeft` and `danceRight`. * - If false, play `idle` every time. - * + * * You can manually set this value, or you can leave it as `null` to determine it automatically. */ public var shouldAlternate:Null = null; @@ -139,7 +139,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass * @param name The name of the current animation. * @param frameNumber The number of the current frame. * @param frameIndex The index of the current frame. - * + * * For example, if an animation was defined as having the indexes [3, 0, 1, 2], * then the first callback would have frameNumber = 0 and frameIndex = 3. */ @@ -223,7 +223,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass /** * Ensure that a given animation exists before playing it. * Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play. - * @param name + * @param name */ function correctAnimationName(name:String):String { diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx index 71ba522c0..36d1600a6 100644 --- a/source/funkin/play/stage/Stage.hx +++ b/source/funkin/play/stage/Stage.hx @@ -19,7 +19,7 @@ typedef StagePropGroup = FlxTypedSpriteGroup; /** * A Stage is a group of objects rendered in the PlayState. - * + * * A Stage is comprised of one or more props, each of which is a FlxSprite. */ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass @@ -39,8 +39,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass * The Stage elements get initialized at the beginning of the game. * They're used to cache the data needed to build the stage, * then accessed and fleshed out when the stage needs to be built. - * - * @param stageId + * + * @param stageId */ public function new(stageId:String) { @@ -543,7 +543,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass /** * Retrieve a list of all the asset paths required to load the stage. * Override this in a scripted class to ensure that all necessary assets are loaded! - * + * * @return An array of file names. */ public function fetchAssetPaths():Array diff --git a/source/funkin/play/stage/StageData.hx b/source/funkin/play/stage/StageData.hx index 002606c6d..6164c3cde 100644 --- a/source/funkin/play/stage/StageData.hx +++ b/source/funkin/play/stage/StageData.hx @@ -31,7 +31,7 @@ class StageDataParser /** * Parses and preloads the game's stage data and scripts when the game starts. - * + * * If you want to force stages to be reloaded, you can just call this function again. */ public static function loadStageCache():Void @@ -68,8 +68,7 @@ class StageDataParser // UNSCRIPTED STAGES // var stageIdList:Array = DataAssets.listDataFilesInPath('stages/'); - var unscriptedStageIds:Array = stageIdList.filter(function(stageId:String):Bool - { + var unscriptedStageIds:Array = stageIdList.filter(function(stageId:String):Bool { return !stageCache.exists(stageId); }); trace(' Instantiating ${unscriptedStageIds.length} non-scripted stages...'); @@ -126,7 +125,7 @@ class StageDataParser /** * Load a stage's JSON file, parse its data, and return it. - * + * * @param stageId The stage to load. * @return The stage data, or null if validation failed. */ @@ -199,8 +198,8 @@ class StageDataParser /** * Set unspecified parameters to their defaults. * If the parameter is mandatory, print an error message. - * @param id - * @param input + * @param id + * @param input * @return The validated stage data */ static function validateStageData(id:String, input:StageData):Null @@ -461,7 +460,7 @@ typedef StageDataProp = * If not zero, this prop will play an animation every X beats of the song. * This requires animations to be defined. If `danceLeft` and `danceRight` are defined, * they will alternated between, otherwise the `idle` animation will be used. - * + * * @default 0 */ var danceEvery:Null; diff --git a/source/funkin/play/stage/StageProp.hx b/source/funkin/play/stage/StageProp.hx index 58de55293..4f67c5e4b 100644 --- a/source/funkin/play/stage/StageProp.hx +++ b/source/funkin/play/stage/StageProp.hx @@ -18,7 +18,7 @@ class StageProp extends FlxSprite implements IStateStageProp /** * Called when this prop is added to the stage. - * @param event + * @param event */ public function onAdd(event:ScriptEvent):Void {} diff --git a/source/funkin/shaderslmfao/AngleMask.hx b/source/funkin/shaderslmfao/AngleMask.hx index 0fe279ad6..b9188201b 100644 --- a/source/funkin/shaderslmfao/AngleMask.hx +++ b/source/funkin/shaderslmfao/AngleMask.hx @@ -13,7 +13,7 @@ class AngleMask extends FlxShader vec2 uv = openfl_TextureCoordv.xy; - + vec2 start = vec2(0.0, 0.0); vec2 end = vec2(endPosition.x / openfl_TextureSize.x, 1.0); diff --git a/source/funkin/shaderslmfao/ColorSwap.hx b/source/funkin/shaderslmfao/ColorSwap.hx index 684e48239..2c1f5664b 100644 --- a/source/funkin/shaderslmfao/ColorSwap.hx +++ b/source/funkin/shaderslmfao/ColorSwap.hx @@ -66,8 +66,8 @@ class ColorSwapShader extends FlxShader const float offset = 1.0 / 128.0; - - + + vec3 normalizeColor(vec3 color) { @@ -101,7 +101,7 @@ class ColorSwapShader extends FlxShader vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv); vec4 swagColor = vec4(rgb2hsv(vec3(color[0], color[1], color[2])), color[3]); - + // [0] is the hue??? swagColor[0] += uTime; // swagColor[1] += uTime; @@ -109,7 +109,7 @@ class ColorSwapShader extends FlxShader // money += swagColor[0]; color = vec4(hsv2rgb(vec3(swagColor[0], swagColor[1], swagColor[2])), swagColor[3]); - + if (awesomeOutline) { @@ -119,7 +119,7 @@ class ColorSwapShader extends FlxShader if (color.a <= 0.5) { float w = size.x / openfl_TextureSize.x; float h = size.y / openfl_TextureSize.y; - + if (flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x + w, openfl_TextureCoordv.y)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y + h)).a != 0. @@ -130,12 +130,12 @@ class ColorSwapShader extends FlxShader } - - + + gl_FragColor = color; - - - /* + + + /* if (color.a > 0.5) gl_FragColor = color; else diff --git a/source/funkin/shaderslmfao/OverlayBlend.hx b/source/funkin/shaderslmfao/OverlayBlend.hx index 14a840451..8845a3b55 100644 --- a/source/funkin/shaderslmfao/OverlayBlend.hx +++ b/source/funkin/shaderslmfao/OverlayBlend.hx @@ -47,7 +47,7 @@ class OverlayBlend extends FlxShader { vec2 funnyUv = openfl_TextureCoordv; vec4 color = flixel_texture2D(bitmap, funnyUv); - + vec2 reallyFunnyUv = vec2(vec2(0.0, 0.0) - gl_FragCoord.xy / openfl_TextureSize.xy); vec4 gf = flixel_texture2D(funnyShit, openfl_TextureCoordv.xy + vec2(0.1, 0.2)); diff --git a/source/funkin/shaderslmfao/ScreenWipeShader.hx b/source/funkin/shaderslmfao/ScreenWipeShader.hx index 2ba035dc0..1aeb069ba 100644 --- a/source/funkin/shaderslmfao/ScreenWipeShader.hx +++ b/source/funkin/shaderslmfao/ScreenWipeShader.hx @@ -45,7 +45,7 @@ class ScreenWipeShader extends FlxShader { vec2 funnyUv = openfl_TextureCoordv; vec4 color = flixel_texture2D(bitmap, funnyUv); - + vec2 reallyFunnyUv = vec2(vec2(0.0, 0.0) - gl_FragCoord.xy / openfl_TextureSize.xy); vec4 gf = flixel_texture2D(funnyShit, openfl_TextureCoordv); diff --git a/source/funkin/shaderslmfao/StrokeShader.hx b/source/funkin/shaderslmfao/StrokeShader.hx index d09b131a7..38dc41636 100644 --- a/source/funkin/shaderslmfao/StrokeShader.hx +++ b/source/funkin/shaderslmfao/StrokeShader.hx @@ -44,7 +44,7 @@ class StrokeShader extends FlxShader if (sample.a == 0.) { float w = size.x / openfl_TextureSize.x; float h = size.y / openfl_TextureSize.y; - + if (flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x + w, openfl_TextureCoordv.y)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y + h)).a != 0. diff --git a/source/funkin/shaderslmfao/TitleOutline.hx b/source/funkin/shaderslmfao/TitleOutline.hx index c10fce8c3..9a849f795 100644 --- a/source/funkin/shaderslmfao/TitleOutline.hx +++ b/source/funkin/shaderslmfao/TitleOutline.hx @@ -50,7 +50,7 @@ class TitleOutline extends FlxShader if (color.a == 0.0) { float w = size.x / openfl_TextureSize.x; float h = size.y / openfl_TextureSize.y; - + vec4 colorOffset = flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y - h)); @@ -59,7 +59,7 @@ class TitleOutline extends FlxShader if (hsvShit.b <= 0.1 && colorOffset.a != 0.) color = vec4(0.0, 1.0, 0.8, color.a); } - + gl_FragColor = color; } diff --git a/source/funkin/ui/AtlasText.hx b/source/funkin/ui/AtlasText.hx index 857587a7b..c311e387a 100644 --- a/source/funkin/ui/AtlasText.hx +++ b/source/funkin/ui/AtlasText.hx @@ -86,7 +86,7 @@ class AtlasText extends FlxTypedSpriteGroup /** * Converts all characters to fit the font's `allowedCase`. - * @param text + * @param text */ function restrictCase(text:String) { diff --git a/source/funkin/ui/ControlsMenu.hx b/source/funkin/ui/ControlsMenu.hx index c9292d39e..bd81cae5c 100644 --- a/source/funkin/ui/ControlsMenu.hx +++ b/source/funkin/ui/ControlsMenu.hx @@ -121,8 +121,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page var margin = 100; menuCamera.deadzone.set(0, margin, menuCamera.width, menuCamera.height - margin * 2); menuCamera.minScrollY = 0; - controlGrid.onChange.add(function(selected) - { + controlGrid.onChange.add(function(selected) { camFollow.y = selected.y; labels.forEach((label) -> label.alpha = 0.6); diff --git a/source/funkin/ui/MenuList.hx b/source/funkin/ui/MenuList.hx index 61d44b960..39b53f998 100644 --- a/source/funkin/ui/MenuList.hx +++ b/source/funkin/ui/MenuList.hx @@ -121,9 +121,9 @@ class MenuTypedList extends FlxTypedGroup /** * Controls navigation on a linear list of items such as Vertical. - * @param prev - * @param next - * @param allowWrap + * @param prev + * @param next + * @param allowWrap */ inline function navList(prev:Bool, next:Bool, allowWrap:Bool) { @@ -164,8 +164,7 @@ class MenuTypedList extends FlxTypedGroup { busy = true; FlxG.sound.play(Paths.sound('confirmMenu')); - FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_) - { + FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_) { busy = false; selected.callback(); }); diff --git a/source/funkin/ui/NgPrompt.hx b/source/funkin/ui/NgPrompt.hx index 9cb153fab..9a2a62940 100644 --- a/source/funkin/ui/NgPrompt.hx +++ b/source/funkin/ui/NgPrompt.hx @@ -36,14 +36,12 @@ class NgPrompt extends Prompt #if web prompt.buttons.getItem("yes").fireInstantly = true; #end - prompt.onYes = function() - { + prompt.onYes = function() { prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end); prompt.setButtons(None); openPassportUrl(); }; - prompt.onNo = function() - { + prompt.onNo = function() { prompt.close(); prompt = null; NGio.cancelLogin(); @@ -92,8 +90,7 @@ class NgPrompt extends Prompt { var user = io.newgrounds.NG.core.user.name; var prompt = new NgPrompt('Log out of $user?', Yes_No); - prompt.onYes = function() - { + prompt.onYes = function() { NGio.logout(); prompt.close(); }; diff --git a/source/funkin/ui/OptionsState.hx b/source/funkin/ui/OptionsState.hx index 2b93e7222..291dafdf1 100644 --- a/source/funkin/ui/OptionsState.hx +++ b/source/funkin/ui/OptionsState.hx @@ -145,8 +145,7 @@ class Page extends FlxGroup function openPrompt(prompt:Prompt, onClose:Void->Void) { enabled = false; - prompt.closeCallback = function() - { + prompt.closeCallback = function() { enabled = true; if (onClose != null) onClose(); } @@ -217,16 +216,15 @@ class OptionsMenu extends Page /** * Calls openPrompt and redraws the login/logout button - * @param prompt - * @param onClose + * @param prompt + * @param onClose */ public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void) { var onPromptClose = checkLoginStatus; if (onClose != null) { - onPromptClose = function() - { + onPromptClose = function() { checkLoginStatus(); onClose(); } diff --git a/source/funkin/ui/PopUpStuff.hx b/source/funkin/ui/PopUpStuff.hx index 4bcdde138..c5828dd0a 100644 --- a/source/funkin/ui/PopUpStuff.hx +++ b/source/funkin/ui/PopUpStuff.hx @@ -55,8 +55,7 @@ class PopUpStuff extends FlxTypedGroup FlxTween.tween(rating, {alpha: 0}, 0.2, { - onComplete: function(tween:FlxTween) - { + onComplete: function(tween:FlxTween) { remove(rating, true); rating.destroy(); }, @@ -106,8 +105,7 @@ class PopUpStuff extends FlxTypedGroup FlxTween.tween(comboSpr, {alpha: 0}, 0.2, { - onComplete: function(tween:FlxTween) - { + onComplete: function(tween:FlxTween) { remove(comboSpr, true); comboSpr.destroy(); }, @@ -153,8 +151,7 @@ class PopUpStuff extends FlxTypedGroup FlxTween.tween(numScore, {alpha: 0}, 0.2, { - onComplete: function(tween:FlxTween) - { + onComplete: function(tween:FlxTween) { remove(numScore, true); numScore.destroy(); }, diff --git a/source/funkin/ui/PreferencesMenu.hx b/source/funkin/ui/PreferencesMenu.hx index 1b6a6c6fd..0bf83c125 100644 --- a/source/funkin/ui/PreferencesMenu.hx +++ b/source/funkin/ui/PreferencesMenu.hx @@ -43,8 +43,7 @@ class PreferencesMenu extends Page menuCamera.deadzone.set(0, margin, menuCamera.width, 40); menuCamera.minScrollY = 0; - items.onChange.add(function(selected) - { + items.onChange.add(function(selected) { camFollow.y = selected.y; }); } @@ -82,8 +81,7 @@ class PreferencesMenu extends Page function createPrefItem(prefName:String, prefString:String, prefValue:Dynamic):Void { - items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() - { + items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() { preferenceCheck(prefString, prefValue); switch (Type.typeof(prefValue).getName()) @@ -145,8 +143,7 @@ class PreferencesMenu extends Page // menuCamera.followLerp = CoolUtil.camLerpShit(0.05); - items.forEach(function(daItem:TextMenuItem) - { + items.forEach(function(daItem:TextMenuItem) { if (items.selectedItem == daItem) daItem.x = 150; else daItem.x = 120; diff --git a/source/funkin/ui/StickerSubState.hx b/source/funkin/ui/StickerSubState.hx index 9658275e9..981c79dfa 100644 --- a/source/funkin/ui/StickerSubState.hx +++ b/source/funkin/ui/StickerSubState.hx @@ -17,7 +17,7 @@ import openfl.geom.Matrix; import openfl.display.Sprite; import openfl.display.Bitmap; -class StickerSubState extends MusicBeatSubstate +class StickerSubState extends MusicBeatSubState { public var grpStickers:FlxTypedGroup; @@ -36,7 +36,8 @@ class StickerSubState extends MusicBeatSubstate add(grpStickers); // makes the stickers on the most recent camera, which is more often than not... a UI camera!! - grpStickers.cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]]; + // grpStickers.cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]]; + grpStickers.cameras = FlxG.cameras.list; if (oldStickers != null) { @@ -208,8 +209,10 @@ class StickerSubState extends MusicBeatSubstate FlxG.switchState(new FreeplayState(this)); case STORY: FlxG.switchState(new StoryMenuState(this)); + case MAIN_MENU: + FlxG.switchState(new MainMenuState()); default: - FlxG.switchState(new FreeplayState(this)); + FlxG.switchState(new MainMenuState()); } } @@ -354,6 +357,7 @@ typedef StickerShit = enum abstract NEXTSTATE(String) { + var MAIN_MENU = 'mainmenu'; var FREEPLAY = 'freeplay'; var STORY = 'story'; } diff --git a/source/funkin/ui/animDebugShit/DebugBoundingState.hx b/source/funkin/ui/animDebugShit/DebugBoundingState.hx index a21384e76..da7a4e3ff 100644 --- a/source/funkin/ui/animDebugShit/DebugBoundingState.hx +++ b/source/funkin/ui/animDebugShit/DebugBoundingState.hx @@ -44,7 +44,7 @@ import sys.io.File; class DebugBoundingState extends FlxState { - /* + /* TODAY'S TO-DO - Cleaner UI */ diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx index 5d00c03b8..9f18acd35 100644 --- a/source/funkin/ui/debug/DebugMenuSubState.hx +++ b/source/funkin/ui/debug/DebugMenuSubState.hx @@ -2,11 +2,11 @@ package funkin.ui.debug; import flixel.FlxObject; import flixel.FlxSprite; -import funkin.MusicBeatSubstate; +import funkin.MusicBeatSubState; import funkin.ui.TextMenuList; import funkin.ui.debug.charting.ChartEditorState; -class DebugMenuSubState extends MusicBeatSubstate +class DebugMenuSubState extends MusicBeatSubState { var items:TextMenuList; diff --git a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx index 4240773e4..625119343 100644 --- a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx @@ -552,7 +552,7 @@ class ChartEditorDialogHandler /** * Builds and opens a dialog displaying the user guide, providing guidance and help on how to use the chart editor. - * + * * @param state The current chart editor state. * @return The dialog that was opened. */ diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 4e050b19b..c61637536 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -58,7 +58,7 @@ using Lambda; * Built with HaxeUI for use by both developers and modders. * * Some functionality is moved to other classes to help maintain my sanity. - * + * * @author MasterEric */ // Give other classes access to private instance fields @@ -602,7 +602,7 @@ class ChartEditorState extends HaxeUIState * A map of the audio tracks for each character's vocals. * - Keys are the character IDs. * - Values are the FlxSound objects to play that character's vocals. - * + * * When switching characters, the elements of the VoicesGroup will be swapped to match the new character. */ var audioVocalTracks:Map = new Map(); @@ -2945,7 +2945,7 @@ class ChartEditorState extends HaxeUIState /** * Loads an instrumental from an absolute file path, replacing the current instrumental. - * + * * @param path The absolute path to the audio file. * @return Success or failure. */ @@ -3132,7 +3132,7 @@ class ChartEditorState extends HaxeUIState /** * Perform (or redo) a command, then add it to the undo stack. - * + * * @param command The command to perform. * @param purgeRedoStack If true, the redo stack will be cleared. */ diff --git a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx index 8e21b3aeb..5a903481e 100644 --- a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx @@ -3,6 +3,7 @@ package funkin.ui.debug.charting; import haxe.ui.data.ArrayDataSource; import funkin.play.character.BaseCharacter.CharacterType; import funkin.play.event.SongEvent; +import funkin.play.event.SongEventData; import funkin.play.song.SongData.SongTimeChange; import funkin.play.song.SongSerializer; import funkin.ui.haxeui.components.CharacterPlayer; @@ -133,8 +134,7 @@ class ChartEditorToolboxHandler toolbox.x = 50; toolbox.y = 50; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxTools', false); } @@ -142,8 +142,7 @@ class ChartEditorToolboxHandler if (toolsGroup == null) return null; - toolsGroup.onChange = (event:UIEvent) -> - { + toolsGroup.onChange = (event:UIEvent) -> { switch (event.target.id) { case 'toolboxToolsGroupSelect': @@ -168,8 +167,7 @@ class ChartEditorToolboxHandler toolbox.x = 75; toolbox.y = 100; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxNotes', false); } @@ -177,8 +175,7 @@ class ChartEditorToolboxHandler var toolboxNotesCustomKindLabel:Label = toolbox.findComponent("toolboxNotesCustomKindLabel", Label); var toolboxNotesCustomKind:TextField = toolbox.findComponent("toolboxNotesCustomKind", TextField); - toolboxNotesNoteKind.onChange = (event:UIEvent) -> - { + toolboxNotesNoteKind.onChange = (event:UIEvent) -> { var isCustom = (event.data.id == '~CUSTOM~'); if (isCustom) @@ -197,8 +194,7 @@ class ChartEditorToolboxHandler } } - toolboxNotesCustomKind.onChange = (event:UIEvent) -> - { + toolboxNotesCustomKind.onChange = (event:UIEvent) -> { state.selectedNoteKind = toolboxNotesCustomKind.text; } @@ -215,8 +211,7 @@ class ChartEditorToolboxHandler toolbox.x = 100; toolbox.y = 150; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxEvents', false); } @@ -232,8 +227,7 @@ class ChartEditorToolboxHandler toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id}); } - toolboxEventsEventKind.onChange = (event:UIEvent) -> - { + toolboxEventsEventKind.onChange = (event:UIEvent) -> { var eventType:String = event.data.value; trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType'); @@ -320,8 +314,7 @@ class ChartEditorToolboxHandler target.addComponent(input); - input.onChange = (event:UIEvent) -> - { + input.onChange = (event:UIEvent) -> { trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${event.target.value}'); if (event.target.value == null) state.selectedEventData.remove(event.target.id); @@ -341,8 +334,7 @@ class ChartEditorToolboxHandler toolbox.x = 125; toolbox.y = 200; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxDifficulty', false); } @@ -352,35 +344,28 @@ class ChartEditorToolboxHandler var difficultyToolboxLoadMetadata:Button = toolbox.findComponent("difficultyToolboxLoadMetadata", Button); var difficultyToolboxLoadChart:Button = toolbox.findComponent("difficultyToolboxLoadChart", Button); - difficultyToolboxSaveMetadata.onClick = (event:UIEvent) -> - { + difficultyToolboxSaveMetadata.onClick = (event:UIEvent) -> { SongSerializer.exportSongMetadata(state.currentSongMetadata); }; - difficultyToolboxSaveChart.onClick = (event:UIEvent) -> - { + difficultyToolboxSaveChart.onClick = (event:UIEvent) -> { SongSerializer.exportSongChartData(state.currentSongChartData); }; - difficultyToolboxSaveAll.onClick = (event:UIEvent) -> - { + difficultyToolboxSaveAll.onClick = (event:UIEvent) -> { state.exportAllSongData(); }; - difficultyToolboxLoadMetadata.onClick = (event:UIEvent) -> - { + difficultyToolboxLoadMetadata.onClick = (event:UIEvent) -> { // Replace metadata for current variation. - SongSerializer.importSongMetadataAsync(function(songMetadata) - { + SongSerializer.importSongMetadataAsync(function(songMetadata) { state.currentSongMetadata = songMetadata; }); }; - difficultyToolboxLoadChart.onClick = (event:UIEvent) -> - { + difficultyToolboxLoadChart.onClick = (event:UIEvent) -> { // Replace chart data for current variation. - SongSerializer.importSongChartDataAsync(function(songChartData) - { + SongSerializer.importSongChartDataAsync(function(songChartData) { state.currentSongChartData = songChartData; state.noteDisplayDirty = true; }); @@ -401,14 +386,12 @@ class ChartEditorToolboxHandler toolbox.x = 150; toolbox.y = 250; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxMetadata', false); } var inputSongName:TextField = toolbox.findComponent('inputSongName', TextField); - inputSongName.onChange = (event:UIEvent) -> - { + inputSongName.onChange = (event:UIEvent) -> { var valid = event.target.text != null && event.target.text != ""; if (valid) @@ -423,8 +406,7 @@ class ChartEditorToolboxHandler }; var inputSongArtist:TextField = toolbox.findComponent('inputSongArtist', TextField); - inputSongArtist.onChange = (event:UIEvent) -> - { + inputSongArtist.onChange = (event:UIEvent) -> { var valid = event.target.text != null && event.target.text != ""; if (valid) @@ -439,8 +421,7 @@ class ChartEditorToolboxHandler }; var inputStage:DropDown = toolbox.findComponent('inputStage', DropDown); - inputStage.onChange = (event:UIEvent) -> - { + inputStage.onChange = (event:UIEvent) -> { var valid = event.data != null && event.data.id != null; if (valid) @@ -450,15 +431,13 @@ class ChartEditorToolboxHandler }; var inputNoteSkin:DropDown = toolbox.findComponent('inputNoteSkin', DropDown); - inputNoteSkin.onChange = (event:UIEvent) -> - { + inputNoteSkin.onChange = (event:UIEvent) -> { if (event.data.id == null) return; state.currentSongMetadata.playData.noteSkin = event.data.id; }; var inputBPM:NumberStepper = toolbox.findComponent('inputBPM', NumberStepper); - inputBPM.onChange = (event:UIEvent) -> - { + inputBPM.onChange = (event:UIEvent) -> { if (event.value == null || event.value <= 0) return; var timeChanges = state.currentSongMetadata.timeChanges; @@ -477,8 +456,7 @@ class ChartEditorToolboxHandler }; var inputScrollSpeed:Slider = toolbox.findComponent('inputScrollSpeed', Slider); - inputScrollSpeed.onChange = (event:UIEvent) -> - { + inputScrollSpeed.onChange = (event:UIEvent) -> { var valid = event.target.value != null && event.target.value > 0; if (valid) @@ -505,8 +483,7 @@ class ChartEditorToolboxHandler toolbox.x = 175; toolbox.y = 300; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxCharacters', false); } @@ -523,8 +500,7 @@ class ChartEditorToolboxHandler toolbox.x = 200; toolbox.y = 350; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxPlayerPreview', false); } @@ -548,8 +524,7 @@ class ChartEditorToolboxHandler toolbox.x = 200; toolbox.y = 350; - toolbox.onDialogClosed = (event:DialogEvent) -> - { + toolbox.onDialogClosed = (event:DialogEvent) -> { state.setUICheckboxSelected('menubarItemToggleToolboxOpponentPreview', false); } diff --git a/source/funkin/ui/haxeui/HaxeUISubState.hx b/source/funkin/ui/haxeui/HaxeUISubState.hx index 963223619..ba218f37c 100644 --- a/source/funkin/ui/haxeui/HaxeUISubState.hx +++ b/source/funkin/ui/haxeui/HaxeUISubState.hx @@ -7,7 +7,7 @@ import haxe.ui.core.Component; import haxe.ui.events.MouseEvent; import haxe.ui.events.UIEvent; -class HaxeUISubState extends MusicBeatSubstate +class HaxeUISubState extends MusicBeatSubState { // The component representing the main UI. public var component:Component; diff --git a/source/funkin/ui/haxeui/components/CharacterPlayer.hx b/source/funkin/ui/haxeui/components/CharacterPlayer.hx index c45aac35e..c6beca123 100644 --- a/source/funkin/ui/haxeui/components/CharacterPlayer.hx +++ b/source/funkin/ui/haxeui/components/CharacterPlayer.hx @@ -119,14 +119,12 @@ class CharacterPlayer extends Box character.scale.x *= _scale; character.scale.y *= _scale; - character.animation.callback = function(name:String = "", frameNumber:Int = -1, frameIndex:Int = -1) - { + character.animation.callback = function(name:String = "", frameNumber:Int = -1, frameIndex:Int = -1) { @:privateAccess character.onAnimationFrame(name, frameNumber, frameIndex); dispatch(new AnimationEvent(AnimationEvent.FRAME)); }; - character.animation.finishCallback = function(name:String = "") - { + character.animation.finishCallback = function(name:String = "") { @:privateAccess character.onAnimationFinished(name); dispatch(new AnimationEvent(AnimationEvent.END)); diff --git a/source/funkin/ui/haxeui/components/Notifbar.hx b/source/funkin/ui/haxeui/components/Notifbar.hx index bd08b4965..91ba16077 100644 --- a/source/funkin/ui/haxeui/components/Notifbar.hx +++ b/source/funkin/ui/haxeui/components/Notifbar.hx @@ -44,8 +44,7 @@ class Notifbar extends SideBar this.action.text = actionText; this.action.visible = true; this.action.disabled = false; - this.action.onClick = (_) -> - { + this.action.onClick = (_) -> { actionCallback(); }; } @@ -98,8 +97,7 @@ class Notifbar extends SideBar action = outerContainer.findComponent('notifbarAction', Button); dismiss = outerContainer.findComponent('notifbarDismiss', Button); - dismiss.onClick = (_) -> - { + dismiss.onClick = (_) -> { dismissNotification(); }; } diff --git a/source/funkin/ui/stageBuildShit/SprStage.hx b/source/funkin/ui/stageBuildShit/SprStage.hx index 681e6f10f..2229adfe9 100644 --- a/source/funkin/ui/stageBuildShit/SprStage.hx +++ b/source/funkin/ui/stageBuildShit/SprStage.hx @@ -18,11 +18,9 @@ class SprStage extends FlxSprite { super(x, y); - FlxMouseEvent.add(this, dragShitFunc, null, function(spr:SprStage) - { + FlxMouseEvent.add(this, dragShitFunc, null, function(spr:SprStage) { if (isSelected() || StageBuilderState.curTool == SELECT) alpha = 0.5; - }, function(spr:SprStage) - { + }, function(spr:SprStage) { alpha = 1; }, false, true, true); } diff --git a/source/funkin/ui/stageBuildShit/StageBuilderState.hx b/source/funkin/ui/stageBuildShit/StageBuilderState.hx index ad40a8657..de874e5ff 100644 --- a/source/funkin/ui/stageBuildShit/StageBuilderState.hx +++ b/source/funkin/ui/stageBuildShit/StageBuilderState.hx @@ -94,8 +94,7 @@ class StageBuilderState extends MusicBeatState hudGrp.add(saveSceneBtn); #if desktop - FlxG.stage.window.onDropFile.add(function(path:String) - { + FlxG.stage.window.onDropFile.add(function(path:String) { trace("DROPPED FILE FROM: " + Std.string(path)); var fileName:String = path.split('\\').pop(); @@ -113,8 +112,7 @@ class StageBuilderState extends MusicBeatState fo.write(sys.io.File.getBytes(path)); - new FlxTimer().start(0.2, function(tmr) - { + new FlxTimer().start(0.2, function(tmr) { var awesomeImg:SprStage = new SprStage(FlxG.mouse.x, FlxG.mouse.y, sprDragShitFunc); awesomeImg.loadGraphic(Paths.image('stageBuild/stageTempImg'), false, 0, 0, true); diff --git a/source/funkin/ui/stageBuildShit/StageEditorCommand.hx b/source/funkin/ui/stageBuildShit/StageEditorCommand.hx index e6ce2777b..3248d16d8 100644 --- a/source/funkin/ui/stageBuildShit/StageEditorCommand.hx +++ b/source/funkin/ui/stageBuildShit/StageEditorCommand.hx @@ -1,5 +1,6 @@ package funkin.ui.stageBuildShit; +import funkin.ui.stageBuildShit.StageOffsetSubState; import flixel.FlxSprite; /** @@ -9,8 +10,8 @@ import flixel.FlxSprite; */ interface StageEditorCommand { - public function execute(state:StageOffsetSubstate):Void; - public function undo(state:StageOffsetSubstate):Void; + public function execute(state:StageOffsetSubState):Void; + public function undo(state:StageOffsetSubState):Void; public function toString():String; } @@ -27,7 +28,7 @@ class MovePropCommand implements StageEditorCommand this.realMove = realMove; } - public function execute(state:StageOffsetSubstate):Void + public function execute(state:StageOffsetSubState):Void { if (realMove) { @@ -36,7 +37,7 @@ class MovePropCommand implements StageEditorCommand } } - public function undo(state:StageOffsetSubstate):Void + public function undo(state:StageOffsetSubState):Void { state.char.x -= xDiff; state.char.y -= yDiff; @@ -58,13 +59,13 @@ class SelectPropCommand implements StageEditorCommand this.prop = prop; } - public function execute(state:StageOffsetSubstate):Void + public function execute(state:StageOffsetSubState):Void { this.prevProp = state.char; state.char = prop; } - public function undo(state:StageOffsetSubstate):Void + public function undo(state:StageOffsetSubState):Void { var funnyShader = state.char.shader; if (state.char != null) state.char.shader = null; diff --git a/source/funkin/ui/stageBuildShit/StageOffsetSubstate.hx b/source/funkin/ui/stageBuildShit/StageOffsetSubState.hx similarity index 99% rename from source/funkin/ui/stageBuildShit/StageOffsetSubstate.hx rename to source/funkin/ui/stageBuildShit/StageOffsetSubState.hx index f6fd261c8..dff0cca6b 100644 --- a/source/funkin/ui/stageBuildShit/StageOffsetSubstate.hx +++ b/source/funkin/ui/stageBuildShit/StageOffsetSubState.hx @@ -23,12 +23,12 @@ import openfl.net.FileReference; * Built with HaxeUI for use by both developers and modders. * * All functionality is kept within this file to ruin my own sanity. - * + * * @author ninjamuffin99 */ // Give other classes access to private instance fields @:allow(funkin.ui.stageBuildShit.StageEditorCommand) -class StageOffsetSubstate extends HaxeUISubState +class StageOffsetSubState extends HaxeUISubState { var uiStuff:Component; @@ -260,7 +260,7 @@ class StageOffsetSubstate extends HaxeUISubState // if (uiStuff != null) remove(uiStuff); // uiStuff = null; - PlayState.disableKeys = false; + PlayState.instance.disableKeys = false; PlayState.instance.resetCamera(); FlxG.mouse.visible = false; close(); diff --git a/source/funkin/ui/story/Level.hx b/source/funkin/ui/story/Level.hx index 5d24de312..3ff0a5321 100644 --- a/source/funkin/ui/story/Level.hx +++ b/source/funkin/ui/story/Level.hx @@ -43,7 +43,8 @@ class Level implements IRegistryEntry */ public function getSongs():Array { - return _data.songs; + // Copy the array so that it can't be modified on accident + return _data.songs.copy(); } /** diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 8a856baf6..b61f1bdee 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -13,6 +13,8 @@ import funkin.data.level.LevelRegistry; import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEventDispatcher; import funkin.play.PlayState; +import funkin.play.PlayStatePlaylist; +import funkin.play.song.Song; import funkin.play.song.SongData.SongDataParser; import funkin.util.Constants; @@ -112,8 +114,8 @@ class StoryMenuState extends MusicBeatState { FlxG.sound.playMusic(Paths.music('freakyMenu')); FlxG.sound.music.fadeIn(4, 0, 0.7); - Conductor.forceBPM(Constants.FREAKY_MENU_BPM); } + Conductor.forceBPM(Constants.FREAKY_MENU_BPM); if (stickerSubState != null) { @@ -390,7 +392,7 @@ class StoryMenuState extends MusicBeatState /** * Changes the selected difficulty. - * @param change +1 (right) to increase difficulty, -1 (left) to decrease difficulty + * @param change +1 (right) to increase difficulty, -1 (left) to decrease difficulty */ function changeDifficulty(change:Int = 0):Void { @@ -474,24 +476,25 @@ class StoryMenuState extends MusicBeatState prop.playConfirm(); } - PlayState.storyPlaylist = currentLevel.getSongs(); - PlayState.isStoryMode = true; + Paths.setCurrentLevel(currentLevel.id); - PlayState.currentSong = SongLoad.loadFromJson(PlayState.storyPlaylist[0].toLowerCase(), PlayState.storyPlaylist[0].toLowerCase()); - PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.storyPlaylist[0].toLowerCase()); + PlayStatePlaylist.playlistSongIds = currentLevel.getSongs(); + PlayStatePlaylist.isStoryMode = true; + PlayStatePlaylist.campaignScore = 0; - // TODO: Fix this. - PlayState.storyWeek = 0; - PlayState.campaignScore = 0; + var targetSongId:String = PlayStatePlaylist.playlistSongIds.shift(); - // TODO: Fix this. - PlayState.storyDifficulty = 0; - PlayState.storyDifficulty_NEW = currentDifficultyId; + var targetSong:Song = SongDataParser.fetchSong(targetSongId); - SongLoad.curDiff = PlayState.storyDifficulty_NEW; + PlayStatePlaylist.campaignId = currentLevel.id; + PlayStatePlaylist.campaignTitle = currentLevel.getTitle(); new FlxTimer().start(1, function(tmr:FlxTimer) { - LoadingState.loadAndSwitchState(new PlayState(), true); + LoadingState.loadAndSwitchState(new PlayState( + { + targetSong: targetSong, + targetDifficulty: currentDifficultyId, + }), true); }); } diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 4379fe48e..c1bac76c4 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -14,7 +14,7 @@ class Constants * The title of the game, for debug printing purposes. * Change this if you're making an engine. */ - public static final TITLE = "Friday Night Funkin'"; + public static final TITLE:String = "Friday Night Funkin'"; /** * The current version number of the game. @@ -26,7 +26,7 @@ class Constants * A suffix to add to the game version. * Add a suffix to prototype builds and remove it for releases. */ - public static final VERSION_SUFFIX = ' PROTOTYPE'; + public static final VERSION_SUFFIX:String = ' PROTOTYPE'; #if debug static function get_VERSION():String @@ -48,12 +48,12 @@ class Constants /** * Link to download the game on Itch.io. */ - public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin/purchase"; + public static final URL_ITCH:String = 'https://ninja-muffin24.itch.io/funkin/purchase'; /** * Link to the game's page on Kickstarter. */ - public static final URL_KICKSTARTER:String = "https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/"; + public static final URL_KICKSTARTER:String = 'https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/'; /** * GIT REPO DATA @@ -64,12 +64,12 @@ class Constants /** * The current Git branch. */ - public static final GIT_BRANCH = funkin.util.macro.GitCommit.getGitBranch(); + public static final GIT_BRANCH:String = funkin.util.macro.GitCommit.getGitBranch(); /** * The current Git commit hash. */ - public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash(); + public static final GIT_HASH:String = funkin.util.macro.GitCommit.getGitCommitHash(); #end /** @@ -87,27 +87,70 @@ class Constants */ public static final COLOR_HEALTH_BAR_GREEN:FlxColor = 0xFF66FF33; + /** + * Default variation for charts. + */ + public static final DEFAULT_VARIATION:String = 'default'; + + /** + * STAGE DEFAULTS + */ + // ============================== + + /** + * Default difficulty for charts. + */ + public static final DEFAULT_DIFFICULTY:String = 'normal'; + + /** + * Default player character for charts. + */ + public static final DEFAULT_CHARACTER:String = 'bf'; + + /** + * Default stage for charts. + */ + public static final DEFAULT_STAGE:String = 'mainStage'; + + /** + * Default song for if the PlayState messes up. + */ + public static final DEFAULT_SONG:String = 'tutorial'; + /** * OTHER */ // ============================== + /** + * All MP3 decoders introduce a playback delay of `528` samples, + * which at 44,100 Hz (samples per second) is ~12 ms. + */ + public static final MP3_DELAY_MS:Float = 528 / 44100 * 1000; + /** * The scale factor to use when increasing the size of pixel art graphics. */ - public static final PIXEL_ART_SCALE = 6; + public static final PIXEL_ART_SCALE:Float = 6; /** * The BPM of the title screen and menu music. * TODO: Move to metadata file. */ - public static final FREAKY_MENU_BPM = 102; + public static final FREAKY_MENU_BPM:Float = 102; /** * The volume at which to play the countdown before the song starts. */ - public static final COUNTDOWN_VOLUME = 0.6; + public static final COUNTDOWN_VOLUME:Float = 0.6; - public static final DEFAULT_VARIATION = 'default'; - public static final DEFAULT_DIFFICULTY = 'normal'; + /** + * The default intensity for camera zooms. + */ + public static final DEFAULT_ZOOM_INTENSITY:Float = 0.015; + + /** + * The default rate for camera zooms (in beats per zoom). + */ + public static final DEFAULT_ZOOM_RATE:Int = 4; } diff --git a/source/funkin/util/FileUtil.hx b/source/funkin/util/FileUtil.hx index c30eacba8..57dc7b12e 100644 --- a/source/funkin/util/FileUtil.hx +++ b/source/funkin/util/FileUtil.hx @@ -18,7 +18,7 @@ class FileUtil /** * Browses for a single file, then calls `onSelect(path)` when a path chosen. * Note that on HTML5 this will immediately fail, you should call `openFile(onOpen:Resource->Void)` instead. - * + * * @param typeFilter Filters what kinds of files can be selected. * @return Whether the file dialog was opened successfully. */ @@ -46,7 +46,7 @@ class FileUtil /** * Browses for a directory, then calls `onSelect(path)` when a path chosen. * Note that on HTML5 this will immediately fail. - * + * * @param typeFilter TODO What does this do? * @return Whether the file dialog was opened successfully. */ @@ -74,7 +74,7 @@ class FileUtil /** * Browses for multiple file, then calls `onSelect(paths)` when a path chosen. * Note that on HTML5 this will immediately fail. - * + * * @return Whether the file dialog was opened successfully. */ public static function browseForMultipleFiles(?typeFilter:Array, ?onSelect:Array->Void, ?onCancel:Void->Void, ?defaultPath:String, @@ -101,7 +101,7 @@ class FileUtil /** * Browses for a file location to save to, then calls `onSelect(path)` when a path chosen. * Note that on HTML5 you can't do much with this, you should call `saveFile(resource:haxe.io.Bytes)` instead. - * + * * @param typeFilter TODO What does this do? * @return Whether the file dialog was opened successfully. */ @@ -129,7 +129,7 @@ class FileUtil /** * Browses for a single file location, then reads it and passes it to `onOpen(resource:haxe.io.Bytes)`. * Works great on desktop and HTML5. - * + * * @param typeFilter TODO What does this do? * @return Whether the file dialog was opened successfully. */ @@ -171,7 +171,7 @@ class FileUtil /** * Browses for a single file location, then writes the provided `haxe.io.Bytes` data and calls `onSave(path)` when done. * Works great on desktop and HTML5. - * + * * @return Whether the file dialog was opened successfully. */ public static function saveFile(data:Bytes, ?onSave:String->Void, ?onCancel:Void->Void, ?defaultFileName:String, ?dialogTitle:String):Bool @@ -204,7 +204,7 @@ class FileUtil * Prompts the user to save multiple files. * On desktop, this will prompt the user for a directory, then write all of the files to there. * On HTML5, this will zip the files up and prompt the user to save that. - * + * * @param typeFilter TODO What does this do? * @return Whether the file dialog was opened successfully. */ @@ -296,7 +296,7 @@ class FileUtil /** * Write string file contents directly to a given path. * Only works on desktop. - * + * * @param mode Whether to Force, Skip, or Ask to overwrite an existing file. */ public static function writeStringToPath(path:String, data:String, mode:FileWriteMode = Skip) @@ -335,7 +335,7 @@ class FileUtil /** * Write byte file contents directly to a given path. * Only works on desktop. - * + * * @param mode Whether to Force, Skip, or Ask to overwrite an existing file. */ public static function writeBytesToPath(path:String, data:Bytes, mode:FileWriteMode = Skip) @@ -432,7 +432,7 @@ class FileUtil /** * Create a Bytes object containing a ZIP file, containing the provided entries. - * + * * @param entries The entries to add to the ZIP file. * @return The ZIP file as a Bytes object. */ @@ -448,7 +448,7 @@ class FileUtil /** * Create a ZIP file entry from a file name and its string contents. - * + * * @param name The name of the file. You can use slashes to create subdirectories. * @param content The string contents of the file. * @return The resulting entry. diff --git a/source/funkin/util/SerializerUtil.hx b/source/funkin/util/SerializerUtil.hx index dae3327c6..662c05250 100644 --- a/source/funkin/util/SerializerUtil.hx +++ b/source/funkin/util/SerializerUtil.hx @@ -1,6 +1,7 @@ package funkin.util; import haxe.Json; +import haxe.io.Bytes; import thx.semver.Version; typedef ScoreInput = @@ -23,7 +24,7 @@ class SerializerUtil } /** - * Convert a JSON string to a Haxe object of the chosen type. + * Convert a JSON string to a Haxe object of the chosen type. */ public static function fromJSONTyped(input:String, type:Class):T { @@ -38,6 +39,14 @@ class SerializerUtil return Json.parse(input); } + /** + * Convert a JSON byte array to a Haxe object. + */ + public static function fromJSONBytes(input:Bytes):Dynamic + { + return Json.parse(input.toString()); + } + /** * Customize how certain types are serialized when converting to JSON. */ diff --git a/source/funkin/util/SortUtil.hx b/source/funkin/util/SortUtil.hx index e10f6dd55..60b522744 100644 --- a/source/funkin/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -19,7 +19,7 @@ class SortUtil /** * Given two Notes, returns 1 or -1 based on whether `a` or `b` has an earlier strumtime. - * + * * @param order Either `FlxSort.ASCENDING` or `FlxSort.DESCENDING` */ public static inline function byStrumtime(order:Int, a:Note, b:Note) diff --git a/source/funkin/util/macro/ClassMacro.hx b/source/funkin/util/macro/ClassMacro.hx index 591a5f0e1..43b437dda 100644 --- a/source/funkin/util/macro/ClassMacro.hx +++ b/source/funkin/util/macro/ClassMacro.hx @@ -7,7 +7,7 @@ import funkin.util.macro.MacroUtil; /** * Macros to generate lists of classes at compile time. - * + * * This code is a bitch glad Jason figured it out. * Based on code from CompileTime: https://github.com/jasononeil/compiletime */ @@ -15,9 +15,9 @@ class ClassMacro { /** * Gets a list of `Class` for all classes in a specified package. - * + * * Example: `var list:Array> = listClassesInPackage("funkin", true);` - * + * * @param targetPackage A String containing the package name to query. * @param includeSubPackages Whether to include classes located in sub-packages of the target package. * @return A list of classes matching the specified criteria. @@ -39,9 +39,9 @@ class ClassMacro /** * Get a list of `Class` for all classes extending a specified class. - * + * * Example: `var list:Array> = listSubclassesOf(FlxSprite);` - * + * * @param targetClass The class to query for subclasses. * @return A list of classes matching the specified criteria. */ @@ -68,7 +68,7 @@ class ClassMacro /** * Callback executed after the typing phase but before the generation phase. * Receives a list of `haxe.macro.Type` for all types in the program. - * + * * Only metadata can be modified at this time, which makes it a BITCH to access the data at runtime. */ static function onGenerate(allTypes:Array) diff --git a/source/funkin/util/macro/FlxMacro.hx b/source/funkin/util/macro/FlxMacro.hx index 313b2561a..0c1a5fa78 100644 --- a/source/funkin/util/macro/FlxMacro.hx +++ b/source/funkin/util/macro/FlxMacro.hx @@ -24,7 +24,7 @@ class FlxMacro { name: "zIndex", // Field name. access: [haxe.macro.Expr.Access.APublic], // Access level - kind: haxe.macro.Expr.FieldType.FVar(macro:Int, macro $v{0}), // Variable type and default value + kind: haxe.macro.Expr.FieldType.FVar(macro :Int, macro $v{0}), // Variable type and default value pos: pos, // The field's position in code. } ]); diff --git a/source/funkin/util/macro/MacroUtil.hx b/source/funkin/util/macro/MacroUtil.hx index e0f06c866..a121200ca 100644 --- a/source/funkin/util/macro/MacroUtil.hx +++ b/source/funkin/util/macro/MacroUtil.hx @@ -8,7 +8,7 @@ class MacroUtil { /** * Gets the value of a Haxe compiler define. - * + * * @param key The name of the define to get the value of. * @param defaultValue The value to return if the define is not set. * @return An expression containing the value of the define. diff --git a/source/funkin/util/tools/ArrayTools.hx b/source/funkin/util/tools/ArrayTools.hx new file mode 100644 index 000000000..02671a8e8 --- /dev/null +++ b/source/funkin/util/tools/ArrayTools.hx @@ -0,0 +1,25 @@ +package funkin.util.tools; + +/** + * A static extension which provides utility functions for Arrays. + */ +class ArrayTools +{ + /** + * Returns a copy of the array with all duplicate elements removed. + * @param array The array to remove duplicates from. + * @return A copy of the array with all duplicate elements removed. + */ + public static function unique(array:Array):Array + { + var result:Array = []; + for (element in array) + { + if (!result.contains(element)) + { + result.push(element); + } + } + return result; + } +} diff --git a/source/funkin/util/tools/IteratorTools.hx b/source/funkin/util/tools/IteratorTools.hx index 261f101e4..9279c1227 100644 --- a/source/funkin/util/tools/IteratorTools.hx +++ b/source/funkin/util/tools/IteratorTools.hx @@ -2,9 +2,9 @@ package funkin.util.tools; /** * A static extension which provides utility functions for Iterators. - * + * * For example, add `using IteratorTools` then call `iterator.array()`. - * + * * @see https://haxe.org/manual/lf-static-extension.html */ class IteratorTools diff --git a/source/funkin/util/tools/MapTools.hx b/source/funkin/util/tools/MapTools.hx index 430b7bc81..461b88bbf 100644 --- a/source/funkin/util/tools/MapTools.hx +++ b/source/funkin/util/tools/MapTools.hx @@ -2,9 +2,9 @@ package funkin.util.tools; /** * A static extension which provides utility functions for Maps. - * + * * For example, add `using MapTools` then call `map.values()`. - * + * * @see https://haxe.org/manual/lf-static-extension.html */ class MapTools diff --git a/source/funkin/util/tools/StringTools.hx b/source/funkin/util/tools/StringTools.hx index 7ecddd0d9..8ff76a080 100644 --- a/source/funkin/util/tools/StringTools.hx +++ b/source/funkin/util/tools/StringTools.hx @@ -7,7 +7,7 @@ class StringTools { /** * Converts a string to title case. For example, "hello world" becomes "Hello World". - * + * * @param value The string to convert. * @return The converted string. */ @@ -29,7 +29,7 @@ class StringTools /** * Converts a string to lower kebab case. For example, "Hello World" becomes "hello-world". - * + * * @param value The string to convert. * @return The converted string. */ @@ -40,7 +40,7 @@ class StringTools /** * Converts a string to upper kebab case, aka screaming kebab case. For example, "Hello World" becomes "HELLO-WORLD". - * + * * @param value The string to convert. * @return The converted string. */ @@ -51,7 +51,7 @@ class StringTools /** * Parses the string data as JSON and returns the resulting object. - * + * * @return The parsed object. */ public static function parseJSON(value:String):Dynamic diff --git a/source/module.xml b/source/module.xml index dd7c9ad20..c3437d993 100644 --- a/source/module.xml +++ b/source/module.xml @@ -1,4 +1,4 @@ - + @@ -14,8 +14,7 @@ - - \ No newline at end of file +