diff --git a/.gitmodules b/.gitmodules
index 8968471e3..17c3cc026 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,6 +2,9 @@
path = assets
url = https://github.com/FunkinCrew/Funkin-history-rewrite-assets
branch = master
+ update = merge
[submodule "art"]
path = art
url = https://github.com/FunkinCrew/Funkin-history-rewrite-art
+ branch = master
+ update = merge
diff --git a/Project.xml b/Project.xml
index 69400d8b1..46ba7f155 100644
--- a/Project.xml
+++ b/Project.xml
@@ -4,10 +4,7 @@
-
-
-
+
@@ -165,7 +162,10 @@
+
+
+
diff --git a/assets b/assets
index e634c8f50..fb7120cf3 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit e634c8f50c34845097283e0f411e1f89409e1498
+Subproject commit fb7120cf30d7accda049409b68d8daa0e1e7650f
diff --git a/hmm.json b/hmm.json
index 22dd6e42f..778b85604 100644
--- a/hmm.json
+++ b/hmm.json
@@ -56,7 +56,7 @@
"name": "haxeui-flixel",
"type": "git",
"dir": null,
- "ref": "9bd0b9e0fea40b8e06a89aac4949512d95064609",
+ "ref": "95c7d66e779626eabd6f48a1cd7aa7f9a503a7f3",
"url": "https://github.com/haxeui/haxeui-flixel"
},
{
diff --git a/source/Main.hx b/source/Main.hx
index dffe666b7..5fbb6747b 100644
--- a/source/Main.hx
+++ b/source/Main.hx
@@ -3,7 +3,7 @@ package;
import flixel.FlxGame;
import flixel.FlxState;
import funkin.util.logging.CrashHandler;
-import funkin.MemoryCounter;
+import funkin.ui.debug.MemoryCounter;
import funkin.save.Save;
import haxe.ui.Toolkit;
import openfl.display.FPS;
@@ -11,6 +11,7 @@ import openfl.display.Sprite;
import openfl.events.Event;
import openfl.Lib;
import openfl.media.Video;
+import funkin.util.CLIUtil;
import openfl.net.NetStream;
class Main extends Sprite
@@ -110,5 +111,6 @@ class Main extends Sprite
Toolkit.init();
Toolkit.theme = 'dark'; // don't be cringe
Toolkit.autoScale = false;
+ funkin.input.Cursor.registerHaxeUICursors();
}
}
diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx
index b79ae0fc4..10bf505f0 100644
--- a/source/funkin/Conductor.hx
+++ b/source/funkin/Conductor.hx
@@ -5,6 +5,7 @@ import flixel.util.FlxSignal;
import flixel.math.FlxMath;
import funkin.play.song.Song.SongDifficulty;
import funkin.data.song.SongData.SongTimeChange;
+import funkin.data.song.SongDataUtils;
/**
* A core class which handles musical timing throughout the game,
@@ -257,6 +258,9 @@ class Conductor
{
timeChanges = [];
+ // Sort in place just in case it's out of order.
+ SongDataUtils.sortTimeChanges(songTimeChanges);
+
for (currentTimeChange in songTimeChanges)
{
// TODO: Maybe handle this different?
diff --git a/source/funkin/CoolUtil.hx b/source/funkin/CoolUtil.hx
deleted file mode 100644
index d07bb4e22..000000000
--- a/source/funkin/CoolUtil.hx
+++ /dev/null
@@ -1,129 +0,0 @@
-package funkin;
-
-import flixel.FlxSprite;
-import flixel.FlxState;
-import flixel.graphics.FlxGraphic;
-import flixel.graphics.frames.FlxAtlasFrames;
-import flixel.math.FlxMath;
-import flixel.math.FlxPoint;
-import flixel.math.FlxRect;
-import flixel.system.FlxAssets.FlxGraphicAsset;
-import flixel.tweens.FlxEase;
-import flixel.tweens.FlxTween;
-import funkin.play.PlayState;
-import funkin.shaderslmfao.ScreenWipeShader;
-import haxe.format.JsonParser;
-import lime.math.Rectangle;
-import lime.utils.Assets;
-import openfl.filters.ShaderFilter;
-
-class CoolUtil
-{
- public static function coolBaseLog(base:Float, fin:Float):Float
- {
- return Math.log(fin) / Math.log(base);
- }
-
- public static function coolTextFile(path:String):Array
- {
- var daList:Array = [];
-
- var swagArray:Array = Assets.getText(path).trim().split('\n');
-
- for (item in swagArray)
- {
- // comment support in the quick lil text formats??? using //
- if (!item.trim().startsWith('//')) daList.push(item);
- }
-
- for (i in 0...daList.length)
- {
- daList[i] = daList[i].trim();
- }
-
- return daList;
- }
-
- public static function numberArray(max:Int, ?min = 0):Array
- {
- var dumbArray:Array = [];
- for (i in min...max)
- {
- dumbArray.push(i);
- }
- return dumbArray;
- }
-
- static var oldCamPos:FlxPoint = new FlxPoint();
- static var oldMousePos:FlxPoint = new FlxPoint();
-
- /**
- * Used to be for general camera middle click dragging, now generalized for any click and drag type shit!
- * Listen I don't make the rules here
- * @param target what you want to be dragged, defaults to CAMERA SCROLL
- * @param jusPres the "justPressed", should be a button of some sort
- * @param pressed the "pressed", which should be the same button as `jusPres`
- */
- public static function mouseCamDrag(?target:FlxPoint, ?jusPres:Bool, ?pressed:Bool):Void
- {
- if (target == null) target = FlxG.camera.scroll;
-
- if (jusPres == null) jusPres = FlxG.mouse.justPressedMiddle;
-
- if (pressed == null) pressed = FlxG.mouse.pressedMiddle;
-
- if (jusPres)
- {
- oldCamPos.set(target.x, target.y);
- oldMousePos.set(FlxG.mouse.screenX, FlxG.mouse.screenY);
- }
-
- if (pressed)
- {
- target.x = oldCamPos.x - (FlxG.mouse.screenX - oldMousePos.x);
- target.y = oldCamPos.y - (FlxG.mouse.screenY - oldMousePos.y);
- }
- }
-
- public static function mouseWheelZoom():Void
- {
- if (FlxG.mouse.wheel != 0) FlxG.camera.zoom += FlxG.mouse.wheel * (0.1 * FlxG.camera.zoom);
- }
-
- /**
- Lerps camera, but accountsfor framerate shit?
- Right now it's simply for use to change the followLerp variable of a camera during update
- TODO LATER MAYBE:
- Actually make and modify the scroll and lerp shit in it's own function
- instead of solely relying on changing the lerp on the fly
- */
- public static function camLerpShit(lerp:Float):Float
- {
- return lerp * (FlxG.elapsed / (1 / 60));
- }
-
- public static function coolSwitchState(state:FlxState, transitionTex:String = "shaderTransitionStuff/coolDots", time:Float = 2)
- {
- var screenShit:FlxSprite = new FlxSprite().loadGraphic(Paths.image("shaderTransitionStuff/coolDots"));
- var screenWipeShit:ScreenWipeShader = new ScreenWipeShader();
-
- screenWipeShit.funnyShit.input = screenShit.pixels;
- FlxTween.tween(screenWipeShit, {daAlphaShit: 1}, time,
- {
- ease: FlxEase.quadInOut,
- onComplete: function(twn) {
- screenShit.destroy();
- FlxG.switchState(new MainMenuState());
- }
- });
- FlxG.camera.setFilters([new ShaderFilter(screenWipeShit)]);
- }
-
- /*
- * frame dependant lerp kinda lol
- */
- public static function coolLerp(base:Float, target:Float, ratio:Float):Float
- {
- return base + camLerpShit(ratio) * (target - base);
- }
-}
diff --git a/source/funkin/DialogueBox.hx b/source/funkin/DialogueBox.hx
deleted file mode 100644
index 68d330dbe..000000000
--- a/source/funkin/DialogueBox.hx
+++ /dev/null
@@ -1,265 +0,0 @@
-package funkin;
-
-import flixel.FlxSprite;
-import flixel.addons.text.FlxTypeText;
-import flixel.group.FlxSpriteGroup;
-import flixel.text.FlxText;
-import flixel.util.FlxColor;
-import flixel.util.FlxTimer;
-import funkin.play.PlayState;
-
-/**
- * Handles dialog boxes and text, like the ones in Week 6.
- */
-class DialogueBox extends FlxSpriteGroup
-{
- var box:FlxSprite;
-
- var curCharacter:String = '';
-
- var dialogue:Alphabet;
- var dialogueList:Array = [];
-
- // SECOND DIALOGUE FOR THE PIXEL SHIT INSTEAD???
- var swagDialogue:FlxTypeText;
-
- var dropText:FlxText;
-
- public var finishThing:Void->Void;
-
- var portraitLeft:FlxSprite;
- var portraitRight:FlxSprite;
-
- var handSelect:FlxSprite;
- var bgFade:FlxSprite;
-
- public function new(talkingRight:Bool = true, ?dialogueList:Array)
- {
- super();
-
- switch (PlayState.instance.currentSong.id.toLowerCase())
- {
- case 'senpai':
- FlxG.sound.playMusic(Paths.music('Lunchbox'), 0);
- FlxG.sound.music.fadeIn(1, 0, 0.8);
- case 'thorns':
- FlxG.sound.playMusic(Paths.music('LunchboxScary'), 0);
- FlxG.sound.music.fadeIn(1, 0, 0.8);
- }
-
- bgFade = new FlxSprite(-200, -200).makeGraphic(Std.int(FlxG.width * 1.3), Std.int(FlxG.height * 1.3), 0xFFB3DFD8);
- bgFade.scrollFactor.set();
- bgFade.alpha = 0;
- add(bgFade);
-
- new FlxTimer().start(0.83, function(tmr:FlxTimer) {
- bgFade.alpha += (1 / 5) * 0.7;
- if (bgFade.alpha > 0.7) bgFade.alpha = 0.7;
- }, 5);
-
- portraitLeft = new FlxSprite(-20, 40);
- portraitLeft.frames = Paths.getSparrowAtlas('weeb/senpaiPortrait');
- portraitLeft.animation.addByPrefix('enter', 'Senpai Portrait Enter', 24, false);
- portraitLeft.setGraphicSize(Std.int(portraitLeft.width * Constants.PIXEL_ART_SCALE * 0.9));
- portraitLeft.updateHitbox();
- portraitLeft.scrollFactor.set();
- add(portraitLeft);
- portraitLeft.visible = false;
-
- portraitRight = new FlxSprite(0, 40);
- portraitRight.frames = Paths.getSparrowAtlas('weeb/bfPortrait');
- portraitRight.animation.addByPrefix('enter', 'Boyfriend portrait enter', 24, false);
- portraitRight.setGraphicSize(Std.int(portraitRight.width * Constants.PIXEL_ART_SCALE * 0.9));
- portraitRight.updateHitbox();
- portraitRight.scrollFactor.set();
- add(portraitRight);
- portraitRight.visible = false;
-
- box = new FlxSprite(-20, 45);
-
- var hasDialog:Bool = false;
- switch (PlayState.instance.currentSong.id.toLowerCase())
- {
- case 'senpai':
- hasDialog = true;
- box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-pixel');
- box.animation.addByPrefix('normalOpen', 'Text Box Appear', 24, false);
- box.animation.addByIndices('normal', 'Text Box Appear', [4], '', 24);
- case 'roses':
- hasDialog = true;
- FlxG.sound.play(Paths.sound('ANGRY_TEXT_BOX'));
-
- box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-senpaiMad');
- box.animation.addByPrefix('normalOpen', 'SENPAI ANGRY IMPACT SPEECH', 24, false);
- box.animation.addByIndices('normal', 'SENPAI ANGRY IMPACT SPEECH', [4], '', 24);
-
- case 'thorns':
- hasDialog = true;
- box.frames = Paths.getSparrowAtlas('weeb/pixelUI/dialogueBox-evil');
- box.animation.addByPrefix('normalOpen', 'Spirit Textbox spawn', 24, false);
- box.animation.addByIndices('normal', 'Spirit Textbox spawn', [11], '', 24);
-
- var face:FlxSprite = new FlxSprite(320, 170).loadGraphic(Paths.image('weeb/spiritFaceForward'));
- face.setGraphicSize(Std.int(face.width * 6));
- add(face);
- }
-
- this.dialogueList = dialogueList;
-
- if (!hasDialog) return;
-
- box.animation.play('normalOpen');
- box.setGraphicSize(Std.int(box.width * Constants.PIXEL_ART_SCALE * 0.9));
- box.updateHitbox();
- add(box);
-
- box.screenCenter(X);
- portraitLeft.screenCenter(X);
-
- handSelect = new FlxSprite(1042, 590).loadGraphic(Paths.image('weeb/pixelUI/hand_textbox'));
- handSelect.setGraphicSize(Std.int(handSelect.width * Constants.PIXEL_ART_SCALE * 0.9));
- handSelect.updateHitbox();
- handSelect.visible = false;
- add(handSelect);
-
- if (!talkingRight)
- {
- // box.flipX = true;
- }
-
- dropText = new FlxText(242, 502, Std.int(FlxG.width * 0.6), '', 32);
- dropText.font = 'Pixel Arial 11 Bold';
- dropText.color = 0xFFD89494;
- add(dropText);
-
- swagDialogue = new FlxTypeText(240, 500, Std.int(FlxG.width * 0.6), '', 32);
- swagDialogue.font = 'Pixel Arial 11 Bold';
- swagDialogue.color = 0xFF3F2021;
- swagDialogue.sounds = [FlxG.sound.load(Paths.sound('pixelText'), 0.6)];
- add(swagDialogue);
-
- dialogue = new Alphabet(0, 80, '', false, true);
- // dialogue.x = 90;
- // add(dialogue);
- }
-
- var dialogueOpened:Bool = false;
- var dialogueStarted:Bool = false;
- var dialogueEnded:Bool = false;
-
- override function update(elapsed:Float):Void
- {
- // HARD CODING CUZ IM STUPDI
- if (PlayState.instance.currentSong.id.toLowerCase() == 'roses') portraitLeft.visible = false;
- if (PlayState.instance.currentSong.id.toLowerCase() == 'thorns')
- {
- portraitLeft.color = FlxColor.BLACK;
- swagDialogue.color = FlxColor.WHITE;
- dropText.color = FlxColor.BLACK;
- }
-
- dropText.text = swagDialogue.text;
-
- if (box.animation.curAnim != null)
- {
- if (box.animation.curAnim.name == 'normalOpen' && box.animation.curAnim.finished)
- {
- box.animation.play('normal');
- dialogueOpened = true;
- }
- }
-
- if (dialogueOpened && !dialogueStarted)
- {
- startDialogue();
- dialogueStarted = true;
- }
-
- if (FlxG.keys.justPressed.ANY && dialogueEnded)
- {
- remove(dialogue);
-
- FlxG.sound.play(Paths.sound('clickText'), 0.8);
-
- if (dialogueList[1] == null && dialogueList[0] != null)
- {
- if (!isEnding)
- {
- isEnding = true;
-
- if (PlayState.instance.currentSong.id.toLowerCase() == 'senpai'
- || PlayState.instance.currentSong.id.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0);
-
- new FlxTimer().start(0.2, function(tmr:FlxTimer) {
- box.alpha -= 1 / 5;
- bgFade.alpha -= 1 / 5 * 0.7;
- portraitLeft.visible = false;
- portraitRight.visible = false;
- swagDialogue.alpha -= 1 / 5;
- handSelect.alpha -= 1 / 5;
- dropText.alpha = swagDialogue.alpha;
- }, 5);
-
- new FlxTimer().start(1.2, function(tmr:FlxTimer) {
- finishThing();
- kill();
- });
- }
- }
- else
- {
- dialogueList.remove(dialogueList[0]);
- startDialogue();
- }
- }
- else if (FlxG.keys.justPressed.ANY && dialogueStarted) swagDialogue.skip();
-
- super.update(elapsed);
- }
-
- var isEnding:Bool = false;
-
- function startDialogue():Void
- {
- cleanDialog();
- // var theDialog:Alphabet = new Alphabet(0, 70, dialogueList[0], false, true);
- // dialogue = theDialog;
- // add(theDialog);
-
- // swagDialogue.text = ;
- swagDialogue.resetText(dialogueList[0]);
- swagDialogue.start(0.04);
- swagDialogue.completeCallback = function() {
- trace('dialogue finish');
- handSelect.visible = true;
- dialogueEnded = true;
- };
- handSelect.visible = false;
- dialogueEnded = false;
-
- switch (curCharacter)
- {
- case 'dad':
- portraitRight.visible = false;
- if (!portraitLeft.visible)
- {
- portraitLeft.visible = true;
- portraitLeft.animation.play('enter');
- }
- case 'bf':
- portraitLeft.visible = false;
- if (!portraitRight.visible)
- {
- portraitRight.visible = true;
- portraitRight.animation.play('enter');
- }
- }
- }
-
- function cleanDialog():Void
- {
- var splitName:Array = dialogueList[0].split(':');
- curCharacter = splitName[1];
- dialogueList[0] = dialogueList[0].substr(splitName[1].length + 2).trim();
- }
-}
diff --git a/source/funkin/FlxSwf.hx b/source/funkin/FlxSwf.hx
deleted file mode 100644
index d37343894..000000000
--- a/source/funkin/FlxSwf.hx
+++ /dev/null
@@ -1,43 +0,0 @@
-package funkin;
-
-import flixel.FlxCamera;
-import flixel.FlxSprite;
-import flixel.graphics.tile.FlxDrawBaseItem;
-import openfl.display.MovieClip;
-
-class FlxSwf extends FlxSprite
-{
- public var swf:MovieClip;
-
- public function new()
- {
- super();
- }
-
- override function draw()
- {
- for (camera in cameras)
- {
- if (!camera.visible || !camera.exists) continue;
-
- getScreenPosition(_point, camera).subtractPoint(offset);
- // assume no render blit for now
- // use camera.canvas
- // camera.canvas.graphics.
- }
- }
-}
-
-class FlxDrawSwfItem extends FlxDrawBaseItem
-{
- public function new()
- {
- super();
- type = FlxDrawItemType.TILES;
- }
-
- override function render(camera:FlxCamera)
- {
- super.render(camera);
- }
-}
diff --git a/source/funkin/Highscore.hx b/source/funkin/Highscore.hx
index 3c9fd82e4..2c18ffa2d 100644
--- a/source/funkin/Highscore.hx
+++ b/source/funkin/Highscore.hx
@@ -1,5 +1,8 @@
package funkin;
+/**
+ * A core class which handles tracking score and combo for the current song.
+ */
class Highscore
{
public static var tallies:Tallies = new Tallies();
diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx
index 4785aa347..13bcd306e 100644
--- a/source/funkin/InitState.hx
+++ b/source/funkin/InitState.hx
@@ -1,5 +1,7 @@
package funkin;
+import funkin.ui.debug.charting.ChartEditorState;
+import funkin.ui.transition.LoadingState;
import flixel.FlxState;
import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond;
@@ -26,11 +28,15 @@ import funkin.play.stage.StageData.StageDataParser;
import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.modding.module.ModuleHandler;
import funkin.ui.title.TitleState;
+import funkin.util.CLIUtil;
+import funkin.util.CLIUtil.CLIParams;
+import funkin.ui.transition.LoadingState;
#if discord_rpc
import Discord.DiscordClient;
#end
/**
+ * A core class which performs initialization of the game.
* The initialization state has several functions:
* - Calls code to set up the game, including loading saves and parsing game data.
* - Chooses whether to start via debug or via launching normally.
@@ -228,13 +234,13 @@ class InitState extends FlxState
#elseif FREEPLAY // -DFREEPLAY
FlxG.switchState(new FreeplayState());
#elseif ANIMATE // -DANIMATE
- FlxG.switchState(new funkin.ui.animDebugShit.FlxAnimateTest());
+ FlxG.switchState(new funkin.ui.debug.anim.FlxAnimateTest());
#elseif CHARTING // -DCHARTING
FlxG.switchState(new funkin.ui.debug.charting.ChartEditorState());
#elseif STAGEBUILD // -DSTAGEBUILD
- FlxG.switchState(new funkin.ui.stageBullshit.StageBuilderState());
+ FlxG.switchState(new funkin.ui.debug.stage.StageBuilderState());
#elseif ANIMDEBUG // -DANIMDEBUG
- FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
+ FlxG.switchState(new funkin.ui.debug.anim.DebugBoundingState());
#elseif LATENCY // -DLATENCY
FlxG.switchState(new funkin.LatencyState());
#else
@@ -247,8 +253,21 @@ class InitState extends FlxState
*/
function startGameNormally():Void
{
- FlxG.sound.cache(Paths.music('freakyMenu/freakyMenu'));
- FlxG.switchState(new TitleState());
+ var params:CLIParams = CLIUtil.processArgs();
+ trace('Command line args: ${params}');
+
+ if (params.chart.shouldLoadChart)
+ {
+ FlxG.switchState(new ChartEditorState(
+ {
+ fnfcTargetPath: params.chart.chartPath,
+ }));
+ }
+ else
+ {
+ FlxG.sound.cache(Paths.music('freakyMenu/freakyMenu'));
+ FlxG.switchState(new TitleState());
+ }
}
/**
diff --git a/source/funkin/MenuCharacter.hx b/source/funkin/MenuCharacter.hx
deleted file mode 100644
index 3e05b9e9f..000000000
--- a/source/funkin/MenuCharacter.hx
+++ /dev/null
@@ -1,42 +0,0 @@
-package funkin;
-
-import flixel.FlxSprite;
-import flixel.graphics.frames.FlxAtlasFrames;
-
-class MenuCharacter extends FlxSprite
-{
- public var character:String;
-
- public function new(x:Float, character:String = 'bf')
- {
- super(x);
-
- this.character = character;
-
- var suffix:String = character;
-
- if (character != "darnell" && character != "nene") suffix = "characters";
-
- var tex = Paths.getSparrowAtlas('campaign_menu_UI_' + suffix);
- frames = tex;
-
- trace(character);
-
- animation.addByPrefix('bf', "BF idle dance white", 24);
- animation.addByPrefix('bfConfirm', 'BF HEY!!', 24, false);
- animation.addByPrefix('gf', "GF Dancing Beat WHITE", 24);
- animation.addByPrefix('dad', "Dad idle dance BLACK LINE", 24);
- animation.addByPrefix('spooky', "spooky dance idle BLACK LINES", 24);
- animation.addByPrefix('pico', "Pico Idle Dance", 24);
- animation.addByPrefix('mom', "Mom Idle BLACK LINES", 24);
- animation.addByPrefix('parents-christmas', "Parent Christmas Idle", 24);
- animation.addByPrefix('senpai', "SENPAI idle Black Lines", 24);
- animation.addByPrefix('tankman', "Tankman Menu BLACK", 24);
- animation.addByPrefix('darnell', "Darnell Black Lines To Scale", 24);
- animation.addByPrefix('nene', "Nene Black Lines To Scale", 24);
- // Parent Christmas Idle
-
- animation.play(character);
- updateHitbox();
- }
-}
diff --git a/source/funkin/NoteSplash.hx b/source/funkin/NoteSplash.hx
deleted file mode 100644
index 81b35b36d..000000000
--- a/source/funkin/NoteSplash.hx
+++ /dev/null
@@ -1,65 +0,0 @@
-package funkin;
-
-import flixel.FlxSprite;
-import haxe.io.Path;
-import flixel.graphics.frames.FlxAtlasFrames;
-
-class NoteSplash extends FlxSprite
-{
- public function new(x:Float, y:Float, noteData:Int = 0):Void
- {
- super(x, y);
-
- animation.addByPrefix('note0-0', 'note impact 1 purple', 24, false);
- animation.addByPrefix('note1-0', 'note impact 1 blue', 24, false);
- animation.addByPrefix('note2-0', 'note impact 1 green', 24, false);
- animation.addByPrefix('note3-0', 'note impact 1 red', 24, false);
- animation.addByPrefix('note0-1', 'note impact 2 purple', 24, false);
- animation.addByPrefix('note1-1', 'note impact 2 blue', 24, false);
- animation.addByPrefix('note2-1', 'note impact 2 green', 24, false);
- animation.addByPrefix('note3-1', 'note impact 2 red', 24, false);
-
- setupNoteSplash(x, y, noteData);
-
- // alpha = 0.75;
- }
-
- public override function update(elapsed:Float):Void
- {
- super.update(elapsed);
-
- if (animation.finished)
- {
- kill();
- }
- }
-
- public static function buildSplashFrames(force:Bool = false):FlxAtlasFrames
- {
- // static variables inside functions are a cool of Haxe 4.3.0.
- static var splashFrames:FlxAtlasFrames = null;
-
- if (splashFrames != null && !force) return splashFrames;
-
- splashFrames = Paths.getSparrowAtlas('noteSplashes');
-
- splashFrames.parent.persist = true;
-
- return splashFrames;
- }
-
- public function setupNoteSplash(x:Float, y:Float, noteData:Int = 0)
- {
- setPosition(x, y);
- alpha = 0.6;
-
- animation.play('note' + noteData + '-' + FlxG.random.int(0, 1), true);
- animation.curAnim.frameRate = 24 + FlxG.random.int(-2, 2);
- animation.finishCallback = function(name) {
- kill();
- };
- updateHitbox();
-
- offset.set(width * 0.3, height * 0.3);
- }
-}
diff --git a/source/funkin/Paths.hx b/source/funkin/Paths.hx
index 07a15dae1..e0212e573 100644
--- a/source/funkin/Paths.hx
+++ b/source/funkin/Paths.hx
@@ -4,6 +4,9 @@ import flixel.graphics.frames.FlxAtlasFrames;
import openfl.utils.AssetType;
import openfl.utils.Assets as OpenFlAssets;
+/**
+ * A core class which handles determining asset paths.
+ */
class Paths
{
static var currentLevel:String;
diff --git a/source/funkin/PlayerSettings.hx b/source/funkin/PlayerSettings.hx
index 7f156366d..2e3e0e425 100644
--- a/source/funkin/PlayerSettings.hx
+++ b/source/funkin/PlayerSettings.hx
@@ -8,8 +8,9 @@ import flixel.input.actions.FlxActionInput;
import flixel.input.gamepad.FlxGamepad;
import flixel.util.FlxSignal;
-// import ui.DeviceManager;
-// import props.Player;
+/**
+ * A core class which represents the current player(s) and their controls and other configuration.
+ */
class PlayerSettings
{
public static var numPlayers(default, null) = 0;
diff --git a/source/funkin/Preferences.hx b/source/funkin/Preferences.hx
index 7e3c3c6d7..6b0911ede 100644
--- a/source/funkin/Preferences.hx
+++ b/source/funkin/Preferences.hx
@@ -3,7 +3,7 @@ package funkin;
import funkin.save.Save;
/**
- * A store of user-configurable, globally relevant values.
+ * A core class which provides a store of user-configurable, globally relevant values.
*/
class Preferences
{
diff --git a/source/Preloader.hx b/source/funkin/Preloader.hx
similarity index 93%
rename from source/Preloader.hx
rename to source/funkin/Preloader.hx
index 3603d1a16..24015be05 100644
--- a/source/Preloader.hx
+++ b/source/funkin/Preloader.hx
@@ -1,4 +1,4 @@
-package;
+package funkin;
import flash.Lib;
import flash.display.Bitmap;
@@ -7,6 +7,7 @@ import flash.display.BlendMode;
import flash.display.Sprite;
import flixel.system.FlxBasePreloader;
import openfl.display.Sprite;
+import funkin.util.CLIUtil;
@:bitmap("art/preloaderArt.png") class LogoImage extends BitmapData {}
@@ -15,6 +16,8 @@ class Preloader extends FlxBasePreloader
public function new(MinDisplayTime:Float = 0, ?AllowedURLs:Array)
{
super(MinDisplayTime, AllowedURLs);
+
+ CLIUtil.resetWorkingDir(); // Bug fix for drag-and-drop.
}
var logo:Sprite;
diff --git a/source/funkin/TankCutscene.hx b/source/funkin/TankCutscene.hx
deleted file mode 100644
index 4bc7349ad..000000000
--- a/source/funkin/TankCutscene.hx
+++ /dev/null
@@ -1,27 +0,0 @@
-package funkin;
-
-import flixel.FlxSprite;
-import flixel.sound.FlxSound;
-
-class TankCutscene extends FlxSprite
-{
- public var startSyncAudio:FlxSound;
-
- public function new(x:Float, y:Float)
- {
- super(x, y);
- }
-
- var startedPlayingSound:Bool = false;
-
- override function update(elapsed:Float)
- {
- if (animation.curAnim.curFrame >= 1 && !startedPlayingSound)
- {
- startSyncAudio.play();
- startedPlayingSound = true;
- }
-
- super.update(elapsed);
- }
-}
diff --git a/source/funkin/Discord.hx b/source/funkin/api/discord/Discord.hx
similarity index 98%
rename from source/funkin/Discord.hx
rename to source/funkin/api/discord/Discord.hx
index d2cf12535..a4d65684e 100644
--- a/source/funkin/Discord.hx
+++ b/source/funkin/api/discord/Discord.hx
@@ -1,4 +1,4 @@
-package funkin;
+package funkin.api.discord;
import Sys.sleep;
#if discord_rpc
diff --git a/source/funkin/api/newgrounds/NGUtil.hx b/source/funkin/api/newgrounds/NGUtil.hx
index ba7d5f916..c8289fc46 100644
--- a/source/funkin/api/newgrounds/NGUtil.hx
+++ b/source/funkin/api/newgrounds/NGUtil.hx
@@ -241,15 +241,3 @@ class NGUtil
}
#end
}
-
-enum ConnectionResult
-{
- /** Log in successful */
- Success;
-
- /** Could not login */
- Fail(msg:String);
-
- /** User cancelled the login */
- Cancelled;
-}
diff --git a/source/funkin/NGio.hx b/source/funkin/api/newgrounds/NGio.hx
similarity index 99%
rename from source/funkin/NGio.hx
rename to source/funkin/api/newgrounds/NGio.hx
index e5f60c8b5..e505bdedf 100644
--- a/source/funkin/NGio.hx
+++ b/source/funkin/api/newgrounds/NGio.hx
@@ -1,4 +1,4 @@
-package funkin;
+package funkin.api.newgrounds;
#if newgrounds
import flixel.util.FlxSignal;
diff --git a/source/funkin/api/newgrounds/README.md b/source/funkin/api/newgrounds/README.md
index f61e1b0fd..09534fb71 100644
--- a/source/funkin/api/newgrounds/README.md
+++ b/source/funkin/api/newgrounds/README.md
@@ -6,4 +6,4 @@ This package contains two main classes:
such as retrieving achievement status.
- `NGUnsafe` contains sensitive utility functions for interacting with the Newgrounds API.
- This includes any functions which scripts should not be able to use,
- such as writing high scores or posting achievements.
\ No newline at end of file
+ such as writing high scores or posting achievements.
diff --git a/source/funkin/audiovis/ABot.hx b/source/funkin/audio/visualize/ABot.hx
similarity index 85%
rename from source/funkin/audiovis/ABot.hx
rename to source/funkin/audio/visualize/ABot.hx
index 11c123fb2..0b2ec619e 100644
--- a/source/funkin/audiovis/ABot.hx
+++ b/source/funkin/audio/visualize/ABot.hx
@@ -1,4 +1,4 @@
-package funkin.audiovis;
+package funkin.audio.visualize;
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
diff --git a/source/funkin/audiovis/ABotVis.hx b/source/funkin/audio/visualize/ABotVis.hx
similarity index 96%
rename from source/funkin/audiovis/ABotVis.hx
rename to source/funkin/audio/visualize/ABotVis.hx
index 060bddcf7..681287808 100644
--- a/source/funkin/audiovis/ABotVis.hx
+++ b/source/funkin/audio/visualize/ABotVis.hx
@@ -1,12 +1,13 @@
-package funkin.audiovis;
+package funkin.audio.visualize;
-import funkin.audiovis.dsp.FFT;
+import funkin.audio.visualize.dsp.FFT;
import flixel.FlxSprite;
import flixel.addons.plugin.taskManager.FlxTask;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
import flixel.math.FlxMath;
import flixel.sound.FlxSound;
+import funkin.util.MathUtil;
using Lambda;
@@ -86,7 +87,7 @@ class ABotVis extends FlxTypedSpriteGroup
for (i in 0...group.members.length)
{
var getSliceShit = function(s:Int) {
- var powShit = FlxMath.remapToRange(s, 0, group.members.length, 0, CoolUtil.coolBaseLog(10, freqShit[0].length));
+ var powShit = FlxMath.remapToRange(s, 0, group.members.length, 0, MathUtil.logBase(10, freqShit[0].length));
return Math.round(Math.pow(10, powShit));
};
diff --git a/source/funkin/audio/visualize/PolygonSpectogram.hx b/source/funkin/audio/visualize/PolygonSpectogram.hx
index 6b7e280ec..604bc910b 100644
--- a/source/funkin/audio/visualize/PolygonSpectogram.hx
+++ b/source/funkin/audio/visualize/PolygonSpectogram.hx
@@ -4,7 +4,7 @@ import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.sound.FlxSound;
import flixel.util.FlxColor;
-import funkin.audiovis.VisShit;
+import funkin.audio.visualize.VisShit;
import funkin.graphics.rendering.MeshRender;
import lime.utils.Int16Array;
diff --git a/source/funkin/audiovis/SpectogramSprite.hx b/source/funkin/audio/visualize/SpectogramSprite.hx
similarity index 98%
rename from source/funkin/audiovis/SpectogramSprite.hx
rename to source/funkin/audio/visualize/SpectogramSprite.hx
index c4f8234eb..63d0fcd2e 100644
--- a/source/funkin/audiovis/SpectogramSprite.hx
+++ b/source/funkin/audio/visualize/SpectogramSprite.hx
@@ -1,4 +1,4 @@
-package funkin.audiovis;
+package funkin.audio.visualize;
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
@@ -8,8 +8,8 @@ import flixel.math.FlxVector;
import flixel.sound.FlxSound;
import flixel.util.FlxColor;
import funkin.audio.visualize.PolygonSpectogram.VISTYPE;
-import funkin.audiovis.VisShit.CurAudioInfo;
-import funkin.audiovis.dsp.FFT;
+import funkin.audio.visualize.VisShit.CurAudioInfo;
+import funkin.audio.visualize.dsp.FFT;
import haxe.Timer;
import lime.system.ThreadPool;
import lime.utils.Int16Array;
diff --git a/source/funkin/audiovis/VisShit.hx b/source/funkin/audio/visualize/VisShit.hx
similarity index 93%
rename from source/funkin/audiovis/VisShit.hx
rename to source/funkin/audio/visualize/VisShit.hx
index ee531a977..5bfb8c7c5 100644
--- a/source/funkin/audiovis/VisShit.hx
+++ b/source/funkin/audio/visualize/VisShit.hx
@@ -1,11 +1,12 @@
-package funkin.audiovis;
+package funkin.audio.visualize;
import flixel.math.FlxMath;
import flixel.sound.FlxSound;
-import funkin.audiovis.dsp.FFT;
+import funkin.audio.visualize.dsp.FFT;
import haxe.Timer;
import lime.system.ThreadPool;
import lime.utils.Int16Array;
+import funkin.util.MathUtil;
using Lambda;
@@ -42,7 +43,7 @@ class VisShit
// helpers, note that spectrum indexes suppose non-negative frequencies
final binSize = fs / fftN;
final indexToFreq = function(k:Int) {
- var powShit:Float = FlxMath.remapToRange(k, 0, halfN, 0, CoolUtil.coolBaseLog(10, halfN)); // 4.3 is almost the log of 20Khz or so. Close enuf lol
+ var powShit:Float = FlxMath.remapToRange(k, 0, halfN, 0, MathUtil.logBase(10, halfN)); // 4.3 is almost the log of 20Khz or so. Close enuf lol
return 1.0 * (Math.pow(10, powShit)); // we need the `1.0` to avoid overflows
};
diff --git a/source/funkin/audiovis/dsp/Complex.hx b/source/funkin/audio/visualize/dsp/Complex.hx
similarity index 98%
rename from source/funkin/audiovis/dsp/Complex.hx
rename to source/funkin/audio/visualize/dsp/Complex.hx
index 523549e99..37861bcc3 100644
--- a/source/funkin/audiovis/dsp/Complex.hx
+++ b/source/funkin/audio/visualize/dsp/Complex.hx
@@ -1,4 +1,4 @@
-package funkin.audiovis.dsp;
+package funkin.audio.visualize.dsp;
/**
Complex number representation.
diff --git a/source/funkin/audiovis/dsp/FFT.hx b/source/funkin/audio/visualize/dsp/FFT.hx
similarity index 97%
rename from source/funkin/audiovis/dsp/FFT.hx
rename to source/funkin/audio/visualize/dsp/FFT.hx
index d1d99140e..dc75acb81 100644
--- a/source/funkin/audiovis/dsp/FFT.hx
+++ b/source/funkin/audio/visualize/dsp/FFT.hx
@@ -1,9 +1,9 @@
-package funkin.audiovis.dsp;
+package funkin.audio.visualize.dsp;
-import funkin.audiovis.dsp.Complex;
+import funkin.audio.visualize.dsp.Complex;
-using funkin.audiovis.dsp.OffsetArray;
-using funkin.audiovis.dsp.Signal;
+using funkin.audio.visualize.dsp.OffsetArray;
+using funkin.audio.visualize.dsp.Signal;
// these are only used for testing, down in FFT.main()
diff --git a/source/funkin/audiovis/dsp/OffsetArray.hx b/source/funkin/audio/visualize/dsp/OffsetArray.hx
similarity index 98%
rename from source/funkin/audiovis/dsp/OffsetArray.hx
rename to source/funkin/audio/visualize/dsp/OffsetArray.hx
index bd066a727..c8a5c27c3 100644
--- a/source/funkin/audiovis/dsp/OffsetArray.hx
+++ b/source/funkin/audio/visualize/dsp/OffsetArray.hx
@@ -1,4 +1,4 @@
-package funkin.audiovis.dsp;
+package funkin.audio.visualize.dsp;
/**
A view into an Array with an indexing offset.
diff --git a/source/funkin/audiovis/dsp/Signal.hx b/source/funkin/audio/visualize/dsp/Signal.hx
similarity index 98%
rename from source/funkin/audiovis/dsp/Signal.hx
rename to source/funkin/audio/visualize/dsp/Signal.hx
index 1f7cc6114..4557dc199 100644
--- a/source/funkin/audiovis/dsp/Signal.hx
+++ b/source/funkin/audio/visualize/dsp/Signal.hx
@@ -1,4 +1,4 @@
-package funkin.audiovis.dsp;
+package funkin.audio.visualize.dsp;
using Lambda;
diff --git a/source/funkin/data/DataParse.hx b/source/funkin/data/DataParse.hx
index 64a53d2a4..cbd168a61 100644
--- a/source/funkin/data/DataParse.hx
+++ b/source/funkin/data/DataParse.hx
@@ -104,6 +104,22 @@ class DataParse
}
}
+ /**
+ * Parser which outputs a `Either>`.
+ */
+ public static function eitherFloatOrFloats(json:Json, name:String):Null>>
+ {
+ switch (json.value)
+ {
+ case JNumber(f):
+ return Either.Left(Std.parseFloat(f));
+ case JArray(fields):
+ return Either.Right(fields.map((field) -> cast Tools.getValue(field)));
+ default:
+ throw 'Expected property $name to be one or multiple floats, but it was ${json.value}.';
+ }
+ }
+
/**
* Parser which outputs a `Either`.
* Used by the FNF legacy JSON importer.
diff --git a/source/funkin/data/DataWrite.hx b/source/funkin/data/DataWrite.hx
index 2f3a7632f..e277cb01c 100644
--- a/source/funkin/data/DataWrite.hx
+++ b/source/funkin/data/DataWrite.hx
@@ -3,11 +3,14 @@ package funkin.data;
import funkin.util.SerializerUtil;
import thx.semver.Version;
import thx.semver.VersionRule;
+import haxe.ds.Either;
/**
* `json2object` has an annotation `@:jcustomwrite` which allows for custom serialization of values to be written to JSON.
*
* Functions must be of the signature `(T) -> String`, where `T` is the type of the property.
+ *
+ * NOTE: Result must include quotation marks if the value is a string! json2object will not add them for you!
*/
class DataWrite
{
@@ -23,11 +26,12 @@ class DataWrite
}
/**
+ *
* `@:jcustomwrite(funkin.data.DataWrite.semverVersion)`
*/
public static function semverVersion(value:Version):String
{
- return value.toString();
+ return '"${value.toString()}"';
}
/**
@@ -35,6 +39,22 @@ class DataWrite
*/
public static function semverVersionRule(value:VersionRule):String
{
- return value.toString();
+ return '"${value.toString()}"';
+ }
+
+ /**
+ * `@:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)`
+ */
+ public static function eitherFloatOrFloats(value:Null>>):String
+ {
+ switch (value)
+ {
+ case null:
+ return '${1.0}';
+ case Left(inner):
+ return '$inner';
+ case Right(inner):
+ return dynamicValue(inner);
+ }
}
}
diff --git a/source/funkin/data/animation/AnimationData.hx b/source/funkin/data/animation/AnimationData.hx
index 9765f784c..a0214096c 100644
--- a/source/funkin/data/animation/AnimationData.hx
+++ b/source/funkin/data/animation/AnimationData.hx
@@ -59,7 +59,10 @@ typedef UnnamedAnimationData =
* The prefix for the frames of the animation as defined by the XML file.
* This will may or may not differ from the `name` of the animation,
* depending on how your animator organized their FLA or whatever.
+ *
+ * NOTE: For Sparrow animations, this is not optional, but for Packer animations it is.
*/
+ @:optional
var prefix:String;
/**
diff --git a/source/funkin/data/event/SongEventData.hx b/source/funkin/data/event/SongEventData.hx
index 831a53fbd..7a167b031 100644
--- a/source/funkin/data/event/SongEventData.hx
+++ b/source/funkin/data/event/SongEventData.hx
@@ -208,25 +208,32 @@ typedef SongEventSchemaField =
type:SongEventFieldType,
/**
- * Used for ENUM values.
+ * Used only 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.
+ * @default No minimum
*/
?min:Float,
+
/**
* Used for INTEGER and FLOAT values.
* The maximum value that can be entered.
+ * @default No maximum
*/
?max:Float,
+
/**
* Used for INTEGER and FLOAT values.
* The step value that will be used when incrementing/decrementing the value.
+ * @default `0.1`
*/
?step:Float,
+
/**
* An optional default value for the field.
*/
diff --git a/source/funkin/data/notestyle/NoteStyleRegistry.hx b/source/funkin/data/notestyle/NoteStyleRegistry.hx
index da45da5f2..4255a644b 100644
--- a/source/funkin/data/notestyle/NoteStyleRegistry.hx
+++ b/source/funkin/data/notestyle/NoteStyleRegistry.hx
@@ -15,8 +15,6 @@ class NoteStyleRegistry extends BaseRegistry
public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
- public static final DEFAULT_NOTE_STYLE_ID:String = "funkin";
-
public static final instance:NoteStyleRegistry = new NoteStyleRegistry();
public function new()
@@ -26,7 +24,7 @@ class NoteStyleRegistry extends BaseRegistry
public function fetchDefault():NoteStyle
{
- return fetchEntry(DEFAULT_NOTE_STYLE_ID);
+ return fetchEntry(Constants.DEFAULT_NOTE_STYLE);
}
/**
diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx
index 783f52a64..e79e1a3f4 100644
--- a/source/funkin/data/song/SongData.hx
+++ b/source/funkin/data/song/SongData.hx
@@ -1,9 +1,13 @@
package funkin.data.song;
-import flixel.util.typeLimit.OneOfTwo;
import funkin.data.song.SongRegistry;
import thx.semver.Version;
+/**
+ * Data containing information about a song.
+ * It should contain all the data needed to display a song in the Freeplay menu, or to load the assets required to play its chart.
+ * Data which is only necessary in-game should be stored in the SongChartData.
+ */
@:nullSafety
class SongMetadata
{
@@ -35,13 +39,11 @@ class SongMetadata
*/
public var playData:SongPlayData;
- // @:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
+ @:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
public var generatedBy:String;
- // @:default(funkin.data.song.SongData.SongTimeFormat.MILLISECONDS)
public var timeFormat:SongTimeFormat;
- // @:default(funkin.data.song.SongData.SongTimeChange.DEFAULT_SONGTIMECHANGES)
public var timeChanges:Array;
/**
@@ -64,7 +66,7 @@ class SongMetadata
this.playData.difficulties = [];
this.playData.characters = new SongCharacterData('bf', 'gf', 'dad');
this.playData.stage = 'mainStage';
- this.playData.noteSkin = 'funkin';
+ this.playData.noteStyle = Constants.DEFAULT_NOTE_STYLE;
this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY;
// Variation ID.
this.variation = (variation == null) ? Constants.DEFAULT_VARIATION : variation;
@@ -298,23 +300,27 @@ class SongPlayData
/**
* The note style used by this song.
- * TODO: Rename to `noteStyle`? Renaming values is a breaking change to the metadata format.
*/
- public var noteSkin:String;
+ public var noteStyle:String;
/**
- * The difficulty rating for this song as displayed in Freeplay.
- * TODO: Adding this is a non-breaking change to the metadata format.
+ * The difficulty ratings for this song as displayed in Freeplay.
+ * Key is a difficulty ID or `default`.
*/
- // public var rating:Int;
+ @:default(['default' => 1])
+ public var ratings:Map;
/**
* The album ID for the album to display in Freeplay.
- * TODO: Adding this is a non-breaking change to the metadata format.
+ * If `null`, display no album.
*/
- // public var album:String;
+ @:optional
+ public var album:Null;
- public function new() {}
+ public function new()
+ {
+ ratings = new Map();
+ }
/**
* Produces a string representation suitable for debugging.
@@ -528,12 +534,22 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
public inline function getInt(key:String):Null
{
- return this.value == null ? null : cast Reflect.field(this.value, key);
+ if (this.value == null) return null;
+ var result = Reflect.field(this.value, key);
+ if (result == null) return null;
+ if (Std.isOfType(result, Int)) return result;
+ if (Std.isOfType(result, String)) return Std.parseInt(cast result);
+ return cast result;
}
public inline function getFloat(key:String):Null
{
- return this.value == null ? null : cast Reflect.field(this.value, key);
+ if (this.value == null) return null;
+ var result = Reflect.field(this.value, key);
+ if (result == null) return null;
+ if (Std.isOfType(result, Float)) return result;
+ if (Std.isOfType(result, String)) return Std.parseFloat(cast result);
+ return cast result;
}
public inline function getString(key:String):String
diff --git a/source/funkin/data/song/SongDataUtils.hx b/source/funkin/data/song/SongDataUtils.hx
index 984af18fa..b149a8855 100644
--- a/source/funkin/data/song/SongDataUtils.hx
+++ b/source/funkin/data/song/SongDataUtils.hx
@@ -3,6 +3,7 @@ package funkin.data.song;
import flixel.util.FlxSort;
import funkin.data.song.SongData.SongEventData;
import funkin.data.song.SongData.SongNoteData;
+import funkin.data.song.SongData.SongTimeChange;
import funkin.util.ClipboardUtil;
import funkin.util.SerializerUtil;
@@ -163,6 +164,18 @@ class SongDataUtils
return events;
}
+ /**
+ * Sort an array of notes by strum time.
+ */
+ public static function sortTimeChanges(timeChanges:Array, desc:Bool = false):Array
+ {
+ // TODO: Modifies the array in place. Is this okay?
+ timeChanges.sort(function(a:SongTimeChange, b:SongTimeChange):Int {
+ return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.timeStamp, b.timeStamp);
+ });
+ return timeChanges;
+ }
+
/**
* Serialize note and event data and write it to the clipboard.
*/
diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx
index 889fca707..8e0f4577d 100644
--- a/source/funkin/data/song/SongRegistry.hx
+++ b/source/funkin/data/song/SongRegistry.hx
@@ -2,6 +2,7 @@ package funkin.data.song;
import funkin.data.song.SongData;
import funkin.data.song.migrator.SongData_v2_0_0.SongMetadata_v2_0_0;
+import funkin.data.song.migrator.SongData_v2_1_0.SongMetadata_v2_1_0;
import funkin.data.song.SongData.SongChartData;
import funkin.data.song.SongData.SongMetadata;
import funkin.play.song.ScriptedSong;
@@ -18,9 +19,9 @@ class SongRegistry extends BaseRegistry
* Handle breaking changes by incrementing this value
* and adding migration to the `migrateStageData()` function.
*/
- public static final SONG_METADATA_VERSION:thx.semver.Version = "2.1.0";
+ public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.0";
- public static final SONG_METADATA_VERSION_RULE:thx.semver.VersionRule = "2.1.x";
+ public static final SONG_METADATA_VERSION_RULE:thx.semver.VersionRule = "2.2.x";
public static final SONG_CHART_DATA_VERSION:thx.semver.Version = "2.0.0";
@@ -165,6 +166,10 @@ class SongRegistry extends BaseRegistry
{
return parseEntryMetadata(id, variation);
}
+ else if (VersionUtil.validateVersion(version, "2.1.x"))
+ {
+ return parseEntryMetadata_v2_1_0(id, variation);
+ }
else if (VersionUtil.validateVersion(version, "2.0.x"))
{
return parseEntryMetadata_v2_0_0(id, variation);
@@ -182,6 +187,10 @@ class SongRegistry extends BaseRegistry
{
return parseEntryMetadataRaw(contents, fileName);
}
+ else if (VersionUtil.validateVersion(version, "2.1.x"))
+ {
+ return parseEntryMetadataRaw_v2_1_0(contents, fileName);
+ }
else if (VersionUtil.validateVersion(version, "2.0.x"))
{
return parseEntryMetadataRaw_v2_0_0(contents, fileName);
@@ -192,12 +201,12 @@ class SongRegistry extends BaseRegistry
}
}
- function parseEntryMetadata_v2_0_0(id:String, ?variation:String):Null
+ function parseEntryMetadata_v2_1_0(id:String, ?variation:String):Null
{
variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
- var parser = new json2object.JsonParser();
- switch (loadEntryMetadataFile(id))
+ var parser = new json2object.JsonParser();
+ switch (loadEntryMetadataFile(id, variation))
{
case {fileName: fileName, contents: contents}:
parser.fromJson(contents, fileName);
@@ -209,6 +218,39 @@ class SongRegistry extends BaseRegistry
printErrors(parser.errors, id);
return null;
}
+ return cleanMetadata(parser.value.migrate(), variation);
+ }
+
+ function parseEntryMetadata_v2_0_0(id:String, ?variation:String):Null
+ {
+ variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
+
+ var parser = new json2object.JsonParser();
+ switch (loadEntryMetadataFile(id, variation))
+ {
+ case {fileName: fileName, contents: contents}:
+ parser.fromJson(contents, fileName);
+ default:
+ return null;
+ }
+ if (parser.errors.length > 0)
+ {
+ printErrors(parser.errors, id);
+ return null;
+ }
+ return cleanMetadata(parser.value.migrate(), variation);
+ }
+
+ function parseEntryMetadataRaw_v2_1_0(contents:String, ?fileName:String = 'raw'):Null
+ {
+ var parser = new json2object.JsonParser();
+ parser.fromJson(contents, fileName);
+
+ if (parser.errors.length > 0)
+ {
+ printErrors(parser.errors, fileName);
+ return null;
+ }
return parser.value.migrate();
}
diff --git a/source/funkin/data/song/importer/ChartManifestData.hx b/source/funkin/data/song/importer/ChartManifestData.hx
new file mode 100644
index 000000000..0c7d2f0b0
--- /dev/null
+++ b/source/funkin/data/song/importer/ChartManifestData.hx
@@ -0,0 +1,84 @@
+package funkin.data.song.importer;
+
+/**
+ * A helper JSON blob found in `.fnfc` files.
+ */
+class ChartManifestData
+{
+ /**
+ * The current semantic version of the chart manifest data.
+ */
+ public static final CHART_MANIFEST_DATA_VERSION:thx.semver.Version = "1.0.0";
+
+ @:default(funkin.data.song.importer.ChartManifestData.CHART_MANIFEST_DATA_VERSION)
+ @:jcustomparse(funkin.data.DataParse.semverVersion)
+ @:jcustomwrite(funkin.data.DataWrite.semverVersion)
+ public var version:thx.semver.Version;
+
+ /**
+ * The internal song ID for this chart.
+ * The metadata and chart data file names are derived from this.
+ */
+ public var songId:String;
+
+ public function new(songId:String)
+ {
+ this.version = CHART_MANIFEST_DATA_VERSION;
+ this.songId = songId;
+ }
+
+ public function getMetadataFileName(?variation:String):String
+ {
+ if (variation == null || variation == '') variation = Constants.DEFAULT_VARIATION;
+
+ return '$songId-metadata${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_DATA}';
+ }
+
+ public function getChartDataFileName(?variation:String):String
+ {
+ if (variation == null || variation == '') variation = Constants.DEFAULT_VARIATION;
+
+ return '$songId-chart${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_DATA}';
+ }
+
+ public function getInstFileName(?variation:String):String
+ {
+ if (variation == null || variation == '') variation = Constants.DEFAULT_VARIATION;
+
+ return 'Inst${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_SOUND}';
+ }
+
+ public function getVocalsFileName(charId:String, ?variation:String):String
+ {
+ if (variation == null || variation == '') variation = Constants.DEFAULT_VARIATION;
+
+ return 'Voices-$charId${variation == Constants.DEFAULT_VARIATION ? '' : '-$variation'}.${Constants.EXT_SOUND}';
+ }
+
+ /**
+ * Serialize this ChartManifestData into a JSON string.
+ * @return The JSON string.
+ */
+ public function serialize(pretty:Bool = true):String
+ {
+ var writer = new json2object.JsonWriter();
+ return writer.write(this, pretty ? ' ' : null);
+ }
+
+ public static function deserialize(contents:String):Null
+ {
+ var parser = new json2object.JsonParser();
+ parser.fromJson(contents, 'manifest.json');
+
+ if (parser.errors.length > 0)
+ {
+ trace('[ChartManifest] Failed to parse chart file manifest');
+
+ for (error in parser.errors)
+ DataError.printError(error);
+
+ return null;
+ }
+ return parser.value;
+ }
+}
diff --git a/source/funkin/data/song/migrator/SongDataMigrator.hx b/source/funkin/data/song/migrator/SongDataMigrator.hx
index b5e08c832..2603ab1f8 100644
--- a/source/funkin/data/song/migrator/SongDataMigrator.hx
+++ b/source/funkin/data/song/migrator/SongDataMigrator.hx
@@ -7,6 +7,8 @@ import funkin.data.song.migrator.SongData_v2_0_0.SongMetadata_v2_0_0;
import funkin.data.song.migrator.SongData_v2_0_0.SongPlayData_v2_0_0;
import funkin.data.song.migrator.SongData_v2_0_0.SongPlayableChar_v2_0_0;
+using funkin.data.song.migrator.SongDataMigrator; // Does this even work lol?
+
/**
* This class contains functions to migrate older data formats to the current one.
*
@@ -15,6 +17,48 @@ import funkin.data.song.migrator.SongData_v2_0_0.SongPlayableChar_v2_0_0;
*/
class SongDataMigrator
{
+ public static overload extern inline function migrate(input:SongData_v2_1_0.SongMetadata_v2_1_0):SongMetadata
+ {
+ return migrate_SongMetadata_v2_1_0(input);
+ }
+
+ public static function migrate_SongMetadata_v2_1_0(input:SongData_v2_1_0.SongMetadata_v2_1_0):SongMetadata
+ {
+ var result:SongMetadata = new SongMetadata(input.songName, input.artist, input.variation);
+ result.version = SongRegistry.SONG_METADATA_VERSION;
+ result.timeFormat = input.timeFormat;
+ result.divisions = input.divisions;
+ result.timeChanges = input.timeChanges;
+ result.looped = input.looped;
+ result.playData = input.playData.migrate();
+ result.generatedBy = input.generatedBy;
+
+ return result;
+ }
+
+ public static overload extern inline function migrate(input:SongData_v2_1_0.SongPlayData_v2_1_0):SongPlayData
+ {
+ return migrate_SongPlayData_v2_1_0(input);
+ }
+
+ public static function migrate_SongPlayData_v2_1_0(input:SongData_v2_1_0.SongPlayData_v2_1_0):SongPlayData
+ {
+ var result:SongPlayData = new SongPlayData();
+ result.songVariations = input.songVariations;
+ result.difficulties = input.difficulties;
+ result.stage = input.stage;
+ result.characters = input.characters;
+
+ // Renamed
+ result.noteStyle = input.noteSkin;
+
+ // Added
+ result.ratings = ['default' => 1];
+ result.album = null;
+
+ return result;
+ }
+
public static overload extern inline function migrate(input:SongData_v2_0_0.SongMetadata_v2_0_0):SongMetadata
{
return migrate_SongMetadata_v2_0_0(input);
@@ -23,12 +67,12 @@ class SongDataMigrator
public static function migrate_SongMetadata_v2_0_0(input:SongData_v2_0_0.SongMetadata_v2_0_0):SongMetadata
{
var result:SongMetadata = new SongMetadata(input.songName, input.artist, input.variation);
- result.version = input.version;
+ result.version = SongRegistry.SONG_METADATA_VERSION;
result.timeFormat = input.timeFormat;
result.divisions = input.divisions;
result.timeChanges = input.timeChanges;
result.looped = input.looped;
- result.playData = migrate_SongPlayData_v2_0_0(input.playData);
+ result.playData = input.playData.migrate();
result.generatedBy = input.generatedBy;
return result;
@@ -45,7 +89,13 @@ class SongDataMigrator
result.songVariations = input.songVariations;
result.difficulties = input.difficulties;
result.stage = input.stage;
- result.noteSkin = input.noteSkin;
+
+ // Added
+ result.ratings = ['default' => 1];
+ result.album = null;
+
+ // Renamed
+ result.noteStyle = input.noteSkin;
// Fetch the first playable character and migrate it.
var firstCharKey:Null = input.playableChars.size() == 0 ? null : input.playableChars.keys().array()[0];
diff --git a/source/funkin/data/song/migrator/SongData_v2_0_0.hx b/source/funkin/data/song/migrator/SongData_v2_0_0.hx
index eeeed2f2b..62e3faf4c 100644
--- a/source/funkin/data/song/migrator/SongData_v2_0_0.hx
+++ b/source/funkin/data/song/migrator/SongData_v2_0_0.hx
@@ -42,6 +42,7 @@ class SongMetadata_v2_0_0
@:default(false)
public var looped:Bool;
+ @:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
public var generatedBy:String;
public var timeFormat:SongData.SongTimeFormat;
@@ -70,6 +71,13 @@ class SongPlayData_v2_0_0
*/
public var playableChars:Map;
+ /**
+ * In metadata version `v2.2.0`, this was renamed to `noteStyle`.
+ */
+ public var noteSkin:String;
+
+ // In 2.2.0, the ratings value was added.
+ // In 2.2.0, the album value was added.
// ==========
// UNMODIFIED VALUES
// ==========
@@ -77,7 +85,6 @@ class SongPlayData_v2_0_0
public var difficulties:Array;
public var stage:String;
- public var noteSkin:String;
public function new() {}
diff --git a/source/funkin/data/song/migrator/SongData_v2_1_0.hx b/source/funkin/data/song/migrator/SongData_v2_1_0.hx
new file mode 100644
index 000000000..57e4102d9
--- /dev/null
+++ b/source/funkin/data/song/migrator/SongData_v2_1_0.hx
@@ -0,0 +1,108 @@
+package funkin.data.song.migrator;
+
+import funkin.data.song.SongData;
+import funkin.data.song.SongRegistry;
+import thx.semver.Version;
+
+@:nullSafety
+class SongMetadata_v2_1_0
+{
+ // ==========
+ // MODIFIED VALUES
+ // ===========
+
+ /**
+ * In metadata `v2.2.0`, `SongPlayData` was refactored.
+ */
+ public var playData:SongPlayData_v2_1_0;
+
+ // ==========
+ // UNMODIFIED VALUES
+ // ==========
+ @:jcustomparse(funkin.data.DataParse.semverVersion)
+ @:jcustomwrite(funkin.data.DataWrite.semverVersion)
+ public var version:Version;
+
+ @:default("Unknown")
+ public var songName:String;
+
+ @:default("Unknown")
+ public var artist:String;
+
+ @:optional
+ @:default(96)
+ public var divisions:Null; // Optional field
+
+ @:optional
+ @:default(false)
+ public var looped:Bool;
+
+ @:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
+ public var generatedBy:String;
+
+ public var timeFormat:SongData.SongTimeFormat;
+
+ public var timeChanges:Array;
+
+ /**
+ * Defaults to `Constants.DEFAULT_VARIATION`. Populated later.
+ */
+ @:jignored
+ public var variation:String;
+
+ public function new(songName:String, artist:String, ?variation:String)
+ {
+ this.version = SongRegistry.SONG_METADATA_VERSION;
+ this.songName = songName;
+ this.artist = artist;
+ this.timeFormat = 'ms';
+ this.divisions = null;
+ this.timeChanges = [new SongTimeChange(0, 100)];
+ this.looped = false;
+ this.playData = new SongPlayData_v2_1_0();
+ this.playData.songVariations = [];
+ this.playData.difficulties = [];
+ this.playData.characters = new SongCharacterData('bf', 'gf', 'dad');
+ this.playData.stage = 'mainStage';
+ this.playData.noteSkin = 'funkin';
+ this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY;
+ // Variation ID.
+ this.variation = (variation == null) ? Constants.DEFAULT_VARIATION : variation;
+ }
+
+ /**
+ * Produces a string representation suitable for debugging.
+ */
+ public function toString():String
+ {
+ return 'SongMetadata[LEGACY:v2.1.0](${this.songName} by ${this.artist}, variation ${this.variation})';
+ }
+}
+
+class SongPlayData_v2_1_0
+{
+ /**
+ * In `v2.2.0`, this value was renamed to `noteStyle`.
+ */
+ public var noteSkin:String;
+
+ // In 2.2.0, the ratings value was added.
+ // In 2.2.0, the album value was added.
+ // ==========
+ // UNMODIFIED VALUES
+ // ==========
+ public var songVariations:Array;
+ public var difficulties:Array;
+ public var characters:SongData.SongCharacterData;
+ public var stage:String;
+
+ public function new() {}
+
+ /**
+ * Produces a string representation suitable for debugging.
+ */
+ public function toString():String
+ {
+ return 'SongPlayData[LEGACY:v2.1.0](${this.songVariations}, ${this.difficulties})';
+ }
+}
diff --git a/source/funkin/shaderslmfao/AngleMask.hx b/source/funkin/graphics/shaders/AngleMask.hx
similarity index 96%
rename from source/funkin/shaderslmfao/AngleMask.hx
rename to source/funkin/graphics/shaders/AngleMask.hx
index b9188201b..30e508a58 100644
--- a/source/funkin/shaderslmfao/AngleMask.hx
+++ b/source/funkin/graphics/shaders/AngleMask.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.system.FlxAssets.FlxShader;
diff --git a/source/funkin/shaderslmfao/BlendModeEffect.hx b/source/funkin/graphics/shaders/BlendModeEffect.hx
similarity index 95%
rename from source/funkin/shaderslmfao/BlendModeEffect.hx
rename to source/funkin/graphics/shaders/BlendModeEffect.hx
index 8fe98f70a..bf2246795 100644
--- a/source/funkin/shaderslmfao/BlendModeEffect.hx
+++ b/source/funkin/graphics/shaders/BlendModeEffect.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.util.FlxColor;
import openfl.display.ShaderParameter;
diff --git a/source/funkin/shaderslmfao/BlendModesShader.hx b/source/funkin/graphics/shaders/BlendModesShader.hx
similarity index 92%
rename from source/funkin/shaderslmfao/BlendModesShader.hx
rename to source/funkin/graphics/shaders/BlendModesShader.hx
index 6807a65c0..acd2c1586 100644
--- a/source/funkin/shaderslmfao/BlendModesShader.hx
+++ b/source/funkin/graphics/shaders/BlendModesShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
diff --git a/source/funkin/shaderslmfao/ColorSwap.hx b/source/funkin/graphics/shaders/ColorSwap.hx
similarity index 99%
rename from source/funkin/shaderslmfao/ColorSwap.hx
rename to source/funkin/graphics/shaders/ColorSwap.hx
index 2c1f5664b..1be4d5429 100644
--- a/source/funkin/shaderslmfao/ColorSwap.hx
+++ b/source/funkin/graphics/shaders/ColorSwap.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxColor;
diff --git a/source/funkin/shaderslmfao/GaussianBlurShader.hx b/source/funkin/graphics/shaders/GaussianBlurShader.hx
similarity index 93%
rename from source/funkin/shaderslmfao/GaussianBlurShader.hx
rename to source/funkin/graphics/shaders/GaussianBlurShader.hx
index ad472ac31..81167655b 100644
--- a/source/funkin/shaderslmfao/GaussianBlurShader.hx
+++ b/source/funkin/graphics/shaders/GaussianBlurShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
diff --git a/source/funkin/shaderslmfao/Grayscale.hx b/source/funkin/graphics/shaders/Grayscale.hx
similarity index 92%
rename from source/funkin/shaderslmfao/Grayscale.hx
rename to source/funkin/graphics/shaders/Grayscale.hx
index 016d64b46..6673ace24 100644
--- a/source/funkin/shaderslmfao/Grayscale.hx
+++ b/source/funkin/graphics/shaders/Grayscale.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
diff --git a/source/funkin/shaderslmfao/HSVShader.hx b/source/funkin/graphics/shaders/HSVShader.hx
similarity index 96%
rename from source/funkin/shaderslmfao/HSVShader.hx
rename to source/funkin/graphics/shaders/HSVShader.hx
index 066a49c96..733bbca7f 100644
--- a/source/funkin/shaderslmfao/HSVShader.hx
+++ b/source/funkin/graphics/shaders/HSVShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
diff --git a/source/funkin/shaderslmfao/LeftMaskShader.hx b/source/funkin/graphics/shaders/LeftMaskShader.hx
similarity index 97%
rename from source/funkin/shaderslmfao/LeftMaskShader.hx
rename to source/funkin/graphics/shaders/LeftMaskShader.hx
index e921a7f2b..f82a5c208 100644
--- a/source/funkin/shaderslmfao/LeftMaskShader.hx
+++ b/source/funkin/graphics/shaders/LeftMaskShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.math.FlxRect;
import flixel.system.FlxAssets.FlxShader;
diff --git a/source/funkin/shaderslmfao/MultiplyShader.hx b/source/funkin/graphics/shaders/MultiplyShader.hx
similarity index 94%
rename from source/funkin/shaderslmfao/MultiplyShader.hx
rename to source/funkin/graphics/shaders/MultiplyShader.hx
index 2868982a2..5fe95f04e 100644
--- a/source/funkin/shaderslmfao/MultiplyShader.hx
+++ b/source/funkin/graphics/shaders/MultiplyShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.system.FlxAssets.FlxShader;
diff --git a/source/funkin/shaderslmfao/OverlayBlend.hx b/source/funkin/graphics/shaders/OverlayBlend.hx
similarity index 97%
rename from source/funkin/shaderslmfao/OverlayBlend.hx
rename to source/funkin/graphics/shaders/OverlayBlend.hx
index 8845a3b55..e44f3152a 100644
--- a/source/funkin/shaderslmfao/OverlayBlend.hx
+++ b/source/funkin/graphics/shaders/OverlayBlend.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.math.FlxPoint;
import flixel.system.FlxAssets.FlxShader;
diff --git a/source/funkin/shaderslmfao/PureColor.hx b/source/funkin/graphics/shaders/PureColor.hx
similarity index 96%
rename from source/funkin/shaderslmfao/PureColor.hx
rename to source/funkin/graphics/shaders/PureColor.hx
index 767a29d0d..1d2216a8c 100644
--- a/source/funkin/shaderslmfao/PureColor.hx
+++ b/source/funkin/graphics/shaders/PureColor.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxColor;
diff --git a/source/funkin/shaderslmfao/ScreenWipeShader.hx b/source/funkin/graphics/shaders/ScreenWipeShader.hx
similarity index 98%
rename from source/funkin/shaderslmfao/ScreenWipeShader.hx
rename to source/funkin/graphics/shaders/ScreenWipeShader.hx
index 1aeb069ba..bc45f0ef6 100644
--- a/source/funkin/shaderslmfao/ScreenWipeShader.hx
+++ b/source/funkin/graphics/shaders/ScreenWipeShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.system.FlxAssets.FlxShader;
diff --git a/source/funkin/shaderslmfao/StrokeShader.hx b/source/funkin/graphics/shaders/StrokeShader.hx
similarity index 98%
rename from source/funkin/shaderslmfao/StrokeShader.hx
rename to source/funkin/graphics/shaders/StrokeShader.hx
index 38dc41636..fd133ac0a 100644
--- a/source/funkin/shaderslmfao/StrokeShader.hx
+++ b/source/funkin/graphics/shaders/StrokeShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxColor;
diff --git a/source/funkin/shaderslmfao/TitleOutline.hx b/source/funkin/graphics/shaders/TitleOutline.hx
similarity index 98%
rename from source/funkin/shaderslmfao/TitleOutline.hx
rename to source/funkin/graphics/shaders/TitleOutline.hx
index 9a849f795..db60fc3ae 100644
--- a/source/funkin/shaderslmfao/TitleOutline.hx
+++ b/source/funkin/graphics/shaders/TitleOutline.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.math.FlxPoint;
import flixel.system.FlxAssets.FlxShader;
diff --git a/source/funkin/shaderslmfao/WaveShader.hx b/source/funkin/graphics/shaders/WaveShader.hx
similarity index 90%
rename from source/funkin/shaderslmfao/WaveShader.hx
rename to source/funkin/graphics/shaders/WaveShader.hx
index 89171b089..8738cc405 100644
--- a/source/funkin/shaderslmfao/WaveShader.hx
+++ b/source/funkin/graphics/shaders/WaveShader.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.system.FlxAssets.FlxShader;
diff --git a/source/funkin/shaderslmfao/WiggleEffectRuntime.hx b/source/funkin/graphics/shaders/WiggleEffectRuntime.hx
similarity index 98%
rename from source/funkin/shaderslmfao/WiggleEffectRuntime.hx
rename to source/funkin/graphics/shaders/WiggleEffectRuntime.hx
index 23f93b672..a04941df5 100644
--- a/source/funkin/shaderslmfao/WiggleEffectRuntime.hx
+++ b/source/funkin/graphics/shaders/WiggleEffectRuntime.hx
@@ -1,4 +1,4 @@
-package funkin.shaderslmfao;
+package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import openfl.Assets;
diff --git a/source/funkin/import.hx b/source/funkin/import.hx
index 8c7124da0..5ca6b03db 100644
--- a/source/funkin/import.hx
+++ b/source/funkin/import.hx
@@ -12,6 +12,7 @@ using Lambda;
using StringTools;
using funkin.util.tools.ArraySortTools;
using funkin.util.tools.ArrayTools;
+using funkin.util.tools.DynamicTools;
using funkin.util.tools.FloatTools;
using funkin.util.tools.Int64Tools;
using funkin.util.tools.IntTools;
diff --git a/source/funkin/input/Controls.hx b/source/funkin/input/Controls.hx
index 7f8e399a2..d74502e6f 100644
--- a/source/funkin/input/Controls.hx
+++ b/source/funkin/input/Controls.hx
@@ -22,96 +22,14 @@ import flixel.util.FlxTimer;
import lime.ui.Haptic;
/**
- * Since, in many cases multiple actions should use similar keys, we don't want the
- * rebinding UI to list every action. ActionBinders are what the user percieves as
- * an input so, for instance, they can't set jump-press and jump-release to different keys.
- */
-enum Control
-{
- // List notes in order from left to right on gameplay screen.
- NOTE_LEFT;
- NOTE_DOWN;
- NOTE_UP;
- NOTE_RIGHT;
- UI_UP;
- UI_LEFT;
- UI_RIGHT;
- UI_DOWN;
- RESET;
- ACCEPT;
- BACK;
- PAUSE;
- CUTSCENE_ADVANCE;
- CUTSCENE_SKIP;
- VOLUME_UP;
- VOLUME_DOWN;
- VOLUME_MUTE;
- #if CAN_CHEAT
- CHEAT;
- #end
-}
-
-enum
-abstract Action(String) to String from String
-{
- var UI_UP = "ui_up";
- var UI_LEFT = "ui_left";
- var UI_RIGHT = "ui_right";
- var UI_DOWN = "ui_down";
- var UI_UP_P = "ui_up-press";
- var UI_LEFT_P = "ui_left-press";
- var UI_RIGHT_P = "ui_right-press";
- var UI_DOWN_P = "ui_down-press";
- var UI_UP_R = "ui_up-release";
- var UI_LEFT_R = "ui_left-release";
- var UI_RIGHT_R = "ui_right-release";
- var UI_DOWN_R = "ui_down-release";
- var NOTE_UP = "note_up";
- var NOTE_LEFT = "note_left";
- var NOTE_RIGHT = "note_right";
- var NOTE_DOWN = "note_down";
- var NOTE_UP_P = "note_up-press";
- var NOTE_LEFT_P = "note_left-press";
- var NOTE_RIGHT_P = "note_right-press";
- var NOTE_DOWN_P = "note_down-press";
- var NOTE_UP_R = "note_up-release";
- var NOTE_LEFT_R = "note_left-release";
- var NOTE_RIGHT_R = "note_right-release";
- var NOTE_DOWN_R = "note_down-release";
- var ACCEPT = "accept";
- var BACK = "back";
- var PAUSE = "pause";
- var CUTSCENE_ADVANCE = "cutscene_advance";
- var CUTSCENE_SKIP = "cutscene_skip";
- var VOLUME_UP = "volume_up";
- var VOLUME_DOWN = "volume_down";
- var VOLUME_MUTE = "volume_mute";
- var RESET = "reset";
- #if CAN_CHEAT
- var CHEAT = "cheat";
- #end
-}
-
-enum Device
-{
- Keys;
- Gamepad(id:Int);
-}
-
-enum KeyboardScheme
-{
- Solo;
- Duo(first:Bool);
- None;
- Custom;
-}
-
-/**
- * A list of actions that a player would invoke via some input device.
- * Uses FlxActions to funnel various inputs to a single action.
+ * A core class which handles receiving player input and interpreting it into game actions.
*/
class Controls extends FlxActionSet
{
+ /**
+ * A list of actions that a player would invoke via some input device.
+ * Uses FlxActions to funnel various inputs to a single action.
+ */
var _ui_up = new FlxActionDigital(Action.UI_UP);
var _ui_left = new FlxActionDigital(Action.UI_LEFT);
var _ui_right = new FlxActionDigital(Action.UI_RIGHT);
@@ -1240,3 +1158,88 @@ class FlxActionInputDigitalAndroid extends FlxActionInputDigital
}
}
#end
+
+/**
+ * Since, in many cases multiple actions should use similar keys, we don't want the
+ * rebinding UI to list every action. ActionBinders are what the user percieves as
+ * an input so, for instance, they can't set jump-press and jump-release to different keys.
+ */
+enum Control
+{
+ // List notes in order from left to right on gameplay screen.
+ NOTE_LEFT;
+ NOTE_DOWN;
+ NOTE_UP;
+ NOTE_RIGHT;
+ UI_UP;
+ UI_LEFT;
+ UI_RIGHT;
+ UI_DOWN;
+ RESET;
+ ACCEPT;
+ BACK;
+ PAUSE;
+ CUTSCENE_ADVANCE;
+ CUTSCENE_SKIP;
+ VOLUME_UP;
+ VOLUME_DOWN;
+ VOLUME_MUTE;
+ #if CAN_CHEAT
+ CHEAT;
+ #end
+}
+
+enum
+abstract Action(String) to String from String
+{
+ var UI_UP = "ui_up";
+ var UI_LEFT = "ui_left";
+ var UI_RIGHT = "ui_right";
+ var UI_DOWN = "ui_down";
+ var UI_UP_P = "ui_up-press";
+ var UI_LEFT_P = "ui_left-press";
+ var UI_RIGHT_P = "ui_right-press";
+ var UI_DOWN_P = "ui_down-press";
+ var UI_UP_R = "ui_up-release";
+ var UI_LEFT_R = "ui_left-release";
+ var UI_RIGHT_R = "ui_right-release";
+ var UI_DOWN_R = "ui_down-release";
+ var NOTE_UP = "note_up";
+ var NOTE_LEFT = "note_left";
+ var NOTE_RIGHT = "note_right";
+ var NOTE_DOWN = "note_down";
+ var NOTE_UP_P = "note_up-press";
+ var NOTE_LEFT_P = "note_left-press";
+ var NOTE_RIGHT_P = "note_right-press";
+ var NOTE_DOWN_P = "note_down-press";
+ var NOTE_UP_R = "note_up-release";
+ var NOTE_LEFT_R = "note_left-release";
+ var NOTE_RIGHT_R = "note_right-release";
+ var NOTE_DOWN_R = "note_down-release";
+ var ACCEPT = "accept";
+ var BACK = "back";
+ var PAUSE = "pause";
+ var CUTSCENE_ADVANCE = "cutscene_advance";
+ var CUTSCENE_SKIP = "cutscene_skip";
+ var VOLUME_UP = "volume_up";
+ var VOLUME_DOWN = "volume_down";
+ var VOLUME_MUTE = "volume_mute";
+ var RESET = "reset";
+ #if CAN_CHEAT
+ var CHEAT = "cheat";
+ #end
+}
+
+enum Device
+{
+ Keys;
+ Gamepad(id:Int);
+}
+
+enum KeyboardScheme
+{
+ Solo;
+ Duo(first:Bool);
+ None;
+ Custom;
+}
diff --git a/source/funkin/input/Cursor.hx b/source/funkin/input/Cursor.hx
index edd9e70f3..c609c9e30 100644
--- a/source/funkin/input/Cursor.hx
+++ b/source/funkin/input/Cursor.hx
@@ -1,5 +1,6 @@
package funkin.input;
+import haxe.ui.backend.flixel.CursorHelper;
import openfl.utils.Assets;
import lime.app.Future;
import openfl.display.BitmapData;
@@ -33,7 +34,7 @@ class Cursor
Cursor.cursorMode = null;
}
- static final CURSOR_DEFAULT_PARAMS:CursorParams =
+ public static final CURSOR_DEFAULT_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-default.png",
scale: 1.0,
@@ -42,7 +43,7 @@ class Cursor
};
static var assetCursorDefault:Null = null;
- static final CURSOR_CROSS_PARAMS:CursorParams =
+ public static final CURSOR_CROSS_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-cross.png",
scale: 1.0,
@@ -51,7 +52,7 @@ class Cursor
};
static var assetCursorCross:Null = null;
- static final CURSOR_ERASER_PARAMS:CursorParams =
+ public static final CURSOR_ERASER_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-eraser.png",
scale: 1.0,
@@ -60,7 +61,7 @@ class Cursor
};
static var assetCursorEraser:Null = null;
- static final CURSOR_GRABBING_PARAMS:CursorParams =
+ public static final CURSOR_GRABBING_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-grabbing.png",
scale: 1.0,
@@ -69,7 +70,7 @@ class Cursor
};
static var assetCursorGrabbing:Null = null;
- static final CURSOR_HOURGLASS_PARAMS:CursorParams =
+ public static final CURSOR_HOURGLASS_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-hourglass.png",
scale: 1.0,
@@ -78,7 +79,7 @@ class Cursor
};
static var assetCursorHourglass:Null = null;
- static final CURSOR_POINTER_PARAMS:CursorParams =
+ public static final CURSOR_POINTER_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-pointer.png",
scale: 1.0,
@@ -87,7 +88,7 @@ class Cursor
};
static var assetCursorPointer:Null = null;
- static final CURSOR_TEXT_PARAMS:CursorParams =
+ public static final CURSOR_TEXT_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-text.png",
scale: 0.2,
@@ -96,7 +97,7 @@ class Cursor
};
static var assetCursorText:Null = null;
- static final CURSOR_TEXT_VERTICAL_PARAMS:CursorParams =
+ public static final CURSOR_TEXT_VERTICAL_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-text-vertical.png",
scale: 0.2,
@@ -105,7 +106,7 @@ class Cursor
};
static var assetCursorTextVertical:Null = null;
- static final CURSOR_ZOOM_IN_PARAMS:CursorParams =
+ public static final CURSOR_ZOOM_IN_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-zoom-in.png",
scale: 1.0,
@@ -114,7 +115,7 @@ class Cursor
};
static var assetCursorZoomIn:Null = null;
- static final CURSOR_ZOOM_OUT_PARAMS:CursorParams =
+ public static final CURSOR_ZOOM_OUT_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-zoom-out.png",
scale: 1.0,
@@ -123,7 +124,7 @@ class Cursor
};
static var assetCursorZoomOut:Null = null;
- static final CURSOR_CROSSHAIR_PARAMS:CursorParams =
+ public static final CURSOR_CROSSHAIR_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-crosshair.png",
scale: 1.0,
@@ -132,7 +133,7 @@ class Cursor
};
static var assetCursorCrosshair:Null = null;
- static final CURSOR_CELL_PARAMS:CursorParams =
+ public static final CURSOR_CELL_PARAMS:CursorParams =
{
graphic: "assets/images/cursor/cursor-cell.png",
scale: 1.0,
@@ -500,6 +501,28 @@ class Cursor
{
trace("Failed to load cursor graphic for cursor mode " + cursorMode + ": " + error);
}
+
+ public static function registerHaxeUICursors():Void
+ {
+ CursorHelper.useCustomCursors = true;
+ registerHaxeUICursor('default', CURSOR_DEFAULT_PARAMS);
+ registerHaxeUICursor('cross', CURSOR_CROSS_PARAMS);
+ registerHaxeUICursor('eraser', CURSOR_ERASER_PARAMS);
+ registerHaxeUICursor('grabbing', CURSOR_GRABBING_PARAMS);
+ registerHaxeUICursor('hourglass', CURSOR_HOURGLASS_PARAMS);
+ registerHaxeUICursor('pointer', CURSOR_POINTER_PARAMS);
+ registerHaxeUICursor('text', CURSOR_TEXT_PARAMS);
+ registerHaxeUICursor('text-vertical', CURSOR_TEXT_VERTICAL_PARAMS);
+ registerHaxeUICursor('zoom-in', CURSOR_ZOOM_IN_PARAMS);
+ registerHaxeUICursor('zoom-out', CURSOR_ZOOM_OUT_PARAMS);
+ registerHaxeUICursor('crosshair', CURSOR_CROSSHAIR_PARAMS);
+ registerHaxeUICursor('cell', CURSOR_CELL_PARAMS);
+ }
+
+ public static function registerHaxeUICursor(id:String, params:CursorParams):Void
+ {
+ CursorHelper.registerCursor(id, params.graphic, params.scale, params.offsetX, params.offsetY);
+ }
}
// https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
diff --git a/source/funkin/input/TurboKeyHandler.hx b/source/funkin/input/TurboKeyHandler.hx
index 099d373b4..36b8e2087 100644
--- a/source/funkin/input/TurboKeyHandler.hx
+++ b/source/funkin/input/TurboKeyHandler.hx
@@ -108,8 +108,7 @@ class TurboKeyHandler extends FlxBasic
* @param repeatDelay How long to wait between repeats.
* @return A TurboKeyHandler
*/
- public static overload inline extern function build(inputKeys:Array, ?delay:Float = DEFAULT_DELAY,
- ?interval:Float = DEFAULT_INTERVAL):TurboKeyHandler
+ public static overload inline extern function build(inputKeys:Array, ?delay:Float = DEFAULT_DELAY, ?interval:Float = DEFAULT_INTERVAL):TurboKeyHandler
{
return new TurboKeyHandler(inputKeys, delay, interval);
}
diff --git a/source/funkin/modding/base/ScriptedMusicBeatState.hx b/source/funkin/modding/base/ScriptedMusicBeatState.hx
index 782e4d0b8..6dc6826c4 100644
--- a/source/funkin/modding/base/ScriptedMusicBeatState.hx
+++ b/source/funkin/modding/base/ScriptedMusicBeatState.hx
@@ -5,4 +5,4 @@ package funkin.modding.base;
* Create a scripted class that extends MusicBeatState to use this.
*/
@:hscriptClass
-class ScriptedMusicBeatState extends funkin.MusicBeatState implements HScriptedClass {}
+class ScriptedMusicBeatState extends funkin.ui.MusicBeatState implements HScriptedClass {}
diff --git a/source/funkin/modding/base/ScriptedMusicBeatSubState.hx b/source/funkin/modding/base/ScriptedMusicBeatSubState.hx
index 7dab3d7dd..79c98ea3f 100644
--- a/source/funkin/modding/base/ScriptedMusicBeatSubState.hx
+++ b/source/funkin/modding/base/ScriptedMusicBeatSubState.hx
@@ -5,4 +5,4 @@ package funkin.modding.base;
* Create a scripted class that extends MusicBeatSubState to use this.
*/
@:hscriptClass
-class ScriptedMusicBeatSubState extends funkin.MusicBeatSubState implements HScriptedClass {}
+class ScriptedMusicBeatSubState extends funkin.ui.MusicBeatSubState implements HScriptedClass {}
diff --git a/source/funkin/play/Fighter.hx b/source/funkin/play/Fighter.hx
deleted file mode 100644
index 691d21b83..000000000
--- a/source/funkin/play/Fighter.hx
+++ /dev/null
@@ -1,68 +0,0 @@
-package funkin.play;
-
-import funkin.play.character.BaseCharacter;
-import flixel.FlxSprite;
-
-class Fighter extends BaseCharacter
-{
- public function new(?x:Float = 0, ?y:Float = 0, ?char:String = "pico-fighter")
- {
- super(char, Custom);
- this.x = x;
- this.y = y;
-
- animation.finishCallback = function(anim:String) {
- switch anim
- {
- case "punch low" | "punch high" | "block" | 'dodge':
- dance(true);
- }
- };
- }
-
- public var actions:Array = [PUNCH, BLOCK, DODGE];
-
- public function doSomething(?forceAction:ACTIONS)
- {
- var daAction:ACTIONS = FlxG.random.getObject(actions);
-
- if (forceAction != null) daAction = forceAction;
-
- switch (daAction)
- {
- case PUNCH:
- punch();
- case BLOCK:
- block();
- case DODGE:
- dodge();
- }
- }
-
- public var curAction:ACTIONS = DODGE;
-
- function dodge()
- {
- playAnimation('dodge');
- curAction = DODGE;
- }
-
- public function block()
- {
- playAnimation('block');
- curAction = BLOCK;
- }
-
- public function punch()
- {
- curAction = PUNCH;
- playAnimation('punch ' + (FlxG.random.bool() ? "low" : "high"));
- }
-}
-
-enum ACTIONS
-{
- DODGE;
- BLOCK;
- PUNCH;
-}
diff --git a/source/funkin/play/GameOverSubState.hx b/source/funkin/play/GameOverSubState.hx
index c5d9b4b0b..6eb53e2d5 100644
--- a/source/funkin/play/GameOverSubState.hx
+++ b/source/funkin/play/GameOverSubState.hx
@@ -7,9 +7,11 @@ import flixel.sound.FlxSound;
import funkin.ui.story.StoryMenuState;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
+import funkin.ui.MusicBeatSubState;
import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.play.PlayState;
+import funkin.ui.freeplay.FreeplayState;
import funkin.play.character.BaseCharacter;
/**
diff --git a/source/funkin/GitarooPause.hx b/source/funkin/play/GitarooPause.hx
similarity index 96%
rename from source/funkin/GitarooPause.hx
rename to source/funkin/play/GitarooPause.hx
index a4dc766be..dbfbf5961 100644
--- a/source/funkin/GitarooPause.hx
+++ b/source/funkin/play/GitarooPause.hx
@@ -1,9 +1,11 @@
-package funkin;
+package funkin.play;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import funkin.play.PlayState;
+import funkin.ui.MusicBeatState;
import flixel.addons.transition.FlxTransitionableState;
+import funkin.ui.mainmenu.MainMenuState;
class GitarooPause extends MusicBeatState
{
diff --git a/source/funkin/PauseSubState.hx b/source/funkin/play/PauseSubState.hx
similarity index 97%
rename from source/funkin/PauseSubState.hx
rename to source/funkin/play/PauseSubState.hx
index 2ce9abf65..f5555b66e 100644
--- a/source/funkin/PauseSubState.hx
+++ b/source/funkin/play/PauseSubState.hx
@@ -1,9 +1,10 @@
-package funkin;
+package funkin.play;
import funkin.play.PlayStatePlaylist;
import flixel.FlxSprite;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
+import funkin.ui.MusicBeatSubState;
import flixel.sound.FlxSound;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
@@ -11,6 +12,7 @@ import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import funkin.play.PlayState;
import funkin.data.song.SongRegistry;
+import funkin.ui.Alphabet;
class PauseSubState extends MusicBeatSubState
{
@@ -231,11 +233,11 @@ class PauseSubState extends MusicBeatSubState
if (PlayStatePlaylist.isStoryMode)
{
- openSubState(new funkin.ui.StickerSubState(null, STORY));
+ openSubState(new funkin.ui.transition.StickerSubState(null, STORY));
}
else
{
- openSubState(new funkin.ui.StickerSubState(null, FREEPLAY));
+ openSubState(new funkin.ui.transition.StickerSubState(null, FREEPLAY));
}
case 'Exit to Chart Editor':
diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx
index 214257fd5..cce424b06 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -1,5 +1,6 @@
package funkin.play;
+import funkin.ui.SwagCamera;
import flixel.addons.transition.FlxTransitionableSubState;
import funkin.ui.debug.charting.ChartEditorState;
import haxe.Int64;
@@ -16,19 +17,24 @@ import flixel.FlxState;
import flixel.FlxSubState;
import flixel.input.keyboard.FlxKey;
import flixel.math.FlxMath;
+import funkin.play.components.ComboMilestone;
import flixel.math.FlxPoint;
+import funkin.play.components.HealthIcon;
+import funkin.ui.MusicBeatSubState;
import flixel.math.FlxRect;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.ui.FlxBar;
import flixel.util.FlxColor;
+import funkin.api.newgrounds.NGio;
import flixel.util.FlxTimer;
import funkin.audio.VoicesGroup;
import funkin.save.Save;
import funkin.Highscore.Tallies;
import funkin.input.PreciseInputManager;
import funkin.modding.events.ScriptEvent;
+import funkin.ui.mainmenu.MainMenuState;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.play.character.BaseCharacter;
import funkin.play.character.CharacterData.CharacterDataParser;
@@ -42,7 +48,6 @@ import funkin.play.notes.NoteDirection;
import funkin.play.notes.Strumline;
import funkin.play.notes.SustainTrail;
import funkin.play.scoring.Scoring;
-import funkin.NoteSplash;
import funkin.play.song.Song;
import funkin.data.song.SongRegistry;
import funkin.data.song.SongData.SongEventData;
@@ -50,9 +55,10 @@ import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongData.SongCharacterData;
import funkin.play.stage.Stage;
import funkin.play.stage.StageData.StageDataParser;
-import funkin.ui.PopUpStuff;
+import funkin.ui.transition.LoadingState;
+import funkin.play.components.PopUpStuff;
import funkin.ui.options.PreferencesMenu;
-import funkin.ui.stageBuildShit.StageOffsetSubState;
+import funkin.ui.debug.stage.StageOffsetSubState;
import funkin.ui.story.StoryMenuState;
import funkin.util.SerializerUtil;
import funkin.util.SortUtil;
@@ -510,8 +516,6 @@ class PlayState extends MusicBeatSubState
}
instance = this;
- NoteSplash.buildSplashFrames();
-
if (!assertChartExists()) return;
if (false)
@@ -700,6 +704,8 @@ class PlayState extends MusicBeatSubState
if (!overrideMusic)
{
+ // Stop the vocals if they already exist.
+ if (vocals != null) vocals.stop();
vocals = currentChart.buildVocals();
if (vocals.members.length == 0)
@@ -1471,7 +1477,7 @@ class PlayState extends MusicBeatSubState
{
case 'school': 'pixel';
case 'schoolEvil': 'pixel';
- default: 'funkin';
+ default: Constants.DEFAULT_NOTE_STYLE;
}
var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId);
if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault();
@@ -1554,6 +1560,8 @@ class PlayState extends MusicBeatSubState
if (!overrideMusic)
{
+ // Stop the vocals if they already exist.
+ if (vocals != null) vocals.stop();
vocals = currentChart.buildVocals();
if (vocals.members.length == 0)
@@ -2389,8 +2397,8 @@ class PlayState extends MusicBeatSubState
#if sys
// spitter for ravy, teehee!!
-
- var output = SerializerUtil.toJSON(inputSpitter);
+ var writer = new json2object.JsonWriter>();
+ var output = writer.write(inputSpitter, ' ');
sys.io.File.saveContent("./scores.json", output);
#end
diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx
index 3f7231c2a..507fa1236 100644
--- a/source/funkin/play/ResultState.hx
+++ b/source/funkin/play/ResultState.hx
@@ -8,16 +8,18 @@ import flixel.graphics.frames.FlxAtlasFrames;
import flixel.graphics.frames.FlxBitmapFont;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxPoint;
+import funkin.ui.MusicBeatSubState;
import flixel.math.FlxRect;
import flixel.text.FlxBitmapText;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
+import funkin.ui.freeplay.FreeplayState;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxGradient;
import flixel.util.FlxTimer;
-import funkin.shaderslmfao.LeftMaskShader;
-import funkin.ui.TallyCounter;
+import funkin.graphics.shaders.LeftMaskShader;
+import funkin.play.components.TallyCounter;
import flxanimate.FlxAnimate.Settings;
class ResultState extends MusicBeatSubState
diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx
index 588b5663d..7ad0892f6 100644
--- a/source/funkin/play/character/BaseCharacter.hx
+++ b/source/funkin/play/character/BaseCharacter.hx
@@ -58,7 +58,7 @@ class BaseCharacter extends Bopper
*/
public var dropNoteCounts(default, null):Array;
- @:allow(funkin.ui.animDebugShit.DebugBoundingState)
+ @:allow(funkin.ui.debug.anim.DebugBoundingState)
final _data:CharacterData;
final singTimeSec:Float;
diff --git a/source/funkin/ComboMilestone.hx b/source/funkin/play/components/ComboMilestone.hx
similarity index 98%
rename from source/funkin/ComboMilestone.hx
rename to source/funkin/play/components/ComboMilestone.hx
index 79e454c44..54d1438f1 100644
--- a/source/funkin/ComboMilestone.hx
+++ b/source/funkin/play/components/ComboMilestone.hx
@@ -1,4 +1,4 @@
-package funkin;
+package funkin.play.components;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;
diff --git a/source/funkin/play/HealthIcon.hx b/source/funkin/play/components/HealthIcon.hx
similarity index 98%
rename from source/funkin/play/HealthIcon.hx
rename to source/funkin/play/components/HealthIcon.hx
index 958933df8..0d90df5a0 100644
--- a/source/funkin/play/HealthIcon.hx
+++ b/source/funkin/play/components/HealthIcon.hx
@@ -1,4 +1,4 @@
-package funkin.play;
+package funkin.play.components;
import funkin.play.character.CharacterData;
import flixel.FlxSprite;
@@ -6,6 +6,7 @@ import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import funkin.play.character.CharacterData.CharacterDataParser;
import openfl.utils.Assets;
+import funkin.util.MathUtil;
/**
* This is a rework of the health icon with the following changes:
@@ -201,19 +202,19 @@ class HealthIcon extends FlxSprite
if (this.width > this.height)
{
// Apply linear interpolation while accounting for frame rate.
- var targetSize:Int = Std.int(CoolUtil.coolLerp(this.width, HEALTH_ICON_SIZE * this.size.x, 0.15));
+ var targetSize:Int = Std.int(MathUtil.coolLerp(this.width, HEALTH_ICON_SIZE * this.size.x, 0.15));
setGraphicSize(targetSize, 0);
}
else
{
- var targetSize:Int = Std.int(CoolUtil.coolLerp(this.height, HEALTH_ICON_SIZE * this.size.y, 0.15));
+ var targetSize:Int = Std.int(MathUtil.coolLerp(this.height, HEALTH_ICON_SIZE * this.size.y, 0.15));
setGraphicSize(0, targetSize);
}
// Lerp the health icon back to its normal angle.
- this.angle = CoolUtil.coolLerp(this.angle, 0, 0.15);
+ this.angle = MathUtil.coolLerp(this.angle, 0, 0.15);
this.updateHitbox();
}
diff --git a/source/funkin/ui/PopUpStuff.hx b/source/funkin/play/components/PopUpStuff.hx
similarity index 99%
rename from source/funkin/ui/PopUpStuff.hx
rename to source/funkin/play/components/PopUpStuff.hx
index 75fc87c8b..38a6ec15a 100644
--- a/source/funkin/ui/PopUpStuff.hx
+++ b/source/funkin/play/components/PopUpStuff.hx
@@ -1,4 +1,4 @@
-package funkin.ui;
+package funkin.play.components;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;
diff --git a/source/funkin/ui/TallyCounter.hx b/source/funkin/play/components/TallyCounter.hx
similarity index 94%
rename from source/funkin/ui/TallyCounter.hx
rename to source/funkin/play/components/TallyCounter.hx
index 72857671e..77e6ef4ec 100644
--- a/source/funkin/ui/TallyCounter.hx
+++ b/source/funkin/play/components/TallyCounter.hx
@@ -1,4 +1,4 @@
-package funkin.ui;
+package funkin.play.components;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;
@@ -8,7 +8,7 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
/**
- * Similar to ComboCounter, but it's not!
+ * Numerical counters used next to each judgement in the Results screen.
*/
class TallyCounter extends FlxTypedSpriteGroup
{
diff --git a/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx b/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx
index 70ac011a2..13697b9f4 100644
--- a/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx
+++ b/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx
@@ -4,6 +4,7 @@ import flixel.FlxState;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.modding.events.ScriptEvent;
import flixel.util.FlxColor;
+import funkin.ui.MusicBeatState;
/**
* A state with displays a conversation with no background.
diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx
index 9562ef2ca..b48eda224 100644
--- a/source/funkin/play/song/Song.hx
+++ b/source/funkin/play/song/Song.hx
@@ -96,11 +96,13 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry
+ function fetchVariationMetadata(id:String, vari:String):Null
{
- var result:Array = [];
- for (vari in variations)
- {
- var version:Null = SongRegistry.instance.fetchEntryMetadataVersion(id, vari);
- if (version == null) continue;
- var meta:Null = SongRegistry.instance.parseEntryMetadataWithMigration(id, vari, version);
- if (meta != null) result.push(meta);
- }
- return result;
+ var version:Null = SongRegistry.instance.fetchEntryMetadataVersion(id, vari);
+ if (version == null) return null;
+ var meta:Null = SongRegistry.instance.parseEntryMetadataWithMigration(id, vari, version);
+ return meta;
}
}
diff --git a/source/funkin/play/stage/Bopper.hx b/source/funkin/play/stage/Bopper.hx
index 187b5ec32..1bc0632f9 100644
--- a/source/funkin/play/stage/Bopper.hx
+++ b/source/funkin/play/stage/Bopper.hx
@@ -85,7 +85,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
return globalOffsets = value;
}
- @:allow(funkin.ui.animDebugShit.DebugBoundingState)
+ @:allow(funkin.ui.debug.anim.DebugBoundingState)
var animOffsets(default, set):Array = [0, 0];
public var originalPosition:FlxPoint = new FlxPoint(0, 0);
diff --git a/source/funkin/play/stage/Stage.hx b/source/funkin/play/stage/Stage.hx
index d7ba38e2a..c8cb8ce66 100644
--- a/source/funkin/play/stage/Stage.hx
+++ b/source/funkin/play/stage/Stage.hx
@@ -177,13 +177,13 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
continue;
}
- if (Std.isOfType(dataProp.scale, Array))
+ switch (dataProp.scale)
{
- propSprite.scale.set(dataProp.scale[0], dataProp.scale[1]);
- }
- else
- {
- propSprite.scale.set(dataProp.scale);
+ case Left(value):
+ propSprite.scale.set(value);
+
+ case Right(values):
+ propSprite.scale.set(values[0], values[1]);
}
propSprite.updateHitbox();
@@ -195,8 +195,15 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
// If pixel, disable antialiasing.
propSprite.antialiasing = !dataProp.isPixel;
- propSprite.scrollFactor.x = dataProp.scroll[0];
- propSprite.scrollFactor.y = dataProp.scroll[1];
+ switch (dataProp.scroll)
+ {
+ case Left(value):
+ propSprite.scrollFactor.x = value;
+ propSprite.scrollFactor.y = value;
+ case Right(values):
+ propSprite.scrollFactor.x = values[0];
+ propSprite.scrollFactor.y = values[1];
+ }
propSprite.zIndex = dataProp.zIndex;
diff --git a/source/funkin/play/stage/StageData.hx b/source/funkin/play/stage/StageData.hx
index c14e05aaf..29ca03b84 100644
--- a/source/funkin/play/stage/StageData.hx
+++ b/source/funkin/play/stage/StageData.hx
@@ -1,7 +1,6 @@
package funkin.play.stage;
import funkin.data.animation.AnimationData;
-import flixel.util.typeLimit.OneOfTwo;
import funkin.play.stage.ScriptedStage;
import funkin.play.stage.Stage;
import funkin.util.VersionUtil;
@@ -157,15 +156,26 @@ class StageDataParser
return rawJson;
}
- static function migrateStageData(rawJson:String, stageId:String)
+ static function migrateStageData(rawJson:String, stageId:String):Null
{
// If you update the stage data format in a breaking way,
// handle migration here by checking the `version` value.
try
{
- var stageData:StageData = cast Json.parse(rawJson);
- return stageData;
+ var parser = new json2object.JsonParser();
+ parser.fromJson(rawJson, '$stageId.json');
+
+ if (parser.errors.length > 0)
+ {
+ trace('[STAGE] Failed to parse stage data');
+
+ for (error in parser.errors)
+ funkin.data.DataError.printError(error);
+
+ return null;
+ }
+ return parser.value;
}
catch (e)
{
@@ -269,24 +279,29 @@ class StageDataParser
inputProp.danceEvery = DEFAULT_DANCEEVERY;
}
- if (inputProp.scale == null)
- {
- inputProp.scale = DEFAULT_SCALE;
- }
-
if (inputProp.animType == null)
{
inputProp.animType = DEFAULT_ANIMTYPE;
}
- if (Std.isOfType(inputProp.scale, Float))
+ switch (inputProp.scale)
{
- inputProp.scale = [inputProp.scale, inputProp.scale];
+ case null:
+ inputProp.scale = Right([DEFAULT_SCALE, DEFAULT_SCALE]);
+ case Left(value):
+ inputProp.scale = Right([value, value]);
+ case Right(_):
+ // Do nothing
}
- if (inputProp.scroll == null)
+ switch (inputProp.scroll)
{
- inputProp.scroll = DEFAULT_SCROLL;
+ case null:
+ inputProp.scroll = Right(DEFAULT_SCROLL);
+ case Left(value):
+ inputProp.scroll = Right([value, value]);
+ case Right(_):
+ // Do nothing
}
if (inputProp.alpha == null)
@@ -294,11 +309,6 @@ class StageDataParser
inputProp.alpha = DEFAULT_ALPHA;
}
- if (Std.isOfType(inputProp.scroll, Float))
- {
- inputProp.scroll = [inputProp.scroll, inputProp.scroll];
- }
-
if (inputProp.animations == null)
{
inputProp.animations = [];
@@ -392,23 +402,39 @@ class StageDataParser
}
}
-typedef StageData =
+class StageData
{
/**
* The sematic version number of the stage data JSON format.
* Supports fancy comparisons like NPM does it's neat.
*/
- var version:String;
+ public var version:String;
- var name:String;
- var cameraZoom:Null;
- var props:Array;
- var characters:
- {
- bf:StageDataCharacter,
- dad:StageDataCharacter,
- gf:StageDataCharacter,
- };
+ public var name:String;
+ public var cameraZoom:Null;
+ public var props:Array;
+ public var characters:StageDataCharacters;
+
+ public function new()
+ {
+ this.version = StageDataParser.STAGE_DATA_VERSION;
+ }
+
+ /**
+ * Convert this StageData into a JSON string.
+ */
+ public function serialize(pretty:Bool = true):String
+ {
+ var writer = new json2object.JsonWriter();
+ return writer.write(this, pretty ? ' ' : null);
+ }
+}
+
+typedef StageDataCharacters =
+{
+ var bf:StageDataCharacter;
+ var dad:StageDataCharacter;
+ var gf:StageDataCharacter;
};
typedef StageDataProp =
@@ -417,6 +443,7 @@ typedef StageDataProp =
* The name of the prop for later lookup by scripts.
* Optional; if unspecified, the prop can't be referenced by scripts.
*/
+ @:optional
var name:String;
/**
@@ -435,27 +462,35 @@ typedef StageDataProp =
* This is just like CSS, it isn't hard.
* @default 0
*/
- var zIndex:Null;
+ @:optional
+ @:default(0)
+ var zIndex:Int;
/**
* If set to true, anti-aliasing will be forcibly disabled on the sprite.
* This prevents blurry images on pixel-art levels.
* @default false
*/
- var isPixel:Null;
+ @:optional
+ @:default(false)
+ var isPixel:Bool;
/**
* Either the scale of the prop as a float, or the [w, h] scale as an array of two floats.
* Pro tip: On pixel-art levels, save the sprite small and set this value to 6 or so to save memory.
- * @default 1
*/
- var scale:OneOfTwo>;
+ @:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
+ @:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
+ @:optional
+ var scale:haxe.ds.Either>;
/**
* The alpha of the prop, as a float.
* @default 1.0
*/
- var alpha:Null;
+ @:optional
+ @:default(1.0)
+ var alpha:Float;
/**
* If not zero, this prop will play an animation every X beats of the song.
@@ -464,7 +499,9 @@ typedef StageDataProp =
*
* @default 0
*/
- var danceEvery:Null;
+ @:default(0)
+ @:optional
+ var danceEvery:Int;
/**
* How much the prop scrolls relative to the camera. Used to create a parallax effect.
@@ -474,25 +511,32 @@ typedef StageDataProp =
* [0, 0] means the prop is not moved.
* @default [0, 0]
*/
- var scroll:OneOfTwo>;
+ @:jcustomparse(funkin.data.DataParse.eitherFloatOrFloats)
+ @:jcustomwrite(funkin.data.DataWrite.eitherFloatOrFloats)
+ @:optional
+ var scroll:haxe.ds.Either>;
/**
* An optional array of animations which the prop can play.
* @default Prop has no animations.
*/
+ @:optional
var animations:Array;
/**
* If animations are used, this is the name of the animation to play first.
* @default Don't play an animation.
*/
- var startingAnimation:String;
+ @:optional
+ var startingAnimation:Null;
/**
* The animation type to use.
* Options: "sparrow", "packer"
* @default "sparrow"
*/
+ @:default("sparrow")
+ @:optional
var animType:String;
};
@@ -503,16 +547,22 @@ typedef StageDataCharacter =
* Again, just like CSS.
* @default 0
*/
- ?zIndex:Int,
+ @:optional
+ @:default(0)
+ var zIndex:Int;
/**
* The position to render the character at.
*/
- position:Array,
+ @:optional
+ @:default([0, 0])
+ var position:Array;
/**
* The camera offsets to apply when focusing on the character on this stage.
* @default [-100, -100] for BF, [100, -100] for DAD/OPPONENT, [0, 0] for GF
*/
- cameraOffsets:Array,
+ @:optional
+ @:default([0, 0])
+ var cameraOffsets:Array;
};
diff --git a/source/funkin/save/migrator/SaveDataMigrator.hx b/source/funkin/save/migrator/SaveDataMigrator.hx
index e7b7c7583..d5b23cfd9 100644
--- a/source/funkin/save/migrator/SaveDataMigrator.hx
+++ b/source/funkin/save/migrator/SaveDataMigrator.hx
@@ -13,8 +13,7 @@ class SaveDataMigrator
*/
public static function migrate(inputData:Dynamic):Save
{
- // This deserializes directly into a `Version` object, not a `String`.
- var version:Null = inputData?.version ?? null;
+ var version:Null = VersionUtil.parseVersion(inputData?.version ?? null);
if (version == null)
{
@@ -24,7 +23,7 @@ class SaveDataMigrator
}
else
{
- if (VersionUtil.validateVersionStr(version, Save.SAVE_DATA_VERSION_RULE))
+ if (VersionUtil.validateVersion(version, Save.SAVE_DATA_VERSION_RULE))
{
// Simply cast the structured data.
var save:Save = inputData;
diff --git a/source/funkin/Alphabet.hx b/source/funkin/ui/Alphabet.hx
similarity index 97%
rename from source/funkin/Alphabet.hx
rename to source/funkin/ui/Alphabet.hx
index 45e9a2aee..66b95f5b8 100644
--- a/source/funkin/Alphabet.hx
+++ b/source/funkin/ui/Alphabet.hx
@@ -1,9 +1,10 @@
-package funkin;
+package funkin.ui;
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup;
import flixel.math.FlxMath;
import flixel.util.FlxTimer;
+import funkin.util.MathUtil;
/**
* Loosley based on FlxTypeText lolol
@@ -151,7 +152,6 @@ class Alphabet extends FlxSpriteGroup
if (AlphaCharacter.alphabet.indexOf(splitWords[loopNum].toLowerCase()) != -1
|| isNumber
|| isSymbol) // if (AlphaCharacter.alphabet.contains(splitWords[loopNum].toLowerCase()) || isNumber || isSymbol)
-
{
if (lastSprite != null && !xPosResetted)
{
@@ -220,8 +220,8 @@ class Alphabet extends FlxSpriteGroup
{
var scaledY = FlxMath.remapToRange(targetY, 0, 1, 0, 1.3);
- y = CoolUtil.coolLerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16);
- x = CoolUtil.coolLerp(x, (targetY * 20) + 90, 0.16);
+ y = MathUtil.coolLerp(y, (scaledY * 120) + (FlxG.height * 0.48), 0.16);
+ x = MathUtil.coolLerp(x, (targetY * 20) + 90, 0.16);
}
super.update(elapsed);
diff --git a/source/funkin/ui/AtlasMenuList.hx b/source/funkin/ui/AtlasMenuList.hx
index 028a00eef..efff6da93 100644
--- a/source/funkin/ui/AtlasMenuList.hx
+++ b/source/funkin/ui/AtlasMenuList.hx
@@ -38,7 +38,7 @@ class AtlasMenuList extends MenuTypedList
/**
* A menu list item which uses single texture atlas.
*/
-class AtlasMenuItem extends MenuItem
+class AtlasMenuItem extends MenuListItem
{
var atlas:FlxAtlasFrames;
diff --git a/source/funkin/MenuItem.hx b/source/funkin/ui/MenuItem.hx
similarity index 93%
rename from source/funkin/MenuItem.hx
rename to source/funkin/ui/MenuItem.hx
index 261092a1a..ba5cc066b 100644
--- a/source/funkin/MenuItem.hx
+++ b/source/funkin/ui/MenuItem.hx
@@ -1,9 +1,10 @@
-package funkin;
+package funkin.ui;
import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxSpriteGroup;
import flixel.math.FlxMath;
+import funkin.util.MathUtil;
import flixel.util.FlxColor;
class MenuItem extends FlxSpriteGroup
@@ -44,7 +45,7 @@ class MenuItem extends FlxSpriteGroup
override function update(elapsed:Float)
{
super.update(elapsed);
- y = CoolUtil.coolLerp(y, (targetY * 120) + 480, 0.17);
+ y = MathUtil.coolLerp(y, (targetY * 120) + 480, 0.17);
if (isFlashing) flashingInt += 1;
diff --git a/source/funkin/ui/MenuList.hx b/source/funkin/ui/MenuList.hx
index f1de8d40e..3ffe3c330 100644
--- a/source/funkin/ui/MenuList.hx
+++ b/source/funkin/ui/MenuList.hx
@@ -6,7 +6,7 @@ import flixel.group.FlxGroup;
import flixel.math.FlxPoint;
import flixel.util.FlxSignal;
-class MenuTypedList extends FlxTypedGroup
+class MenuTypedList extends FlxTypedGroup
{
public var selectedIndex(default, null) = 0;
public var selectedItem(get, never):T;
@@ -206,7 +206,7 @@ class MenuTypedList extends FlxTypedGroup
}
}
-class MenuItem extends FlxSprite
+class MenuListItem extends FlxSprite
{
public var callback:Void->Void;
public var name:String;
@@ -261,7 +261,7 @@ class MenuItem extends FlxSprite
}
}
-class MenuTypedItem extends MenuItem
+class MenuTypedItem extends MenuListItem
{
public var label(default, set):T;
diff --git a/source/funkin/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx
similarity index 89%
rename from source/funkin/MusicBeatState.hx
rename to source/funkin/ui/MusicBeatState.hx
index 524a84314..4e0e19d5e 100644
--- a/source/funkin/MusicBeatState.hx
+++ b/source/funkin/ui/MusicBeatState.hx
@@ -1,6 +1,7 @@
-package funkin;
+package funkin.ui;
import funkin.modding.IScriptedClass.IEventHandler;
+import funkin.ui.mainmenu.MainMenuState;
import flixel.FlxState;
import flixel.FlxSubState;
import flixel.addons.transition.FlxTransitionableState;
@@ -57,27 +58,45 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
Conductor.stepHit.remove(this.stepHit);
}
- override function update(elapsed:Float)
+ function handleControls():Void
{
- super.update(elapsed);
+ var isHaxeUIFocused:Bool = haxe.ui.focus.FocusManager.instance?.focus != null;
- // Rebindable volume keys.
- if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted();
- else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1);
- else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
+ if (!isHaxeUIFocused)
+ {
+ // Rebindable volume keys.
+ if (controls.VOLUME_MUTE) FlxG.sound.toggleMuted();
+ else if (controls.VOLUME_UP) FlxG.sound.changeVolume(0.1);
+ else if (controls.VOLUME_DOWN) FlxG.sound.changeVolume(-0.1);
+ }
+ }
+ function handleFunctionControls():Void
+ {
// Emergency exit button.
if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState());
// This can now be used in EVERY STATE YAY!
if (FlxG.keys.justPressed.F5) debug_refreshModules();
+ }
+ function handleQuickWatch():Void
+ {
// Display Conductor info in the watch window.
FlxG.watch.addQuick("songPosition", Conductor.songPosition);
FlxG.watch.addQuick("bpm", Conductor.bpm);
FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime);
FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime);
FlxG.watch.addQuick("currentStepTime", Conductor.currentStepTime);
+ }
+
+ override function update(elapsed:Float)
+ {
+ super.update(elapsed);
+
+ handleControls();
+ handleFunctionControls();
+ handleQuickWatch();
dispatchEvent(new UpdateScriptEvent(elapsed));
}
diff --git a/source/funkin/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx
similarity index 98%
rename from source/funkin/MusicBeatSubState.hx
rename to source/funkin/ui/MusicBeatSubState.hx
index 8b27876f9..64df6ee71 100644
--- a/source/funkin/MusicBeatSubState.hx
+++ b/source/funkin/ui/MusicBeatSubState.hx
@@ -1,8 +1,9 @@
-package funkin;
+package funkin.ui;
import flixel.addons.transition.FlxTransitionableSubState;
import flixel.FlxSubState;
import flixel.text.FlxText;
+import funkin.ui.mainmenu.MainMenuState;
import flixel.util.FlxColor;
import funkin.modding.events.ScriptEvent;
import funkin.modding.IScriptedClass.IEventHandler;
diff --git a/source/funkin/SwagCamera.hx b/source/funkin/ui/SwagCamera.hx
similarity index 87%
rename from source/funkin/SwagCamera.hx
rename to source/funkin/ui/SwagCamera.hx
index 386eaea62..70791d38f 100644
--- a/source/funkin/SwagCamera.hx
+++ b/source/funkin/ui/SwagCamera.hx
@@ -1,8 +1,9 @@
-package funkin;
+package funkin.ui;
import flixel.FlxCamera;
import flixel.FlxSprite;
import flixel.math.FlxPoint;
+import funkin.util.MathUtil;
class SwagCamera extends FlxCamera
{
@@ -92,10 +93,10 @@ class SwagCamera extends FlxCamera
else
{
// THIS THE PART THAT ACTUALLY MATTERS LOL
- scroll.x = CoolUtil.coolLerp(scroll.x, _scrollTarget.x, followLerp);
- scroll.y = CoolUtil.coolLerp(scroll.y, _scrollTarget.y, followLerp);
- // scroll.x += (_scrollTarget.x - scroll.x) * CoolUtil.camLerpShit(followLerp);
- // scroll.y += (_scrollTarget.y - scroll.y) * CoolUtil.camLerpShit(followLerp);
+ scroll.x = MathUtil.coolLerp(scroll.x, _scrollTarget.x, followLerp);
+ scroll.y = MathUtil.coolLerp(scroll.y, _scrollTarget.y, followLerp);
+ // scroll.x += (_scrollTarget.x - scroll.x) * MathUtil.cameraLerp(followLerp);
+ // scroll.y += (_scrollTarget.y - scroll.y) * MathUtil.cameraLerp(followLerp);
}
}
}
diff --git a/source/funkin/ui/debug/DebugMenuSubState.hx b/source/funkin/ui/debug/DebugMenuSubState.hx
index 7ef4cb238..ef02a802e 100644
--- a/source/funkin/ui/debug/DebugMenuSubState.hx
+++ b/source/funkin/ui/debug/DebugMenuSubState.hx
@@ -3,9 +3,10 @@ package funkin.ui.debug;
import flixel.math.FlxPoint;
import flixel.FlxObject;
import flixel.FlxSprite;
-import funkin.MusicBeatSubState;
+import funkin.ui.MusicBeatSubState;
import funkin.ui.TextMenuList;
import funkin.ui.debug.charting.ChartEditorState;
+import funkin.ui.MusicBeatSubState;
class DebugMenuSubState extends MusicBeatSubState
{
@@ -85,13 +86,13 @@ class DebugMenuSubState extends MusicBeatSubState
function openAnimationEditor()
{
- FlxG.switchState(new funkin.ui.animDebugShit.DebugBoundingState());
+ FlxG.switchState(new funkin.ui.debug.anim.DebugBoundingState());
trace('Animation Editor');
}
function testStickers()
{
- openSubState(new funkin.ui.StickerSubState());
+ openSubState(new funkin.ui.transition.StickerSubState());
trace('opened stickers');
}
diff --git a/source/funkin/MemoryCounter.hx b/source/funkin/ui/debug/MemoryCounter.hx
similarity index 97%
rename from source/funkin/MemoryCounter.hx
rename to source/funkin/ui/debug/MemoryCounter.hx
index 658febe59..312d853e7 100644
--- a/source/funkin/MemoryCounter.hx
+++ b/source/funkin/ui/debug/MemoryCounter.hx
@@ -1,4 +1,4 @@
-package funkin;
+package funkin.ui.debug;
import openfl.text.TextFormat;
import openfl.system.System;
diff --git a/source/funkin/ui/animDebugShit/DebugBoundingState.hx b/source/funkin/ui/debug/anim/DebugBoundingState.hx
similarity index 98%
rename from source/funkin/ui/animDebugShit/DebugBoundingState.hx
rename to source/funkin/ui/debug/anim/DebugBoundingState.hx
index 297c44e8e..4e06913b4 100644
--- a/source/funkin/ui/animDebugShit/DebugBoundingState.hx
+++ b/source/funkin/ui/debug/anim/DebugBoundingState.hx
@@ -1,4 +1,4 @@
-package funkin.ui.animDebugShit;
+package funkin.ui.debug.anim;
import funkin.util.SerializerUtil;
import funkin.play.character.CharacterData;
@@ -15,6 +15,7 @@ import flixel.math.FlxPoint;
import flixel.sound.FlxSound;
import flixel.text.FlxText;
import flixel.util.FlxColor;
+import funkin.util.MouseUtil;
import flixel.util.FlxSpriteUtil;
import flixel.util.FlxTimer;
import funkin.play.character.BaseCharacter;
@@ -25,6 +26,7 @@ import haxe.ui.components.DropDown;
import haxe.ui.core.Component;
import haxe.ui.events.ItemEvent;
import haxe.ui.events.UIEvent;
+import funkin.ui.mainmenu.MainMenuState;
import lime.utils.Assets as LimeAssets;
import openfl.Assets;
import openfl.events.Event;
@@ -32,6 +34,7 @@ import openfl.events.IOErrorEvent;
import openfl.geom.Rectangle;
import openfl.net.FileReference;
import openfl.net.URLLoader;
+import funkin.ui.mainmenu.MainMenuState;
import openfl.net.URLRequest;
import openfl.utils.ByteArray;
import funkin.input.Cursor;
@@ -363,8 +366,8 @@ class DebugBoundingState extends FlxState
if (FlxG.keys.justPressed.F4) FlxG.switchState(new MainMenuState());
- CoolUtil.mouseCamDrag();
- CoolUtil.mouseWheelZoom();
+ MouseUtil.mouseCamDrag();
+ MouseUtil.mouseWheelZoom();
// bg.scale.x = FlxG.camera.zoom;
// bg.scale.y = FlxG.camera.zoom;
diff --git a/source/funkin/ui/animDebugShit/FlxAnimateTest.hx b/source/funkin/ui/debug/anim/FlxAnimateTest.hx
similarity index 94%
rename from source/funkin/ui/animDebugShit/FlxAnimateTest.hx
rename to source/funkin/ui/debug/anim/FlxAnimateTest.hx
index 738e109ef..c83d2c370 100644
--- a/source/funkin/ui/animDebugShit/FlxAnimateTest.hx
+++ b/source/funkin/ui/debug/anim/FlxAnimateTest.hx
@@ -1,7 +1,8 @@
-package funkin.ui.animDebugShit;
+package funkin.ui.debug.anim;
import flixel.FlxG;
import funkin.graphics.adobeanimate.FlxAtlasSprite;
+import funkin.ui.MusicBeatState;
/**
* A simple test of FlxAnimate.
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 366e446e5..382bab592 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -16,6 +16,7 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.tweens.misc.VarTween;
import flixel.util.FlxColor;
+import funkin.ui.mainmenu.MainMenuState;
import flixel.util.FlxSort;
import flixel.util.FlxTimer;
import funkin.audio.visualize.PolygonSpectogram;
@@ -31,7 +32,7 @@ import funkin.input.TurboKeyHandler;
import funkin.modding.events.ScriptEvent;
import funkin.play.character.BaseCharacter.CharacterType;
import funkin.play.character.CharacterData;
-import funkin.play.HealthIcon;
+import funkin.play.components.HealthIcon;
import funkin.play.notes.NoteSprite;
import funkin.play.PlayState;
import funkin.play.song.Song;
@@ -90,6 +91,7 @@ import haxe.ui.core.Component;
import haxe.ui.core.Screen;
import haxe.ui.events.DragEvent;
import haxe.ui.events.UIEvent;
+import haxe.ui.focus.FocusManager;
import haxe.ui.notifications.NotificationManager;
import haxe.ui.notifications.NotificationType;
import openfl.display.BitmapData;
@@ -547,22 +549,14 @@ class ChartEditorState extends HaxeUIState
// HaxeUI
/**
- * Whether the user's mouse cursor is hovering over a SOLID component of the HaxeUI.
- * If so, ignore mouse events underneath as well as certain key events.
+ * Whether the user is focused on an input in the Haxe UI, and inputs are being fed into it.
+ * If the user clicks off the input, focus will leave.
*/
- var isCursorOverHaxeUI(get, never):Bool;
+ var isHaxeUIFocused(get, never):Bool;
- function get_isCursorOverHaxeUI():Bool
+ function get_isHaxeUIFocused():Bool
{
- return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
- }
-
- var isCursorOverHaxeUIButton(get, never):Bool;
-
- function get_isCursorOverHaxeUIButton():Bool
- {
- return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY, haxe.ui.components.Button)
- || Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY, haxe.ui.components.Link);
+ return FocusManager.instance.focus != null;
}
/**
@@ -1046,17 +1040,17 @@ class ChartEditorState extends HaxeUIState
function get_currentSongNoteStyle():String
{
- if (currentSongMetadata.playData.noteSkin == null)
+ if (currentSongMetadata.playData.noteStyle == null)
{
// Initialize to the default value if not set.
- currentSongMetadata.playData.noteSkin = 'funkin';
+ currentSongMetadata.playData.noteStyle = Constants.DEFAULT_NOTE_STYLE;
}
- return currentSongMetadata.playData.noteSkin;
+ return currentSongMetadata.playData.noteStyle;
}
function set_currentSongNoteStyle(value:String):String
{
- return currentSongMetadata.playData.noteSkin = value;
+ return currentSongMetadata.playData.noteStyle = value;
}
var currentSongStage(get, set):String;
@@ -1326,10 +1320,22 @@ class ChartEditorState extends HaxeUIState
*/
// ==============================
- public function new()
+ /**
+ * The params which were passed in when the Chart Editor was initialized.
+ */
+ var params:Null;
+
+ /**
+ * The current file path which the chart editor is working with.
+ */
+ public var currentWorkingFilePath:Null;
+
+ public function new(?params:ChartEditorParams)
{
// Load the HaxeUI XML file.
super(CHART_EDITOR_LAYOUT);
+
+ this.params = params;
}
public override function dispatchEvent(event:ScriptEvent):Void
@@ -1410,7 +1416,33 @@ class ChartEditorState extends HaxeUIState
refresh();
- this.openWelcomeDialog(false);
+ if (params != null && params.fnfcTargetPath != null)
+ {
+ // Chart editor was opened from the command line. Open the FNFC file now!
+ if (ChartEditorImportExportHandler.loadFromFNFCPath(this, params.fnfcTargetPath))
+ {
+ // Don't open the welcome dialog!
+
+ #if !mac
+ NotificationManager.instance.addNotification(
+ {
+ title: 'Success',
+ body: 'Loaded chart (${params.fnfcTargetPath})',
+ type: NotificationType.Success,
+ expiryMs: Constants.NOTIFICATION_DISMISS_TIME
+ });
+ #end
+ }
+ else
+ {
+ // Song failed to load, open the Welcome dialog so we aren't in a broken state.
+ ChartEditorDialogHandler.openWelcomeDialog(this, false);
+ }
+ }
+ else
+ {
+ ChartEditorDialogHandler.openWelcomeDialog(this, false);
+ }
}
override function destroy():Void
@@ -1772,11 +1804,15 @@ class ChartEditorState extends HaxeUIState
noteSnapQuantIndex++;
if (noteSnapQuantIndex >= SNAP_QUANTS.length) noteSnapQuantIndex = 0;
});
+ addUIRightClickListener('playbarNoteSnap', function(_) {
+ noteSnapQuantIndex--;
+ if (noteSnapQuantIndex < 0) noteSnapQuantIndex = SNAP_QUANTS.length - 1;
+ });
// Add functionality to the menu items.
addUIClickListener('menubarItemNewChart', _ -> this.openWelcomeDialog(true));
- addUIClickListener('menubarItemOpenChart', _ -> this.openBrowseWizard(true));
+ addUIClickListener('menubarItemOpenChart', _ -> this.openBrowseFNFC(true));
addUIClickListener('menubarItemSaveChartAs', _ -> this.exportAllSongData());
addUIClickListener('menubarItemLoadInst', _ -> this.openUploadInstDialog(true));
addUIClickListener('menubarItemImportChart', _ -> this.openImportChartDialog('legacy', true));
@@ -1918,7 +1954,7 @@ class ChartEditorState extends HaxeUIState
addUIChangeListener('menubarItemVolumeVocals', function(event:UIEvent) {
var volume:Float = (event?.value ?? 0) / 100.0;
if (audioVocalTrackGroup != null) audioVocalTrackGroup.volume = volume;
- vocalsVolumeLabel.text = 'Vocals - ${Std.int(event.value)}%';
+ vocalsVolumeLabel.text = 'Voices - ${Std.int(event.value)}%';
});
}
@@ -2499,8 +2535,8 @@ class ChartEditorState extends HaxeUIState
*/
function handleScrollKeybinds():Void
{
- // Don't scroll when the cursor is over the UI, unless a playbar button (the << >> ones) is pressed.
- if (isCursorOverHaxeUI && playbarButtonPressed == null) return;
+ // Don't scroll when the user is interacting with the UI, unless a playbar button (the << >> ones) is pressed.
+ if (isHaxeUIFocused && playbarButtonPressed == null) return;
var scrollAmount:Float = 0; // Amount to scroll the grid.
var playheadAmount:Float = 0; // Amount to scroll the playhead relative to the grid.
@@ -2699,7 +2735,7 @@ class ChartEditorState extends HaxeUIState
if (FlxG.mouse.justReleased) FlxG.sound.play(Paths.sound("chartingSounds/ClickUp"));
// Note: If a menu is open in HaxeUI, don't handle cursor behavior.
- var shouldHandleCursor:Bool = !isCursorOverHaxeUI
+ var shouldHandleCursor:Bool = !isHaxeUIFocused
|| (selectionBoxStartPos != null)
|| (dragTargetNote != null || dragTargetEvent != null);
var eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1;
@@ -3259,14 +3295,14 @@ class ChartEditorState extends HaxeUIState
{
// Create an event and place it in the chart.
// TODO: Figure out configuring event data.
- var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData);
+ var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData.clone());
performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL));
}
else
{
// Create a note and place it in the chart.
- var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind);
+ var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind.clone());
performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
@@ -3786,7 +3822,7 @@ class ChartEditorState extends HaxeUIState
// CTRL + O = Open Chart
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.O)
{
- this.openBrowseWizard(true);
+ this.openBrowseFNFC(true);
}
// CTRL + SHIFT + S = Save As
@@ -3802,6 +3838,16 @@ class ChartEditorState extends HaxeUIState
}
}
+ @:nullSafety(Off)
+ function quitChartEditor():Void
+ {
+ autoSave();
+ stopWelcomeMusic();
+ // TODO: PR Flixel to make onComplete nullable.
+ if (audioInstTrack != null) audioInstTrack.onComplete = null;
+ FlxG.switchState(new MainMenuState());
+ }
+
/**
* Handle keybinds for edit menu items.
*/
@@ -3921,7 +3967,7 @@ class ChartEditorState extends HaxeUIState
*/
function handleTestKeybinds():Void
{
- if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER)
+ if (!isHaxeUIDialogOpen && !isHaxeUIFocused && FlxG.keys.justPressed.ENTER)
{
var minimal = FlxG.keys.pressed.SHIFT;
this.hideAllToolboxes();
@@ -3938,8 +3984,10 @@ class ChartEditorState extends HaxeUIState
if (FlxG.keys.justPressed.F1) this.openUserGuideDialog();
}
- function handleQuickWatch():Void
+ override function handleQuickWatch():Void
{
+ super.handleQuickWatch();
+
FlxG.watch.addQuick('scrollPosInPixels', scrollPositionInPixels);
FlxG.watch.addQuick('playheadPosInPixels', playheadPositionInPixels);
@@ -4093,16 +4141,6 @@ class ChartEditorState extends HaxeUIState
#end
}
- /**
- * Called when the user presses the Quit button.
- */
- function quitChartEditor():Void
- {
- autoSave();
- stopWelcomeMusic();
- FlxG.switchState(new MainMenuState());
- }
-
/**
* Called when the window is closed while we are in the chart editor.
* @param exitCode The exit code of the window.
@@ -4576,7 +4614,7 @@ class ChartEditorState extends HaxeUIState
if (inputStage != null) inputStage.value = currentSongMetadata.playData.stage;
var inputNoteStyle:Null = toolbox.findComponent('inputNoteStyle', DropDown);
- if (inputNoteStyle != null) inputNoteStyle.value = currentSongMetadata.playData.noteSkin;
+ if (inputNoteStyle != null) inputNoteStyle.value = currentSongMetadata.playData.noteStyle;
var inputBPM:Null = toolbox.findComponent('inputBPM', NumberStepper);
if (inputBPM != null) inputBPM.value = currentSongMetadata.timeChanges[0].bpm;
@@ -4720,6 +4758,14 @@ enum ChartEditorLiveInputStyle
WASD;
}
+typedef ChartEditorParams =
+{
+ /**
+ * If non-null, load this song immediately instead of the welcome screen.
+ */
+ var ?fnfcTargetPath:String;
+};
+
/**
* Available themes for the chart editor state.
*/
diff --git a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx
index 2bd719df2..4c9d91407 100644
--- a/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx
+++ b/source/funkin/ui/debug/charting/components/ChartEditorEventSprite.hx
@@ -134,21 +134,25 @@ class ChartEditorEventSprite extends FlxSprite
function set_eventData(value:Null):Null
{
- this.eventData = value;
-
- if (this.eventData == null)
+ if (value == null)
{
+ this.eventData = null;
// Disown parent. MAKE SURE TO REVIVE BEFORE REUSING
this.kill();
+ this.visible = false;
+ return null;
+ }
+ else
+ {
+ this.visible = true;
+ // Only play the animation if the event type has changed.
+ // if (this.eventData == null || this.eventData.event != value.event)
+ playAnimation(value.event);
+ this.eventData = value;
+ // Update the position to match the note data.
+ updateEventPosition();
return this.eventData;
}
-
- this.visible = true;
- playAnimation(this.eventData.event);
- // Update the position to match the note data.
- updateEventPosition();
-
- return this.eventData;
}
public function updateEventPosition(?origin:FlxObject)
diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx
index 072004a43..5ea125eb4 100644
--- a/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx
+++ b/source/funkin/ui/debug/charting/handlers/ChartEditorAudioHandler.hx
@@ -258,7 +258,7 @@ class ChartEditorAudioHandler
{
var data:Null = state.audioVocalTrackData.get(key);
if (data == null) continue;
- zipEntries.push(FileUtil.makeZIPEntryFromBytes('Vocals-${key}.ogg', data));
+ zipEntries.push(FileUtil.makeZIPEntryFromBytes('Voices-${key}.ogg', data));
}
return zipEntries;
diff --git a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx
index 529707156..f5cbccff6 100644
--- a/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx
+++ b/source/funkin/ui/debug/charting/handlers/ChartEditorDialogHandler.hx
@@ -15,8 +15,6 @@ import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.play.song.Song;
import funkin.play.stage.StageData;
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
-import funkin.ui.haxeui.components.FunkinDropDown;
-import funkin.ui.haxeui.components.FunkinLink;
import funkin.util.Constants;
import funkin.util.FileUtil;
import funkin.util.SerializerUtil;
@@ -54,12 +52,13 @@ class ChartEditorDialogHandler
// Paths to HaxeUI layout files for each dialog.
static final CHART_EDITOR_DIALOG_ABOUT_LAYOUT:String = Paths.ui('chart-editor/dialogs/about');
static final CHART_EDITOR_DIALOG_WELCOME_LAYOUT:String = Paths.ui('chart-editor/dialogs/welcome');
+ static final CHART_EDITOR_DIALOG_UPLOAD_CHART_LAYOUT:String = Paths.ui('chart-editor/dialogs/upload-chart');
static final CHART_EDITOR_DIALOG_UPLOAD_INST_LAYOUT:String = Paths.ui('chart-editor/dialogs/upload-inst');
static final CHART_EDITOR_DIALOG_SONG_METADATA_LAYOUT:String = Paths.ui('chart-editor/dialogs/song-metadata');
static final CHART_EDITOR_DIALOG_UPLOAD_VOCALS_LAYOUT:String = Paths.ui('chart-editor/dialogs/upload-vocals');
static final CHART_EDITOR_DIALOG_UPLOAD_VOCALS_ENTRY_LAYOUT:String = Paths.ui('chart-editor/dialogs/upload-vocals-entry');
- static final CHART_EDITOR_DIALOG_OPEN_CHART_LAYOUT:String = Paths.ui('chart-editor/dialogs/open-chart');
- static final CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT:String = Paths.ui('chart-editor/dialogs/open-chart-entry');
+ static final CHART_EDITOR_DIALOG_OPEN_CHART_PARTS_LAYOUT:String = Paths.ui('chart-editor/dialogs/open-chart-parts');
+ static final CHART_EDITOR_DIALOG_OPEN_CHART_PARTS_ENTRY_LAYOUT:String = Paths.ui('chart-editor/dialogs/open-chart-parts-entry');
static final CHART_EDITOR_DIALOG_IMPORT_CHART_LAYOUT:String = Paths.ui('chart-editor/dialogs/import-chart');
static final CHART_EDITOR_DIALOG_USER_GUIDE_LAYOUT:String = Paths.ui('chart-editor/dialogs/user-guide');
static final CHART_EDITOR_DIALOG_ADD_VARIATION_LAYOUT:String = Paths.ui('chart-editor/dialogs/add-variation');
@@ -86,6 +85,11 @@ class ChartEditorDialogHandler
var dialog:Null