Funkin/source/funkin/ui/ControlsMenu.hx

354 lines
9 KiB
Haxe
Raw Normal View History

package funkin.ui;
2021-02-23 19:59:36 -05:00
import flixel.FlxCamera;
import flixel.FlxObject;
import flixel.FlxSprite;
import flixel.group.FlxGroup;
2021-04-04 13:17:46 -04:00
import flixel.input.actions.FlxActionInput;
import flixel.input.gamepad.FlxGamepadInputID;
2021-02-23 19:59:36 -05:00
import flixel.input.keyboard.FlxKey;
2022-04-18 19:36:09 -04:00
import funkin.Controls;
import funkin.ui.AtlasText;
import funkin.ui.MenuList;
import funkin.ui.TextMenuList;
2021-02-23 19:59:36 -05:00
class ControlsMenu extends funkin.ui.OptionsState.Page
2021-02-23 19:59:36 -05:00
{
2021-03-22 08:48:52 -04:00
inline static public var COLUMNS = 2;
2021-03-16 10:56:08 -04:00
static var controlList = Control.createAll();
/*
* Defines groups of controls that cannot share inputs, like left and right. Say, if ACCEPT is Z, Back is X,
* if the player sets Back to Z it also set ACCEPT to X. This prevents the player from setting the controls in
* a way the prevents them from changing more controls or exiting the menu.
*/
2021-04-04 13:17:46 -04:00
static var controlGroups:Array<Array<Control>> = [
[NOTE_UP, NOTE_DOWN, NOTE_LEFT, NOTE_RIGHT],
[UI_UP, UI_DOWN, UI_LEFT, UI_RIGHT, ACCEPT, BACK]
];
2021-03-16 10:56:08 -04:00
var itemGroups:Array<Array<InputItem>> = [for (i in 0...controlGroups.length) []];
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
var controlGrid:MenuTypedList<InputItem>;
2021-03-22 08:48:52 -04:00
var deviceList:TextMenuList;
2021-03-13 21:11:56 -05:00
var menuCamera:FlxCamera;
2021-03-16 10:56:08 -04:00
var prompt:Prompt;
2021-03-22 08:48:52 -04:00
var camFollow:FlxObject;
var labels:FlxTypedGroup<AtlasText>;
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
var currentDevice:Device = Keys;
var deviceListSelected = false;
2021-04-04 13:17:46 -04:00
2021-03-13 21:11:56 -05:00
public function new()
{
super();
2021-04-04 13:17:46 -04:00
2021-03-13 21:11:56 -05:00
menuCamera = new FlxCamera();
2021-03-23 18:30:58 -04:00
FlxG.cameras.add(menuCamera, false);
2021-03-13 21:11:56 -05:00
menuCamera.bgColor = 0x0;
2021-03-16 10:56:08 -04:00
camera = menuCamera;
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
labels = new FlxTypedGroup<AtlasText>();
2021-03-16 11:20:40 -04:00
var headers = new FlxTypedGroup<AtlasText>();
2021-03-22 08:48:52 -04:00
controlGrid = new MenuTypedList(Columns(COLUMNS), Vertical);
2021-04-04 13:17:46 -04:00
2021-03-16 11:20:40 -04:00
add(labels);
add(headers);
add(controlGrid);
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
if (FlxG.gamepads.numActiveGamepads > 0)
{
var devicesBg = new FlxSprite();
2021-03-22 09:40:32 -04:00
devicesBg.makeGraphic(FlxG.width, 100, 0xFFfafd6d);
2021-03-22 08:48:52 -04:00
add(devicesBg);
deviceList = new TextMenuList(Horizontal, None);
add(deviceList);
deviceListSelected = true;
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
var item;
2021-04-04 13:17:46 -04:00
2022-04-18 19:36:09 -04:00
item = deviceList.createItem("Keyboard", AtlasFont.BOLD, selectDevice.bind(Keys));
2021-03-22 08:48:52 -04:00
item.x = FlxG.width / 2 - item.width - 30;
item.y = (devicesBg.height - item.height) / 2;
2021-04-04 13:17:46 -04:00
2022-04-18 19:36:09 -04:00
item = deviceList.createItem("Gamepad", AtlasFont.BOLD, selectDevice.bind(Gamepad(FlxG.gamepads.firstActive.id)));
2021-03-22 08:48:52 -04:00
item.x = FlxG.width / 2 + 30;
item.y = (devicesBg.height - item.height) / 2;
}
2021-04-04 13:17:46 -04:00
2021-03-13 21:11:56 -05:00
// FlxG.debugger.drawDebug = true;
2021-03-22 08:48:52 -04:00
var y = deviceList == null ? 30 : 120;
2021-03-16 10:56:08 -04:00
var spacer = 70;
var currentHeader:String = null;
// list order is determined by enum order
2021-03-13 21:11:56 -05:00
for (i in 0...controlList.length)
{
var control = controlList[i];
var name = control.getName();
2021-03-16 10:56:08 -04:00
if (currentHeader != "UI_" && name.indexOf("UI_") == 0)
{
currentHeader = "UI_";
2022-04-18 19:36:09 -04:00
headers.add(new AtlasText(0, y, "UI", AtlasFont.BOLD)).screenCenter(X);
2021-03-16 10:56:08 -04:00
y += spacer;
}
else if (currentHeader != "NOTE_" && name.indexOf("NOTE_") == 0)
{
currentHeader = "NOTE_";
2022-04-18 19:36:09 -04:00
headers.add(new AtlasText(0, y, "NOTES", AtlasFont.BOLD)).screenCenter(X);
2021-03-16 10:56:08 -04:00
y += spacer;
}
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
if (currentHeader != null && name.indexOf(currentHeader) == 0)
name = name.substr(currentHeader.length);
2021-04-04 13:17:46 -04:00
2022-04-18 19:36:09 -04:00
var label = labels.add(new AtlasText(150, y, name, AtlasFont.BOLD));
2021-03-16 11:20:40 -04:00
label.alpha = 0.6;
2021-03-22 08:48:52 -04:00
for (i in 0...COLUMNS)
createItem(label.x + 400 + i * 300, y, control, i);
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
y += spacer;
2021-03-13 21:11:56 -05:00
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
camFollow = new FlxObject(FlxG.width / 2, 0, 70, 70);
if (deviceList != null)
{
camFollow.y = deviceList.selectedItem.y;
controlGrid.selectedItem.idle();
2021-03-22 09:40:32 -04:00
controlGrid.enabled = false;
2021-03-22 08:48:52 -04:00
}
else
camFollow.y = controlGrid.selectedItem.y;
2021-04-04 13:17:46 -04:00
2021-03-13 21:11:56 -05:00
menuCamera.follow(camFollow, null, 0.06);
var margin = 100;
menuCamera.deadzone.set(0, margin, menuCamera.width, menuCamera.height - margin * 2);
2021-03-22 08:48:52 -04:00
menuCamera.minScrollY = 0;
2021-04-04 13:17:46 -04:00
controlGrid.onChange.add(function(selected)
2021-03-16 11:20:40 -04:00
{
camFollow.y = selected.y;
2021-04-04 13:17:46 -04:00
labels.forEach((label) -> label.alpha = 0.6);
2021-03-22 08:48:52 -04:00
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
2021-03-16 11:20:40 -04:00
});
2021-04-04 13:17:46 -04:00
2021-03-16 11:20:40 -04:00
prompt = new Prompt("\nPress any key to rebind\n\n\n\n Escape to cancel", None);
2021-03-16 10:56:08 -04:00
prompt.create();
2021-03-22 09:40:32 -04:00
prompt.createBgFromMargin(100, 0xFFfafd6d);
2021-03-16 10:56:08 -04:00
prompt.back.scrollFactor.set(0, 0);
prompt.exists = false;
add(prompt);
2021-03-13 21:11:56 -05:00
}
2021-04-04 13:17:46 -04:00
2021-03-13 21:11:56 -05:00
function createItem(x = 0.0, y = 0.0, control:Control, index:Int)
{
2021-03-22 08:48:52 -04:00
var item = new InputItem(x, y, currentDevice, control, index, onSelect);
2021-03-16 10:56:08 -04:00
for (i in 0...controlGroups.length)
2021-03-13 21:11:56 -05:00
{
2021-03-16 10:56:08 -04:00
if (controlGroups[i].contains(control))
itemGroups[i].push(item);
2021-03-13 21:11:56 -05:00
}
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
return controlGrid.addItem(item.name, item);
2021-03-13 21:11:56 -05:00
}
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
function onSelect():Void
2021-03-13 21:11:56 -05:00
{
controlGrid.enabled = false;
2021-03-16 10:56:08 -04:00
canExit = false;
prompt.exists = true;
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
function goToDeviceList()
{
controlGrid.selectedItem.idle();
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 0.6;
controlGrid.enabled = false;
deviceList.enabled = true;
canExit = true;
camFollow.y = deviceList.selectedItem.y;
deviceListSelected = true;
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
function selectDevice(device:Device)
{
currentDevice = device;
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
for (item in controlGrid.members)
item.updateDevice(currentDevice);
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
var inputName = device == Keys ? "key" : "button";
var cancel = device == Keys ? "Escape" : "Back";
2021-04-04 13:17:46 -04:00
// todo: alignment
2021-03-22 22:39:35 -04:00
if (device == Keys)
prompt.setText('\nPress any key to rebind\n\n\n\n $cancel to cancel');
else
prompt.setText('\nPress any button\n to rebind\n\n\n $cancel to cancel');
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
controlGrid.selectedItem.select();
labels.members[Std.int(controlGrid.selectedIndex / COLUMNS)].alpha = 1.0;
controlGrid.enabled = true;
deviceList.enabled = false;
deviceListSelected = false;
canExit = false;
}
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
override function update(elapsed:Float)
{
super.update(elapsed);
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
var controls = PlayerSettings.player1.controls;
2021-03-23 18:28:05 -04:00
if (controlGrid.enabled && deviceList != null && deviceListSelected == false && controls.BACK)
2021-03-22 08:48:52 -04:00
goToDeviceList();
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
if (prompt.exists)
{
2021-03-22 08:48:52 -04:00
switch (currentDevice)
2021-03-16 10:56:08 -04:00
{
2021-03-22 08:48:52 -04:00
case Keys:
{
2021-04-04 13:17:46 -04:00
// check released otherwise bugs can happen when you change the BACK key
var key = FlxG.keys.firstJustReleased();
if (key != NONE)
{
if (key != ESCAPE)
onInputSelect(key);
closePrompt();
}
2021-03-22 08:48:52 -04:00
}
case Gamepad(id):
{
2021-04-04 13:17:46 -04:00
var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
if (button != NONE)
{
if (button != BACK)
onInputSelect(button);
closePrompt();
}
2021-03-22 08:48:52 -04:00
}
2021-03-16 10:56:08 -04:00
}
}
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
function onInputSelect(input:Int)
2021-03-16 10:56:08 -04:00
{
var item = controlGrid.selectedItem;
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
// check if that key is already set for this
var column0 = Math.floor(controlGrid.selectedIndex / 2) * 2;
for (i in 0...COLUMNS)
{
if (controlGrid.members[column0 + i].input == input)
return;
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
// Check if items in the same group already have the new input
2021-03-16 10:56:08 -04:00
for (group in itemGroups)
{
if (group.contains(item))
{
for (otherItem in group)
{
2021-03-22 08:48:52 -04:00
if (otherItem != item && otherItem.input == input)
2021-03-16 10:56:08 -04:00
{
// replace that input with this items old input.
2021-03-22 08:48:52 -04:00
PlayerSettings.player1.controls.replaceBinding(otherItem.control, currentDevice, item.input, otherItem.input);
2021-03-16 10:56:08 -04:00
// Don't use resetItem() since items share names/labels
otherItem.input = item.input;
otherItem.label.text = item.label.text;
}
}
}
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
PlayerSettings.player1.controls.replaceBinding(item.control, currentDevice, input, item.input);
2021-03-16 10:56:08 -04:00
// Don't use resetItem() since items share names/labels
2021-03-22 08:48:52 -04:00
item.input = input;
item.label.text = item.getLabel(input);
2021-04-04 13:17:46 -04:00
2021-03-22 22:39:35 -04:00
PlayerSettings.player1.saveControls();
2021-03-16 10:56:08 -04:00
}
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
function closePrompt()
{
prompt.exists = false;
2021-03-22 22:39:35 -04:00
controlGrid.enabled = true;
if (deviceList == null)
canExit = true;
2021-03-16 10:56:08 -04:00
}
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
override function destroy()
{
super.destroy();
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
itemGroups = null;
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
if (FlxG.cameras.list.contains(menuCamera))
FlxG.cameras.remove(menuCamera);
2021-03-13 21:11:56 -05:00
}
2021-04-04 13:17:46 -04:00
2021-03-13 21:11:56 -05:00
override function set_enabled(value:Bool)
{
2021-03-22 08:48:52 -04:00
if (value == false)
{
controlGrid.enabled = false;
if (deviceList != null)
deviceList.enabled = false;
}
else
{
controlGrid.enabled = !deviceListSelected;
if (deviceList != null)
deviceList.enabled = deviceListSelected;
}
2021-03-13 21:11:56 -05:00
return super.set_enabled(value);
}
2021-03-16 10:56:08 -04:00
}
class InputItem extends TextMenuItem
{
2021-03-22 08:48:52 -04:00
public var device(default, null):Device = Keys;
2021-03-16 10:56:08 -04:00
public var control:Control;
public var input:Int = -1;
2021-03-22 08:48:52 -04:00
public var index:Int = -1;
2021-04-04 13:17:46 -04:00
public function new(x = 0.0, y = 0.0, device, control, index, ?callback)
2021-03-16 10:56:08 -04:00
{
2021-03-22 08:48:52 -04:00
this.device = device;
2021-03-16 10:56:08 -04:00
this.control = control;
2021-03-22 08:48:52 -04:00
this.index = index;
this.input = getInput();
2021-04-04 13:17:46 -04:00
2022-04-18 19:36:09 -04:00
super(x, y, getLabel(input), DEFAULT, callback);
2021-03-22 08:48:52 -04:00
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
public function updateDevice(device:Device)
{
if (this.device != device)
{
this.device = device;
input = getInput();
label.text = getLabel(input);
}
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
function getInput()
{
var list = PlayerSettings.player1.controls.getInputsFor(control, device);
2021-03-16 10:56:08 -04:00
if (list.length > index)
{
2021-03-22 08:48:52 -04:00
if (list[index] != FlxKey.ESCAPE || list[index] != FlxGamepadInputID.BACK)
return list[index];
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
if (list.length > ControlsMenu.COLUMNS)
2021-03-16 10:56:08 -04:00
// Escape isn't mappable, show a third option, instead.
2021-03-22 08:48:52 -04:00
return list[ControlsMenu.COLUMNS];
2021-03-16 10:56:08 -04:00
}
2021-04-04 13:17:46 -04:00
2021-03-22 08:48:52 -04:00
return -1;
2021-03-16 10:56:08 -04:00
}
2021-04-04 13:17:46 -04:00
2021-03-16 10:56:08 -04:00
public function getLabel(input:Int)
{
2021-03-22 08:48:52 -04:00
return input == -1 ? "---" : InputFormatter.format(input, device);
2021-03-16 10:56:08 -04:00
}
2021-04-04 13:17:46 -04:00
}