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);
|
||||
}
|
||||
|
||||
public function getActionFromControl(control:Control):FLxAction return macro_getActionFromControl(control);
|
||||
|
||||
public function getKeyName(control:Control, idx:Int = 0):String
|
||||
{
|
||||
var action = macro_getActionFromControl(control);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,8 @@ class Options
|
||||
applyKeybinds();
|
||||
FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? LOW : BEST;
|
||||
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,172 +1,87 @@
|
||||
package funkin.options;
|
||||
|
||||
import funkin.backend.system.framerate.Framerate;
|
||||
import funkin.options.TreeMenu;
|
||||
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<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 {
|
||||
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 bg:FunkinSprite;
|
||||
|
||||
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 FunkinSprite().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]));
|
||||
}
|
||||
}
|
||||
});
|
||||
}]);
|
||||
/*tree = [
|
||||
for (o in mainOptions) {
|
||||
|
||||
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() {
|
||||
for(o in main.members) {
|
||||
o.reloadStrings();
|
||||
}
|
||||
optionsTree.reloadStrings();
|
||||
reloadLabels();
|
||||
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);
|
||||
bg.screenCenter();
|
||||
}
|
||||
|
||||
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.applySettings();
|
||||
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;
|
||||
|
||||
import flixel.FlxState;
|
||||
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 flixel.util.FlxSignal;
|
||||
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 {
|
||||
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 length(default, null):Int = 0;
|
||||
public var lastMenu:Null<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;
|
||||
|
||||
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.camera.width - 8, 32);
|
||||
titleLabel.borderSize = 1.25;
|
||||
titleLabel.scrollFactor.set();
|
||||
|
||||
descLabel = new FunkinText(4, 0, FlxG.camera.width - 8, 16);
|
||||
descLabel.scrollFactor.set();
|
||||
}
|
||||
|
||||
override function 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() {
|
||||
if (optionsTree.members.length <= 0) {
|
||||
exit();
|
||||
} else {
|
||||
if (menuChangeTween != null)
|
||||
menuChangeTween.cancel();
|
||||
public function updateMenuPositions(fromIndex:Int = 0, cameraScroll = false) {
|
||||
var last = tree[position - 1], menu:TreeMenuScreen;
|
||||
while (fromIndex < length) if ((menu = tree[fromIndex++]) != null) {
|
||||
menu.x = last == null ? 0 : last.x + Math.max(FlxG.camera.width, last.width);
|
||||
last = menu;
|
||||
}
|
||||
|
||||
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 (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel();
|
||||
if (cameraScroll) FlxG.camera.scroll.x = lastMenu.x;
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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 < 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);
|
||||
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.width = width) - 8;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 = {
|
||||
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) {
|
||||
this.parent = parent;
|
||||
moves = false;
|
||||
}
|
||||
|
||||
typedef OptionTypeDef = {
|
||||
var type:Class<OptionType>;
|
||||
var args:Array<Dynamic>;
|
||||
}
|
||||
override function draw() {
|
||||
var i = 0, menu:TreeMenuScreen;
|
||||
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;
|
||||
|
||||
class AppearanceOptions extends OptionsScreen {
|
||||
public override function new(title:String, desc:String) {
|
||||
super(title, desc, "AppearanceOptions.");
|
||||
class AppearanceOptions extends TreeMenuScreen {
|
||||
public override function new() {
|
||||
super('optionsTree.appearance-name', 'optionsTree.appearance-desc', "AppearanceOptions.");
|
||||
|
||||
/*
|
||||
add(new NumOption(
|
||||
getName("framerate"),
|
||||
getDesc("framerate"),
|
||||
@@ -43,13 +45,12 @@ class AppearanceOptions extends OptionsScreen {
|
||||
"gpuOnlyBitmaps"));
|
||||
}
|
||||
#end
|
||||
*/
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -30,13 +30,13 @@ class GameplayOptions extends OptionsScreen {
|
||||
getName("autoPause"),
|
||||
getDesc("autoPause"),
|
||||
"autoPause"));
|
||||
add(new MeterOption(
|
||||
/*add(new MeterOption(
|
||||
getName("volumeSFX"),
|
||||
getDesc("volumeSFX"),
|
||||
0, // minimum
|
||||
1, // maximum
|
||||
0.1, // change
|
||||
"volumeSFX"));
|
||||
"volumeSFX"));*/
|
||||
add(offsetSetting = new NumOption(
|
||||
getName("songOffset"),
|
||||
getDesc("songOffset"),
|
||||
|
||||
@@ -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,57 @@
|
||||
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;
|
||||
|
||||
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 __text:Alphabet;
|
||||
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);
|
||||
public function new(text:String, desc:String, options:Array<Dynamic>, displayOptions:Array<String>, ?optionName:String, ?selectCallback:String->Void = null, ?parent:Dynamic) {
|
||||
super(text, 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.parent = parent = parent != null ? parent : Options;
|
||||
this.optionName = optionName;
|
||||
|
||||
add(__text = new Alphabet(100, 20, text, "bold"));
|
||||
add(__selectiontext = new Alphabet(__text.width + 120, 20, formatTextOption(), "normal"));
|
||||
var fieldValue = Reflect.field(parent, optionName);
|
||||
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() {
|
||||
super.reloadStrings();
|
||||
this.rawText = rawText;
|
||||
}
|
||||
|
||||
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.x = __text.x + __text.width + 12;
|
||||
__selectiontext.text = formatTextOption();
|
||||
Reflect.setField(parent, optionName, options[currentSelection]);
|
||||
if(selectCallback != null)
|
||||
selectCallback(options[currentSelection]);
|
||||
}
|
||||
|
||||
private function formatTextOption() {
|
||||
var currentOptionString = ": ";
|
||||
if((currentSelection > 0))
|
||||
currentOptionString += "< ";
|
||||
else
|
||||
currentOptionString += " ";
|
||||
override function changeSelection(change:Int) {
|
||||
currentSelection = FlxMath.wrap(currentSelection + change, 0, options.length - 1);
|
||||
__selectionText.text = formatTextOption();
|
||||
CoolUtil.playMenuSFX(SCROLL);
|
||||
|
||||
currentOptionString += displayOptions[currentSelection];
|
||||
if (selectCallback != null) selectCallback(options[currentSelection]);
|
||||
}
|
||||
|
||||
if(!(currentSelection >= options.length - 1))
|
||||
currentOptionString += " >";
|
||||
function formatTextOption() {
|
||||
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 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 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 function new(text:String, desc:String) {
|
||||
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) {
|
||||
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() {}
|
||||
}
|
||||
@@ -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