diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml index ad8a02f0..341559cb 100644 --- a/assets/languages/en/Options.xml +++ b/assets/languages/en/Options.xml @@ -147,6 +147,9 @@ Developer Mode If checked, you will be able to access developer features like certain keybinds, editors, developer options, state reloads, console and more. + Allow Mod Configs Warning Popup + 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. diff --git a/assets/languages/pl/Options.xml b/assets/languages/pl/Options.xml index 0549951f..de4512d7 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. - Migające Menu Jeśli wyłączone, tło menu głównego nie będzie migać kolorów po wybraniu opcji. Inne migania będą znacznie wolniejsze. - 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) 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/options/Options.hx b/source/funkin/options/Options.hx index 9551c51c..39d03594 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -45,6 +45,7 @@ class Options 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 diff --git a/source/funkin/options/TreeMenu.hx b/source/funkin/options/TreeMenu.hx index befed063..9a8b933a 100644 --- a/source/funkin/options/TreeMenu.hx +++ b/source/funkin/options/TreeMenu.hx @@ -89,6 +89,9 @@ class TreeMenu extends UIState { if (cameraScroll && last != null) { if (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel(); FlxG.camera.scroll.x = last.x; + + for (menu in tree) if (menu != null) menu.transitioning = false; + for (menu in previousMenus) if (menu != null) menu.transitioning = false; } } @@ -203,8 +206,9 @@ class TreeMenu extends UIState { }}); for (menu in tree) if (menu != null) menu.transitioning = true; - for (menu in previousMenus) if (menu != null) menu.transitioning = true; } + + for (menu in previousMenus) if (menu != null) menu.transitioning = true; } override function update(elapsed:Float) { @@ -250,6 +254,7 @@ class TreeMenu extends UIState { public function reloadStrings() { for (menu in tree) if (menu != null) menu.reloadStrings(); + updateLabels(); } public function destroyPreviousMenus() { diff --git a/source/funkin/options/TreeMenuScreen.hx b/source/funkin/options/TreeMenuScreen.hx index b03ab959..5c8a1d42 100644 --- a/source/funkin/options/TreeMenuScreen.hx +++ b/source/funkin/options/TreeMenuScreen.hx @@ -48,8 +48,8 @@ class TreeMenuScreen extends FlxSpriteGroup { 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 / 30); - var rightTurboControl:TurboControls = new TurboControls([Control.RIGHT], 0.2, 1 / 30); + 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; @@ -126,6 +126,8 @@ class TreeMenuScreen extends FlxSpriteGroup { public function close() { onClose.dispatch(); + if (curOption != null) curOption.selected = false; + if (parent == null) return destroy(); else parent.removeMenu(this); diff --git a/source/funkin/options/categories/AppearanceOptions.hx b/source/funkin/options/categories/AppearanceOptions.hx index 125509a5..57e48e03 100644 --- a/source/funkin/options/categories/AppearanceOptions.hx +++ b/source/funkin/options/categories/AppearanceOptions.hx @@ -2,7 +2,7 @@ package funkin.options.categories; class AppearanceOptions extends TreeMenuScreen { public function new() { - super('optionsTree.appearance-name', 'optionsTree.appearance-desc', "AppearanceOptions."); + super('optionsTree.appearance-name', 'optionsTree.appearance-desc', 'AppearanceOptions.'); add(new NumOption(getNameID('framerate'), getDescID('framerate'), 30, 240, 1, @@ -35,7 +35,7 @@ class AppearanceOptions extends TreeMenuScreen { class AdvancedAppearanceOptions extends TreeMenuScreen { public function new() { - super('optionsMenu.advanced', 'optionsTree.appearance.advanced-desc', "AppearanceOptions.Advanced."); + super('optionsMenu.advanced', 'optionsTree.appearance.advanced-desc', 'AppearanceOptions.Advanced.'); for (option in [ new Checkbox(getNameID('antialiasing'), getDescID('antialiasing'), 'antialiasing', __changeAntialiasing), diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx index 0abcbae1..3faa11db 100644 --- a/source/funkin/options/categories/GameplayOptions.hx +++ b/source/funkin/options/categories/GameplayOptions.hx @@ -4,7 +4,66 @@ import flixel.util.FlxTimer; import funkin.backend.system.Conductor; class GameplayOptions extends TreeMenuScreen { + var __metronome = FlxG.sound.load(Paths.sound('editors/charter/metronome')); + var offsetSetting:NumOption; + + 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')); + add(offsetSetting = new NumOption(getNameID('songOffset'), getDescID('songOffset'), -999, 999, 1, 'songOffset', __changeOffset)); + + add(new Separator()); + add(new TextOption('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', ' >', () -> + parent.addMenu(new AdvancedGameplayOptions()))); + } + + private function __changeOffset(offset:Float) Conductor.songOffset = offset; + + var __lastBeat:Int = 0; + var __lastSongBeat:Int = 0; + + override function update(elapsed:Float) { + super.update(elapsed); + FlxG.camera.zoom = CoolUtil.fpsLerp(FlxG.camera.zoom, 1, 0.1); + + if (offsetSetting.selected) { + if (__lastBeat != Conductor.curBeat) { + FlxG.camera.zoom += 0.03; + __lastBeat = Conductor.curBeat; + } + + var beat = Math.floor(Conductor.getTimeInBeats(FlxG.sound.music.time)); + if (__lastSongBeat != beat) { + __metronome.replay(); + __lastSongBeat = beat; + } + } + } + + 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; + } } -class AdvancedGameplayOptions extends OptionsScreen { -} +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 d166643e..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 TreeMenuScreen { - public override function new(title:String, desc:String) { - super(title, desc, "LanguageOptions."); + 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 ffa8eadd..e72bafc8 100644 --- a/source/funkin/options/categories/MiscOptions.hx +++ b/source/funkin/options/categories/MiscOptions.hx @@ -1,4 +1,26 @@ package funkin.options.categories; 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(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 Separator()); + add(new TextOption(getNameID('resetSaveData'), getDescID('resetSaveData'), () -> { + // TODO: SAVE DATA RESETTING + })); + } +} \ No newline at end of file diff --git a/source/funkin/options/type/Checkbox.hx b/source/funkin/options/type/Checkbox.hx index 5241686c..592b7723 100644 --- a/source/funkin/options/type/Checkbox.hx +++ b/source/funkin/options/type/Checkbox.hx @@ -19,8 +19,8 @@ class Checkbox extends TextOption { "checking" => FlxPoint.get(35, 29) ]; - public function new(text:String, desc:String, optionName:String, ?selectCallback:Void->Void = null, ?parent:Dynamic) { - super(text, desc); + public function new(text:String, desc:String, optionName:String, ?selectCallback:Void->Void, ?parent:Dynamic) { + super(text, desc, selectCallback); this.optionName = optionName; this.parent = parent = parent != null ? parent : Options; @@ -36,14 +36,13 @@ class Checkbox extends TextOption { __text.x = 100; - var fieldValue = Reflect.field(parent, optionName); if (optionName != null) checked = Reflect.field(parent, optionName); else checked = false; } public var firstFrame:Bool = true; - public override function update(elapsed:Float) { + override function update(elapsed:Float) { if (checkbox.animation.curAnim == null) checkbox.animation.play(checked ? "checked" : "unchecked", true); super.update(elapsed); @@ -64,15 +63,15 @@ class Checkbox extends TextOption { } 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 select() { + override function select() { if (locked) return; + checked = !checked; if (optionName != null) Reflect.setField(parent, optionName, checked); @@ -82,8 +81,8 @@ class Checkbox extends TextOption { 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/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/NewOption.hx b/source/funkin/options/type/NewOption.hx index 28255b8c..07f4fdf0 100644 --- a/source/funkin/options/type/NewOption.hx +++ b/source/funkin/options/type/NewOption.hx @@ -12,6 +12,7 @@ class NewOption extends TextOption { super(name, desc, callback); __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/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 ac48eafa..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,75 +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 select() { + 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