Skip to content

Commit

Permalink
Hardcoded controller support
Browse files Browse the repository at this point in the history
To quote myself:
"Having all of these hardcoded in the mod is terrible, but this is genuinely the best way I've been able to find after over a week of testing."
  • Loading branch information
SabreML committed Apr 2, 2023
1 parent 0b1363c commit cc1e799
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 43 deletions.
2 changes: 1 addition & 1 deletion JollyRebind/modinfo.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "sabreml.jollyrebind",
"name": "Jolly Rebind",
"version": "1.1.2",
"version": "1.2.0",
"target_game_version": "v1.9.07b",
"authors": "SabreML",
"description": "Adds a customisable input to change the Jolly Co-op pointing button.",
Expand Down
1 change: 1 addition & 0 deletions src/JollyMenuKeybinds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ private static void JollyPlayerSelector_UpdateHK(On.JollyCoop.JollyMenu.JollyPla
keybindWrappers[self.index].ThisConfig.greyedOut = !self.Joined;
}

// Move the custom colour buttons out of the way of the keybinders.
private static void JollyPlayerSelector_AddColorButtonHK(On.JollyCoop.JollyMenu.JollyPlayerSelector.orig_AddColorButton orig, JollyPlayerSelector self)
{
orig(self);
Expand Down
1 change: 1 addition & 0 deletions src/JollyRebindConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ private static void ModButtonHK(On.Menu.Remix.MenuModList.ModButton.orig_ctor or
if (self.itf.GetType() == typeof(JollyRebindConfig))
{
// `type` is a readonly field, so it can't be assigned to in a hook. (as far as I'm aware anyway)
// This is just equivalent to `self.type = MenuModList.ModButton.ItfType.Blank`.
FieldInfo typeField = typeof(MenuModList.ModButton).GetField("type", BindingFlags.Public | BindingFlags.Instance);
typeField.SetValue(self, MenuModList.ModButton.ItfType.Blank);
}
Expand Down
144 changes: 102 additions & 42 deletions src/JollyRebindMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,13 @@

namespace JollyRebind
{
[BepInPlugin("sabreml.jollyrebind", "JollyRebind", "1.1.2")]
[BepInPlugin("sabreml.jollyrebind", "JollyRebind", "1.2.0")]
public class JollyRebindMod : BaseUnityPlugin
{
// A `HashSet` of previously logged controller element exceptions.
// These are tracked because `JollyInputUpdate` happens once every frame, and this could very quickly spam the log file otherwise.
private static readonly HashSet<string> exceptionLogLog = new HashSet<string>();

// A dictionary of rewired `elementIdentifierName`s to their corresponding Unity `KeyCode`s.
private static readonly Dictionary<string, KeyCode> rewiredElemToKeyCode = new Dictionary<string, KeyCode>
{
{ "A", KeyCode.JoystickButton0 },
{ "B", KeyCode.JoystickButton1 },
{ "X", KeyCode.JoystickButton2 },
{ "Y", KeyCode.JoystickButton3 },
{ "Left Shoulder", KeyCode.JoystickButton4 },
{ "Right Shoulder", KeyCode.JoystickButton5 },
{ "Back", KeyCode.JoystickButton6 },
{ "Start", KeyCode.JoystickButton7 },
{ "Left Stick Button", KeyCode.JoystickButton8 },
{ "Right Stick Button", KeyCode.JoystickButton9 }
};

public void OnEnable()
{
On.RainWorld.OnModsInit += Init;
Expand All @@ -48,57 +33,132 @@ private void Init(On.RainWorld.orig_OnModsInit orig, RainWorld self)
MachineConnector.SetRegisteredOI(Info.Metadata.GUID, new JollyRebindConfig());
}

// If the player has a custom keybind set (i.e, not the map key), this method sets `jollyButtonDown` to `true` depending on if the key is being held down.
// If the player has a custom keybind set (AKA: Not the map key), this method sets `jollyButtonDown` to true if the key is being held down.
private void JollyInputUpdateHK(On.Player.orig_JollyInputUpdate orig, Player self)
{
orig(self);

// The player's pointing keybind from the jolly menu keybinder.
// The player's custom pointing keybind from the jolly menu keybinder.
KeyCode playerKeybind = JollyRebindConfig.PlayerPointInputs[self.playerState.playerNumber].Value;
// The game's map keybind.
KeyCode? mapKeybind = null;

// The player's `Options.ControlSetup`, containing their Rewired data.
Options.ControlSetup playerControls = RWCustom.Custom.rainWorld.options.controls[self.playerState.playerNumber];
// If the player's pointing keybind is different to their map key.
if (playerKeybind != GetMapKey(self.playerState.playerNumber))
{
// Override whatever `jollyButtonDown` was set to in `orig()`.
self.jollyButtonDown = Input.GetKey(playerKeybind);
}
}

// Returns `playerNumber`'s map key from the game's settings as a `KeyCode`.
private static KeyCode GetMapKey(int playerNumber)
{
Options.ControlSetup playerControls = RWCustom.Custom.rainWorld.options.controls[playerNumber];

// If the player is using a controller.
if (self.input[0].gamePad)
if (playerControls.gamePad)
{
// Get the button on the controller which is bound to the 'Map' action.
// The button on the controller which is bound to the 'Map' action.
ActionElementMap mapKeyElementMap = playerControls.gameControlMap.ButtonMaps
.First(elementMap => elementMap.actionId == RewiredConsts.Action.Map);

// If that button's name is in the `rewiredElemToKeyCode` dictionary,
if (rewiredElemToKeyCode.TryGetValue(mapKeyElementMap.elementIdentifierName, out KeyCode keyCode))
// If that button's name is in the `rewiredNameToKeyCode` dictionary for the controller.
try
{
// Set `mapKeybind` to the Rewired element's corresponding Unity `KeyCode`.
mapKeybind = keyCode;
return rewiredNameToKeyCode[playerControls.GetActivePreset()][mapKeyElementMap.elementIdentifierName];
}
// Otherwise, make an exception log with the name and ID of the button the player is trying to use.
else
catch (KeyNotFoundException)
{
string exceptionString = $"(JollyRebind) Unknown controller element '{mapKeyElementMap.elementIdentifierName} ({mapKeyElementMap.elementIdentifierId})'! Defaulting to the map key.";
// If it's /not/ in the dictionary, make an exception log with the name and ID of the button the player is trying to use.
string exceptionString = string.Format(
"(JollyRebind) Unknown map button element '{0} ({1})'! [GUID: {2}]",
mapKeyElementMap.elementIdentifierName,
mapKeyElementMap.elementIdentifierId,
mapKeyElementMap.controllerMap.hardwareGuid
);
LogExceptionOnce(exceptionString);

// If this exceptionString hasn't been logged already.
if (exceptionLogLog.Add(exceptionString))
{
Debug.LogException(new System.Exception(exceptionString));
}
return;
// Return `KeyCode.None` so that it defaults to using their map key.
// (Their keybind isn't going to be 'None', so `jollyButtonDown` won't be overridden by it)
return KeyCode.None;
}
}
// If the player is using a keyboard.
else
{
mapKeybind = playerControls.KeyboardMap;
return playerControls.KeyboardMap;
}
}

// If the player's keybind is different to the game's map key.
if (playerKeybind != mapKeybind)
// Sends an exception to `exceptionLog.txt` as long has it hasn't already been logged before in this session.
private static void LogExceptionOnce(string text)
{
// If this exceptionString hasn't been logged already.
if (exceptionLogLog.Add(text))
{
// Override whatever `jollyButtonDown` was set to in `orig()`.
self.jollyButtonDown = Input.GetKey(playerKeybind);
Debug.LogException(new System.Exception(text));
}
}


// A 2D dictionary containing Rewired `elementIdentifierName`s to their corresponding Unity `KeyCode`s,
// categorised by the controller preset they belong to.
// (Button names taken from the Rewired 'RewiredControllerElementIdentifiers.csv' file from the Rewired documentation)
private static readonly Dictionary<Options.ControlSetup.Preset, Dictionary<string, KeyCode>> rewiredNameToKeyCode =
new Dictionary<Options.ControlSetup.Preset, Dictionary<string, KeyCode>>
{
[Options.ControlSetup.Preset.PS4DualShock] = new Dictionary<string, KeyCode>
{
{ "Cross", KeyCode.JoystickButton0 },
{ "Circle", KeyCode.JoystickButton1 },
{ "Square", KeyCode.JoystickButton2 },
{ "Triangle", KeyCode.JoystickButton3 },
{ "L1", KeyCode.JoystickButton4 },
{ "R1", KeyCode.JoystickButton5 },
{ "Share", KeyCode.JoystickButton6 },
{ "Options", KeyCode.JoystickButton7 },
{ "Left Stick Button", KeyCode.JoystickButton8 },
{ "Right Stick Button", KeyCode.JoystickButton9 }
},
[Options.ControlSetup.Preset.PS5DualSense] = new Dictionary<string, KeyCode>
{
{ "Cross", KeyCode.JoystickButton0 },
{ "Circle", KeyCode.JoystickButton1 },
{ "Square", KeyCode.JoystickButton2 },
{ "Triangle", KeyCode.JoystickButton3 },
{ "L1", KeyCode.JoystickButton4 },
{ "R1", KeyCode.JoystickButton5 },
{ "Create", KeyCode.JoystickButton6 },
{ "Options", KeyCode.JoystickButton7 },
{ "Left Stick Button", KeyCode.JoystickButton8 },
{ "Right Stick Button", KeyCode.JoystickButton9 }
},
[Options.ControlSetup.Preset.XBox] = new Dictionary<string, KeyCode>
{
{ "A", KeyCode.JoystickButton0 },
{ "B", KeyCode.JoystickButton1 },
{ "X", KeyCode.JoystickButton2 },
{ "Y", KeyCode.JoystickButton3 },
{ "Left Shoulder", KeyCode.JoystickButton4 },
{ "Right Shoulder", KeyCode.JoystickButton5 },
{ "Back", KeyCode.JoystickButton6 },
{ "Start", KeyCode.JoystickButton7 },
{ "Left Stick Button", KeyCode.JoystickButton8 },
{ "Right Stick Button", KeyCode.JoystickButton9 }
},
[Options.ControlSetup.Preset.SwitchProController] = new Dictionary<string, KeyCode>
{
{ "B", KeyCode.JoystickButton0 },
{ "A", KeyCode.JoystickButton1 },
{ "Y", KeyCode.JoystickButton2 },
{ "X", KeyCode.JoystickButton3 },
{ "L", KeyCode.JoystickButton4 },
{ "R", KeyCode.JoystickButton5 },
{ "- Button", KeyCode.JoystickButton6 },
{ "+ Button", KeyCode.JoystickButton7 },
{ "Left Stick", KeyCode.JoystickButton8 },
{ "Right Stick", KeyCode.JoystickButton9 }
}
};
// (Having all of these hardcoded in the mod is terrible, but this is genuinely the best way I've been able to find after over a week of testing.)
}
}

0 comments on commit cc1e799

Please sign in to comment.