Merge branch 'main' into raltyro-fft
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB |
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<TextureAtlas imagePath="meter.png" width="102" height="175">
|
||||
<SubTexture name="meter empty corner0000.png" x="0" y="104" width="39" height="71"/>
|
||||
<SubTexture name="meter empty piece0000.png" x="42" y="104" width="2" height="71"/>
|
||||
<SubTexture name="meter filled corner0000.png" x="47" y="104" width="39" height="71"/>
|
||||
<SubTexture name="meter filled piece0000.png" x="89" y="104" width="2" height="71"/>
|
||||
<SubTexture name="meter slider0000.png" x="0" y="0" width="102" height="102"/>
|
||||
</TextureAtlas>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<TextureAtlas imagePath="slider.png" width="463" height="151">
|
||||
<SubTexture name="bar empty0000.png" x="119" y="134" width="70" height="4" rotated="true" frameX="-0" frameY="-0" frameWidth="4" frameHeight="70"/>
|
||||
<SubTexture name="bar filled0000.png" x="193" y="131" width="70" height="4" rotated="true" frameX="-0" frameY="-0" frameWidth="4" frameHeight="70"/>
|
||||
<SubTexture name="corner empty0000.png" x="267" y="114" width="70" height="26" rotated="true" frameX="-0" frameY="-0" frameWidth="26" frameHeight="70"/>
|
||||
<SubTexture name="corner filled0000.png" x="341" y="108" width="70" height="26" rotated="true" frameX="-0" frameY="-0" frameWidth="26" frameHeight="70"/>
|
||||
<SubTexture name="segment empty0000.png" x="372" y="98" width="70" height="4" rotated="true" frameX="-0" frameY="-0" frameWidth="4" frameHeight="70"/>
|
||||
<SubTexture name="segment filled0000.png" x="193" y="144" width="70" height="4" rotated="true" frameX="-0" frameY="-0" frameWidth="4" frameHeight="70"/>
|
||||
<SubTexture name="slider deselect0000.png" x="372" y="2" width="89" height="91" frameX="-0" frameY="-12" frameWidth="89" frameHeight="109"/>
|
||||
<SubTexture name="slider deselect0001.png" x="372" y="2" width="89" height="91" frameX="-0" frameY="-12" frameWidth="89" frameHeight="109"/>
|
||||
<SubTexture name="slider deselect0002.png" x="250" y="2" width="56" height="108" frameX="-17" frameY="-1" frameWidth="89" frameHeight="109"/>
|
||||
<SubTexture name="slider deselect0003.png" x="250" y="2" width="56" height="108" frameX="-17" frameY="-1" frameWidth="89" frameHeight="109"/>
|
||||
<SubTexture name="slider segment0000.png" x="61" y="2" width="54" height="137" frameX="-0" frameY="-1" frameWidth="54" frameHeight="138"/>
|
||||
<SubTexture name="slider segment0001.png" x="61" y="2" width="54" height="137" frameX="-0" frameY="-1" frameWidth="54" frameHeight="138"/>
|
||||
<SubTexture name="slider selected0000.png" x="119" y="2" width="56" height="124" frameX="-0" frameY="-0" frameWidth="56" frameHeight="124"/>
|
||||
<SubTexture name="slider selecting0000.png" x="2" y="2" width="55" height="140" frameX="-7" frameY="-0" frameWidth="67" frameHeight="140"/>
|
||||
<SubTexture name="slider selecting0001.png" x="2" y="2" width="55" height="140" frameX="-7" frameY="-0" frameWidth="67" frameHeight="140"/>
|
||||
<SubTexture name="slider selecting0002.png" x="179" y="2" width="67" height="121" frameX="-0" frameY="-10" frameWidth="67" frameHeight="140"/>
|
||||
<SubTexture name="slider selecting0003.png" x="179" y="2" width="67" height="121" frameX="-0" frameY="-10" frameWidth="67" frameHeight="140"/>
|
||||
<SubTexture name="slider unselected0000.png" x="310" y="2" width="58" height="102" frameX="-0" frameY="-0" frameWidth="58" frameHeight="102"/>
|
||||
</TextureAtlas>
|
||||
@@ -46,7 +46,9 @@
|
||||
<str id="acceptDifficulty">Press ACCEPT to edit the chart for the selected difficulty.</str>
|
||||
<str id="selectDifficulty">Select a difficulty to continue.</str>
|
||||
<str id="newDifficulty">New Difficulty</str>
|
||||
<str id="newDifficultyDesc">Press ACCEPT to create a new chart difficulty.</str>
|
||||
<str id="newSong">New Song</str>
|
||||
<str id="newSongDesc">Press ACCEPT to create a new song.</str>
|
||||
</group>
|
||||
|
||||
<group name="ChartCreationScreen" prefix="chartCreation.">
|
||||
@@ -225,6 +227,8 @@
|
||||
<group name="SongMenu" prefix="song.">
|
||||
<str id="goStart">Go back to the start</str>
|
||||
<str id="goEnd">Go forward to the end</str> <!-- used to be "Go to the end" -->
|
||||
<str id="addOpponentCamera">Add camera on Opponent</str>
|
||||
<str id="addPlayerCamera">Add camera on Player</str>
|
||||
<str id="muteInst">Mute instrumental</str>
|
||||
<str id="muteVoices">Mute voices</str>
|
||||
</group>
|
||||
@@ -435,6 +439,11 @@
|
||||
<str id="win-title">Creating New Character</str>
|
||||
<str id="charName">Character Name $* Required$</str>
|
||||
<str id="charImgName">Character Image File $* Required$</str>
|
||||
|
||||
<group name="Warnings" prefix="warnings.">
|
||||
<str id="char-exists-title">Creating Character: Error!</str>
|
||||
<str id="char-exists-body">The character you are trying to create already exists, if you would like to override it delete the character first!</str>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -442,6 +451,7 @@
|
||||
<group name="AlphabetSelection" prefix="selection.">
|
||||
<str id="acceptTypeface">Press ACCEPT to edit this typeface.</str>
|
||||
<str id="newTypeface">New Typeface</str>
|
||||
<str id="newTypefaceDesc">Press ACCEPT to create a new typeface.</str>
|
||||
<str id="desc">Select a typeface to edit</str>
|
||||
|
||||
<group name="Warnings" prefix="warnings.">
|
||||
@@ -554,6 +564,11 @@
|
||||
<str id="title">Stage Info</str>
|
||||
<str id="stageName">Stage Name</str>
|
||||
<str id="stagePath">Stage Path</str>
|
||||
|
||||
<group name="Warnings" prefix="warnings.">
|
||||
<str id="stage-exists-title">Creating Stage: Error!</str>
|
||||
<str id="stage-exists-body">The stage you are trying to create alreadly exists, if you would like to override it delete the stage first!</str>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group name="StageInfoScreen" prefix="stageInfoScreen">
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
|
||||
<str id="optionsTree.gameplay-name">Gameplay</str>
|
||||
<str id="optionsTree.gameplay-desc">Change Gameplay options such as Downscroll, Scroll Speed, Naughtyness...</str>
|
||||
<str id="optionsTree.gameplay.advanced-desc">Change more advanced options such as Window Size, Streaming Audio...</str>
|
||||
<str id="optionsTree.gameplay.advanced-desc">Change more advanced options such as Hit Window Size, Streaming Audio...</str>
|
||||
|
||||
<str id="optionsTree.appearance-name">Appearance</str>
|
||||
<str id="optionsTree.appearance-desc">Change Appearance options such as Flashing menus...</str>
|
||||
<str id="optionsTree.appearance.advanced-desc">Change more advanced options such as VRAM-Only Sprites, Antialiasing, Shaders</str>
|
||||
|
||||
<str id="optionsTree.language-name">Language</str>
|
||||
<str id="optionsTree.language-desc">Change the language that the engine uses.</str>
|
||||
@@ -23,6 +24,9 @@
|
||||
<str id="optionsTree.miscellaneous-name">Miscellaneous</str>
|
||||
<str id="optionsTree.miscellaneous-desc">Use this menu to reset save data or engine settings.</str>
|
||||
|
||||
<str id="optionsTree.debug-name">Debug Options</str>
|
||||
<str id="optionsTree.debug-desc">Debug Options for Developer Mode.</str>
|
||||
|
||||
<!-- cant have debug here -->
|
||||
</group>
|
||||
|
||||
@@ -62,8 +66,8 @@
|
||||
<str id="framerate-name">Framerate</str>
|
||||
<str id="framerate-desc">Pretty self explanatory, isn't it?</str>
|
||||
|
||||
<str id="antialiasing-name">Antialiasing</str>
|
||||
<str id="antialiasing-desc">If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites.</str>
|
||||
<str id="flashingMenu-name">Flashing Menu</str>
|
||||
<str id="flashingMenu-desc">If unchecked, will disable menu flashing when you select an option in the Main Menu, and other flashes will be slower.</str>
|
||||
|
||||
<str id="colorHealthBar-name">Colored Healthbar</str>
|
||||
<str id="colorHealthBar-desc">If unchecked, the game will use the original red/green health bar from the week 6 fnf game.</str>
|
||||
@@ -71,17 +75,25 @@
|
||||
<str id="week6PixelPerfect-name">Pixel Perfect Effect</str>
|
||||
<str id="week6PixelPerfect-desc">If checked, Week 6 will have a pixel perfect effect to it enabled, aligning every pixel on the screen.</str>
|
||||
|
||||
<str id="gameplayShaders-name">Gameplay Shaders</str>
|
||||
<str id="gameplayShaders-desc">If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices.</str>
|
||||
<group name="AppearanceOptionsAdvanced" prefix="Advanced.">
|
||||
<str id="quality-name">Quality</str>
|
||||
<str id="quality-desc">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.</str>
|
||||
<str id="quality-low">LOW</str>
|
||||
<str id="quality-high">HIGH</str>
|
||||
<str id="quality-custom">CUSTOM</str>
|
||||
|
||||
<str id="flashingMenu-name">Flashing Menu</str>
|
||||
<str id="flashingMenu-desc">If unchecked, will disable menu flashing when you select an option in the Main Menu, and other flashes will be slower.</str>
|
||||
<str id="antialiasing-name">Antialiasing</str>
|
||||
<str id="antialiasing-desc">If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites.</str>
|
||||
|
||||
<str id="lowMemoryMode-name">Low Memory Mode</str>
|
||||
<str id="lowMemoryMode-desc">If checked, will disable certain background elements in stages to reduce memory usage.</str>
|
||||
<str id="lowMemoryMode-name">Low Memory Mode</str>
|
||||
<str id="lowMemoryMode-desc">If checked, will disable certain background elements in stages to reduce memory usage.</str>
|
||||
|
||||
<str id="gpuOnlyBitmaps-name">VRAM-Only Sprites</str>
|
||||
<str id="gpuOnlyBitmaps-desc">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.</str>
|
||||
<str id="gameplayShaders-name">Gameplay Shaders</str>
|
||||
<str id="gameplayShaders-desc">If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices.</str>
|
||||
|
||||
<str id="gpuOnlyBitmaps-name">VRAM-Only Sprites</str>
|
||||
<str id="gpuOnlyBitmaps-desc">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.</str>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group name="GameplayOptions" prefix="GameplayOptions.">
|
||||
@@ -100,6 +112,9 @@
|
||||
<str id="autoPause-name">Auto Pause</str>
|
||||
<str id="autoPause-desc">If checked, switching windows will pause the game.</str>
|
||||
|
||||
<str id="volumeMusic-name">Music Volume</str>
|
||||
<str id="volumeMusic-desc">Changes only the volume of the music instead of everything.</str>
|
||||
|
||||
<str id="volumeSFX-name">SFX Volume</str>
|
||||
<str id="volumeSFX-desc">Changes only the volume of the SFX instead of everything.</str>
|
||||
|
||||
@@ -137,6 +152,9 @@
|
||||
<str id="devMode-name">Developer Mode</str>
|
||||
<str id="devMode-desc">If checked, you will be able to access developer features like certain keybinds, editors, developer options, state reloads, console and more.</str>
|
||||
|
||||
<str id="allowConfigWarning-name">Show Configs Warning</str>
|
||||
<str id="allowConfigWarning-desc">If checked, a popup at the start of the game will appear if the current mod doesn't have a mod configuration file</str>
|
||||
|
||||
<str id="resetSaveData-name">Reset Save Data</str>
|
||||
<str id="resetSaveData-desc">Select this option to reset save data. This will remove all of your highscores.</str>
|
||||
</group>
|
||||
@@ -157,8 +175,14 @@
|
||||
<str id="editorSFX-name">Editor SFXs</str>
|
||||
<str id="editorSFX-desc">If checked, will play sound effects when working on editors (ex: will play sfxs when checking checkboxes...)</str>
|
||||
|
||||
<str id="editorPrettyPrint-name">Pretty Print</str>
|
||||
<str id="editorPrettyPrint-desc">If checked, the saved files from the editor will be formatted to be easily viewable (does not apply to xmls...)</str>
|
||||
<str id="editorCharterPrettyPrint-name">Chart Pretty Print</str>
|
||||
<str id="editorCharterPrettyPrint-desc">If checked, the saved files from the chart editor will be formatted to be easily viewable</str>
|
||||
|
||||
<str id="editorCharacterPrettyPrint-name">Character Pretty Print</str>
|
||||
<str id="editorCharacterPrettyPrint-desc">If checked, the saved files from the character editor will be formatted to be easily viewable</str>
|
||||
|
||||
<str id="editorStagePrettyPrint-name">Stage Pretty Print</str>
|
||||
<str id="editorStagePrettyPrint-desc">If checked, the saved files from the stage editor will be formatted to be easily viewable</str>
|
||||
|
||||
<str id="intensiveBlur-name">Intensive Blur</str>
|
||||
<str id="intensiveBlur-desc">If checked, will use more intensive blur that may be laggier but look better.</str>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<language>
|
||||
<group name="General Editor Translations" prefix="editor.">
|
||||
<str id="autosavingIn">Autozapisywanie za {0} sekund{1}</str>
|
||||
<str id="autosavingCanceledSuffix">((Anulowane)!</str>
|
||||
<str id="autosavingCanceledSuffix">(Anulowane)!</str>
|
||||
|
||||
<str id="ok">Ok</str>
|
||||
<str id="cancel">Anuluj</str>
|
||||
@@ -225,6 +225,8 @@
|
||||
<group name="SongMenu" prefix="song.">
|
||||
<str id="goStart">Wróć do początku</str>
|
||||
<str id="goEnd">Idź do końca</str> <!-- used to be "Go to the end" -->
|
||||
<str id="addOpponentCamera">Dodaj kamerę na Oponenta</str>
|
||||
<str id="addPlayerCamera">Dodaj kamerę na Gracza</str>
|
||||
<str id="muteInst">Wycisz Instrumental</str>
|
||||
<str id="muteVoices">Wycisz Wokale</str>
|
||||
</group>
|
||||
|
||||
@@ -62,26 +62,28 @@
|
||||
<str id="framerate-name">Klatki</str>
|
||||
<str id="framerate-desc">Ilość Klatek na sekundę ktore gra renderuje</str>
|
||||
|
||||
<str id="antialiasing-name">Wygładzanie</str>
|
||||
<str id="antialiasing-desc">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.</str>
|
||||
|
||||
<str id="colorHealthBar-name">Kolorowe Zdrowie</str>
|
||||
<str id="colorHealthBar-desc">Jeśli wyłączone, Pasek zdrowia będzie tylko używał czerwonego i zielonego koloru, takiego jak z oryginalnej gry.</str>
|
||||
|
||||
<str id="week6PixelPerfect-name">Perfekcyjne Piksele</str>
|
||||
<str id="week6PixelPerfect-desc">Jeśli włączone, Tydzień 6 będzie miał efekt perfekcyjnych pikseli, ustawiający każdy piksel na ekranie.</str>
|
||||
|
||||
<str id="gameplayShaders-name">Shadery Rozgrywki</str>
|
||||
<str id="gameplayShaders-desc">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.</str>
|
||||
|
||||
<str id="flashingMenu-name">Migające Menu</str>
|
||||
<str id="flashingMenu-desc">Jeśli wyłączone, tło menu głównego nie będzie migać kolorów po wybraniu opcji. Inne migania będą znacznie wolniejsze.</str>
|
||||
|
||||
<str id="lowMemoryMode-name">Niskie zużycie pamięci</str>
|
||||
<group name="AppearanceOptionsAdvanced" prefix="Advanced.">
|
||||
<str id="antialiasing-name">Wygładzanie</str>
|
||||
<str id="antialiasing-desc">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.</str>
|
||||
|
||||
<str id="lowMemoryMode-name">Niskie zużycie pamięci</str>
|
||||
<str id="lowMemoryMode-desc">Jeśli włączone, usunie niektóre elementy tła, co użyje mniej pamięci.</str>
|
||||
|
||||
<str id="gpuOnlyBitmaps-name">Sprite'y w VRAM</str>
|
||||
<str id="gpuOnlyBitmaps-desc">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.</str>
|
||||
<str id="gameplayShaders-name">Shadery Rozgrywki</str>
|
||||
<str id="gameplayShaders-desc">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.</str>
|
||||
|
||||
<str id="gpuOnlyBitmaps-name">Sprite'y w VRAM</str>
|
||||
<str id="gpuOnlyBitmaps-desc">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.</str>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group name="GameplayOptions" prefix="GameplayOptions.">
|
||||
@@ -112,7 +114,7 @@
|
||||
|
||||
<str id="streamedMusic-name">Strumieniowana Muzyka</str>
|
||||
<str id="streamedMusic-desc">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)</str>
|
||||
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.
|
||||
|
||||
<str id="streamedVocals-name">Strumieniowane Wokale</str>
|
||||
<str id="streamedVocals-desc">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)</str>
|
||||
</group>
|
||||
@@ -157,9 +159,6 @@ If checked, only musics will have streamed audio, ALSO freeing a LOT of memory w
|
||||
<str id="editorSFX-name">Dźwięki Edytorów</str>
|
||||
<str id="editorSFX-desc">Jeśli włączone, niektóre elementy edytorów będą grać dźwięki. (Na przykład: dźwięki pól wyboru...)</str>
|
||||
|
||||
<str id="editorPrettyPrint-name">Ładny Druk</str>
|
||||
<str id="editorPrettyPrint-desc">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...)</str>
|
||||
|
||||
<str id="intensiveBlur-name">Mocniejszy Rozmaz</str>
|
||||
<str id="intensiveBlur-desc">Jeśli włączone, gra użyje mocniejszego rozmazu, który kosztuje wydajność, ale wygląda lepiej.</str>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Control>;
|
||||
public function new(controls:Array<Control>, ?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<FlxAction>;
|
||||
public function new(actions:Array<FlxAction>, ?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<FlxKey>;
|
||||
public function new(keys:Array<FlxKey>, ?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<FlxGamepadInputID>;
|
||||
public var gamepad:FlxGamepad;
|
||||
public function new(inputs:Array<FlxGamepadInputID>, ?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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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<String, String> {
|
||||
var redirects:Map<String, String> = [
|
||||
];
|
||||
var redirects:Map<String, String> = [];
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,6 @@ class EditorPicker extends MusicBeatSubstate {
|
||||
|
||||
// Name is for backwards compatibility, don't use it, use id instead
|
||||
public var options:Array<Editor> = [
|
||||
{
|
||||
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
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -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<FlxSprite>,
|
||||
?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Dynamic>)
|
||||
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<String> = [];
|
||||
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<OptionType> = [
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -486,7 +486,8 @@ class CharacterEditor extends UIState {
|
||||
button.anim
|
||||
]);
|
||||
|
||||
return "<!DOCTYPE codename-engine-character>\n" + Printer.print(charXML, true);
|
||||
var xmlThingYea:String = "<!DOCTYPE codename-engine-character>\n" + Printer.print(charXML, Options.editorCharacterPrettyPrint);
|
||||
return Options.editorCharacterPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
|
||||
}
|
||||
|
||||
var clipboard:FlxPoint = FlxPoint.get();
|
||||
|
||||
@@ -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<Dynamic>)
|
||||
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<String> = [];
|
||||
|
||||
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<String>, isMods:Bool, folderPath:String = ""):Array<OptionType> {
|
||||
var list:Array<OptionType> = [];
|
||||
function generateList(modsList:Array<String>, isMods:Bool, folderPath:String = ""):Array<FlxSprite> {
|
||||
var list:Array<FlxSprite> = [];
|
||||
|
||||
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<OptionType> = generateList(newModsList, isMods, folderPath + folderName + "/");
|
||||
optionsTree.add(new OptionsScreen(folderPath + folderName, translate("desc-folder", [folderPath + folderName + "/"]), newList));
|
||||
var newList:Array<FlxSprite> = 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<String> = [];
|
||||
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<OptionType> = [
|
||||
for(s in freeplayList.songs) new EditorIconOption(s.name, TU.translate("charterSelection.acceptSong"), s.icon, function() {
|
||||
curSong = s;
|
||||
var list:Array<OptionType> = [
|
||||
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<OptionType> = [
|
||||
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'));
|
||||
|
||||
@@ -726,8 +726,8 @@ class StageEditor extends UIState {
|
||||
}
|
||||
}
|
||||
|
||||
var xmlThingYea:String = "<!DOCTYPE codename-engine-stage>\n" + Printer.print(xml, Options.editorPrettyPrint);
|
||||
return Options.editorPrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
|
||||
var xmlThingYea:String = "<!DOCTYPE codename-engine-stage>\n" + Printer.print(xml, Options.editorStagePrettyPrint);
|
||||
return Options.editorStagePrettyPrint ? xmlThingYea : xmlThingYea.replace("\n", "");
|
||||
}
|
||||
|
||||
function _edit_undo(_) {
|
||||
|
||||
@@ -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<String> = [];
|
||||
|
||||
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<String> = Stage.getList(true, true);
|
||||
|
||||
var list:Array<OptionType> = [
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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<FlxTextFormatMarkerPair> = [];
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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<OptionType> = [];
|
||||
|
||||
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<OptionType> {
|
||||
var credsMenus:Array<OptionType> = [];
|
||||
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<FlxSprite> {
|
||||
var credsMenus:Array<FlxSprite> = [];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String, Bool> = "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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<TreeMenuScreen, Class<TreeMenuScreen>, (name:String, desc:String) -> TreeMenuScreen>;
|
||||
var ?substate:OneOfThree<MusicBeatSubstate, Class<MusicBeatSubstate>, (name:String, desc:String) -> MusicBeatSubstate>;
|
||||
var ?suffix:String;
|
||||
}
|
||||
|
||||
class OptionsMenu extends TreeMenu {
|
||||
public static var mainOptions:Array<OptionCategory> = [
|
||||
{ // 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<OptionsScreen>
|
||||
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<OptionsScreen>
|
||||
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<TreeMenuScreen>
|
||||
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<TreeMenuScreen>
|
||||
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<OptionType> {
|
||||
var options:Array<OptionType> = [];
|
||||
// XML STUFF
|
||||
public function parseOptionsFromXML(xml:Access):Array<FlxSprite> {
|
||||
var options:Array<FlxSprite> = [];
|
||||
|
||||
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)))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
package funkin.options;
|
||||
|
||||
import funkin.options.type.OptionType;
|
||||
|
||||
class OptionsScreen extends FlxTypedSpriteGroup<OptionType> {
|
||||
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<Dynamic>):String {
|
||||
return TU.translate(getNameID(name), args);
|
||||
}
|
||||
public function getDesc(name:String, ?args:Array<Dynamic>):String {
|
||||
return TU.translate(getDescID(name), args);
|
||||
}
|
||||
public function translate(name:String, ?args:Array<Dynamic>):String {
|
||||
return TU.translate(prefix + name, args);
|
||||
}
|
||||
|
||||
public function new(name:String, desc:String, ?prefix:String, ?options:Array<OptionType>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package funkin.options;
|
||||
|
||||
class OptionsTree extends FlxTypedGroup<OptionsScreen> {
|
||||
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) {
|
||||
}
|
||||
}
|
||||
@@ -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:FlxTypedSignal<TreeMenuScreen->Void> = new FlxTypedSignal();
|
||||
public var onMenuChanged:FlxTypedSignal<TreeMenuScreen->Void> = 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<TreeMenuScreen> = [];
|
||||
public var treeLength(default, null):Int = 0;
|
||||
public var previousMenus:Array<TreeMenuScreen> = [];
|
||||
public var destroyMenus:Bool = true;
|
||||
|
||||
public static var lastState:Class<FlxState> = 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<OptionsScreen, Class<OptionsScreen>, (name:String, desc:String) -> OptionsScreen>;
|
||||
var ?substate:OneOfThree<MusicBeatSubstate, Class<MusicBeatSubstate>, (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<OptionType>;
|
||||
var args:Array<Dynamic>;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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<Dynamic>):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<TurboBasic>;
|
||||
|
||||
var curOption:ITreeOption;
|
||||
var curFloatOption:ITreeFloatOption;
|
||||
var __firstFrame:Bool = true;
|
||||
|
||||
public function new(name:String, desc:String, ?prefix:String, ?objects:Array<FlxSprite>) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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<OptionType> = [];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> = 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#if !macro
|
||||
package funkin.options.categories;
|
||||
|
||||
import funkin.options.TreeMenu;
|
||||
import funkin.options.TreeMenuScreen;
|
||||
import funkin.options.type.*;
|
||||
#end
|
||||
@@ -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<Dynamic>;
|
||||
public var displayOptions:Array<String>;
|
||||
|
||||
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<Dynamic>, displayOptions:Array<String>, 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<Dynamic>, displayOptions:Array<String>, ?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() {}
|
||||
}
|
||||
@@ -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<String, FlxPoint> = [
|
||||
"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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ class GithubIconOption extends TextOption
|
||||
this.icon = new GithubUserIcon(user, size, waitUntilLoad);
|
||||
this.usePortrait = usePortrait;
|
||||
add(icon);
|
||||
__text.x = 100;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, FlxPoint> = [
|
||||
"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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user