Files
CodenameEngine/source/funkin/backend/system/Controls.hx
T
2024-07-09 11:24:07 +02:00

327 lines
9.1 KiB
Haxe

package funkin.backend.system;
import flixel.input.FlxInput;
import flixel.input.actions.FlxAction;
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;
enum Control
{
UP;
LEFT;
RIGHT;
DOWN;
NOTE_UP;
NOTE_LEFT;
NOTE_RIGHT;
NOTE_DOWN;
RESET;
ACCEPT;
BACK;
PAUSE;
//CHEAT;
SWITCHMOD;
}
enum KeyboardScheme
{
Solo;
Duo(first:Bool);
None;
Custom;
}
/**
* A list of actions that a player would invoke via some input device.
* Uses FlxActions to funnel various inputs to a single action.
*/
// Macro generated
// A and B are swapped for switch
@:noCustomClass
@:nullSafety
@:build(funkin.backend.system.macros.ControlsMacro.build())
class Controls extends FlxActionSet
{
// Menus
#if !switch
@:rawGamepad([DPAD_UP, LEFT_STICK_DIGITAL_UP])
#else
@:rawGamepad([DPAD_UP, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP])
#end
@:pressed("up") public var UP(get, set): Bool;
@:justPressed("up") public var UP_P(get, set): Bool;
@:justReleased("up") public var UP_R(get, set): Bool;
#if !switch
@:rawGamepad([DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT])
#else
@:rawGamepad([DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT])
#end
@:pressed("left") public var LEFT(get, set): Bool;
@:justPressed("left") public var LEFT_P(get, set): Bool;
@:justReleased("left") public var LEFT_R(get, set): Bool;
#if !switch
@:rawGamepad([DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT])
#else
@:rawGamepad([DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT])
#end
@:pressed("right") public var RIGHT(get, set): Bool;
@:justPressed("right") public var RIGHT_P(get, set): Bool;
@:justReleased("right") public var RIGHT_R(get, set): Bool;
#if !switch
@:rawGamepad([DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN])
#else
@:rawGamepad([DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN])
#end
@:pressed("down") public var DOWN(get, set): Bool;
@:justPressed("down") public var DOWN_P(get, set): Bool;
@:justReleased("down") public var DOWN_R(get, set): Bool;
// Note Controls
#if !switch
@:rawGamepad([DPAD_UP, LEFT_STICK_DIGITAL_UP])
#else
@:rawGamepad([DPAD_UP, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP])
#end
@:pressed("note-up") public var NOTE_UP(get, set): Bool;
@:justPressed("note-up") public var NOTE_UP_P(get, set): Bool;
@:justReleased("note-up") public var NOTE_UP_R(get, set): Bool;
#if !switch
@:rawGamepad([DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT])
#else
@:rawGamepad([DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT])
#end
@:pressed("note-left") public var NOTE_LEFT(get, set): Bool;
@:justPressed("note-left") public var NOTE_LEFT_P(get, set): Bool;
@:justReleased("note-left") public var NOTE_LEFT_R(get, set): Bool;
#if !switch
@:rawGamepad([DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT])
#else
@:rawGamepad([DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT])
#end
@:pressed("note-right") public var NOTE_RIGHT(get, set): Bool;
@:justPressed("note-right") public var NOTE_RIGHT_P(get, set): Bool;
@:justReleased("note-right") public var NOTE_RIGHT_R(get, set): Bool;
#if !switch
@:rawGamepad([DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN])
#else
@:rawGamepad([DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN])
#end
@:pressed("note-down") public var NOTE_DOWN(get, set): Bool;
@:justPressed("note-down") public var NOTE_DOWN_P(get, set): Bool;
@:justReleased("note-down") public var NOTE_DOWN_R(get, set): Bool;
@:gamepad([A])
@:justPressed("accept") public var ACCEPT(get, set): Bool;
@:pressed("accept") public var ACCEPT_HOLD(get, set): Bool;
@:justReleased("accept") public var ACCEPT_R(get, set): Bool;
@:gamepad([B])
@:justPressed("back") public var BACK(get, set): Bool;
@:pressed("back") public var BACK_HOLD(get, set): Bool;
@:justReleased("back") public var BACK_R(get, set): Bool;
@:gamepad([START])
@:justPressed("pause") public var PAUSE(get, set): Bool;
@:pressed("pause") public var PAUSE_HOLD(get, set): Bool;
@:justReleased("pause") public var PAUSE_R(get, set): Bool;
@:gamepad([Y])
@:justPressed("reset") public var RESET(get, set): Bool;
@:pressed("reset") public var RESET_HOLD(get, set): Bool;
@:justReleased("reset") public var RESET_R(get, set): Bool;
//@:gamepad([])
//@:justPressed("cheat") public var CHEAT(get, set): Bool;
//@:pressed("cheat") public var CHEAT_HOLD(get, set): Bool;
//@:justReleased("cheat") public var CHEAT_R(get, set): Bool;
@:gamepad([FlxGamepadInputID.BACK]) // select button
@:justPressed("switchmod") public var SWITCHMOD(get, set): Bool;
@:pressed("switchmod") public var SWITCHMOD_HOLD(get, set): Bool;
@:justReleased("switchmod") public var SWITCHMOD_R(get, set): Bool;
var byName:Map<String, FlxActionDigital> = [];
public var gamepadsAdded:Array<Int> = [];
public var keyboardScheme:KeyboardScheme = None;
public function new(name, scheme = None)
{
super(name);
macro_addKeysToActions();
for (action in digitalActions)
byName[action.name] = action;
setKeyboardScheme(scheme, false);
}
public function getKeyName(control:Control, idx:Int = 0):String
{
var action = macro_getActionFromControl(control);
var input = action.inputs[idx];
return switch input.device
{
case KEYBOARD: return '${(input.inputID : FlxKey)}';
case GAMEPAD: return '${(input.inputID : FlxGamepadInputID)}';
case device: throw 'unhandled device: $device';
}
}
public function replaceBindingKeyboard(control:Control, ?toAdd:Int, ?toRemove:Int)
{
if (toAdd == toRemove)
return;
if (toRemove != null)
unbindKeys(control, [toRemove]);
if (toAdd != null)
bindKeys(control, [toAdd]);
}
public function replaceBindingGamepad(control:Control, deviceID:Int, ?toAdd:Int, ?toRemove:Int)
{
if (toAdd == toRemove)
return;
if (toRemove != null)
unbindButtons(control, deviceID, [toRemove]);
if (toAdd != null)
bindButtons(control, deviceID, [toAdd]);
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public inline function bindKeys(control:Control, keys:Array<FlxKey>)
{
macro_forEachBound(control, (action, state) -> addKeys(action, keys, state));
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public inline function unbindKeys(control:Control, keys:Array<FlxKey>)
{
macro_forEachBound(control, (action, _) -> removeKeys(action, keys));
}
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>)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (input.device == KEYBOARD && keys.contains(cast input.inputID))
action.remove(input);
}
}
public function setKeyboardScheme(scheme:KeyboardScheme, reset = true)
{
if (reset)
removeKeyboard();
keyboardScheme = scheme;
macro_bindControls(scheme);
}
function removeKeyboard()
{
for (action in this.digitalActions)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (input.device == KEYBOARD)
action.remove(input);
}
}
}
public function addGamepad(id:Int, buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
{
gamepadsAdded.push(id);
for (control => buttons in buttonMap)
bindButtons(control, id, buttons);
}
public function removeGamepad(deviceID:Int = FlxInputDeviceID.ALL):Void
{
for (action in this.digitalActions)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (isGamepad(input, deviceID))
action.remove(input);
}
}
gamepadsAdded.remove(deviceID);
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public inline function bindButtons(control:Control, id, buttons)
{
macro_forEachBound(control, (action, state) -> addButtons(action, buttons, state, id));
}
/**
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
* If binder is a literal you can inline this
*/
public inline function unbindButtons(control:Control, gamepadID:Int, buttons)
{
macro_forEachBound(control, (action, _) -> removeButtons(action, gamepadID, buttons));
}
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>)
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (isGamepad(input, gamepadID) && buttons.contains(cast input.inputID))
action.remove(input);
}
}
inline static function isGamepad(input:FlxActionInput, deviceID:Int)
{
return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID);
}
}