custom controls stuff
This commit is contained in:
@@ -150,6 +150,7 @@ class Controls extends FlxActionSet
|
||||
@:pressed("switchmod") public var SWITCHMOD_HOLD(get, set): Bool;
|
||||
@:justReleased("switchmod") public var SWITCHMOD_R(get, set): Bool;
|
||||
|
||||
@:allow(funkin.backend.utils.ControlsUtil)
|
||||
var byName:Map<String, FlxActionDigital> = [];
|
||||
|
||||
public var gamepadsAdded:Array<Int> = [];
|
||||
@@ -219,13 +220,13 @@ class Controls extends FlxActionSet
|
||||
macro_forEachBound(control, (action, _) -> removeKeys(action, keys));
|
||||
}
|
||||
|
||||
inline static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
|
||||
public inline static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
|
||||
{
|
||||
for (key in keys)
|
||||
action.addKey(key, state);
|
||||
}
|
||||
|
||||
static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>)
|
||||
public static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>)
|
||||
{
|
||||
var i = action.inputs.length;
|
||||
while (i-- > 0)
|
||||
@@ -302,13 +303,13 @@ class Controls extends FlxActionSet
|
||||
macro_forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons));
|
||||
}
|
||||
|
||||
inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id)
|
||||
public inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id)
|
||||
{
|
||||
for (button in buttons)
|
||||
action.addGamepad(button, state, id);
|
||||
}
|
||||
|
||||
static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>)
|
||||
public static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>)
|
||||
{
|
||||
var i = action.inputs.length;
|
||||
while (i-- > 0)
|
||||
@@ -319,8 +320,21 @@ class Controls extends FlxActionSet
|
||||
}
|
||||
}
|
||||
|
||||
inline static function isGamepad(input:FlxActionInput, deviceID:Int)
|
||||
public inline static function isGamepad(input:FlxActionInput, deviceID:Int)
|
||||
{
|
||||
return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID);
|
||||
}
|
||||
}
|
||||
|
||||
@:nullSafety(Off)
|
||||
public inline function getJustPressed(name:String) {
|
||||
return ControlsUtil.getJustPressed(this, name);
|
||||
}
|
||||
@:nullSafety(Off)
|
||||
public inline function getJustReleased(name:String) {
|
||||
return ControlsUtil.getJustReleased(this, name);
|
||||
}
|
||||
@:nullSafety(Off)
|
||||
public inline function getPressed(name:String) {
|
||||
return ControlsUtil.getPressed(this, name);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ class MainState extends FlxState {
|
||||
#end
|
||||
Options.save();
|
||||
|
||||
ControlsUtil.resetCustomControls();
|
||||
FlxG.bitmap.reset();
|
||||
FlxG.sound.destroy(true);
|
||||
|
||||
@@ -57,6 +58,7 @@ class MainState extends FlxState {
|
||||
ModsFolder.onModSwitch.dispatch(ModsFolder.currentModFolder);
|
||||
DiscordUtil.init();
|
||||
EventsData.reloadEvents();
|
||||
ControlsUtil.loadCustomControls();
|
||||
TitleState.initialized = false;
|
||||
|
||||
if (betaWarningShown)
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package funkin.backend.utils;
|
||||
|
||||
import funkin.options.PlayerSettings;
|
||||
import flixel.input.FlxInput;
|
||||
import flixel.input.actions.FlxAction;
|
||||
import funkin.backend.system.Controls;
|
||||
import flixel.input.actions.FlxActionInput;
|
||||
import flixel.input.actions.FlxActionManager;
|
||||
import flixel.input.actions.FlxActionSet;
|
||||
import flixel.input.gamepad.FlxGamepadInputID;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import haxe.xml.Access;
|
||||
|
||||
class ControlsUtil {
|
||||
|
||||
private static var storedCustomControls:Array<String> = [];
|
||||
|
||||
public static function initCustomControl(controls:Controls, name:String) {
|
||||
if (controls.byName.exists(name)) return;
|
||||
|
||||
controls.byName.set(name, new FlxActionDigital(name));
|
||||
controls.byName.set(name+"_P", new FlxActionDigital(name+"_P"));
|
||||
controls.byName.set(name+"_R", new FlxActionDigital(name+"_R"));
|
||||
storedCustomControls.push(name);
|
||||
}
|
||||
|
||||
public static function addKeysToCustomControl(controls:Controls, name:String, keys:Array<FlxKey>) {
|
||||
if (!controls.byName.exists(name)) return;
|
||||
|
||||
var pressed:FlxActionDigital = controls.byName.get(name);
|
||||
if (pressed != null) Controls.addKeys(pressed, keys, PRESSED);
|
||||
|
||||
var justPressed:FlxActionDigital = controls.byName.get(name+"_P");
|
||||
if (justPressed != null) Controls.addKeys(justPressed, keys, JUST_PRESSED);
|
||||
|
||||
var justReleased:FlxActionDigital = controls.byName.get(name+"_R");
|
||||
if (justReleased != null) Controls.addKeys(justReleased, keys, JUST_RELEASED);
|
||||
}
|
||||
|
||||
public static function removeKeysFromCustomControl(controls:Controls, name:String, keys:Array<FlxKey>) {
|
||||
if (!controls.byName.exists(name)) return;
|
||||
|
||||
var pressed:FlxActionDigital = controls.byName.get(name);
|
||||
if (pressed != null) Controls.removeKeys(pressed, keys);
|
||||
|
||||
var justPressed:FlxActionDigital = controls.byName.get(name+"_P");
|
||||
if (justPressed != null) Controls.removeKeys(justPressed, keys);
|
||||
|
||||
var justReleased:FlxActionDigital = controls.byName.get(name+"_R");
|
||||
if (justReleased != null) Controls.removeKeys(justReleased, keys);
|
||||
}
|
||||
|
||||
public static function resetCustomControls() {
|
||||
removeCustomControls(PlayerSettings.solo.controls);
|
||||
removeCustomControls(PlayerSettings.player1.controls);
|
||||
removeCustomControls(PlayerSettings.player2.controls);
|
||||
storedCustomControls = [];
|
||||
}
|
||||
|
||||
private static inline function removeCustomControls(controls:Controls) {
|
||||
for (name in storedCustomControls) {
|
||||
var pressed:FlxActionDigital = controls.byName.get(name);
|
||||
if (pressed != null) pressed.destroy();
|
||||
|
||||
var justPressed:FlxActionDigital = controls.byName.get(name+"_P");
|
||||
if (justPressed != null) justPressed.destroy();
|
||||
|
||||
var justReleased:FlxActionDigital = controls.byName.get(name+"_R");
|
||||
if (justReleased != null) justReleased.destroy();
|
||||
|
||||
controls.byName.remove(name);
|
||||
controls.byName.remove(name+"_P");
|
||||
controls.byName.remove(name+"_R");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static inline function checkControl(controls:Controls, name:String) {
|
||||
var control = getControl(controls, name);
|
||||
if (control != null) return control.check();
|
||||
return false;
|
||||
}
|
||||
|
||||
public static inline function getControl(controls:Controls, name:String) : FlxActionDigital {
|
||||
return controls.byName.get(name);
|
||||
}
|
||||
|
||||
public static inline function getJustPressed(controls:Controls, name:String) {
|
||||
return checkControl(controls, name + "_P");
|
||||
}
|
||||
public static inline function getJustReleased(controls:Controls, name:String) {
|
||||
return checkControl(controls, name + "_R");
|
||||
}
|
||||
public static inline function getPressed(controls:Controls, name:String) {
|
||||
return checkControl(controls, name);
|
||||
}
|
||||
|
||||
public static function loadCustomControls() {
|
||||
var xmlPath = Paths.xml("config/controls");
|
||||
for(source in [funkin.backend.assets.AssetsLibraryList.AssetSource.SOURCE, funkin.backend.assets.AssetsLibraryList.AssetSource.MODS]) {
|
||||
if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT", source)) {
|
||||
var access:Access = null;
|
||||
try {
|
||||
access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT", source)).firstElement());
|
||||
} catch(e) {
|
||||
Logs.trace('Error while parsing controls.xml: ${Std.string(e)}', ERROR);
|
||||
}
|
||||
|
||||
if (access != null) {
|
||||
parseControlsXml(access);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static inline function parseControlsXml(xml:Access) {
|
||||
for (category in xml.elements) {
|
||||
for (control in category.elements) {
|
||||
if (!control.has.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = control.getAtt("name");
|
||||
var saveName = control.getAtt("saveName").getDefault("");
|
||||
var defaultKeyP1 = control.getAtt("keyP1").getDefault("");
|
||||
var defaultKeyP2 = control.getAtt("keyP2").getDefault("");
|
||||
|
||||
var keyP1:Null<FlxKey> = 0;
|
||||
var keyP2:Null<FlxKey> = 0;
|
||||
|
||||
ControlsUtil.initCustomControl(PlayerSettings.solo.controls, name);
|
||||
ControlsUtil.initCustomControl(PlayerSettings.player1.controls, name);
|
||||
ControlsUtil.initCustomControl(PlayerSettings.player2.controls, name);
|
||||
|
||||
if (saveName != "") {
|
||||
var p1:Null<FlxKey> = Reflect.getProperty(FlxG.save.data, "P1_"+saveName);
|
||||
var p2:Null<FlxKey> = Reflect.getProperty(FlxG.save.data, "P2_"+saveName);
|
||||
|
||||
//check for save data (and load default if it doesnt exist)
|
||||
if (p1 != null) {
|
||||
keyP1 = p1;
|
||||
} else {
|
||||
if (defaultKeyP1 != "") keyP1 = FlxKey.fromString(defaultKeyP1);
|
||||
Reflect.setProperty(FlxG.save.data, "P1_"+saveName, keyP1);
|
||||
}
|
||||
|
||||
if (p2 != null) {
|
||||
keyP2 = p2;
|
||||
} else {
|
||||
if (defaultKeyP2 != "") keyP2 = FlxKey.fromString(defaultKeyP2);
|
||||
Reflect.setProperty(FlxG.save.data, "P2_"+saveName, keyP2);
|
||||
}
|
||||
|
||||
} else {
|
||||
//if no save data just load the default
|
||||
if (defaultKeyP1 != "") keyP1 = FlxKey.fromString(defaultKeyP1);
|
||||
if (defaultKeyP2 != "") keyP2 = FlxKey.fromString(defaultKeyP2);
|
||||
}
|
||||
|
||||
ControlsUtil.addKeysToCustomControl(PlayerSettings.solo.controls, name, [keyP1, keyP2]);
|
||||
ControlsUtil.addKeysToCustomControl(PlayerSettings.player1.controls, name, [keyP1, 0]);
|
||||
ControlsUtil.addKeysToCustomControl(PlayerSettings.player2.controls, name, [0, keyP2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class Strum extends FlxSprite {
|
||||
public var getJustReleased:StrumLine->Bool = null;
|
||||
|
||||
public inline function __getPressed(strumLine:StrumLine):Bool {
|
||||
return getPressed != null ? getPressed(strumLine) : switch(ID) {
|
||||
return getPressed != null ? getPressed(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getPressed(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) {
|
||||
case 0: strumLine.controls.NOTE_LEFT;
|
||||
case 1: strumLine.controls.NOTE_DOWN;
|
||||
case 2: strumLine.controls.NOTE_UP;
|
||||
@@ -35,7 +35,7 @@ class Strum extends FlxSprite {
|
||||
}
|
||||
}
|
||||
public inline function __getJustPressed(strumLine:StrumLine) {
|
||||
return getJustPressed != null ? getJustPressed(strumLine) : switch(ID) {
|
||||
return getJustPressed != null ? getJustPressed(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getJustPressed(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) {
|
||||
case 0: strumLine.controls.NOTE_LEFT_P;
|
||||
case 1: strumLine.controls.NOTE_DOWN_P;
|
||||
case 2: strumLine.controls.NOTE_UP_P;
|
||||
@@ -44,7 +44,7 @@ class Strum extends FlxSprite {
|
||||
}
|
||||
}
|
||||
public inline function __getJustReleased(strumLine:StrumLine) {
|
||||
return getJustReleased != null ? getJustReleased(strumLine) : switch(ID) {
|
||||
return getJustReleased != null ? getJustReleased(strumLine) : strumLine.members.length != 4 ? ControlsUtil.getJustReleased(strumLine.controls, strumLine.members.length+"k"+ID) : switch(ID) {
|
||||
case 0: strumLine.controls.NOTE_LEFT_R;
|
||||
case 1: strumLine.controls.NOTE_DOWN_R;
|
||||
case 2: strumLine.controls.NOTE_UP_R;
|
||||
|
||||
@@ -15,16 +15,19 @@ class KeybindSetting extends FlxTypedSpriteGroup<FlxSprite> {
|
||||
public var p2Selected:Bool = false;
|
||||
public var value:String;
|
||||
|
||||
public var custom:Bool = false;
|
||||
|
||||
public var option1:Null<FlxKey>;
|
||||
public var option2:Null<FlxKey>;
|
||||
public function new(x:Float, y:Float, name:String, value:String, ?sparrowIcon:String, ?sparrowAnim:String) {
|
||||
public function new(x:Float, y:Float, name:String, value:String, ?sparrowIcon:String, ?sparrowAnim:String, ?custom:Bool = false) {
|
||||
super();
|
||||
this.value = value;
|
||||
this.custom = custom;
|
||||
title = new Alphabet(0, 0, name, true);
|
||||
add(title);
|
||||
|
||||
var controlArrayP1:Array<FlxKey> = Reflect.field(Options, 'P1_${value}');
|
||||
var controlArrayP2:Array<FlxKey> = Reflect.field(Options, 'P2_${value}');
|
||||
var controlArrayP1:Array<FlxKey> = custom ? [Reflect.getProperty(FlxG.save.data, 'P1_${value}')] : Reflect.field(Options, 'P1_${value}');
|
||||
var controlArrayP2:Array<FlxKey> = custom ? [Reflect.getProperty(FlxG.save.data, 'P2_${value}')] : Reflect.field(Options, 'P2_${value}');
|
||||
|
||||
option1 = controlArrayP1[0];
|
||||
option2 = controlArrayP2[0];
|
||||
@@ -76,19 +79,35 @@ class KeybindSetting extends FlxTypedSpriteGroup<FlxSprite> {
|
||||
KeybindsOptions.instance.openSubState(new ChangeKeybindSubState(function(key:FlxKey) {
|
||||
flicker.stop();
|
||||
flicker.destroy();
|
||||
if (p2)
|
||||
Reflect.setField(Options, 'P2_$value', [option2 = key]);
|
||||
else
|
||||
Reflect.setField(Options, 'P1_$value', [option1 = key]);
|
||||
if (!custom) {
|
||||
if (p2)
|
||||
Reflect.setField(Options, 'P2_$value', [option2 = key]);
|
||||
else
|
||||
Reflect.setField(Options, 'P1_$value', [option1 = key]);
|
||||
} else {
|
||||
if (p2)
|
||||
Reflect.setProperty(FlxG.save.data, 'P2_$value', option2 = key);
|
||||
else
|
||||
Reflect.setProperty(FlxG.save.data, 'P1_$value', option1 = key);
|
||||
}
|
||||
|
||||
updateText();
|
||||
callback();
|
||||
}, function() {
|
||||
flicker.stop();
|
||||
flicker.destroy();
|
||||
if (p2)
|
||||
Reflect.setField(Options, 'P2_$value', [option2 = 0]);
|
||||
else
|
||||
Reflect.setField(Options, 'P1_$value', [option1 = 0]);
|
||||
if (!custom) {
|
||||
if (p2)
|
||||
Reflect.setField(Options, 'P2_$value', [option2 = 0]);
|
||||
else
|
||||
Reflect.setField(Options, 'P1_$value', [option1 = 0]);
|
||||
} else {
|
||||
if (p2)
|
||||
Reflect.setProperty(FlxG.save.data, 'P2_$value', option2 = 0);
|
||||
else
|
||||
Reflect.setProperty(FlxG.save.data, 'P1_$value', option1 = 0);
|
||||
}
|
||||
updateText();
|
||||
cancelCallback();
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package funkin.options.keybinds;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
import haxe.xml.Access;
|
||||
using StringTools;
|
||||
|
||||
|
||||
class KeybindsOptions extends MusicBeatSubstate {
|
||||
public static var instance:KeybindsOptions;
|
||||
|
||||
public var categories = [
|
||||
public var categories:Array<ControlsCategory> = [
|
||||
{
|
||||
name: 'Notes',
|
||||
settings: [
|
||||
@@ -127,6 +128,9 @@ class KeybindsOptions extends MusicBeatSubstate {
|
||||
FlxG.camera.follow(camFollow, LOCKON, 0.125);
|
||||
}
|
||||
|
||||
var customCategories = loadCustomCategories();
|
||||
for (i in customCategories) categories.push(i);
|
||||
|
||||
var k:Int = 0;
|
||||
for(category in categories) {
|
||||
k++;
|
||||
@@ -152,8 +156,10 @@ class KeybindsOptions extends MusicBeatSubstate {
|
||||
};
|
||||
e.name = e.name.substring(5, e.name.length - 1);
|
||||
}
|
||||
if (e.sparrowIcon != null) sparrowIcon = e.sparrowIcon;
|
||||
if (e.sparrowAnim != null) sparrowAnim = e.sparrowAnim;
|
||||
|
||||
var text = new KeybindSetting(100, k * 75, e.name, e.control, sparrowIcon, sparrowAnim);
|
||||
var text = new KeybindSetting(100, k * 75, e.name, e.control, sparrowIcon, sparrowAnim, e.custom == null ? false : e.custom);
|
||||
if (!isSubState)
|
||||
text.bind1.color = text.bind2.color = FlxColor.BLACK;
|
||||
alphabets.add(text);
|
||||
@@ -199,7 +205,9 @@ class KeybindsOptions extends MusicBeatSubstate {
|
||||
close();
|
||||
else
|
||||
FlxG.switchState(new OptionsMenu());
|
||||
ControlsUtil.resetCustomControls();
|
||||
Options.applyKeybinds();
|
||||
ControlsUtil.loadCustomControls();
|
||||
Options.save();
|
||||
return;
|
||||
}
|
||||
@@ -248,4 +256,59 @@ class KeybindsOptions extends MusicBeatSubstate {
|
||||
camFollow.setPosition(FlxG.width / 2, FlxG.height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
public function loadCustomCategories() {
|
||||
var customCategories:Array<ControlsCategory> = [];
|
||||
|
||||
var xmlPath = Paths.xml("config/controls");
|
||||
for(source in [funkin.backend.assets.AssetsLibraryList.AssetSource.SOURCE, funkin.backend.assets.AssetsLibraryList.AssetSource.MODS]) {
|
||||
if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT", source)) {
|
||||
var access:Access = null;
|
||||
try {
|
||||
access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT", source)).firstElement());
|
||||
} catch(e) {
|
||||
Logs.trace('Error while parsing controls.xml: ${Std.string(e)}', ERROR);
|
||||
}
|
||||
|
||||
if (access != null) {
|
||||
for (category in access.elements) {
|
||||
if (!category.has.name) continue;
|
||||
|
||||
var cat:ControlsCategory = {
|
||||
name: category.getAtt("name"),
|
||||
settings: []
|
||||
};
|
||||
|
||||
for (control in category.elements) {
|
||||
if (control.has.menuName && control.has.saveName) {
|
||||
cat.settings.push({
|
||||
name: control.getAtt("menuName"),
|
||||
control: control.getAtt("saveName"),
|
||||
custom: true,
|
||||
sparrowIcon: control.getAtt("menuIcon").getDefault(null),
|
||||
sparrowAnim: control.getAtt("menuAnim").getDefault(null)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
customCategories.push(cat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return customCategories;
|
||||
}
|
||||
}
|
||||
|
||||
typedef KeybindSettingData = {
|
||||
var name:String;
|
||||
var control:String;
|
||||
var ?custom:Bool;
|
||||
var ?sparrowIcon:String;
|
||||
var ?sparrowAnim:String;
|
||||
}
|
||||
|
||||
typedef ControlsCategory = {
|
||||
var name:String;
|
||||
var settings:Array<KeybindSettingData>;
|
||||
}
|
||||
Reference in New Issue
Block a user