Files
Funkin/source/funkin/input/Controls.hx
T

1817 lines
49 KiB
Haxe

package funkin.input;
import flixel.input.actions.FlxAction.FlxActionDigital;
import funkin.util.InputUtil;
import flixel.input.FlxInput.FlxInputState;
import flixel.input.actions.FlxActionInput;
import flixel.input.actions.FlxActionSet;
import flixel.input.gamepad.FlxGamepadInputID;
import flixel.input.keyboard.FlxKey;
import flixel.math.FlxPoint;
/**
* A core class which handles receiving player input and interpreting it into game actions.
*/
class Controls extends FlxActionSet
{
/*
* A list of actions that a player would invoke via some input device.
* Uses FlxActions to funnel various inputs to a single action.
*/
var _ui_up:FunkinAction = new FunkinAction(Action.UI_UP);
var _ui_left:FunkinAction = new FunkinAction(Action.UI_LEFT);
var _ui_right:FunkinAction = new FunkinAction(Action.UI_RIGHT);
var _ui_down:FunkinAction = new FunkinAction(Action.UI_DOWN);
var _note_up:FunkinAction = new FunkinAction(Action.NOTE_UP);
var _note_left:FunkinAction = new FunkinAction(Action.NOTE_LEFT);
var _note_right:FunkinAction = new FunkinAction(Action.NOTE_RIGHT);
var _note_down:FunkinAction = new FunkinAction(Action.NOTE_DOWN);
var _accept:FunkinAction = new FunkinAction(Action.ACCEPT);
var _back:FunkinAction = new FunkinAction(Action.BACK);
var _pause:FunkinAction = new FunkinAction(Action.PAUSE);
var _reset:FunkinAction = new FunkinAction(Action.RESET);
#if FEATURE_SCREENSHOTS
var _window_screenshot:FunkinAction = new FunkinAction(Action.WINDOW_SCREENSHOT);
#end
var _window_fullscreen:FunkinAction = new FunkinAction(Action.WINDOW_FULLSCREEN);
var _freeplay_favorite:FunkinAction = new FunkinAction(Action.FREEPLAY_FAVORITE);
var _freeplay_left:FunkinAction = new FunkinAction(Action.FREEPLAY_LEFT);
var _freeplay_right:FunkinAction = new FunkinAction(Action.FREEPLAY_RIGHT);
var _freeplay_char_select:FunkinAction = new FunkinAction(Action.FREEPLAY_CHAR_SELECT);
var _freeplay_jump_to_top:FunkinAction = new FunkinAction(Action.FREEPLAY_JUMP_TO_TOP);
var _freeplay_jump_to_bottom:FunkinAction = new FunkinAction(Action.FREEPLAY_JUMP_TO_BOTTOM);
var _cutscene_advance:FunkinAction = new FunkinAction(Action.CUTSCENE_ADVANCE);
#if FEATURE_DEBUG_MENU
var _debug_menu:FunkinAction = new FunkinAction(Action.DEBUG_MENU);
#end
#if FEATURE_CHART_EDITOR
var _debug_chart:FunkinAction = new FunkinAction(Action.DEBUG_CHART);
#end
#if FEATURE_STAGE_EDITOR
var _debug_stage:FunkinAction = new FunkinAction(Action.DEBUG_STAGE);
#end
var _debug_display:FunkinAction = new FunkinAction(Action.DEBUG_DISPLAY);
var _volume_up:FunkinAction = new FunkinAction(Action.VOLUME_UP);
var _volume_down:FunkinAction = new FunkinAction(Action.VOLUME_DOWN);
var _volume_mute:FunkinAction = new FunkinAction(Action.VOLUME_MUTE);
var byName:Map<String, FunkinAction> = new Map<String, FunkinAction>();
public var gamepadsAdded:Array<Int> = [];
public var keyboardScheme = KeyboardScheme.None;
public var UI_UP(get, never):Bool;
inline function get_UI_UP():Bool
{
return _ui_up.checkPressed();
}
public var UI_LEFT(get, never):Bool;
inline function get_UI_LEFT():Bool
{
return _ui_left.checkPressed();
}
public var UI_RIGHT(get, never):Bool;
inline function get_UI_RIGHT():Bool
{
return _ui_right.checkPressed();
}
public var UI_DOWN(get, never):Bool;
inline function get_UI_DOWN():Bool
{
return _ui_down.checkPressed();
}
public var UI_UP_P(get, never):Bool;
inline function get_UI_UP_P():Bool
{
return _ui_up.checkJustPressed();
}
public var UI_LEFT_P(get, never):Bool;
inline function get_UI_LEFT_P():Bool
{
return _ui_left.checkJustPressed();
}
public var UI_RIGHT_P(get, never):Bool;
inline function get_UI_RIGHT_P():Bool
{
return _ui_right.checkJustPressed();
}
public var UI_DOWN_P(get, never):Bool;
inline function get_UI_DOWN_P():Bool
{
return _ui_down.checkJustPressed();
}
public var UI_UP_R(get, never):Bool;
inline function get_UI_UP_R():Bool
{
return _ui_up.checkJustReleased();
}
public var UI_LEFT_R(get, never):Bool;
inline function get_UI_LEFT_R():Bool
{
return _ui_left.checkJustReleased();
}
public var UI_RIGHT_R(get, never):Bool;
inline function get_UI_RIGHT_R():Bool
{
return _ui_right.checkJustReleased();
}
public var UI_DOWN_R(get, never):Bool;
inline function get_UI_DOWN_R():Bool
{
return _ui_down.checkJustReleased();
}
public var NOTE_UP(get, never):Bool;
inline function get_NOTE_UP():Bool
{
return _note_up.checkPressed();
}
public var NOTE_LEFT(get, never):Bool;
inline function get_NOTE_LEFT():Bool
{
return _note_left.checkPressed();
}
public var NOTE_RIGHT(get, never):Bool;
inline function get_NOTE_RIGHT():Bool
{
return _note_right.checkPressed();
}
public var NOTE_DOWN(get, never):Bool;
inline function get_NOTE_DOWN():Bool
{
return _note_down.checkPressed();
}
public var NOTE_UP_P(get, never):Bool;
inline function get_NOTE_UP_P():Bool
{
return _note_up.checkJustPressed();
}
public var NOTE_LEFT_P(get, never):Bool;
inline function get_NOTE_LEFT_P():Bool
{
return _note_left.checkJustPressed();
}
public var NOTE_RIGHT_P(get, never):Bool;
inline function get_NOTE_RIGHT_P():Bool
{
return _note_right.checkJustPressed();
}
public var NOTE_DOWN_P(get, never):Bool;
inline function get_NOTE_DOWN_P():Bool
{
return _note_down.checkJustPressed();
}
public var NOTE_UP_R(get, never):Bool;
inline function get_NOTE_UP_R():Bool
{
return _note_up.checkJustReleased();
}
public var NOTE_LEFT_R(get, never):Bool;
inline function get_NOTE_LEFT_R():Bool
{
return _note_left.checkJustReleased();
}
public var NOTE_RIGHT_R(get, never):Bool;
inline function get_NOTE_RIGHT_R():Bool
{
return _note_right.checkJustReleased();
}
public var NOTE_DOWN_R(get, never):Bool;
inline function get_NOTE_DOWN_R():Bool
{
return _note_down.checkJustReleased();
}
public var ACCEPT(get, never):Bool;
inline function get_ACCEPT():Bool
{
return _accept.checkPressed();
}
public var ACCEPT_P(get, never):Bool;
inline function get_ACCEPT_P():Bool
{
return _accept.checkJustPressed();
}
public var ACCEPT_R(get, never):Bool;
inline function get_ACCEPT_R():Bool
{
return _accept.checkJustReleased();
}
public var BACK(get, never):Bool;
inline function get_BACK():Bool
{
return _back.checkPressed();
}
public var BACK_P(get, never):Bool;
inline function get_BACK_P():Bool
{
return _back.checkJustPressed();
}
public var BACK_R(get, never):Bool;
inline function get_BACK_R():Bool
{
return _back.checkJustReleased();
}
public var PAUSE(get, never):Bool;
inline function get_PAUSE():Bool
{
return _pause.checkPressed();
}
public var PAUSE_P(get, never):Bool;
inline function get_PAUSE_P():Bool
{
return _pause.checkJustPressed();
}
public var PAUSE_R(get, never):Bool;
inline function get_PAUSE_R():Bool
{
return _pause.checkJustReleased();
}
public var RESET(get, never):Bool;
inline function get_RESET():Bool
{
return _reset.checkPressed();
}
public var RESET_P(get, never):Bool;
inline function get_RESET_P():Bool
{
return _reset.checkJustPressed();
}
public var RESET_R(get, never):Bool;
inline function get_RESET_R():Bool
{
return _reset.checkJustReleased();
}
public var WINDOW_FULLSCREEN(get, never):Bool;
inline function get_WINDOW_FULLSCREEN():Bool
{
return _window_fullscreen.check();
}
#if FEATURE_SCREENSHOTS
public var WINDOW_SCREENSHOT(get, never):Bool;
inline function get_WINDOW_SCREENSHOT():Bool
{
return _window_screenshot.check();
}
#end
public var FREEPLAY_FAVORITE(get, never):Bool;
inline function get_FREEPLAY_FAVORITE():Bool
{
return _freeplay_favorite.check();
}
public var FREEPLAY_LEFT(get, never):Bool;
inline function get_FREEPLAY_LEFT():Bool
{
return _freeplay_left.check();
}
public var FREEPLAY_RIGHT(get, never):Bool;
inline function get_FREEPLAY_RIGHT():Bool
{
return _freeplay_right.check();
}
public var FREEPLAY_CHAR_SELECT(get, never):Bool;
inline function get_FREEPLAY_CHAR_SELECT():Bool
{
return _freeplay_char_select.check();
}
public var FREEPLAY_JUMP_TO_TOP(get, never):Bool;
inline function get_FREEPLAY_JUMP_TO_TOP():Bool
{
return _freeplay_jump_to_top.check();
}
public var FREEPLAY_JUMP_TO_BOTTOM(get, never):Bool;
inline function get_FREEPLAY_JUMP_TO_BOTTOM():Bool
{
return _freeplay_jump_to_bottom.check();
}
public var CUTSCENE_ADVANCE(get, never):Bool;
inline function get_CUTSCENE_ADVANCE():Bool
{
return _cutscene_advance.check();
}
#if FEATURE_DEBUG_MENU
public var DEBUG_MENU(get, never):Bool;
inline function get_DEBUG_MENU():Bool
{
return _debug_menu.check();
}
#end
#if FEATURE_CHART_EDITOR
public var DEBUG_CHART(get, never):Bool;
inline function get_DEBUG_CHART():Bool
{
return _debug_chart.check();
}
#end
#if FEATURE_STAGE_EDITOR
public var DEBUG_STAGE(get, never):Bool;
inline function get_DEBUG_STAGE():Bool
{
return _debug_stage.check();
}
#end
public var DEBUG_DISPLAY(get, never):Bool;
inline function get_DEBUG_DISPLAY():Bool
{
return _debug_display.check();
}
public var VOLUME_UP(get, never):Bool;
inline function get_VOLUME_UP():Bool
{
return _volume_up.check();
}
public var VOLUME_DOWN(get, never):Bool;
inline function get_VOLUME_DOWN():Bool
{
return _volume_down.check();
}
public var VOLUME_MUTE(get, never):Bool;
inline function get_VOLUME_MUTE():Bool
{
return _volume_mute.check();
}
public function new(name, ?scheme:KeyboardScheme)
{
super(name);
add(_ui_up);
add(_ui_left);
add(_ui_right);
add(_ui_down);
add(_note_up);
add(_note_left);
add(_note_right);
add(_note_down);
add(_accept);
add(_back);
add(_pause);
add(_reset);
#if FEATURE_SCREENSHOTS add(_window_screenshot); #end
add(_window_fullscreen);
add(_freeplay_favorite);
add(_freeplay_left);
add(_freeplay_right);
add(_freeplay_char_select);
add(_freeplay_jump_to_top);
add(_freeplay_jump_to_bottom);
add(_cutscene_advance);
#if FEATURE_DEBUG_MENU add(_debug_menu); #end
#if FEATURE_CHART_EDITOR add(_debug_chart); #end
#if FEATURE_STAGE_EDITOR add(_debug_stage); #end
add(_debug_display);
add(_volume_up);
add(_volume_down);
add(_volume_mute);
for (action in digitalActions)
{
if (Std.isOfType(action, FunkinAction))
{
var funkinAction:FunkinAction = cast action;
byName[funkinAction.name] = funkinAction;
if (funkinAction.namePressed != null) byName[funkinAction.namePressed] = funkinAction;
if (funkinAction.nameReleased != null) byName[funkinAction.nameReleased] = funkinAction;
}
}
if (scheme == null) scheme = None;
setKeyboardScheme(scheme, false);
}
override function update():Void
{
super.update();
}
public function check(name:Action, trigger:FlxInputState = JUST_PRESSED, gamepadOnly:Bool = false):Bool
{
#if FEATURE_DEBUG_FUNCTIONS
if (!byName.exists(name)) throw 'Invalid name: $name';
#end
var action = byName[name];
if (gamepadOnly)
{
if(action.checkFiltered(trigger, GAMEPAD)) action.updateLastDeviceUsed();
return action.checkFiltered(trigger, GAMEPAD);
}
else
{
if(action.checkFiltered(trigger)) action.updateLastDeviceUsed();
return action.checkFiltered(trigger);
}
}
public function getKeysForAction(name:Action):Array<FlxKey>
{
#if FEATURE_DEBUG_FUNCTIONS
if (!byName.exists(name)) throw 'Invalid name: $name';
#end
// TODO: Revert to `.map().filter()` once HashLink doesn't complain anymore.
var result:Array<FlxKey> = [];
for (input in byName[name].inputs)
{
if (input.device == KEYBOARD) result.push(input.inputID);
}
return result;
}
public function getButtonsForAction(name:Action):Array<FlxGamepadInputID>
{
#if FEATURE_DEBUG_FUNCTIONS
if (!byName.exists(name)) throw 'Invalid name: $name';
#end
var result:Array<FlxGamepadInputID> = [];
for (input in byName[name].inputs)
{
if (input.device == GAMEPAD) result.push(input.inputID);
}
return result;
}
public function getDialogueName(action:FlxActionDigital, ?ignoreSurrounding:Bool = false):String
{
if (action.inputs.length == 0) return 'N/A';
var input = FunkinAction.lastDeviceUsed == KEYBOARD ? action.inputs[0] : action.inputs[1];
if (ignoreSurrounding == false)
{
return switch (FunkinAction.lastDeviceUsed)
{
case KEYBOARD:
return '[${InputUtil.format(input.inputID, Keys).toUpperCase()}]';
case GAMEPAD:
return '(${InputUtil.format(input.inputID, Gamepad(input.deviceID)).toUpperCase()})';
case device:
throw 'unhandled device: $device';
}
}
else
{
return switch (FunkinAction.lastDeviceUsed)
{
case KEYBOARD:
return InputUtil.format(input.inputID, Keys).toUpperCase();
case GAMEPAD:
return InputUtil.format(input.inputID, Gamepad(input.deviceID)).toUpperCase();
case device:
throw 'unhandled device: $device';
}
}
}
public function getDialogueNameFromToken(token:String, ?ignoreSurrounding:Bool = false):String
{
return getDialogueName(getActionFromControl(Control.createByName(token.toUpperCase())), ignoreSurrounding);
}
public function getDialogueNameFromControl(control:Control, ?ignoreSurrounding:Bool = false):String
{
return getDialogueName(getActionFromControl(control), ignoreSurrounding);
}
function getActionFromControl(control:Control):FlxActionDigital
{
return switch (control)
{
case UI_UP:
_ui_up;
case UI_DOWN:
_ui_down;
case UI_LEFT:
_ui_left;
case UI_RIGHT:
_ui_right;
case NOTE_UP:
_note_up;
case NOTE_DOWN:
_note_down;
case NOTE_LEFT:
_note_left;
case NOTE_RIGHT:
_note_right;
case ACCEPT:
_accept;
case BACK:
_back;
case PAUSE:
_pause;
case RESET:
_reset;
#if FEATURE_SCREENSHOTS
case WINDOW_SCREENSHOT:
_window_screenshot;
#end
case WINDOW_FULLSCREEN:
_window_fullscreen;
case FREEPLAY_FAVORITE:
_freeplay_favorite;
case FREEPLAY_LEFT:
_freeplay_left;
case FREEPLAY_RIGHT:
_freeplay_right;
case FREEPLAY_CHAR_SELECT:
_freeplay_char_select;
case FREEPLAY_JUMP_TO_TOP:
_freeplay_jump_to_top;
case FREEPLAY_JUMP_TO_BOTTOM:
_freeplay_jump_to_bottom;
case CUTSCENE_ADVANCE:
_cutscene_advance;
#if FEATURE_DEBUG_MENU
case DEBUG_MENU:
_debug_menu;
#end
#if FEATURE_CHART_EDITOR
case DEBUG_CHART:
_debug_chart;
#end
#if FEATURE_STAGE_EDITOR
case DEBUG_STAGE:
_debug_stage;
#end
case DEBUG_DISPLAY:
_debug_display;
case VOLUME_UP:
_volume_up;
case VOLUME_DOWN:
_volume_down;
case VOLUME_MUTE:
_volume_mute;
}
}
/**
* Calls a function passing each action bound by the specified control
* @param control
* @param func
*/
function forEachBound(control:Control, func:FunkinAction->FlxInputState->Void):Void
{
switch (control)
{
case UI_UP:
func(_ui_up, PRESSED);
func(_ui_up, JUST_PRESSED);
func(_ui_up, JUST_RELEASED);
case UI_LEFT:
func(_ui_left, PRESSED);
func(_ui_left, JUST_PRESSED);
func(_ui_left, JUST_RELEASED);
case UI_RIGHT:
func(_ui_right, PRESSED);
func(_ui_right, JUST_PRESSED);
func(_ui_right, JUST_RELEASED);
case UI_DOWN:
func(_ui_down, PRESSED);
func(_ui_down, JUST_PRESSED);
func(_ui_down, JUST_RELEASED);
case NOTE_UP:
func(_note_up, PRESSED);
func(_note_up, JUST_PRESSED);
func(_note_up, JUST_RELEASED);
case NOTE_LEFT:
func(_note_left, PRESSED);
func(_note_left, JUST_PRESSED);
func(_note_left, JUST_RELEASED);
case NOTE_RIGHT:
func(_note_right, PRESSED);
func(_note_right, JUST_PRESSED);
func(_note_right, JUST_RELEASED);
case NOTE_DOWN:
func(_note_down, PRESSED);
func(_note_down, JUST_PRESSED);
func(_note_down, JUST_RELEASED);
case ACCEPT:
func(_accept, PRESSED);
func(_accept, JUST_PRESSED);
func(_accept, JUST_RELEASED);
case BACK:
func(_back, PRESSED);
func(_back, JUST_PRESSED);
func(_back, JUST_RELEASED);
case PAUSE:
func(_pause, PRESSED);
func(_pause, JUST_PRESSED);
func(_pause, JUST_RELEASED);
case RESET:
func(_reset, PRESSED);
func(_reset, JUST_PRESSED);
func(_reset, JUST_RELEASED);
#if FEATURE_SCREENSHOTS
case WINDOW_SCREENSHOT:
func(_window_screenshot, JUST_PRESSED);
#end
case WINDOW_FULLSCREEN:
func(_window_fullscreen, JUST_PRESSED);
case FREEPLAY_FAVORITE:
func(_freeplay_favorite, JUST_PRESSED);
case FREEPLAY_LEFT:
func(_freeplay_left, JUST_PRESSED);
case FREEPLAY_RIGHT:
func(_freeplay_right, JUST_PRESSED);
case FREEPLAY_CHAR_SELECT:
func(_freeplay_char_select, JUST_PRESSED);
case FREEPLAY_JUMP_TO_TOP:
func(_freeplay_jump_to_top, JUST_PRESSED);
case FREEPLAY_JUMP_TO_BOTTOM:
func(_freeplay_jump_to_bottom, JUST_PRESSED);
case CUTSCENE_ADVANCE:
func(_cutscene_advance, JUST_PRESSED);
#if FEATURE_DEBUG_MENU
case DEBUG_MENU:
func(_debug_menu, JUST_PRESSED);
#end
#if FEATURE_CHART_EDITOR
case DEBUG_CHART:
func(_debug_chart, JUST_PRESSED);
#end
#if FEATURE_STAGE_EDITOR
case DEBUG_STAGE:
func(_debug_stage, JUST_PRESSED);
#end
case DEBUG_DISPLAY:
func(_debug_display, JUST_PRESSED);
case VOLUME_UP:
func(_volume_up, JUST_PRESSED);
case VOLUME_DOWN:
func(_volume_down, JUST_PRESSED);
case VOLUME_MUTE:
func(_volume_mute, JUST_PRESSED);
}
}
public function replaceBinding(control:Control, device:Device, toAdd:Int, toRemove:Int):Void
{
if (toAdd == toRemove) return;
switch (device)
{
case Keys:
forEachBound(control, function(action, state) replaceKey(action, toAdd, toRemove, state));
case Gamepad(id):
forEachBound(control, function(action, state) replaceButton(action, id, toAdd, toRemove, state));
}
}
function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState):Void
{
if (action.inputs.length == 0)
{
// Add the keybind, don't replace.
addKeys(action, [toAdd], state);
return;
}
var hasReplaced:Bool = false;
for (i in 0...action.inputs.length)
{
var input = action.inputs[i];
if (input == null) continue;
if (input.device == KEYBOARD && input.inputID == toRemove)
{
if (toAdd == FlxKey.NONE)
{
// Remove the keybind, don't replace.
action.inputs.remove(input);
}
else
{
// Replace the keybind.
@:privateAccess
action.inputs[i].inputID = toAdd;
}
hasReplaced = true;
}
else if (input.device == KEYBOARD && input.inputID == toAdd)
{
// This key is already bound!
if (hasReplaced)
{
// Remove the duplicate keybind, don't replace.
action.inputs.remove(input);
}
else
{
hasReplaced = true;
}
}
}
if (!hasReplaced)
{
addKeys(action, [toAdd], state);
}
}
function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState):Void
{
if (action.inputs.length == 0)
{
addButtons(action, [toAdd], state, deviceID);
return;
}
var hasReplaced:Bool = false;
for (i in 0...action.inputs.length)
{
var input = action.inputs[i];
if (input == null) continue;
if (isGamepad(input, deviceID) && input.inputID == toRemove)
{
@:privateAccess
action.inputs[i].inputID = toAdd;
hasReplaced = true;
}
}
if (!hasReplaced)
{
addButtons(action, [toAdd], state, deviceID);
}
}
public function copyFrom(controls:Controls, ?device:Device):Void
{
for (name in controls.byName.keys())
{
var action = controls.byName[name];
for (input in action.inputs)
{
if (device == null || isDevice(input, device)) byName[name].add(cast input);
}
}
switch (device)
{
case null:
// add all
for (gamepad in controls.gamepadsAdded) if (gamepadsAdded.indexOf(gamepad) == -1) gamepadsAdded.push(gamepad);
mergeKeyboardScheme(controls.keyboardScheme);
case Gamepad(id):
gamepadsAdded.push(id);
case Keys:
mergeKeyboardScheme(controls.keyboardScheme);
}
}
public inline function copyTo(controls:Controls, ?device:Device):Void
{
controls.copyFrom(this, device);
}
function mergeKeyboardScheme(scheme:KeyboardScheme):Void
{
if (scheme != None)
{
switch (keyboardScheme)
{
case None:
keyboardScheme = scheme;
default:
keyboardScheme = Custom;
}
}
}
/**
* 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 function bindKeys(control:Control, keys:Array<FlxKey>):Void
{
forEachBound(control, function(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 function unbindKeys(control:Control, keys:Array<FlxKey>):Void
{
forEachBound(control, function(action, _) removeKeys(action, keys));
}
static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState):Void
{
for (key in keys)
{
if (key == FlxKey.NONE) continue; // Ignore unbound keys.
action.addKey(key, state);
}
}
static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>):Void
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (input.device == KEYBOARD && keys.indexOf(cast input.inputID) != -1) action.remove(input);
}
}
public function setKeyboardScheme(scheme:KeyboardScheme, reset = true)
{
if (reset) removeKeyboard();
keyboardScheme = scheme;
bindKeys(Control.UI_UP, getDefaultKeybinds(scheme, Control.UI_UP));
bindKeys(Control.UI_DOWN, getDefaultKeybinds(scheme, Control.UI_DOWN));
bindKeys(Control.UI_LEFT, getDefaultKeybinds(scheme, Control.UI_LEFT));
bindKeys(Control.UI_RIGHT, getDefaultKeybinds(scheme, Control.UI_RIGHT));
bindKeys(Control.NOTE_UP, getDefaultKeybinds(scheme, Control.NOTE_UP));
bindKeys(Control.NOTE_DOWN, getDefaultKeybinds(scheme, Control.NOTE_DOWN));
bindKeys(Control.NOTE_LEFT, getDefaultKeybinds(scheme, Control.NOTE_LEFT));
bindKeys(Control.NOTE_RIGHT, getDefaultKeybinds(scheme, Control.NOTE_RIGHT));
bindKeys(Control.ACCEPT, getDefaultKeybinds(scheme, Control.ACCEPT));
bindKeys(Control.BACK, getDefaultKeybinds(scheme, Control.BACK));
bindKeys(Control.PAUSE, getDefaultKeybinds(scheme, Control.PAUSE));
bindKeys(Control.RESET, getDefaultKeybinds(scheme, Control.RESET));
#if FEATURE_SCREENSHOTS
bindKeys(Control.WINDOW_SCREENSHOT, getDefaultKeybinds(scheme, Control.WINDOW_SCREENSHOT));
#end
bindKeys(Control.WINDOW_FULLSCREEN, getDefaultKeybinds(scheme, Control.WINDOW_FULLSCREEN));
bindKeys(Control.FREEPLAY_FAVORITE, getDefaultKeybinds(scheme, Control.FREEPLAY_FAVORITE));
bindKeys(Control.FREEPLAY_LEFT, getDefaultKeybinds(scheme, Control.FREEPLAY_LEFT));
bindKeys(Control.FREEPLAY_RIGHT, getDefaultKeybinds(scheme, Control.FREEPLAY_RIGHT));
bindKeys(Control.FREEPLAY_CHAR_SELECT, getDefaultKeybinds(scheme, Control.FREEPLAY_CHAR_SELECT));
bindKeys(Control.FREEPLAY_JUMP_TO_TOP, getDefaultKeybinds(scheme, Control.FREEPLAY_JUMP_TO_TOP));
bindKeys(Control.FREEPLAY_JUMP_TO_BOTTOM, getDefaultKeybinds(scheme, Control.FREEPLAY_JUMP_TO_BOTTOM));
bindKeys(Control.CUTSCENE_ADVANCE, getDefaultKeybinds(scheme, Control.CUTSCENE_ADVANCE));
#if FEATURE_DEBUG_MENU
bindKeys(Control.DEBUG_MENU, getDefaultKeybinds(scheme, Control.DEBUG_MENU));
#end
#if FEATURE_CHART_EDITOR
bindKeys(Control.DEBUG_CHART, getDefaultKeybinds(scheme, Control.DEBUG_CHART));
#end
#if FEATURE_STAGE_EDITOR
bindKeys(Control.DEBUG_STAGE, getDefaultKeybinds(scheme, Control.DEBUG_STAGE));
#end
bindKeys(Control.DEBUG_DISPLAY, getDefaultKeybinds(scheme, Control.DEBUG_DISPLAY));
bindKeys(Control.VOLUME_UP, getDefaultKeybinds(scheme, Control.VOLUME_UP));
bindKeys(Control.VOLUME_DOWN, getDefaultKeybinds(scheme, Control.VOLUME_DOWN));
bindKeys(Control.VOLUME_MUTE, getDefaultKeybinds(scheme, Control.VOLUME_MUTE));
}
function getDefaultKeybinds(scheme:KeyboardScheme, control:Control):Array<FlxKey>
{
switch (scheme)
{
case Solo:
switch (control)
{
case Control.UI_UP:
return [W, FlxKey.UP];
case Control.UI_DOWN:
return [S, FlxKey.DOWN];
case Control.UI_LEFT:
return [A, FlxKey.LEFT];
case Control.UI_RIGHT:
return [D, FlxKey.RIGHT];
case Control.NOTE_UP:
return [W, FlxKey.UP];
case Control.NOTE_DOWN:
return [S, FlxKey.DOWN];
case Control.NOTE_LEFT:
return [A, FlxKey.LEFT];
case Control.NOTE_RIGHT:
return [D, FlxKey.RIGHT];
case Control.ACCEPT:
return [Z, SPACE, ENTER];
case Control.BACK:
return [X, BACKSPACE, ESCAPE];
case Control.PAUSE:
return [P, ENTER, ESCAPE];
case Control.RESET:
return [R];
case Control.WINDOW_FULLSCREEN:
return [F11]; // We use F for other things LOL.
#if FEATURE_SCREENSHOTS
case Control.WINDOW_SCREENSHOT:
return [F3];
#end
case Control.FREEPLAY_FAVORITE:
return [F]; // Favorite a song on the menu
case Control.FREEPLAY_LEFT:
return [Q]; // Switch tabs on the menu
case Control.FREEPLAY_RIGHT:
return [E]; // Switch tabs on the menu
case Control.FREEPLAY_CHAR_SELECT:
return [TAB];
case Control.FREEPLAY_JUMP_TO_TOP:
return [HOME];
case Control.FREEPLAY_JUMP_TO_BOTTOM:
return [END];
case Control.CUTSCENE_ADVANCE:
return [Z, ENTER];
#if FEATURE_DEBUG_MENU
case Control.DEBUG_MENU:
return [GRAVEACCENT];
#end
#if FEATURE_CHART_EDITOR
case Control.DEBUG_CHART:
return [];
#end
#if FEATURE_STAGE_EDITOR
case Control.DEBUG_STAGE:
return [];
#end
case Control.DEBUG_DISPLAY:
return [F6];
case Control.VOLUME_UP:
return [PLUS, NUMPADPLUS];
case Control.VOLUME_DOWN:
return [MINUS, NUMPADMINUS];
case Control.VOLUME_MUTE:
return [ZERO, NUMPADZERO];
}
case Duo(true):
switch (control)
{
case Control.UI_UP:
return [W];
case Control.UI_DOWN:
return [S];
case Control.UI_LEFT:
return [A];
case Control.UI_RIGHT:
return [D];
case Control.NOTE_UP:
return [W];
case Control.NOTE_DOWN:
return [S];
case Control.NOTE_LEFT:
return [A];
case Control.NOTE_RIGHT:
return [D];
case Control.ACCEPT:
return [G, Z];
case Control.BACK:
return [H, X];
case Control.PAUSE:
return [ONE];
case Control.RESET:
return [R];
#if FEATURE_SCREENSHOTS
case Control.WINDOW_SCREENSHOT:
return [F3];
#end
case Control.WINDOW_FULLSCREEN:
return [F11];
case Control.FREEPLAY_FAVORITE:
return [F]; // Favorite a song on the menu
case Control.FREEPLAY_LEFT:
return [Q]; // Switch tabs on the menu
case Control.FREEPLAY_RIGHT:
return [E]; // Switch tabs on the menu
case Control.FREEPLAY_CHAR_SELECT:
return [TAB];
case Control.FREEPLAY_JUMP_TO_TOP:
return [HOME];
case Control.FREEPLAY_JUMP_TO_BOTTOM:
return [END];
case Control.CUTSCENE_ADVANCE:
return [G, Z];
#if FEATURE_DEBUG_MENU
case Control.DEBUG_MENU:
return [GRAVEACCENT];
#end
#if FEATURE_CHART_EDITOR
case Control.DEBUG_CHART:
return [];
#end
#if FEATURE_STAGE_EDITOR
case Control.DEBUG_STAGE:
return [];
#end
case Control.DEBUG_DISPLAY:
return [F6];
case Control.VOLUME_UP:
return [PLUS];
case Control.VOLUME_DOWN:
return [MINUS];
case Control.VOLUME_MUTE:
return [ZERO];
}
case Duo(false):
switch (control)
{
case Control.UI_UP:
return [FlxKey.UP];
case Control.UI_DOWN:
return [FlxKey.DOWN];
case Control.UI_LEFT:
return [FlxKey.LEFT];
case Control.UI_RIGHT:
return [FlxKey.RIGHT];
case Control.NOTE_UP:
return [FlxKey.UP];
case Control.NOTE_DOWN:
return [FlxKey.DOWN];
case Control.NOTE_LEFT:
return [FlxKey.LEFT];
case Control.NOTE_RIGHT:
return [FlxKey.RIGHT];
case Control.ACCEPT:
return [ENTER];
case Control.BACK:
return [ESCAPE];
case Control.PAUSE:
return [ONE];
case Control.RESET:
return [R];
#if FEATURE_SCREENSHOTS
case Control.WINDOW_SCREENSHOT:
return [];
#end
case Control.WINDOW_FULLSCREEN:
return [];
case Control.FREEPLAY_FAVORITE:
return [];
case Control.FREEPLAY_LEFT:
return [];
case Control.FREEPLAY_RIGHT:
return [];
case Control.FREEPLAY_CHAR_SELECT:
return [];
case Control.FREEPLAY_JUMP_TO_TOP:
return [];
case Control.FREEPLAY_JUMP_TO_BOTTOM:
return [];
case Control.CUTSCENE_ADVANCE:
return [ENTER];
#if FEATURE_DEBUG_MENU
case Control.DEBUG_MENU:
return [];
#end
#if FEATURE_CHART_EDITOR
case Control.DEBUG_CHART:
return [];
#end
#if FEATURE_STAGE_EDITOR
case Control.DEBUG_STAGE:
return [];
#end
case Control.DEBUG_DISPLAY:
return [];
case Control.VOLUME_UP:
return [NUMPADPLUS];
case Control.VOLUME_DOWN:
return [NUMPADMINUS];
case Control.VOLUME_MUTE:
return [NUMPADZERO];
}
default:
// Fallthrough.
}
return [];
}
function removeKeyboard():Void
{
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 addGamepadWithSaveData(id:Int, ?padData:Dynamic):Void
{
gamepadsAdded.push(id);
fromSaveData(padData, Gamepad(id));
}
inline function addGamepadLiteral(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
{
gamepadsAdded.push(id);
for (control in buttonMap.keys()) bindButtons(control, id, buttonMap[control]);
}
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);
}
public function addDefaultGamepad(id:Int):Void
{
addGamepadLiteral(id, [
Control.ACCEPT => getDefaultGamepadBinds(Control.ACCEPT),
Control.BACK => getDefaultGamepadBinds(Control.BACK),
Control.UI_UP => getDefaultGamepadBinds(Control.UI_UP),
Control.UI_DOWN => getDefaultGamepadBinds(Control.UI_DOWN),
Control.UI_LEFT => getDefaultGamepadBinds(Control.UI_LEFT),
Control.UI_RIGHT => getDefaultGamepadBinds(Control.UI_RIGHT),
Control.NOTE_UP => getDefaultGamepadBinds(Control.NOTE_UP),
Control.NOTE_DOWN => getDefaultGamepadBinds(Control.NOTE_DOWN),
Control.NOTE_LEFT => getDefaultGamepadBinds(Control.NOTE_LEFT),
Control.NOTE_RIGHT => getDefaultGamepadBinds(Control.NOTE_RIGHT),
Control.PAUSE => getDefaultGamepadBinds(Control.PAUSE),
Control.RESET => getDefaultGamepadBinds(Control.RESET),
Control.WINDOW_FULLSCREEN => getDefaultGamepadBinds(Control.WINDOW_FULLSCREEN),
#if FEATURE_SCREENSHOTS Control.WINDOW_SCREENSHOT => getDefaultGamepadBinds(Control.WINDOW_SCREENSHOT), #end
Control.CUTSCENE_ADVANCE => getDefaultGamepadBinds(Control.CUTSCENE_ADVANCE),
Control.FREEPLAY_FAVORITE => getDefaultGamepadBinds(Control.FREEPLAY_FAVORITE),
Control.FREEPLAY_LEFT => getDefaultGamepadBinds(Control.FREEPLAY_LEFT),
Control.FREEPLAY_RIGHT => getDefaultGamepadBinds(Control.FREEPLAY_RIGHT),
Control.FREEPLAY_CHAR_SELECT => getDefaultGamepadBinds(Control.FREEPLAY_CHAR_SELECT),
Control.FREEPLAY_JUMP_TO_TOP => getDefaultGamepadBinds(Control.FREEPLAY_JUMP_TO_TOP),
Control.FREEPLAY_JUMP_TO_BOTTOM => getDefaultGamepadBinds(Control.FREEPLAY_JUMP_TO_BOTTOM),
Control.VOLUME_UP => getDefaultGamepadBinds(Control.VOLUME_UP),
Control.VOLUME_DOWN => getDefaultGamepadBinds(Control.VOLUME_DOWN),
Control.VOLUME_MUTE => getDefaultGamepadBinds(Control.VOLUME_MUTE),
#if FEATURE_DEBUG_MENU Control.DEBUG_MENU => getDefaultGamepadBinds(Control.DEBUG_MENU), #end
#if FEATURE_CHART_EDITOR Control.DEBUG_CHART => getDefaultGamepadBinds(Control.DEBUG_CHART), #end
#if FEATURE_STAGE_EDITOR Control.DEBUG_STAGE => getDefaultGamepadBinds(Control.DEBUG_STAGE), #end
Control.DEBUG_DISPLAY => getDefaultGamepadBinds(Control.DEBUG_DISPLAY),
]);
}
function getDefaultGamepadBinds(control:Control):Array<FlxGamepadInputID>
{
return switch (control)
{
case Control.ACCEPT:
[A];
case Control.BACK:
[B];
case Control.UI_UP:
[DPAD_UP, LEFT_STICK_DIGITAL_UP];
case Control.UI_DOWN:
[DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN];
case Control.UI_LEFT:
[DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT];
case Control.UI_RIGHT:
[DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT];
case Control.NOTE_UP:
[DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP];
case Control.NOTE_DOWN:
[DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN];
case Control.NOTE_LEFT:
[DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT];
case Control.NOTE_RIGHT:
[DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT];
case Control.PAUSE:
[START];
case Control.RESET:
[FlxGamepadInputID.BACK]; // Back (i.e. Select)
case Control.WINDOW_FULLSCREEN:
[];
#if FEATURE_SCREENSHOTS
case Control.WINDOW_SCREENSHOT:
[];
#end
case Control.CUTSCENE_ADVANCE:
[A];
case Control.FREEPLAY_FAVORITE:
[Y]; // Back (i.e. Select)
case Control.FREEPLAY_LEFT:
[LEFT_SHOULDER];
case Control.FREEPLAY_RIGHT:
[RIGHT_SHOULDER];
case Control.FREEPLAY_CHAR_SELECT:
[X];
case Control.FREEPLAY_JUMP_TO_TOP:
[RIGHT_STICK_DIGITAL_UP];
case Control.FREEPLAY_JUMP_TO_BOTTOM:
[RIGHT_STICK_DIGITAL_DOWN];
case Control.VOLUME_UP:
[];
case Control.VOLUME_DOWN:
[];
case Control.VOLUME_MUTE:
[];
#if FEATURE_DEBUG_MENU
case Control.DEBUG_MENU:
[];
#end
#if FEATURE_CHART_EDITOR
case Control.DEBUG_CHART:
[];
#end
#if FEATURE_STAGE_EDITOR
case Control.DEBUG_STAGE:
[];
#end
case Control.DEBUG_DISPLAY:
[];
default:
[];
}
}
/**
* 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 function bindButtons(control:Control, id:Int, buttons):Void
{
forEachBound(control, function(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 function unbindButtons(control:Control, gamepadID:Int, buttons):Void
{
forEachBound(control, function(action, _) removeButtons(action, gamepadID, buttons));
}
static inline function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id:Int):Void
{
for (button in buttons)
{
if (button == FlxGamepadInputID.NONE) continue; // Ignore unbound keys.
action.addGamepad(button, state, id);
}
}
static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>):Void
{
var i = action.inputs.length;
while (i-- > 0)
{
var input = action.inputs[i];
if (isGamepad(input, gamepadID) && buttons.indexOf(cast input.inputID) != -1) action.remove(input);
}
}
public function getInputsFor(control:Control, device:Device, ?list:Array<Int>):Array<Int>
{
if (list == null) list = [];
switch (device)
{
case Keys:
for (input in getActionFromControl(control).inputs)
{
if (input.device == KEYBOARD) list.push(input.inputID);
}
case Gamepad(id):
for (input in getActionFromControl(control).inputs)
{
if (isGamepad(input, id)) list.push(input.inputID);
}
}
return list;
}
/**
* NOTE: When loading controls:
* An EMPTY array means the control is uninitialized and needs to be reset to default.
* An array with a single FlxKey.NONE means the control was intentionally unbound by the user.
*/
public function fromSaveData(data:Dynamic, device:Device):Void
{
for (control in Control.createAll())
{
var inputs:Array<Int> = Reflect.field(data, control.getName());
inputs = inputs?.distinct();
if (inputs != null)
{
if (inputs.length == 0)
{
trace('Control ${control} is missing bindings, resetting to default.');
switch (device)
{
case Keys:
bindKeys(control, getDefaultKeybinds(Solo, control));
case Gamepad(id):
bindButtons(control, id, getDefaultGamepadBinds(control));
}
}
else if (inputs == [FlxKey.NONE])
{
trace('Control ${control} is unbound, leaving it be.');
}
else
{
switch (device)
{
case Keys:
bindKeys(control, inputs.copy());
case Gamepad(id):
bindButtons(control, id, inputs.copy());
}
}
}
else
{
trace('Control ${control} is missing bindings, resetting to default.');
switch (device)
{
case Keys:
bindKeys(control, getDefaultKeybinds(Solo, control));
case Gamepad(id):
bindButtons(control, id, getDefaultGamepadBinds(control));
}
}
}
}
/**
* NOTE: When saving controls:
* An EMPTY array means the control is uninitialized and needs to be reset to default.
* An array with a single FlxKey.NONE means the control was intentionally unbound by the user.
*/
public function createSaveData(device:Device):Dynamic
{
var isEmpty = true;
var data = {};
for (control in Control.createAll())
{
var inputs = getInputsFor(control, device);
isEmpty = isEmpty && inputs.length == 0;
if (inputs.length == 0)
{
inputs = [FlxKey.NONE];
}
else
{
inputs = inputs.distinct();
}
Reflect.setField(data, control.getName(), inputs);
}
return isEmpty ? null : data;
}
static function isDevice(input:FlxActionInput, device:Device):Bool
{
return switch (device)
{
case Keys:
input.device == KEYBOARD;
case Gamepad(id):
isGamepad(input, id);
}
}
static inline function isGamepad(input:FlxActionInput, deviceID:Int):Bool
{
return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID);
}
}
typedef SaveInputLists =
{
?keys:Array<Int>,
?pad:Array<Int>
};
typedef Swipes =
{
?initTouchPos:FlxPoint,
?touchAngle:Float,
?touchLength:Float,
?curTouchPos:FlxPoint
};
/**
* An FlxActionDigital with additional functionality, including:
* - Combining `pressed` and `released` inputs into one action.
* - Filtering by input method (`KEYBOARD`, `MOUSE`, `GAMEPAD`, etc).
*/
class FunkinAction extends FlxActionDigital
{
public var namePressed(default, null):Null<String>;
public var nameReleased(default, null):Null<String>;
var cache:Map<String,
{timestamp:Float, value:Bool}> = [];
public function new(?name:String = '', ?namePressed:String, ?nameReleased:String)
{
super(name);
this.namePressed = namePressed;
this.nameReleased = nameReleased;
updateLastDeviceUsed();
}
/**
* Input checks default to whether the input was just pressed, on any input device.
*/
override public function check():Bool
{
return checkFiltered(JUST_PRESSED);
}
/**
* Check whether the input is currently being held.
*/
public function checkPressed():Bool
{
if (checkFiltered(PRESSED)) updateLastDeviceUsed();
return checkFiltered(PRESSED);
}
/**
* Check whether the input is currently being held, and was not held last frame.
*/
public function checkJustPressed():Bool
{
if (checkFiltered(JUST_PRESSED)) updateLastDeviceUsed();
return checkFiltered(JUST_PRESSED);
}
/**
* Check whether the input is not currently being held.
*/
public function checkReleased():Bool
{
return checkFiltered(RELEASED);
}
/**
* Check whether the input is not currently being held, and was held last frame.
*/
public function checkJustReleased():Bool
{
if (checkFiltered(JUST_RELEASED)) updateLastDeviceUsed();
return checkFiltered(JUST_RELEASED);
}
/**
* Check whether the input is currently being held by a gamepad device.
*/
public function checkPressedGamepad():Bool
{
return checkFiltered(PRESSED, GAMEPAD);
}
/**
* Check whether the input is currently being held by a gamepad device, and was not held last frame.
*/
public function checkJustPressedGamepad():Bool
{
return checkFiltered(JUST_PRESSED, GAMEPAD);
}
/**
* Check whether the input is not currently being held by a gamepad device.
*/
public function checkReleasedGamepad():Bool
{
return checkFiltered(RELEASED, GAMEPAD);
}
/**
* Check whether the input is not currently being held by a gamepad device, and was held last frame.
*/
public function checkJustReleasedGamepad():Bool
{
return checkFiltered(JUST_RELEASED, GAMEPAD);
}
public function checkMultiFiltered(?filterTriggers:Array<FlxInputState>, ?filterDevices:Array<FlxInputDevice>):Bool
{
filterTriggers ??= [PRESSED, JUST_PRESSED];
filterDevices ??= [];
// Perform checkFiltered for each combination.
for (i in filterTriggers)
{
if (filterDevices.length == 0)
{
if (checkFiltered(i))
{
return true;
}
}
else
{
for (j in filterDevices)
{
if (checkFiltered(i, j))
{
return true;
}
}
}
}
return false;
}
/**
* Performs the functionality of `FlxActionDigital.check()`, but with optional filters.
* @param action The action to check for.
* @param filterTrigger Optionally filter by trigger condition (`JUST_PRESSED`, `PRESSED`, `JUST_RELEASED`, `RELEASED`).
* @param filterDevice Optionally filter by device (`KEYBOARD`, `MOUSE`, `GAMEPAD`, `OTHER`).
* @return bool if our input has been triggered
*/
public function checkFiltered(?filterTrigger:FlxInputState, ?filterDevice:FlxInputDevice):Bool
{
// Make sure we only update the inputs once per frame.
var key = '${filterTrigger}:${filterDevice}';
var cacheEntry = cache.get(key);
if (cacheEntry != null && cacheEntry.timestamp == FlxG.game.ticks)
{
return cacheEntry.value;
}
_x = null;
_y = null;
_timestamp = FlxG.game.ticks;
triggered = false;
var i = inputs?.length ?? 0;
while (i-- > 0) // Iterate backwards, since we may remove items
{
var input = inputs[i];
// Filter out dead inputs.
if (input.destroyed)
{
inputs.remove(input);
continue;
}
// Update the input.
input.update();
// Check whether the input is the right trigger.
if (filterTrigger != null && input.trigger != filterTrigger)
{
continue;
}
// Check whether the input is the right device.
if (filterDevice != null && input.device != filterDevice)
{
continue;
}
// Check whether the input has triggered.
if (input.check(this))
{
triggered = true;
}
}
cache.set(key, {timestamp: FlxG.game.ticks, value: triggered});
return triggered;
}
public static var lastDeviceUsed:FlxInputDevice;
/**
* Checks which is the last device you have used and stores the value in `lastDeviceUsed`.
*/
public function updateLastDeviceUsed()
{
if (FlxG.keys.pressed.ANY)
{
lastDeviceUsed = FlxInputDevice.KEYBOARD;
return;
}
if (FlxG.gamepads.lastActive != null)
{
lastDeviceUsed = FlxInputDevice.GAMEPAD;
return;
}
// Default value (just in case everything's null somehow)
lastDeviceUsed = FlxInputDevice.KEYBOARD;
}
}
/**
* Since, in many cases multiple actions should use similar keys, we don't want the
* rebinding UI to list every action. ActionBinders are what the user percieves as
* an input so, for instance, they can't set jump-press and jump-release to different keys.
*/
enum Control
{
// NOTE
NOTE_LEFT;
NOTE_DOWN;
NOTE_UP;
NOTE_RIGHT;
// UI
UI_LEFT;
UI_DOWN;
UI_UP;
UI_RIGHT;
ACCEPT;
BACK;
PAUSE;
RESET;
// CUTSCENE
CUTSCENE_ADVANCE;
// FREEPLAY
FREEPLAY_FAVORITE;
FREEPLAY_LEFT;
FREEPLAY_RIGHT;
FREEPLAY_CHAR_SELECT;
FREEPLAY_JUMP_TO_TOP;
FREEPLAY_JUMP_TO_BOTTOM;
// WINDOW
#if FEATURE_SCREENSHOTS WINDOW_SCREENSHOT; #end
WINDOW_FULLSCREEN;
// VOLUME
VOLUME_UP;
VOLUME_DOWN;
VOLUME_MUTE;
// DEBUG
#if FEATURE_DEBUG_MENU DEBUG_MENU; #end
#if FEATURE_CHART_EDITOR DEBUG_CHART; #end
#if FEATURE_STAGE_EDITOR DEBUG_STAGE; #end
DEBUG_DISPLAY;
}
enum abstract Action(String) to String from String
{
// NOTE
public var NOTE_UP = 'note_up';
public var NOTE_LEFT = 'note_left';
public var NOTE_RIGHT = 'note_right';
public var NOTE_DOWN = 'note_down';
// UI
public var UI_UP = 'ui_up';
public var UI_LEFT = 'ui_left';
public var UI_RIGHT = 'ui_right';
public var UI_DOWN = 'ui_down';
public var ACCEPT = 'accept';
public var BACK = 'back';
public var PAUSE = 'pause';
public var RESET = 'reset';
// WINDOW
public var WINDOW_FULLSCREEN = 'window_fullscreen';
#if FEATURE_SCREENSHOTS
var WINDOW_SCREENSHOT = 'window_screenshot';
#end
// CUTSCENE
public var CUTSCENE_ADVANCE = 'cutscene_advance';
// FREEPLAY
public var FREEPLAY_FAVORITE = 'freeplay_favorite';
public var FREEPLAY_LEFT = 'freeplay_left';
public var FREEPLAY_RIGHT = 'freeplay_right';
public var FREEPLAY_CHAR_SELECT = 'freeplay_char_select';
public var FREEPLAY_JUMP_TO_TOP = 'freeplay_jump_to_top';
public var FREEPLAY_JUMP_TO_BOTTOM = 'freeplay_jump_to_bottom';
// VOLUME
public var VOLUME_UP = 'volume_up';
public var VOLUME_DOWN = 'volume_down';
public var VOLUME_MUTE = 'volume_mute';
// DEBUG
#if FEATURE_DEBUG_MENU
var DEBUG_MENU = 'debug_menu';
#end
#if FEATURE_CHART_EDITOR
var DEBUG_CHART = 'debug_chart';
#end
#if FEATURE_STAGE_EDITOR
var DEBUG_STAGE = 'debug_stage';
#end
public var DEBUG_DISPLAY = 'debug_display';
}
enum Device
{
Keys;
Gamepad(id:Int);
}
enum KeyboardScheme
{
Solo;
Duo(first:Bool);
None;
Custom;
}