diff --git a/.github/actions/setup-haxeshit/action.yml b/.github/actions/setup-haxeshit/action.yml
index dcf5fd0a7..cb5e68f61 100644
--- a/.github/actions/setup-haxeshit/action.yml
+++ b/.github/actions/setup-haxeshit/action.yml
@@ -13,7 +13,7 @@ runs:
- name: Installing Haxe lol
run: |
haxe -version
- haxelib git haxelib https://github.com/HaxeFoundation/haxelib.git development
+ haxelib git haxelib https://github.com/HaxeFoundation/haxelib.git master
haxelib version
haxelib --global install hmm
shell: bash
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/.vscode/settings.json b/.vscode/settings.json
index 80d2bf76a..92d49c3d4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -71,7 +71,7 @@
"files.eol": "\n",
"haxe.displayPort": "auto",
- "haxe.enableCompilationServer": true,
+ "haxe.enableCompilationServer": false,
"haxe.displayServer": {
"arguments": ["-v"]
},
@@ -97,15 +97,35 @@
"args": ["-debug"]
},
{
- "label": "Windows / Debug (DEBUG ASSETS)",
- "target": "windows",
- "args": ["-debug", "-DDEBUG_ASSETS"]
- },
- {
- "label": "Windows / Debug (ANIMATE)",
+ "label": "Windows / Debug (FlxAnimate Test)",
"target": "windows",
"args": ["-debug", "-DANIMATE"]
},
+ {
+ "label": "Windows / Debug (Straight to Freeplay)",
+ "target": "windows",
+ "args": ["-debug", "-DFREEPLAY"]
+ },
+ {
+ "label": "Windows / Debug (Straight to Play - Bopeebo Normal)",
+ "target": "windows",
+ "args": ["-debug", "-DSONG=bopeebo -DDIFFICULTY=normal"]
+ },
+ {
+ "label": "Windows / Debug (Straight to Chart Editor)",
+ "target": "windows",
+ "args": ["-debug", "-DCHARTING"]
+ },
+ {
+ "label": "Windows / Debug (Straight to Animation Editor)",
+ "target": "windows",
+ "args": ["-debug", "-DANIMDEBUG"]
+ },
+ {
+ "label": "Windows / Debug (Latency Test)",
+ "target": "windows",
+ "args": ["-debug", "-DLATENCY"]
+ },
{
"label": "HTML5 / Debug",
"target": "html5",
diff --git a/Project.xml b/Project.xml
index 9fad26fd7..46ba7f155 100644
--- a/Project.xml
+++ b/Project.xml
@@ -162,7 +162,10 @@
+
+
+
diff --git a/assets b/assets
index 8e8aeb064..fb7120cf3 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit 8e8aeb06472ca294c569818cbefb1bb3dfce7854
+Subproject commit fb7120cf30d7accda049409b68d8daa0e1e7650f
diff --git a/docs/style-guide.md b/docs/style-guide.md
index 71ae844c4..1131cca2b 100644
--- a/docs/style-guide.md
+++ b/docs/style-guide.md
@@ -24,7 +24,7 @@ Example:
```
/**
* Finds the largest deviation from the desired time inside this VoicesGroup.
- *
+ *
* @param targetTime The time to check against.
* If none is provided, it checks the time of all members against the first member of this VoicesGroup.
* @return The largest deviation from the target time found.
@@ -52,3 +52,10 @@ import sys.io.File;
#end
```
+## Argument Formatting
+
+[Optional arguments](https://haxe.org/manual/types-function-optional-arguments.html) and [default arguments](https://haxe.org/manual/types-function-default-values.html) should be mutually exclusive and not used together!
+
+For example, `myFunction(?input:Int)` should be used if you want the argument to be a `Null` whose value is `null` if no value is passed, and `myFunction(input:Int = 0)` should be used if you want the argument to be an `Int`, whose value is `0` if no value is passed.
+
+Using both at the same time is considered valid by Haxe, but `myFunction(?input:Int = 0)` results in a `Null` whose value defaults to 0 anyway, so it's never null, but it's annotated as nullable! The biggest consequence of this is that it makes null safety more annoying.
diff --git a/hmm.json b/hmm.json
index 1d9e51d69..778b85604 100644
--- a/hmm.json
+++ b/hmm.json
@@ -49,14 +49,14 @@
"name": "haxeui-core",
"type": "git",
"dir": null,
- "ref": "db6f81191abe386d891aca5a65c27ba6f8e10598",
+ "ref": "815e94dd5aa6cf09c5ddcef1666a54449ffde8dc",
"url": "https://github.com/haxeui/haxeui-core"
},
{
"name": "haxeui-flixel",
"type": "git",
"dir": null,
- "ref": "e10f51fe33b8d8d2dd3f21a0fd1d7c4d88d5d5c0",
+ "ref": "95c7d66e779626eabd6f48a1cd7aa7f9a503a7f3",
"url": "https://github.com/haxeui/haxeui-flixel"
},
{
diff --git a/source/Main.hx b/source/Main.hx
index 726c1fdbf..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;
@@ -111,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 fbde22e1b..13bcd306e 100644
--- a/source/funkin/InitState.hx
+++ b/source/funkin/InitState.hx
@@ -1,6 +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;
@@ -11,7 +12,7 @@ import flixel.math.FlxRect;
import flixel.FlxSprite;
import flixel.system.debug.log.LogStyle;
import flixel.util.FlxColor;
-import funkin.ui.PreferencesMenu;
+import funkin.ui.options.PreferencesMenu;
import funkin.util.macro.MacroUtil;
import funkin.util.WindowUtil;
import funkin.play.PlayStatePlaylist;
@@ -29,11 +30,13 @@ 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.
@@ -231,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
@@ -282,6 +285,10 @@ class InitState extends FlxState
return;
}
+ // Load and cache the song's charts.
+ // TODO: Do this in the loading state.
+ songData.cacheCharts(true);
+
LoadingState.loadAndSwitchState(new funkin.play.PlayState(
{
targetSong: songData,
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/Options.hx b/source/funkin/Options.hx
deleted file mode 100644
index bc8a98570..000000000
--- a/source/funkin/Options.hx
+++ /dev/null
@@ -1,6 +0,0 @@
-package funkin;
-
-class Options
-{
- public static var masterVolume:Float = 1;
-}
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 e97cfe384..2e3e0e425 100644
--- a/source/funkin/PlayerSettings.hx
+++ b/source/funkin/PlayerSettings.hx
@@ -1,15 +1,16 @@
package funkin;
import funkin.save.Save;
-import funkin.Controls;
+import funkin.input.Controls;
import flixel.FlxCamera;
import funkin.input.PreciseInputManager;
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/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/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/song/SongData.hx b/source/funkin/data/song/SongData.hx
index c0bd26332..e79e1a3f4 100644
--- a/source/funkin/data/song/SongData.hx
+++ b/source/funkin/data/song/SongData.hx
@@ -453,7 +453,7 @@ class SongChartData
}
}
-class SongEventData
+class SongEventDataRaw
{
/**
* The timestamp of the event. The timestamp is in the format of the song's time format.
@@ -509,40 +509,67 @@ class SongEventData
return _stepTime = Conductor.getTimeInSteps(this.time);
}
+}
+
+/**
+ * Wrap SongEventData in an abstract so we can overload operators.
+ */
+@:forward(time, event, value, activated, getStepTime)
+abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataRaw
+{
+ public function new(time:Float, event:String, value:Dynamic = null)
+ {
+ this = new SongEventDataRaw(time, event, value);
+ }
public inline function getDynamic(key:String):Null
{
- return value == null ? null : Reflect.field(value, key);
+ return this.value == null ? null : Reflect.field(this.value, key);
}
public inline function getBool(key:String):Null
{
- return value == null ? null : cast Reflect.field(value, key);
+ return this.value == null ? null : cast Reflect.field(this.value, key);
}
public inline function getInt(key:String):Null
{
- return value == null ? null : cast Reflect.field(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 value == null ? null : cast Reflect.field(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
{
- return value == null ? null : cast Reflect.field(value, key);
+ return this.value == null ? null : cast Reflect.field(this.value, key);
}
public inline function getArray(key:String):Array
{
- return value == null ? null : cast Reflect.field(value, key);
+ return this.value == null ? null : cast Reflect.field(this.value, key);
}
public inline function getBoolArray(key:String):Array
{
- return value == null ? null : cast Reflect.field(value, key);
+ return this.value == null ? null : cast Reflect.field(this.value, key);
+ }
+
+ public function clone():SongEventData
+ {
+ return new SongEventData(this.time, this.event, this.value);
}
@:op(A == B)
@@ -590,7 +617,7 @@ class SongEventData
}
}
-class SongNoteData
+class SongNoteDataRaw
{
/**
* The timestamp of the note. The timestamp is in the format of the song's time format.
@@ -661,6 +688,48 @@ class SongNoteData
return _stepTime = Conductor.getTimeInSteps(this.time);
}
+ @:jignored
+ var _stepLength:Null = null;
+
+ /**
+ * @param force Set to `true` to force recalculation (good after BPM changes)
+ * @return The length of the hold note in steps, or `0` if this is not a hold note.
+ */
+ public function getStepLength(force = false):Float
+ {
+ if (this.length <= 0) return 0.0;
+
+ if (_stepLength != null && !force) return _stepLength;
+
+ return _stepLength = Conductor.getTimeInSteps(this.time + this.length) - getStepTime();
+ }
+
+ public function setStepLength(value:Float):Void
+ {
+ if (value <= 0)
+ {
+ this.length = 0.0;
+ }
+ else
+ {
+ var lengthMs:Float = Conductor.getStepTimeInMs(value) - this.time;
+ this.length = lengthMs;
+ }
+ _stepLength = null;
+ }
+}
+
+/**
+ * Wrap SongNoteData in an abstract so we can overload operators.
+ */
+@:forward
+abstract SongNoteData(SongNoteDataRaw) from SongNoteDataRaw to SongNoteDataRaw
+{
+ public function new(time:Float, data:Int, length:Float = 0, kind:String = '')
+ {
+ this = new SongNoteDataRaw(time, data, length, kind);
+ }
+
/**
* The direction of the note, if applicable.
* Strips the strumline index from the data.
@@ -674,7 +743,12 @@ class SongNoteData
public function getDirectionName(strumlineSize:Int = 4):String
{
- switch (this.data % strumlineSize)
+ return SongNoteData.buildDirectionName(this.data, strumlineSize);
+ }
+
+ public static function buildDirectionName(data:Int, strumlineSize:Int = 4):String
+ {
+ switch (data % strumlineSize)
{
case 0:
return 'Left';
@@ -711,36 +785,6 @@ class SongNoteData
return getStrumlineIndex(strumlineSize) == 0;
}
- @:jignored
- var _stepLength:Null = null;
-
- /**
- * @param force Set to `true` to force recalculation (good after BPM changes)
- * @return The length of the hold note in steps, or `0` if this is not a hold note.
- */
- public function getStepLength(force = false):Float
- {
- if (this.length <= 0) return 0.0;
-
- if (_stepLength != null && !force) return _stepLength;
-
- return _stepLength = Conductor.getTimeInSteps(this.time + this.length) - getStepTime();
- }
-
- public function setStepLength(value:Float):Void
- {
- if (value <= 0)
- {
- this.length = 0.0;
- }
- else
- {
- var lengthMs:Float = Conductor.getStepTimeInMs(value) - this.time;
- this.length = lengthMs;
- }
- _stepLength = null;
- }
-
@:jignored
public var isHoldNote(get, never):Bool;
@@ -803,6 +847,11 @@ class SongNoteData
return this.time <= other.time;
}
+ public function clone():SongNoteData
+ {
+ return new SongNoteData(this.time, this.data, this.length, this.kind);
+ }
+
/**
* Produces a string representation suitable for debugging.
*/
diff --git a/source/funkin/data/song/SongDataUtils.hx b/source/funkin/data/song/SongDataUtils.hx
index 3ff3943c6..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;
@@ -66,8 +67,14 @@ class SongDataUtils
var result = notes.filter(function(note:SongNoteData):Bool {
for (x in subtrahend)
+ {
+ // The currently iterated note is in the subtrahend array.
// SongNoteData's == operation has been overridden so that this will work.
- if (x == note) return false;
+ if (x == note)
+ {
+ return false;
+ }
+ }
return true;
});
@@ -157,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/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 1c3a0fdb4..5ca6b03db 100644
--- a/source/funkin/import.hx
+++ b/source/funkin/import.hx
@@ -12,7 +12,10 @@ 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;
using funkin.util.tools.IteratorTools;
using funkin.util.tools.MapTools;
using funkin.util.tools.SongEventDataArrayTools;
diff --git a/source/funkin/Controls.hx b/source/funkin/input/Controls.hx
similarity index 99%
rename from source/funkin/Controls.hx
rename to source/funkin/input/Controls.hx
index 9372c4dc6..d74502e6f 100644
--- a/source/funkin/Controls.hx
+++ b/source/funkin/input/Controls.hx
@@ -1,5 +1,4 @@
-
-package funkin;
+package funkin.input;
import flixel.input.gamepad.FlxGamepad;
import flixel.util.FlxDirectionFlags;
@@ -23,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);
@@ -1241,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/modding/events/ScriptEvent.hx b/source/funkin/modding/events/ScriptEvent.hx
index 586a6206c..18f934aee 100644
--- a/source/funkin/modding/events/ScriptEvent.hx
+++ b/source/funkin/modding/events/ScriptEvent.hx
@@ -10,265 +10,12 @@ import funkin.play.notes.NoteDirection;
import openfl.events.EventType;
import openfl.events.KeyboardEvent;
-typedef ScriptEventType = EventType;
-
/**
* This is a base class for all events that are issued to scripted classes.
* It can be used to identify the type of event called, store data, and cancel event propagation.
*/
class ScriptEvent
{
- /**
- * Called when the relevant object is created.
- * Keep in mind that the constructor may be called before the object is needed,
- * for the purposes of caching data or otherwise.
- *
- * This event is not cancelable.
- */
- public static inline final CREATE:ScriptEventType = 'CREATE';
-
- /**
- * Called when the relevant object is destroyed.
- * This should perform relevant cleanup to ensure good performance.
- *
- * This event is not cancelable.
- */
- public static inline final DESTROY:ScriptEventType = 'DESTROY';
-
- /**
- * Called when the relevent object is added to the game state.
- * This assumes all data is loaded and ready to go.
- *
- * This event is not cancelable.
- */
- public static inline final ADDED:ScriptEventType = 'ADDED';
-
- /**
- * Called during the update function.
- * This is called every frame, so be careful!
- *
- * This event is not cancelable.
- */
- public static inline final UPDATE:ScriptEventType = 'UPDATE';
-
- /**
- * Called when the player moves to pause the game.
- *
- * This event IS cancelable! Canceling the event will prevent the game from pausing.
- */
- public static inline final PAUSE:ScriptEventType = 'PAUSE';
-
- /**
- * Called when the player moves to unpause the game while paused.
- *
- * This event IS cancelable! Canceling the event will prevent the game from resuming.
- */
- public static inline final RESUME:ScriptEventType = 'RESUME';
-
- /**
- * Called once per step in the song. This happens 4 times per measure.
- *
- * This event is not cancelable.
- */
- public static inline final SONG_BEAT_HIT:ScriptEventType = 'BEAT_HIT';
-
- /**
- * Called once per step in the song. This happens 16 times per measure.
- *
- * This event is not cancelable.
- */
- public static inline final SONG_STEP_HIT:ScriptEventType = 'STEP_HIT';
-
- /**
- * Called when a character hits a note.
- * Important information such as judgement/timing, note data, player/opponent, etc. are all provided.
- *
- * This event IS cancelable! Canceling this event prevents the note from being hit,
- * and will likely result in a miss later.
- */
- public static inline final NOTE_HIT:ScriptEventType = 'NOTE_HIT';
-
- /**
- * Called when a character misses a note.
- * Important information such as note data, player/opponent, etc. are all provided.
- *
- * This event IS cancelable! Canceling this event prevents the note from being considered missed,
- * avoiding a combo break and lost health.
- */
- public static inline final NOTE_MISS:ScriptEventType = 'NOTE_MISS';
-
- /**
- * Called when a character presses a note when there was none there, causing them to lose health.
- * Important information such as direction pressed, etc. are all provided.
- *
- * This event IS cancelable! Canceling this event prevents the note from being considered missed,
- * avoiding lost health/score and preventing the miss animation.
- */
- public static inline final NOTE_GHOST_MISS:ScriptEventType = 'NOTE_GHOST_MISS';
-
- /**
- * Called when a song event is reached in the chart.
- *
- * This event IS cancelable! Cancelling this event prevents the event from being triggered,
- * thus blocking its normal functionality.
- */
- public static inline final SONG_EVENT:ScriptEventType = 'SONG_EVENT';
-
- /**
- * Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin.
- *
- * This event is not cancelable.
- */
- public static inline final SONG_START:ScriptEventType = 'SONG_START';
-
- /**
- * Called when the song ends. This happens as the instrumental and vocals end.
- *
- * This event is not cancelable.
- */
- public static inline final SONG_END:ScriptEventType = 'SONG_END';
-
- /**
- * Called when the countdown begins. This occurs before the song starts.
- *
- * This event IS cancelable! Canceling this event will prevent the countdown from starting.
- * - The song will not start until you call Countdown.performCountdown() later.
- * - Note that calling performCountdown() will trigger this event again, so be sure to add logic to ignore it.
- */
- public static inline final COUNTDOWN_START:ScriptEventType = 'COUNTDOWN_START';
-
- /**
- * Called when a step of the countdown happens.
- * Includes information about what step of the countdown was hit.
- *
- * This event IS cancelable! Canceling this event will pause the countdown.
- * - The countdown will not resume until you call PlayState.resumeCountdown().
- */
- public static inline final COUNTDOWN_STEP:ScriptEventType = 'COUNTDOWN_STEP';
-
- /**
- * Called when the countdown is done but just before the song starts.
- *
- * This event is not cancelable.
- */
- public static inline final COUNTDOWN_END:ScriptEventType = 'COUNTDOWN_END';
-
- /**
- * Called before the game over screen triggers and the death animation plays.
- *
- * This event is not cancelable.
- */
- public static inline final GAME_OVER:ScriptEventType = 'GAME_OVER';
-
- /**
- * Called after the player presses a key to restart the game.
- * This can happen from the pause menu or the game over screen.
- *
- * This event IS cancelable! Canceling this event will prevent the game from restarting.
- */
- public static inline final SONG_RETRY:ScriptEventType = 'SONG_RETRY';
-
- /**
- * Called when the player pushes down any key on the keyboard.
- *
- * This event is not cancelable.
- */
- public static inline final KEY_DOWN:ScriptEventType = 'KEY_DOWN';
-
- /**
- * Called when the player releases a key on the keyboard.
- *
- * This event is not cancelable.
- */
- public static inline final KEY_UP:ScriptEventType = 'KEY_UP';
-
- /**
- * Called when the game has finished loading the notes from JSON.
- * This allows modders to mutate the notes before they are used in the song.
- *
- * This event is not cancelable.
- */
- public static inline final SONG_LOADED:ScriptEventType = 'SONG_LOADED';
-
- /**
- * Called when the game is about to switch the current FlxState.
- *
- * This event is not cancelable.
- */
- public static inline final STATE_CHANGE_BEGIN:ScriptEventType = 'STATE_CHANGE_BEGIN';
-
- /**
- * Called when the game has finished switching the current FlxState.
- *
- * This event is not cancelable.
- */
- public static inline final STATE_CHANGE_END:ScriptEventType = 'STATE_CHANGE_END';
-
- /**
- * Called when the game is about to open a new FlxSubState.
- *
- * This event is not cancelable.
- */
- public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = 'SUBSTATE_OPEN_BEGIN';
-
- /**
- * Called when the game has finished opening a new FlxSubState.
- *
- * This event is not cancelable.
- */
- public static inline final SUBSTATE_OPEN_END:ScriptEventType = 'SUBSTATE_OPEN_END';
-
- /**
- * Called when the game is about to close the current FlxSubState.
- *
- * This event is not cancelable.
- */
- public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = 'SUBSTATE_CLOSE_BEGIN';
-
- /**
- * Called when the game has finished closing the current FlxSubState.
- *
- * This event is not cancelable.
- */
- public static inline final SUBSTATE_CLOSE_END:ScriptEventType = 'SUBSTATE_CLOSE_END';
-
- /**
- * Called when the game starts a conversation.
- *
- * This event is not cancelable.
- */
- public static inline final DIALOGUE_START:ScriptEventType = 'DIALOGUE_START';
-
- /**
- * Called to display the next line of conversation.
- *
- * This event IS cancelable! Canceling this event will prevent the conversation from moving to the next line.
- * - This event is called when the conversation starts, or when the user presses ACCEPT to advance the conversation.
- */
- public static inline final DIALOGUE_LINE:ScriptEventType = 'DIALOGUE_LINE';
-
- /**
- * Called to skip scrolling the current line of conversation.
- *
- * This event IS cancelable! Canceling this event will prevent the conversation from skipping to the next line.
- * - This event is called when the user presses ACCEPT to advance the conversation while it is already advancing.
- */
- public static inline final DIALOGUE_COMPLETE_LINE:ScriptEventType = 'DIALOGUE_COMPLETE_LINE';
-
- /**
- * Called to skip the conversation.
- *
- * This event IS cancelable! Canceling this event will prevent the conversation from skipping.
- */
- public static inline final DIALOGUE_SKIP:ScriptEventType = 'DIALOGUE_SKIP';
-
- /**
- * Called when the game ends a conversation.
- *
- * This event is not cancelable.
- */
- public static inline final DIALOGUE_END:ScriptEventType = 'DIALOGUE_END';
-
/**
* If true, the behavior associated with this event can be prevented.
* For example, cancelling COUNTDOWN_START should prevent the countdown from starting,
@@ -411,7 +158,7 @@ class GhostMissNoteScriptEvent extends ScriptEvent
public function new(dir:NoteDirection, hasPossibleNotes:Bool, healthChange:Float, scoreChange:Int):Void
{
- super(ScriptEvent.NOTE_GHOST_MISS, true);
+ super(NOTE_GHOST_MISS, true);
this.dir = dir;
this.hasPossibleNotes = hasPossibleNotes;
this.healthChange = healthChange;
@@ -439,7 +186,7 @@ class SongEventScriptEvent extends ScriptEvent
public function new(event:funkin.data.song.SongData.SongEventData):Void
{
- super(ScriptEvent.SONG_EVENT, true);
+ super(SONG_EVENT, true);
this.event = event;
}
@@ -462,7 +209,7 @@ class UpdateScriptEvent extends ScriptEvent
public function new(elapsed:Float):Void
{
- super(ScriptEvent.UPDATE, false);
+ super(UPDATE, false);
this.elapsed = elapsed;
}
@@ -591,7 +338,7 @@ class SongLoadScriptEvent extends ScriptEvent
public function new(id:String, difficulty:String, notes:Array):Void
{
- super(ScriptEvent.SONG_LOADED, false);
+ super(SONG_LOADED, false);
this.id = id;
this.difficulty = difficulty;
this.notes = notes;
@@ -660,7 +407,7 @@ class PauseScriptEvent extends ScriptEvent
public function new(gitaroo:Bool):Void
{
- super(ScriptEvent.PAUSE, true);
+ super(PAUSE, true);
this.gitaroo = gitaroo;
}
}
diff --git a/source/funkin/modding/events/ScriptEventDispatcher.hx b/source/funkin/modding/events/ScriptEventDispatcher.hx
index 5e3e32a46..f5d797ea4 100644
--- a/source/funkin/modding/events/ScriptEventDispatcher.hx
+++ b/source/funkin/modding/events/ScriptEventDispatcher.hx
@@ -23,15 +23,16 @@ class ScriptEventDispatcher
// IScriptedClass
switch (event.type)
{
- case ScriptEvent.CREATE:
+ case CREATE:
target.onCreate(event);
return;
- case ScriptEvent.DESTROY:
+ case DESTROY:
target.onDestroy(event);
return;
- case ScriptEvent.UPDATE:
+ case UPDATE:
target.onUpdate(cast event);
return;
+ default: // Continue;
}
if (Std.isOfType(target, IStateStageProp))
@@ -39,9 +40,10 @@ class ScriptEventDispatcher
var t:IStateStageProp = cast(target, IStateStageProp);
switch (event.type)
{
- case ScriptEvent.ADDED:
+ case ADDED:
t.onAdd(cast event);
return;
+ default: // Continue;
}
}
@@ -50,21 +52,22 @@ class ScriptEventDispatcher
var t:IDialogueScriptedClass = cast(target, IDialogueScriptedClass);
switch (event.type)
{
- case ScriptEvent.DIALOGUE_START:
+ case DIALOGUE_START:
t.onDialogueStart(cast event);
return;
- case ScriptEvent.DIALOGUE_LINE:
+ case DIALOGUE_LINE:
t.onDialogueLine(cast event);
return;
- case ScriptEvent.DIALOGUE_COMPLETE_LINE:
+ case DIALOGUE_COMPLETE_LINE:
t.onDialogueCompleteLine(cast event);
return;
- case ScriptEvent.DIALOGUE_SKIP:
+ case DIALOGUE_SKIP:
t.onDialogueSkip(cast event);
return;
- case ScriptEvent.DIALOGUE_END:
+ case DIALOGUE_END:
t.onDialogueEnd(cast event);
return;
+ default: // Continue;
}
}
@@ -73,54 +76,55 @@ class ScriptEventDispatcher
var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass);
switch (event.type)
{
- case ScriptEvent.NOTE_HIT:
+ case NOTE_HIT:
t.onNoteHit(cast event);
return;
- case ScriptEvent.NOTE_MISS:
+ case NOTE_MISS:
t.onNoteMiss(cast event);
return;
- case ScriptEvent.NOTE_GHOST_MISS:
+ case NOTE_GHOST_MISS:
t.onNoteGhostMiss(cast event);
return;
- case ScriptEvent.SONG_BEAT_HIT:
+ case SONG_BEAT_HIT:
t.onBeatHit(cast event);
return;
- case ScriptEvent.SONG_STEP_HIT:
+ case SONG_STEP_HIT:
t.onStepHit(cast event);
return;
- case ScriptEvent.SONG_START:
+ case SONG_START:
t.onSongStart(event);
return;
- case ScriptEvent.SONG_END:
+ case SONG_END:
t.onSongEnd(event);
return;
- case ScriptEvent.SONG_RETRY:
+ case SONG_RETRY:
t.onSongRetry(event);
return;
- case ScriptEvent.GAME_OVER:
+ case GAME_OVER:
t.onGameOver(event);
return;
- case ScriptEvent.PAUSE:
+ case PAUSE:
t.onPause(cast event);
return;
- case ScriptEvent.RESUME:
+ case RESUME:
t.onResume(event);
return;
- case ScriptEvent.SONG_EVENT:
+ case SONG_EVENT:
t.onSongEvent(cast event);
return;
- case ScriptEvent.COUNTDOWN_START:
+ case COUNTDOWN_START:
t.onCountdownStart(cast event);
return;
- case ScriptEvent.COUNTDOWN_STEP:
+ case COUNTDOWN_STEP:
t.onCountdownStep(cast event);
return;
- case ScriptEvent.COUNTDOWN_END:
+ case COUNTDOWN_END:
t.onCountdownEnd(cast event);
return;
- case ScriptEvent.SONG_LOADED:
+ case SONG_LOADED:
t.onSongLoaded(cast event);
return;
+ default: // Continue;
}
}
@@ -129,24 +133,25 @@ class ScriptEventDispatcher
var t = cast(target, IStateChangingScriptedClass);
switch (event.type)
{
- case ScriptEvent.STATE_CHANGE_BEGIN:
+ case STATE_CHANGE_BEGIN:
t.onStateChangeBegin(cast event);
return;
- case ScriptEvent.STATE_CHANGE_END:
+ case STATE_CHANGE_END:
t.onStateChangeEnd(cast event);
return;
- case ScriptEvent.SUBSTATE_OPEN_BEGIN:
+ case SUBSTATE_OPEN_BEGIN:
t.onSubStateOpenBegin(cast event);
return;
- case ScriptEvent.SUBSTATE_OPEN_END:
+ case SUBSTATE_OPEN_END:
t.onSubStateOpenEnd(cast event);
return;
- case ScriptEvent.SUBSTATE_CLOSE_BEGIN:
+ case SUBSTATE_CLOSE_BEGIN:
t.onSubStateCloseBegin(cast event);
return;
- case ScriptEvent.SUBSTATE_CLOSE_END:
+ case SUBSTATE_CLOSE_END:
t.onSubStateCloseEnd(cast event);
return;
+ default: // Continue;
}
}
else
diff --git a/source/funkin/modding/events/ScriptEventType.hx b/source/funkin/modding/events/ScriptEventType.hx
new file mode 100644
index 000000000..e06b5ad24
--- /dev/null
+++ b/source/funkin/modding/events/ScriptEventType.hx
@@ -0,0 +1,271 @@
+package funkin.modding.events;
+
+enum abstract ScriptEventType(String) from String to String
+{
+ /**
+ * Called when the relevant object is created.
+ * Keep in mind that the constructor may be called before the object is needed,
+ * for the purposes of caching data or otherwise.
+ *
+ * This event is not cancelable.
+ */
+ var CREATE = 'CREATE';
+
+ /**
+ * Called when the relevant object is destroyed.
+ * This should perform relevant cleanup to ensure good performance.
+ *
+ * This event is not cancelable.
+ */
+ var DESTROY = 'DESTROY';
+
+ /**
+ * Called when the relevent object is added to the game state.
+ * This assumes all data is loaded and ready to go.
+ *
+ * This event is not cancelable.
+ */
+ var ADDED = 'ADDED';
+
+ /**
+ * Called during the update function.
+ * This is called every frame, so be careful!
+ *
+ * This event is not cancelable.
+ */
+ var UPDATE = 'UPDATE';
+
+ /**
+ * Called when the player moves to pause the game.
+ *
+ * This event IS cancelable! Canceling the event will prevent the game from pausing.
+ */
+ var PAUSE = 'PAUSE';
+
+ /**
+ * Called when the player moves to unpause the game while paused.
+ *
+ * This event IS cancelable! Canceling the event will prevent the game from resuming.
+ */
+ var RESUME = 'RESUME';
+
+ /**
+ * Called once per step in the song. This happens 4 times per measure.
+ *
+ * This event is not cancelable.
+ */
+ var SONG_BEAT_HIT = 'BEAT_HIT';
+
+ /**
+ * Called once per step in the song. This happens 16 times per measure.
+ *
+ * This event is not cancelable.
+ */
+ var SONG_STEP_HIT = 'STEP_HIT';
+
+ /**
+ * Called when a character hits a note.
+ * Important information such as judgement/timing, note data, player/opponent, etc. are all provided.
+ *
+ * This event IS cancelable! Canceling this event prevents the note from being hit,
+ * and will likely result in a miss later.
+ */
+ var NOTE_HIT = 'NOTE_HIT';
+
+ /**
+ * Called when a character misses a note.
+ * Important information such as note data, player/opponent, etc. are all provided.
+ *
+ * This event IS cancelable! Canceling this event prevents the note from being considered missed,
+ * avoiding a combo break and lost health.
+ */
+ var NOTE_MISS = 'NOTE_MISS';
+
+ /**
+ * Called when a character presses a note when there was none there, causing them to lose health.
+ * Important information such as direction pressed, etc. are all provided.
+ *
+ * This event IS cancelable! Canceling this event prevents the note from being considered missed,
+ * avoiding lost health/score and preventing the miss animation.
+ */
+ var NOTE_GHOST_MISS = 'NOTE_GHOST_MISS';
+
+ /**
+ * Called when a song event is reached in the chart.
+ *
+ * This event IS cancelable! Cancelling this event prevents the event from being triggered,
+ * thus blocking its normal functionality.
+ */
+ var SONG_EVENT = 'SONG_EVENT';
+
+ /**
+ * Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin.
+ *
+ * This event is not cancelable.
+ */
+ var SONG_START = 'SONG_START';
+
+ /**
+ * Called when the song ends. This happens as the instrumental and vocals end.
+ *
+ * This event is not cancelable.
+ */
+ var SONG_END = 'SONG_END';
+
+ /**
+ * Called when the countdown begins. This occurs before the song starts.
+ *
+ * This event IS cancelable! Canceling this event will prevent the countdown from starting.
+ * - The song will not start until you call Countdown.performCountdown() later.
+ * - Note that calling performCountdown() will trigger this event again, so be sure to add logic to ignore it.
+ */
+ var COUNTDOWN_START = 'COUNTDOWN_START';
+
+ /**
+ * Called when a step of the countdown happens.
+ * Includes information about what step of the countdown was hit.
+ *
+ * This event IS cancelable! Canceling this event will pause the countdown.
+ * - The countdown will not resume until you call PlayState.resumeCountdown().
+ */
+ var COUNTDOWN_STEP = 'COUNTDOWN_STEP';
+
+ /**
+ * Called when the countdown is done but just before the song starts.
+ *
+ * This event is not cancelable.
+ */
+ var COUNTDOWN_END = 'COUNTDOWN_END';
+
+ /**
+ * Called before the game over screen triggers and the death animation plays.
+ *
+ * This event is not cancelable.
+ */
+ var GAME_OVER = 'GAME_OVER';
+
+ /**
+ * Called after the player presses a key to restart the game.
+ * This can happen from the pause menu or the game over screen.
+ *
+ * This event IS cancelable! Canceling this event will prevent the game from restarting.
+ */
+ var SONG_RETRY = 'SONG_RETRY';
+
+ /**
+ * Called when the player pushes down any key on the keyboard.
+ *
+ * This event is not cancelable.
+ */
+ var KEY_DOWN = 'KEY_DOWN';
+
+ /**
+ * Called when the player releases a key on the keyboard.
+ *
+ * This event is not cancelable.
+ */
+ var KEY_UP = 'KEY_UP';
+
+ /**
+ * Called when the game has finished loading the notes from JSON.
+ * This allows modders to mutate the notes before they are used in the song.
+ *
+ * This event is not cancelable.
+ */
+ var SONG_LOADED = 'SONG_LOADED';
+
+ /**
+ * Called when the game is about to switch the current FlxState.
+ *
+ * This event is not cancelable.
+ */
+ var STATE_CHANGE_BEGIN = 'STATE_CHANGE_BEGIN';
+
+ /**
+ * Called when the game has finished switching the current FlxState.
+ *
+ * This event is not cancelable.
+ */
+ var STATE_CHANGE_END = 'STATE_CHANGE_END';
+
+ /**
+ * Called when the game is about to open a new FlxSubState.
+ *
+ * This event is not cancelable.
+ */
+ var SUBSTATE_OPEN_BEGIN = 'SUBSTATE_OPEN_BEGIN';
+
+ /**
+ * Called when the game has finished opening a new FlxSubState.
+ *
+ * This event is not cancelable.
+ */
+ var SUBSTATE_OPEN_END = 'SUBSTATE_OPEN_END';
+
+ /**
+ * Called when the game is about to close the current FlxSubState.
+ *
+ * This event is not cancelable.
+ */
+ var SUBSTATE_CLOSE_BEGIN = 'SUBSTATE_CLOSE_BEGIN';
+
+ /**
+ * Called when the game has finished closing the current FlxSubState.
+ *
+ * This event is not cancelable.
+ */
+ var SUBSTATE_CLOSE_END = 'SUBSTATE_CLOSE_END';
+
+ /**
+ * Called when the game starts a conversation.
+ *
+ * This event is not cancelable.
+ */
+ var DIALOGUE_START = 'DIALOGUE_START';
+
+ /**
+ * Called to display the next line of conversation.
+ *
+ * This event IS cancelable! Canceling this event will prevent the conversation from moving to the next line.
+ * - This event is called when the conversation starts, or when the user presses ACCEPT to advance the conversation.
+ */
+ var DIALOGUE_LINE = 'DIALOGUE_LINE';
+
+ /**
+ * Called to skip scrolling the current line of conversation.
+ *
+ * This event IS cancelable! Canceling this event will prevent the conversation from skipping to the next line.
+ * - This event is called when the user presses ACCEPT to advance the conversation while it is already advancing.
+ */
+ var DIALOGUE_COMPLETE_LINE = 'DIALOGUE_COMPLETE_LINE';
+
+ /**
+ * Called to skip the conversation.
+ *
+ * This event IS cancelable! Canceling this event will prevent the conversation from skipping.
+ */
+ var DIALOGUE_SKIP = 'DIALOGUE_SKIP';
+
+ /**
+ * Called when the game ends a conversation.
+ *
+ * This event is not cancelable.
+ */
+ var DIALOGUE_END = 'DIALOGUE_END';
+
+ /**
+ * Allow for comparing `ScriptEventType` to `String`.
+ */
+ @:op(A == B) private static inline function equals(a:ScriptEventType, b:String):Bool
+ {
+ return (a : String) == b;
+ }
+
+ /**
+ * Allow for comparing `ScriptEventType` to `String`.
+ */
+ @:op(A != B) private static inline function notEquals(a:ScriptEventType, b:String):Bool
+ {
+ return (a : String) != b;
+ }
+}
diff --git a/source/funkin/modding/module/ModuleHandler.hx b/source/funkin/modding/module/ModuleHandler.hx
index 3cc7b7984..4711e7419 100644
--- a/source/funkin/modding/module/ModuleHandler.hx
+++ b/source/funkin/modding/module/ModuleHandler.hx
@@ -55,7 +55,7 @@ class ModuleHandler
static function onStateSwitchComplete():Void
{
- callEvent(new StateChangeScriptEvent(ScriptEvent.STATE_CHANGE_END, FlxG.state, true));
+ callEvent(new StateChangeScriptEvent(STATE_CHANGE_END, FlxG.state, true));
}
static function addToModuleCache(module:Module):Void
@@ -119,7 +119,7 @@ class ModuleHandler
{
if (moduleCache != null)
{
- var event = new ScriptEvent(ScriptEvent.DESTROY, false);
+ var event = new ScriptEvent(DESTROY, false);
// Note: Ignore stopPropagation()
for (key => value in moduleCache)
@@ -148,6 +148,6 @@ class ModuleHandler
public static inline function callOnCreate():Void
{
- callEvent(new ScriptEvent(ScriptEvent.CREATE, false));
+ callEvent(new ScriptEvent(CREATE, false));
}
}
diff --git a/source/funkin/play/Countdown.hx b/source/funkin/play/Countdown.hx
index 9796c7161..d23574ce2 100644
--- a/source/funkin/play/Countdown.hx
+++ b/source/funkin/play/Countdown.hx
@@ -43,7 +43,7 @@ class Countdown
Conductor.update(PlayState.instance.startTimestamp + Conductor.beatLengthMs * -5);
// Handle onBeatHit events manually
// @:privateAccess
- // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, 0, 0));
+ // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0));
// The timer function gets called based on the beat of the song.
countdownTimer = new FlxTimer();
@@ -59,7 +59,7 @@ class Countdown
// onBeatHit events are now properly dispatched by the Conductor even at negative timestamps,
// so calling this is no longer necessary.
- // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, 0, 0));
+ // PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0));
// Countdown graphic.
showCountdownGraphic(countdownStep, isPixelStyle);
@@ -94,11 +94,11 @@ class Countdown
switch (index)
{
case BEFORE:
- event = new CountdownScriptEvent(ScriptEvent.COUNTDOWN_START, index);
+ event = new CountdownScriptEvent(COUNTDOWN_START, index);
case THREE | TWO | ONE | GO: // I didn't know you could use `|` in a switch/case block!
- event = new CountdownScriptEvent(ScriptEvent.COUNTDOWN_STEP, index);
+ event = new CountdownScriptEvent(COUNTDOWN_STEP, index);
case AFTER:
- event = new CountdownScriptEvent(ScriptEvent.COUNTDOWN_END, index, false);
+ event = new CountdownScriptEvent(COUNTDOWN_END, index, false);
default:
return true;
}
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 9a126e509..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.PreferencesMenu;
-import funkin.ui.stageBuildShit.StageOffsetSubState;
+import funkin.ui.transition.LoadingState;
+import funkin.play.components.PopUpStuff;
+import funkin.ui.options.PreferencesMenu;
+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)
@@ -682,7 +686,7 @@ class PlayState extends MusicBeatSubState
{
if (!assertChartExists()) return;
- dispatchEvent(new ScriptEvent(ScriptEvent.SONG_RETRY));
+ dispatchEvent(new ScriptEvent(SONG_RETRY));
resetCamera();
@@ -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)
@@ -867,7 +873,7 @@ class PlayState extends MusicBeatSubState
deathCounter += 1;
- dispatchEvent(new ScriptEvent(ScriptEvent.GAME_OVER));
+ dispatchEvent(new ScriptEvent(GAME_OVER));
// Disable updates, preventing animations in the background from playing.
persistentUpdate = false;
@@ -994,7 +1000,7 @@ class PlayState extends MusicBeatSubState
{
if (Std.isOfType(subState, PauseSubState))
{
- var event:ScriptEvent = new ScriptEvent(ScriptEvent.RESUME, true);
+ var event:ScriptEvent = new ScriptEvent(RESUME, true);
dispatchEvent(event);
@@ -1097,7 +1103,7 @@ class PlayState extends MusicBeatSubState
if (this.currentStage != null)
{
remove(currentStage);
- var event:ScriptEvent = new ScriptEvent(ScriptEvent.DESTROY, false);
+ var event:ScriptEvent = new ScriptEvent(DESTROY, false);
ScriptEventDispatcher.callEvent(currentStage, event);
currentStage = null;
}
@@ -1116,7 +1122,7 @@ class PlayState extends MusicBeatSubState
super.debug_refreshModules();
- var event:ScriptEvent = new ScriptEvent(ScriptEvent.CREATE, false);
+ var event:ScriptEvent = new ScriptEvent(CREATE, false);
ScriptEventDispatcher.callEvent(currentSong, event);
}
@@ -1332,7 +1338,7 @@ class PlayState extends MusicBeatSubState
if (currentStage != null)
{
// Actually create and position the sprites.
- var event:ScriptEvent = new ScriptEvent(ScriptEvent.CREATE, false);
+ var event:ScriptEvent = new ScriptEvent(CREATE, false);
ScriptEventDispatcher.callEvent(currentStage, event);
// Apply camera zoom level from stage data.
@@ -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)
@@ -1640,7 +1648,7 @@ class PlayState extends MusicBeatSubState
add(currentConversation);
refresh();
- var event:ScriptEvent = new ScriptEvent(ScriptEvent.CREATE, false);
+ var event:ScriptEvent = new ScriptEvent(CREATE, false);
ScriptEventDispatcher.callEvent(currentConversation, event);
}
@@ -1664,7 +1672,7 @@ class PlayState extends MusicBeatSubState
*/
function startSong():Void
{
- dispatchEvent(new ScriptEvent(ScriptEvent.SONG_START));
+ dispatchEvent(new ScriptEvent(SONG_START));
startingSong = false;
@@ -1783,7 +1791,7 @@ class PlayState extends MusicBeatSubState
// Call an event to allow canceling the note hit.
// NOTE: This is what handles the character animations!
- var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_HIT, note, 0, true);
+ var event:NoteScriptEvent = new NoteScriptEvent(NOTE_HIT, note, 0, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
@@ -1872,7 +1880,7 @@ class PlayState extends MusicBeatSubState
{
// Call an event to allow canceling the note miss.
// NOTE: This is what handles the character animations!
- var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_MISS, note, 0, true);
+ var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, 0, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
@@ -2021,7 +2029,7 @@ class PlayState extends MusicBeatSubState
function goodNoteHit(note:NoteSprite, input:PreciseInputEvent):Void
{
- var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_HIT, note, Highscore.tallies.combo + 1, true);
+ var event:NoteScriptEvent = new NoteScriptEvent(NOTE_HIT, note, Highscore.tallies.combo + 1, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
@@ -2053,7 +2061,7 @@ class PlayState extends MusicBeatSubState
// a MISS is when you let a note scroll past you!!
Highscore.tallies.missed++;
- var event:NoteScriptEvent = new NoteScriptEvent(ScriptEvent.NOTE_MISS, note, Highscore.tallies.combo, true);
+ var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Highscore.tallies.combo, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) return;
@@ -2385,7 +2393,7 @@ class PlayState extends MusicBeatSubState
*/
function endSong():Void
{
- dispatchEvent(new ScriptEvent(ScriptEvent.SONG_END));
+ dispatchEvent(new ScriptEvent(SONG_END));
#if sys
// spitter for ravy, teehee!!
@@ -2593,7 +2601,7 @@ class PlayState extends MusicBeatSubState
{
remove(currentStage);
currentStage.kill();
- dispatchEvent(new ScriptEvent(ScriptEvent.DESTROY, false));
+ dispatchEvent(new ScriptEvent(DESTROY, false));
currentStage = null;
}
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/play/character/CharacterData.hx b/source/funkin/play/character/CharacterData.hx
index 8be9f25c7..abe8bf992 100644
--- a/source/funkin/play/character/CharacterData.hx
+++ b/source/funkin/play/character/CharacterData.hx
@@ -254,7 +254,7 @@ class CharacterDataParser
char.debug = debug;
// Call onCreate only in the fetchCharacter() function, not at application initialization.
- ScriptEventDispatcher.callEvent(char, new ScriptEvent(ScriptEvent.CREATE));
+ ScriptEventDispatcher.callEvent(char, new ScriptEvent(CREATE));
return char;
}
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/Conversation.hx b/source/funkin/play/cutscene/dialogue/Conversation.hx
index 2b7db381c..46acf3f37 100644
--- a/source/funkin/play/cutscene/dialogue/Conversation.hx
+++ b/source/funkin/play/cutscene/dialogue/Conversation.hx
@@ -120,7 +120,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
this.alpha = 1.0;
// Start the dialogue.
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_START, this, false));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_START, this, false));
}
function setupMusic():Void
@@ -214,7 +214,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
return;
}
- ScriptEventDispatcher.callEvent(nextSpeaker, new ScriptEvent(ScriptEvent.CREATE, true));
+ ScriptEventDispatcher.callEvent(nextSpeaker, new ScriptEvent(CREATE, true));
currentSpeaker = nextSpeaker;
currentSpeaker.zIndex = 200;
@@ -258,7 +258,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
return;
}
- ScriptEventDispatcher.callEvent(nextDialogueBox, new ScriptEvent(ScriptEvent.CREATE, true));
+ ScriptEventDispatcher.callEvent(nextDialogueBox, new ScriptEvent(CREATE, true));
currentDialogueBox = nextDialogueBox;
currentDialogueBox.zIndex = 300;
@@ -293,7 +293,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
public function startConversation():Void
{
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_START, this, true));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_START, this, true));
}
/**
@@ -308,13 +308,13 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
switch (state)
{
case ConversationState.Start:
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_START, this, true));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_START, this, true));
case ConversationState.Opening:
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_COMPLETE_LINE, this, true));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_COMPLETE_LINE, this, true));
case ConversationState.Speaking:
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_COMPLETE_LINE, this, true));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_COMPLETE_LINE, this, true));
case ConversationState.Idle:
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_LINE, this, true));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_LINE, this, true));
case ConversationState.Ending:
// Skip the outro.
endOutro();
@@ -371,7 +371,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
*/
public function skipConversation():Void
{
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_SKIP, this, true));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_SKIP, this, true));
}
static var outroTween:FlxTween;
@@ -405,7 +405,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
public function endOutro():Void
{
outroTween = null;
- ScriptEventDispatcher.callEvent(this, new ScriptEvent(ScriptEvent.DESTROY, false));
+ ScriptEventDispatcher.callEvent(this, new ScriptEvent(DESTROY, false));
}
/**
@@ -445,7 +445,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
if (currentDialogueEntry >= currentDialogueEntryCount)
{
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_END, this, false));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_END, this, false));
}
else
{
@@ -485,7 +485,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
propagateEvent(event);
if (event.eventCanceled) return;
- dispatchEvent(new DialogueScriptEvent(ScriptEvent.DIALOGUE_END, this, false));
+ dispatchEvent(new DialogueScriptEvent(DIALOGUE_END, this, false));
}
public function onDialogueEnd(event:DialogueScriptEvent):Void
diff --git a/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx b/source/funkin/play/cutscene/dialogue/ConversationDebugState.hx
index 4d7f74a58..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.
@@ -30,7 +31,7 @@ class ConversationDebugState extends MusicBeatState
conversation.completeCallback = onConversationComplete;
add(conversation);
- ScriptEventDispatcher.callEvent(conversation, new ScriptEvent(ScriptEvent.CREATE, false));
+ ScriptEventDispatcher.callEvent(conversation, new ScriptEvent(CREATE, false));
}
function onConversationComplete():Void
diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx
index 60b995c06..369a4144a 100644
--- a/source/funkin/play/notes/Strumline.hx
+++ b/source/funkin/play/notes/Strumline.hx
@@ -12,7 +12,7 @@ import funkin.play.notes.NoteSplash;
import funkin.play.notes.NoteSprite;
import funkin.play.notes.SustainTrail;
import funkin.data.song.SongData.SongNoteData;
-import funkin.ui.PreferencesMenu;
+import funkin.ui.options.PreferencesMenu;
import funkin.util.SortUtil;
/**
diff --git a/source/funkin/play/notes/SustainTrail.hx b/source/funkin/play/notes/SustainTrail.hx
index f55799828..ab4bf5f16 100644
--- a/source/funkin/play/notes/SustainTrail.hx
+++ b/source/funkin/play/notes/SustainTrail.hx
@@ -8,7 +8,7 @@ import flixel.FlxSprite;
import flixel.graphics.FlxGraphic;
import flixel.graphics.tile.FlxDrawTrianglesItem;
import flixel.math.FlxMath;
-import funkin.ui.PreferencesMenu;
+import funkin.ui.options.PreferencesMenu;
/**
* This is based heavily on the `FlxStrip` class. It uses `drawTriangles()` to clip a sustain note
diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx
index 90920a710..b48eda224 100644
--- a/source/funkin/play/song/Song.hx
+++ b/source/funkin/play/song/Song.hx
@@ -47,8 +47,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry;
- final _metadata:Array;
-
+ // key = variation id, value = metadata
+ final _metadata:Map;
final variations:Array;
final difficulties:Map;
@@ -62,7 +62,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry 0) return _metadata[0]?.songName ?? DEFAULT_SONGNAME;
+ if (_metadata.size() > 0) return _metadata.get(Constants.DEFAULT_VARIATION)?.songName ?? DEFAULT_SONGNAME;
return DEFAULT_SONGNAME;
}
@@ -71,7 +71,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry 0) return _metadata[0]?.artist ?? DEFAULT_ARTIST;
+ if (_metadata.size() > 0) return _metadata.get(Constants.DEFAULT_VARIATION)?.artist ?? DEFAULT_ARTIST;
return DEFAULT_ARTIST;
}
@@ -88,7 +88,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry _data];
variations.clear();
variations.push(Constants.DEFAULT_VARIATION);
@@ -100,11 +100,11 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry
{
- return _metadata;
+ return _metadata.values();
}
/**
@@ -149,10 +149,10 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry = [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 89b85d14c..c8cb8ce66 100644
--- a/source/funkin/play/stage/Stage.hx
+++ b/source/funkin/play/stage/Stage.hx
@@ -7,6 +7,7 @@ import flixel.system.FlxAssets.FlxShader;
import flixel.util.FlxSort;
import funkin.modding.IScriptedClass;
import funkin.modding.events.ScriptEvent;
+import funkin.modding.events.ScriptEventType;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.play.character.BaseCharacter;
import funkin.play.stage.StageData.StageDataCharacter;
@@ -409,7 +410,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
// Add the character to the scene.
this.add(character);
- ScriptEventDispatcher.callEvent(character, new ScriptEvent(ScriptEvent.ADDED, false));
+ ScriptEventDispatcher.callEvent(character, new ScriptEvent(ADDED, false));
#if debug
debugIconGroup.add(debugIcon);
diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx
index dcf7f9f0d..efb3ee623 100644
--- a/source/funkin/save/Save.hx
+++ b/source/funkin/save/Save.hx
@@ -1,11 +1,13 @@
package funkin.save;
import flixel.util.FlxSave;
-import funkin.Controls.Device;
+import funkin.save.migrator.SaveDataMigrator;
+import thx.semver.Version;
+import funkin.input.Controls.Device;
import funkin.save.migrator.RawSaveData_v1_0_0;
import funkin.save.migrator.SaveDataMigrator;
-import funkin.ui.debug.charting.ChartEditorState.LiveInputStyle;
-import funkin.ui.debug.charting.ChartEditorThemeHandler.ChartEditorTheme;
+import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
+import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
import thx.semver.Version;
@:nullSafety
@@ -99,7 +101,7 @@ abstract Save(RawSaveData)
// Reasonable defaults.
previousFiles: [],
noteQuant: 3,
- liveInputStyle: LiveInputStyle.None,
+ chartEditorLiveInputStyle: ChartEditorLiveInputStyle.None,
theme: ChartEditorTheme.Light,
playtestStartTime: false,
downscroll: false,
@@ -192,21 +194,21 @@ abstract Save(RawSaveData)
return this.optionsChartEditor.noteQuant;
}
- public var chartEditorLiveInputStyle(get, set):LiveInputStyle;
+ public var chartEditorLiveInputStyle(get, set):ChartEditorLiveInputStyle;
- function get_chartEditorLiveInputStyle():LiveInputStyle
+ function get_chartEditorLiveInputStyle():ChartEditorLiveInputStyle
{
- if (this.optionsChartEditor.liveInputStyle == null) this.optionsChartEditor.liveInputStyle = LiveInputStyle.None;
+ if (this.optionsChartEditor.chartEditorLiveInputStyle == null) this.optionsChartEditor.chartEditorLiveInputStyle = ChartEditorLiveInputStyle.None;
- return this.optionsChartEditor.liveInputStyle;
+ return this.optionsChartEditor.chartEditorLiveInputStyle;
}
- function set_chartEditorLiveInputStyle(value:LiveInputStyle):LiveInputStyle
+ function set_chartEditorLiveInputStyle(value:ChartEditorLiveInputStyle):ChartEditorLiveInputStyle
{
// Set and apply.
- this.optionsChartEditor.liveInputStyle = value;
+ this.optionsChartEditor.chartEditorLiveInputStyle = value;
flush();
- return this.optionsChartEditor.liveInputStyle;
+ return this.optionsChartEditor.chartEditorLiveInputStyle;
}
public var chartEditorDownscroll(get, set):Bool;
@@ -938,9 +940,9 @@ typedef SaveDataChartEditorOptions =
/**
* Live input style in the Chart Editor.
- * @default `LiveInputStyle.None`
+ * @default `ChartEditorLiveInputStyle.None`
*/
- var ?liveInputStyle:LiveInputStyle;
+ var ?chartEditorLiveInputStyle:ChartEditorLiveInputStyle;
/**
* Theme in the Chart Editor.
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 77%
rename from source/funkin/MusicBeatState.hx
rename to source/funkin/ui/MusicBeatState.hx
index 9a986a8b5..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;
@@ -11,6 +12,7 @@ import funkin.modding.PolymodHandler;
import funkin.modding.events.ScriptEvent;
import funkin.modding.module.ModuleHandler;
import funkin.util.SortUtil;
+import funkin.input.Controls;
/**
* MusicBeatState actually represents the core utility FlxState of the game.
@@ -56,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));
}
@@ -117,7 +137,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
public function stepHit():Bool
{
- var event = new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep);
+ var event = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep);
dispatchEvent(event);
@@ -128,7 +148,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
public function beatHit():Bool
{
- var event = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep);
+ var event = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep);
dispatchEvent(event);
@@ -148,7 +168,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
override function startOutro(onComplete:() -> Void):Void
{
- var event = new StateChangeScriptEvent(ScriptEvent.STATE_CHANGE_BEGIN, null, true);
+ var event = new StateChangeScriptEvent(STATE_CHANGE_BEGIN, null, true);
dispatchEvent(event);
@@ -164,7 +184,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
public override function openSubState(targetSubState:FlxSubState):Void
{
- var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_BEGIN, targetSubState, true);
+ var event = new SubStateScriptEvent(SUBSTATE_OPEN_BEGIN, targetSubState, true);
dispatchEvent(event);
@@ -175,12 +195,12 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
function onOpenSubStateComplete(targetState:FlxSubState):Void
{
- dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_END, targetState, true));
+ dispatchEvent(new SubStateScriptEvent(SUBSTATE_OPEN_END, targetState, true));
}
public override function closeSubState():Void
{
- var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_CLOSE_BEGIN, this.subState, true);
+ var event = new SubStateScriptEvent(SUBSTATE_CLOSE_BEGIN, this.subState, true);
dispatchEvent(event);
@@ -191,6 +211,6 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
function onCloseSubStateComplete(targetState:FlxSubState):Void
{
- dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_CLOSE_END, targetState, true));
+ dispatchEvent(new SubStateScriptEvent(SUBSTATE_CLOSE_END, targetState, true));
}
}
diff --git a/source/funkin/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx
similarity index 93%
rename from source/funkin/MusicBeatSubState.hx
rename to source/funkin/ui/MusicBeatSubState.hx
index 31d1bd14c..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;
@@ -10,6 +11,7 @@ import funkin.modding.module.ModuleHandler;
import funkin.modding.PolymodHandler;
import funkin.util.SortUtil;
import flixel.util.FlxSort;
+import funkin.input.Controls;
/**
* MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
@@ -96,7 +98,7 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl
*/
public function stepHit():Bool
{
- var event:ScriptEvent = new SongTimeScriptEvent(ScriptEvent.SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep);
+ var event:ScriptEvent = new SongTimeScriptEvent(SONG_STEP_HIT, Conductor.currentBeat, Conductor.currentStep);
dispatchEvent(event);
@@ -112,7 +114,7 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl
*/
public function beatHit():Bool
{
- var event:ScriptEvent = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep);
+ var event:ScriptEvent = new SongTimeScriptEvent(SONG_BEAT_HIT, Conductor.currentBeat, Conductor.currentStep);
dispatchEvent(event);
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/ChartEditorCommand.hx b/source/funkin/ui/debug/charting/ChartEditorCommand.hx
deleted file mode 100644
index 98ca810d3..000000000
--- a/source/funkin/ui/debug/charting/ChartEditorCommand.hx
+++ /dev/null
@@ -1,879 +0,0 @@
-package funkin.ui.debug.charting;
-
-import haxe.ui.notifications.NotificationType;
-import haxe.ui.notifications.NotificationManager;
-import funkin.data.song.SongData.SongEventData;
-import funkin.data.song.SongData.SongNoteData;
-import funkin.data.song.SongDataUtils;
-
-using Lambda;
-
-/**
- * Actions in the chart editor are backed by the Command pattern
- * (see Bob Nystrom's book "Game Programming Patterns" for more info)
- *
- * To make a function compatible with the undo/redo history, create a new class
- * that implements ChartEditorCommand, then call `ChartEditorState.performCommand(new Command())`
- */
-interface ChartEditorCommand
-{
- /**
- * Calling this function should perform the action that this command represents.
- * @param state The ChartEditorState to perform the action on.
- */
- public function execute(state:ChartEditorState):Void;
-
- /**
- * Calling this function should perform the inverse of the action that this command represents,
- * effectively undoing the action.
- * @param state The ChartEditorState to undo the action on.
- */
- public function undo(state:ChartEditorState):Void;
-
- /**
- * Get a short description of the action (for the UI).
- * For example, return `Add Left Note` to display `Undo Add Left Note` in the menu.
- */
- public function toString():String;
-}
-
-@:nullSafety
-class AddNotesCommand implements ChartEditorCommand
-{
- var notes:Array;
- var appendToSelection:Bool;
-
- public function new(notes:Array, appendToSelection:Bool = false)
- {
- this.notes = notes;
- this.appendToSelection = appendToSelection;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- for (note in notes)
- {
- state.currentSongChartNoteData.push(note);
- }
-
- if (appendToSelection)
- {
- state.currentNoteSelection = state.currentNoteSelection.concat(notes);
- }
- else
- {
- state.currentNoteSelection = notes;
- state.currentEventSelection = [];
- }
-
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
- state.currentNoteSelection = [];
- state.currentEventSelection = [];
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- if (notes.length == 1)
- {
- var dir:String = notes[0].getDirectionName();
- return 'Add $dir Note';
- }
-
- return 'Add ${notes.length} Notes';
- }
-}
-
-@:nullSafety
-class RemoveNotesCommand implements ChartEditorCommand
-{
- var notes:Array;
-
- public function new(notes:Array)
- {
- this.notes = notes;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
- state.currentNoteSelection = [];
- state.currentEventSelection = [];
-
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- for (note in notes)
- {
- state.currentSongChartNoteData.push(note);
- }
- state.currentNoteSelection = notes;
- state.currentEventSelection = [];
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- if (notes.length == 1 && notes[0] != null)
- {
- var dir:String = notes[0].getDirectionName();
- return 'Remove $dir Note';
- }
-
- return 'Remove ${notes.length} Notes';
- }
-}
-
-/**
- * Appends one or more items to the selection.
- */
-@:nullSafety
-class SelectItemsCommand implements ChartEditorCommand
-{
- var notes:Array;
- var events:Array;
-
- public function new(notes:Array, events:Array)
- {
- this.notes = notes;
- this.events = events;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- for (note in this.notes)
- {
- state.currentNoteSelection.push(note);
- }
-
- for (event in this.events)
- {
- state.currentEventSelection.push(event);
- }
-
- // state.noteDisplayDirty = true;
- // state.notePreviewDirty = true;
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentNoteSelection = SongDataUtils.subtractNotes(state.currentNoteSelection, this.notes);
- state.currentEventSelection = SongDataUtils.subtractEvents(state.currentEventSelection, this.events);
-
- // state.noteDisplayDirty = true;
- // state.notePreviewDirty = true;
- }
-
- public function toString():String
- {
- var len:Int = notes.length + events.length;
-
- if (notes.length == 0)
- {
- if (events.length == 1)
- {
- return 'Select Event';
- }
- else
- {
- return 'Select ${events.length} Events';
- }
- }
- else if (events.length == 0)
- {
- if (notes.length == 1)
- {
- return 'Select Note';
- }
- else
- {
- return 'Select ${notes.length} Notes';
- }
- }
-
- return 'Select ${len} Items';
- }
-}
-
-@:nullSafety
-class AddEventsCommand implements ChartEditorCommand
-{
- var events:Array;
- var appendToSelection:Bool;
-
- public function new(events:Array, appendToSelection:Bool = false)
- {
- this.events = events;
- this.appendToSelection = appendToSelection;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- for (event in events)
- {
- state.currentSongChartEventData.push(event);
- }
-
- if (appendToSelection)
- {
- state.currentEventSelection = state.currentEventSelection.concat(events);
- }
- else
- {
- state.currentNoteSelection = [];
- state.currentEventSelection = events;
- }
-
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
-
- state.currentNoteSelection = [];
- state.currentEventSelection = [];
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- var len:Int = events.length;
- return 'Add $len Events';
- }
-}
-
-@:nullSafety
-class RemoveEventsCommand implements ChartEditorCommand
-{
- var events:Array;
-
- public function new(events:Array)
- {
- this.events = events;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
- state.currentEventSelection = [];
-
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- for (event in events)
- {
- state.currentSongChartEventData.push(event);
- }
- state.currentEventSelection = events;
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- if (events.length == 1 && events[0] != null)
- {
- return 'Remove Event';
- }
-
- return 'Remove ${events.length} Events';
- }
-}
-
-@:nullSafety
-class RemoveItemsCommand implements ChartEditorCommand
-{
- var notes:Array;
- var events:Array;
-
- public function new(notes:Array, events:Array)
- {
- this.notes = notes;
- this.events = events;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
- state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
-
- state.currentNoteSelection = [];
- state.currentEventSelection = [];
-
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- for (note in notes)
- {
- state.currentSongChartNoteData.push(note);
- }
-
- for (event in events)
- {
- state.currentSongChartEventData.push(event);
- }
-
- state.currentNoteSelection = notes;
- state.currentEventSelection = events;
-
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- return 'Remove ${notes.length + events.length} Items';
- }
-}
-
-@:nullSafety
-class SwitchDifficultyCommand implements ChartEditorCommand
-{
- var prevDifficulty:String;
- var newDifficulty:String;
- var prevVariation:String;
- var newVariation:String;
-
- public function new(prevDifficulty:String, newDifficulty:String, prevVariation:String, newVariation:String)
- {
- this.prevDifficulty = prevDifficulty;
- this.newDifficulty = newDifficulty;
- this.prevVariation = prevVariation;
- this.newVariation = newVariation;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.selectedVariation = newVariation != null ? newVariation : prevVariation;
- state.selectedDifficulty = newDifficulty != null ? newDifficulty : prevDifficulty;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.selectedVariation = prevVariation != null ? prevVariation : newVariation;
- state.selectedDifficulty = prevDifficulty != null ? prevDifficulty : newDifficulty;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function toString():String
- {
- return 'Switch Difficulty';
- }
-}
-
-@:nullSafety
-class DeselectItemsCommand implements ChartEditorCommand
-{
- var notes:Array;
- var events:Array;
-
- public function new(notes:Array, events:Array)
- {
- this.notes = notes;
- this.events = events;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentNoteSelection = SongDataUtils.subtractNotes(state.currentNoteSelection, this.notes);
- state.currentEventSelection = SongDataUtils.subtractEvents(state.currentEventSelection, this.events);
-
- // state.noteDisplayDirty = true;
- // state.notePreviewDirty = true;
- }
-
- public function undo(state:ChartEditorState):Void
- {
- for (note in this.notes)
- {
- state.currentNoteSelection.push(note);
- }
-
- for (event in this.events)
- {
- state.currentEventSelection.push(event);
- }
-
- // state.noteDisplayDirty = true;
- // state.notePreviewDirty = true;
- }
-
- public function toString():String
- {
- var noteCount = notes.length + events.length;
-
- if (noteCount == 1)
- {
- var dir:String = notes[0].getDirectionName();
- return 'Deselect $dir Items';
- }
-
- return 'Deselect ${noteCount} Items';
- }
-}
-
-/**
- * Sets the selection rather than appends it.
- * Deselects any notes that are not in the new selection.
- */
-@:nullSafety
-class SetItemSelectionCommand implements ChartEditorCommand
-{
- var notes:Array;
- var events:Array;
- var previousNoteSelection:Array;
- var previousEventSelection:Array;
-
- public function new(notes:Array, events:Array, previousNoteSelection:Array,
- previousEventSelection:Array)
- {
- this.notes = notes;
- this.events = events;
- this.previousNoteSelection = previousNoteSelection == null ? [] : previousNoteSelection;
- this.previousEventSelection = previousEventSelection == null ? [] : previousEventSelection;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentNoteSelection = notes;
- state.currentEventSelection = events;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentNoteSelection = previousNoteSelection;
- state.currentEventSelection = previousEventSelection;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function toString():String
- {
- return 'Select ${notes.length} Items';
- }
-}
-
-@:nullSafety
-class SelectAllItemsCommand implements ChartEditorCommand
-{
- var previousNoteSelection:Array;
- var previousEventSelection:Array;
-
- public function new(?previousNoteSelection:Array, ?previousEventSelection:Array)
- {
- this.previousNoteSelection = previousNoteSelection == null ? [] : previousNoteSelection;
- this.previousEventSelection = previousEventSelection == null ? [] : previousEventSelection;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentNoteSelection = state.currentSongChartNoteData;
- state.currentEventSelection = state.currentSongChartEventData;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentNoteSelection = previousNoteSelection;
- state.currentEventSelection = previousEventSelection;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function toString():String
- {
- return 'Select All Items';
- }
-}
-
-@:nullSafety
-class InvertSelectedItemsCommand implements ChartEditorCommand
-{
- var previousNoteSelection:Array;
- var previousEventSelection:Array;
-
- public function new(?previousNoteSelection:Array, ?previousEventSelection:Array)
- {
- this.previousNoteSelection = previousNoteSelection == null ? [] : previousNoteSelection;
- this.previousEventSelection = previousEventSelection == null ? [] : previousEventSelection;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentNoteSelection = SongDataUtils.subtractNotes(state.currentSongChartNoteData, previousNoteSelection);
- state.currentEventSelection = SongDataUtils.subtractEvents(state.currentSongChartEventData, previousEventSelection);
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentNoteSelection = previousNoteSelection;
- state.currentEventSelection = previousEventSelection;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function toString():String
- {
- return 'Invert Selected Items';
- }
-}
-
-@:nullSafety
-class DeselectAllItemsCommand implements ChartEditorCommand
-{
- var previousNoteSelection:Array;
- var previousEventSelection:Array;
-
- public function new(?previousNoteSelection:Array, ?previousEventSelection:Array)
- {
- this.previousNoteSelection = previousNoteSelection == null ? [] : previousNoteSelection;
- this.previousEventSelection = previousEventSelection == null ? [] : previousEventSelection;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- state.currentNoteSelection = [];
- state.currentEventSelection = [];
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentNoteSelection = previousNoteSelection;
- state.currentEventSelection = previousEventSelection;
-
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- }
-
- public function toString():String
- {
- return 'Deselect All Items';
- }
-}
-
-@:nullSafety
-class CutItemsCommand implements ChartEditorCommand
-{
- var notes:Array;
- var events:Array;
-
- public function new(notes:Array, events:Array)
- {
- this.notes = notes;
- this.events = events;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- // Copy the notes.
- SongDataUtils.writeItemsToClipboard(
- {
- notes: SongDataUtils.buildNoteClipboard(notes),
- events: SongDataUtils.buildEventClipboard(events)
- });
-
- // Delete the notes.
- state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
- state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
- state.currentNoteSelection = [];
- state.currentEventSelection = [];
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentSongChartNoteData = state.currentSongChartNoteData.concat(notes);
- state.currentSongChartEventData = state.currentSongChartEventData.concat(events);
-
- state.currentNoteSelection = notes;
- state.currentEventSelection = events;
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- state.sortChartData();
- }
-
- public function toString():String
- {
- var len:Int = notes.length + events.length;
-
- if (notes.length == 0) return 'Cut $len Events to Clipboard';
- else if (events.length == 0) return 'Cut $len Notes to Clipboard';
- else
- return 'Cut $len Items to Clipboard';
- }
-}
-
-@:nullSafety
-class FlipNotesCommand implements ChartEditorCommand
-{
- var notes:Array = [];
- var flippedNotes:Array = [];
-
- public function new(notes:Array)
- {
- this.notes = notes;
- this.flippedNotes = SongDataUtils.flipNotes(notes);
- }
-
- public function execute(state:ChartEditorState):Void
- {
- // Delete the notes.
- state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
-
- // Add the flipped notes.
- state.currentSongChartNoteData = state.currentSongChartNoteData.concat(flippedNotes);
-
- state.currentNoteSelection = flippedNotes;
- state.currentEventSelection = [];
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, flippedNotes);
- state.currentSongChartNoteData = state.currentSongChartNoteData.concat(notes);
-
- state.currentNoteSelection = notes;
- state.currentEventSelection = [];
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- var len:Int = notes.length;
- return 'Flip $len Notes';
- }
-}
-
-@:nullSafety
-class PasteItemsCommand implements ChartEditorCommand
-{
- var targetTimestamp:Float;
- // Notes we added with this command, for undo.
- var addedNotes:Array = [];
- var addedEvents:Array = [];
-
- public function new(targetTimestamp:Float)
- {
- this.targetTimestamp = targetTimestamp;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- var currentClipboard:SongClipboardItems = SongDataUtils.readItemsFromClipboard();
-
- if (currentClipboard.valid != true)
- {
- #if !mac
- NotificationManager.instance.addNotification(
- {
- title: 'Failed to Paste',
- body: 'Could not parse clipboard contents.',
- type: NotificationType.Error,
- expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
- });
- #end
- return;
- }
-
- trace(currentClipboard.notes);
-
- addedNotes = SongDataUtils.offsetSongNoteData(currentClipboard.notes, Std.int(targetTimestamp));
- addedEvents = SongDataUtils.offsetSongEventData(currentClipboard.events, Std.int(targetTimestamp));
-
- state.currentSongChartNoteData = state.currentSongChartNoteData.concat(addedNotes);
- state.currentSongChartEventData = state.currentSongChartEventData.concat(addedEvents);
- state.currentNoteSelection = addedNotes.copy();
- state.currentEventSelection = addedEvents.copy();
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
-
- #if !mac
- NotificationManager.instance.addNotification(
- {
- title: 'Paste Successful',
- body: 'Successfully pasted clipboard contents.',
- type: NotificationType.Success,
- expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
- });
- #end
- }
-
- public function undo(state:ChartEditorState):Void
- {
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
-
- state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, addedNotes);
- state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, addedEvents);
- state.currentNoteSelection = [];
- state.currentEventSelection = [];
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- var currentClipboard:SongClipboardItems = SongDataUtils.readItemsFromClipboard();
-
- var len:Int = currentClipboard.notes.length + currentClipboard.events.length;
-
- if (currentClipboard.notes.length == 0) return 'Paste $len Events';
- else if (currentClipboard.events.length == 0) return 'Paste $len Notes';
- else
- return 'Paste $len Items';
- }
-}
-
-@:nullSafety
-class ExtendNoteLengthCommand implements ChartEditorCommand
-{
- var note:SongNoteData;
- var oldLength:Float;
- var newLength:Float;
-
- public function new(note:SongNoteData, newLength:Float)
- {
- this.note = note;
- this.oldLength = note.length;
- this.newLength = newLength;
- }
-
- public function execute(state:ChartEditorState):Void
- {
- note.length = newLength;
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function undo(state:ChartEditorState):Void
- {
- ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
-
- note.length = oldLength;
-
- state.saveDataDirty = true;
- state.noteDisplayDirty = true;
- state.notePreviewDirty = true;
-
- state.sortChartData();
- }
-
- public function toString():String
- {
- return 'Extend Note Length';
- }
-}
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 7babd6038..aa5372327 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -17,41 +17,65 @@ 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;
import funkin.audio.VoicesGroup;
import funkin.data.notestyle.NoteStyleRegistry;
-import funkin.data.song.SongData.SongCharacterData;
import funkin.data.song.SongData.SongChartData;
import funkin.data.song.SongData.SongEventData;
import funkin.data.song.SongData.SongMetadata;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongDataUtils;
-import funkin.data.song.SongRegistry;
import funkin.input.Cursor;
import funkin.input.TurboKeyHandler;
import funkin.modding.events.ScriptEvent;
import funkin.play.character.BaseCharacter.CharacterType;
import funkin.play.character.CharacterData;
import funkin.play.character.CharacterData.CharacterDataParser;
-import funkin.play.HealthIcon;
+import funkin.play.components.HealthIcon;
import funkin.play.notes.NoteSprite;
-import funkin.play.notes.Strumline;
import funkin.play.PlayState;
import funkin.play.song.Song;
+import funkin.data.song.SongData.SongChartData;
+import funkin.data.song.SongRegistry;
+import funkin.data.song.SongData.SongEventData;
+import funkin.data.song.SongData.SongMetadata;
+import funkin.data.song.SongData.SongNoteData;
+import funkin.data.song.SongData.SongCharacterData;
+import funkin.data.song.SongDataUtils;
+import funkin.ui.debug.charting.commands.ChartEditorCommand;
+import funkin.ui.debug.charting.handlers.ChartEditorShortcutHandler;
import funkin.play.stage.StageData;
import funkin.save.Save;
-import funkin.ui.debug.charting.ChartEditorCommand;
-import funkin.ui.debug.charting.ChartEditorThemeHandler.ChartEditorTheme;
-import funkin.ui.debug.charting.ChartEditorToolboxHandler.ChartEditorToolMode;
+import funkin.ui.debug.charting.commands.AddEventsCommand;
+import funkin.ui.debug.charting.commands.AddNotesCommand;
+import funkin.ui.debug.charting.commands.ChartEditorCommand;
+import funkin.ui.debug.charting.commands.CutItemsCommand;
+import funkin.ui.debug.charting.commands.DeselectAllItemsCommand;
+import funkin.ui.debug.charting.commands.DeselectItemsCommand;
+import funkin.ui.debug.charting.commands.ExtendNoteLengthCommand;
+import funkin.ui.debug.charting.commands.FlipNotesCommand;
+import funkin.ui.debug.charting.commands.InvertSelectedItemsCommand;
+import funkin.ui.debug.charting.commands.MoveEventsCommand;
+import funkin.ui.debug.charting.commands.MoveItemsCommand;
+import funkin.ui.debug.charting.commands.MoveNotesCommand;
+import funkin.ui.debug.charting.commands.PasteItemsCommand;
+import funkin.ui.debug.charting.commands.RemoveEventsCommand;
+import funkin.ui.debug.charting.commands.RemoveItemsCommand;
+import funkin.ui.debug.charting.commands.RemoveNotesCommand;
+import funkin.ui.debug.charting.commands.SelectAllItemsCommand;
+import funkin.ui.debug.charting.commands.SelectItemsCommand;
+import funkin.ui.debug.charting.commands.SetItemSelectionCommand;
+import funkin.ui.debug.charting.components.ChartEditorEventSprite;
+import funkin.ui.debug.charting.components.ChartEditorHoldNoteSprite;
+import funkin.ui.debug.charting.components.ChartEditorNotePreview;
+import funkin.ui.debug.charting.components.ChartEditorNoteSprite;
+import funkin.ui.debug.charting.components.ChartEditorSelectionSquareSprite;
import funkin.ui.haxeui.components.CharacterPlayer;
-import funkin.ui.haxeui.components.FunkinMenuItem;
import funkin.ui.haxeui.HaxeUIState;
import funkin.util.Constants;
-import funkin.util.DateUtil;
-import funkin.util.FileUtil;
-import funkin.util.SerializerUtil;
import funkin.util.SortUtil;
import funkin.util.WindowUtil;
import haxe.DynamicAccess;
@@ -65,17 +89,18 @@ import haxe.ui.components.TextField;
import haxe.ui.containers.dialogs.CollapsibleDialog;
import haxe.ui.containers.Frame;
import haxe.ui.containers.menus.Menu;
+import haxe.ui.containers.menus.MenuItem;
import haxe.ui.containers.TreeView;
import haxe.ui.containers.TreeViewNode;
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.Assets;
import openfl.display.BitmapData;
-import openfl.geom.Rectangle;
+import funkin.util.FileUtil;
using Lambda;
@@ -88,14 +113,6 @@ using Lambda;
* @author MasterEric
*/
@:nullSafety
-// Give other classes access to private instance fields
-@:allow(funkin.ui.debug.charting.ChartEditorCommand)
-@:allow(funkin.ui.debug.charting.ChartEditorDropdowns)
-@:allow(funkin.ui.debug.charting.ChartEditorDialogHandler)
-@:allow(funkin.ui.debug.charting.ChartEditorThemeHandler)
-@:allow(funkin.ui.debug.charting.ChartEditorAudioHandler)
-@:allow(funkin.ui.debug.charting.ChartEditorImportExportHandler)
-@:allow(funkin.ui.debug.charting.ChartEditorToolboxHandler)
class ChartEditorState extends HaxeUIState
{
/**
@@ -103,129 +120,223 @@ class ChartEditorState extends HaxeUIState
*/
// ==============================
// XML Layouts
- static final CHART_EDITOR_LAYOUT:String = Paths.ui('chart-editor/main-view');
+ public static final CHART_EDITOR_LAYOUT:String = Paths.ui('chart-editor/main-view');
- static final CHART_EDITOR_NOTIFBAR_LAYOUT:String = Paths.ui('chart-editor/components/notifbar');
- static final CHART_EDITOR_PLAYBARHEAD_LAYOUT:String = Paths.ui('chart-editor/components/playbar-head');
+ public static final CHART_EDITOR_NOTIFBAR_LAYOUT:String = Paths.ui('chart-editor/components/notifbar');
+ public static final CHART_EDITOR_PLAYBARHEAD_LAYOUT:String = Paths.ui('chart-editor/components/playbar-head');
- static final CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:String = Paths.ui('chart-editor/toolbox/tools');
- static final CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/notedata');
- static final CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/eventdata');
- static final CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/metadata');
- static final CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:String = Paths.ui('chart-editor/toolbox/difficulty');
- static final CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:String = Paths.ui('chart-editor/toolbox/player-preview');
- static final CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:String = Paths.ui('chart-editor/toolbox/opponent-preview');
+ public static final CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/notedata');
+ public static final CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/eventdata');
+ public static final CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:String = Paths.ui('chart-editor/toolbox/metadata');
+ public static final CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:String = Paths.ui('chart-editor/toolbox/difficulty');
+ public static final CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:String = Paths.ui('chart-editor/toolbox/player-preview');
+ public static final CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:String = Paths.ui('chart-editor/toolbox/opponent-preview');
// Validation
- static final SUPPORTED_MUSIC_FORMATS:Array = ['ogg'];
+ public static final SUPPORTED_MUSIC_FORMATS:Array = ['ogg'];
+
+ // Layout
/**
* The base grid size for the chart editor.
*/
public static final GRID_SIZE:Int = 40;
+ /**
+ * The width of the scroll area.
+ */
public static final PLAYHEAD_SCROLL_AREA_WIDTH:Int = 12;
+ /**
+ * The height of the playhead, in pixels.
+ */
public static final PLAYHEAD_HEIGHT:Int = Std.int(GRID_SIZE / 8);
+ /**
+ * The width of the border between grid squares, where the crosshair changes from "Place Notes" to "Select Notes".
+ */
public static final GRID_SELECTION_BORDER_WIDTH:Int = 6;
+ /**
+ * The height of the menu bar in the layout.
+ */
+ public static final MENU_BAR_HEIGHT:Int = 32;
+
+ /**
+ * The height of the playbar in the layout.
+ */
+ public static final PLAYBAR_HEIGHT:Int = 48;
+
+ /**
+ * The amount of padding between the menu bar and the chart grid when fully scrolled up.
+ */
+ public static final GRID_TOP_PAD:Int = 8;
+
+ // Colors
+ // Background color tint.
+ public static final CURSOR_COLOR:FlxColor = 0xE0FFFFFF;
+ public static final PREVIEW_BG_COLOR:FlxColor = 0xFF303030;
+ public static final PLAYHEAD_SCROLL_AREA_COLOR:FlxColor = 0xFF682B2F;
+ public static final SPECTROGRAM_COLOR:FlxColor = 0xFFFF0000;
+ public static final PLAYHEAD_COLOR:FlxColor = 0xC0BD0231;
+
+ // Timings
+
+ /**
+ * Duration, in seconds, for the scroll easing animation.
+ */
+ public static final SCROLL_EASE_DURATION:Float = 0.2;
+
+ // Other
+
/**
* Number of notes in each player's strumline.
*/
public static final STRUMLINE_SIZE:Int = 4;
- /**
- * The height of the menu bar in the layout.
- */
- static final MENU_BAR_HEIGHT:Int = 32;
-
- /**
- * The height of the playbar in the layout.
- */
- static final PLAYBAR_HEIGHT:Int = 48;
-
- /**
- * Duration to wait before autosaving the chart.
- */
- static final AUTOSAVE_TIMER_DELAY:Float = 60.0 * 5.0;
-
- /**
- * The amount of padding between the menu bar and the chart grid when fully scrolled up.
- */
- static final GRID_TOP_PAD:Int = 8;
-
- /**
- * Duration, in milliseconds, until toast notifications are automatically hidden.
- */
- static final NOTIFICATION_DISMISS_TIME:Int = 5000;
-
- /**
- * Duration, in seconds, for the scroll easing animation.
- */
- static final SCROLL_EASE_DURATION:Float = 0.2;
-
- // UI Element Colors
- // Background color tint.
- static final CURSOR_COLOR:FlxColor = 0xE0FFFFFF;
- static final PREVIEW_BG_COLOR:FlxColor = 0xFF303030;
- static final PLAYHEAD_SCROLL_AREA_COLOR:FlxColor = 0xFF682B2F;
- static final SPECTROGRAM_COLOR:FlxColor = 0xFFFF0000;
- static final PLAYHEAD_COLOR:FlxColor = 0xC0BD0231;
-
/**
* How many pixels far the user needs to move the mouse before the cursor is considered to be dragged rather than clicked.
*/
- static final DRAG_THRESHOLD:Float = 16.0;
+ public static final DRAG_THRESHOLD:Float = 16.0;
/**
- * Types of notes you can snap to.
+ * Precisions of notes you can snap to.
*/
- static final SNAP_QUANTS:Array = [4, 8, 12, 16, 20, 24, 32, 48, 64, 96, 192];
+ public static final SNAP_QUANTS:Array = [4, 8, 12, 16, 20, 24, 32, 48, 64, 96, 192];
- static final BASE_QUANT:Int = 16;
+ /**
+ * The default note snapping value.
+ */
+ public static final BASE_QUANT:Int = 16;
+
+ /**
+ * The index of thet default note snapping value in the `SNAP_QUANTS` array.
+ */
+ public static final BASE_QUANT_INDEX:Int = 3;
/**
* INSTANCE DATA
*/
// ==============================
+ // Song Length
/**
- * The internal index of what note snapping value is in use.
- * Increment to make placement more preceise and decrement to make placement less precise.
+ * The length of the current instrumental, in milliseconds.
*/
- var noteSnapQuantIndex:Int = 3; // default is 16
+ @:isVar var songLengthInMs(get, set):Float = 0;
- /**
- * The current note snapping value.
- * For example, `32` when snapping to 32nd notes.
- */
- public var noteSnapQuant(get, never):Int;
-
- function get_noteSnapQuant():Int
+ function get_songLengthInMs():Float
{
- return SNAP_QUANTS[noteSnapQuantIndex];
+ if (songLengthInMs <= 0) return 1000;
+ return songLengthInMs;
+ }
+
+ function set_songLengthInMs(value:Float):Float
+ {
+ this.songLengthInMs = value;
+
+ // Make sure playhead doesn't go outside the song.
+ if (playheadPositionInMs > songLengthInMs) playheadPositionInMs = songLengthInMs;
+
+ return this.songLengthInMs;
}
/**
- * The ratio of the current note snapping value to the default.
- * For example, `32` becomes `0.5` when snapping to 16th notes.
+ * The length of the current instrumental, converted to steps.
+ * Dependant on BPM, because the size of a grid square does not change with BPM but the length of a beat does.
*/
- public var noteSnapRatio(get, never):Float;
+ var songLengthInSteps(get, set):Float;
- function get_noteSnapRatio():Float
+ function get_songLengthInSteps():Float
{
- return BASE_QUANT / noteSnapQuant;
+ return Conductor.getTimeInSteps(songLengthInMs);
+ }
+
+ function set_songLengthInSteps(value:Float):Float
+ {
+ // Getting a reasonable result from setting songLengthInSteps requires that Conductor.mapBPMChanges be called first.
+ songLengthInMs = Conductor.getStepTimeInMs(value);
+ return value;
}
/**
- * scrollPosition is the current position in the song, in pixels.
+ * The length of the current instrumental, in PIXELS.
+ * Dependant on BPM, because the size of a grid square does not change with BPM but the length of a beat does.
+ */
+ var songLengthInPixels(get, set):Int;
+
+ function get_songLengthInPixels():Int
+ {
+ return Std.int(songLengthInSteps * GRID_SIZE);
+ }
+
+ function set_songLengthInPixels(value:Int):Int
+ {
+ songLengthInSteps = value / GRID_SIZE;
+ return value;
+ }
+
+ // Scroll Position
+
+ /**
+ * The relative scroll position in the song, in pixels.
* One pixel is 1/40 of 1 step, and 1/160 of 1 beat.
*/
var scrollPositionInPixels(default, set):Float = -1.0;
+ function set_scrollPositionInPixels(value:Float):Float
+ {
+ if (value < 0)
+ {
+ // If we're scrolling up, and we hit the top,
+ // but the playhead is in the middle, move the playhead up.
+ if (playheadPositionInPixels > 0)
+ {
+ var amount:Float = scrollPositionInPixels - value;
+ playheadPositionInPixels -= amount;
+ }
+
+ value = 0;
+ }
+
+ if (value > songLengthInPixels) value = songLengthInPixels;
+
+ if (value == scrollPositionInPixels) return value;
+
+ // Difference in pixels.
+ var diff:Float = value - scrollPositionInPixels;
+
+ this.scrollPositionInPixels = value;
+
+ // Move the grid sprite to the correct position.
+ if (gridTiledSprite != null && gridPlayheadScrollArea != null)
+ {
+ if (isViewDownscroll)
+ {
+ gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
+ gridPlayheadScrollArea.y = gridTiledSprite.y;
+ }
+ else
+ {
+ gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
+ gridPlayheadScrollArea.y = gridTiledSprite.y;
+ }
+ }
+
+ // Move the rendered notes to the correct position.
+ renderedNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
+ renderedHoldNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
+ renderedEvents.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
+ renderedSelectionSquares.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
+ // Offset the selection box start position, if we are dragging.
+ if (selectionBoxStartPos != null) selectionBoxStartPos.y -= diff;
+ // Update the note preview viewport box.
+ setNotePreviewViewportBounds(calculateNotePreviewViewportBounds());
+ return this.scrollPositionInPixels;
+ }
+
/**
- * scrollPosition, converted to steps.
+ * The relative scroll position in the song, converted to steps.
* NOT dependant on BPM, because the size of a grid square does not change with BPM.
*/
var scrollPositionInSteps(get, set):Float;
@@ -242,7 +353,7 @@ class ChartEditorState extends HaxeUIState
}
/**
- * scrollPosition, converted to milliseconds.
+ * The relative scroll position in the song, converted to milliseconds.
* DEPENDANT on BPM, because the duration of a grid square changes with BPM.
*/
var scrollPositionInMs(get, set):Float;
@@ -258,11 +369,13 @@ class ChartEditorState extends HaxeUIState
return value;
}
+ // Playhead (on the grid)
+
/**
- * The position of the playhead, in pixels, relative to the scrollPosition.
- * 0 means playhead is at the top of the grid.
- * 40 means the playhead is 1 grid length below the base position.
- * -40 means the playhead is 1 grid length above the base position.
+ * The position of the playhead, in pixels, relative to the `scrollPositionInPixels`.
+ * `0` means playhead is at the top of the grid.
+ * `40` means the playhead is 1 grid length below the base position.
+ * `-40` means the playhead is 1 grid length above the base position.
*/
var playheadPositionInPixels(default, set):Float = 0.0;
@@ -314,77 +427,7 @@ class ChartEditorState extends HaxeUIState
return value;
}
- /**
- * songLength, in milliseconds.
- */
- @:isVar var songLengthInMs(get, set):Float = 0;
-
- function get_songLengthInMs():Float
- {
- if (songLengthInMs <= 0) return 1000;
- return songLengthInMs;
- }
-
- function set_songLengthInMs(value:Float):Float
- {
- this.songLengthInMs = value;
-
- // Make sure playhead doesn't go outside the song.
- if (playheadPositionInMs > songLengthInMs) playheadPositionInMs = songLengthInMs;
-
- return this.songLengthInMs;
- }
-
- /**
- * songLength, converted to steps.
- * Dependant on BPM, because the size of a grid square does not change with BPM but the length of a beat does.
- */
- var songLengthInSteps(get, set):Float;
-
- function get_songLengthInSteps():Float
- {
- return Conductor.getTimeInSteps(songLengthInMs);
- }
-
- function set_songLengthInSteps(value:Float):Float
- {
- // Getting a reasonable result from setting songLengthInSteps requires that Conductor.mapBPMChanges be called first.
- songLengthInMs = Conductor.getStepTimeInMs(value);
- return value;
- }
-
- /**
- * This is the song's length in PIXELS, same format as scrollPosition.
- * Dependant on BPM, because the size of a grid square does not change with BPM but the length of a beat does.
- */
- var songLengthInPixels(get, set):Int;
-
- function get_songLengthInPixels():Int
- {
- return Std.int(songLengthInSteps * GRID_SIZE);
- }
-
- function set_songLengthInPixels(value:Int):Int
- {
- songLengthInSteps = value / GRID_SIZE;
- return value;
- }
-
- /**
- * The current theme used by the editor.
- * Dictates the appearance of many UI elements.
- * Currently hardcoded to just Light and Dark.
- */
- var currentTheme(default, set):ChartEditorTheme = ChartEditorTheme.Light;
-
- function set_currentTheme(value:ChartEditorTheme):ChartEditorTheme
- {
- if (value == null || value == currentTheme) return currentTheme;
-
- currentTheme = value;
- ChartEditorThemeHandler.updateTheme(this);
- return value;
- }
+ // Playbar (at the bottom)
/**
* Whether a skip button has been pressed on the playbar, and which one.
@@ -404,47 +447,62 @@ class ChartEditorState extends HaxeUIState
*/
var playbarHeadDraggingWasPlaying:Bool = false;
+ // Tools Status
+
/**
* The note kind to use for notes being placed in the chart. Defaults to `''`.
*/
var selectedNoteKind:String = '';
/**
- * The note kind to use for notes being placed in the chart. Defaults to `''`.
+ * The event type to use for events being placed in the chart. Defaults to `''`.
*/
var selectedEventKind:String = 'FocusCamera';
/**
- * The note data as a struct.
+ * The event data to use for events being placed in the chart.
*/
var selectedEventData:DynamicAccess = {};
/**
- * Whether to play a metronome sound while the playhead is moving.
+ * The internal index of what note snapping value is in use.
+ * Increment to make placement more preceise and decrement to make placement less precise.
*/
- var isMetronomeEnabled:Bool = true;
+ var noteSnapQuantIndex:Int = BASE_QUANT_INDEX;
/**
- * Use the tool window to affect how the user interacts with the program.
+ * The current note snapping value.
+ * For example, `32` when snapping to 32nd notes.
*/
- var currentToolMode:ChartEditorToolMode = ChartEditorToolMode.Select;
+ var noteSnapQuant(get, never):Int;
+
+ function get_noteSnapQuant():Int
+ {
+ return SNAP_QUANTS[noteSnapQuantIndex];
+ }
/**
- * The character sprite in the Player Preview window.
- * `null` until accessed.
+ * The ratio of the current note snapping value to the default.
+ * For example, `32` becomes `0.5` when snapping to 16th notes.
*/
- var currentPlayerCharacterPlayer:Null = null;
+ var noteSnapRatio(get, never):Float;
- /**
- * The character sprite in the Opponent Preview window.
- * `null` until accessed.
- */
- var currentOpponentCharacterPlayer:Null = null;
+ function get_noteSnapRatio():Float
+ {
+ return BASE_QUANT / noteSnapQuant;
+ }
/**
* The currently selected live input style.
*/
- var currentLiveInputStyle:LiveInputStyle = LiveInputStyle.None;
+ var currentLiveInputStyle:ChartEditorLiveInputStyle = None;
+
+ /**
+ * If true, playtesting a chart will skip to the current playhead position.
+ */
+ var playtestStartTime:Bool = false;
+
+ // Visuals
/**
* Whether the current view is in downscroll mode.
@@ -467,20 +525,64 @@ class ChartEditorState extends HaxeUIState
}
/**
- * If true, playtesting a chart will skip to the current playhead position.
+ * The current theme used by the editor.
+ * Dictates the appearance of many UI elements.
+ * Currently hardcoded to just Light and Dark.
*/
- var playtestStartTime:Bool = false;
+ var currentTheme(default, set):ChartEditorTheme = ChartEditorTheme.Light;
+
+ function set_currentTheme(value:ChartEditorTheme):ChartEditorTheme
+ {
+ if (value == null || value == currentTheme) return currentTheme;
+
+ currentTheme = value;
+ this.updateTheme();
+ return value;
+ }
/**
- * Whether hitsounds are enabled for at least one character.
+ * The character sprite in the Player Preview window.
+ * `null` until accessed.
*/
- var hitsoundsEnabled(get, never):Bool;
+ var currentPlayerCharacterPlayer:Null = null;
- function get_hitsoundsEnabled():Bool
+ /**
+ * The character sprite in the Opponent Preview window.
+ * `null` until accessed.
+ */
+ var currentOpponentCharacterPlayer:Null = null;
+
+ // HaxeUI
+
+ /**
+ * 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 isHaxeUIFocused(get, never):Bool;
+
+ function get_isHaxeUIFocused():Bool
{
- return hitsoundsEnabledPlayer || hitsoundsEnabledOpponent;
+ return FocusManager.instance.focus != null;
}
+ /**
+ * Set by ChartEditorDialogHandler, used to prevent background interaction while the dialog is open.
+ */
+ var isHaxeUIDialogOpen:Bool = false;
+
+ /**
+ * The Dialog components representing the currently available tool windows.
+ * Dialogs are retained here even when collapsed or hidden.
+ */
+ var activeToolboxes:Map = new Map();
+
+ // Audio
+
+ /**
+ * Whether to play a metronome sound while the playhead is moving.
+ */
+ var isMetronomeEnabled:Bool = true;
+
/**
* Whether hitsounds are enabled for the player.
*/
@@ -492,107 +594,117 @@ class ChartEditorState extends HaxeUIState
var hitsoundsEnabledOpponent:Bool = true;
/**
- * Whether the user's mouse cursor is hovering over a SOLID component of the HaxeUI.
- * If so, ignore mouse events underneath.
+ * Whether hitsounds are enabled for at least one character.
*/
- var isCursorOverHaxeUI(get, never):Bool;
+ var hitsoundsEnabled(get, never):Bool;
- function get_isCursorOverHaxeUI():Bool
+ function get_hitsoundsEnabled():Bool
{
- return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
+ return hitsoundsEnabledPlayer || hitsoundsEnabledOpponent;
}
- 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);
- }
+ // Auto-save
/**
- * Set by ChartEditorDialogHandler, used to prevent background interaction while the dialog is open.
+ * A timer used to auto-save the chart after a period of inactivity.
*/
- public var isHaxeUIDialogOpen:Bool = false;
+ var autoSaveTimer:Null = null;
+
+ // Scrolling
/**
- * The variation ID for the difficulty which is currently being edited.
+ * Whether the user's last mouse click was on the playhead scroll area.
*/
- var selectedVariation(default, set):String = Constants.DEFAULT_VARIATION;
+ var gridPlayheadScrollAreaPressed:Bool = false;
/**
- * Setter called when we are switching variations.
- * We will likely need to switch instrumentals as well.
+ * Where the user's last mouse click was on the note preview scroll area.
+ * `null` if the user isn't clicking on the note preview.
*/
- function set_selectedVariation(value:String):String
- {
- // Don't update if we're already on the variation.
- if (selectedVariation == value) return selectedVariation;
- selectedVariation = value;
-
- // Make sure view is updated when the variation changes.
- noteDisplayDirty = true;
- notePreviewDirty = true;
- notePreviewViewportBoundsDirty = true;
-
- switchToCurrentInstrumental();
-
- return selectedVariation;
- }
+ var notePreviewScrollAreaStartPos:Null = null;
/**
- * The difficulty ID for the difficulty which is currently being edited.
+ * The current process that is lerping the scroll position.
+ * Used to cancel the previous lerp if the user scrolls again.
*/
- var selectedDifficulty(default, set):String = Constants.DEFAULT_DIFFICULTY;
+ var currentScrollEase:Null;
- function set_selectedDifficulty(value:String):String
- {
- selectedDifficulty = value;
-
- // Make sure view is updated when the difficulty changes.
- noteDisplayDirty = true;
- notePreviewDirty = true;
- notePreviewViewportBoundsDirty = true;
-
- // Make sure the difficulty we selected is in the list of difficulties.
- currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
-
- return selectedDifficulty;
- }
+ // Note Placement
/**
- * The instrumental ID which is currently selected.
+ * The SongNoteData which is currently being placed.
+ * `null` if the user isn't currently placing a note.
+ * As the user drags, we will update this note's sustain length, and finalize the note when they release.
*/
- var currentInstrumentalId(get, set):String;
+ var currentPlaceNoteData:Null = null;
- function get_currentInstrumentalId():String
- {
- var instId:Null = currentSongMetadata.playData.characters.instrumental;
- if (instId == null || instId == '') instId = (selectedVariation == Constants.DEFAULT_VARIATION) ? '' : selectedVariation;
- return instId;
- }
-
- function set_currentInstrumentalId(value:String):String
- {
- return currentSongMetadata.playData.characters.instrumental = value;
- }
+ // Note Movement
/**
- * The character ID for the character which is currently selected.
+ * The note sprite we are currently moving, if any.
*/
- var selectedCharacter(default, set):String = Constants.DEFAULT_CHARACTER;
+ var dragTargetNote:Null = null;
- function set_selectedCharacter(value:String):String
- {
- selectedCharacter = value;
+ /**
+ * The song event sprite we are currently moving, if any.
+ */
+ var dragTargetEvent:Null = null;
- // Make sure view is updated when the character changes.
- noteDisplayDirty = true;
- notePreviewDirty = true;
- notePreviewViewportBoundsDirty = true;
+ /**
+ * The amount of vertical steps the note sprite has moved by since the user started dragging.
+ */
+ var dragTargetCurrentStep:Float = 0;
- return selectedCharacter;
- }
+ /**
+ * The amount of horizontal columns the note sprite has moved by since the user started dragging.
+ */
+ var dragTargetCurrentColumn:Int = 0;
+
+ // Hold Note Dragging
+
+ /**
+ * The current length of the hold note we are dragging, in steps.
+ * Play a sound when this value changes.
+ */
+ var dragLengthCurrent:Float = 0;
+
+ /**
+ * Flip-flop to alternate between two stretching sounds.
+ */
+ var stretchySounds:Bool = false;
+
+ // Selection
+
+ /**
+ * The notes which are currently in the user's selection.
+ */
+ var currentNoteSelection:Array = [];
+
+ /**
+ * The events which are currently in the user's selection.
+ */
+ var currentEventSelection:Array = [];
+
+ /**
+ * The position where the user clicked to start a selection.
+ * `null` if the user isn't currently selecting anything.
+ * The selection box extends from this point to the current mouse position.
+ */
+ var selectionBoxStartPos:Null = null;
+
+ // History
+
+ /**
+ * The list of command previously performed. Used for undoing previous actions.
+ */
+ var undoHistory:Array = [];
+
+ /**
+ * The list of commands that have been undone. Used for redoing previous actions.
+ */
+ var redoHistory:Array = [];
+
+ // Dirty Flags
/**
* Whether the note display render group has been modified and needs to be updated.
@@ -631,7 +743,7 @@ class ChartEditorState extends HaxeUIState
if (value)
{
// Start the auto-save timer.
- autoSaveTimer = new FlxTimer().start(AUTOSAVE_TIMER_DELAY, (_) -> autoSave());
+ autoSaveTimer = new FlxTimer().start(Constants.AUTOSAVE_TIMER_DELAY_SEC, (_) -> autoSave());
}
else
{
@@ -649,11 +761,6 @@ class ChartEditorState extends HaxeUIState
return saveDataDirty;
}
- /**
- * A timer used to auto-save the chart after a period of inactivity.
- */
- var autoSaveTimer:Null = null;
-
/**
* Whether the difficulty tree view in the toolbox has been modified and needs to be updated.
* This happens when we add/remove difficulties.
@@ -678,17 +785,12 @@ class ChartEditorState extends HaxeUIState
*/
var opponentPreviewDirty:Bool = true;
- var isInPlaytestMode:Bool = false;
-
/**
- * The list of command previously performed. Used for undoing previous actions.
+ * Whether the undo/redo histories have changed since the last time the UI was updated.
*/
- var undoHistory:Array = [];
+ var commandHistoryDirty:Bool = true;
- /**
- * The list of commands that have been undone. Used for redoing previous actions.
- */
- var redoHistory:Array = [];
+ // Input
/**
* Handler used to track how long the user has been holding the undo keybind.
@@ -730,52 +832,6 @@ class ChartEditorState extends HaxeUIState
*/
var pageDownKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.PAGEDOWN);
- /**
- * Whether the undo/redo histories have changed since the last time the UI was updated.
- */
- var commandHistoryDirty:Bool = true;
-
- /**
- * The notes which are currently in the user's selection.
- */
- var currentNoteSelection:Array = [];
-
- /**
- * The events which are currently in the user's selection.
- */
- var currentEventSelection:Array = [];
-
- /**
- * The position where the user clicked to start a selection.
- * `null` if the user isn't currently selecting anything.
- * The selection box extends from this point to the current mouse position.
- */
- var selectionBoxStartPos:Null = null;
-
- /**
- * Whether the user's last mouse click was on the playhead scroll area.
- */
- var gridPlayheadScrollAreaPressed:Bool = false;
-
- /**
- * Where the user's last mouse click was on the note preview scroll area.
- * `null` if the user isn't clicking on the note preview.
- */
- var notePreviewScrollAreaStartPos:Null = null;
-
- /**
- * The SongNoteData which is currently being placed.
- * `null` if the user isn't currently placing a note.
- * As the user drags, we will update this note's sustain length.
- */
- var currentPlaceNoteData:Null = null;
-
- /**
- * The Dialog components representing the currently available tool windows.
- * Dialogs are retained here even when collapsed or hidden.
- */
- var activeToolboxes:Map = new Map();
-
/**
* AUDIO AND SOUND DATA
*/
@@ -784,7 +840,7 @@ class ChartEditorState extends HaxeUIState
/**
* The chill audio track that plays when you open the Chart Editor.
*/
- public var welcomeMusic:FlxSound = new FlxSound();
+ var welcomeMusic:FlxSound = new FlxSound();
/**
* The audio track for the instrumental.
@@ -988,7 +1044,7 @@ class ChartEditorState extends HaxeUIState
return currentSongChartData.events = value;
}
- public var currentSongNoteStyle(get, set):String;
+ var currentSongNoteStyle(get, set):String;
function get_currentSongNoteStyle():String
{
@@ -1064,10 +1120,67 @@ class ChartEditorState extends HaxeUIState
}
/**
- * SIGNALS
+ * The variation ID for the difficulty which is currently being edited.
*/
- // ==============================
- // public var onDifficultyChange(default, never):FlxTypedSignalVoid> = new FlxTypedSignalVoid>();
+ var selectedVariation(default, set):String = Constants.DEFAULT_VARIATION;
+
+ /**
+ * Setter called when we are switching variations.
+ * We will likely need to switch instrumentals as well.
+ */
+ function set_selectedVariation(value:String):String
+ {
+ // Don't update if we're already on the variation.
+ if (selectedVariation == value) return selectedVariation;
+ selectedVariation = value;
+
+ // Make sure view is updated when the variation changes.
+ noteDisplayDirty = true;
+ notePreviewDirty = true;
+ notePreviewViewportBoundsDirty = true;
+
+ switchToCurrentInstrumental();
+
+ return selectedVariation;
+ }
+
+ /**
+ * The difficulty ID for the difficulty which is currently being edited.
+ */
+ var selectedDifficulty(default, set):String = Constants.DEFAULT_DIFFICULTY;
+
+ function set_selectedDifficulty(value:String):String
+ {
+ selectedDifficulty = value;
+
+ // Make sure view is updated when the difficulty changes.
+ noteDisplayDirty = true;
+ notePreviewDirty = true;
+ notePreviewViewportBoundsDirty = true;
+
+ // Make sure the difficulty we selected is in the list of difficulties.
+ currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
+
+ return selectedDifficulty;
+ }
+
+ /**
+ * The instrumental ID which is currently selected.
+ */
+ var currentInstrumentalId(get, set):String;
+
+ function get_currentInstrumentalId():String
+ {
+ var instId:Null = currentSongMetadata.playData.characters.instrumental;
+ if (instId == null || instId == '') instId = (selectedVariation == Constants.DEFAULT_VARIATION) ? '' : selectedVariation;
+ return instId;
+ }
+
+ function set_currentInstrumentalId(value:String):String
+ {
+ return currentSongMetadata.playData.characters.instrumental = value;
+ }
+
/**
* RENDER OBJECTS
*/
@@ -1104,6 +1217,9 @@ class ChartEditorState extends HaxeUIState
*/
var gridPlayhead:FlxSpriteGroup = new FlxSpriteGroup();
+ /**
+ * The sprite for the scroll area under
+ */
var gridPlayheadScrollArea:Null = null;
/**
@@ -1172,7 +1288,7 @@ class ChartEditorState extends HaxeUIState
/**
* The item in the menubar to save the currently opened chart.
*/
- var menubarItemSaveChart:Null = null;
+ var menubarItemSaveChart:Null