Options Rewrite WIP not functional yet
This commit is contained in:
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="130" width="70" height="12" rotated="true" frameX="-0" frameY="-0" frameWidth="12" frameHeight="70"/>
|
||||||
|
<SubTexture name="bar filled0000.png" x="193" y="127" width="70" height="12" rotated="true" frameX="-0" frameY="-0" frameWidth="12" frameHeight="70"/>
|
||||||
|
<SubTexture name="corner empty0000.png" x="267" y="114" width="70" height="28" rotated="true" frameX="-0" frameY="-0" frameWidth="28" frameHeight="70"/>
|
||||||
|
<SubTexture name="corner filled0000.png" x="341" y="108" width="70" height="28" rotated="true" frameX="-0" frameY="-0" frameWidth="28" frameHeight="70"/>
|
||||||
|
<SubTexture name="segment empty0000.png" x="372" y="97" width="70" height="6" rotated="true" frameX="-0" frameY="-0" frameWidth="6" frameHeight="70"/>
|
||||||
|
<SubTexture name="segment filled0000.png" x="193" y="143" width="70" height="6" rotated="true" frameX="-0" frameY="-0" frameWidth="6" 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>
|
||||||
@@ -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 inline final DEFAULT_DELAY:Float = 0.4;
|
||||||
|
public static inline final 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 = DEFAULT_DELAY, interval = DEFAULT_INTERVAL, allPress = false) {
|
||||||
|
super();
|
||||||
|
this.delay = delay;
|
||||||
|
this.interval = 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 ?? 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -191,6 +191,8 @@ class Controls extends FlxActionSet
|
|||||||
setKeyboardScheme(scheme, false);
|
setKeyboardScheme(scheme, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getActionFromControl(control:Control):FLxAction return macro_getActionFromControl(control);
|
||||||
|
|
||||||
public function getKeyName(control:Control, idx:Int = 0):String
|
public function getKeyName(control:Control, idx:Int = 0):String
|
||||||
{
|
{
|
||||||
var action = macro_getActionFromControl(control);
|
var action = macro_getActionFromControl(control);
|
||||||
|
|||||||
@@ -519,8 +519,8 @@ final class CoolUtil
|
|||||||
* @param menuSFX Menu SFX to play
|
* @param menuSFX Menu SFX to play
|
||||||
* @param volume At which volume it should play
|
* @param volume At which volume it should play
|
||||||
*/
|
*/
|
||||||
@:noUsing public static inline function playMenuSFX(menuSFX:CoolSfx = SCROLL, volume:Float = 1) {
|
@:noUsing public static inline function playMenuSFX(menuSFX:CoolSfx = SCROLL, volume:Float = 1):FlxSound {
|
||||||
FlxG.sound.play(Paths.sound('menu/' + switch(menuSFX) {
|
return FlxG.sound.play(Paths.sound('menu/' + switch(menuSFX) {
|
||||||
case CONFIRM: 'confirm';
|
case CONFIRM: 'confirm';
|
||||||
case CANCEL: 'cancel';
|
case CANCEL: 'cancel';
|
||||||
case SCROLL: 'scroll';
|
case SCROLL: 'scroll';
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ class Alphabet extends FlxSprite {
|
|||||||
// for menu shit
|
// for menu shit
|
||||||
public var targetY:Float = 0;
|
public var targetY:Float = 0;
|
||||||
public var isMenuItem:Bool = false;
|
public var isMenuItem:Bool = false;
|
||||||
|
public var itemHeight:Float = 120;
|
||||||
|
|
||||||
public function new(?x:Float, ?y:Float, ?text:String = "", ?font:OneOfTwo<String, Bool> = "normal") {
|
public function new(?x:Float, ?y:Float, ?text:String = "", ?font:OneOfTwo<String, Bool> = "normal") {
|
||||||
super(x, y);
|
super(x, y);
|
||||||
@@ -202,7 +203,7 @@ class Alphabet extends FlxSprite {
|
|||||||
if (isMenuItem) {
|
if (isMenuItem) {
|
||||||
var scaledY = targetY * 1.3;
|
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);
|
x = CoolUtil.fpsLerp(x, (targetY * 20) + 90, 0.16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,7 +209,8 @@ class Options
|
|||||||
applyKeybinds();
|
applyKeybinds();
|
||||||
FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? LOW : BEST;
|
FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? LOW : BEST;
|
||||||
FlxG.autoPause = autoPause;
|
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() {
|
public static function applyKeybinds() {
|
||||||
|
|||||||
@@ -1,172 +1,87 @@
|
|||||||
package funkin.options;
|
package funkin.options;
|
||||||
|
|
||||||
import funkin.backend.system.framerate.Framerate;
|
|
||||||
import funkin.options.TreeMenu;
|
|
||||||
import funkin.options.categories.*;
|
import funkin.options.categories.*;
|
||||||
import funkin.options.type.*;
|
import funkin.options.type.*;
|
||||||
import funkin.options.type.Checkbox;
|
|
||||||
import haxe.xml.Access;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
class OptionsMenu extends TreeMenu {
|
class OptionsMenu extends TreeMenu {
|
||||||
public static var mainOptions:Array<OptionCategory> = [
|
public static var mainOptions:Array<OptionCategory> = [
|
||||||
{ // name and desc are actually the translations ids! - Nex
|
{ // name and desc are actually the translations ids! - Nex
|
||||||
name: 'optionsTree.controls-name',
|
name: 'optionsTree.controls-name',
|
||||||
desc: 'optionsTree.controls-desc',
|
desc: 'optionsTree.controls-desc',
|
||||||
state: null,
|
suffix: '';
|
||||||
substate: (name:String, desc:String) -> {
|
substate: funkin.options.keybinds.KeybindsOptions;
|
||||||
return new funkin.options.keybinds.KeybindsOptions();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'optionsTree.gameplay-name',
|
name: 'optionsTree.gameplay-name',
|
||||||
desc: 'optionsTree.gameplay-desc',
|
desc: 'optionsTree.gameplay-desc',
|
||||||
suffix: " >",
|
|
||||||
state: GameplayOptions
|
state: GameplayOptions
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'optionsTree.appearance-name',
|
name: 'optionsTree.appearance-name',
|
||||||
desc: 'optionsTree.appearance-desc',
|
desc: 'optionsTree.appearance-desc',
|
||||||
suffix: " >",
|
|
||||||
state: AppearanceOptions
|
state: AppearanceOptions
|
||||||
},
|
},
|
||||||
#if TRANSLATIONS_SUPPORT
|
#if TRANSLATIONS_SUPPORT
|
||||||
{
|
{
|
||||||
name: 'optionsTree.language-name',
|
name: 'optionsTree.language-name',
|
||||||
desc: 'optionsTree.language-desc',
|
desc: 'optionsTree.language-desc',
|
||||||
suffix: " >",
|
|
||||||
state: LanguageOptions
|
state: LanguageOptions
|
||||||
},
|
},
|
||||||
#end
|
#end
|
||||||
{
|
{
|
||||||
name: 'optionsTree.miscellaneous-name',
|
name: 'optionsTree.miscellaneous-name',
|
||||||
desc: 'optionsTree.miscellaneous-desc',
|
desc: 'optionsTree.miscellaneous-desc',
|
||||||
suffix: " >",
|
|
||||||
state: MiscOptions
|
state: MiscOptions
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
var bg:FlxSprite;
|
var bg:FunkinSprite;
|
||||||
|
|
||||||
public override function create() {
|
override function create() {
|
||||||
super.create();
|
super.create();
|
||||||
|
|
||||||
CoolUtil.playMenuSong();
|
CoolUtil.playMenuSong();
|
||||||
|
|
||||||
DiscordUtil.call("onMenuLoaded", ["Options Menu"]);
|
DiscordUtil.call("onMenuLoaded", ["Options Menu"]);
|
||||||
|
|
||||||
bg = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
|
add(bg = new FunkinSprite().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;
|
bg.antialiasing = true;
|
||||||
add(bg);
|
bg.scrollFactor.set();
|
||||||
|
updateBG();
|
||||||
|
|
||||||
main = new OptionsScreen("optionsMenu.header.title", "optionsMenu.header.desc", [for(o in mainOptions) {
|
/*tree = [
|
||||||
new TextOption(o.name, o.desc, o.suffix, function() {
|
for (o in mainOptions) {
|
||||||
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]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}]);
|
|
||||||
|
|
||||||
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
|
|
||||||
var xmlPath = Paths.xml('config/options/LIB_$i');
|
|
||||||
|
|
||||||
if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT")) {
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
];*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reloadStrings() {
|
public function updateBG() {
|
||||||
for(o in main.members) {
|
var scaleX:Float = FlxG.width / bg.width;
|
||||||
o.reloadStrings();
|
var scaleY:Float = FlxG.height / bg.height;
|
||||||
}
|
bg.scale.x = bg.scale.y = Math.max(scaleX, scaleY);
|
||||||
optionsTree.reloadStrings();
|
bg.screenCenter();
|
||||||
reloadLabels();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function exit() {
|
override function onResize(width:Int, height:Int) {
|
||||||
|
super.onResize(width, height);
|
||||||
|
if (!UIState.resolutionAware) return;
|
||||||
|
|
||||||
|
updateBG();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function exit() {
|
||||||
Options.save();
|
Options.save();
|
||||||
Options.applySettings();
|
Options.applySettings();
|
||||||
super.exit();
|
super.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* XML STUFF
|
|
||||||
*/
|
|
||||||
public function parseOptionsFromXML(xml:Access):Array<OptionType> {
|
|
||||||
var options:Array<OptionType> = [];
|
|
||||||
|
|
||||||
for(node in xml.elements) {
|
|
||||||
if (!node.has.name) {
|
|
||||||
Logs.warn("An option node requires a name attribute.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var name = node.getAtt("name");
|
|
||||||
var desc = node.getAtt("desc").getDefault("optionsMenu.desc-missing");
|
|
||||||
|
|
||||||
switch(node.name) {
|
|
||||||
case "checkbox":
|
|
||||||
if (!node.has.id) {
|
|
||||||
Logs.warn("A checkbox option requires an \"id\" for option saving.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
options.push(new Checkbox(name, desc, node.att.id, FlxG.save.data));
|
|
||||||
|
|
||||||
case "number":
|
|
||||||
if (!node.has.id) {
|
|
||||||
Logs.warn("A number option requires an \"id\" for option saving.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
options.push(new NumOption(name, desc, Std.parseFloat(node.att.min), Std.parseFloat(node.att.max), Std.parseFloat(node.att.change), node.att.id, null, FlxG.save.data));
|
|
||||||
case "choice":
|
|
||||||
if (!node.has.id) {
|
|
||||||
Logs.warn("A choice option requires an \"id\" for option saving.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var optionOptions:Array<Dynamic> = [];
|
|
||||||
var optionDisplayOptions:Array<String> = [];
|
|
||||||
|
|
||||||
for(choice in node.elements) {
|
|
||||||
optionOptions.push(choice.att.value);
|
|
||||||
optionDisplayOptions.push(choice.att.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(optionOptions.length > 0)
|
|
||||||
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)));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+231
-110
@@ -1,148 +1,269 @@
|
|||||||
package funkin.options;
|
package funkin.options;
|
||||||
|
|
||||||
import flixel.FlxState;
|
import flixel.util.FlxSignal;
|
||||||
import flixel.tweens.FlxTween;
|
|
||||||
import flixel.util.typeLimit.OneOfTwo;
|
|
||||||
import flixel.util.typeLimit.OneOfThree;
|
|
||||||
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.backend.system.framerate.Framerate;
|
||||||
|
import funkin.editors.ui.UIState;
|
||||||
|
|
||||||
|
interface ITreeOption {
|
||||||
|
var desc:String;
|
||||||
|
var selected:Bool;
|
||||||
|
|
||||||
|
function changeSelection(change:Int);
|
||||||
|
function select():Void;
|
||||||
|
}
|
||||||
|
|
||||||
class TreeMenu extends UIState {
|
class TreeMenu extends UIState {
|
||||||
public var main:OptionsScreen;
|
public var onMenuClosed:FlxTypedSignal<TreeMenuScreen->Void> = new FlxTypedSignal();
|
||||||
public var optionsTree:OptionsTree;
|
public var onMenuChanged:FlxTypedSignal<TreeMenuScreen->Void> = new FlxTypedSignal();
|
||||||
public var pathLabel:FunkinText;
|
|
||||||
public var pathDesc:FunkinText;
|
|
||||||
public var pathBG:FlxSprite;
|
|
||||||
|
|
||||||
public var screenScroll(default, set):Float;
|
public var tree(default, null):Array<TreeMenuScreen>;
|
||||||
private inline function set_screenScroll(val:Float) {
|
public var length(default, null):Int = 0;
|
||||||
FlxG.camera.scroll.x = val * FlxG.camera.width;
|
public var lastMenu:Null<TreeMenuScreen>;
|
||||||
return screenScroll = val;
|
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) {
|
public var titleLabel:FunkinText;
|
||||||
if (lastState == null) lastState = Type.getClass(FlxG.state);
|
public var descLabel:FunkinText;
|
||||||
|
public var bgLabel:FlxSprite;
|
||||||
|
|
||||||
|
var menuChangeTween:FlxTween;
|
||||||
|
var _drawer:TreeMenuDrawer;
|
||||||
|
|
||||||
|
public function new(exitCallback:TreeMenu->Void,
|
||||||
|
scriptsAllowed:Bool = true, ?scriptName:String)
|
||||||
|
{
|
||||||
super(scriptsAllowed, scriptName);
|
super(scriptsAllowed, scriptName);
|
||||||
|
this.exitCallback = exitCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override function createPost() {
|
override function create() {
|
||||||
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() ) ]);
|
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);
|
titleLabel = new FunkinText(4, 4, FlxG.camera.width - 8, 32);
|
||||||
pathLabel.borderSize = 1.25;
|
titleLabel.borderSize = 1.25;
|
||||||
pathLabel.scrollFactor.set();
|
titleLabel.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);
|
|
||||||
|
|
||||||
|
descLabel = new FunkinText(4, 0, FlxG.camera.width - 8, 16);
|
||||||
|
descLabel.scrollFactor.set();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function createPost() {
|
||||||
super.createPost();
|
super.createPost();
|
||||||
|
|
||||||
|
if ((length = tree.length) != 0) {
|
||||||
|
updateMenuPositions(true);
|
||||||
|
tree.last().inputEnabled = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
add(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);
|
||||||
|
updateLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onMenuChange() {
|
public function updateMenuPositions(fromIndex:Int = 0, cameraScroll = false) {
|
||||||
if (optionsTree.members.length <= 0) {
|
var last = tree[position - 1], menu:TreeMenuScreen;
|
||||||
exit();
|
while (fromIndex < length) if ((menu = tree[fromIndex++]) != null) {
|
||||||
} else {
|
menu.x = last == null ? 0 : last.x + Math.max(FlxG.camera.width, last.width);
|
||||||
if (menuChangeTween != null)
|
last = menu;
|
||||||
menuChangeTween.cancel();
|
}
|
||||||
|
|
||||||
menuChangeTween = FlxTween.num(screenScroll, Math.max(0, (optionsTree.members.length-1)), 1.5, {ease: menuTransitionEase, onComplete: function(t) {
|
if (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel();
|
||||||
optionsTree.clearLastMenu();
|
if (cameraScroll) FlxG.camera.scroll.x = lastMenu.x;
|
||||||
menuChangeTween = null;
|
}
|
||||||
}}, (val:Float) -> screenScroll = val);
|
|
||||||
|
|
||||||
reloadLabels();
|
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 updateDesc(?customText:String) {
|
||||||
|
var last = tree.last();
|
||||||
|
|
||||||
|
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 add(menu:TreeMenuScreen):TreeMenuScreen {
|
||||||
|
if (menu == null) return null;
|
||||||
|
if (tree.indexOf(menu) != -1) return menu;
|
||||||
|
|
||||||
|
tree.push(menu);
|
||||||
|
menu.parent = this;
|
||||||
|
length++;
|
||||||
|
|
||||||
|
var prev = tree[length - 1];
|
||||||
|
menu.x = prev == null ? 0 : prev.x + Math.max(FlxG.camera.width, prev.width);
|
||||||
|
|
||||||
|
menuChanged();
|
||||||
|
menu.inputEnabled = true;
|
||||||
|
prev.inputEnabled = false;
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insert(position:Int, menu:TreeMenuScreen):TreeMenuScreen {
|
||||||
|
if (menu == null) return null;
|
||||||
|
if (tree.indexOf(menu) != -1) return menu;
|
||||||
|
|
||||||
|
if (position < 0) position = length - ((-position - 1) % length);
|
||||||
|
tree.insert(position, menu);
|
||||||
|
menu.parent = this;
|
||||||
|
|
||||||
|
var lastChanged = position >= length++;
|
||||||
|
updateMenuPositions(position, !lastChanged);
|
||||||
|
|
||||||
|
if (lastChanged) {
|
||||||
|
menuChanged();
|
||||||
|
menu.inputEnabled = true;
|
||||||
|
tree[length - 1].inputEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pop():TreeMenuScreen {
|
||||||
|
(lastMenu = tree.pop(position)).parent = null;
|
||||||
|
length--;
|
||||||
|
|
||||||
|
menuChanged();
|
||||||
|
lastMenu.inputEnabled = false;
|
||||||
|
menu.inputEnabled = true;
|
||||||
|
|
||||||
|
return lastMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(menu:TreeMenuScreen):TreeMenuScreen {
|
||||||
|
return if (menu == null) null; else removePosition(tree.indexOf(menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function removePosition(position:Int):TreeMenuScreen {
|
||||||
|
if (position < 0 || position >= length) return null;
|
||||||
|
|
||||||
|
destroyLastMenu();
|
||||||
|
(lastMenu = tree.swapAndPop(position)).parent = null;
|
||||||
|
|
||||||
|
if (position == --length) {
|
||||||
|
menuChanged();
|
||||||
|
lastMenu.inputEnabled = false;
|
||||||
|
tree[length - 1].inputEnabled = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateMenuPositions(position, true);
|
||||||
|
updateLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function menuChanged() {
|
||||||
|
if (length == 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;
|
||||||
|
lastMenu.transitioning = false;
|
||||||
|
}});
|
||||||
|
|
||||||
|
for (menu in tree) if (menu != null) menu.transitioning = true;
|
||||||
|
lastMenu.transitioning = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onMenuClose(m:OptionsScreen) {
|
override function update(elapsed:Float) {
|
||||||
CoolUtil.playMenuSFX(CANCEL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reloadLabels() {
|
|
||||||
var t = "";
|
|
||||||
for(o in optionsTree.members)
|
|
||||||
t += '${o.name} > ';
|
|
||||||
pathLabel.text = t;
|
|
||||||
|
|
||||||
var idk:OptionsScreen = optionsTree.members.last();
|
|
||||||
if (idk.members.length > 0) updateDesc(idk.members[idk.curSelected].desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 exit() {
|
|
||||||
FlxG.switchState((lastState != null) ? Type.createInstance(lastState, []) : new MainMenuState());
|
|
||||||
lastState = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var menuChangeTween:FlxTween;
|
|
||||||
public override function update(elapsed:Float) {
|
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
Framerate.offset.y = pathBG.height;
|
|
||||||
|
|
||||||
// in case path gets so long it goes offscreen
|
var i = 0, menu:TreeMenuScreen;
|
||||||
pathLabel.x = lerp(pathLabel.x, Math.max(0, FlxG.width - 4 - pathLabel.width), 0.125);
|
while (i < length) {
|
||||||
|
if ((menu = tree[i++]) == null || !menu.active || !menu.exists) continue;
|
||||||
|
if (i == length || menu.persistentUpdate || menu.transitioning) menu.update(elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastMenu != null) {
|
||||||
|
if (lastMenu.transitioning) lastMenu.update(elapsed);
|
||||||
|
else destroyLastMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
destroyLastMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function onResize(width:Int, height:Int) {
|
||||||
super.onResize(width, height);
|
super.onResize(width, height);
|
||||||
if (!UIState.resolutionAware) return;
|
if (!UIState.resolutionAware) return;
|
||||||
|
|
||||||
if (width < FlxG.initialWidth || height < FlxG.initialHeight) {
|
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
|
updateMenuPositions(true);
|
||||||
|
descLabel.width = titleLabel.width = (bgLabel.width = width) - 8;
|
||||||
if (pathDesc != null && pathLabel != null)
|
|
||||||
pathDesc.width = pathLabel.width = width - 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static inline function menuTransitionEase(e:Float)
|
public function reloadStrings() {
|
||||||
return FlxEase.quintInOut(FlxEase.cubeOut(e));
|
for (menu in tree) if (menu != null) menu.reloadStrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroyLastMenu() {
|
||||||
|
lastMenu = destroyMenus ? FlxDestroyUtil.destroy(lastMenu) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exit() {
|
||||||
|
if (exitCallback != null) return exitCallback(this);
|
||||||
|
|
||||||
|
FlxG.switchState(new 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 = {
|
final class TreeMenuDrawer extends FlxBasic {
|
||||||
var name:String;
|
public var parent:TreeMenu;
|
||||||
var desc:String;
|
public function new(parent:TreeMenu) {
|
||||||
var state:OneOfThree<OptionsScreen, Class<OptionsScreen>, (name:String, desc:String) -> OptionsScreen>;
|
this.parent = parent;
|
||||||
var ?substate:OneOfThree<MusicBeatSubstate, Class<MusicBeatSubstate>, (name:String, desc:String) -> MusicBeatSubstate>;
|
moves = false;
|
||||||
var ?suffix:String;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
typedef OptionTypeDef = {
|
override function draw() {
|
||||||
var type:Class<OptionType>;
|
var i = 0, menu:TreeMenuScreen;
|
||||||
var args:Array<Dynamic>;
|
while (i < parent.length) {
|
||||||
}
|
if ((menu = parent.tree[i++]) == null || !menu.active || !menu.exists) continue;
|
||||||
|
if (i == length || menu.persistentUpdate || menu.transitioning) menu.update(elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.lastMenu != null) parent.lastMenu.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package funkin.options;
|
||||||
|
|
||||||
|
import flixel.util.FlxSignal;
|
||||||
|
import funkin.backend.system.Controls;
|
||||||
|
import funkin.backend.TurboControls;
|
||||||
|
import funkin.options.TreeMenu.ITreeOption;
|
||||||
|
|
||||||
|
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 itemHeight:Float = 120;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (rawName == (rawName = v)) return v;
|
||||||
|
name = TU.exists(rawName) ? TU.translate(rawName) : rawName;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_rawDesc(v:String) {
|
||||||
|
if (rawDesc == (rawDesc = v)) return v;
|
||||||
|
desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getNameID(name):String return prefix + name + "-name";
|
||||||
|
public inline function getDescID(name):String return prefix + name + "-desc";
|
||||||
|
|
||||||
|
public var controls(get, never):Controls;
|
||||||
|
inline function get_controls():Controls return PlayerSettings.solo.controls;
|
||||||
|
|
||||||
|
var leftTurboControl:TurboControls = new TurboControls([Control.LEFT]);
|
||||||
|
var rightTurboControl:TurboControls = new TurboControls([Control.RIGHT]);
|
||||||
|
var upTurboControl:TurboControls = new TurboControls([Control.UP]);
|
||||||
|
var downTurboControl:TurboControls = new TurboControls([Control.DOWN]);
|
||||||
|
var turboBasics:Array<TurboBasics>;
|
||||||
|
|
||||||
|
var curOption:ITreeOption;
|
||||||
|
var _firstFrame:Bool = true;
|
||||||
|
|
||||||
|
public function new(name:String, desc:String, ?prefix:String, ?options:Array<OptionType>) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
rawName = name;
|
||||||
|
rawDesc = desc;
|
||||||
|
|
||||||
|
turboBasics = [leftTurboControl, rightTurboControl, upTurboControl, downTurboControl];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reloadStrings() {
|
||||||
|
name = TU.exists(rawName) ? TU.translate(rawName) : rawName;
|
||||||
|
desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
|
||||||
|
|
||||||
|
for (object in members) if (object != null) object.reloadStrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function update(elapsed:Float) {
|
||||||
|
super.update(elapsed);
|
||||||
|
|
||||||
|
if (_firstFrame) {
|
||||||
|
_firstFrame = false;
|
||||||
|
if (members[curSelected] is ITreeOption) (curOption = cast members[curSelected]:ITreeOption).selected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputEnabled) {
|
||||||
|
for (basic in turboBasics) basic.update(elapsed);
|
||||||
|
changeSelection((upTurboControl.activated ? -1 : 0) + (downTurboControl.activated ? 1 : 0) - FlxG.mouse.wheel);
|
||||||
|
|
||||||
|
if (i > 0 && curOption != null) {
|
||||||
|
if (controls.ACCEPT || FLxG.mouse.justPressed) curOption.select();
|
||||||
|
if (leftTurboControl.activated) curOption.changeSelection(-1);
|
||||||
|
if (rightTurboControl.activated) curOption.changeSelection(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controls.BACK || FlxG.mouse.justPressedRight) close();
|
||||||
|
}
|
||||||
|
|
||||||
|
var i:Int = 0, object:FlxSprite;
|
||||||
|
while (i < length) if ((object = members[i++]) != null) {
|
||||||
|
var h = object.height;
|
||||||
|
object.y = CoolUtil.fpsLerp(object.y, y + (FlxG.camera.height - h) * 0.5 + (i - curSelected) * itemHeight, 0.25);
|
||||||
|
object.x = x - 50 + Math.abs(Math.cos((object.y + (FlxG.camera.height + h) * 0.5 - FlxG.camera.scroll.y) / (FlxG.camera.height * 1.25) * Math.PI)) * 150;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close() {
|
||||||
|
onClose.dispatch();
|
||||||
|
|
||||||
|
if (parent == null) return destroy();
|
||||||
|
else parent.remove(this);
|
||||||
|
|
||||||
|
CoolUtil.playMenuSFX(CANCEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changeSelection(change:Int, force:Bool = false) {
|
||||||
|
if (length == 0 || (change == 0 && !force)) return;
|
||||||
|
|
||||||
|
curSelected = FlxMath.wrap(curSelected + change, 0, members.length - 1);
|
||||||
|
if (members[curSelected] is ITreeOption) (curOption = cast members[curSelected]:ITreeOption).selected = true;
|
||||||
|
updateMenuDesc();
|
||||||
|
|
||||||
|
CoolUtil.playMenuSFX(SCROLL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateMenuDesc(?customTxt:String) {
|
||||||
|
if (parent != null) parent.updateDesc(customTxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
override function destroy() {
|
||||||
|
super.destroy();
|
||||||
|
|
||||||
|
for (basic in turboBasics) basic.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package funkin.options.categories;
|
package funkin.options.categories;
|
||||||
|
|
||||||
class AppearanceOptions extends OptionsScreen {
|
class AppearanceOptions extends TreeMenuScreen {
|
||||||
public override function new(title:String, desc:String) {
|
public override function new() {
|
||||||
super(title, desc, "AppearanceOptions.");
|
super('optionsTree.appearance-name', 'optionsTree.appearance-desc', "AppearanceOptions.");
|
||||||
|
|
||||||
|
/*
|
||||||
add(new NumOption(
|
add(new NumOption(
|
||||||
getName("framerate"),
|
getName("framerate"),
|
||||||
getDesc("framerate"),
|
getDesc("framerate"),
|
||||||
@@ -43,13 +45,12 @@ class AppearanceOptions extends OptionsScreen {
|
|||||||
"gpuOnlyBitmaps"));
|
"gpuOnlyBitmaps"));
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private function __changeFPS(change:Float) {
|
private function __changeFPS(value:Float) {
|
||||||
// if statement cause of the flixel warning
|
var framerate = Math.floor(value);
|
||||||
if(FlxG.updateFramerate < Std.int(change))
|
if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
|
||||||
FlxG.drawFramerate = FlxG.updateFramerate = Std.int(change);
|
else FlxG.updateFramerate = FlxG.drawFramerate = framerate;
|
||||||
else
|
|
||||||
FlxG.updateFramerate = FlxG.drawFramerate = Std.int(change);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,13 @@ class GameplayOptions extends OptionsScreen {
|
|||||||
getName("autoPause"),
|
getName("autoPause"),
|
||||||
getDesc("autoPause"),
|
getDesc("autoPause"),
|
||||||
"autoPause"));
|
"autoPause"));
|
||||||
add(new MeterOption(
|
/*add(new MeterOption(
|
||||||
getName("volumeSFX"),
|
getName("volumeSFX"),
|
||||||
getDesc("volumeSFX"),
|
getDesc("volumeSFX"),
|
||||||
0, // minimum
|
0, // minimum
|
||||||
1, // maximum
|
1, // maximum
|
||||||
0.1, // change
|
0.1, // change
|
||||||
"volumeSFX"));
|
"volumeSFX"));*/
|
||||||
add(offsetSetting = new NumOption(
|
add(offsetSetting = new NumOption(
|
||||||
getName("songOffset"),
|
getName("songOffset"),
|
||||||
getDesc("songOffset"),
|
getDesc("songOffset"),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#if !macro
|
#if !macro
|
||||||
package funkin.options.categories;
|
package funkin.options.categories;
|
||||||
|
|
||||||
|
import funkin.options.TreeMenu;
|
||||||
|
import funkin.options.TreeMenuScreen;
|
||||||
import funkin.options.type.*;
|
import funkin.options.type.*;
|
||||||
#end
|
#end
|
||||||
@@ -1,88 +1,57 @@
|
|||||||
package funkin.options.type;
|
package funkin.options.type;
|
||||||
|
|
||||||
/**
|
|
||||||
* Option type that allows you to select an array of options.
|
|
||||||
**/
|
|
||||||
class ArrayOption extends OptionType {
|
class ArrayOption extends OptionType {
|
||||||
public var selectCallback:String->Void;
|
public var selectCallback:String->Void;
|
||||||
|
|
||||||
private var __text:Alphabet;
|
|
||||||
private var __selectiontext:Alphabet;
|
|
||||||
|
|
||||||
public var options:Array<Dynamic>;
|
public var options:Array<Dynamic>;
|
||||||
public var displayOptions:Array<String>;
|
public var displayOptions:Array<String>;
|
||||||
|
|
||||||
public var currentSelection:Int;
|
public var currentSelection:Int;
|
||||||
|
|
||||||
var optionName:String;
|
|
||||||
|
|
||||||
public var parent:Dynamic;
|
public var parent:Dynamic;
|
||||||
|
public var optionName:String;
|
||||||
|
|
||||||
private var rawText(default, set):String;
|
var __text:Alphabet;
|
||||||
|
var __selectionText:Alphabet;
|
||||||
|
|
||||||
public var text(get, set):String;
|
public function new(text:String, desc:String, options:Array<Dynamic>, displayOptions:Array<String>, ?optionName:String, ?selectCallback:String->Void = null, ?parent:Dynamic) {
|
||||||
private function get_text() {return __text.text;}
|
super(text, desc);
|
||||||
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.selectCallback = selectCallback;
|
||||||
this.displayOptions = displayOptions;
|
this.displayOptions = displayOptions;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
if (parent == null)
|
this.parent = parent = parent != null ? parent : Options;
|
||||||
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;
|
this.optionName = optionName;
|
||||||
|
|
||||||
add(__text = new Alphabet(100, 20, text, "bold"));
|
var fieldValue = Reflect.field(parent, optionName);
|
||||||
add(__selectiontext = new Alphabet(__text.width + 120, 20, formatTextOption(), "normal"));
|
if (fieldValue != null) currentSelection = CoolUtil.maxInt(0, options.indexOf(fieldValue));
|
||||||
|
|
||||||
rawText = text;
|
add(__text = new Alphabet(20, 0, text, 'bold'));
|
||||||
|
add(__selectionText = new Alphabet(__text.x + __text.width + 12, 0, formatTextOption(), 'bold'));
|
||||||
}
|
}
|
||||||
|
|
||||||
override function reloadStrings() {
|
override function reloadStrings() {
|
||||||
super.reloadStrings();
|
super.reloadStrings();
|
||||||
this.rawText = rawText;
|
__selectiontext.x = __text.x + __text.width + 12;
|
||||||
}
|
|
||||||
|
|
||||||
function set_rawText(v:String) {
|
|
||||||
rawText = v;
|
|
||||||
__text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override function draw() {
|
|
||||||
super.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
__selectiontext.text = formatTextOption();
|
||||||
Reflect.setField(parent, optionName, options[currentSelection]);
|
|
||||||
if(selectCallback != null)
|
|
||||||
selectCallback(options[currentSelection]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function formatTextOption() {
|
override function changeSelection(change:Int) {
|
||||||
var currentOptionString = ": ";
|
currentSelection = FlxMath.wrap(currentSelection + change, 0, options.length - 1);
|
||||||
if((currentSelection > 0))
|
__selectionText.text = formatTextOption();
|
||||||
currentOptionString += "< ";
|
CoolUtil.playMenuSFX(SCROLL);
|
||||||
else
|
|
||||||
currentOptionString += " ";
|
|
||||||
|
|
||||||
currentOptionString += displayOptions[currentSelection];
|
if (selectCallback != null) selectCallback(options[currentSelection]);
|
||||||
|
}
|
||||||
|
|
||||||
if(!(currentSelection >= options.length - 1))
|
function formatTextOption() {
|
||||||
currentOptionString += " >";
|
var s = ": ";
|
||||||
|
|
||||||
return currentOptionString;
|
if (currentSelection > 0) s += "< ";
|
||||||
|
else s += " ";
|
||||||
|
|
||||||
|
s += TU.exists(displayOptions[currentSelection]) ? TU.translate(displayOptions[currentSelection]) : displayOptions[currentSelection];
|
||||||
|
|
||||||
|
if (currentSelection < options.length - 1) s += " >";
|
||||||
|
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,40 +2,48 @@ package funkin.options.type;
|
|||||||
|
|
||||||
import flixel.group.FlxSpriteGroup;
|
import flixel.group.FlxSpriteGroup;
|
||||||
import funkin.backend.system.Controls;
|
import funkin.backend.system.Controls;
|
||||||
|
import funkin.options.TreeMenu.ITreeOption;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all option types.
|
* Base class for all option types.
|
||||||
* Used in OptionsMenu.
|
* Used in OptionsMenu.
|
||||||
**/
|
**/
|
||||||
class OptionType extends FlxSpriteGroup {
|
class OptionType extends FlxSpriteGroup implements ITreeOption {
|
||||||
public var controls(get, never):Controls;
|
|
||||||
public var selected:Bool = false;
|
public var selected:Bool = false;
|
||||||
|
|
||||||
|
public var text(default, set):String;
|
||||||
|
public var rawText(default, set):String;
|
||||||
public var desc:String;
|
public var desc:String;
|
||||||
public var rawDesc(default, set):String;
|
public var rawDesc(default, set):String;
|
||||||
|
|
||||||
public function new(desc:String) {
|
public function new(text:String, desc:String) {
|
||||||
super();
|
super();
|
||||||
rawDesc = desc;
|
reloadStrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
function set_rawDesc(v:String) {
|
||||||
rawDesc = v;
|
rawDesc = v;
|
||||||
this.desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
|
desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function reloadStrings() {
|
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) {
|
public override function update(elapsed:Float) {
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
alpha = (selected ? 1 : 0.6);
|
alpha = (selected ? 1 : 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function changeSelection(change:Int) {}
|
||||||
|
public function select() {}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package funkin.options.type;
|
||||||
|
|
||||||
|
class SliderOption extends OptionType {
|
||||||
|
public var selectCallback:Float->Void;
|
||||||
|
|
||||||
|
public var min:Float;
|
||||||
|
public var max:Float;
|
||||||
|
public var segmentVal:Float;
|
||||||
|
|
||||||
|
public var currentSelection:Float;
|
||||||
|
|
||||||
|
public var parent:Dynamic;
|
||||||
|
|
||||||
|
//public var barWidth(get, set):Int;
|
||||||
|
|
||||||
|
public var text(get, set):String;
|
||||||
|
private function get_text() {return __text.text;}
|
||||||
|
private function set_text(v:String) {return __text.text = v;}
|
||||||
|
private var rawText(default, set):String;
|
||||||
|
|
||||||
|
var __text:Alphabet;
|
||||||
|
var optionName:String;
|
||||||
|
|
||||||
|
public function new(text:String, desc:String, min:Float, max:Float, segmentVal:Float, optionName:String, barWidth:Int = 520,
|
||||||
|
?selectCallback:Float->Void = null, ?parent:Dynamic)
|
||||||
|
{
|
||||||
|
super(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_rawText(v:String) {
|
||||||
|
rawText = v;
|
||||||
|
__text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user