Options Rewrite WIP Appearance Restructure

This commit is contained in:
Ralty
2025-07-26 18:53:25 +07:00
parent f20fc25a82
commit ef6cdd5371
28 changed files with 586 additions and 762 deletions
+21 -11
View File
@@ -12,10 +12,11 @@
<str id="optionsTree.gameplay-name">Gameplay</str>
<str id="optionsTree.gameplay-desc">Change Gameplay options such as Downscroll, Scroll Speed, Naughtyness...</str>
<str id="optionsTree.gameplay.advanced-desc">Change more advanced options such as Window Size, Streaming Audio...</str>
<str id="optionsTree.gameplay.advanced-desc">Change more advanced options such as Hit Window Size, Streaming Audio...</str>
<str id="optionsTree.appearance-name">Appearance</str>
<str id="optionsTree.appearance-desc">Change Appearance options such as Flashing menus...</str>
<str id="optionsTree.appearance.advanced-desc">Change more advanced options such as VRAM-Only Sprites, Antialiasing, Shaders</str>
<str id="optionsTree.language-name">Language</str>
<str id="optionsTree.language-desc">Change the language that the engine uses.</str>
@@ -62,8 +63,15 @@
<str id="framerate-name">Framerate</str>
<str id="framerate-desc">Pretty self explanatory, isn't it?</str>
<str id="antialiasing-name">Antialiasing</str>
<str id="antialiasing-desc">If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites.</str>
<str id="flashingMenu-name">Flashing Menu</str>
<str id="flashingMenu-desc">If unchecked, will disable menu flashing when you select an option in the Main Menu, and other flashes will be slower.</str>
<str id="quality-name">Quality</str>
<str id="quality-desc">Quality modes to choose, "LOW" = Disable every heavy visual effects as it can get to smoothen the gameplay, "HIGH" = Enable all of the visual effects, with the cost of performance, "CUSTOM" = Allow of custom options that is available in Advanced.</str>
<str id="quality-low">LOW</str>
<str id="quality-high">HIGH</str>
<str id="quality-custom">CUSTOM</str>
<str id="quality-optionDisabled-desc">Following option is locked for Quality Option, to unlock set Quality to CUSTOM</str>
<str id="colorHealthBar-name">Colored Healthbar</str>
<str id="colorHealthBar-desc">If unchecked, the game will use the original red/green health bar from the week 6 fnf game.</str>
@@ -71,17 +79,19 @@
<str id="week6PixelPerfect-name">Pixel Perfect Effect</str>
<str id="week6PixelPerfect-desc">If checked, Week 6 will have a pixel perfect effect to it enabled, aligning every pixel on the screen.</str>
<str id="gameplayShaders-name">Gameplay Shaders</str>
<str id="gameplayShaders-desc">If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices.</str>
<group name="AppearanceOptionsAdvanced" prefix="Advanced.">
<str id="antialiasing-name">Antialiasing</str>
<str id="antialiasing-desc">If unchecked, will disable antialiasing on every sprite. Can boost performances at the cost of sharper, more pixely sprites.</str>
<str id="flashingMenu-name">Flashing Menu</str>
<str id="flashingMenu-desc">If unchecked, will disable menu flashing when you select an option in the Main Menu, and other flashes will be slower.</str>
<str id="lowMemoryMode-name">Low Memory Mode</str>
<str id="lowMemoryMode-desc">If checked, will disable certain background elements in stages to reduce memory usage.</str>
<str id="lowMemoryMode-name">Low Memory Mode</str>
<str id="lowMemoryMode-desc">If checked, will disable certain background elements in stages to reduce memory usage.</str>
<str id="gameplayShaders-name">Gameplay Shaders</str>
<str id="gameplayShaders-desc">If unchecked, gameplay shaders (visual effects like Thorns's Chromatic Aberration) wont be loaded; this may be helpful on weak devices.</str>
<str id="gpuOnlyBitmaps-name">VRAM-Only Sprites</str>
<str id="gpuOnlyBitmaps-desc">If checked, will only store the bitmaps in the GPU, freeing a LOT of memory (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on bigger sprites. If you aren't sure, leave this on.</str>
<str id="gpuOnlyBitmaps-name">VRAM-Only Sprites</str>
<str id="gpuOnlyBitmaps-desc">If checked, will only store the bitmaps in the GPU, freeing a LOT of memory (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on bigger sprites. If you aren't sure, leave this on.</str>
</group>
</group>
<group name="GameplayOptions" prefix="GameplayOptions.">
+1 -1
View File
@@ -191,7 +191,7 @@ class Controls extends FlxActionSet
setKeyboardScheme(scheme, false);
}
public function getActionFromControl(control:Control):FLxAction return macro_getActionFromControl(control);
public function getActionFromControl(control:Control):FlxAction return macro_getActionFromControl(control);
public function getKeyName(control:Control, idx:Int = 0):String
{
+1 -63
View File
@@ -10,71 +10,9 @@ class DebugOptions extends TreeMenu {
super.create();
FlxG.camera.fade(0xFF000000, 0.5, true);
var bg:FlxSprite = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
// bg.scrollFactor.set();
bg.scale.set(1.15, 1.15);
bg.updateHitbox();
bg.screenCenter();
bg.scrollFactor.set();
bg.antialiasing = true;
add(bg);
main = new DebugOptionsScreen();
}
}
class DebugOptionsScreen extends OptionsScreen {
public override function new() {
prefix = "DebugOptions.";
super(translate("title"), translate("desc"), prefix);
#if windows
add(new TextOption(
getName("showConsole"),
getDesc("showConsole"),
function() {
NativeAPI.allocConsole();
}));
#end
add(new Checkbox(
getName("editorsResizable"),
getDesc("editorsResizable"),
"editorsResizable"));
add(new Checkbox(
getName("bypassEditorsResize"),
getDesc("bypassEditorsResize"),
"bypassEditorsResize"));
add(new Checkbox(
getName("editorSFX"),
getDesc("editorSFX"),
"editorSFX"));
add(new Checkbox(
getName("editorPrettyPrint"),
getDesc("editorPrettyPrint"),
"editorPrettyPrint"));
add(new Checkbox(
getName("intensiveBlur"),
getDesc("intensiveBlur"),
"intensiveBlur"));
add(new Checkbox(
getName("charterAutoSaves"),
getDesc("charterAutoSaves"),
"charterAutoSaves"));
add(new NumOption(
getName("charterAutoSaveTime"),
getDesc("charterAutoSaveTime"),
60, 60*10, 30,
"charterAutoSaveTime"
));
add(new NumOption(
getName("charterAutoSaveWarningTime"),
getDesc("charterAutoSaveWarningTime"),
0, 15, 1,
"charterAutoSaveWarningTime"
));
add(new Checkbox(
getName("charterAutoSavesSeparateFolder"),
getDesc("charterAutoSavesSeparateFolder"),
"charterAutoSavesSeparateFolder"));
}
}
+6
View File
@@ -9,6 +9,8 @@ class EditorTreeMenu extends TreeMenu {
public var bgType:String = "default";
public var bgMovement:FlxPoint = new FlxPoint();
public var main:OptionsScreen;
public override function create() {
super.create();
@@ -31,4 +33,8 @@ class EditorTreeMenu extends TreeMenu {
public override function exit() {
FlxG.switchState(new MainMenuState());
}
public function onMenuChange() {
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ import flixel.tweens.FlxTween;
import funkin.backend.FunkinText;
import funkin.backend.scripting.events.menu.MenuChangeEvent;
import funkin.backend.scripting.events.NameEvent;
import funkin.menus.credits.CreditsMain;
//import funkin.menus.credits.CreditsMain;
import funkin.options.OptionsMenu;
import lime.app.Application;
@@ -168,7 +168,7 @@ class MainMenuState extends MusicBeatState
{
case 'story mode': FlxG.switchState(new StoryMenuState());
case 'freeplay': FlxG.switchState(new FreeplayState());
case 'donate', 'credits': FlxG.switchState(new CreditsMain()); // kept donate for not breaking scripts, if you don't want donate to bring you to the credits menu, thats easy softcodable - Nex
//case 'donate', 'credits': FlxG.switchState(new CreditsMain()); // kept donate for not breaking scripts, if you don't want donate to bring you to the credits menu, thats easy softcodable - Nex
case 'options': FlxG.switchState(new OptionsMenu());
}
});
+1 -3
View File
@@ -14,7 +14,6 @@ import funkin.backend.utils.FunkinParentDisabler;
import funkin.editors.charter.Charter;
import funkin.menus.StoryMenuState;
import funkin.options.OptionsMenu;
import funkin.options.TreeMenu;
import funkin.options.keybinds.KeybindsOptions;
class PauseSubState extends MusicBeatSubstate
@@ -166,8 +165,7 @@ class PauseSubState extends MusicBeatSubstate
persistentDraw = false;
openSubState(new KeybindsOptions());
case "Change Options":
TreeMenu.lastState = PlayState;
FlxG.switchState(new OptionsMenu());
FlxG.switchState(new OptionsMenu((_) -> FlxG.switchState(new PlayState())));
case "Exit to charter":
FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, false));
case "Exit to menu":
@@ -1,136 +0,0 @@
package funkin.menus.credits;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import funkin.backend.system.github.GitHub;
import funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor;
import funkin.options.PlayerSettings;
import funkin.options.type.GithubIconOption;
using StringTools;
class CreditsCodename extends funkin.options.OptionsScreen {
public var error:Bool = false;
public var totalContributions:Int = 0;
public var contribFormats:Array<FlxTextFormatMarkerPair> = [];
public override function new()
{
super("Codename Engine", TU.translate("credits.allContributors"));
tryUpdating(true);
}
// blame the secondary threads if the code has to look this bad - Nex
private var _canReset:Bool = true;
private var _downloadingSteps:Int = 0;
public override function update(elapsed:Float) {
super.update(elapsed);
if (_downloadingSteps == 2) {
_downloadingSteps = 0;
_canReset = true;
displayList();
} else if (_downloadingSteps == 1) {
_downloadingSteps = 0;
_canReset = true;
updateMenuDesc();
}
else if(_canReset && PlayerSettings.solo.controls.RESET) tryUpdating();
}
public function tryUpdating(forceDisplaying:Bool = false) {
updateMenuDesc(TU.translate("credits.downloadingList"));
_canReset = false;
Main.execAsync(function() {
if(checkUpdate() || forceDisplaying) _downloadingSteps = 2;
else _downloadingSteps = 1;
});
}
public override function updateMenuDesc(?txt:String) {
if(!_canReset) return;
super.updateMenuDesc(txt);
updateMarkup();
}
public function updateMarkup() {
if(parent == null || parent.treeParent == null) return;
var text:String = parent.treeParent.pathDesc.text;
parent.treeParent.pathDesc.text = "";
parent.treeParent.pathDesc.applyMarkup(text, contribFormats = [
new FlxTextFormatMarkerPair(new FlxTextFormat(Flags.MAIN_DEVS_COLOR), '*'),
new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(Flags.MIN_CONTRIBUTIONS_COLOR, Flags.MAIN_DEVS_COLOR, Options.contributors[curSelected].contributions / totalContributions)), '~')
]);
}
override function close() {
super.close();
for (frmt in contribFormats) parent.treeParent.pathDesc.removeFormat(frmt.format);
}
public function checkUpdate():Bool {
var curTime:Float = Date.now().getTime();
if(Options.lastUpdated != null && curTime < Options.lastUpdated + 120000) return false; // Fuck you Github rate limits - Nex
Options.lastUpdated = curTime;
error = false;
var idk = GitHub.getContributors(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) {
error = true;
var errMsg:String = 'Error while trying to download contributors list:\n${CoolUtil.removeIP(e.message)}';
Logs.error(errMsg.replace('\n', ' '));
funkin.backend.utils.NativeAPI.showMessageBox("Codename Engine Warning", errMsg, MSG_WARNING);
});
if(error) return false;
if((idk is Array)) {
var contributors:Array<CreditsGitHubContributor> = [];
for(e in idk) contributors.push({
login: e.login,
avatar_url: e.avatar_url,
html_url: e.html_url,
id: e.id,
contributions: e.contributions
});
Options.contributors = contributors;
}
Logs.verbose('[CreditsCodename] Contributors list Updated!');
var errorOnMain:Bool = false;
var idk2 = GitHub.getOrganizationMembers(Flags.REPO_OWNER, function(e) {
errorOnMain = true;
var errMsg:String = 'Error while trying to download ${Flags.REPO_OWNER} members list:\n${CoolUtil.removeIP(e.message)}';
Logs.error(errMsg.replace('\n', ' '));
funkin.backend.utils.NativeAPI.showMessageBox("Codename Engine Warning", errMsg, MSG_WARNING);
});
if(!errorOnMain) {
Options.mainDevs = [for(m in idk2) m.id];
Logs.verbose('[CreditsCodename] Main Devs list Updated!');
}
return true;
}
public function displayList() {
//if (curSelected > Options.contributors.length - 1) changeSelection(-(curSelected - (Options.contributors.length - 1)));
if (curSelected > Options.contributors.length - 1) curSelected = Options.contributors.length - 1;
changeSelection(0, true);
while (members.length > 0) {
members[0].destroy();
remove(members[0], true);
}
totalContributions = 0;
for(c in Options.contributors) totalContributions += c.contributions;
for(c in Options.contributors) {
var text = TU.translate("credits.totalContributions", [c.contributions, totalContributions, FlxMath.roundDecimal(c.contributions / totalContributions * 100, 2)]);
var opt:GithubIconOption = new GithubIconOption(c, text);
if(Options.mainDevs.contains(c.id)) {
opt.desc += TU.translate("credits.mainDev");
@:privateAccess opt.__text.color = Flags.MAIN_DEVS_COLOR;
}
add(opt);
}
updateMenuDesc();
}
}
-106
View File
@@ -1,106 +0,0 @@
package funkin.menus.credits;
import flixel.util.FlxColor;
import funkin.backend.assets.AssetSource;
import funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor;
import funkin.options.OptionsScreen;
import funkin.options.TreeMenu;
import funkin.options.type.*;
import haxe.xml.Access;
class CreditsMain extends TreeMenu {
var bg:FlxSprite;
var items:Array<OptionType> = [];
public override function create() {
bg = new FlxSprite(-80).loadAnimatedGraphic(Paths.image('menus/menuBGBlue'));
// bg.scrollFactor.set();
bg.scale.set(1.15, 1.15);
bg.updateHitbox();
bg.screenCenter();
bg.scrollFactor.set();
bg.antialiasing = true;
add(bg);
for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) {
var xmlPath = Paths.xml('config/credits/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('[CreditsMain] Error while parsing credits.xml: ${Std.string(e)}', ERROR);
if (access != null) for (c in parseCreditsFromXML(access)) items.push(c);
}
}
items.push(new TextOption("Codename Engine >", TU.translate("credits.selectCodename"), function() {
optionsTree.add(Type.createInstance(CreditsCodename, []));
}));
items.push(new TextOption("Friday Night Funkin'", TU.translate("credits.selectBase"), function() {
CoolUtil.openURL(Flags.URL_FNF_ITCH);
}));
main = new OptionsScreen(TU.translate("credits.name"), TU.translate("credits.madePossible"), items);
super.create();
DiscordUtil.call("onMenuLoaded", ["Credits Menu"]);
}
/**
* XML STUFF
*/
public function parseCreditsFromXML(xml:Access, source:AssetSource = BOTH):Array<OptionType> {
var credsMenus:Array<OptionType> = [];
for(node in xml.elements) {
var desc = node.getAtt("desc").getDefault("No Description");
if (node.name == "github") {
if (!node.has.user) {
Logs.warn("A github node requires a user attribute.", "CreditsMain");
continue;
}
var username = node.getAtt("user");
var user:CreditsGitHubContributor = { // Kind of forcing
login: username,
html_url: 'https://github.com/$username',
avatar_url: 'https://github.com/$username.png'
};
var opt:GithubIconOption = new GithubIconOption(user, desc, null,
node.has.customName ? node.att.customName : null,
node.has.size ? Std.parseInt(node.att.size) : 96,
node.has.portrait ? node.att.portrait.toLowerCase() == "false" ? false : true : true
);
if (node.has.color)
@:privateAccess opt.__text.color = FlxColor.fromString(node.att.color);
credsMenus.push(opt);
} else {
if (!node.has.name) {
Logs.warn("A credit node requires a name attribute.", "CreditsMain");
continue;
}
var name = node.getAtt("name");
switch(node.name) {
case "credit":
var opt:PortraitOption = new PortraitOption(name, desc, function() if(node.has.url) CoolUtil.openURL(node.att.url),
node.has.icon && Paths.assetsTree.existsSpecific(Paths.image('credits/${node.att.icon}'), "IMAGE", source) ?
FlxG.bitmap.add(Paths.image('credits/${node.att.icon}')) : null, node.has.size ? Std.parseInt(node.att.size) : 96,
node.has.portrait ? node.att.portrait.toLowerCase() == "false" ? false : true : true
);
if (node.has.color)
@:privateAccess opt.__text.color = FlxColor.fromString(node.att.color);
credsMenus.push(opt);
case "menu":
credsMenus.push(new TextOption(name + " >", desc, function() {
optionsTree.add(new OptionsScreen(name, desc, parseCreditsFromXML(node, source)));
}));
}
}
}
return credsMenus;
}
}
+5
View File
@@ -0,0 +1,5 @@
package funkin.menus.ui;
class Slider extends FlxSprite {
}
+14 -1
View File
@@ -44,6 +44,7 @@ class Options
public static var language = "en"; // default to english, Flags.DEFAULT_LANGUAGE should not modify this
public static var streamedMusic:Bool = true;
public static var streamedVocals:Bool = true;
public static var quality:Int = 1;
#if MODCHARTING_FEATURES
public static var modchartingHoldSubdivisions:Int = 4;
#end
@@ -207,7 +208,19 @@ class Options
public static function applySettings() {
applyKeybinds();
FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? LOW : BEST;
switch (quality) {
case 0:
antialiasing = false;
lowMemoryMode = true;
gameplayShaders = false;
case 1:
antialiasing = true;
lowMemoryMode = false;
gameplayShaders = true;
}
FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? BEST : LOW;
FlxG.autoPause = autoPause;
if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
else FlxG.updateFramerate = FlxG.drawFramerate = framerate;
+98 -9
View File
@@ -1,12 +1,15 @@
package funkin.options;
import haxe.xml.Access;
import flixel.util.typeLimit.OneOfThree;
import funkin.editors.ui.UIState;
import funkin.options.categories.*;
import funkin.options.type.*;
typedef OptionCategory = {
var name:String;
var desc:String;
var ?state:OneOfThree<OptionsScreen, Class<OptionsScreen>, (name:String, desc:String) -> OptionsScreen>;
var ?state:OneOfThree<TreeMenuScreen, Class<TreeMenuScreen>, (name:String, desc:String) -> TreeMenuScreen>;
var ?substate:OneOfThree<MusicBeatSubstate, Class<MusicBeatSubstate>, (name:String, desc:String) -> MusicBeatSubstate>;
var ?suffix:String;
}
@@ -16,8 +19,8 @@ class OptionsMenu extends TreeMenu {
{ // name and desc are actually the translations ids! - Nex
name: 'optionsTree.controls-name',
desc: 'optionsTree.controls-desc',
suffix: '';
substate: funkin.options.keybinds.KeybindsOptions;
suffix: '',
substate: funkin.options.keybinds.KeybindsOptions
},
{
name: 'optionsTree.gameplay-name',
@@ -43,7 +46,7 @@ class OptionsMenu extends TreeMenu {
}
];
var bg:FunkinSprite;
var bg:FlxSprite;
override function create() {
super.create();
@@ -52,23 +55,54 @@ class OptionsMenu extends TreeMenu {
DiscordUtil.call("onMenuLoaded", ["Options Menu"]);
add(bg = new FunkinSprite().loadAnimatedGraphic(Paths.image('menus/menuBGBlue')));
add(bg = new FlxSprite().loadAnimatedGraphic(Paths.image('menus/menuBGBlue')));
bg.antialiasing = true;
bg.scrollFactor.set();
updateBG();
/*tree = [
for (o in mainOptions) {
addMenu(new TreeMenuScreen('optionsMenu.header.title', 'optionsMenu.header.desc', [for (o in mainOptions) new TextOption(o.name, o.desc, o.suffix != null ? o.suffix : " >", () -> {
if (o.substate != null) {
persistentUpdate = false;
persistentDraw = true;
if (o.substate is MusicBeatSubstate)
openSubState(o.substate);
else if(Reflect.isFunction(o.substate)) {
var substate:(name:String, desc:String) -> MusicBeatSubstate = o.substate;
openSubState(substate(o.name, o.desc));
}
else // o.substate is Class<TreeMenuScreen>
openSubState(Type.createInstance(o.substate, [o.name, o.desc]));
}
];*/
else {
if (o.state is TreeMenuScreen)
addMenu(o.state);
else if (Reflect.isFunction(o.state)) {
var state:(name:String, desc:String) -> TreeMenuScreen = o.state;
addMenu(state(o.name, o.desc));
}
else { // o.state is Class<TreeMenuScreen>
addMenu(Type.createInstance(o.state, [o.name, o.desc]));
}
}
})]));
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 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.scale.x = bg.scale.y = Math.max(scaleX, scaleY) * 1.15;
bg.screenCenter();
}
@@ -84,4 +118,59 @@ class OptionsMenu extends TreeMenu {
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 TreeMenuScreen(name, desc, parseOptionsFromXML(node)));
}));
}
}
return options;
}
*/
}
+5 -3
View File
@@ -1,3 +1,5 @@
// REMOVE THIS WHEN COMPLETE
package funkin.options;
import funkin.options.type.OptionType;
@@ -92,13 +94,13 @@ class OptionsScreen extends FlxTypedSpriteGroup<OptionType> {
if (members.length > 0) {
members[curSelected].selected = true;
if (controls.ACCEPT || (FlxG.mouse.justReleased && Main.timeSinceFocus > 0.25)) {
members[curSelected].onSelect();
members[curSelected].select();
onSelect(members[curSelected]);
}
if (controls.LEFT_P)
members[curSelected].onChangeSelection(-1);
members[curSelected].changeSelection(-1);
if (controls.RIGHT_P)
members[curSelected].onChangeSelection(1);
members[curSelected].changeSelection(1);
}
if (controls.BACK || FlxG.mouse.justReleasedRight)
close();
+91
View File
@@ -0,0 +1,91 @@
// REMOVE THIS WHEN COMPLETE
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) {
}
}
+88 -67
View File
@@ -1,6 +1,10 @@
package funkin.options;
import flixel.tweens.FlxTween;
import flixel.util.FlxArrayUtil;
import flixel.util.FlxColor;
import flixel.util.FlxSignal;
import funkin.backend.FunkinText;
import funkin.backend.system.framerate.Framerate;
import funkin.editors.ui.UIState;
@@ -8,7 +12,8 @@ interface ITreeOption {
var desc:String;
var selected:Bool;
function changeSelection(change:Int);
function reloadStrings():Void;
function changeSelection(change:Int):Void;
function select():Void;
}
@@ -16,9 +21,9 @@ class TreeMenu extends UIState {
public var onMenuClosed:FlxTypedSignal<TreeMenuScreen->Void> = new FlxTypedSignal();
public var onMenuChanged:FlxTypedSignal<TreeMenuScreen->Void> = new FlxTypedSignal();
public var tree(default, null):Array<TreeMenuScreen>;
public var length(default, null):Int = 0;
public var lastMenu:Null<TreeMenuScreen>;
public var tree(default, null):Array<TreeMenuScreen> = [];
public var treeLength(default, null):Int = 0;
public var previousMenus:Array<TreeMenuScreen> = [];
public var destroyMenus:Bool = true;
public var exitCallback:TreeMenu->Void;
@@ -27,10 +32,13 @@ class TreeMenu extends UIState {
public var descLabel:FunkinText;
public var bgLabel:FlxSprite;
var menuChangeTween:FlxTween;
var _drawer:TreeMenuDrawer;
public var optionsTree:OptionsTree;
public function new(exitCallback:TreeMenu->Void,
var menuChangeTween:FlxTween;
var __drawer:TreeMenuDrawer;
var __treeCreated:Bool = false;
public function new(?exitCallback:TreeMenu->Void,
scriptsAllowed:Bool = true, ?scriptName:String)
{
super(scriptsAllowed, scriptName);
@@ -44,40 +52,44 @@ class TreeMenu extends UIState {
bgLabel.alpha = 0.25;
bgLabel.scrollFactor.set();
titleLabel = new FunkinText(4, 4, FlxG.camera.width - 8, 32);
titleLabel = new FunkinText(4, 4, FlxG.width - 8, 32);
titleLabel.borderSize = 1.25;
titleLabel.scrollFactor.set();
descLabel = new FunkinText(4, 0, FlxG.camera.width - 8, 16);
descLabel = new FunkinText(4, 0, FlxG.width - 8, 16);
descLabel.scrollFactor.set();
}
override function createPost() {
super.createPost();
if ((length = tree.length) != 0) {
if ((treeLength = 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"));
addMenu(new TreeMenuScreen("Fallback TreeMenuScreen", "Please insert menus into \"tree\" variable in your extended class in create or before createPost"));
add(_drawer = new TreeMenuDrawer(this));
add(__drawer = new TreeMenuDrawer(this));
add(bgLabel);
add(titleLabel);
add(descLabel);
updateLabels();
__treeCreated = true;
}
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;
var last = tree[fromIndex - 1], menu:TreeMenuScreen;
while (fromIndex < treeLength) if ((menu = tree[fromIndex++]) != null) {
menu.x = last == null ? 0 : last.x + Math.max(FlxG.width, last.width);
(last = menu).parent = this;
}
if (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel();
if (cameraScroll) FlxG.camera.scroll.x = lastMenu.x;
if (cameraScroll && last != null) {
if (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel();
FlxG.camera.scroll.x = last.x;
}
}
public function updateLabels() {
@@ -96,100 +108,102 @@ class TreeMenu extends UIState {
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;
descLabel.text += "\n" + cast(last.members[last.curSelected], ITreeOption).desc;
bgLabel.scale.set(FLxG.width, descLabel.y + descLabel.height + 2);
bgLabel.scale.set(FlxG.width, descLabel.y + descLabel.height + 2);
bgLabel.updateHitbox();
Framerate.offset.y = bgLabel.height + 2;
}
public function add(menu:TreeMenuScreen):TreeMenuScreen {
public function addMenu(menu:TreeMenuScreen):TreeMenuScreen {
if (menu == null) return null;
if (tree.indexOf(menu) != -1) return menu;
tree.push(menu);
if (!__treeCreated) return menu;
menu.parent = this;
length++;
treeLength++;
var prev = tree[length - 1];
menu.x = prev == null ? 0 : prev.x + Math.max(FlxG.camera.width, prev.width);
var prev = tree[treeLength - 2];
menu.x = prev == null ? 0 : prev.x + Math.max(FlxG.width, prev.width);
destroyPreviousMenus();
menuChanged();
menu.inputEnabled = true;
prev.inputEnabled = false;
if (prev != null) prev.inputEnabled = false;
return menu;
}
public function insert(position:Int, menu:TreeMenuScreen):TreeMenuScreen {
public function insertMenu(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);
if (position < 0) position = treeLength - ((-position - 1) % treeLength);
tree.insert(position, menu);
if (!__treeCreated) return menu;
menu.parent = this;
var lastChanged = position >= length++;
var lastChanged = position >= treeLength++;
updateMenuPositions(position, !lastChanged);
if (lastChanged) {
destroyPreviousMenus();
menuChanged();
menu.inputEnabled = true;
tree[length - 1].inputEnabled = false;
if (treeLength > 0) tree[treeLength - 2].inputEnabled = false;
}
return menu;
}
public function pop():TreeMenuScreen {
(lastMenu = tree.pop(position)).parent = null;
length--;
public function popMenu():TreeMenuScreen
return removeMenuPosition(treeLength - 1);
menuChanged();
lastMenu.inputEnabled = false;
menu.inputEnabled = true;
public function removeMenu(menu:TreeMenuScreen):TreeMenuScreen
return if (menu == null) null; else removeMenuPosition(tree.indexOf(menu));
return lastMenu;
}
public function removeMenuPosition(position:Int):TreeMenuScreen {
if (position < 0 || position >= treeLength || treeLength == 0) return null;
public function remove(menu:TreeMenuScreen):TreeMenuScreen {
return if (menu == null) null; else removePosition(tree.indexOf(menu));
}
tree[position] = tree[tree.length - 1];
var menu = tree.pop();
menu.parent = null;
public function removePosition(position:Int):TreeMenuScreen {
if (position < 0 || position >= length) return null;
if (!__treeCreated) return menu;
destroyLastMenu();
(lastMenu = tree.swapAndPop(position)).parent = null;
if (position == --length) {
previousMenus.push(menu);
if (position == --treeLength) {
menuChanged();
lastMenu.inputEnabled = false;
tree[length - 1].inputEnabled = true;
menu.inputEnabled = false;
if (treeLength > 0) tree[treeLength - 1].inputEnabled = true;
}
else {
updateMenuPositions(position, true);
updateLabels();
}
return lastMenu;
onMenuClosed.dispatch(menu);
return menu;
}
public function menuChanged() {
if (length == 0) exit();
if (treeLength == 0) exit();
else {
updateLabels();
if (menuChangeTween != null && menuChangeTween.active) menuChangeTween.cancel();
menuChangeTween = FlxTween.tween(FlxG.camera.scroll, {x: tree.last().x}, 1.5, {ease: menuTransitionEase, onComplete: (t) -> {
for (menu in tree) if (menu != null) menu.transitioning = false;
lastMenu.transitioning = false;
for (menu in previousMenus) if (menu != null) menu.transitioning = false;
}});
for (menu in tree) if (menu != null) menu.transitioning = true;
lastMenu.transitioning = true;
for (menu in previousMenus) if (menu != null) menu.transitioning = true;
}
}
@@ -197,14 +211,18 @@ class TreeMenu extends UIState {
super.update(elapsed);
var i = 0, menu:TreeMenuScreen;
while (i < length) {
while (i < treeLength) {
if ((menu = tree[i++]) == null || !menu.active || !menu.exists) continue;
if (i == length || menu.persistentUpdate || menu.transitioning) menu.update(elapsed);
if (i == treeLength || menu.persistentUpdate || menu.transitioning) menu.update(elapsed);
}
if (lastMenu != null) {
if (lastMenu.transitioning) lastMenu.update(elapsed);
else destroyLastMenu();
i = previousMenus.length;
while (i-- > 0) {
if ((menu = previousMenus[i]).transitioning) menu.update(elapsed);
else {
menu.destroy();
FlxArrayUtil.swapAndPop(previousMenus, i);
}
}
// in case path gets so long it goes offscreen, ALTHOUGH this nevers happens anyway since we have set a expected width to the label.
@@ -213,7 +231,7 @@ class TreeMenu extends UIState {
override function destroy() {
super.destroy();
destroyLastMenu();
destroyPreviousMenus();
}
override function onResize(width:Int, height:Int) {
@@ -226,21 +244,23 @@ class TreeMenu extends UIState {
}
updateMenuPositions(true);
descLabel.width = titleLabel.width = (bgLabel.width = width) - 8;
descLabel.width = titleLabel.width = (bgLabel.scale.x = width) - 8;
bgLabel.updateHitbox();
}
public function reloadStrings() {
for (menu in tree) if (menu != null) menu.reloadStrings();
}
public function destroyLastMenu() {
lastMenu = destroyMenus ? FlxDestroyUtil.destroy(lastMenu) : null;
public function destroyPreviousMenus() {
for (menu in previousMenus) menu.destroy();
previousMenus.resize(0);
}
public function exit() {
if (exitCallback != null) return exitCallback(this);
FlxG.switchState(new MainMenuState());
FlxG.switchState(new funkin.menus.MainMenuState());
}
public function updateAll(elapsed:Float) {
@@ -253,17 +273,18 @@ class TreeMenu extends UIState {
final class TreeMenuDrawer extends FlxBasic {
public var parent:TreeMenu;
public function new(parent:TreeMenu) {
super();
this.parent = parent;
moves = false;
}
override function draw() {
var i = 0, menu:TreeMenuScreen;
while (i < parent.length) {
while (i < parent.treeLength) {
if ((menu = parent.tree[i++]) == null || !menu.active || !menu.exists) continue;
if (i == length || menu.persistentUpdate || menu.transitioning) menu.update(elapsed);
if (i == parent.treeLength || menu.persistentUpdate || menu.transitioning) menu.draw();
}
if (parent.lastMenu != null) parent.lastMenu.draw();
i = parent.previousMenus.length;
while (i-- > 0) if (parent.previousMenus[i] != null) parent.previousMenus[i].draw();
}
}
+55 -27
View File
@@ -1,9 +1,11 @@
package funkin.options;
import flixel.group.FlxSpriteGroup;
import flixel.util.FlxSignal;
import funkin.backend.system.Controls;
import funkin.backend.TurboControls;
import funkin.options.TreeMenu.ITreeOption;
import funkin.options.type.Separator;
class TreeMenuScreen extends FlxSpriteGroup {
public var persistentUpdate:Bool = false;
@@ -15,7 +17,6 @@ class TreeMenuScreen extends FlxSpriteGroup {
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;
@@ -28,74 +29,97 @@ class TreeMenuScreen extends FlxSpriteGroup {
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;
rawName = v;
name = TU.exists(v) ? TU.translate(v) : v;
return v;
}
function set_rawDesc(v:String) {
if (rawDesc == (rawDesc = v)) return v;
desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
rawDesc = v;
desc = TU.exists(v) ? TU.translate(v) : v;
return v;
}
public inline function getNameID(name):String return prefix + name + "-name";
public inline function getDescID(name):String return prefix + name + "-desc";
public inline function getID(name):String return prefix + name;
public function translate(name:String, ?args:Array<Dynamic>):String return TU.translate(getID(name), args);
public var controls(get, never):Controls;
inline function get_controls():Controls return PlayerSettings.solo.controls;
var leftTurboControl:TurboControls = new TurboControls([Control.LEFT]);
var rightTurboControl:TurboControls = new TurboControls([Control.RIGHT]);
var leftTurboControl:TurboControls = new TurboControls([Control.LEFT], 0.2, 1 / 30);
var rightTurboControl:TurboControls = new TurboControls([Control.RIGHT], 0.2, 1 / 30);
var upTurboControl:TurboControls = new TurboControls([Control.UP]);
var downTurboControl:TurboControls = new TurboControls([Control.DOWN]);
var turboBasics:Array<TurboBasics>;
var turboBasics:Array<TurboBasic>;
var curOption:ITreeOption;
var _firstFrame:Bool = true;
var __firstFrame:Bool = true;
public function new(name:String, desc:String, ?prefix:String, ?options:Array<OptionType>) {
public function new(name:String, desc:String, ?prefix:String, ?objects:Array<FlxSprite>) {
super();
this.prefix = prefix;
rawName = name;
rawDesc = desc;
turboBasics = [leftTurboControl, rightTurboControl, upTurboControl, downTurboControl];
if (objects != null) for (object in objects) add(object);
}
public function reloadStrings() {
name = TU.exists(rawName) ? TU.translate(rawName) : rawName;
desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc;
rawName = rawName;
rawDesc = rawDesc;
for (object in members) if (object != null) object.reloadStrings();
for (object in members) if (object != null && object is ITreeOption) cast(object, ITreeOption).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 (__firstFrame) {
__firstFrame = false;
if (members[curSelected] is ITreeOption) (curOption = cast members[curSelected]).selected = true;
updateItems(true);
return;
}
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 (length > 0 && curOption != null) {
if (controls.ACCEPT || (FlxG.mouse.justPressed && FlxG.mouse.overlaps(members[curSelected]))) curOption.select();
if (leftTurboControl.activated) curOption.changeSelection(-1);
if (rightTurboControl.activated) curOption.changeSelection(1);
}
if (controls.BACK || FlxG.mouse.justPressedRight) close();
if (controls.BACK || (FlxG.mouse.justPressedRight && Main.timeSinceFocus > 0.3)) close();
}
updateItems();
}
public function updateItems(force = false) {
var r = force ? 1 : 0.25, initY = FlxG.height * 0.5;
var i = curSelected, y = initY, object:FlxSprite = null, itemHeight:Float = 0;
inline function updateItem() {
object.y = CoolUtil.fpsLerp(object.y, y - (itemHeight = object.height) * 0.5, r);
object.x = x + 100 - Math.pow(Math.abs((object.y - (FlxG.height - itemHeight) * 0.5) / itemHeight / FlxG.height * FlxG.initialHeight), 1.6) * 15;
}
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;
updateItem();
y += itemHeight;
}
y = initY;
i = curSelected;
while (i-- > 0) if ((object = members[i]) != null) {
y -= itemHeight;
updateItem();
}
}
@@ -103,7 +127,7 @@ class TreeMenuScreen extends FlxSpriteGroup {
onClose.dispatch();
if (parent == null) return destroy();
else parent.remove(this);
else parent.removeMenu(this);
CoolUtil.playMenuSFX(CANCEL);
}
@@ -111,8 +135,12 @@ class TreeMenuScreen extends FlxSpriteGroup {
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;
var prevSelect = curSelected = FlxMath.wrap(curSelected + change, 0, members.length - 1);
while (members[curSelected] is Separator)
if ((curSelected = FlxMath.wrap(curSelected + (change > 0 ? 1 : -1), 0, members.length - 1)) == prevSelect) break;
if (curOption != null) curOption.selected = false;
if (members[curSelected] is ITreeOption) (curOption = cast members[curSelected]).selected = true;
updateMenuDesc();
CoolUtil.playMenuSFX(SCROLL);
@@ -1,51 +1,24 @@
package funkin.options.categories;
class AppearanceOptions extends TreeMenuScreen {
public override function new() {
public function new() {
super('optionsTree.appearance-name', 'optionsTree.appearance-desc', "AppearanceOptions.");
/*
add(new NumOption(
getName("framerate"),
getDesc("framerate"),
30, // minimum
240, // maximum
10, // change
"framerate", // save name or smth
__changeFPS)); // callback
add(new Checkbox(
getName("antialiasing"),
getDesc("antialiasing"),
"antialiasing"));
add(new Checkbox(
getName("colorHealthBar"),
getDesc("colorHealthBar"),
"colorHealthBar"));
add(new Checkbox(
getName("week6PixelPerfect"),
getDesc("week6PixelPerfect"),
"week6PixelPerfect"));
add(new Checkbox(
getName("gameplayShaders"),
getDesc("gameplayShaders"),
"gameplayShaders"));
add(new Checkbox(
getName("flashingMenu"),
getDesc("flashingMenu"),
"flashingMenu"));
add(new Checkbox(
getName("lowMemoryMode"),
getDesc("lowMemoryMode"),
"lowMemoryMode"));
#if sys
if (!Main.forceGPUOnlyBitmapsOff) {
add(new Checkbox(
getName("gpuOnlyBitmaps"),
getDesc("gpuOnlyBitmaps"),
"gpuOnlyBitmaps"));
}
#end
*/
add(new NumOption(getNameID('framerate'), getDescID('framerate'),
30, 240, 1,
'framerate', __changeFPS
));
add(new Checkbox(getNameID('flashingMenu'), getDescID('flashingMenu'), 'flashingMenu'));
add(new ArrayOption(getNameID('quality'), getDescID('quality'),
[0, 1, 2], [getID('quality-low'), getID('quality-high'), getID('quality-custom')],
'quality', __changeQuality, null
));
add(new Checkbox(getNameID('colorHealthBar'), getDescID('colorHealthBar'), 'colorHealthBar'));
add(new Checkbox(getNameID('week6PixelPerfect'), getDescID('week6PixelPerfect'), 'week6PixelPerfect'));
add(new Separator());
add(new TextOption('optionsMenu.advanced', 'optionsTree.appearance.advanced-desc', ' >', () ->
parent.addMenu(new AdvancedAppearanceOptions())));
}
private function __changeFPS(value:Float) {
@@ -53,4 +26,30 @@ class AppearanceOptions extends TreeMenuScreen {
if (FlxG.updateFramerate < framerate) FlxG.drawFramerate = FlxG.updateFramerate = framerate;
else FlxG.updateFramerate = FlxG.drawFramerate = framerate;
}
private function __changeQuality(value:Dynamic) {
var antialiasing = value == 0 ? false : (value == 1 ? true : Options.antialiasing);
FlxG.game.stage.quality = (FlxG.enableAntialiasing = antialiasing) ? BEST : LOW;
}
}
class AdvancedAppearanceOptions extends TreeMenuScreen {
public function new() {
super('optionsMenu.advanced', 'optionsTree.appearance.advanced-desc', "AppearanceOptions.Advanced.");
for (option in [
new Checkbox(getNameID('antialiasing'), getDescID('antialiasing'), 'antialiasing', __changeAntialiasing),
new Checkbox(getNameID('lowMemoryMode'), getDescID('lowMemoryMode'), 'lowMemoryMode'),
new Checkbox(getNameID('gameplayShaders'), getDescID('gameplayShaders'), 'gameplayShaders')
]) {
add(option);
if (option.locked = Options.quality != 2) option.rawDesc = 'AppearanceOptions.quality-optionDisabled-desc';
}
add(new Checkbox(getNameID('gpuOnlyBitmaps'), getDescID('gpuOnlyBitmaps'), 'gpuOnlyBitmaps'));
}
private function __changeAntialiasing() {
FlxG.game.stage.quality = (FlxG.enableAntialiasing = Options.antialiasing) ? BEST : LOW;
}
}
@@ -3,96 +3,8 @@ package funkin.options.categories;
import flixel.util.FlxTimer;
import funkin.backend.system.Conductor;
class GameplayOptions extends OptionsScreen {
var __metronome = FlxG.sound.load(Paths.sound('editors/charter/metronome'));
var offsetSetting:NumOption;
public override function new(title:String, desc:String) {
super(title, desc, "GameplayOptions.");
add(new Checkbox(
getName("downscroll"),
getDesc("downscroll"),
"downscroll"));
add(new Checkbox(
getName("ghostTapping"),
getDesc("ghostTapping"),
"ghostTapping"));
add(new Checkbox(
getName("naughtyness"),
getDesc("naughtyness"),
"naughtyness"));
add(new Checkbox(
getName("camZoomOnBeat"),
getDesc("camZoomOnBeat"),
"camZoomOnBeat"));
add(new Checkbox(
getName("autoPause"),
getDesc("autoPause"),
"autoPause"));
/*add(new MeterOption(
getName("volumeSFX"),
getDesc("volumeSFX"),
0, // minimum
1, // maximum
0.1, // change
"volumeSFX"));*/
add(offsetSetting = new NumOption(
getName("songOffset"),
getDesc("songOffset"),
-999, // minimum
999, // maximum
1, // change
"songOffset", // save name or smth
__changeOffset)); // callback
add(new TextOption('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc', ' >', () ->
parent.add(new AdvancedGameplayOptions('optionsMenu.advanced', 'optionsTree.gameplay.advanced-desc'))));
}
private function __changeOffset(offset)
Conductor.songOffset = offset;
var __lastBeat:Int = 0;
var __lastSongBeat:Int = 0;
override function update(elapsed) {
super.update(elapsed);
FlxG.camera.zoom = CoolUtil.fpsLerp(FlxG.camera.zoom, 1, 0.04);
if (offsetSetting.selected) {
FlxG.sound.music.volume = 0.5;
if (__lastBeat != Conductor.curBeat) {
FlxG.camera.zoom += 0.03;
__lastBeat = Conductor.curBeat;
}
var beat = Math.floor(Conductor.getTimeInBeats(FlxG.sound.music.time));
if (__lastSongBeat != beat) {
__metronome.replay();
__lastSongBeat = beat;
}
}
else
FlxG.sound.music.volume = 1;
}
override function close() {
FlxG.camera.zoom = 1;
FlxG.sound.music.volume = 1;
super.close();
}
class GameplayOptions extends TreeMenuScreen {
}
class AdvancedGameplayOptions extends OptionsScreen {
public override function new(title:String, desc:String) {
super(title, desc, "GameplayOptions.Advanced.");
add(new Checkbox(
getName("streamedMusic"),
getDesc("streamedMusic"),
"streamedMusic"));
add(new Checkbox(
getName("streamedVocals"),
getDesc("streamedVocals"),
"streamedVocals"));
}
}
@@ -33,7 +33,7 @@ class LanguageRadio extends RadioButton {
}
}
class LanguageOptions extends OptionsScreen {
class LanguageOptions extends TreeMenuScreen {
public override function new(title:String, desc:String) {
super(title, desc, "LanguageOptions.");
@@ -1,35 +1,4 @@
package funkin.options.categories;
class MiscOptions extends OptionsScreen {
public override function new(title:String, desc:String) {
super(title, desc, "MiscOptions.");
add(new Checkbox(
getName("devMode"),
getDesc("devMode"),
"devMode"));
#if UPDATE_CHECKING
add(new Checkbox(
getName("betaUpdates"),
getDesc("betaUpdates"),
"betaUpdates"));
add(new TextOption(
getName("checkForUpdates"),
getDesc("checkForUpdates"),
function() {
var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates();
if (report.newUpdate) {
FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
} else {
CoolUtil.playMenuSFX(CANCEL);
updateDescText(TU.translate(prefix + "checkForUpdates-noUpdateFound"));
}
}));
#end
add(new TextOption(
getName("resetSaveData"),
getDesc("resetSaveData"),
function() {
// TODO: SAVE DATA RESETTING
}));
}
class MiscOptions extends TreeMenuScreen {
}
+28 -20
View File
@@ -1,7 +1,7 @@
package funkin.options.type;
class ArrayOption extends OptionType {
public var selectCallback:String->Void;
class ArrayOption extends TextOption {
public var changedCallback:String->Void;
public var options:Array<Dynamic>;
public var displayOptions:Array<String>;
@@ -10,36 +10,32 @@ class ArrayOption extends OptionType {
public var parent:Dynamic;
public var optionName:String;
var __text:Alphabet;
var __selectionText:Alphabet;
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;
override function set_text(v:String) {
super.set_text(v);
__selectionText.x = __text.x + __text.width + 12;
return v;
}
public function new(text:String, desc:String, options:Array<Dynamic>, displayOptions:Array<String>, ?optionName:String, ?changedCallback:Dynamic->Void = null, ?parent:Dynamic) {
this.changedCallback = changedCallback;
this.displayOptions = displayOptions;
this.options = options;
this.parent = parent = parent != null ? parent : Options;
this.optionName = optionName;
this.parent = parent = parent != null ? parent : Options;
var fieldValue = Reflect.field(parent, optionName);
if (fieldValue != null) currentSelection = CoolUtil.maxInt(0, options.indexOf(fieldValue));
add(__text = new Alphabet(20, 0, text, 'bold'));
add(__selectionText = new Alphabet(__text.x + __text.width + 12, 0, formatTextOption(), 'bold'));
__selectionText = new Alphabet(0, 20, formatTextOption(), 'bold');
super(text, desc);
add(__selectionText);
}
override function reloadStrings() {
super.reloadStrings();
__selectiontext.x = __text.x + __text.width + 12;
__selectiontext.text = formatTextOption();
}
override function changeSelection(change:Int) {
currentSelection = FlxMath.wrap(currentSelection + change, 0, options.length - 1);
__selectionText.text = formatTextOption();
CoolUtil.playMenuSFX(SCROLL);
if (selectCallback != null) selectCallback(options[currentSelection]);
super.reloadStrings();
}
function formatTextOption() {
@@ -54,4 +50,16 @@ class ArrayOption extends OptionType {
return s;
}
override function changeSelection(change:Int) {
if (locked) return;
currentSelection = FlxMath.wrap(currentSelection + change, 0, options.length - 1);
__selectionText.text = formatTextOption();
CoolUtil.playMenuSFX(SCROLL);
if (optionName != null) Reflect.setField(parent, optionName, options[currentSelection]);
if (changedCallback != null) changedCallback(options[currentSelection]);
}
override function select() {}
}
+28 -41
View File
@@ -10,25 +10,21 @@ class Checkbox extends TextOption {
public var checked(default, set):Bool;
public var parent:Dynamic;
public var optionName:String;
private var offsets:Map<String, FlxPoint> = [
"unchecked" => FlxPoint.get(0, -70),
"checked" => FlxPoint.get(23, -30),
"checked" => FlxPoint.get(23, -32),
"unchecking" => FlxPoint.get(25, -12),
"checking" => FlxPoint.get(35, 31)
"checking" => FlxPoint.get(35, 29)
];
public function new(text:String, desc:String, optionName:String, ?parent:Dynamic) {
super(text, desc, null);
public function new(text:String, desc:String, optionName:String, ?selectCallback:Void->Void = null, ?parent:Dynamic) {
super(text, desc);
this.optionName = optionName;
this.parent = parent = parent != null ? parent : Options;
if (parent == null)
parent = Options;
this.parent = parent;
checkbox = new FlxSprite(10, -40);
add(checkbox = new FlxSprite(10, -30));
checkbox.frames = Paths.getFrames('menus/options/checkboxThingie');
checkbox.animation.addByPrefix("unchecked", "Check Box unselected0", 24);
checkbox.animation.addByPrefix("checked", "Check Box Selected Static0", 24);
@@ -37,61 +33,52 @@ class Checkbox extends TextOption {
checkbox.antialiasing = true;
checkbox.scale.set(0.75, 0.75);
checkbox.updateHitbox();
add(checkbox);
this.optionName = optionName;
if(optionName != null) {
checked = Reflect.field(parent, optionName);
} else {
checked = false;
}
__text.x = 100;
var fieldValue = Reflect.field(parent, optionName);
checked = fieldValue != null && fieldValue;
}
public var firstFrame:Bool = true;
public override function update(elapsed:Float) {
if(checkbox.animation.curAnim == null) {
checkbox.animation.play(checked ? "checked" : "unchecked", true);
}
if (checkbox.animation.curAnim == null) checkbox.animation.play(checked ? "checked" : "unchecked", true);
super.update(elapsed);
switch(checkbox.animation.curAnim.name) {
case "unchecking":
if(checkbox.animation.curAnim.finished) {
checkbox.animation.play("unchecked", true);
}
case "checking":
if(checkbox.animation.curAnim.finished) {
checkbox.animation.play("checked", true);
}
if (checkbox.animation.curAnim.finished) switch(checkbox.animation.curAnim.name) {
case "unchecking": checkbox.animation.play("unchecked", true);
case "checking": checkbox.animation.play("checked", true);
}
firstFrame = false;
}
override function draw() {
if(checkbox.animation.curAnim != null) {
if (checkbox.animation.curAnim != null) {
var offset = offsets[checkbox.animation.curAnim.name];
if (offset != null)
checkbox.frameOffset.set(offset.x, offset.y);
if (offset != null) checkbox.frameOffset.set(offset.x, offset.y);
}
super.draw();
}
function set_checked(v:Bool) {
if(checked != v) {
if (checked != v) {
checked = v;
if(!firstFrame) {
checkbox.animation.play(checked ? "checking" : "unchecking", true);
}
if (!firstFrame) checkbox.animation.play(checked ? "checking" : "unchecking", true);
}
return v;
}
public override function onSelect() {
public override function select() {
if (locked) return;
checked = !checked;
if(optionName != null) {
Reflect.setField(parent, optionName, checked);
}
if (optionName != null) Reflect.setField(parent, optionName, checked);
if (checked) CoolUtil.playMenuSFX(CHECKED);
else CoolUtil.playMenuSFX(UNCHECKED);
if (selectCallback != null) selectCallback();
}
public override function destroy() {
+3 -13
View File
@@ -20,11 +20,7 @@ class MeterOption extends OptionType {
public var barWidth(default, set):Int;
private var rawText(default, set):String;
public var text(get, set):String;
private function get_text() {return __text.text;}
private function set_text(v:String) {return __text.text = v;}
override function set_text(v:String) return __text.text = text = v;
private var __text:Alphabet;
var optionName:String;
@@ -43,7 +39,7 @@ class MeterOption extends OptionType {
public function new(text:String, desc:String, min:Float, max:Float, changeVal:Float, optionName:String, barWidth:Int = 520,
?selectCallback:Float->Void = null, ?parent:Dynamic)
{
super(desc);
super(text, desc);
this.selectCallback = selectCallback;
this.min = min;
@@ -162,12 +158,6 @@ class MeterOption extends OptionType {
x += temp * barScale;
}
function set_rawText(v:String) {
rawText = v;
__text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
return v;
}
override function draw() {
lastX = x;
@@ -196,7 +186,7 @@ class MeterOption extends OptionType {
this.rawText = rawText;
}
override function onChangeSelection(change:Float) {
override function changeSelection(change:Float) {
if (currentSelection <= min && change == -1 || currentSelection >= max && change == 1) return;
currentSelection = FlxMath.roundDecimal(currentSelection + (change * changeVal), FlxMath.getDecimals(changeVal));
+31 -49
View File
@@ -1,72 +1,54 @@
package funkin.options.type;
import flixel.effects.FlxFlicker;
/**
* Option type that allows stepping through a number.
**/
class NumOption extends OptionType {
public var selectCallback:Float->Void;
private var __text:Alphabet;
private var __number:Alphabet;
class NumOption extends TextOption {
public var changedCallback:Float->Void;
public var min:Float;
public var max:Float;
public var step:Float;
public var currentSelection:Float;
public var changeVal:Float;
var optionName:String;
public var parent:Dynamic;
public var optionName:String;
private var rawText(default, set):String;
var __number:Alphabet;
public var text(get, set):String;
private function get_text() {return __text.text;}
private function set_text(v:String) {return __text.text = v;}
public function new(text:String, desc:String, min:Float, max:Float, changeVal:Float, optionName:String, ?selectCallback:Float->Void = null, ?parent:Dynamic) {
super(desc);
this.selectCallback = selectCallback;
this.min = min;
this.max = max;
if (parent == null)
parent = Options;
this.parent = parent;
if(Reflect.field(parent, optionName) != null)
this.currentSelection = Reflect.field(parent, optionName);
this.changeVal = changeVal;
this.optionName = optionName;
add(__text = new Alphabet(20, 20, "", "bold"));
add(__number = new Alphabet(0, 20, ': $currentSelection', "bold"));
rawText = text;
}
override function reloadStrings() {
super.reloadStrings();
this.rawText = rawText;
}
function set_rawText(v:String) {
rawText = v;
__text.text = TU.exists(rawText) ? TU.translate(rawText) : rawText;
override function set_text(v:String) {
super.set_text(v);
__number.x = __text.x + __text.width + 12;
return v;
}
override function onChangeSelection(change:Float):Void
{
if(currentSelection <= min && change == -1 || currentSelection >= max && change == 1) return;
currentSelection = FlxMath.roundDecimal(currentSelection + (change * changeVal), FlxMath.getDecimals(changeVal));
public function new(text:String, desc:String, min:Float, max:Float, step:Float, optionName:String, ?changedCallback:Float->Void = null, ?parent:Dynamic) {
this.changedCallback = changedCallback;
this.min = min;
this.max = max;
this.step = step;
this.optionName = optionName;
this.parent = parent = parent != null ? parent : Options;
var fieldValue = Reflect.field(parent, optionName);
if (fieldValue != null) currentSelection = fieldValue;
__number = new Alphabet(0, 20, ': $currentSelection', 'bold');
super(text, desc);
add(__number);
}
override function changeSelection(change:Int):Void {
if (locked) return;
if (currentSelection == (currentSelection = FlxMath.bound(currentSelection + change, min, max))) return;
__number.text = ': $currentSelection';
Reflect.setField(parent, optionName, currentSelection);
if(selectCallback != null)
selectCallback(currentSelection);
if (changedCallback != null) changedCallback(currentSelection);
CoolUtil.playMenuSFX(SCROLL);
}
override function select() {}
}
+13 -1
View File
@@ -10,15 +10,25 @@ import funkin.options.TreeMenu.ITreeOption;
**/
class OptionType extends FlxSpriteGroup implements ITreeOption {
public var selected:Bool = false;
public var locked(default, set):Bool;
public var text(default, set):String;
public var rawText(default, set):String;
public var desc:String;
public var rawDesc(default, set):String;
public var itemHeight:Float = 120;
public function new(text:String, desc:String) {
super();
reloadStrings();
rawText = text;
rawDesc = desc;
}
function set_locked(v:Bool) {
if (locked == (locked = v)) return v;
color = locked ? 0xFF7F7F7F : 0xFFFFFFFF;
return v;
}
function set_text(v:String) return text = v;
@@ -46,4 +56,6 @@ class OptionType extends FlxSpriteGroup implements ITreeOption {
public function changeSelection(change:Int) {}
public function select() {}
override function get_height() return itemHeight;
}
+3 -1
View File
@@ -101,7 +101,9 @@ class RadioButton extends TextOption {
return v;
}
public override function onSelect() {
public override function select() {
if (locked) return;
checked = true;
if(checked) {
onSet(value);
+30
View File
@@ -0,0 +1,30 @@
package funkin.options.type;
import flixel.math.FlxPoint;
import flixel.util.FlxDestroyUtil;
import flixel.FlxBasic;
class Separator extends FlxSprite {
var separatorHeight:Float;
public function new(height = 67) {
super();
separatorHeight = height;
}
override function initVars() {
flixelType = OBJECT;
offset = FlxPoint.get();
origin = FlxPoint.get();
scale = FlxPoint.get(1, 1);
scrollFactor = FlxPoint.get(1, 1);
initMotionVars();
}
override function update(elapsed:Float) {}
override function draw() {}
override function get_height():Float return separatorHeight;
override function get_width():Float return 600;
}
+2 -11
View File
@@ -13,10 +13,7 @@ class SliderOption extends OptionType {
//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;
override function set_text(v:String) return __text.text = text = v;
var __text:Alphabet;
var optionName:String;
@@ -24,12 +21,6 @@ class SliderOption extends OptionType {
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;
super(text, desc);
}
}
+15 -32
View File
@@ -6,46 +6,29 @@ import flixel.effects.FlxFlicker;
* Option type that has text.
**/
class TextOption extends OptionType {
public var suffix:String;
public var selectCallback:Void->Void;
private var __text:Alphabet;
private var rawText(default, set):String;
public var text(get, set):String;
private function get_text() {return __text.text;}
private function set_text(v:String) {return __text.text = v;}
/**
* If you change this afterwards you will need to call `reloadStrings()`, or manually set rawText to rawText.
**/
public var suffix:String;
var __text:Alphabet;
override function set_text(v:String) {
__text.text = v + suffix;
return text = v;
}
public function new(text:String, desc:String, ?suffix:String = "", ?selectCallback:Void->Void = null) {
super(desc);
this.selectCallback = selectCallback;
add(__text = new Alphabet(100, 20, "", "bold"));
this.suffix = suffix;
rawText = text;
this.selectCallback = selectCallback;
__text = new Alphabet(20, 20, "", "bold");
super(text, desc);
add(__text);
}
override function reloadStrings() {
super.reloadStrings();
this.rawText = rawText;
}
function set_rawText(v:String) {
rawText = v;
__text.text = (TU.exists(rawText) ? TU.translate(rawText) : rawText) + suffix;
return v;
}
public override function draw() {
super.draw();
}
public override function onSelect() {
super.onSelect();
override function select() {
if (locked) return;
CoolUtil.playMenuSFX(CONFIRM);
FlxFlicker.flicker(this, 1, Options.flashingMenu ? 0.06 : 0.15, true, false);
selectCallback();
if (selectCallback != null) selectCallback();
}
}