diff --git a/assets/images/editors/bgs/default.png b/assets/images/editors/bgs/default.png
new file mode 100644
index 00000000..1b99f9a5
Binary files /dev/null and b/assets/images/editors/bgs/default.png differ
diff --git a/assets/images/menus/options/meter.png b/assets/images/menus/options/meter.png
deleted file mode 100644
index 4607e9b0..00000000
Binary files a/assets/images/menus/options/meter.png and /dev/null differ
diff --git a/assets/images/menus/options/meter.xml b/assets/images/menus/options/meter.xml
deleted file mode 100644
index fddf0e4e..00000000
--- a/assets/images/menus/options/meter.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/assets/images/menus/options/slider.png b/assets/images/menus/options/slider.png
new file mode 100644
index 00000000..e2bf2192
Binary files /dev/null and b/assets/images/menus/options/slider.png differ
diff --git a/assets/images/menus/options/slider.xml b/assets/images/menus/options/slider.xml
new file mode 100644
index 00000000..81a9dd2b
--- /dev/null
+++ b/assets/images/menus/options/slider.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml
index 86ca86ac..a0df9dc4 100644
--- a/assets/languages/en/Editors.xml
+++ b/assets/languages/en/Editors.xml
@@ -46,7 +46,9 @@
Press ACCEPT to edit the chart for the selected difficulty.
Select a difficulty to continue.
New Difficulty
+ Press ACCEPT to create a new chart difficulty.
New Song
+ Press ACCEPT to create a new song.
@@ -225,6 +227,8 @@
Go back to the start
Go forward to the end
+ Add camera on Opponent
+ Add camera on Player
Mute instrumental
Mute voices
@@ -435,6 +439,11 @@
Creating New Character
Character Name $* Required$
Character Image File $* Required$
+
+
+ Creating Character: Error!
+ The character you are trying to create already exists, if you would like to override it delete the character first!
+
@@ -442,6 +451,7 @@
Press ACCEPT to edit this typeface.
New Typeface
+ Press ACCEPT to create a new typeface.
Select a typeface to edit
@@ -554,6 +564,11 @@
Stage Info
Stage Name
Stage Path
+
+
+ Creating Stage: Error!
+ The stage you are trying to create alreadly exists, if you would like to override it delete the stage first!
+
diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml
index f0ec9ea7..7145f793 100644
--- a/assets/languages/en/Options.xml
+++ b/assets/languages/en/Options.xml
@@ -12,10 +12,11 @@
Gameplay
Change Gameplay options such as Downscroll, Scroll Speed, Naughtyness...
- Change more advanced options such as Window Size, Streaming Audio...
+ Change more advanced options such as Hit Window Size, Streaming Audio...
Appearance
Change Appearance options such as Flashing menus...
+ Change more advanced options such as VRAM-Only Sprites, Antialiasing, Shaders
Language
Change the language that the engine uses.
@@ -23,6 +24,9 @@
Miscellaneous
Use this menu to reset save data or engine settings.
+ Debug Options
+ Debug Options for Developer Mode.
+
@@ -62,8 +66,8 @@
Framerate
Pretty self explanatory, isn't it?
- Antialiasing
- If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites.
+
+
Colored Healthbar
If unchecked, the game will use the original red/green health bar from the week 6 fnf game.
@@ -71,17 +75,25 @@
Pixel Perfect Effect
If checked, Week 6 will have a pixel perfect effect to it enabled, aligning every pixel on the screen.
- Gameplay Shaders
- If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices.
+
+ Quality
+ Quality modes to choose, "LOW" = Disable every heavy visual effects as it can get to smoothen the gameplay, "HIGH" = Enable all of the visual effects, with the cost of performance, "CUSTOM" = Allow of custom options that is available in Advanced.
+ LOW
+ HIGH
+ CUSTOM
-
-
+ Antialiasing
+ If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites.
- Low Memory Mode
- If checked, will disable certain background elements in stages to reduce memory usage.
+ Low Memory Mode
+ If checked, will disable certain background elements in stages to reduce memory usage.
- VRAM-Only Sprites
- If checked, will only store the bitmaps in the GPU, freeing a LOT of memory (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on bigger sprites. If you aren't sure, leave this on.
+ Gameplay Shaders
+ If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices.
+
+ VRAM-Only Sprites
+ If checked, will only store the bitmaps in the GPU, freeing a LOT of memory (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on bigger sprites. If you aren't sure, leave this on.
+
@@ -100,6 +112,9 @@
Auto Pause
If checked, switching windows will pause the game.
+ Music Volume
+ Changes only the volume of the music instead of everything.
+
SFX Volume
Changes only the volume of the SFX instead of everything.
@@ -137,6 +152,9 @@
Developer Mode
If checked, you will be able to access developer features like certain keybinds, editors, developer options, state reloads, console and more.
+ Show Configs Warning
+ If checked, a popup at the start of the game will appear if the current mod doesn't have a mod configuration file
+
Reset Save Data
Select this option to reset save data. This will remove all of your highscores.
@@ -157,8 +175,14 @@
Editor SFXs
If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...)
- Pretty Print
- If checked, the saved files from the editor will be formatted to be easily viewable (does not apply to xmls...)
+ Chart Pretty Print
+ If checked, the saved files from the chart editor will be formatted to be easily viewable
+
+ Character Pretty Print
+ If checked, the saved files from the character editor will be formatted to be easily viewable
+
+ Stage Pretty Print
+ If checked, the saved files from the stage editor will be formatted to be easily viewable
Intensive Blur
If checked, will use more intensive blur that may be laggier but look better.
diff --git a/assets/languages/pl/Editors.xml b/assets/languages/pl/Editors.xml
index 2e306386..d41bd2cf 100644
--- a/assets/languages/pl/Editors.xml
+++ b/assets/languages/pl/Editors.xml
@@ -2,7 +2,7 @@
Autozapisywanie za {0} sekund{1}
- ((Anulowane)!
+ (Anulowane)!
Ok
Anuluj
@@ -225,6 +225,8 @@
Wróć do początku
Idź do końca
+ Dodaj kamerę na Oponenta
+ Dodaj kamerę na Gracza
Wycisz Instrumental
Wycisz Wokale
diff --git a/assets/languages/pl/Options.xml b/assets/languages/pl/Options.xml
index 0549951f..68526dde 100644
--- a/assets/languages/pl/Options.xml
+++ b/assets/languages/pl/Options.xml
@@ -62,26 +62,28 @@
Klatki
Ilość Klatek na sekundę ktore gra renderuje
- Wygładzanie
- Jeśli wyłączone, Wygładzanie nie będzie widoczne na Sprite'ach. Może poprawić wydajność na koszt ostrzejszego, pikselowatego wyglądu Sprite'ów.
-
Kolorowe Zdrowie
Jeśli wyłączone, Pasek zdrowia będzie tylko używał czerwonego i zielonego koloru, takiego jak z oryginalnej gry.
Perfekcyjne Piksele
Jeśli włączone, Tydzień 6 będzie miał efekt perfekcyjnych pikseli, ustawiający każdy piksel na ekranie.
- Shadery Rozgrywki
- Jeśli wyłączone, Shadery Rozgrywki (efekty wizualne jak Aberacja Chromatyczna z Thorns) nie będą ładowane; może być to pomocne na słabszych urządzeniach.
-
- Niskie zużycie pamięci
+
+ Wygładzanie
+ Jeśli wyłączone, Wygładzanie nie będzie widoczne na Sprite'ach. Może poprawić wydajność na koszt ostrzejszego, pikselowatego wyglądu Sprite'ów.
+
+ Niskie zużycie pamięci
Jeśli włączone, usunie niektóre elementy tła, co użyje mniej pamięci.
- Sprite'y w VRAM
- Jeśli włączone, utrzyma obrazy tylko w GPU, uwalniając więcej pamięci (EKSPERYMENTALNE). Wyłaczenie tego użyje o wiele więcej pamięci, szczególnie przy większych duszkach. Zostaw włączone w wypadku wątpliwości.
+ Shadery Rozgrywki
+ Jeśli wyłączone, Shadery Rozgrywki (efekty wizualne jak Aberacja Chromatyczna z Thorns) nie będą ładowane; może być to pomocne na słabszych urządzeniach.
+
+ Sprite'y w VRAM
+ Jeśli włączone, utrzyma obrazy tylko w GPU, uwalniając więcej pamięci (EKSPERYMENTALNE). Wyłaczenie tego użyje o wiele więcej pamięci, szczególnie przy większych duszkach. Zostaw włączone w wypadku wątpliwości.
+
@@ -112,7 +114,7 @@
Strumieniowana Muzyka
Jeśli włączone, tylko muzyka będzie strumieniować audio, uwalniając więcej pamięci, na możliwy koszt większego użycia CPU. (EKSPERYMENTALNE)
-If checked, only musics will have streamed audio, ALSO freeing a LOT of memory with the downside of higher cpu usage if more audio are being streamed at once (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on longer songs. If you aren't sure, leave this on.
+
Strumieniowane Wokale
Jeśli włączone, tylko wokale będą strumieniować audio, uwalniając więcej pamięci, na możliwy koszt większego użycia CPU. (EKSPERYMENTALNE)
@@ -157,9 +159,6 @@ If checked, only musics will have streamed audio, ALSO freeing a LOT of memory w
Dźwięki Edytorów
Jeśli włączone, niektóre elementy edytorów będą grać dźwięki. (Na przykład: dźwięki pól wyboru...)
- Ładny Druk
- Jeśli włączone, zapisane pliki z edytorów będą używać formatowania łatwiejszego do odczytania. (opcja nie dotyczy plików xml, tylko po angielsku...)
-
Mocniejszy Rozmaz
Jeśli włączone, gra użyje mocniejszego rozmazu, który kosztuje wydajność, ale wygląda lepiej.
diff --git a/source/flixel/sound/FlxSound.hx b/source/flixel/sound/FlxSound.hx
index 6e83609e..eb39c4ac 100644
--- a/source/flixel/sound/FlxSound.hx
+++ b/source/flixel/sound/FlxSound.hx
@@ -312,6 +312,7 @@ class FlxSound extends FlxBasic {
_source = null;
_sound = null;
+ if (autoDestroy) persist = false;
reset();
}
else if (_channel != null && _channel.__isValid) {
@@ -880,7 +881,7 @@ class FlxSound extends FlxBasic {
cleanup(false, true);
startSound(time);
}
- else if (playing) {
+ else {
_source.offset = 0;
_source.currentTime = time + _offset;
}
diff --git a/source/funkin/backend/TurboControls.hx b/source/funkin/backend/TurboControls.hx
new file mode 100644
index 00000000..0990aaa2
--- /dev/null
+++ b/source/funkin/backend/TurboControls.hx
@@ -0,0 +1,121 @@
+package funkin.backend;
+
+import flixel.input.actions.FlxAction;
+import flixel.input.gamepad.FlxGamepadInputID;
+import flixel.input.gamepad.FlxGamepad;
+import flixel.input.keyboard.FlxKey;
+import flixel.input.FlxInput.FlxInputState;
+import flixel.FlxBasic;
+
+import funkin.backend.system.Controls;
+import funkin.options.PlayerSettings;
+
+class TurboBasic extends FlxBasic {
+ public static var DEFAULT_DELAY:Float = 0.4;
+ public static var DEFAULT_INTERVAL:Float = 1 / 18;
+
+ public var delay:Float;
+ public var interval:Float;
+ public var activated(default, null):Bool;
+ public var pressed(get, never):Bool; function get_pressed() return false;
+ public var allPress:Bool;
+
+ var time:Float = 0;
+
+ public function new(?delay:Float, ?interval:Float, allPress = false) {
+ super();
+ this.delay = delay != null ? delay : DEFAULT_DELAY;
+ this.interval = interval != null ? interval : DEFAULT_INTERVAL;
+ this.allPress = allPress;
+ }
+
+ override function update(elapsed:Float) {
+ super.update(elapsed);
+
+ if (pressed) {
+ if (time == 0) activated = true;
+ else if (activated = (time >= delay + interval)) time -= interval;
+ time += elapsed;
+ }
+ else {
+ activated = false;
+ time = 0;
+ }
+ }
+}
+
+class TurboControls extends TurboBasic {
+ public var controlsInstance:Controls;
+ public var controls:Array;
+ public function new(controls:Array, ?controlsInstance:Controls, ?delay:Float, ?interval:Float, ?allPress:Bool) {
+ super(delay, interval, allPress);
+ this.controls = controls;
+ this.controlsInstance = controlsInstance != null ? controlsInstance : PlayerSettings.solo.controls;
+ }
+
+ override function get_pressed() {
+ if (allPress) {
+ for (control in controls) if (!controlsInstance.getActionFromControl(control).check()) return false;
+ }
+ else {
+ for (control in controls) if (controlsInstance.getActionFromControl(control).check()) return true;
+ }
+ return allPress;
+ }
+}
+
+class TurboActions extends TurboBasic {
+ public var actions:Array;
+ public function new(actions:Array, ?delay:Float, ?interval:Float, ?allPress:Bool) {
+ super(delay, interval, allPress);
+ this.actions = actions;
+ }
+
+ override function get_pressed() {
+ if (allPress) {
+ for (action in actions) if (!action.check()) return false;
+ }
+ else {
+ for (action in actions) if (action.check()) return true;
+ }
+ return allPress;
+ }
+}
+
+class TurboKeys extends TurboBasic {
+ public var keys:Array;
+ public function new(keys:Array, ?delay:Float, ?interval:Float, ?allPress:Bool) {
+ super(delay, interval, allPress);
+ this.keys = keys;
+ }
+
+ override function get_pressed() {
+ if (allPress) {
+ for (key in keys) if (!FlxG.keys.checkStatus(key, PRESSED)) return false;
+ }
+ else {
+ for (key in keys) if (FlxG.keys.checkStatus(key, PRESSED)) return true;
+ }
+ return allPress;
+ }
+}
+
+class TurboButtons extends TurboBasic {
+ public var inputs:Array;
+ public var gamepad:FlxGamepad;
+ public function new(inputs:Array, ?gamepad:FlxGamepad, ?delay:Float, ?interval:Float, ?allPress:Bool) {
+ super(delay, interval, allPress);
+ this.inputs = inputs;
+ this.gamepad = gamepad != null ? gamepad : FlxG.gamepads.firstActive;
+ }
+
+ override function get_pressed() {
+ if (allPress) {
+ for (input in inputs) if (!gamepad.checkStatus(input, PRESSED)) return false;
+ }
+ else {
+ for (input in inputs) if (gamepad.checkStatus(input, PRESSED)) return true;
+ }
+ return allPress;
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/backend/assets/ZipFolderLibrary.hx b/source/funkin/backend/assets/ZipFolderLibrary.hx
index ea4dab25..68ef537a 100644
--- a/source/funkin/backend/assets/ZipFolderLibrary.hx
+++ b/source/funkin/backend/assets/ZipFolderLibrary.hx
@@ -27,11 +27,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary {
this.libName = libName;
this.basePath = basePath;
-
- if(modName == null)
- this.modName = libName;
- else
- this.modName = modName;
+
+ this.modName = (modName == null) ? libName : modName;
zip = SysZip.openFromFile(basePath);
zip.read();
diff --git a/source/funkin/backend/scripting/Script.hx b/source/funkin/backend/scripting/Script.hx
index 1e0db90d..55b22053 100644
--- a/source/funkin/backend/scripting/Script.hx
+++ b/source/funkin/backend/scripting/Script.hx
@@ -112,9 +112,9 @@ class Script extends FlxBasic implements IFlxDestroyable {
* if you wanna modify it, please edit `hscript.Interp.importRedirects` directly.
**/
public static function getDefaultImportRedirects():Map {
- var redirects:Map = [
- ];
+ var redirects:Map = [];
+ // Events
final events = "funkin.backend.scripting.events.";
redirects[events + "CharacterNodeEvent"] = events + "character.CharacterNodeEvent";
redirects[events + "CharacterXMLEvent"] = events + "character.CharacterXMLEvent";
@@ -146,6 +146,9 @@ class Script extends FlxBasic implements IFlxDestroyable {
redirects[events + "StageNodeEvent"] = events + "stage.StageNodeEvent";
redirects[events + "StageXMLEvent"] = events + "stage.StageXMLEvent";
+ // Old State Names
+ redirects["funkin.menus.BetaWarningState"] = "funkin.menus.WarningState";
+
return redirects;
}
diff --git a/source/funkin/backend/system/Controls.hx b/source/funkin/backend/system/Controls.hx
index 0778469d..5f6f8f98 100644
--- a/source/funkin/backend/system/Controls.hx
+++ b/source/funkin/backend/system/Controls.hx
@@ -191,6 +191,8 @@ class Controls extends FlxActionSet
setKeyboardScheme(scheme, false);
}
+ public function getActionFromControl(control:Control):FlxAction return macro_getActionFromControl(control);
+
public function getKeyName(control:Control, idx:Int = 0):String
{
var action = macro_getActionFromControl(control);
diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx
index f5844278..d2d3a1bf 100644
--- a/source/funkin/backend/system/Main.hx
+++ b/source/funkin/backend/system/Main.hx
@@ -11,6 +11,7 @@ import flixel.math.FlxRect;
import flixel.system.ui.FlxSoundTray;
import funkin.backend.assets.AssetsLibraryList;
import funkin.backend.assets.ModsFolder;
+import funkin.backend.assets.AssetSource;
import funkin.backend.system.framerate.SystemInfo;
import funkin.backend.system.modules.*;
import funkin.editors.SaveWarning;
diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx
index ba58479c..0d415f26 100644
--- a/source/funkin/backend/system/MainState.hx
+++ b/source/funkin/backend/system/MainState.hx
@@ -122,7 +122,7 @@ class MainState extends FlxState {
CoolUtil.safeAddAttributes('./.temp/', NativeAPI.FileAttribute.HIDDEN);
#end
- if (Options.devMode) {
+ if (Options.devMode && Options.allowConfigWarning) {
var lib:ModsFolderLibrary;
for (e in Paths.assetsTree.libraries) {
@:privateAccess if (!(e is openfl.utils.AssetLibrary) || !((lib = cast cast(e, openfl.utils.AssetLibrary).__proxy) is ModsFolderLibrary)) continue;
diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx
index d24e153a..175e0eb0 100644
--- a/source/funkin/backend/utils/CoolUtil.hx
+++ b/source/funkin/backend/utils/CoolUtil.hx
@@ -519,8 +519,8 @@ final class CoolUtil
* @param menuSFX Menu SFX to play
* @param volume At which volume it should play
*/
- @:noUsing public static inline function playMenuSFX(menuSFX:CoolSfx = SCROLL, volume:Float = 1) {
- FlxG.sound.play(Paths.sound('menu/' + switch(menuSFX) {
+ @:noUsing public static inline function playMenuSFX(menuSFX:CoolSfx = SCROLL, volume:Float = 1):FlxSound {
+ return FlxG.sound.play(Paths.sound('menu/' + switch(menuSFX) {
case CONFIRM: 'confirm';
case CANCEL: 'cancel';
case SCROLL: 'scroll';
@@ -528,7 +528,7 @@ final class CoolUtil
case UNCHECKED: 'checkboxUnchecked';
case WARNING: 'warningMenu';
default: 'scroll';
- }), volume);
+ }), volume * Options.volumeSFX);
}
/**
diff --git a/source/funkin/editors/DebugOptions.hx b/source/funkin/editors/DebugOptions.hx
deleted file mode 100644
index 93d85992..00000000
--- a/source/funkin/editors/DebugOptions.hx
+++ /dev/null
@@ -1,80 +0,0 @@
-package funkin.editors;
-
-import funkin.backend.utils.NativeAPI;
-import funkin.options.OptionsScreen;
-import funkin.options.TreeMenu;
-import funkin.options.type.*;
-
-class DebugOptions extends TreeMenu {
- public override function create() {
- super.create();
-
- FlxG.camera.fade(0xFF000000, 0.5, true);
-
- var bg:FlxSprite = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
- // bg.scrollFactor.set();
- bg.scale.set(1.15, 1.15);
- bg.updateHitbox();
- bg.screenCenter();
- bg.scrollFactor.set();
- bg.antialiasing = true;
- add(bg);
-
- main = new DebugOptionsScreen();
- }
-}
-
-class DebugOptionsScreen extends OptionsScreen {
- public override function new() {
- prefix = "DebugOptions.";
- super(translate("title"), translate("desc"), prefix);
- #if windows
- add(new TextOption(
- getName("showConsole"),
- getDesc("showConsole"),
- function() {
- NativeAPI.allocConsole();
- }));
- #end
- add(new Checkbox(
- getName("editorsResizable"),
- getDesc("editorsResizable"),
- "editorsResizable"));
- add(new Checkbox(
- getName("bypassEditorsResize"),
- getDesc("bypassEditorsResize"),
- "bypassEditorsResize"));
- add(new Checkbox(
- getName("editorSFX"),
- getDesc("editorSFX"),
- "editorSFX"));
- add(new Checkbox(
- getName("editorPrettyPrint"),
- getDesc("editorPrettyPrint"),
- "editorPrettyPrint"));
- add(new Checkbox(
- getName("intensiveBlur"),
- getDesc("intensiveBlur"),
- "intensiveBlur"));
- add(new Checkbox(
- getName("charterAutoSaves"),
- getDesc("charterAutoSaves"),
- "charterAutoSaves"));
- add(new NumOption(
- getName("charterAutoSaveTime"),
- getDesc("charterAutoSaveTime"),
- 60, 60*10, 30,
- "charterAutoSaveTime"
- ));
- add(new NumOption(
- getName("charterAutoSaveWarningTime"),
- getDesc("charterAutoSaveWarningTime"),
- 0, 15, 1,
- "charterAutoSaveWarningTime"
- ));
- add(new Checkbox(
- getName("charterAutoSavesSeparateFolder"),
- getDesc("charterAutoSavesSeparateFolder"),
- "charterAutoSavesSeparateFolder"));
- }
-}
\ No newline at end of file
diff --git a/source/funkin/editors/EditorPicker.hx b/source/funkin/editors/EditorPicker.hx
index ff349203..ea751908 100644
--- a/source/funkin/editors/EditorPicker.hx
+++ b/source/funkin/editors/EditorPicker.hx
@@ -8,11 +8,6 @@ class EditorPicker extends MusicBeatSubstate {
// Name is for backwards compatibility, don't use it, use id instead
public var options:Array = [
- {
- name: "Alphabet Editor",
- id: "alphabet",
- state: funkin.editors.alphabet.AlphabetSelection
- },
{
name: "Chart Editor",
id: "chart",
@@ -28,6 +23,11 @@ class EditorPicker extends MusicBeatSubstate {
id: "stage",
state: funkin.editors.stage.StageSelection
},
+ {
+ name: "Alphabet Editor",
+ id: "alphabet",
+ state: funkin.editors.alphabet.AlphabetSelection
+ },
#if (debug || debug_ui)
{
name: "UI Debug State",
@@ -42,11 +42,6 @@ class EditorPicker extends MusicBeatSubstate {
onClick: function() {
CoolUtil.openURL(Flags.URL_WIKI);
}
- },
- {
- name: "Debug Options",
- id: "debugOptions",
- state: DebugOptions
}
];
diff --git a/source/funkin/editors/EditorTreeMenu.hx b/source/funkin/editors/EditorTreeMenu.hx
index 7bf5a0fb..1a7a0e37 100644
--- a/source/funkin/editors/EditorTreeMenu.hx
+++ b/source/funkin/editors/EditorTreeMenu.hx
@@ -1,26 +1,25 @@
package funkin.editors;
import flixel.addons.display.FlxBackdrop;
-import funkin.menus.MainMenuState;
-import funkin.options.*;
+import funkin.options.type.OptionType;
-class EditorTreeMenu extends TreeMenu {
+class EditorTreeMenu extends funkin.options.TreeMenu {
public var bg:FlxBackdrop;
public var bgType:String = "default";
public var bgMovement:FlxPoint = new FlxPoint();
- public override function create() {
+ override function create() {
super.create();
-
UIState.setResolutionAware();
-
FlxG.camera.fade(0xFF000000, 0.5, true);
+ }
- bg = new FlxBackdrop();
+ override function createPost() {
+ insert(0, bg = new FlxBackdrop());
bg.loadGraphic(Paths.image('editors/bgs/${bgType}'));
bg.antialiasing = true;
setBackgroundRotation(-5);
- add(bg);
+ super.createPost();
}
public inline function setBackgroundRotation(rotation:Float) {
@@ -28,7 +27,47 @@ class EditorTreeMenu extends TreeMenu {
bg.velocity.set(85, 0).degrees = bg.rotation;
}
- public override function exit() {
- FlxG.switchState(new MainMenuState());
+ override function exit() {
+ FlxG.switchState(new funkin.menus.MainMenuState());
+ }
+
+ override function update(elapsed:Float) {
+ super.update(elapsed);
+
+ bg.colorTransform.redOffset = lerp(bg.colorTransform.redOffset, 0, 0.0625);
+ bg.colorTransform.greenOffset = lerp(bg.colorTransform.greenOffset, 0, 0.0625);
+ bg.colorTransform.blueOffset = lerp(bg.colorTransform.blueOffset, 0, 0.0625);
+ bg.colorTransform.redMultiplier = lerp(bg.colorTransform.redMultiplier, 1, 0.0625);
+ bg.colorTransform.greenMultiplier = lerp(bg.colorTransform.greenMultiplier, 1, 0.0625);
+ bg.colorTransform.blueMultiplier = lerp(bg.colorTransform.blueMultiplier, 1, 0.0625);
+ }
+
+ override function menuChanged() @:privateAccess {
+ super.menuChanged();
+ if (previousMenus.length > 0) return; // selected a sub-menu
+
+ var prev = tree[tree.length - 2];
+ if (prev == null || prev.curOption == null || !(prev.curOption is OptionType)) return;
+
+ // small flashbang
+ var color = cast(prev.curOption, OptionType).editorFlashColor;
+ bg.colorTransform.redOffset = 0.25 * color.red;
+ bg.colorTransform.greenOffset = 0.25 * color.green;
+ bg.colorTransform.blueOffset = 0.25 * color.blue;
+ bg.colorTransform.redMultiplier = FlxMath.lerp(1, color.redFloat, 0.25);
+ bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25);
+ bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25);
+ }
+}
+
+class EditorTreeMenuScreen extends funkin.options.TreeMenuScreen {
+ public function new(name:String, desc:String, ?prefix:String, ?objects:Array,
+ ?newButton:String, ?newButtonDesc:String, ?newCallback:Void->Void)
+ {
+ super(name, desc, prefix, objects);
+ if (newCallback != null) {
+ insert(0, new funkin.options.type.NewOption(getID(newButton), getID(newButtonDesc), newCallback));
+ curSelected = 1;
+ }
}
}
\ No newline at end of file
diff --git a/source/funkin/editors/alphabet/AlphabetSelection.hx b/source/funkin/editors/alphabet/AlphabetSelection.hx
index 1b8155ee..bb274c8f 100644
--- a/source/funkin/editors/alphabet/AlphabetSelection.hx
+++ b/source/funkin/editors/alphabet/AlphabetSelection.hx
@@ -1,51 +1,35 @@
package funkin.editors.alphabet;
+import haxe.io.Path;
import haxe.xml.Access;
import funkin.game.Character;
-import funkin.options.OptionsScreen;
-import funkin.options.type.IconOption;
+import funkin.editors.EditorTreeMenu;
import funkin.options.type.NewOption;
import funkin.options.type.TextOption;
import funkin.options.type.OptionType;
-import haxe.io.Path;
-class AlphabetSelection extends EditorTreeMenu
-{
- inline function translate(id:String, ?args:Array)
- return TU.translate("editor.alphabet.selection." + id, args);
-
- public override function create()
- {
- bgType = "charter";
+class AlphabetSelection extends EditorTreeMenu {
+ override function create() {
super.create();
+ DiscordUtil.call("onEditorTreeLoaded", ["Alphabet Editor"]);
+ addMenu(new AlphabetSelectionScreen());
+ }
+}
+
+class AlphabetSelectionScreen extends EditorTreeMenuScreen {
+ public function new() {
+ super('editor.alphabet.name', 'editor.alphabet.selection.desc', 'editor.alphabet.selection.', 'newTypeface', 'newTypefaceDesc', () -> {
+ parent.openSubState(new UIWarningSubstate(translate('warnings.notImplemented-title'), translate('warnings.notImplemented-body'), [
+ {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}}
+ ]));
+ });
var modsList:Array = [];
- for(file in Paths.getFolderContent('data/alphabet/', true, BOTH)) // mods ? MODS : BOTH
+ for (file in Paths.getFolderContent('data/alphabet/', true, BOTH)) // mods ? MODS : BOTH
if (Path.extension(file) == "xml") modsList.push(CoolUtil.getFilename(file));
- var list:Array = [
- for (typeface in modsList)
- new AlphabetIconOption(typeface, translate("acceptTypeface"), typeface, function() {
- FlxG.switchState(new AlphabetEditor(typeface));
- })
- ];
-
- var newChar = translate("newTypeface");
- list.insert(0, new NewOption(newChar, newChar, function() {
- openSubState(new UIWarningSubstate(translate("warnings.notImplemented-title"), translate("warnings.notImplemented-body"), [
- {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}}
- ]));
- }));
-
- main = new OptionsScreen(TU.translate("editor.alphabet.name"), translate("desc"), list);
-
- DiscordUtil.call("onEditorTreeLoaded", ["Alphabet Editor"]);
- }
-
- override function createPost() {
- super.createPost();
-
- main.changeSelection(1);
+ for (typeface in modsList)
+ add(new AlphabetIconOption(typeface, getID('acceptTypeface'), typeface, () -> FlxG.switchState(new AlphabetEditor(typeface))));
}
}
@@ -57,7 +41,7 @@ class AlphabetIconOption extends TextOption {
var xml = Xml.parse(Assets.getText(Paths.xml('alphabet/$typeface'))).firstElement();
var spritesheet = null;
- for(node in xml.elements()) {
+ for (node in xml.elements()) {
if (node.nodeName == "spritesheet") {
spritesheet = node.firstChild().nodeValue;
break;
@@ -73,17 +57,19 @@ class AlphabetIconOption extends TextOption {
iconSpr.frames = Paths.getFrames(spritesheet);
iconSpr.antialiasing = true;
var frameToUse = iconSpr.frames.frames[0];
- for(frame in iconSpr.frames.frames) {
+ for (frame in iconSpr.frames.frames) {
if (frame.name.toUpperCase().startsWith("A")) {
frameToUse = frame;
break;
}
}
iconSpr.frame = frameToUse;
- if(useColorOffsets) {
+ if (useColorOffsets) {
iconSpr.colorTransform.color = -1;
}
iconSpr.setPosition(90 - iconSpr.width - 20, (__text.height - iconSpr.height) / 2);
add(iconSpr);
+
+ __text.x = 100;
}
}
\ No newline at end of file
diff --git a/source/funkin/editors/character/CharacterEditor.hx b/source/funkin/editors/character/CharacterEditor.hx
index 264f7039..68f167fd 100644
--- a/source/funkin/editors/character/CharacterEditor.hx
+++ b/source/funkin/editors/character/CharacterEditor.hx
@@ -486,7 +486,8 @@ class CharacterEditor extends UIState {
button.anim
]);
- return "\n" + Printer.print(charXML, true);
+ var xmlThingYea:String = "\n" + Printer.print(charXML, Options.editorCharacterPrettyPrint);
+ return Options.editorCharacterPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
}
var clipboard:FlxPoint = FlxPoint.get();
diff --git a/source/funkin/editors/character/CharacterSelection.hx b/source/funkin/editors/character/CharacterSelection.hx
index 9f009ccb..1b9d2d70 100644
--- a/source/funkin/editors/character/CharacterSelection.hx
+++ b/source/funkin/editors/character/CharacterSelection.hx
@@ -1,81 +1,69 @@
package funkin.editors.character;
import haxe.xml.Printer;
-import funkin.editors.ui.UIImageExplorer.ImageSaveData;
import funkin.game.Character;
-import funkin.options.OptionsScreen;
+import funkin.editors.ui.UIImageExplorer.ImageSaveData;
+import funkin.editors.EditorTreeMenu;
import funkin.options.type.IconOption;
import funkin.options.type.NewOption;
import funkin.options.type.TextOption;
import funkin.options.type.OptionType;
-import funkin.backend.assets.ModsFolder;
-class CharacterSelection extends EditorTreeMenu
-{
- inline function translate(id:String, ?args:Array)
- return TU.translate("characterSelection." + id, args);
+class CharacterSelection extends EditorTreeMenu {
+ override function create() {
+ super.create();
+ DiscordUtil.call("onEditorTreeLoaded", ["Character Editor"]);
+ addMenu(new CharacterSelectionScreen());
+ }
+}
+class CharacterSelectionScreen extends EditorTreeMenuScreen {
public var modsList:Array = [];
- public override function create()
- {
- bgType = "charter";
- super.create();
+ public function new() {
+ super('editor.character.name', 'characterSelection.desc', 'characterSelection.', 'newCharacter', 'newCharacterDesc', () -> {
+ parent.openSubState(new CharacterCreationScreen(createCharacter));
+ });
var isMods:Bool = true;
modsList = Character.getList(true, true);
- if(modsList.length == 0) {
+ if (modsList.length == 0) {
modsList = Character.getList(false, true);
isMods = false;
}
- function generateList(modsList:Array, isMods:Bool, folderPath:String = ""):Array {
- var list:Array = [];
+ function generateList(modsList:Array, isMods:Bool, folderPath:String = ""):Array {
+ var list:Array = [];
- for(char in modsList) {
- if(char.endsWith("/")) {
+ for (char in modsList) {
+ if (char.endsWith("/")) {
var folderName = CoolUtil.getFilename(char.substr(0, char.length-1));
- list.push(new TextOption(folderName + " >", translate("acceptFolder"), function() {
+ list.push(new TextOption(folderName, getID('acceptFolder'), ' >', () -> {
var newModsList = Character.getList(isMods, true, char);
- var newList:Array = generateList(newModsList, isMods, folderPath + folderName + "/");
- optionsTree.add(new OptionsScreen(folderPath + folderName, translate("desc-folder", [folderPath + folderName + "/"]), newList));
+ var newList:Array = generateList(newModsList, isMods, folderPath + folderName + "/");
+ parent.addMenu(new EditorTreeMenuScreen(folderPath + folderName, translate('desc-folder', [folderPath + folderName + "/"]), newList));
+ }));
+ }
+ else {
+ list.push(new IconOption(char, getID('acceptCharacter'), Character.getIconFromCharName(folderPath + char, char), () -> {
+ FlxG.switchState(new CharacterEditor(folderPath + char));
}));
- } else {
- list.push(new IconOption(char, translate("acceptCharacter"), Character.getIconFromCharName(folderPath + char, char),
- function() {
- FlxG.switchState(new CharacterEditor(folderPath + char));
- })
- );
}
}
- list.insert(0, new NewOption(translate("newCharacter"), translate("newCharacterDesc"), function() {
- openSubState(new CharacterCreationScreen(createCharacter));
- }));
-
return list;
}
- var list = generateList(modsList, isMods);
-
- main = new OptionsScreen(TU.translate("editor.character.name"), translate("desc"), list);
-
- DiscordUtil.call("onEditorTreeLoaded", ["Character Editor"]);
- }
-
- override function createPost() {
- super.createPost();
-
- main.changeSelection(1);
+ for (o in generateList(modsList, isMods)) add(o);
}
public function createCharacter(name:String, imageSaveData:ImageSaveData, xml:Xml) {
var characterAlreadyExists:Bool = modsList.contains(name);
if (characterAlreadyExists) {
- openSubState(new UIWarningSubstate("Creating Character: Error!", "The character you are trying to create already exists, if you would like to override it delete the character first!", [
- {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
+ parent.openSubState(new UIWarningSubstate(TU.translate('characterCreationScreen.warning.char-exists-title'), TU.translate('characterCreationScreen.warning.char-exists-body'), [
+ {label: TU.translate('editor.ok'), color: 0xFFFF0000, onClick: (t) -> {}}
]));
return;
}
@@ -88,11 +76,10 @@ class CharacterSelection extends EditorTreeMenu
UIImageExplorer.saveFilesGlobal(imageSaveData, '${Paths.getAssetsRoot()}/images/characters');
// Add to Menu >:D
- var option:IconOption = new IconOption(name, "Press ACCEPT to edit this character.", Character.getIconFromCharName(name),
- function() {
- FlxG.switchState(new CharacterEditor(name));
- }
- );
- optionsTree.members[optionsTree.members.length-1].insert(1, option);
+ var option:IconOption = new IconOption(name, getID('acceptCharacter'), Character.getIconFromCharName(name), () -> {
+ FlxG.switchState(new CharacterEditor(name));
+ });
+
+ insert(1, option);
}
}
\ No newline at end of file
diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx
index 84d1dd57..445c30bc 100644
--- a/source/funkin/editors/charter/Charter.hx
+++ b/source/funkin/editors/charter/Charter.hx
@@ -789,9 +789,9 @@ class Charter extends UIState {
var songPath:String = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}';
if (Options.charterAutoSavesSeparateFolder)
- Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, folder: "autosaves", prettyPrint: Options.editorPrettyPrint});
+ Chart.save(songPath, PlayState.SONG, __autoSaveLocation, {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, folder: "autosaves", prettyPrint: Options.editorCharterPrettyPrint});
else // These two chart saves are particular, to avoid any kind of loss, stuff like meta, global and local events will be save all together - Nex
- Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, prettyPrint: Options.editorPrettyPrint});
+ Chart.save(songPath, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: true, saveLocalEvents: true, saveGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
undos.save();
}
autoSaveNotif.cancelled = false;
@@ -1483,7 +1483,7 @@ class Charter extends UIState {
}
public static function saveChartAs(shouldBuild:Bool = true, withEvents:Bool = true) {
- saveAs(Chart.filterChartForSaving(PlayState.SONG, false, withEvents, false), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
+ saveAs(Chart.filterChartForSaving(PlayState.SONG, false, withEvents, false), null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
defaultSaveFile: '${__diff.toLowerCase()}.json'
}, null, shouldBuild);
if (undos != null) undos.save();
@@ -1496,7 +1496,7 @@ class Charter extends UIState {
var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events};
var path = '${Paths.getAssetsRoot()}/songs/${__song.toLowerCase()}/events.json';
- if (data.events != null && data.events.length > 0) CoolUtil.safeSaveFile(path, Json.stringify(data, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null));
+ if (data.events != null && data.events.length > 0) CoolUtil.safeSaveFile(path, Json.stringify(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null));
else if (FileSystem.exists(path)) FileSystem.deleteFile(path); // Instead of replacing with a useless empty file, deletes the file directly - Nex
#else
saveEventsAs(shouldBuild);
@@ -1507,7 +1507,7 @@ class Charter extends UIState {
if (shouldBuild && instance != null) instance.buildChart();
var data = {events: Chart.filterChartForSaving(PlayState.SONG, false, false, true).events};
- saveAs(data, null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
+ saveAs(data, null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
defaultSaveFile: 'events.json'
}, null, false);
}
@@ -1532,13 +1532,13 @@ class Charter extends UIState {
}
public static function saveLegacyChartAs(shouldBuild:Bool = true) {
- saveAs(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
+ saveAs(FNFLegacyParser.encode(PlayState.SONG), null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json',
}, null, shouldBuild);
}
public static function savePsychChartAs(shouldBuild:Bool = true) {
- saveAs(PsychParser.encode(PlayState.SONG), null, Options.editorPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
+ saveAs(PsychParser.encode(PlayState.SONG), null, Options.editorCharterPrettyPrint ? Flags.JSON_PRETTY_PRINT : null, {
defaultSaveFile: '${__song.toLowerCase().replace(" ", "-")}${__diff.toLowerCase() == Flags.DEFAULT_DIFFICULTY ? "" : '-${__diff.toLowerCase()}'}.json',
}, null, shouldBuild);
}
@@ -1556,7 +1556,7 @@ class Charter extends UIState {
#if sys
public static function saveTo(path:String, separateEvents:Bool = false, shouldBuild:Bool = true) {
if (shouldBuild && instance != null) instance.buildChart();
- Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveLocalEvents: !separateEvents, prettyPrint: Options.editorPrettyPrint});
+ Chart.save(path, PlayState.SONG, __diff.toLowerCase(), {saveMetaInChart: false, saveLocalEvents: !separateEvents, prettyPrint: Options.editorCharterPrettyPrint});
}
#end
#end
@@ -1853,6 +1853,34 @@ class Charter extends UIState {
return bookmarks;
}
+ function _opponent_camera_add(_) {
+ var __event:CharterEvent = null;
+
+ __event = new CharterEvent(curStepFloat, [{
+ name: "Camera Movement",
+ params:[0],
+ time: Conductor.getTimeForStep(curStepFloat)
+ }], true);
+ __event.refreshEventIcons();
+ __event.global = true;
+ rightEventsGroup.add(__event);
+ undos.addToUndo(CEditEvent(__event, [], __event.events));
+
+ }
+ function _player_camera_add(_) {
+ var __event:CharterEvent = null;
+
+ __event = new CharterEvent(curStepFloat, [{
+ name: "Camera Movement",
+ params:[1],
+ time: Conductor.getTimeForStep(curStepFloat)
+ }], true);
+ __event.refreshEventIcons();
+ rightEventsGroup.add(__event);
+ undos.addToUndo(CEditEvent(__event, [], __event.events));
+
+ }
+
function _bookmarks_add(_) {
var addBookmarkAt = function(name:String, color:FlxColor, daStep:Float)
{
@@ -1965,6 +1993,17 @@ class Charter extends UIState {
onSelect: _song_end
},
null,
+ {
+ label: translate("song.addOpponentCamera"),
+ keybind: [O],
+ onSelect: _opponent_camera_add
+ },
+ {
+ label: translate("song.addPlayerCamera"),
+ keybind: [P],
+ onSelect: _player_camera_add
+ },
+ null,
{
label: translate("bookmarks.addBookmarkHere"),
onSelect: _bookmarks_add
diff --git a/source/funkin/editors/charter/CharterSelection.hx b/source/funkin/editors/charter/CharterSelection.hx
index 467817d7..85776f44 100644
--- a/source/funkin/editors/charter/CharterSelection.hx
+++ b/source/funkin/editors/charter/CharterSelection.hx
@@ -1,118 +1,83 @@
package funkin.editors.charter;
+import flixel.util.FlxColor;
import funkin.backend.chart.Chart;
-import funkin.backend.chart.ChartData.ChartMetaData;
import funkin.backend.chart.ChartData;
-import funkin.backend.system.framerate.Framerate;
-import funkin.editors.EditorTreeMenu;
import funkin.editors.charter.SongCreationScreen.SongCreationData;
+import funkin.editors.EditorTreeMenu;
import funkin.menus.FreeplayState.FreeplaySonglist;
-import funkin.options.*;
import funkin.options.type.*;
-import funkin.options.type.NewOption;
import haxe.Json;
using StringTools;
class CharterSelection extends EditorTreeMenu {
- public var freeplayList:FreeplaySonglist;
- public var curSong:ChartMetaData;
- public override function create() {
- bgType = "charter";
-
+ override function create() {
super.create();
+ DiscordUtil.call("onEditorTreeLoaded", ["Chart Editor"]);
+ addMenu(new CharterSelectionScreen());
+ bgType = 'charter';
+ }
+}
- Framerate.offset.y = 60;
+class CharterSelectionScreen extends EditorTreeMenuScreen {
+ public var freeplayList:FreeplaySonglist;
+ public var songList:Array = [];
+ public var curSong:ChartMetaData;
+ inline public function makeChartOption(d:String, name:String):TextOption {
+ return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d)));
+ }
+
+ public function makeSongOption(s:ChartMetaData):IconOption {
+ songList.push(s.name.toLowerCase());
+
+ var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> {
+ curSong = s;
+
+ var screen = new EditorTreeMenuScreen(s.name, getID('selectDifficulty'), [
+ for (d in s.difficulties) if (d != '') makeChartOption(d, s.name)
+ ]);
+
+ #if sys
+ screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> {
+ parent.openSubState(new ChartCreationScreen(saveChart));
+ }));
+ screen.curSelected = 1;
+ #end
+
+ parent.addMenu(screen);
+ });
+ opt.suffix = ' >';
+ opt.editorFlashColor = s.color.getDefault(FlxColor.WHITE);
+
+ return opt;
+ }
+
+ public function new() {
+ super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', () -> {
+ parent.openSubState(new SongCreationScreen(saveSong));
+ });
freeplayList = FreeplaySonglist.get(false);
- var list:Array = [
- for(s in freeplayList.songs) new EditorIconOption(s.name, TU.translate("charterSelection.acceptSong"), s.icon, function() {
- curSong = s;
- var list:Array = [
- for(d in s.difficulties) if (d != "")
- new TextOption(d, TU.translate("charterSelection.acceptDifficulty"), function() {
- FlxG.switchState(new Charter(s.name, d));
- })
- ];
- #if sys
- var newDiff = TU.translate("charterSelection.newDifficulty");
- list.push(new NewOption(newDiff, newDiff, function() {
- FlxG.state.openSubState(new ChartCreationScreen(saveChart));
- }));
- #end
- optionsTree.add(new OptionsScreen(s.name, TU.translate("charterSelection.selectDifficulty"), list));
- }, s.color.getDefault(0xFFFFFFFF))
- ];
-
- #if sys
- var newSong = TU.translate("charterSelection.newSong");
- list.insert(0, new NewOption(newSong, newSong, function() {
- FlxG.state.openSubState(new SongCreationScreen(saveSong));
- }));
- #end
-
- main = new OptionsScreen(TU.translate("editor.chart.name"), TU.translate("charterSelection.desc"), list);
-
- DiscordUtil.call("onEditorTreeLoaded", ["Chart Editor"]);
- }
-
- override function createPost() {
- super.createPost();
-
- main.changeSelection(1);
- }
-
- public override function update(elapsed:Float) {
- super.update(elapsed);
-
- bg.colorTransform.redOffset = lerp(bg.colorTransform.redOffset, 0, 0.0625);
- bg.colorTransform.greenOffset = lerp(bg.colorTransform.greenOffset, 0, 0.0625);
- bg.colorTransform.blueOffset = lerp(bg.colorTransform.blueOffset, 0, 0.0625);
- bg.colorTransform.redMultiplier = lerp(bg.colorTransform.redMultiplier, 1, 0.0625);
- bg.colorTransform.greenMultiplier = lerp(bg.colorTransform.greenMultiplier, 1, 0.0625);
- bg.colorTransform.blueMultiplier = lerp(bg.colorTransform.blueMultiplier, 1, 0.0625);
- }
-
- public override function onMenuChange() {
- super.onMenuChange();
- if (optionsTree.members.length > 1) { // selected a song
- if(main != null) {
- var opt = main.members[main.curSelected];
- if(opt is EditorIconOption) {
- var opt:EditorIconOption = cast opt;
-
- // small flashbang
- var color = opt.flashColor;
- bg.colorTransform.redOffset = 0.25 * color.red;
- bg.colorTransform.greenOffset = 0.25 * color.green;
- bg.colorTransform.blueOffset = 0.25 * color.blue;
- bg.colorTransform.redMultiplier = FlxMath.lerp(1, color.redFloat, 0.25);
- bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25);
- bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25);
- }
- }
- }
+ for (i => s in freeplayList.songs) add(makeSongOption(s));
}
#if sys
public function saveSong(creation:SongCreationData, ?callback:String -> SongCreationData -> Void) {
- var songAlreadyExists:Bool = [for (s in freeplayList.songs) s.name.toLowerCase()].contains(creation.meta.name.toLowerCase());
-
+ var songAlreadyExists:Bool = songList.contains(creation.meta.name.toLowerCase());
if (songAlreadyExists) {
- openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [
- {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}},
+ parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [
+ {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
]));
return;
}
- // Paths
- var songsDir:String = '${Paths.getAssetsRoot()}/songs/';
- var songFolder:String = '$songsDir${creation.meta.name}';
+ var songFolder:String = '${Paths.getAssetsRoot()}/songs/${creation.meta.name}';
#if sys
// Make Directories
- sys.FileSystem.createDirectory(songFolder);
+ CoolUtil.addMissingFolders(songFolder);
sys.FileSystem.createDirectory('$songFolder/song');
sys.FileSystem.createDirectory('$songFolder/charts');
@@ -125,52 +90,30 @@ class CharterSelection extends EditorTreeMenu {
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent.${Flags.SOUND_EXT}', creation.oppVocals);
#end
- if (callback != null)
- callback(songFolder, creation);
-
- var option = new EditorIconOption(creation.meta.name, TU.translate("charterSelection.acceptSong"), creation.meta.icon, function() {
- curSong = creation.meta;
- var list:Array = [
- for(d in creation.meta.difficulties)
- if (d != "") new TextOption(d, TU.translate("charterSelection.acceptDifficulty"), function() {
- FlxG.switchState(new Charter(creation.meta.name, d));
- })
- ];
- var newDiff = TU.translate("charterSelection.newDifficulty");
- list.push(new NewOption(newDiff, newDiff, function() {
- FlxG.state.openSubState(new ChartCreationScreen(saveChart));
- }));
- optionsTree.insert(1, new OptionsScreen(creation.meta.name, TU.translate("charterSelection.selectDifficulty"), list));
- }, creation.meta.color.getDefault(0xFFFFFFFF));
+ if (callback != null) callback(songFolder, creation);
// Add to List
freeplayList.songs.insert(0, creation.meta);
- main.insert(1, option);
+ insert(1, makeSongOption(creation.meta));
}
public function saveChart(name:String, data:ChartData) {
var difficultyAlreadyExists:Bool = curSong.difficulties.contains(name);
-
if (difficultyAlreadyExists) {
- openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [
- {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}},
+ parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [
+ {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
]));
return;
}
- // Paths
var songFolder:String = '${Paths.getAssetsRoot()}/songs/${curSong.name}';
// Save Files
CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, Flags.JSON_PRETTY_PRINT));
// Add to List
- // duplicated code, todo: fix this
curSong.difficulties.push(name);
- var option = new TextOption(name, TU.translate("charterSelection.acceptDifficulty"), function() {
- FlxG.switchState(new Charter(curSong.name, name));
- });
- optionsTree.members[optionsTree.members.length-1].insert(optionsTree.members[optionsTree.members.length-1].length-1, option);
+ parent.tree.last().insert(parent.tree.last().length - 1, makeChartOption(name, curSong.name));
// Add to Meta
var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json'));
diff --git a/source/funkin/editors/stage/StageEditor.hx b/source/funkin/editors/stage/StageEditor.hx
index d02cc997..8900c24f 100644
--- a/source/funkin/editors/stage/StageEditor.hx
+++ b/source/funkin/editors/stage/StageEditor.hx
@@ -726,8 +726,8 @@ class StageEditor extends UIState {
}
}
- var xmlThingYea:String = "\n" + Printer.print(xml, Options.editorPrettyPrint);
- return Options.editorPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
+ var xmlThingYea:String = "\n" + Printer.print(xml, Options.editorStagePrettyPrint);
+ return Options.editorStagePrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
}
function _edit_undo(_) {
diff --git a/source/funkin/editors/stage/StageSelection.hx b/source/funkin/editors/stage/StageSelection.hx
index b78aebc3..418e0190 100644
--- a/source/funkin/editors/stage/StageSelection.hx
+++ b/source/funkin/editors/stage/StageSelection.hx
@@ -1,46 +1,47 @@
package funkin.editors.stage;
import funkin.editors.stage.StageCreationScreen.StageCreationData;
+import funkin.editors.EditorTreeMenu;
import funkin.game.Stage;
-import funkin.options.OptionsScreen;
import funkin.options.type.NewOption;
import funkin.options.type.OptionType;
import funkin.options.type.TextOption;
-class StageSelection extends EditorTreeMenu
-{
+using StringTools;
+
+class StageSelection extends EditorTreeMenu {
+ override function create() {
+ super.create();
+ DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]);
+ addMenu(new StageSelectionScreen());
+ }
+}
+
+class StageSelectionScreen extends EditorTreeMenuScreen {
public var stages:Array = [];
- public override function create()
- {
- bgType = "charter";
- super.create();
+ public function makeStageOption(stage:String):TextOption {
+ return new TextOption(stage, getID('acceptStage'), () -> {
+ FlxG.switchState(new StageEditor(stage));
+ });
+ }
+
+ public function new() {
+ super('editor.stage.name', 'stageSelection.desc', 'stageSelection.', 'newStage', 'acceptNewStage', () -> {
+ parent.openSubState(new StageCreationScreen(saveStage));
+ });
var modsList:Array = Stage.getList(true, true);
-
- var list:Array = [
- for (stage in (modsList.length == 0 ? Stage.getList(false, true) : modsList)) {
- stages.push(stage);
- new TextOption(stage, TU.translate("stageSelection.acceptStage"),
- function() {
- FlxG.switchState(new StageEditor(stage));
- });
- }
- ];
-
- list.insert(0, new NewOption(TU.translate("stageSelection.newStage"), TU.translate("stageSelection.acceptNewStage"), function() {
- FlxG.state.openSubState(new StageCreationScreen(saveStage));
- }));
-
- main = new OptionsScreen(TU.translate("editor.stage.name"), TU.translate("stageSelection.desc"), list);
-
- DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]);
+ for (stage in (modsList.length == 0 ? Stage.getList(false, true) : modsList)) {
+ stages.push(stage.toLowerCase());
+ add(makeStageOption(stage));
+ }
}
public function saveStage(creation:StageCreationData) {
- if ([for (s in stages) s.toLowerCase()].contains(creation.name.toLowerCase())) {
- openSubState(new UIWarningSubstate("Creating Stage: Error!", "The stage you are trying to create alreadly exists, if you would like to override it delete the stage first!", [
- {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
+ if (stages.contains(creation.name.toLowerCase())) {
+ parent.openSubState(new UIWarningSubstate(TU.translate("stageCreationScreen.warnings.stage-exists-title"), TU.translate("stageCreationScreen.warnings.stage-exists-body"), [
+ {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
]));
return;
}
@@ -51,15 +52,7 @@ class StageSelection extends EditorTreeMenu
#end
// Add to List
- var option = new TextOption(creation.name, TU.translate("stageSelection.acceptStage"), function() {
- FlxG.switchState(new StageEditor(creation.name));
- });
- optionsTree.members[optionsTree.members.length-1].insert(1, option);
- }
-
- override function createPost() {
- super.createPost();
-
- main.changeSelection(1);
+ stages.push(creation.name.toLowerCase());
+ parent.tree.last().insert(parent.tree.last().length - 1, makeStageOption(creation.name));
}
}
\ No newline at end of file
diff --git a/source/funkin/editors/ui/UIAudioPlayer.hx b/source/funkin/editors/ui/UIAudioPlayer.hx
index 4b87dd4d..b919f5f4 100644
--- a/source/funkin/editors/ui/UIAudioPlayer.hx
+++ b/source/funkin/editors/ui/UIAudioPlayer.hx
@@ -29,9 +29,9 @@ class UIAudioPlayer extends UIButton {
public function new(x:Float, y:Float, bytes:Bytes) {
sound = FlxG.sound.load(Sound.fromAudioBuffer(AudioBuffer.fromBytes(bytes)));
- super(x, y, null, function () {
+ super(x, y, null, () -> {
if (sound.playing) sound.pause();
- else sound.play(false, sound.time);
+ else sound.play(sound.time + 1 >= sound.length ? 0 : sound.time);
}, 58 - 16, 58 - 16);
playingSprite = new FlxSprite(x + ((58 - 16)/2) - 8, y + ((58 - 16)/2) - 8).loadGraphic(Paths.image('editors/ui/audio-buttons'), true, 16, 16);
@@ -107,20 +107,17 @@ class UIAudioPlayer extends UIButton {
var spritePos:FlxPoint = sprite.getScreenPosition(FlxPoint.get(), __lastDrawCameras[0]);
- if (((mousePos.x > (spritePos.x)) && (mousePos.x < (spritePos.x) + timeBar.barWidth))
- && ((mousePos.y > (spritePos.y)) && (mousePos.y < (spritePos.y) + timeBar.barHeight))) {
- if (FlxG.mouse.justPressed) {
- draggingObj = sprite;
- wasPlaying = draggingObj == timeBar ? sound.playing : false;
- }
+ if (((mousePos.x > spritePos.x) && (mousePos.x < spritePos.x + sprite.barWidth))
+ && ((mousePos.y > spritePos.y) && (mousePos.y < spritePos.y + sprite.barHeight))) {
+ if (FlxG.mouse.justPressed) {
+ if ((draggingObj = sprite) == timeBar && (wasPlaying = sound.playing)) sound.pause();
+ }
if (FlxG.mouse.pressed) {
- if (draggingObj == timeBar) {
- if (sound.playing) sound.pause();
+ if (draggingObj == timeBar)
sound.time = FlxMath.remapToRange(mousePos.x - spritePos.x, 0, timeBar.barWidth, 0, sound.length);
- } else if (draggingObj == volumeBar) {
+ else if (draggingObj == volumeBar)
sound.volume = FlxMath.remapToRange(mousePos.x - spritePos.x, 0, volumeBar.barWidth, 0, 1);
- }
}
}
spritePos.put();
@@ -128,8 +125,10 @@ class UIAudioPlayer extends UIButton {
mousePos.put();
if (FlxG.mouse.released) {
- if (draggingObj == timeBar && wasPlaying)
- sound.play(wasPlaying = false, sound.time);
+ if (draggingObj == timeBar && wasPlaying) {
+ sound.play();
+ wasPlaying = false;
+ }
draggingObj = null;
}
}
diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx
index 15a9729f..32008f18 100644
--- a/source/funkin/game/Character.hx
+++ b/source/funkin/game/Character.hx
@@ -429,12 +429,12 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
var realFlipped:Bool = isPlayer ? !__baseFlipped : __baseFlipped;
if (realFlipped) xml.set("flipX", "true");
- xml.set("icon", getIcon());
+ if (icon != curCharacter) xml.set("icon", getIcon());
if (gameOverCharacter != Character.FALLBACK_DEAD_CHARACTER) xml.set("gameOverChar", gameOverCharacter);
if (iconColor != null) xml.set("color", iconColor.toWebString());
- xml.set("sprite", sprite);
+ if (sprite != curCharacter) xml.set("sprite", sprite);
if (scale.x != 1) xml.set("scale", Std.string(FlxMath.roundDecimal(scale.x, 4)));
if (!antialiasing) xml.set("antialiasing", antialiasing == true ? "true" : "false");
@@ -453,12 +453,12 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
animXml.set("name", anim.name);
animXml.set("anim", anim.anim);
- animXml.set("loop", Std.string(anim.loop));
- animXml.set("fps", Std.string(FlxMath.roundDecimal(anim.fps, 2)));
+ if (anim.loop) animXml.set("loop", Std.string(anim.loop));
+ if (FlxMath.roundDecimal(anim.fps, 2) != 24) animXml.set("fps", Std.string(FlxMath.roundDecimal(anim.fps, 2)));
var offset:FlxPoint = getAnimOffset(anim.name);
- animXml.set("x", Std.string(FlxMath.roundDecimal(offset.x, 2)));
- animXml.set("y", Std.string(FlxMath.roundDecimal(offset.y, 2)));
+ if (FlxMath.roundDecimal(offset.x, 2) != 0) animXml.set("x", Std.string(FlxMath.roundDecimal(offset.x, 2)));
+ if (FlxMath.roundDecimal(offset.y, 2) != 0) animXml.set("y", Std.string(FlxMath.roundDecimal(offset.y, 2)));
offset.putWeak();
if (anim.indices.length > 0)
@@ -474,6 +474,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
}
this.xml = new Access(xml);
+
return xml;
}
diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx
index f652b36c..2f054d9f 100644
--- a/source/funkin/menus/PauseSubState.hx
+++ b/source/funkin/menus/PauseSubState.hx
@@ -14,7 +14,6 @@ import funkin.backend.utils.FunkinParentDisabler;
import funkin.editors.charter.Charter;
import funkin.menus.StoryMenuState;
import funkin.options.OptionsMenu;
-import funkin.options.TreeMenu;
import funkin.options.keybinds.KeybindsOptions;
class PauseSubState extends MusicBeatSubstate
@@ -166,8 +165,7 @@ class PauseSubState extends MusicBeatSubstate
persistentDraw = false;
openSubState(new KeybindsOptions());
case "Change Options":
- TreeMenu.lastState = PlayState;
- FlxG.switchState(new OptionsMenu());
+ FlxG.switchState(new OptionsMenu((_) -> FlxG.switchState(new PlayState())));
case "Exit to charter":
FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
case "Exit to menu":
diff --git a/source/funkin/menus/credits/CreditsCodename.hx b/source/funkin/menus/credits/CreditsCodename.hx
index 70e172dc..7051e2f6 100644
--- a/source/funkin/menus/credits/CreditsCodename.hx
+++ b/source/funkin/menus/credits/CreditsCodename.hx
@@ -9,21 +9,20 @@ import funkin.options.type.GithubIconOption;
using StringTools;
-class CreditsCodename extends funkin.options.OptionsScreen {
+class CreditsCodename extends funkin.options.TreeMenuScreen {
public var error:Bool = false;
public var totalContributions:Int = 0;
public var contribFormats:Array = [];
- public override function new()
- {
- super("Codename Engine", TU.translate("credits.allContributors"));
+ public function new() {
+ super("Codename Engine", "credits.allContributors");
tryUpdating(true);
}
// blame the secondary threads if the code has to look this bad - Nex
private var _canReset:Bool = true;
private var _downloadingSteps:Int = 0;
- public override function update(elapsed:Float) {
+ override function update(elapsed:Float) {
super.update(elapsed);
if (_downloadingSteps == 2) {
@@ -35,37 +34,37 @@ class CreditsCodename extends funkin.options.OptionsScreen {
_canReset = true;
updateMenuDesc();
}
- else if(_canReset && PlayerSettings.solo.controls.RESET) tryUpdating();
+ else if (_canReset && PlayerSettings.solo.controls.RESET) tryUpdating();
}
public function tryUpdating(forceDisplaying:Bool = false) {
updateMenuDesc(TU.translate("credits.downloadingList"));
_canReset = false;
- Main.execAsync(function() {
- if(checkUpdate() || forceDisplaying) _downloadingSteps = 2;
+ Main.execAsync(() -> {
+ if (checkUpdate() || forceDisplaying) _downloadingSteps = 2;
else _downloadingSteps = 1;
});
}
public override function updateMenuDesc(?txt:String) {
- if(!_canReset) return;
+ if (!_canReset) return;
super.updateMenuDesc(txt);
updateMarkup();
}
public function updateMarkup() {
- if(parent == null || parent.treeParent == null) return;
- var text:String = parent.treeParent.pathDesc.text;
- parent.treeParent.pathDesc.text = "";
- parent.treeParent.pathDesc.applyMarkup(text, contribFormats = [
+ if (parent == null) return;
+ var text:String = parent.descLabel.text;
+ parent.descLabel.text = "";
+ parent.descLabel.applyMarkup(text, contribFormats = [
new FlxTextFormatMarkerPair(new FlxTextFormat(Flags.MAIN_DEVS_COLOR), '*'),
new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(Flags.MIN_CONTRIBUTIONS_COLOR, Flags.MAIN_DEVS_COLOR, Options.contributors[curSelected].contributions / totalContributions)), '~')
]);
}
override function close() {
+ for (frmt in contribFormats) parent.descLabel.removeFormat(frmt.format);
super.close();
- for (frmt in contribFormats) parent.treeParent.pathDesc.removeFormat(frmt.format);
}
public function checkUpdate():Bool {
diff --git a/source/funkin/menus/credits/CreditsMain.hx b/source/funkin/menus/credits/CreditsMain.hx
index 76485cbf..79182bcc 100644
--- a/source/funkin/menus/credits/CreditsMain.hx
+++ b/source/funkin/menus/credits/CreditsMain.hx
@@ -3,8 +3,8 @@ package funkin.menus.credits;
import flixel.util.FlxColor;
import funkin.backend.assets.AssetSource;
import funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor;
-import funkin.options.OptionsScreen;
import funkin.options.TreeMenu;
+import funkin.options.TreeMenuScreen;
import funkin.options.type.*;
import haxe.xml.Access;
@@ -12,15 +12,18 @@ class CreditsMain extends TreeMenu {
var bg:FlxSprite;
var items:Array = [];
- public override function create() {
- bg = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
- // bg.scrollFactor.set();
- bg.scale.set(1.15, 1.15);
- bg.updateHitbox();
- bg.screenCenter();
- bg.scrollFactor.set();
+ override function create() {
+ super.create();
+
+ DiscordUtil.call("onMenuLoaded", ["Credits Menu"]);
+
+ add(bg = new FlxSprite().loadAnimatedGraphic(Paths.image('menus/menuBGBlue')));
bg.antialiasing = true;
- add(bg);
+ bg.scrollFactor.set();
+ updateBG();
+
+ var first = new TreeMenuScreen('credits.name', 'credits.madePossible');
+ addMenu(first);
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
var xmlPath = Paths.xml('config/credits/LIB_$i');
@@ -29,28 +32,24 @@ class CreditsMain extends TreeMenu {
var access:Access = null;
try access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT")))
catch(e) Logs.trace('[CreditsMain] Error while parsing credits.xml: ${Std.string(e)}', ERROR);
- if (access != null) for (c in parseCreditsFromXML(access)) items.push(c);
+ if (access != null) for (c in parseCreditsFromXML(access)) first.add(c);
}
}
- items.push(new TextOption("Codename Engine >", TU.translate("credits.selectCodename"), function() {
- optionsTree.add(Type.createInstance(CreditsCodename, []));
- }));
- items.push(new TextOption("Friday Night Funkin'", TU.translate("credits.selectBase"), function() {
- CoolUtil.openURL(Flags.URL_FNF_ITCH);
- }));
-
- main = new OptionsScreen(TU.translate("credits.name"), TU.translate("credits.madePossible"), items);
- super.create();
-
- DiscordUtil.call("onMenuLoaded", ["Credits Menu"]);
+ first.add(new TextOption('Codename Engine', 'credits.selectCodename', ' >', () -> addMenu(new CreditsCodename())));
+ first.add(new TextOption('Friday Night Funkin\'', 'credits.selectBase', ' >', () -> CoolUtil.openURL(Flags.URL_FNF_ITCH)));
}
- /**
- * XML STUFF
- */
- public function parseCreditsFromXML(xml:Access, source:AssetSource = BOTH):Array {
- var credsMenus:Array = [];
+ public function updateBG() {
+ var scaleX:Float = FlxG.width / bg.width;
+ var scaleY:Float = FlxG.height / bg.height;
+ bg.scale.x = bg.scale.y = Math.max(scaleX, scaleY) * 1.15;
+ bg.screenCenter();
+ }
+
+ // XML STUFF
+ public function parseCreditsFromXML(xml:Access, source:AssetSource = BOTH):Array {
+ var credsMenus:Array = [];
for(node in xml.elements) {
var desc = node.getAtt("desc").getDefault("No Description");
@@ -94,13 +93,11 @@ class CreditsMain extends TreeMenu {
credsMenus.push(opt);
case "menu":
- credsMenus.push(new TextOption(name + " >", desc, function() {
- optionsTree.add(new OptionsScreen(name, desc, parseCreditsFromXML(node, source)));
- }));
+ credsMenus.push(new TextOption(name, desc, ' >', () -> addMenu(new TreeMenuScreen(name, desc, parseCreditsFromXML(node, source)))));
}
}
}
return credsMenus;
}
-}
+}
\ No newline at end of file
diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx
index c3508c31..fcc447a9 100644
--- a/source/funkin/menus/ui/Alphabet.hx
+++ b/source/funkin/menus/ui/Alphabet.hx
@@ -167,6 +167,7 @@ class Alphabet extends FlxSprite {
// for menu shit
public var targetY:Float = 0;
public var isMenuItem:Bool = false;
+ public var itemHeight:Float = 120;
public function new(?x:Float, ?y:Float, ?text:String = "", ?font:OneOfTwo = "normal") {
super(x, y);
@@ -202,7 +203,7 @@ class Alphabet extends FlxSprite {
if (isMenuItem) {
var scaledY = targetY * 1.3;
- y = CoolUtil.fpsLerp(y, (scaledY * 120) + (FlxG.height - height) * 0.5, 0.16);
+ y = CoolUtil.fpsLerp(y, (scaledY * itemHeight) + (FlxG.height - height) * 0.5, 0.16);
x = CoolUtil.fpsLerp(x, (targetY * 20) + 90, 0.16);
}
}
diff --git a/source/funkin/menus/ui/Slider.hx b/source/funkin/menus/ui/Slider.hx
new file mode 100644
index 00000000..c55d9724
--- /dev/null
+++ b/source/funkin/menus/ui/Slider.hx
@@ -0,0 +1,300 @@
+package funkin.menus.ui;
+
+import flixel.animation.FlxAnimation;
+import flixel.graphics.frames.FlxFramesCollection;
+import flixel.graphics.frames.FlxFrame;
+import flixel.math.FlxAngle;
+import flixel.math.FlxPoint;
+import flixel.math.FlxRect;
+import flixel.util.FlxDestroyUtil;
+
+class Slider extends FlxSprite {
+ public var playSound:Bool = true;
+ public var selected:Bool = false;
+ public var value:Float;
+ public var segments(default, set):Int;
+
+ public var barWidth(default, set):Float;
+ public var barHeight(default, null):Float;
+ public var showSlider:Bool = true;
+
+ public var barFramerate:Float = 24.0;
+ var __animTime:Float = 0.0;
+ var __frameTime:Int;
+ var __curSegments:Int;
+ var __barFrame:FlxFrame;
+ var __barClipRect:FlxRect;
+ var __valueWidth:Float;
+ var __cornerWidth:Float;
+ var __segmentWidth:Float;
+ var __barWidth:Float;
+ var __flipX:Bool;
+ var __flipY:Bool;
+
+ public function new(?x:Float, ?y:Float, value:Float = 0.5, barWidth:Float = 500, segments:Int = 5) {
+ super(x, y);
+ this.value = value;
+ this.barWidth = barWidth;
+ this.segments = segments;
+
+ frames = Paths.getFrames('menus/options/slider');
+ antialiasing = true;
+ }
+
+ override function initVars() {
+ super.initVars();
+ __barClipRect = FlxRect.get();
+ }
+
+ override function destroy() {
+ super.destroy();
+ __barFrame = FlxDestroyUtil.destroy(__barFrame);
+ __barClipRect = FlxDestroyUtil.put(__barClipRect);
+ }
+
+ function playAnimOrDefault(a:String, d:String) {
+ if (animation.exists(a)) animation.play(a);
+ else animation.play(d);
+ }
+
+ override function update(elapsed:Float) {
+ super.update(elapsed);
+ __frameTime = Math.floor((__animTime += elapsed) * barFramerate);
+
+ if (animation.curAnim == null) {
+ animation.play(selected ? 'selected' : 'unselected');
+ __curSegments = CoolUtil.minInt(segments, Math.floor(value * (segments + 1)));
+ }
+ else {
+ switch (animation.curAnim.name) {
+ case 'selected': if (!selected) playAnimOrDefault('deselect', 'unselected');
+ case 'unselected': if (selected) playAnimOrDefault('selecting', 'selected');
+ case 'selecting' | 'segment': if (animation.finished) animation.play('selected');
+ case 'deselect': if (animation.finished) animation.play('unselected');
+ }
+
+ if (__curSegments != (__curSegments = CoolUtil.minInt(segments, Math.floor(value * (segments + 1)))) && playSound) {
+ FlxG.sound.play(Paths.sound('menu/volume')).pitch = 0.75 + __curSegments * 0.5 / (segments + 1);
+ if (animation.curAnim.name == 'selected' && animation.exists('segment')) animation.play('segment');
+ }
+ }
+ }
+
+ override function isSimpleRender(?camera:FlxCamera):Bool return false;
+ override function drawComplex(camera:FlxCamera) {
+ __flipX = checkFlipX();
+ __flipY = checkFlipY();
+ __valueWidth = barWidth * value;
+
+ var width = barWidth / (segments + 1), x = drawCorner(camera, 0, false);
+ var end = barWidth - __cornerWidth, nextSegment = width - (segments == 0 ? 0 : __segmentWidth * 0.5);
+ while (x < end) {
+ if (x == (x = drawBar(camera, x, Math.min(nextSegment - x, end - x)))) break;
+ if (x < end && x < nextSegment + __segmentWidth * 0.5) {
+ x = drawSegment(camera, x, Math.min(__segmentWidth, nextSegment + __segmentWidth - x), CoolUtil.bound(x - end, 0, __segmentWidth));
+ }
+ nextSegment += width;
+ }
+ drawCorner(camera, x, true);
+
+ if (showSlider) {
+ _frame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY);
+ prepareDrawMatrix(camera, -_frame.sourceSize.x * 0.5 + (__flipX ? barWidth - __valueWidth : __valueWidth), -(_frame.sourceSize.y - barHeight) * 0.5);
+ forceDrawFrame(camera, _frame);
+ }
+ }
+
+ inline function prepareDrawMatrix(camera:FlxCamera, x:Float, y:Float) {
+ _matrix.translate(-origin.x, -origin.y);
+
+ if (frameOffsetAngle != null && frameOffsetAngle != angle) {
+ var angleOff = (frameOffsetAngle - angle) * FlxAngle.TO_RAD;
+ var cos = Math.cos(angleOff), sin = Math.sin(angleOff);
+
+ _matrix.rotateWithTrig(cos, -sin);
+ _matrix.translate(-frameOffset.x + x, -frameOffset.y + y);
+ _matrix.rotateWithTrig(cos, sin);
+ }
+ else
+ _matrix.translate(-frameOffset.x + x, -frameOffset.y + y);
+
+ _matrix.scale(scale.x, scale.y);
+
+ if (bakedRotationAngle <= 0) {
+ updateTrig();
+ if (angle != 0) _matrix.rotateWithTrig(_cosAngle, _sinAngle);
+ }
+
+ getScreenPosition(_point, camera).subtractPoint(offset);
+ _point.add(origin.x, origin.y);
+ _matrix.translate(_point.x, _point.y);
+
+ if (isPixelPerfectRender(camera)) {
+ _matrix.tx = Math.floor(_matrix.tx);
+ _matrix.ty = Math.floor(_matrix.ty);
+ }
+
+ doAdditionalMatrixStuff(_matrix, camera);
+ }
+
+ function forceDrawFrame(camera:FlxCamera, frame:FlxFrame) {
+ if (layer != null)
+ layer.drawPixels(this, camera, frame, frame.parent.bitmap, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null);
+ else
+ camera.drawPixels(frame, frame.parent.bitmap, _matrix, colorTransform, blend, antialiasing, shaderEnabled ? shader : null);
+ }
+
+ function drawCorner(camera:FlxCamera, x:Float, flip:Bool):Float {
+ var anim, frame;
+
+ if (__valueWidth > x) {
+ anim = getBarAnim(1, true);
+ frame = frames.frames[anim.frames[__frameTime % anim.numFrames]];
+
+ if (flip) __barClipRect.set(__cornerWidth - __valueWidth + x, 0, __valueWidth - x, frame.sourceSize.y);
+ else __barClipRect.set(0, 0, __valueWidth - x, frame.sourceSize.y);
+ __barFrame = frame.clipTo(__barClipRect, __barFrame);
+ __barFrame.prepareMatrix(_matrix, ANGLE_0, flip != __flipX != camera.flipX, __flipY != camera.flipY);
+ prepareDrawMatrix(camera, __flipX ? barWidth - x - frame.sourceSize.x : x, 0);
+ forceDrawFrame(camera, __barFrame);
+ }
+
+ if (__valueWidth - x < __cornerWidth) {
+ anim = getBarAnim(1, false);
+ frame = frames.frames[anim.frames[__frameTime % anim.numFrames]];
+
+ if (flip) __barClipRect.set(0, 0, __cornerWidth - __valueWidth + x, frame.sourceSize.y);
+ else __barClipRect.set(__valueWidth - x, 0, __cornerWidth - __valueWidth + x, frame.sourceSize.y);
+ __barFrame = frame.clipTo(__barClipRect, __barFrame);
+ __barFrame.prepareMatrix(_matrix, ANGLE_0, flip != __flipX != camera.flipX, __flipY != camera.flipY);
+ prepareDrawMatrix(camera, __flipX ? barWidth - x - frame.sourceSize.x : x, 0);
+ forceDrawFrame(camera, __barFrame);
+ }
+
+ return x + __cornerWidth;
+ }
+
+ function drawBar(camera:FlxCamera, x:Float, width:Float):Float {
+ if (width <= 0) return x;
+ var anim, frame, s;
+
+ if (__valueWidth > x) {
+ anim = getBarAnim(0, true);
+ frame = frames.frames[anim.frames[__frameTime % anim.numFrames]];
+ s = width / frame.sourceSize.x;
+
+ __barClipRect.set(0, 0, (__valueWidth - x) / s, frame.sourceSize.y);
+ __barFrame = frame.clipTo(__barClipRect, __barFrame);
+ __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY);
+ _matrix.scale(s, 1);
+ prepareDrawMatrix(camera, __flipX ? barWidth - x - width : x, 0);
+ forceDrawFrame(camera, __barFrame);
+ }
+
+ if (__valueWidth - x < width) {
+ anim = getBarAnim(0, false);
+ frame = frames.frames[anim.frames[__frameTime % anim.numFrames]];
+ s = width / frame.sourceSize.x;
+
+ __barClipRect.set((__valueWidth - x) / s, 0, (width - __valueWidth + x) / s, frame.sourceSize.y);
+ __barFrame = frame.clipTo(__barClipRect, __barFrame);
+ __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY);
+ _matrix.scale(s, 1);
+ prepareDrawMatrix(camera, __flipX ? barWidth - x - width : x, 0);
+ forceDrawFrame(camera, __barFrame);
+ }
+
+ return x + width;
+ }
+
+ function drawSegment(camera:FlxCamera, x:Float, width:Float, offset:Float):Float {
+ if (width <= 0) return x;
+ var anim, frame;
+
+ if (__valueWidth > x) {
+ anim = getBarAnim(2, true);
+ frame = frames.frames[anim.frames[__frameTime % anim.numFrames]];
+
+ __barClipRect.set(offset, 0, __valueWidth - x, frame.sourceSize.y);
+ __barFrame = frame.clipTo(__barClipRect, __barFrame);
+ __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY);
+ prepareDrawMatrix(camera, __flipX ? barWidth - x - width : x, 0);
+ forceDrawFrame(camera, __barFrame);
+ }
+
+ if (__valueWidth - x < __segmentWidth) {
+ anim = getBarAnim(2, false);
+ frame = frames.frames[anim.frames[__frameTime % anim.numFrames]];
+
+ __barClipRect.set(__valueWidth - x + offset, 0, __segmentWidth - offset - __valueWidth + x, frame.sourceSize.y);
+ __barFrame = frame.clipTo(__barClipRect, __barFrame);
+ __barFrame.prepareMatrix(_matrix, ANGLE_0, __flipX != camera.flipX, __flipY != camera.flipY);
+ prepareDrawMatrix(camera, __flipX ? barWidth - x - width : x, 0);
+ forceDrawFrame(camera, __barFrame);
+ }
+
+ return x + width;
+ }
+
+ function getBarAnim(type:Int, filled:Bool):FlxAnimation {
+ var name = (type == 1 ? 'corner ' : (type == 2 ? 'segment ' : 'bar ')) + (filled ? 'filled0' : 'empty0');
+ if (animation.exists(name)) return animation.getByName(name);
+
+ animation.addByPrefix(name, name, barFramerate, true);
+ return animation.getByName(name);
+ }
+
+ inline function sliderResetFrameSize() {
+ frameWidth = Std.int(barWidth);
+ frameHeight = Std.int(barHeight);
+ _halfSize.set(0.5 * frameWidth, 0.5 * frameHeight);
+ resetSize();
+ }
+
+ override function resetHelpers() {
+ animation.addByPrefix('unselected', 'slider unselected0', 24, true);
+ animation.addByPrefix('selected', 'slider selected0', 24, true);
+ animation.addByPrefix('deselect', 'slider deselect0', 24, false);
+ animation.addByPrefix('selecting', 'slider selecting0', 24, false);
+ animation.addByPrefix('segment', 'slider segment0', 24, false);
+
+ var anim, width;
+ inline function checkSize(type) {
+ barHeight = Math.max(barHeight, frames.frames[anim.frames[0]].sourceSize.y);
+ width = frames.frames[anim.frames[0]].sourceSize.x;
+ switch (type) {
+ case 0: __barWidth = Math.max(__barWidth, width);
+ case 1: __cornerWidth = Math.max(__cornerWidth, width);
+ case 2: __segmentWidth = Math.max(__segmentWidth, width);
+ }
+ }
+
+ barHeight = 0;
+ for (type in 0...3) {
+ if ((anim = getBarAnim(type, false)) != null) checkSize(type);
+ if ((anim = getBarAnim(type, true)) != null) checkSize(type);
+ }
+
+ sliderResetFrameSize();
+ resetSizeFromFrame();
+ centerOrigin();
+
+ if (FlxG.renderBlit) {
+ dirty = true;
+ updateFramePixels();
+ }
+ }
+
+ override function set_frame(v:FlxFrame):FlxFrame {
+ super.set_frame(v);
+ if (v != null) sliderResetFrameSize();
+ return v;
+ }
+
+ function set_barWidth(v:Float):Float {
+ origin.x = _halfSize.x = 0.5 * (frameWidth = Std.int(barWidth = v));
+ return v;
+ }
+
+ function set_segments(v:Int):Int return segments = CoolUtil.maxInt(v, 0);
+}
\ No newline at end of file
diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx
index a60d519d..0b0bda8d 100644
--- a/source/funkin/options/Options.hx
+++ b/source/funkin/options/Options.hx
@@ -29,6 +29,7 @@ class Options
public static var autoPause:Bool = true;
public static var antialiasing:Bool = true;
public static var volume:Float = 1;
+ public static var volumeMusic:Float = 1;
public static var volumeSFX:Float = 1;
public static var week6PixelPerfect:Bool = true;
public static var gameplayShaders:Bool = true;
@@ -44,6 +45,8 @@ class Options
public static var language = "en"; // default to english, Flags.DEFAULT_LANGUAGE should not modify this
public static var streamedMusic:Bool = true;
public static var streamedVocals:Bool = true;
+ public static var quality:Int = 1;
+ public static var allowConfigWarning:Bool = true;
#if MODCHARTING_FEATURES
public static var modchartingHoldSubdivisions:Int = 4;
#end
@@ -55,7 +58,11 @@ class Options
*/
public static var intensiveBlur:Bool = true;
public static var editorSFX:Bool = true;
- public static var editorPrettyPrint:Bool = false;
+
+ public static var editorCharterPrettyPrint:Bool = false;
+ public static var editorCharacterPrettyPrint:Bool = true;
+ public static var editorStagePrettyPrint:Bool = true;
+
public static var editorsResizable:Bool = true;
public static var bypassEditorsResize:Bool = false;
public static var maxUndos:Int = 120;
@@ -207,9 +214,23 @@ class Options
public static function applySettings() {
applyKeybinds();
- FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? LOW : BEST;
+
+ switch (quality) {
+ case 0:
+ antialiasing = false;
+ lowMemoryMode = true;
+ gameplayShaders = false;
+ case 1:
+ antialiasing = true;
+ lowMemoryMode = false;
+ gameplayShaders = true;
+ }
+
+ FlxG.sound.defaultMusicGroup.volume = volumeMusic;
+ FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? BEST : LOW;
FlxG.autoPause = autoPause;
- FlxG.drawFramerate = FlxG.updateFramerate = framerate;
+ if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
+ else FlxG.updateFramerate = FlxG.drawFramerate = framerate;
}
public static function applyKeybinds() {
diff --git a/source/funkin/options/OptionsMenu.hx b/source/funkin/options/OptionsMenu.hx
index ace34aad..6359a21f 100644
--- a/source/funkin/options/OptionsMenu.hx
+++ b/source/funkin/options/OptionsMenu.hx
@@ -1,93 +1,94 @@
package funkin.options;
-import funkin.backend.system.framerate.Framerate;
-import funkin.options.TreeMenu;
+import haxe.xml.Access;
+import flixel.util.typeLimit.OneOfThree;
+import funkin.editors.ui.UIState;
import funkin.options.categories.*;
import funkin.options.type.*;
-import funkin.options.type.Checkbox;
-import haxe.xml.Access;
+
+typedef OptionCategory = {
+ var name:String;
+ var desc:String;
+ var ?state:OneOfThree, (name:String, desc:String) -> TreeMenuScreen>;
+ var ?substate:OneOfThree, (name:String, desc:String) -> MusicBeatSubstate>;
+ var ?suffix:String;
+}
class OptionsMenu extends TreeMenu {
public static var mainOptions:Array = [
{ // name and desc are actually the translations ids! - Nex
name: 'optionsTree.controls-name',
desc: 'optionsTree.controls-desc',
- state: null,
- substate: (name:String, desc:String) -> {
- return new funkin.options.keybinds.KeybindsOptions();
- }
+ suffix: '',
+ substate: funkin.options.keybinds.KeybindsOptions
},
{
name: 'optionsTree.gameplay-name',
desc: 'optionsTree.gameplay-desc',
- suffix: " >",
state: GameplayOptions
},
{
name: 'optionsTree.appearance-name',
desc: 'optionsTree.appearance-desc',
- suffix: " >",
state: AppearanceOptions
},
#if TRANSLATIONS_SUPPORT
{
name: 'optionsTree.language-name',
desc: 'optionsTree.language-desc',
- suffix: " >",
state: LanguageOptions
},
#end
{
name: 'optionsTree.miscellaneous-name',
desc: 'optionsTree.miscellaneous-desc',
- suffix: " >",
state: MiscOptions
}
];
var bg:FlxSprite;
+ var debugOption:TextOption;
- public override function create() {
+ override function create() {
super.create();
CoolUtil.playMenuSong();
DiscordUtil.call("onMenuLoaded", ["Options Menu"]);
- bg = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
- // bg.scrollFactor.set();
- bg.scale.set(1.15, 1.15);
- bg.updateHitbox();
- bg.screenCenter();
- bg.scrollFactor.set();
+ add(bg = new FlxSprite().loadAnimatedGraphic(Paths.image('menus/menuBGBlue')));
bg.antialiasing = true;
- add(bg);
+ bg.scrollFactor.set();
+ updateBG();
- main = new OptionsScreen("optionsMenu.header.title", "optionsMenu.header.desc", [for(o in mainOptions) {
- new TextOption(o.name, o.desc, o.suffix, function() {
- if (o.substate != null) {
- persistentUpdate = false;
- persistentDraw = true;
- if (o.substate is MusicBeatSubstate) {
- openSubState(o.substate);
- } else if(Reflect.isFunction(o.substate)) {
- var substate:(name:String, desc:String) -> MusicBeatSubstate = o.substate;
- openSubState(substate(o.name, o.desc));
- } else { // o.substate is Class
- openSubState(Type.createInstance(o.substate, [o.name, o.desc]));
- }
- } else {
- if (o.state is OptionsScreen) {
- optionsTree.add(o.state);
- } else if(Reflect.isFunction(o.state)) {
- var state:(name:String, desc:String) -> OptionsScreen = o.state;
- optionsTree.add(state(o.name, o.desc));
- } else { // o.state is Class
- optionsTree.add(Type.createInstance(o.state, [o.name, o.desc]));
- }
+ addMenu(new TreeMenuScreen('optionsMenu.header.title', 'optionsMenu.header.desc', [for (o in mainOptions) new TextOption(o.name, o.desc, o.suffix != null ? o.suffix : " >", () -> {
+ if (o.substate != null) {
+ persistentUpdate = false;
+ persistentDraw = true;
+
+ if (o.substate is MusicBeatSubstate)
+ openSubState(o.substate);
+ else if(Reflect.isFunction(o.substate)) {
+ var substate:(name:String, desc:String) -> MusicBeatSubstate = o.substate;
+ openSubState(substate(o.name, o.desc));
}
- });
- }]);
+ else // o.substate is Class
+ openSubState(Type.createInstance(o.substate, [o.name, o.desc]));
+ }
+ else {
+ if (o.state is TreeMenuScreen)
+ addMenu(o.state);
+ else if (Reflect.isFunction(o.state)) {
+ var state:(name:String, desc:String) -> TreeMenuScreen = o.state;
+ addMenu(state(o.name, o.desc));
+ }
+ else { // o.state is Class
+ addMenu(Type.createInstance(o.state, [o.name, o.desc]));
+ }
+ }
+ })]));
+
+ checkDebugOption();
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
var xmlPath = Paths.xml('config/options/LIB_$i');
@@ -96,30 +97,55 @@ class OptionsMenu extends TreeMenu {
var access:Access = null;
try access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT")))
catch(e) Logs.trace('Error while parsing options.xml: ${Std.string(e)}', ERROR);
- if (access != null) for (o in parseOptionsFromXML(access)) main.add(o);
+ if (access != null) for (o in parseOptionsFromXML(access)) tree.first().add(o);
}
}
}
- public function reloadStrings() {
- for(o in main.members) {
- o.reloadStrings();
+ function checkDebugOption() {
+ var screen = tree.first();
+ if (Options.devMode) {
+ if (debugOption == null) {
+ screen.insert(CoolUtil.minInt(screen.length, mainOptions.length),
+ debugOption = new TextOption('optionsTree.debug-name', 'optionsTree.debug-desc', ' >', () -> addMenu(new DebugOptions()))
+ );
+ }
+ }
+ else if (debugOption != null) {
+ screen.remove(debugOption, true);
+ debugOption = flixel.util.FlxDestroyUtil.destroy(debugOption);
+ if (screen.curSelected >= screen.length) screen.changeSelection(0, true);
}
- optionsTree.reloadStrings();
- reloadLabels();
}
- public override function exit() {
+ public function updateBG() {
+ var scaleX:Float = FlxG.width / bg.width;
+ var scaleY:Float = FlxG.height / bg.height;
+ bg.scale.x = bg.scale.y = Math.max(scaleX, scaleY) * 1.15;
+ bg.screenCenter();
+ }
+
+ override function onResize(width:Int, height:Int) {
+ super.onResize(width, height);
+ if (!UIState.resolutionAware) return;
+
+ updateBG();
+ }
+
+ override function menuChanged() {
+ super.menuChanged();
+ checkDebugOption();
+ }
+
+ override function exit() {
Options.save();
Options.applySettings();
super.exit();
}
- /**
- * XML STUFF
- */
- public function parseOptionsFromXML(xml:Access):Array {
- var options:Array = [];
+ // XML STUFF
+ public function parseOptionsFromXML(xml:Access):Array {
+ var options:Array = [];
for(node in xml.elements) {
if (!node.has.name) {
@@ -135,7 +161,7 @@ class OptionsMenu extends TreeMenu {
Logs.warn("A checkbox option requires an \"id\" for option saving.");
continue;
}
- options.push(new Checkbox(name, desc, node.att.id, FlxG.save.data));
+ options.push(new Checkbox(name, desc, node.att.id, null, FlxG.save.data));
case "number":
if (!node.has.id) {
@@ -161,9 +187,7 @@ class OptionsMenu extends TreeMenu {
options.push(new ArrayOption(name, desc, optionOptions, optionDisplayOptions, node.att.id, null, FlxG.save.data));
case "menu":
- options.push(new TextOption(name + " >", desc, function() {
- optionsTree.add(new OptionsScreen(name, desc, parseOptionsFromXML(node)));
- }));
+ options.push(new TextOption(name, desc, ' >', () -> addMenu(new TreeMenuScreen(name, desc, parseOptionsFromXML(node)))));
}
}
diff --git a/source/funkin/options/OptionsScreen.hx b/source/funkin/options/OptionsScreen.hx
deleted file mode 100644
index 53dd5b06..00000000
--- a/source/funkin/options/OptionsScreen.hx
+++ /dev/null
@@ -1,139 +0,0 @@
-package funkin.options;
-
-import funkin.options.type.OptionType;
-
-class OptionsScreen extends FlxTypedSpriteGroup {
- public static var optionHeight:Float = 120;
-
- public var parent:OptionsTree;
-
- public var curSelected:Int = 0;
- public var id:Int = 0;
-
- private var __firstFrame:Bool = true;
-
- public var name:String;
- public var desc:String;
- /**
- * The prefix to add to the translations ids.
- **/
- public var prefix:String = "";
-
- private var rawName(default, set):String;
- private var rawDesc(default, set):String;
-
- public inline function getNameID(name):String {
- return prefix + name + "-name";
- }
- public inline function getDescID(name):String {
- return prefix + name + "-desc";
- }
-
- public function getName(name:String, ?args:Array):String {
- return TU.translate(getNameID(name), args);
- }
- public function getDesc(name:String, ?args:Array):String {
- return TU.translate(getDescID(name), args);
- }
- public function translate(name:String, ?args:Array):String {
- return TU.translate(prefix + name, args);
- }
-
- public function new(name:String, desc:String, ?prefix:String, ?options:Array) {
- super();
- rawName = name;
- rawDesc = desc;
- this.prefix = prefix != null ? prefix : "";
- if (options != null) for(o in options) add(o);
- }
-
- function set_rawName(v:String) {
- rawName = v;
- this.name = TU.exists(rawName) ? TU.translate(rawName) : rawName;
- return v;
- }
-
- function set_rawDesc(v:String) {
- rawDesc = v;
- this.desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
- return v;
- }
-
- public function reloadStrings() {
- this.rawName = rawName;
- this.rawDesc = rawDesc;
-
- for(o in members) {
- o.reloadStrings();
- }
- }
-
- public override function update(elapsed:Float) {
- super.update(elapsed);
-
- var controls = PlayerSettings.solo.controls;
-
- changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
- x = id * FlxG.width;
- for(k=>option in members) {
- if(option == null) continue;
-
- var y:Float = ((FlxG.height - optionHeight) / 2) + ((k - curSelected) * optionHeight);
-
- option.selected = false;
- option.y = __firstFrame ? y : CoolUtil.fpsLerp(option.y, y, 0.25);
- option.x = x + (-50 + (Math.abs(Math.cos((option.y + (optionHeight / 2) - (FlxG.camera.scroll.y + (FlxG.camera.height / 2))) / (FlxG.camera.height * 1.25) * Math.PI)) * 150));
- }
- if (__firstFrame) {
- __firstFrame = false;
- return;
- }
-
- if (members.length > 0) {
- members[curSelected].selected = true;
- if (controls.ACCEPT || (FlxG.mouse.justReleased && Main.timeSinceFocus > 0.25)) {
- members[curSelected].onSelect();
- onSelect(members[curSelected]);
- }
- if (controls.LEFT_P)
- members[curSelected].onChangeSelection(-1);
- if (controls.RIGHT_P)
- members[curSelected].onChangeSelection(1);
- }
- if (controls.BACK || FlxG.mouse.justReleasedRight)
- close();
- }
-
- public function close() {
- onClose(this);
- }
-
- public function changeSelection(sel:Int, force:Bool = false) {
- if (members.length <= 0 || (sel == 0 && !force)) return;
-
- CoolUtil.playMenuSFX(SCROLL);
- curSelected = FlxMath.wrap(curSelected + sel, 0, members.length-1);
- members[curSelected].selected = true;
- updateMenuDesc();
- }
-
- public function updateMenuDesc(?customTxt:String) {
- if (parent == null || parent.treeParent == null) return;
- parent.treeParent.updateDesc(customTxt != null ? customTxt : members[curSelected].desc);
- }
-
- public dynamic function onClose(o:OptionsScreen) {}
- public dynamic function onSelect(o:OptionType) {
- if(o is funkin.options.type.RadioButton) {
- var orb:funkin.options.type.RadioButton = cast o;
- for(e in members) {
- if(e is funkin.options.type.RadioButton) {
- var rb:funkin.options.type.RadioButton = cast e;
- if(rb.forId == orb.forId && rb != o) {
- rb.checked = false;
- }
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/source/funkin/options/OptionsTree.hx b/source/funkin/options/OptionsTree.hx
deleted file mode 100644
index 9fd9e590..00000000
--- a/source/funkin/options/OptionsTree.hx
+++ /dev/null
@@ -1,89 +0,0 @@
-package funkin.options;
-
-class OptionsTree extends FlxTypedGroup {
- public var lastMenu:OptionsScreen;
- public var treeParent:TreeMenu;
- public var wasClosing:Bool = false;
- //public override function new() {
- // super();
- //}
-
- public override function update(elapsed:Float) {
- var last = members.last();
- if (last != null)
- last.update(elapsed);
- }
-
- public function updateAll(elapsed:Float) {
- for (member in members) {
- if(member == null) continue;
- if(member.active && member.exists) {
- member.update(elapsed);
- }
- }
- }
-
- public override function draw() {
- super.draw();
- if (lastMenu != null) { // manually draw lastMenu since it got removed from the members list
- lastMenu.draw();
- }
- }
-
- public function reloadStrings() {
- for(o in members) {
- o.reloadStrings();
- }
- }
-
- public override function add(m:OptionsScreen) {
- super.add(m);
- setup(m);
- clearLastMenu();
- wasClosing = false;
- onMenuChange();
- return m;
- }
- public override function insert(pos:Int, m:OptionsScreen) {
- var last = members.last();
- super.insert(pos, m);
- setup(m);
- if (last != members.last()) {
- wasClosing = false;
- onMenuChange();
- }
- return m;
- }
-
- public function setup(m:OptionsScreen) {
- m.onClose = __subMenuClose;
- m.id = members.indexOf(m);
- m.parent = this;
- m.update(0);
- }
-
- function __subMenuClose(m:OptionsScreen) {
- wasClosing = true;
- clearLastMenu();
- lastMenu = m;
- remove(m, true);
- onMenuChange();
- onMenuClose(m);
- }
-
- public function clearLastMenu() {
- lastMenu = FlxDestroyUtil.destroy(lastMenu);
- }
-
- public override function destroy() {
- super.destroy();
- lastMenu = FlxDestroyUtil.destroy(lastMenu);
- }
-
- public dynamic function onMenuChange() {
-
- }
-
- public dynamic function onMenuClose(m:OptionsScreen) {
- }
-}
\ No newline at end of file
diff --git a/source/funkin/options/TreeMenu.hx b/source/funkin/options/TreeMenu.hx
index 41717e88..33ae47c6 100644
--- a/source/funkin/options/TreeMenu.hx
+++ b/source/funkin/options/TreeMenu.hx
@@ -1,148 +1,298 @@
package funkin.options;
-import flixel.FlxState;
import flixel.tweens.FlxTween;
-import flixel.util.typeLimit.OneOfTwo;
-import flixel.util.typeLimit.OneOfThree;
+import flixel.util.FlxArrayUtil;
+import flixel.util.FlxColor;
+import flixel.util.FlxSignal;
import funkin.backend.FunkinText;
-import funkin.editors.ui.UIState;
-import funkin.menus.MainMenuState;
-import funkin.options.type.OptionType;
-import funkin.options.type.TextOption;
import funkin.backend.system.framerate.Framerate;
+import funkin.editors.ui.UIState;
+
+interface ITreeOption {
+ var desc:String;
+ var selected:Bool;
+
+ function changeSelection(change:Int):Void;
+ function select():Void;
+}
+
+interface ITreeFloatOption extends ITreeOption {
+ function changeValue(change:Float):Void;
+}
class TreeMenu extends UIState {
- public var main:OptionsScreen;
- public var optionsTree:OptionsTree;
- public var pathLabel:FunkinText;
- public var pathDesc:FunkinText;
- public var pathBG:FlxSprite;
+ public var onMenuClosed:FlxTypedSignalVoid> = new FlxTypedSignal();
+ public var onMenuChanged:FlxTypedSignalVoid> = new FlxTypedSignal();
- public var screenScroll(default, set):Float;
- private inline function set_screenScroll(val:Float) {
- FlxG.camera.scroll.x = val * FlxG.camera.width;
- return screenScroll = val;
- }
+ public var tree(default, null):Array = [];
+ public var treeLength(default, null):Int = 0;
+ public var previousMenus:Array = [];
+ public var destroyMenus:Bool = true;
- public static var lastState:Class = null; // Static for fixing the softlock bugs when resetting the state - Nex
+ public var exitCallback:TreeMenu->Void;
- public function new(scriptsAllowed:Bool = true, ?scriptName:String) {
- if (lastState == null) lastState = Type.getClass(FlxG.state);
+ public var titleLabel:FunkinText;
+ public var descLabel:FunkinText;
+ public var bgLabel:FlxSprite;
+
+ var menuChangeTween:FlxTween;
+ var __drawer:TreeMenuDrawer;
+ var __treeCreated:Bool = false;
+
+ public function new(?exitCallback:TreeMenu->Void,
+ scriptsAllowed:Bool = true, ?scriptName:String)
+ {
super(scriptsAllowed, scriptName);
+ this.exitCallback = exitCallback;
}
- public override function createPost() {
- if (main == null) main = new OptionsScreen("Fallback Treemenu", "Please set the \"main\" variable in your extended class before createPost", [new TextOption("Oops! No Options", "This doesn't look like it was supposed to happen...", () -> FlxG.resetState() ) ]);
+ override function create() {
+ super.create();
- screenScroll = -1;
+ bgLabel = new FlxSprite().makeGraphic(1, 1, FlxColor.BLACK);
+ bgLabel.alpha = 0.25;
+ bgLabel.scrollFactor.set();
- pathLabel = new FunkinText(4, 4, FlxG.width - 8, "> Tree Menu", 32, true);
- pathLabel.borderSize = 1.25;
- pathLabel.scrollFactor.set();
-
- pathDesc = new FunkinText(4, pathLabel.y + pathLabel.height + 2, FlxG.width - 8, "Current Tree Menu Description", 16, true);
- pathDesc.scrollFactor.set();
-
- pathBG = new FlxSprite().makeGraphic(1, 1, 0xFF000000);
- pathBG.scale.set(FlxG.width, pathDesc.y + pathDesc.height + 2);
- pathBG.updateHitbox();
- pathBG.alpha = 0.25;
- pathBG.scrollFactor.set();
-
- optionsTree = new OptionsTree();
- optionsTree.onMenuChange = onMenuChange;
- optionsTree.onMenuClose = onMenuClose;
- optionsTree.treeParent = this;
- optionsTree.add(main);
-
-
- add(optionsTree);
- add(pathBG);
- add(pathLabel);
- add(pathDesc);
+ titleLabel = new FunkinText(4, 4, FlxG.width - 8, 32);
+ titleLabel.borderSize = 1.25;
+ titleLabel.scrollFactor.set();
+ descLabel = new FunkinText(4, 0, FlxG.width - 8, 16);
+ descLabel.scrollFactor.set();
+ }
+ override function createPost() {
super.createPost();
+
+ if ((treeLength = tree.length) != 0) {
+ updateMenuPositions(true);
+ tree.last().inputEnabled = true;
+ }
+ else
+ addMenu(new TreeMenuScreen("Fallback TreeMenuScreen", "Please insert menus into \"tree\" variable in your extended class in create or before createPost"));
+
+ add(__drawer = new TreeMenuDrawer(this));
+ add(bgLabel);
+ add(titleLabel);
+ add(descLabel);
+
+ FlxG.camera.scroll.x = -FlxG.width;
+ menuChanged();
+
+ __treeCreated = true;
}
- public function onMenuChange() {
- if (optionsTree.members.length <= 0) {
- exit();
- } else {
- if (menuChangeTween != null)
- menuChangeTween.cancel();
+ public function updateMenuPositions(fromIndex:Int = 0, cameraScroll = false) {
+ var last = tree[fromIndex - 1], menu:TreeMenuScreen;
+ while (fromIndex < treeLength) if ((menu = tree[fromIndex++]) != null) {
+ menu.x = last == null ? 0 : last.x + Math.max(FlxG.width, last.width);
+ (last = menu).parent = this;
+ }
- menuChangeTween = FlxTween.num(screenScroll, Math.max(0, (optionsTree.members.length-1)), 1.5, {ease: menuTransitionEase, onComplete: function(t) {
- optionsTree.clearLastMenu();
- menuChangeTween = null;
- }}, (val:Float) -> screenScroll = val);
+ if (cameraScroll && last != null) {
+ if (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel();
+ FlxG.camera.scroll.x = last.x;
- reloadLabels();
+ for (menu in tree) if (menu != null) menu.transitioning = false;
+ for (menu in previousMenus) if (menu != null) menu.transitioning = false;
}
}
- public function onMenuClose(m:OptionsScreen) {
- CoolUtil.playMenuSFX(CANCEL);
+ public function updateLabels() {
+ var s = "", last = tree.last();
+ for (menu in tree) if (menu != null) s += menu.name + " > ";
+
+ titleLabel.text = s;
+ descLabel.y = titleLabel.y + titleLabel.height + 2;
+
+ updateDesc();
}
- public function reloadLabels() {
- var t = "";
- for(o in optionsTree.members)
- t += '${o.name} > ';
- pathLabel.text = t;
+ public function updateDesc(?customText:String) {
+ var last = tree.last();
- var idk:OptionsScreen = optionsTree.members.last();
- if (idk.members.length > 0) updateDesc(idk.members[idk.curSelected].desc);
+ descLabel.text = last.desc;
+ if (customText != null && customText.length > 0) descLabel.text += "\n" + customText;
+ else if (last.curSelected >= 0 && last.curSelected < last.length && last.members[last.curSelected] is ITreeOption)
+ descLabel.text += "\n" + cast(last.members[last.curSelected], ITreeOption).desc;
+
+ bgLabel.scale.set(FlxG.width, descLabel.y + descLabel.height + 2);
+ bgLabel.updateHitbox();
+
+ Framerate.offset.y = bgLabel.height + 2;
}
- public function updateDesc(moreTxt:String = '') {
- pathDesc.text = optionsTree.members.last().desc;
- if (moreTxt != null && moreTxt.length > 0) pathDesc.text += '\n' + moreTxt;
- pathBG.scale.set(FlxG.width, pathDesc.y + pathDesc.height + 2);
- pathBG.updateHitbox();
+ public function addMenu(menu:TreeMenuScreen):TreeMenuScreen {
+ if (menu == null) return null;
+ if (tree.indexOf(menu) != -1) return menu;
+
+ tree.push(menu);
+ if (!__treeCreated) return menu;
+
+ menu.parent = this;
+ treeLength++;
+
+ var prev = tree[treeLength - 2];
+ menu.x = prev == null ? 0 : prev.x + Math.max(FlxG.width, prev.width);
+
+ destroyPreviousMenus();
+ menuChanged();
+ menu.inputEnabled = true;
+ if (prev != null) prev.inputEnabled = false;
+
+ return menu;
}
- public function exit() {
- FlxG.switchState((lastState != null) ? Type.createInstance(lastState, []) : new MainMenuState());
- lastState = null;
+ public function insertMenu(position:Int, menu:TreeMenuScreen):TreeMenuScreen {
+ if (menu == null) return null;
+ if (tree.indexOf(menu) != -1) return menu;
+
+ if (position < 0) position = treeLength - ((-position - 1) % treeLength);
+
+ tree.insert(position, menu);
+ if (!__treeCreated) return menu;
+
+ menu.parent = this;
+
+ var lastChanged = position >= treeLength++;
+ updateMenuPositions(position, !lastChanged);
+
+ if (lastChanged) {
+ destroyPreviousMenus();
+ menuChanged();
+ menu.inputEnabled = true;
+ if (treeLength > 0) tree[treeLength - 2].inputEnabled = false;
+ }
+
+ return menu;
}
- var menuChangeTween:FlxTween;
- public override function update(elapsed:Float) {
+ public function popMenu():TreeMenuScreen
+ return removeMenuPosition(treeLength - 1);
+
+ public function removeMenu(menu:TreeMenuScreen):TreeMenuScreen
+ return if (menu == null) null; else removeMenuPosition(tree.indexOf(menu));
+
+ public function removeMenuPosition(position:Int):TreeMenuScreen {
+ if (position < 0 || position >= treeLength || treeLength == 0) return null;
+
+ tree[position] = tree[tree.length - 1];
+ var menu = tree.pop();
+ menu.parent = null;
+
+ if (!__treeCreated) return menu;
+
+ previousMenus.push(menu);
+ if (position == --treeLength) {
+ menuChanged();
+ menu.inputEnabled = false;
+ if (treeLength > 0) tree[treeLength - 1].inputEnabled = true;
+ }
+ else {
+ updateMenuPositions(position, true);
+ updateLabels();
+ }
+
+ onMenuClosed.dispatch(menu);
+ return menu;
+ }
+
+ public function menuChanged() {
+ if (treeLength == 0) exit();
+ else {
+ updateLabels();
+
+ if (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel();
+ menuChangeTween = FlxTween.tween(FlxG.camera.scroll, {x: tree.last().x}, 1.5, {ease: menuTransitionEase, onComplete: (t) -> {
+ for (menu in tree) if (menu != null) menu.transitioning = false;
+ for (menu in previousMenus) if (menu != null) menu.transitioning = false;
+ }});
+
+ for (menu in tree) if (menu != null) menu.transitioning = true;
+ }
+
+ for (menu in previousMenus) if (menu != null) menu.transitioning = true;
+ }
+
+ override function update(elapsed:Float) {
super.update(elapsed);
- Framerate.offset.y = pathBG.height;
- // in case path gets so long it goes offscreen
- pathLabel.x = lerp(pathLabel.x, Math.max(0, FlxG.width - 4 - pathLabel.width), 0.125);
+ var i = 0, menu:TreeMenuScreen;
+ while (i < treeLength) {
+ if ((menu = tree[i++]) == null || !menu.active || !menu.exists) continue;
+ if (i == treeLength || menu.persistentUpdate || menu.transitioning) menu.update(elapsed);
+ }
+
+ i = previousMenus.length;
+ while (i-- > 0) {
+ if ((menu = previousMenus[i]).transitioning) menu.update(elapsed);
+ else {
+ menu.destroy();
+ FlxArrayUtil.swapAndPop(previousMenus, i);
+ }
+ }
+
+ // in case path gets so long it goes offscreen, ALTHOUGH this nevers happens anyway since we have set a expected width to the label.
+ //titleLabel.x = lerp(titleLabel.x, Math.max(0, FlxG.width - 4 - titleLabel.width), 0.125);
}
- public override function onResize(width:Int, height:Int) {
+ override function destroy() {
+ super.destroy();
+ destroyPreviousMenus();
+ }
+
+ override function onResize(width:Int, height:Int) {
super.onResize(width, height);
if (!UIState.resolutionAware) return;
if (width < FlxG.initialWidth || height < FlxG.initialHeight) {
- width = FlxG.initialWidth; height = FlxG.initialHeight;
+ width = FlxG.initialWidth;
+ height = FlxG.initialHeight;
}
- screenScroll = screenScroll; // Updating the cam position - Nex
-
- if (pathDesc != null && pathLabel != null)
- pathDesc.width = pathLabel.width = width - 8;
+ updateMenuPositions(true);
+ descLabel.width = titleLabel.width = (bgLabel.scale.x = width) - 8;
+ bgLabel.updateHitbox();
}
- public static inline function menuTransitionEase(e:Float)
- return FlxEase.quintInOut(FlxEase.cubeOut(e));
+ public function reloadStrings() {
+ for (menu in tree) if (menu != null) menu.reloadStrings();
+ updateLabels();
+ }
+
+ public function destroyPreviousMenus() {
+ for (menu in previousMenus) menu.destroy();
+ previousMenus.resize(0);
+ }
+
+ public function exit() {
+ if (exitCallback != null) return exitCallback(this);
+
+ FlxG.switchState(new funkin.menus.MainMenuState());
+ }
+
+ public function updateAll(elapsed:Float) {
+ for (menu in tree) if (menu != null && menu.active && menu.exists) menu.update(elapsed);
+ }
+
+ public dynamic function menuTransitionEase(e:Float) return FlxEase.quintInOut(FlxEase.cubeOut(e));
}
-typedef OptionCategory = {
- var name:String;
- var desc:String;
- var state:OneOfThree, (name:String, desc:String) -> OptionsScreen>;
- var ?substate:OneOfThree, (name:String, desc:String) -> MusicBeatSubstate>;
- var ?suffix:String;
-}
+final class TreeMenuDrawer extends FlxBasic {
+ public var parent:TreeMenu;
+ public function new(parent:TreeMenu) {
+ super();
+ this.parent = parent;
+ }
-typedef OptionTypeDef = {
- var type:Class;
- var args:Array;
-}
+ override function draw() {
+ var i = 0, menu:TreeMenuScreen;
+ while (i < parent.treeLength) {
+ if ((menu = parent.tree[i++]) == null || !menu.active || !menu.exists) continue;
+ if (i == parent.treeLength || menu.persistentUpdate || menu.transitioning) menu.draw();
+ }
+
+ i = parent.previousMenus.length;
+ while (i-- > 0) if (parent.previousMenus[i] != null) parent.previousMenus[i].draw();
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/options/TreeMenuScreen.hx b/source/funkin/options/TreeMenuScreen.hx
new file mode 100644
index 00000000..33883fc0
--- /dev/null
+++ b/source/funkin/options/TreeMenuScreen.hx
@@ -0,0 +1,192 @@
+package funkin.options;
+
+import flixel.group.FlxSpriteGroup;
+import flixel.util.FlxSignal;
+import funkin.backend.system.Controls;
+import funkin.backend.TurboControls;
+import funkin.options.TreeMenu.ITreeOption;
+import funkin.options.TreeMenu.ITreeFloatOption;
+import funkin.options.type.OptionType;
+import funkin.options.type.Separator;
+
+class TreeMenuScreen extends FlxSpriteGroup {
+ public var persistentUpdate:Bool = false;
+ public var persistentDraw:Bool = false;
+
+ public var onClose:FlxSignal = new FlxSignal();
+
+ public var parent:TreeMenu;
+ public var transitioning:Bool = false;
+ public var inputEnabled:Bool = false;
+ public var curSelected:Int = 0;
+
+ public var name:String;
+ public var desc:String;
+ /**
+ * The prefix to add to the translations ids.
+ **/
+ public var prefix:String = "";
+
+ private var rawName(default, set):String;
+ private var rawDesc(default, set):String;
+
+ function set_rawName(v:String) {
+ rawName = v;
+ name = TU.exists(v) ? TU.translate(v) : v;
+ return v;
+ }
+
+ function set_rawDesc(v:String) {
+ rawDesc = v;
+ desc = TU.exists(v) ? TU.translate(v) : v;
+ return v;
+ }
+
+ public inline function getNameID(name):String return prefix + name + "-name";
+ public inline function getDescID(name):String return prefix + name + "-desc";
+ public inline function getID(name):String return prefix + name;
+ public function translate(name:String, ?args:Array):String return TU.translate(getID(name), args);
+
+ public var controls(get, never):Controls;
+ inline function get_controls():Controls return PlayerSettings.solo.controls;
+
+ var leftTurboControl:TurboControls = new TurboControls([Control.LEFT], 0.2, 1 / 48);
+ var rightTurboControl:TurboControls = new TurboControls([Control.RIGHT], 0.2, 1 / 48);
+ var upTurboControl:TurboControls = new TurboControls([Control.UP]);
+ var downTurboControl:TurboControls = new TurboControls([Control.DOWN]);
+ var turboBasics:Array;
+
+ var curOption:ITreeOption;
+ var curFloatOption:ITreeFloatOption;
+ var __firstFrame:Bool = true;
+
+ public function new(name:String, desc:String, ?prefix:String, ?objects:Array) {
+ super();
+ this.prefix = prefix;
+ rawName = name;
+ rawDesc = desc;
+
+ turboBasics = [leftTurboControl, rightTurboControl, upTurboControl, downTurboControl];
+
+ if (objects != null) for (object in objects) add(object);
+ }
+
+ public function reloadStrings() {
+ rawName = rawName;
+ rawDesc = rawDesc;
+
+ for (object in members) if (object != null && object is OptionType) cast(object, OptionType).reloadStrings();
+ }
+
+ override function update(elapsed:Float) {
+ super.update(elapsed);
+
+ if (__firstFrame) {
+ __firstFrame = false;
+ if (members[curSelected] is ITreeOption) {
+ (curOption = cast members[curSelected]).selected = true;
+ if (curOption is ITreeFloatOption) curFloatOption = cast curOption;
+ }
+ updateItems(true);
+ return;
+ }
+
+ if (inputEnabled) {
+ for (basic in turboBasics) basic.update(elapsed);
+
+ var change = (upTurboControl.activated ? -1 : 0) + (downTurboControl.activated ? 1 : 0) - FlxG.mouse.wheel, mouseControl = false;
+ if (FlxG.mouse.justPressed) {
+ for (i in CoolUtil.maxInt(curSelected - 3, 0)...CoolUtil.minInt(curSelected + 4, length))
+ if (i != curSelected && members[i] != null && mouseOverlaps(members[i])) {
+ change = i - curSelected;
+ mouseControl = true;
+ break;
+ }
+ }
+ changeSelection(change);
+
+ if (length > 0 && curOption != null) {
+ if (controls.ACCEPT || (!mouseControl && FlxG.mouse.justPressed && mouseOverlaps(members[curSelected]))) curOption.select();
+ if (curFloatOption != null) {
+ if (controls.LEFT) curFloatOption.changeValue(-elapsed);
+ if (controls.RIGHT) curFloatOption.changeValue(elapsed);
+ }
+ else {
+ if (leftTurboControl.activated) curOption.changeSelection(-1);
+ if (rightTurboControl.activated) curOption.changeSelection(1);
+ }
+ }
+
+ if (controls.BACK || (FlxG.mouse.justPressedRight && Main.timeSinceFocus > 0.3)) close();
+ }
+
+ updateItems();
+ }
+
+ public function updateItems(force = false) {
+ var r = force ? 1 : 0.25, initY = FlxG.height * 0.5;
+ var i = curSelected, y = initY, object:FlxSprite = null, itemHeight:Float = 0;
+
+ inline function updateItem() {
+ object.y = CoolUtil.fpsLerp(object.y, y - (itemHeight = object.height) * 0.5, r);
+ object.x = x + 100 - Math.pow(Math.abs((object.y - (FlxG.height - itemHeight) * 0.5) / itemHeight / FlxG.height * FlxG.initialHeight), 1.6) * 15;
+ }
+
+ while (i < length) if ((object = members[i++]) != null) {
+ updateItem();
+ y += itemHeight;
+ }
+
+ y = initY;
+ i = curSelected;
+ while (i-- > 0) if ((object = members[i]) != null) {
+ y -= itemHeight;
+ updateItem();
+ }
+ }
+
+ public function close() {
+ onClose.dispatch();
+
+ if (curOption != null) curOption.selected = false;
+
+ if (parent == null) return destroy();
+ else parent.removeMenu(this);
+
+ CoolUtil.playMenuSFX(CANCEL).persist = true;
+ }
+
+ public function changeSelection(change:Int, force:Bool = false) {
+ if (length == 0 || (change == 0 && !force)) return;
+
+ var prevSelect = curSelected = FlxMath.wrap(curSelected + change, 0, members.length - 1);
+ while (members[curSelected] is Separator)
+ if ((curSelected = FlxMath.wrap(curSelected + (change > 0 ? 1 : -1), 0, members.length - 1)) == prevSelect) break;
+
+ if (curOption != null) curOption.selected = false;
+ if (members[curSelected] is ITreeOption) {
+ (curOption = cast members[curSelected]).selected = true;
+ if (curOption is ITreeFloatOption) curFloatOption = cast curOption;
+ else curFloatOption = null;
+ }
+ else {
+ curOption = null;
+ curFloatOption = null;
+ }
+ updateMenuDesc();
+
+ CoolUtil.playMenuSFX(SCROLL);
+ }
+
+ public function updateMenuDesc(?customTxt:String) {
+ if (parent != null) parent.updateDesc(customTxt);
+ }
+
+ function mouseOverlaps(sprite:FlxSprite):Bool
+ return sprite.overlapsPoint(FlxG.mouse.getPosition(@:privateAccess flixel.input.FlxPointer._cachedPoint), true);
+
+ override function destroy() {
+ super.destroy();
+ for (basic in turboBasics) basic.destroy();
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/options/categories/AppearanceOptions.hx b/source/funkin/options/categories/AppearanceOptions.hx
index fd364bdd..0f55e496 100644
--- a/source/funkin/options/categories/AppearanceOptions.hx
+++ b/source/funkin/options/categories/AppearanceOptions.hx
@@ -1,55 +1,63 @@
package funkin.options.categories;
-class AppearanceOptions extends OptionsScreen {
- public override function new(title:String, desc:String) {
- super(title, desc, "AppearanceOptions.");
- add(new NumOption(
- getName("framerate"),
- getDesc("framerate"),
- 30, // minimum
- 240, // maximum
- 10, // change
- "framerate", // save name or smth
- __changeFPS)); // callback
- add(new Checkbox(
- getName("antialiasing"),
- getDesc("antialiasing"),
- "antialiasing"));
- add(new Checkbox(
- getName("colorHealthBar"),
- getDesc("colorHealthBar"),
- "colorHealthBar"));
- add(new Checkbox(
- getName("week6PixelPerfect"),
- getDesc("week6PixelPerfect"),
- "week6PixelPerfect"));
- add(new Checkbox(
- getName("gameplayShaders"),
- getDesc("gameplayShaders"),
- "gameplayShaders"));
- add(new Checkbox(
- getName("flashingMenu"),
- getDesc("flashingMenu"),
- "flashingMenu"));
- add(new Checkbox(
- getName("lowMemoryMode"),
- getDesc("lowMemoryMode"),
- "lowMemoryMode"));
- #if sys
- if (!Main.forceGPUOnlyBitmapsOff) {
- add(new Checkbox(
- getName("gpuOnlyBitmaps"),
- getDesc("gpuOnlyBitmaps"),
- "gpuOnlyBitmaps"));
- }
- #end
+class AppearanceOptions extends TreeMenuScreen {
+ public function new() {
+ super('optionsTree.appearance-name', 'optionsTree.appearance-desc', 'AppearanceOptions.');
+
+ add(new NumOption(getNameID('framerate'), getDescID('framerate'),
+ 30, 240, 1,
+ 'framerate', __changeFPS
+ ));
+ add(new Checkbox(getNameID('flashingMenu'), getDescID('flashingMenu'), 'flashingMenu'));
+ add(new Checkbox(getNameID('colorHealthBar'), getDescID('colorHealthBar'), 'colorHealthBar'));
+ add(new Checkbox(getNameID('week6PixelPerfect'), getDescID('week6PixelPerfect'), 'week6PixelPerfect'));
+
+ add(new Separator());
+ add(new TextOption('optionsMenu.advanced', 'optionsTree.appearance.advanced-desc', ' >', () ->
+ parent.addMenu(new AdvancedAppearanceOptions())));
}
- private function __changeFPS(change:Float) {
- // if statement cause of the flixel warning
- if(FlxG.updateFramerate < Std.int(change))
- FlxG.drawFramerate = FlxG.updateFramerate = Std.int(change);
- else
- FlxG.updateFramerate = FlxG.drawFramerate = Std.int(change);
+ private function __changeFPS(value:Float) {
+ var framerate = Math.floor(value);
+ if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
+ else FlxG.updateFramerate = FlxG.drawFramerate = framerate;
+ }
+}
+
+class AdvancedAppearanceOptions extends TreeMenuScreen {
+ var qualityOptions:Array = [];
+
+ public function new() {
+ super('optionsMenu.advanced', 'optionsTree.appearance.advanced-desc', 'AppearanceOptions.Advanced.');
+
+ add(new ArrayOption(getNameID('quality'), getDescID('quality'),
+ [0, 1, 2], [getID('quality-low'), getID('quality-high'), getID('quality-custom')],
+ 'quality', __changeQuality, null
+ ));
+
+ for (option in (qualityOptions = [
+ new Checkbox(getNameID('antialiasing'), getDescID('antialiasing'), 'antialiasing', __changeAntialiasing),
+ new Checkbox(getNameID('lowMemoryMode'), getDescID('lowMemoryMode'), 'lowMemoryMode'),
+ new Checkbox(getNameID('gameplayShaders'), getDescID('gameplayShaders'), 'gameplayShaders')
+ ]))
+ add(option);
+
+ add(new Checkbox(getNameID('gpuOnlyBitmaps'), getDescID('gpuOnlyBitmaps'), 'gpuOnlyBitmaps'));
+
+ updateQualityOptions();
+ }
+
+ private function updateQualityOptions() {
+ for (option in qualityOptions) option.locked = Options.quality != 2;
+ }
+
+ private function __changeQuality(value:Dynamic) {
+ var antialiasing = value == 0 ? false : (value == 1 ? true : Options.antialiasing);
+ FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? BEST : LOW;
+ updateQualityOptions();
+ }
+
+ private function __changeAntialiasing() {
+ FlxG.game.stage.quality = (FlxG.enableAntialiasing = Options.antialiasing) ? BEST : LOW;
}
}
\ No newline at end of file
diff --git a/source/funkin/options/categories/DebugOptions.hx b/source/funkin/options/categories/DebugOptions.hx
new file mode 100644
index 00000000..b0ff8e71
--- /dev/null
+++ b/source/funkin/options/categories/DebugOptions.hx
@@ -0,0 +1,22 @@
+package funkin.options.categories;
+
+class DebugOptions extends TreeMenuScreen {
+ public function new() {
+ super('DebugOptions.title', 'DebugOptions.desc', 'DebugOptions.');
+
+ #if windows
+ add(new TextOption(getNameID("showConsole"), getDescID("showConsole"), () -> funkin.backend.utils.NativeAPI.allocConsole()));
+ #end
+ add(new Checkbox(getNameID("editorsResizable"), getDescID("editorsResizable"), "editorsResizable"));
+ add(new Checkbox(getNameID("bypassEditorsResize"), getDescID("bypassEditorsResize"), "bypassEditorsResize"));
+ add(new Checkbox(getNameID("editorSFX"), getDescID("editorSFX"), "editorSFX"));
+ add(new Checkbox(getNameID("editorCharterPrettyPrint"), getDescID("editorCharterPrettyPrint"), "editorCharterPrettyPrint"));
+ add(new Checkbox(getNameID("editorCharacterPrettyPrint"), getDescID("editorCharacterPrettyPrint"), "editorCharacterPrettyPrint"));
+ add(new Checkbox(getNameID("editorStagePrettyPrint"), getDescID("editorStagePrettyPrint"), "editorStagePrettyPrint"));
+ add(new Checkbox(getNameID("intensiveBlur"), getDescID("intensiveBlur"), "intensiveBlur"));
+ add(new Checkbox(getNameID("charterAutoSaves"), getDescID("charterAutoSaves"), "charterAutoSaves"));
+ add(new NumOption(getNameID("charterAutoSaveTime"), getDescID("charterAutoSaveTime"), 60, 60*10, 1, "charterAutoSaveTime"));
+ add(new NumOption(getNameID("charterAutoSaveWarningTime"), getDescID("charterAutoSaveWarningTime"), 0, 15, 1, "charterAutoSaveWarningTime"));
+ add(new Checkbox(getNameID("charterAutoSavesSeparateFolder"), getDescID("charterAutoSavesSeparateFolder"), "charterAutoSavesSeparateFolder"));
+ }
+}
\ No newline at end of file
diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx
index 4e1f8a7e..7d66f351 100644
--- a/source/funkin/options/categories/GameplayOptions.hx
+++ b/source/funkin/options/categories/GameplayOptions.hx
@@ -3,64 +3,39 @@ package funkin.options.categories;
import flixel.util.FlxTimer;
import funkin.backend.system.Conductor;
-class GameplayOptions extends OptionsScreen {
+class GameplayOptions extends TreeMenuScreen {
var __metronome = FlxG.sound.load(Paths.sound('editors/charter/metronome'));
-
var offsetSetting:NumOption;
- public override function new(title:String, desc:String) {
- super(title, desc, "GameplayOptions.");
- add(new Checkbox(
- getName("downscroll"),
- getDesc("downscroll"),
- "downscroll"));
- add(new Checkbox(
- getName("ghostTapping"),
- getDesc("ghostTapping"),
- "ghostTapping"));
- add(new Checkbox(
- getName("naughtyness"),
- getDesc("naughtyness"),
- "naughtyness"));
- add(new Checkbox(
- getName("camZoomOnBeat"),
- getDesc("camZoomOnBeat"),
- "camZoomOnBeat"));
- add(new Checkbox(
- getName("autoPause"),
- getDesc("autoPause"),
- "autoPause"));
- add(new MeterOption(
- getName("volumeSFX"),
- getDesc("volumeSFX"),
- 0, // minimum
- 1, // maximum
- 0.1, // change
- "volumeSFX"));
- add(offsetSetting = new NumOption(
- getName("songOffset"),
- getDesc("songOffset"),
- -999, // minimum
- 999, // maximum
- 1, // change
- "songOffset", // save name or smth
- __changeOffset)); // callback
+ public function new() {
+ super('optionsTree.gameplay-name', 'optionsTree.gameplay-desc', 'GameplayOptions.');
+
+ add(new Checkbox(getNameID('downscroll'), getDescID('downscroll'), 'downscroll'));
+ add(new Checkbox(getNameID('ghostTapping'), getDescID('ghostTapping'), 'ghostTapping'));
+ add(new Checkbox(getNameID('naughtyness'), getDescID('naughtyness'), 'naughtyness'));
+ add(new Checkbox(getNameID('camZoomOnBeat'), getDescID('camZoomOnBeat'), 'camZoomOnBeat'));
+ add(new Checkbox(getNameID('autoPause'), getDescID('autoPause'), 'autoPause', __changeAutoPause));
+ add(offsetSetting = new NumOption(getNameID('songOffset'), getDescID('songOffset'), -999, 999, 1, 'songOffset', __changeOffset));
+ add(new SliderOption(getNameID('volumeMusic'), getDescID('volumeMusic'), 0, 1, 1, 5, 'volumeMusic', -1, __changeVolumeMusic));
+ add(new SliderOption(getNameID('volumeSFX'), getDescID('volumeSFX'), 0, 1, 1, 5, 'volumeSFX'));
+
+ add(new Separator());
add(new TextOption('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', ' >', () ->
- parent.add(new AdvancedGameplayOptions('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc'))));
+ parent.addMenu(new AdvancedGameplayOptions())));
}
- private function __changeOffset(offset)
- Conductor.songOffset = offset;
+ private function __changeAutoPause() FlxG.autoPause = Options.autoPause;
+ private function __changeOffset(offset:Float) Conductor.songOffset = offset;
+ private function __changeVolumeMusic(value:Float) FlxG.sound.defaultMusicGroup.volume = value;
var __lastBeat:Int = 0;
var __lastSongBeat:Int = 0;
- override function update(elapsed) {
+ override function update(elapsed:Float) {
super.update(elapsed);
- FlxG.camera.zoom = CoolUtil.fpsLerp(FlxG.camera.zoom, 1, 0.04);
+ FlxG.camera.zoom = CoolUtil.fpsLerp(FlxG.camera.zoom, 1, 0.1);
if (offsetSetting.selected) {
- FlxG.sound.music.volume = 0.5;
if (__lastBeat != Conductor.curBeat) {
FlxG.camera.zoom += 0.03;
__lastBeat = Conductor.curBeat;
@@ -72,27 +47,27 @@ class GameplayOptions extends OptionsScreen {
__lastSongBeat = beat;
}
}
- else
- FlxG.sound.music.volume = 1;
+ }
+
+ override function changeSelection(change:Int, force:Bool = false) {
+ super.changeSelection(change, force);
+ if (offsetSetting.selected) FlxG.sound.music.volume = 0.5;
+ else FlxG.sound.music.volume = 1;
}
override function close() {
+ super.close();
+
FlxG.camera.zoom = 1;
FlxG.sound.music.volume = 1;
- super.close();
}
}
-class AdvancedGameplayOptions extends OptionsScreen {
- public override function new(title:String, desc:String) {
- super(title, desc, "GameplayOptions.Advanced.");
- add(new Checkbox(
- getName("streamedMusic"),
- getDesc("streamedMusic"),
- "streamedMusic"));
- add(new Checkbox(
- getName("streamedVocals"),
- getDesc("streamedVocals"),
- "streamedVocals"));
+class AdvancedGameplayOptions extends TreeMenuScreen {
+ public function new() {
+ super('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', 'GameplayOptions.Advanced.');
+
+ add(new Checkbox(getNameID('streamedMusic'), getDescID('streamedMusic'), 'streamedMusic'));
+ add(new Checkbox(getNameID('streamedVocals'), getDescID('streamedVocals'), 'streamedVocals'));
}
-}
+}
\ No newline at end of file
diff --git a/source/funkin/options/categories/LanguageOptions.hx b/source/funkin/options/categories/LanguageOptions.hx
index b3e64433..878e6741 100644
--- a/source/funkin/options/categories/LanguageOptions.hx
+++ b/source/funkin/options/categories/LanguageOptions.hx
@@ -3,17 +3,13 @@ package funkin.options.categories;
class LanguageRadio extends RadioButton {
public var langID:String;
- public function new(name:String, langID:String) {
+ public function new(screen:TreeMenuScreen, name:String, langID:String) {
+ super(screen, name, "LanguageOptions.language-desc", null, langID, null, "languageSelector");
this.langID = langID;
- super(name, "LanguageOptions.language-desc", null, langID, null, "languageSelector");
checked = langID == TU.curLanguage;
}
- override function reloadStrings() {
- super.reloadStrings();
- }
-
override function set_rawDesc(v:String) {
rawDesc = v;
var config = TU.getConfig(langID);
@@ -23,28 +19,25 @@ class LanguageRadio extends RadioButton {
return v;
}
- override dynamic function onSet(value:Dynamic) {
+ override function select() {
+ var prev = checked;
+ super.select();
+ if (prev == checked) return;
+
TranslationUtil.setLanguage(value);
- if(FlxG.state is OptionsMenu) {
- var menu:OptionsMenu = cast FlxG.state;
- menu.reloadStrings();
- trace("Reloading strings");
- }
+ if (screen.parent == null) screen.reloadStrings();
+ else screen.parent.reloadStrings();
}
}
-class LanguageOptions extends OptionsScreen {
- public override function new(title:String, desc:String) {
- super(title, desc, "LanguageOptions.");
+class LanguageOptions extends TreeMenuScreen {
+ public override function new() {
+ super('optionsTree.language-name', 'optionsTree.language-desc', 'LanguageOptions.');
- var langArray:Array = TranslationUtil.foundLanguages;
-
- for(lang in langArray) {
+ for (lang in TranslationUtil.foundLanguages) {
var split = lang.split("/");
- var langId = split.first();
- var langName = split.last();
- var radio = new LanguageRadio(langName, langId);
- add(radio);
+ var langId = split.first(), langName = split.last();
+ add(new LanguageRadio(this, langName, langId));
}
}
-}
+}
\ No newline at end of file
diff --git a/source/funkin/options/categories/MiscOptions.hx b/source/funkin/options/categories/MiscOptions.hx
index 95b55aef..e72bafc8 100644
--- a/source/funkin/options/categories/MiscOptions.hx
+++ b/source/funkin/options/categories/MiscOptions.hx
@@ -1,35 +1,26 @@
package funkin.options.categories;
-class MiscOptions extends OptionsScreen {
- public override function new(title:String, desc:String) {
- super(title, desc, "MiscOptions.");
- add(new Checkbox(
- getName("devMode"),
- getDesc("devMode"),
- "devMode"));
+class MiscOptions extends TreeMenuScreen {
+ public function new() {
+ super('optionsTree.miscellaneous-name', 'optionsTree.miscellaneous-desc', 'MiscOptions.');
+
+ add(new Checkbox(getNameID('devMode'), getDescID('devMode'), 'devMode'));
+ add(new Checkbox(getNameID('allowConfigWarning'), getDescID('allowConfigWarning'), 'allowConfigWarning'));
#if UPDATE_CHECKING
- add(new Checkbox(
- getName("betaUpdates"),
- getDesc("betaUpdates"),
- "betaUpdates"));
- add(new TextOption(
- getName("checkForUpdates"),
- getDesc("checkForUpdates"),
- function() {
- var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates();
- if (report.newUpdate) {
- FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
- } else {
- CoolUtil.playMenuSFX(CANCEL);
- updateDescText(TU.translate(prefix + "checkForUpdates-noUpdateFound"));
- }
+ add(new Checkbox(getNameID('betaUpdates'), getDescID('betaUpdates'), 'betaUpdates'));
+ add(new TextOption(getNameID('checkForUpdates'), getDescID('checkForUpdates'), () -> {
+ var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates();
+ if (report.newUpdate) FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
+ else {
+ CoolUtil.playMenuSFX(CANCEL);
+ updateDescText(translate('checkForUpdates-noUpdateFound'));
+ }
}));
#end
- add(new TextOption(
- getName("resetSaveData"),
- getDesc("resetSaveData"),
- function() {
- // TODO: SAVE DATA RESETTING
+
+ add(new Separator());
+ add(new TextOption(getNameID('resetSaveData'), getDescID('resetSaveData'), () -> {
+ // TODO: SAVE DATA RESETTING
}));
}
-}
+}
\ No newline at end of file
diff --git a/source/funkin/options/categories/import.hx b/source/funkin/options/categories/import.hx
index 49bf382e..cb4b5c3a 100644
--- a/source/funkin/options/categories/import.hx
+++ b/source/funkin/options/categories/import.hx
@@ -1,5 +1,7 @@
#if !macro
package funkin.options.categories;
+import funkin.options.TreeMenu;
+import funkin.options.TreeMenuScreen;
import funkin.options.type.*;
#end
\ No newline at end of file
diff --git a/source/funkin/options/type/ArrayOption.hx b/source/funkin/options/type/ArrayOption.hx
index 885d0335..52c33904 100644
--- a/source/funkin/options/type/ArrayOption.hx
+++ b/source/funkin/options/type/ArrayOption.hx
@@ -1,88 +1,64 @@
package funkin.options.type;
-/**
- * Option type that allows you to select an array of options.
-**/
-class ArrayOption extends OptionType {
- public var selectCallback:String->Void;
-
- private var __text:Alphabet;
- private var __selectiontext:Alphabet;
+class ArrayOption extends TextOption {
+ public var changedCallback:String->Void;
public var options:Array;
public var displayOptions:Array;
-
public var currentSelection:Int;
- var optionName:String;
-
public var parent:Dynamic;
+ public var optionName:String;
- private var rawText(default, set):String;
+ var __selectionText:Alphabet;
- public var text(get, set):String;
- private function get_text() {return __text.text;}
- private function set_text(v:String) {return __text.text = v;}
-
- public function new(text:String, desc:String, options:Array, displayOptions:Array, optionName:String, ?selectCallback:String->Void = null, ?parent:Dynamic) {
- super(desc);
- this.selectCallback = selectCallback;
- this.displayOptions = displayOptions;
- this.options = options;
- if (parent == null)
- parent = Options;
-
- this.parent = parent;
-
- var fieldValue = Reflect.field(parent, optionName);
- if(fieldValue != null)
- this.currentSelection = CoolUtil.maxInt(0, options.indexOf(fieldValue));
-
- this.optionName = optionName;
-
- add(__text = new Alphabet(100, 20, text, "bold"));
- add(__selectiontext = new Alphabet(__text.width + 120, 20, formatTextOption(), "normal"));
-
- rawText = text;
- }
-
- override function reloadStrings() {
- super.reloadStrings();
- this.rawText = rawText;
- }
-
- function set_rawText(v:String) {
- rawText = v;
- __text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
+ override function set_text(v:String) {
+ super.set_text(v);
+ __selectionText.x = __text.x + __text.width + 12;
return v;
}
- public override function draw() {
- super.draw();
+ public function new(text:String, desc:String, options:Array, displayOptions:Array, ?optionName:String, ?changedCallback:Dynamic->Void = null, ?parent:Dynamic) {
+ this.changedCallback = changedCallback;
+ this.displayOptions = displayOptions;
+ this.options = options;
+ this.optionName = optionName;
+ this.parent = parent = parent != null ? parent : Options;
+
+ var fieldValue = Reflect.field(parent, optionName);
+ if (fieldValue != null) currentSelection = CoolUtil.maxInt(0, options.indexOf(fieldValue));
+
+ __selectionText = new Alphabet(0, 20, formatTextOption(), 'bold');
+ super(text, desc);
+ add(__selectionText);
}
- public override function onChangeSelection(change:Float):Void
- {
- if(currentSelection <= 0 && change == -1 || currentSelection >= options.length - 1 && change == 1) return;
- currentSelection += Math.round(change);
- __selectiontext.text = formatTextOption();
- Reflect.setField(parent, optionName, options[currentSelection]);
- if(selectCallback != null)
- selectCallback(options[currentSelection]);
+ override function reloadStrings() {
+ __selectionText.text = formatTextOption();
+ super.reloadStrings();
}
- private function formatTextOption() {
- var currentOptionString = ": ";
- if((currentSelection > 0))
- currentOptionString += "< ";
- else
- currentOptionString += " ";
+ function formatTextOption() {
+ var s = ": ";
- currentOptionString += displayOptions[currentSelection];
+ if (currentSelection > 0) s += "< ";
+ else s += " ";
- if(!(currentSelection >= options.length - 1))
- currentOptionString += " >";
+ s += TU.exists(displayOptions[currentSelection]) ? TU.translate(displayOptions[currentSelection]) : displayOptions[currentSelection];
- return currentOptionString;
+ if (currentSelection < options.length - 1) s += " >";
+
+ return s;
}
+
+ override function changeSelection(change:Int) {
+ if (locked || currentSelection == (currentSelection = CoolUtil.boundInt(currentSelection + change, 0, options.length - 1))) return;
+ __selectionText.text = formatTextOption();
+ CoolUtil.playMenuSFX(SCROLL);
+
+ if (optionName != null) Reflect.setField(parent, optionName, options[currentSelection]);
+ if (changedCallback != null) changedCallback(options[currentSelection]);
+ }
+
+ override function select() {}
}
\ No newline at end of file
diff --git a/source/funkin/options/type/Checkbox.hx b/source/funkin/options/type/Checkbox.hx
index 77947bd7..2e333420 100644
--- a/source/funkin/options/type/Checkbox.hx
+++ b/source/funkin/options/type/Checkbox.hx
@@ -10,25 +10,26 @@ class Checkbox extends TextOption {
public var checked(default, set):Bool;
public var parent:Dynamic;
-
public var optionName:String;
private var offsets:Map = [
"unchecked" => FlxPoint.get(0, -70),
- "checked" => FlxPoint.get(23, -30),
+ "checked" => FlxPoint.get(23, -32),
"unchecking" => FlxPoint.get(25, -12),
- "checking" => FlxPoint.get(35, 31)
+ "checking" => FlxPoint.get(35, 29)
];
- public function new(text:String, desc:String, optionName:String, ?parent:Dynamic) {
- super(text, desc, null);
+ override function set_text(v:String) {
+ super.set_text(v);
+ checkbox.x = __text.x + __text.width + 26;
+ return v;
+ }
- if (parent == null)
- parent = Options;
+ public function new(text:String, desc:String, optionName:String, ?selectCallback:Void->Void, ?parent:Dynamic) {
+ this.optionName = optionName;
+ this.parent = parent = parent != null ? parent : Options;
- this.parent = parent;
-
- checkbox = new FlxSprite(10, -40);
+ checkbox = new FlxSprite(10, -30);
checkbox.frames = Paths.getFrames('menus/options/checkboxThingie');
checkbox.animation.addByPrefix("unchecked", "Check Box unselected0", 24);
checkbox.animation.addByPrefix("checked", "Check Box Selected Static0", 24);
@@ -37,65 +38,59 @@ class Checkbox extends TextOption {
checkbox.antialiasing = true;
checkbox.scale.set(0.75, 0.75);
checkbox.updateHitbox();
- add(checkbox);
- this.optionName = optionName;
- if(optionName != null) {
- checked = Reflect.field(parent, optionName);
- } else {
- checked = false;
- }
+ //__text.x = 100;
+
+ if (optionName != null) checked = Reflect.field(parent, optionName);
+ else checked = false;
+
+ super(text, desc, selectCallback);
+ add(checkbox);
}
public var firstFrame:Bool = true;
- public override function update(elapsed:Float) {
- if(checkbox.animation.curAnim == null) {
- checkbox.animation.play(checked ? "checked" : "unchecked", true);
- }
+ override function update(elapsed:Float) {
+ if (checkbox.animation.curAnim == null) checkbox.animation.play(checked ? "checked" : "unchecked", true);
super.update(elapsed);
- switch(checkbox.animation.curAnim.name) {
- case "unchecking":
- if(checkbox.animation.curAnim.finished) {
- checkbox.animation.play("unchecked", true);
- }
- case "checking":
- if(checkbox.animation.curAnim.finished) {
- checkbox.animation.play("checked", true);
- }
+
+ if (checkbox.animation.curAnim.finished) switch(checkbox.animation.curAnim.name) {
+ case "unchecking": checkbox.animation.play("unchecked", true);
+ case "checking": checkbox.animation.play("checked", true);
}
firstFrame = false;
}
override function draw() {
- if(checkbox.animation.curAnim != null) {
+ if (checkbox.animation.curAnim != null) {
var offset = offsets[checkbox.animation.curAnim.name];
- if (offset != null)
- checkbox.frameOffset.set(offset.x, offset.y);
+ if (offset != null) checkbox.frameOffset.set(offset.x, offset.y);
}
super.draw();
}
function set_checked(v:Bool) {
- if(checked != v) {
- checked = v;
- if(!firstFrame) {
- checkbox.animation.play(checked ? "checking" : "unchecking", true);
- }
- }
+ if (checked == (checked = v)) return v;
+
+ if (!firstFrame) checkbox.animation.play(checked ? "checking" : "unchecking", true);
return v;
}
- public override function onSelect() {
+ override function select() {
+ if (locked) return;
+
checked = !checked;
- if(optionName != null) {
- Reflect.setField(parent, optionName, checked);
- }
+ if (optionName != null) Reflect.setField(parent, optionName, checked);
+
+ if (checked) CoolUtil.playMenuSFX(CHECKED);
+ else CoolUtil.playMenuSFX(UNCHECKED);
+
+ if (selectCallback != null) selectCallback();
}
- public override function destroy() {
+ override function destroy() {
super.destroy();
- for(e in offsets) e.put();
+ for (e in offsets) e.put();
}
}
\ No newline at end of file
diff --git a/source/funkin/options/type/EditorIconOption.hx b/source/funkin/options/type/EditorIconOption.hx
deleted file mode 100644
index 5303fb01..00000000
--- a/source/funkin/options/type/EditorIconOption.hx
+++ /dev/null
@@ -1,13 +0,0 @@
-package funkin.options.type;
-
-import flixel.util.FlxColor;
-
-class EditorIconOption extends IconOption {
- public var flashColor:FlxColor;
-
- public function new(name:String, desc:String, icon:String, callback:Void->Void, flashColor:FlxColor = 0) {
- super(name, desc, icon, callback);
-
- this.flashColor = flashColor;
- }
-}
\ No newline at end of file
diff --git a/source/funkin/options/type/GithubIconOption.hx b/source/funkin/options/type/GithubIconOption.hx
index be3567a8..16916b55 100644
--- a/source/funkin/options/type/GithubIconOption.hx
+++ b/source/funkin/options/type/GithubIconOption.hx
@@ -26,6 +26,7 @@ class GithubIconOption extends TextOption
this.icon = new GithubUserIcon(user, size, waitUntilLoad);
this.usePortrait = usePortrait;
add(icon);
+ __text.x = 100;
}
}
diff --git a/source/funkin/options/type/IconOption.hx b/source/funkin/options/type/IconOption.hx
index b61c5c67..547c9f59 100644
--- a/source/funkin/options/type/IconOption.hx
+++ b/source/funkin/options/type/IconOption.hx
@@ -12,6 +12,8 @@ class IconOption extends TextOption {
public function new(name:String, desc:String, icon:String, callback:Void->Void) {
super(name, desc, callback);
+ __text.x = 100;
+
iconSpr = new HealthIcon(icon, false);
iconSpr.setPosition(90 - iconSpr.width, (__text.height - iconSpr.height) / 2);
iconSpr.setUnstretchedGraphicSize(150, 150, true);
diff --git a/source/funkin/options/type/MeterOption.hx b/source/funkin/options/type/MeterOption.hx
index 7902e09e..831a4adc 100644
--- a/source/funkin/options/type/MeterOption.hx
+++ b/source/funkin/options/type/MeterOption.hx
@@ -20,11 +20,7 @@ class MeterOption extends OptionType {
public var barWidth(default, set):Int;
- private var rawText(default, set):String;
-
- public var text(get, set):String;
- private function get_text() {return __text.text;}
- private function set_text(v:String) {return __text.text = v;}
+ override function set_text(v:String) return __text.text = text = v;
private var __text:Alphabet;
var optionName:String;
@@ -43,7 +39,7 @@ class MeterOption extends OptionType {
public function new(text:String, desc:String, min:Float, max:Float, changeVal:Float, optionName:String, barWidth:Int = 520,
?selectCallback:Float->Void = null, ?parent:Dynamic)
{
- super(desc);
+ super(text, desc);
this.selectCallback = selectCallback;
this.min = min;
@@ -162,12 +158,6 @@ class MeterOption extends OptionType {
x += temp * barScale;
}
- function set_rawText(v:String) {
- rawText = v;
- __text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
- return v;
- }
-
override function draw() {
lastX = x;
@@ -196,7 +186,7 @@ class MeterOption extends OptionType {
this.rawText = rawText;
}
- override function onChangeSelection(change:Float) {
+ override function changeSelection(change:Float) {
if (currentSelection <= min && change == -1 || currentSelection >= max && change == 1) return;
currentSelection = FlxMath.roundDecimal(currentSelection + (change * changeVal), FlxMath.getDecimals(changeVal));
diff --git a/source/funkin/options/type/NewOption.hx b/source/funkin/options/type/NewOption.hx
index 28255b8c..85181412 100644
--- a/source/funkin/options/type/NewOption.hx
+++ b/source/funkin/options/type/NewOption.hx
@@ -10,8 +10,10 @@ class NewOption extends TextOption {
public function new(name:String, desc:String, callback:Void->Void) {
super(name, desc, callback);
+ itemHeight = 160;
__text.color = FlxColor.LIME;
+ __text.x = 100;
iconSpr = new FlxSprite().loadGraphic(Paths.image("editors/new"));
iconSpr.setPosition(90 - iconSpr.width, (__text.height - iconSpr.height) / 2);
diff --git a/source/funkin/options/type/NumOption.hx b/source/funkin/options/type/NumOption.hx
index e570e197..953294ff 100644
--- a/source/funkin/options/type/NumOption.hx
+++ b/source/funkin/options/type/NumOption.hx
@@ -1,72 +1,53 @@
package funkin.options.type;
-import flixel.effects.FlxFlicker;
-
/**
* Option type that allows stepping through a number.
**/
-class NumOption extends OptionType {
- public var selectCallback:Float->Void;
-
- private var __text:Alphabet;
- private var __number:Alphabet;
+class NumOption extends TextOption {
+ public var changedCallback:Float->Void;
public var min:Float;
public var max:Float;
+ public var step:Float;
- public var currentSelection:Float;
- public var changeVal:Float;
-
- var optionName:String;
+ public var currentValue:Float;
public var parent:Dynamic;
+ public var optionName:String;
- private var rawText(default, set):String;
+ var __number:Alphabet;
- public var text(get, set):String;
- private function get_text() {return __text.text;}
- private function set_text(v:String) {return __text.text = v;}
-
- public function new(text:String, desc:String, min:Float, max:Float, changeVal:Float, optionName:String, ?selectCallback:Float->Void = null, ?parent:Dynamic) {
- super(desc);
- this.selectCallback = selectCallback;
- this.min = min;
- this.max = max;
- if (parent == null)
- parent = Options;
-
- this.parent = parent;
-
- if(Reflect.field(parent, optionName) != null)
- this.currentSelection = Reflect.field(parent, optionName);
- this.changeVal = changeVal;
- this.optionName = optionName;
-
- add(__text = new Alphabet(20, 20, "", "bold"));
- add(__number = new Alphabet(0, 20, ': $currentSelection', "bold"));
- rawText = text;
- }
-
- override function reloadStrings() {
- super.reloadStrings();
- this.rawText = rawText;
- }
-
- function set_rawText(v:String) {
- rawText = v;
- __text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
+ override function set_text(v:String) {
+ super.set_text(v);
__number.x = __text.x + __text.width + 12;
return v;
}
- override function onChangeSelection(change:Float):Void
- {
- if(currentSelection <= min && change == -1 || currentSelection >= max && change == 1) return;
- currentSelection = FlxMath.roundDecimal(currentSelection + (change * changeVal), FlxMath.getDecimals(changeVal));
- __number.text = ': $currentSelection';
+ public function new(text:String, desc:String, min:Float, max:Float, step:Float, optionName:String, ?changedCallback:Float->Void = null, ?parent:Dynamic) {
+ this.changedCallback = changedCallback;
+ this.min = min;
+ this.max = max;
+ this.step = step;
+ this.optionName = optionName;
+ this.parent = parent = parent != null ? parent : Options;
- Reflect.setField(parent, optionName, currentSelection);
- if(selectCallback != null)
- selectCallback(currentSelection);
+ if (Reflect.field(parent, optionName) != null) currentValue = Reflect.field(parent, optionName);
+
+ __number = new Alphabet(0, 20, ': $currentValue', 'bold');
+ super(text, desc);
+ add(__number);
}
+
+ override function changeSelection(change:Int):Void {
+ if (locked) return;
+ if (currentValue == (currentValue = FlxMath.bound(currentValue + change, min, max))) return;
+ __number.text = ': $currentValue';
+
+ Reflect.setField(parent, optionName, currentValue);
+ if (changedCallback != null) changedCallback(currentValue);
+
+ CoolUtil.playMenuSFX(SCROLL);
+ }
+
+ override function select() {}
}
\ No newline at end of file
diff --git a/source/funkin/options/type/OptionType.hx b/source/funkin/options/type/OptionType.hx
index 44cd94e1..3d4d25f0 100644
--- a/source/funkin/options/type/OptionType.hx
+++ b/source/funkin/options/type/OptionType.hx
@@ -1,41 +1,64 @@
package funkin.options.type;
import flixel.group.FlxSpriteGroup;
+import flixel.util.FlxColor;
import funkin.backend.system.Controls;
+import funkin.options.TreeMenu.ITreeOption;
/**
* Base class for all option types.
* Used in OptionsMenu.
**/
-class OptionType extends FlxSpriteGroup {
- public var controls(get, never):Controls;
+class OptionType extends FlxSpriteGroup implements ITreeOption {
public var selected:Bool = false;
+ public var locked(default, set):Bool;
+
+ public var text(default, set):String;
+ public var rawText(default, set):String;
public var desc:String;
public var rawDesc(default, set):String;
- public function new(desc:String) {
+ public var itemHeight:Float = 120;
+
+ public var editorFlashColor:FlxColor = FlxColor.WHITE;
+
+ public function new(text:String, desc:String) {
super();
+ rawText = text;
rawDesc = desc;
}
+ function set_locked(v:Bool) {
+ if (locked == (locked = v)) return v;
+ color = locked ? 0xFF7F7F7F : 0xFFFFFFFF;
+ return v;
+ }
+
+ function set_text(v:String) return text = v;
+ function set_rawText(v:String) {
+ rawText = v;
+ text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
+ return v;
+ }
+
function set_rawDesc(v:String) {
rawDesc = v;
- this.desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
+ desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
return v;
}
public function reloadStrings() {
- this.rawDesc = rawDesc;
+ rawText = rawText;
+ rawDesc = rawDesc;
}
- private function get_controls() {return PlayerSettings.solo.controls;}
-
- public function onSelect() {}
-
- public function onChangeSelection(change:Float) {}
-
public override function update(elapsed:Float) {
super.update(elapsed);
alpha = (selected ? 1 : 0.6);
}
+
+ public function changeSelection(change:Int) {}
+ public function select() {}
+
+ override function get_height() return itemHeight;
}
\ No newline at end of file
diff --git a/source/funkin/options/type/PortraitOption.hx b/source/funkin/options/type/PortraitOption.hx
index a719b32d..b512a8f2 100644
--- a/source/funkin/options/type/PortraitOption.hx
+++ b/source/funkin/options/type/PortraitOption.hx
@@ -12,6 +12,8 @@ class PortraitOption extends TextOption {
public function new(name:String, desc:String, callback:Void->Void, ?graphic:FlxGraphic, size:Int = 96, usePortrait:Bool = true) {
super(name, desc, callback);
+ __text.x = 100;
+
if (graphic != null) addPortrait(graphic, size, usePortrait);
}
diff --git a/source/funkin/options/type/RadioButton.hx b/source/funkin/options/type/RadioButton.hx
index d43240ac..b576c192 100644
--- a/source/funkin/options/type/RadioButton.hx
+++ b/source/funkin/options/type/RadioButton.hx
@@ -1,17 +1,19 @@
package funkin.options.type;
import flixel.math.FlxPoint;
+import funkin.options.TreeMenuScreen;
class RadioButton extends TextOption {
public var radio:FlxSprite;
public var checked(default, set):Bool;
public var parent:Dynamic;
- public var value:Dynamic;
- public var forId:String;
-
public var optionName:String;
+ public var screen:TreeMenuScreen;
+ public var forId:String;
+ public var value:Dynamic;
+
private var offsets:Map = [
"unchecked" => FlxPoint.get(0, -65),
"checked" => FlxPoint.get(0, -65),
@@ -19,26 +21,15 @@ class RadioButton extends TextOption {
"checking" => FlxPoint.get(17, -40)
];
- public function new(text:String, desc:String, optionName:String, value:Dynamic, ?parent:Dynamic, ?forId:String, ?options:OptionsScreen) {
- super(text, desc, null);
+ public function new(?screen:TreeMenuScreen, text:String, desc:String, optionName:String, value:Dynamic, ?selectCallback:Void->Void, ?parent:Dynamic, ?forId:String) {
+ super(text, desc, selectCallback);
+ this.screen = screen;
+ this.optionName = optionName;
+ this.parent = parent = parent != null ? parent : Options;
+ this.forId = forId = forId == null ? optionName : forId;
+ this.value = value;
- if (parent == null)
- parent = Options;
-
- if(forId == null)
- forId = optionName;
-
- this.parent = parent;
- this.forId = forId;
-
- if(options != null) {
- if(FlxG.state is OptionsMenu) {
- var menu:OptionsMenu = cast FlxG.state;
- options = menu.optionsTree.members.last();
- }
- }
-
- radio = new FlxSprite(10, -40);
+ radio = new FlxSprite(10, -23);
radio.frames = Paths.getFrames('menus/options/radioCrank');
radio.animation.addByPrefix("unchecked", "Radio unselected0", 24);
radio.animation.addByPrefix("checked", "Radio Selected Static0", 24);
@@ -49,73 +40,58 @@ class RadioButton extends TextOption {
radio.updateHitbox();
add(radio);
- this.value = value;
+ __text.x = 100;
- this.optionName = optionName;
- if(optionName != null) {
- checked = Reflect.field(parent, optionName) == value;
- } else {
- checked = false;
- }
+ if (optionName != null) checked = Reflect.field(parent, optionName) == value;
+ else checked = false;
}
public var firstFrame:Bool = true;
- public override function update(elapsed:Float) {
- if(radio.animation.curAnim == null) {
- radio.animation.play(checked ? "checked" : "unchecked", true);
- }
+ override function update(elapsed:Float) {
+ if (radio.animation.curAnim == null) radio.animation.play(checked ? "checked" : "unchecked", true);
super.update(elapsed);
- switch(radio.animation.curAnim.name) {
- case "unchecking":
- if(radio.animation.curAnim.finished) {
- radio.animation.play("unchecked", true);
- trace("UNCHECKING finished");
- }
- case "checking":
- if(radio.animation.curAnim.finished) {
- radio.animation.play("checked", true);
- trace("CHECKING finished");
- }
+
+ if (radio.animation.curAnim.finished) switch(radio.animation.curAnim.name) {
+ case "unchecking": radio.animation.play("unchecked", true);
+ case "checking": radio.animation.play("checked", true);
}
firstFrame = false;
}
override function draw() {
- if(radio.animation.curAnim != null) {
+ if (radio.animation.curAnim != null) {
var offset = offsets[radio.animation.curAnim.name];
- if (offset != null)
- radio.frameOffset.set(offset.x, offset.y);
+ if (offset != null) radio.frameOffset.set(offset.x, offset.y);
}
super.draw();
}
function set_checked(v:Bool) {
- if(checked != v) {
- checked = v;
- if(!firstFrame) {
- radio.animation.play(checked ? "checking" : "unchecking", true);
- }
- }
+ if (checked == (checked = v)) return v;
+
+ if (!firstFrame) radio.animation.play(checked ? "checking" : "unchecking", true);
return v;
}
- public override function onSelect() {
+ override function select() {
+ if (locked) return;
+
checked = true;
- if(checked) {
- onSet(value);
+ if (optionName != null) Reflect.setField(parent, optionName, value);
+
+ CoolUtil.playMenuSFX(CHECKED);
+
+ if (screen != null) {
+ for (item in screen.members) if (item != null && item != this && item is RadioButton) cast(item, RadioButton).checked = false;
}
+
+ if (selectCallback != null) selectCallback();
}
- public dynamic function onSet(value:Dynamic) {
- if(optionName != null) {
- Reflect.setField(parent, optionName, value);
- }
- }
-
- public override function destroy() {
+ override function destroy() {
super.destroy();
- for(e in offsets) e.put();
+ for (e in offsets) e.put();
}
}
\ No newline at end of file
diff --git a/source/funkin/options/type/Separator.hx b/source/funkin/options/type/Separator.hx
new file mode 100644
index 00000000..716c3576
--- /dev/null
+++ b/source/funkin/options/type/Separator.hx
@@ -0,0 +1,30 @@
+package funkin.options.type;
+
+import flixel.math.FlxPoint;
+import flixel.util.FlxDestroyUtil;
+import flixel.FlxBasic;
+
+class Separator extends FlxSprite {
+ var separatorHeight:Float;
+ public function new(height = 67) {
+ super();
+ separatorHeight = height;
+ }
+
+ override function initVars() {
+ flixelType = OBJECT;
+
+ offset = FlxPoint.get();
+ origin = FlxPoint.get();
+ scale = FlxPoint.get(1, 1);
+ scrollFactor = FlxPoint.get(1, 1);
+
+ initMotionVars();
+ }
+
+ override function update(elapsed:Float) {}
+ override function draw() {}
+
+ override function get_height():Float return separatorHeight;
+ override function get_width():Float return 600;
+}
\ No newline at end of file
diff --git a/source/funkin/options/type/SliderOption.hx b/source/funkin/options/type/SliderOption.hx
new file mode 100644
index 00000000..955b61d9
--- /dev/null
+++ b/source/funkin/options/type/SliderOption.hx
@@ -0,0 +1,88 @@
+package funkin.options.type;
+
+import funkin.menus.ui.Slider;
+import funkin.options.TreeMenu.ITreeFloatOption;
+
+class SliderOption extends TextOption implements ITreeFloatOption {
+ public var changedCallback:Float->Void;
+
+ public var min:Float;
+ public var max:Float;
+ public var step:Float;
+
+ public var currentValue:Float;
+
+ public var parent:Dynamic;
+ public var optionName:String;
+
+ public var slider:Slider;
+ public var dynamicWidth:Bool;
+
+ var __mouseControl:Bool;
+
+ function getValue():Float return (currentValue - min) / (max - min);
+
+ override function set_text(v:String) {
+ super.set_text(v);
+ slider.x = __text.x + __text.width + 30;
+ if (dynamicWidth) {
+ slider.barWidth = 1100 - __text.width;
+ slider.updateHitbox();
+ }
+ return v;
+ }
+
+ public function new(text:String, desc:String, min:Float, max:Float, step:Float, ?segments:Int, optionName:String, barWidth = -1,
+ ?changedCallback:Float->Void = null, ?parent:Dynamic)
+ {
+ this.changedCallback = changedCallback;
+ this.min = min;
+ this.max = max;
+ this.step = step;
+ this.optionName = optionName;
+ this.parent = parent = parent != null ? parent : Options;
+
+ if (Reflect.field(parent, optionName) != null) currentValue = Reflect.field(parent, optionName);
+
+ slider = new Slider(12, 0, getValue(), (dynamicWidth = barWidth < 0) ? 600 : barWidth, segments);
+ slider.scale.set(0.75, 0.75);
+ slider.updateHitbox();
+
+ super(text, desc);
+ add(slider);
+ slider.y = Math.floor(__text.y + (__text.height - slider.height) * 0.5 + 2);
+ }
+
+ override function update(elapsed:Float) {
+ if (slider.selected = selected && !locked) {
+ if (__mouseControl && FlxG.mouse.justReleased) __mouseControl = false;
+ else if (FlxG.mouse.justPressed) __mouseControl = slider.overlapsPoint(FlxG.mouse.getPosition(@:privateAccess flixel.input.FlxPointer._cachedPoint), true);
+
+ if (__mouseControl) {
+ var p = @:privateAccess flixel.input.FlxPointer._cachedPoint;
+ if (!FlxG.mouse.justPressed) FlxG.mouse.getPosition(p);
+ p.subtractPoint(camera.scroll);
+
+ slider.getScreenPosition(_point, camera);
+ changeValue((FlxMath.remapToRange(p.x - _point.x, 0, slider.width, min, max) - currentValue) / step);
+ }
+ }
+ else
+ __mouseControl = false;
+
+ slider.value = getValue();
+ //__text.x = slider.x + slider.width + 30;
+
+ super.update(elapsed);
+ }
+
+ public function changeValue(change:Float):Void {
+ if (locked) return;
+ if (currentValue == (currentValue = FlxMath.bound(currentValue + change * step, min, max))) return;
+
+ Reflect.setField(parent, optionName, currentValue);
+ if (changedCallback != null) changedCallback(currentValue);
+ }
+
+ override function select() {}
+}
\ No newline at end of file
diff --git a/source/funkin/options/type/TextOption.hx b/source/funkin/options/type/TextOption.hx
index 2d2b5187..64b93b25 100644
--- a/source/funkin/options/type/TextOption.hx
+++ b/source/funkin/options/type/TextOption.hx
@@ -6,46 +6,35 @@ import flixel.effects.FlxFlicker;
* Option type that has text.
**/
class TextOption extends OptionType {
+ public var suffix(default, set):String;
public var selectCallback:Void->Void;
- private var __text:Alphabet;
- private var rawText(default, set):String;
-
- public var text(get, set):String;
- private function get_text() {return __text.text;}
- private function set_text(v:String) {return __text.text = v;}
-
- /**
- * If you change this afterwards you will need to call `reloadStrings()`, or manually set rawText to rawText.
- **/
- public var suffix:String;
-
- public function new(text:String, desc:String, ?suffix:String = "", ?selectCallback:Void->Void = null) {
- super(desc);
- this.selectCallback = selectCallback;
- add(__text = new Alphabet(100, 20, "", "bold"));
- this.suffix = suffix;
- rawText = text;
+ var __text:Alphabet;
+ override function set_text(v:String) {
+ __text.text = v + suffix;
+ return text = v;
}
- override function reloadStrings() {
- super.reloadStrings();
- this.rawText = rawText;
- }
-
- function set_rawText(v:String) {
- rawText = v;
- __text.text = (TU.exists(rawText) ? TU.translate(rawText) : rawText) + suffix;
+ function set_suffix(v:String) {
+ if (suffix == (suffix = v)) return v;
+ __text.text = text + suffix;
return v;
}
- public override function draw() {
- super.draw();
+ public function new(text:String, desc:String, ?suffix:String = "", ?selectCallback:Void->Void = null) {
+ @:bypassAccessor this.suffix = suffix;
+ this.selectCallback = selectCallback;
+
+ __text = new Alphabet(20, 20, "", "bold");
+
+ super(text, desc);
+ add(__text);
}
- public override function onSelect() {
- super.onSelect();
+
+ override function select() {
+ if (locked) return;
CoolUtil.playMenuSFX(CONFIRM);
FlxFlicker.flicker(this, 1, Options.flashingMenu ? 0.06 : 0.15, true, false);
- selectCallback();
+ if (selectCallback != null) selectCallback();
}
}
\ No newline at end of file
diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx
index 478ed150..2414d990 100644
--- a/source/lime/_internal/backend/native/NativeAudioSource.hx
+++ b/source/lime/_internal/backend/native/NativeAudioSource.hx
@@ -136,9 +136,9 @@ class NativeAudioSource {
public function play() {
if (playing || disposed) return;
+ var t = completed ? 0 : getCurrentTime();
playing = true;
- if (completed) setCurrentTime(0);
- else setCurrentTime(getCurrentTime());
+ setCurrentTime(t);
}
public function pause() {
@@ -394,6 +394,8 @@ class NativeAudioSource {
AL.sourcePlay(handle);
}
}
+ else
+ completed = false;
if (!playing && streamed) bufferTimes[STREAM_MAX_BUFFERS - (requestBuffers = queuedBuffers = 1)] = value / 1000;