diff --git a/OWML.Common/IModInputHandler.cs b/OWML.Common/IModInputHandler.cs
index e4a67087..73386ce5 100644
--- a/OWML.Common/IModInputHandler.cs
+++ b/OWML.Common/IModInputHandler.cs
@@ -2,6 +2,8 @@
{
public interface IModInputHandler
{
+ IModInputTextures Textures { get; }
+
IModInputCombination RegisterCombination(IModBehaviour mod, string name, string combination);
void UnregisterCombination(IModInputCombination combo);
bool IsPressedExact(IModInputCombination combo);
diff --git a/OWML.Common/IModInputTextures.cs b/OWML.Common/IModInputTextures.cs
new file mode 100644
index 00000000..715146fc
--- /dev/null
+++ b/OWML.Common/IModInputTextures.cs
@@ -0,0 +1,10 @@
+using UnityEngine;
+
+namespace OWML.Common
+{
+ public interface IModInputTextures
+ {
+ Texture2D KeyTexture(string key);
+ Texture2D KeyTexture(KeyCode key);
+ }
+}
diff --git a/OWML.Common/OWML.Common.csproj b/OWML.Common/OWML.Common.csproj
index fda96cf7..5c1c9ad7 100644
--- a/OWML.Common/OWML.Common.csproj
+++ b/OWML.Common/OWML.Common.csproj
@@ -39,6 +39,7 @@
+
diff --git a/OWML.Launcher/App.cs b/OWML.Launcher/App.cs
index ae0b4861..e04ed595 100644
--- a/OWML.Launcher/App.cs
+++ b/OWML.Launcher/App.cs
@@ -21,6 +21,8 @@ public class App
private readonly OWPatcher _owPatcher;
private readonly VRPatcher _vrPatcher;
+ private const string VrArgument = " -vrmode openvr";
+
public App(IOwmlConfig owmlConfig, IModManifest owmlManifest, IModConsole writer, IModFinder modFinder,
OutputListener listener, PathFinder pathFinder, OWPatcher owPatcher, VRPatcher vrPatcher)
{
@@ -55,9 +57,10 @@ public void Run(string[] args)
ShowModList(mods);
- PatchGame(mods);
+ var hasVrMod = HasVrMod(mods);
+ PatchGame(hasVrMod);
- StartGame(args);
+ StartGame(args, hasVrMod);
if (hasPortArgument)
{
@@ -142,13 +145,18 @@ private void OnOutput(string s)
}
}
- private void PatchGame(IList mods)
+ private bool HasVrMod(IList mods)
{
- _owPatcher.PatchGame();
-
var vrMod = mods.FirstOrDefault(x => x.Config.RequireVR && x.Config.Enabled);
- var enableVR = vrMod != null;
- _writer.WriteLine(enableVR ? $"{vrMod.Manifest.UniqueName} requires VR." : "No mods require VR.");
+ var hasVrMod = vrMod != null;
+ _writer.WriteLine(hasVrMod ? $"{vrMod.Manifest.UniqueName} requires VR." : "No mods require VR.");
+ return hasVrMod;
+ }
+
+ private void PatchGame(bool enableVR)
+ {
+ _owPatcher.PatchGame();
+ _vrPatcher.PatchVR(enableVR);
try
{
_vrPatcher.PatchVR(enableVR);
@@ -159,12 +167,17 @@ private void PatchGame(IList mods)
}
}
- private void StartGame(string[] args)
+ private void StartGame(string[] args, bool enableVR)
{
_writer.WriteLine("Starting game...");
try
{
- Process.Start($"{_owmlConfig.GamePath}/OuterWilds.exe", string.Join(" ", args));
+ var gameArgs = string.Join(" ", args);
+ if (enableVR)
+ {
+ gameArgs += VrArgument;
+ }
+ Process.Start($"{_owmlConfig.GamePath}/OuterWilds.exe", gameArgs);
}
catch (Exception ex)
{
diff --git a/OWML.Launcher/OWML.Config.json b/OWML.Launcher/OWML.Config.json
index 49b33d21..67380528 100644
--- a/OWML.Launcher/OWML.Config.json
+++ b/OWML.Launcher/OWML.Config.json
@@ -1,5 +1,5 @@
{
"gamePath": "C:/Program Files/Epic Games/OuterWilds",
"verbose": false,
- "combinationsBlockInput" : false
+ "combinationsBlockInput": false
}
diff --git a/OWML.Launcher/OWML.Manifest.json b/OWML.Launcher/OWML.Manifest.json
index 93c8391d..db34c053 100644
--- a/OWML.Launcher/OWML.Manifest.json
+++ b/OWML.Launcher/OWML.Manifest.json
@@ -2,6 +2,6 @@
"author": "Alek",
"name": "OWML",
"uniqueName": "Alek.OWML",
- "version": "0.3.57",
+ "version": "0.4.0",
"description": "The mod loader and mod framework for Outer Wilds"
}
diff --git a/OWML.ModHelper.Assets/ModAssets.cs b/OWML.ModHelper.Assets/ModAssets.cs
index 80ca911e..e2324746 100644
--- a/OWML.ModHelper.Assets/ModAssets.cs
+++ b/OWML.ModHelper.Assets/ModAssets.cs
@@ -93,7 +93,7 @@ private IEnumerator LoadMesh(ObjectAsset modAsset, string objectPath)
var meshFilter = modAsset.AddComponent();
meshFilter.mesh = mesh;
yield return new WaitForEndOfFrame();
- modAsset.SetAsset(modAsset.gameObject);
+ modAsset.SetMeshFilter(meshFilter);
}
private IEnumerator LoadTexture(ObjectAsset modAsset, string imagePath)
@@ -111,6 +111,7 @@ private IEnumerator LoadTexture(ObjectAsset modAsset, string imagePath)
}
var meshRenderer = modAsset.AddComponent();
meshRenderer.material.mainTexture = texture;
+ modAsset.SetMeshRenderer(meshRenderer);
}
private IEnumerator LoadMesh(MeshAsset modAsset, string objectPath)
diff --git a/OWML.ModHelper.Assets/ObjectAsset.cs b/OWML.ModHelper.Assets/ObjectAsset.cs
index 659010f3..349e8d90 100644
--- a/OWML.ModHelper.Assets/ObjectAsset.cs
+++ b/OWML.ModHelper.Assets/ObjectAsset.cs
@@ -4,5 +4,28 @@ namespace OWML.ModHelper.Assets
{
public class ObjectAsset : ModAsset
{
+ private MeshRenderer _meshRenderer;
+ private MeshFilter _meshFilter;
+
+ public void SetMeshRenderer(MeshRenderer meshRenderer)
+ {
+ _meshRenderer = meshRenderer;
+ SetAssetIfComplete();
+ }
+
+ public void SetMeshFilter(MeshFilter meshFilter)
+ {
+ _meshFilter = meshFilter;
+ SetAssetIfComplete();
+ }
+
+ private void SetAssetIfComplete()
+ {
+ if (_meshFilter != null && _meshRenderer != null)
+ {
+ SetAsset(gameObject);
+ }
+ }
+
}
}
diff --git a/OWML.ModHelper.Events/HarmonyHelper.cs b/OWML.ModHelper.Events/HarmonyHelper.cs
index 5f8dc4aa..0f4c1ffe 100644
--- a/OWML.ModHelper.Events/HarmonyHelper.cs
+++ b/OWML.ModHelper.Events/HarmonyHelper.cs
@@ -42,16 +42,21 @@ private HarmonyInstance CreateInstance()
private MethodInfo GetMethod(string methodName)
{
var targetType = typeof(T);
+ MethodInfo result = null;
try
{
_logger.Log($"Getting method {methodName} of {targetType.Name}");
- return targetType.GetAnyMethod(methodName);
+ result = targetType.GetAnyMethod(methodName);
}
catch (Exception ex)
{
_console.WriteLine($"Exception while getting method {methodName} of {targetType.Name}: {ex}");
- return null;
}
+ if (result == null)
+ {
+ _console.WriteLine($"Error: Original method {methodName} of class {targetType} not found");
+ }
+ return result;
}
public void AddPrefix(string methodName, Type patchType, string patchMethodName)
diff --git a/OWML.ModHelper.Events/ModEvents.cs b/OWML.ModHelper.Events/ModEvents.cs
index fa84a336..8256a67e 100644
--- a/OWML.ModHelper.Events/ModEvents.cs
+++ b/OWML.ModHelper.Events/ModEvents.cs
@@ -56,7 +56,7 @@ private void SubscribeToEvent(Common.Events ev)
var type = typeof(T);
if (IsSubscribedTo(type, ev))
{
- _console.WriteLine($"Warning: already subscribed to {ev} of {type.Name}");
+ _logger.Log($"Already subscribed to {ev} of {type.Name}");
return;
}
AddToEventList(_subscribedEvents, type, ev);
diff --git a/OWML.ModHelper.Input/BindingChangeListener.cs b/OWML.ModHelper.Input/BindingChangeListener.cs
index e467cf19..1af6c104 100644
--- a/OWML.ModHelper.Input/BindingChangeListener.cs
+++ b/OWML.ModHelper.Input/BindingChangeListener.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.ObjectModel;
-using System.Collections.Generic;
-using OWML.Common;
+using OWML.Common;
using UnityEngine;
namespace OWML.ModHelper.Input
@@ -14,13 +11,13 @@ public class BindingChangeListener : MonoBehaviour
internal void Initialize(ModInputHandler inputHandler, IModEvents events)
{
_inputHandler = inputHandler;
- events.Subscribe(Common.Events.AfterStart);
+ events.Subscribe(Events.AfterStart);
events.OnEvent += OnEvent;
}
- private void OnEvent(MonoBehaviour behaviour, Common.Events ev)
+ private void OnEvent(MonoBehaviour behaviour, Events ev)
{
- if (behaviour.GetType() == typeof(TitleScreenManager) && ev == Common.Events.AfterStart)
+ if (behaviour.GetType() == typeof(TitleScreenManager) && ev == Events.AfterStart)
{
_updateInputsNext = true;
}
@@ -29,7 +26,7 @@ private void OnEvent(MonoBehaviour behaviour, Common.Events ev)
private void Start()
{
DontDestroyOnLoad(gameObject);
- GlobalMessenger.AddListener("KeyBindingsChanged", new Callback(PrepareForUpdate));
+ GlobalMessenger.AddListener("KeyBindingsChanged", PrepareForUpdate);
}
private void PrepareForUpdate()
diff --git a/OWML.ModHelper.Input/ModInputCombination.cs b/OWML.ModHelper.Input/ModInputCombination.cs
index 6ca5bc96..6bf3213d 100644
--- a/OWML.ModHelper.Input/ModInputCombination.cs
+++ b/OWML.ModHelper.Input/ModInputCombination.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.ObjectModel;
+using System.Collections.ObjectModel;
using System.Collections.Generic;
using OWML.Common;
using UnityEngine;
@@ -8,11 +7,6 @@ namespace OWML.ModHelper.Input
{
public class ModInputCombination : IModInputCombination
{
- private const int GamePadKeyDiff = 20;
- private const int MaxUsefulKey = 350;
- private const int MaxComboLength = 7;
- private const string XboxPrefix = "xbox_";
-
public float LastPressedMoment { get; private set; }
public bool IsFirst { get; private set; }
public float PressDuration => LastPressedMoment - _firstPressedMoment;
@@ -24,8 +18,8 @@ public class ModInputCombination : IModInputCombination
private bool _isPressed;
private float _firstPressedMoment;
- private List _singles = new List();
- private List _hashes = new List();
+ private readonly List _singles = new List();
+ private readonly List _hashes;
internal ModInputCombination(IModManifest mod, string name, string combination)
{
@@ -34,104 +28,12 @@ internal ModInputCombination(IModManifest mod, string name, string combination)
_hashes = StringToHashes(combination);
}
- private KeyCode StringToKeyCodeKeyboard(string keyboardKey)
- {
- if (keyboardKey == "control" || keyboardKey == "ctrl")
- {
- return KeyCode.LeftControl;
- }
- if (keyboardKey == "shift")
- {
- return KeyCode.LeftShift;
- }
- if (keyboardKey == "alt")
- {
- return KeyCode.LeftAlt;
- }
- var code = (KeyCode)Enum.Parse(typeof(KeyCode), keyboardKey, true);
- return Enum.IsDefined(typeof(KeyCode), code) ? code : KeyCode.None;
- }
-
- private KeyCode StringToKeyCodeGamepad(string gamepadKey)
- {
- var gamepadcodeCode = (JoystickButton)Enum.Parse(typeof(JoystickButton), gamepadKey, true);
- return (Enum.IsDefined(typeof(JoystickButton), gamepadcodeCode)) ?
- InputTranslator.GetButtonKeyCode(gamepadcodeCode) : KeyCode.None;
- }
-
- private KeyCode StringToKeyCodeXbox(string xboxKey)
- {
- switch (xboxKey[0])
- {
- case 'A':
- return InputTranslator.GetButtonKeyCode(JoystickButton.FaceDown);
- case 'B':
- return InputTranslator.GetButtonKeyCode(JoystickButton.FaceRight);
- case 'X':
- return InputTranslator.GetButtonKeyCode(JoystickButton.FaceLeft);
- case 'Y':
- return InputTranslator.GetButtonKeyCode(JoystickButton.FaceUp);
- default:
- return StringToKeyCodeGamepad(xboxKey);
- }
- }
-
- private KeyCode StringToKeyCode(string key)
- {
- var trimmedKey = key.Trim();
- return trimmedKey.Contains(XboxPrefix) ? StringToKeyCodeXbox(trimmedKey.Substring(XboxPrefix.Length))
- : StringToKeyCodeKeyboard(trimmedKey);
- }
-
- private int[] StringToKeyArray(string stringCombination)
- {
- var keyCombination = new int[MaxComboLength];
- var i = 0;
- foreach (var key in stringCombination.Trim().ToLower().Split('+'))
- {
- var code = StringToKeyCode(key);
- if ((int)code >= MaxUsefulKey)
- {
- code -= (((int)code - MaxUsefulKey + GamePadKeyDiff) / GamePadKeyDiff) * GamePadKeyDiff;
- }
- if (code == KeyCode.None)
- {
- keyCombination[0] = (int)RegistrationCode.InvalidCombination;
- return keyCombination;
- }
- if (i >= MaxComboLength)
- {
- keyCombination[0] = (int)RegistrationCode.CombinationTooLong;
- return keyCombination;
- }
- keyCombination[i] = (int)code;
- i++;
- }
- Array.Sort(keyCombination);
- return keyCombination;
- }
-
- private long StringToHash(string stringCombination)
- {
- var keyCombination = StringToKeyArray(stringCombination);
- if (keyCombination[0] < 0)
- {
- return keyCombination[0];
- }
- long hash = 0;
- for (var i = 0; i < MaxComboLength; i++)
- {
- hash = hash * MaxUsefulKey + keyCombination[i];
- }
- return hash;
- }
-
private List StringToHashes(string combinations)
{
var hashes = new List();
foreach (var combo in combinations.Split('/'))
{
- var hash = StringToHash(combo);
+ var hash = ModInputLibrary.StringToHash(combo);
if (hash <= 0)
{
hashes.Clear();
@@ -139,10 +41,10 @@ private List StringToHashes(string combinations)
return hashes;
}
hashes.Add(hash);
- if (hash < MaxUsefulKey)
+ if (hash < ModInputLibrary.MaxUsefulKey)
{
_singles.Add((KeyCode)hash);
- }
+ }
}
return hashes;
}
@@ -161,4 +63,4 @@ public void InternalSetPressed(bool isPressed = true)
_isPressed = isPressed;
}
}
-}
+}
\ No newline at end of file
diff --git a/OWML.ModHelper.Input/ModInputHandler.cs b/OWML.ModHelper.Input/ModInputHandler.cs
index b3a1f1eb..4148c496 100644
--- a/OWML.ModHelper.Input/ModInputHandler.cs
+++ b/OWML.ModHelper.Input/ModInputHandler.cs
@@ -5,7 +5,6 @@
using System.Linq;
using OWML.Common;
using UnityEngine;
-using System.Security.Policy;
namespace OWML.ModHelper.Input
{
@@ -13,28 +12,30 @@ public class ModInputHandler : IModInputHandler
{
private const float Cooldown = 0.05f;
private const float TapDuration = 0.1f;
- private const int MinUsefulKey = 8;
- private const int MaxUsefulKey = 350;
- private const int MaxComboLength = 7;
- private const int GamePadKeyDiff = 20;
private const BindingFlags NonPublic = BindingFlags.NonPublic | BindingFlags.Instance;
internal static ModInputHandler Instance { get; private set; }
- private HashSet _singlesPressed = new HashSet();
- private Dictionary _comboRegistry = new Dictionary();
- private HashSet _gameBindingRegistry = new HashSet();
- private HashSet _toResetOnNextFrame = new HashSet();
- private float[] _timeout = new float[MaxUsefulKey];
- private int[] _gameBindingCounter = new int[MaxUsefulKey];
+ private readonly HashSet _singlesPressed = new HashSet();
+ private readonly Dictionary _comboRegistry = new Dictionary();
+ private readonly HashSet _gameBindingRegistry = new HashSet();
+ private readonly HashSet _toResetOnNextFrame = new HashSet();
+ private readonly float[] _timeout = new float[ModInputLibrary.MaxUsefulKey];
+ private readonly int[] _gameBindingCounter = new int[ModInputLibrary.MaxUsefulKey];
private IModInputCombination _currentCombination;
private int _lastSingleUpdate;
private int _lastCombinationUpdate;
private readonly IModLogger _logger;
private readonly IModConsole _console;
+ public IModInputTextures Textures { get; }
+
public ModInputHandler(IModLogger logger, IModConsole console, IHarmonyHelper patcher, IOwmlConfig owmlConfig, IModEvents events)
{
+ var textures = new ModInputTextures();
+ textures.FillTextureLibrary();
+ Textures = textures;
+
_console = console;
_logger = logger;
@@ -50,15 +51,13 @@ public ModInputHandler(IModLogger logger, IModConsole console, IHarmonyHelper pa
Instance = this;
}
- internal bool IsPressedAndIgnored(KeyCode code)
+ internal bool IsPressedAndIgnored(KeyCode key)
{
UpdateCurrentCombination();
- var intKey = (int)code;
- if ((int)code >= MaxUsefulKey)
- {
- intKey -= ((intKey - MaxUsefulKey + GamePadKeyDiff) / GamePadKeyDiff) * GamePadKeyDiff;
- }
- return UnityEngine.Input.GetKey(code) && _currentCombination != null && Time.realtimeSinceStartup - _timeout[intKey] < Cooldown;
+ var cleanKey = ModInputLibrary.NormalizeKeyCode(key);
+ return UnityEngine.Input.GetKey(cleanKey) &&
+ _currentCombination != null &&
+ Time.realtimeSinceStartup - _timeout[(int)cleanKey] < Cooldown;
}
private long? HashFromKeyboard()
@@ -66,18 +65,18 @@ internal bool IsPressedAndIgnored(KeyCode code)
long hash = 0;
var keysCount = 0;
var countdownTrigger = true;
- for (var code = MinUsefulKey; code < MaxUsefulKey; code++)
+ for (var code = ModInputLibrary.MinUsefulKey; code < ModInputLibrary.MaxUsefulKey; code++)
{
if (!(Enum.IsDefined(typeof(KeyCode), (KeyCode)code) && UnityEngine.Input.GetKey((KeyCode)code)))
{
continue;
}
keysCount++;
- if (keysCount > MaxComboLength)
+ if (keysCount > ModInputLibrary.MaxComboLength)
{
return null;
}
- hash = hash * MaxUsefulKey + code;
+ hash = hash * ModInputLibrary.MaxUsefulKey + code;
if (Time.realtimeSinceStartup - _timeout[code] > Cooldown)
{
countdownTrigger = false;
@@ -94,7 +93,7 @@ private IModInputCombination CombinationFromKeyboard()
{
return null;
}
- long hash = (long)nullableHash;
+ var hash = (long)nullableHash;
if (hash < 0)
{
countdownTrigger = true;
@@ -106,19 +105,19 @@ private IModInputCombination CombinationFromKeyboard()
}
var combination = _comboRegistry[hash];
- if (!(combination == _currentCombination) && countdownTrigger)
+ if (combination != _currentCombination && countdownTrigger)
{
return null;
}
- if (hash < MaxUsefulKey)
+ if (hash < ModInputLibrary.MaxUsefulKey)
{
return combination;
}
while (hash > 0)
{
- _timeout[hash % MaxUsefulKey] = Time.realtimeSinceStartup;
- hash /= MaxUsefulKey;
+ _timeout[hash % ModInputLibrary.MaxUsefulKey] = Time.realtimeSinceStartup;
+ hash /= ModInputLibrary.MaxUsefulKey;
}
return combination;
}
@@ -162,31 +161,24 @@ public bool IsPressedExact(IModInputCombination combination)
public bool IsNewlyPressedExact(IModInputCombination combination)
{
- if (combination == null)
- {
- return false;
- }
- return IsPressedExact(combination) && combination.IsFirst;
+ return combination != null &&
+ IsPressedExact(combination) &&
+ combination.IsFirst;
}
public bool WasTappedExact(IModInputCombination combination)
{
- if (combination == null)
- {
- return false;
- }
- return !IsPressedExact(combination)
- && (combination.PressDuration < TapDuration)
- && combination.IsFirst;
+ return combination != null &&
+ !IsPressedExact(combination) &&
+ combination.PressDuration < TapDuration &&
+ combination.IsFirst;
}
public bool WasNewlyReleasedExact(IModInputCombination combination)
{
- if (combination == null)
- {
- return false;
- }
- return !IsPressedExact(combination) && combination.IsFirst;
+ return combination != null &&
+ !IsPressedExact(combination) &&
+ combination.IsFirst;
}
private void UpdateSinglesPressed()
@@ -222,93 +214,78 @@ private bool IsPressedSingle(IModInputCombination combination)
{
return true;
}
- foreach (var key in combination.Singles)
+ var single = combination.Singles.FirstOrDefault(key => UnityEngine.Input.GetKey(key) && !IsPressedAndIgnored(key));
+ if (single == 0)
{
- if (UnityEngine.Input.GetKey(key) && !IsPressedAndIgnored(key))
- {
- _singlesPressed.Add(combination);
- combination.InternalSetPressed();
- return true;
- }
+ return false;
}
- return false;
+ _singlesPressed.Add(combination);
+ combination.InternalSetPressed();
+ return true;
}
public bool IsPressed(IModInputCombination combination)
{
- if (combination == null)
- {
- return false;
- }
- return IsPressedExact(combination) || IsPressedSingle(combination);
+ return combination != null &&
+ (IsPressedExact(combination) || IsPressedSingle(combination));
}
public bool IsNewlyPressed(IModInputCombination combination)
{
- if (combination == null)
- {
- return false;
- }
- return IsPressed(combination) && combination.IsFirst;
+ return combination != null &&
+ IsPressed(combination) &&
+ combination.IsFirst;
}
public bool WasTapped(IModInputCombination combination)
{
- if (combination == null)
- {
- return false;
- }
- return (!IsPressed(combination)) && (combination.PressDuration < TapDuration)
- && combination.IsFirst;
+ return combination != null &&
+ !IsPressed(combination) &&
+ combination.PressDuration < TapDuration &&
+ combination.IsFirst;
}
public bool WasNewlyReleased(IModInputCombination combination)
{
- if (combination == null)
- {
- return false;
- }
- return (!IsPressed(combination)) && combination.IsFirst;
+ return combination != null &&
+ !IsPressed(combination) &&
+ combination.IsFirst;
}
private RegistrationCode SwapCombination(IModInputCombination combination, bool toUnregister)
{
- bool taken = false;
+ var taken = false;
if (combination.Hashes[0] <= 0)
{
return (RegistrationCode)combination.Hashes[0];
}
- foreach (long hash in combination.Hashes)
+ foreach (var hash in combination.Hashes)
{
if (toUnregister)
{
_comboRegistry.Remove(hash);
continue;
}
- if (_comboRegistry.ContainsKey(hash) || (hash < MaxUsefulKey && _gameBindingCounter[hash] > 0))
+ if (_comboRegistry.ContainsKey(hash) || hash < ModInputLibrary.MaxUsefulKey && _gameBindingCounter[hash] > 0)
{
taken = true;
continue;
}
_comboRegistry.Add(hash, combination);
}
- if (taken)
- {
- return RegistrationCode.CombinationTaken;
- }
- return RegistrationCode.AllNormal;
+ return taken ? RegistrationCode.CombinationTaken : RegistrationCode.AllNormal;
}
private List GetCollisions(ReadOnlyCollection hashes)
{
- List combos = new List();
- foreach (long hash in hashes)
+ var combos = new List();
+ foreach (var hash in hashes)
{
if (_comboRegistry.ContainsKey(hash))
{
combos.Add(_comboRegistry[hash].FullName);
}
- if (hash < MaxUsefulKey && _gameBindingCounter[hash] > 0)
+ if (hash < ModInputLibrary.MaxUsefulKey && _gameBindingCounter[hash] > 0)
{
combos.Add("Outer Wilds." + Enum.GetName(typeof(KeyCode), (KeyCode)hash));
}
@@ -316,6 +293,21 @@ private List GetCollisions(ReadOnlyCollection hashes)
return combos;
}
+ public List GetCollisions(string combinations)
+ {
+ var hashes = new List();
+ foreach (var combo in combinations.Split('/'))
+ {
+ var hash = ModInputLibrary.StringToHash(combo);
+ if (hash <= 0)
+ {
+ return new List { ((RegistrationCode)(-hash)).ToString() };
+ }
+ hashes.Add(hash);
+ }
+ return GetCollisions(hashes.AsReadOnly());
+ }
+
public IModInputCombination RegisterCombination(IModBehaviour mod, string name, string combination)
{
var combo = new ModInputCombination(mod.ModHelper.Manifest, name, combination);
@@ -330,7 +322,7 @@ public IModInputCombination RegisterCombination(IModBehaviour mod, string name,
case RegistrationCode.CombinationTaken:
_console.WriteLine($"Failed to register \"{combo.FullName}\": already in use by following mods:");
var collisions = GetCollisions(combo.Hashes);
- foreach (string collision in collisions)
+ foreach (var collision in collisions)
{
_console.WriteLine($"\"{collision}\"");
}
@@ -358,7 +350,7 @@ public void UnregisterCombination(IModInputCombination combination)
_console.WriteLine($"Failed to unregister \"{combination.FullName}\": too long!");
return;
case RegistrationCode.AllNormal:
- _logger.Log($"succesfully unregistered \"{combination.FullName}\"");
+ _logger.Log($"Successfully unregistered \"{combination.FullName}\"");
return;
default:
return;
@@ -367,29 +359,20 @@ public void UnregisterCombination(IModInputCombination combination)
internal void SwapGamesBinding(InputCommand binding, bool toUnregister)
{
- if ((_gameBindingRegistry.Contains(binding) ^ toUnregister) || binding == null)
+ if (_gameBindingRegistry.Contains(binding) ^ toUnregister || binding == null)
{
return;
}
var fields = binding is SingleAxisCommand ?
- typeof(SingleAxisCommand).GetFields(NonPublic) : typeof(DoubleAxisCommand).GetFields(NonPublic);
- foreach (var field in fields)
+ typeof(SingleAxisCommand).GetFields(NonPublic) :
+ typeof(DoubleAxisCommand).GetFields(NonPublic);
+ foreach (var field in fields.Where(x => x.FieldType == typeof(List)))
{
- if (field.FieldType == typeof(List))
+ var keys = (List)field.GetValue(binding);
+ foreach (var key in keys.Where(x => x != KeyCode.None))
{
- var keys = (List)(field.GetValue(binding));
- foreach (var key in keys)
- {
- if (key != KeyCode.None)
- {
- var intKey = (int)key;
- if ((int)key >= MaxUsefulKey)
- {
- intKey -= ((intKey - MaxUsefulKey + GamePadKeyDiff) / GamePadKeyDiff) * GamePadKeyDiff;
- }
- _gameBindingCounter[intKey] += toUnregister ? -1 : 1;
- }
- }
+ var intKey = (int)ModInputLibrary.NormalizeKeyCode(key);
+ _gameBindingCounter[intKey] += toUnregister ? -1 : 1;
}
}
if (toUnregister)
@@ -414,10 +397,9 @@ internal void UnregisterGamesBinding(InputCommand binding)
internal void UpdateGamesBindings()
{
- Array.ForEach(_gameBindingCounter, x => x = 0);
_gameBindingRegistry.Clear();
var inputCommands = typeof(InputLibrary).GetFields(BindingFlags.Public | BindingFlags.Static);
- Array.ForEach(inputCommands, field => RegisterGamesBinding(field.GetValue(null) as InputCommand));
+ inputCommands.ToList().ForEach(field => RegisterGamesBinding(field.GetValue(null) as InputCommand));
}
}
-}
+}
\ No newline at end of file
diff --git a/OWML.ModHelper.Input/ModInputLibrary.cs b/OWML.ModHelper.Input/ModInputLibrary.cs
new file mode 100644
index 00000000..63cb2577
--- /dev/null
+++ b/OWML.ModHelper.Input/ModInputLibrary.cs
@@ -0,0 +1,139 @@
+using System;
+using UnityEngine;
+
+namespace OWML.ModHelper.Input
+{
+ public static class ModInputLibrary
+ {
+ public const float ScaleDown = 0.75f;
+ public const string XboxPrefix = "xbox_";
+ public const int MinUsefulKey = 8;
+ public const int MinGamepadKey = 330;
+ public const int MaxUsefulKey = 350;
+ public const int MaxComboLength = 7;
+ public const int GamePadKeyDiff = 20;
+
+ public static KeyCode NormalizeKeyCode(KeyCode key)
+ {
+ if ((int)key >= MaxUsefulKey)
+ {
+ key -= ((int)key - MaxUsefulKey + GamePadKeyDiff) / GamePadKeyDiff * GamePadKeyDiff;
+ }
+ return key;
+ }
+
+ public static JoystickButton XboxButtonToJoystickButton(string xboxKey)
+ {
+ switch (xboxKey[0])
+ {
+ case 'a':
+ return JoystickButton.FaceDown;
+ case 'b':
+ return JoystickButton.FaceRight;
+ case 'x':
+ return JoystickButton.FaceLeft;
+ case 'y':
+ return JoystickButton.FaceUp;
+ default:
+ var code = (JoystickButton)Enum.Parse(typeof(JoystickButton), xboxKey);
+ return Enum.IsDefined(typeof(JoystickButton), code) ? code : JoystickButton.None;
+ }
+ }
+
+ public static string JoystickButtonToXboxButton(JoystickButton key)
+ {
+ switch (key)
+ {
+ case JoystickButton.FaceDown:
+ return "a";
+ case JoystickButton.FaceRight:
+ return "b";
+ case JoystickButton.FaceLeft:
+ return "x";
+ case JoystickButton.FaceUp:
+ return "y";
+ default:
+ return key.ToString();
+ }
+ }
+
+ private static KeyCode StringToKeyCodeKeyboard(string keyboardKey)
+ {
+ switch (keyboardKey)
+ {
+ case "control":
+ case "ctrl":
+ return KeyCode.LeftControl;
+ case "shift":
+ return KeyCode.LeftShift;
+ case "alt":
+ return KeyCode.LeftAlt;
+ default:
+ var code = (KeyCode)Enum.Parse(typeof(KeyCode), keyboardKey, true);
+ return Enum.IsDefined(typeof(KeyCode), code) ? code : KeyCode.None;
+ }
+ }
+
+ private static KeyCode StringToKeyCodeGamepad(string xboxKey)
+ {
+ var gamepadCode = XboxButtonToJoystickButton(xboxKey);
+ return gamepadCode == JoystickButton.None ? KeyCode.None : NormalizeKeyCode(InputTranslator.GetButtonKeyCode(gamepadCode));
+ }
+
+ public static KeyCode StringToKeyCode(string key)
+ {
+ var trimmedKey = key.Trim();
+ return trimmedKey.Contains(XboxPrefix) ?
+ StringToKeyCodeGamepad(trimmedKey.Substring(XboxPrefix.Length)) :
+ StringToKeyCodeKeyboard(trimmedKey);
+ }
+
+ private static int[] StringToKeyArray(string stringCombination)
+ {
+ var keyCombination = new int[MaxComboLength];
+ var i = 0;
+ foreach (var key in stringCombination.Trim().ToLower().Split('+'))
+ {
+ var code = StringToKeyCode(key);
+ if (code == KeyCode.None)
+ {
+ keyCombination[0] = (int)RegistrationCode.InvalidCombination;
+ return keyCombination;
+ }
+ if (i >= MaxComboLength)
+ {
+ keyCombination[0] = (int)RegistrationCode.CombinationTooLong;
+ return keyCombination;
+ }
+ keyCombination[i] = (int)code;
+ i++;
+ }
+ Array.Sort(keyCombination);
+ return keyCombination;
+ }
+
+ internal static long StringToHash(string stringCombination)
+ {
+ var keyCombination = StringToKeyArray(stringCombination);
+ if (keyCombination[0] < 0)
+ {
+ return keyCombination[0];
+ }
+ long hash = 0;
+ for (var i = 0; i < MaxComboLength; i++)
+ {
+ hash = hash * MaxUsefulKey + keyCombination[i];
+ }
+ return hash;
+ }
+
+ public static string KeyCodeToString(KeyCode key)
+ {
+ var config = OWInput.GetActivePadConfig() ?? InputUtil.GamePadConfig_Xbox;
+ key = NormalizeKeyCode(key);
+ return (int)key >= MinGamepadKey ?
+ XboxPrefix + JoystickButtonToXboxButton(InputTranslator.ConvertKeyCodeToButton(key, config)) :
+ key.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/OWML.ModHelper.Input/ModInputTextures.cs b/OWML.ModHelper.Input/ModInputTextures.cs
new file mode 100644
index 00000000..a97dccb1
--- /dev/null
+++ b/OWML.ModHelper.Input/ModInputTextures.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using OWML.Common;
+
+namespace OWML.ModHelper.Input
+{
+ public class ModInputTextures : IModInputTextures
+ {
+ private Dictionary _loadedTextures;
+
+ internal void FillTextureLibrary()
+ {
+ _loadedTextures = new Dictionary();
+ var config = OWInput.GetActivePadConfig() ?? InputUtil.GamePadConfig_Xbox;
+ for (var code = ModInputLibrary.MinUsefulKey; code < ModInputLibrary.MaxUsefulKey; code++)
+ {
+ var key = (KeyCode)code;
+ if (!Enum.IsDefined(typeof(KeyCode), key))
+ {
+ continue;
+ }
+ var keyName = ModInputLibrary.KeyCodeToString(key);
+ if (_loadedTextures.ContainsKey(keyName))
+ {
+ continue;
+ }
+ var toStore = (int)key >= ModInputLibrary.MinGamepadKey ?
+ ButtonPromptLibrary.SharedInstance.GetButtonTexture(InputTranslator.ConvertKeyCodeToButton(key, config)) :
+ ButtonPromptLibrary.SharedInstance.GetButtonTexture(key);
+ _loadedTextures.Add(keyName, toStore);
+ }
+ }
+
+ public Texture2D KeyTexture(string key)
+ {
+ return KeyTexture(ModInputLibrary.StringToKeyCode(key));
+ }
+
+ public Texture2D KeyTexture(KeyCode key)
+ {
+ if (_loadedTextures == null)
+ {
+ FillTextureLibrary();
+ }
+ var keyName = ModInputLibrary.KeyCodeToString(key);
+ if (_loadedTextures.ContainsKey(keyName))
+ {
+ return _loadedTextures[keyName];
+ }
+ var config = OWInput.GetActivePadConfig() ?? InputUtil.GamePadConfig_Xbox;
+ var toStore = (int)key >= ModInputLibrary.MinGamepadKey ?
+ ButtonPromptLibrary.SharedInstance.GetButtonTexture(InputTranslator.ConvertKeyCodeToButton(key, config)) :
+ ButtonPromptLibrary.SharedInstance.GetButtonTexture(key);
+ _loadedTextures.Add(keyName, toStore);
+ return toStore;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OWML.ModHelper.Input/OWML.ModHelper.Input.csproj b/OWML.ModHelper.Input/OWML.ModHelper.Input.csproj
index 7d44b013..8e0df88b 100644
--- a/OWML.ModHelper.Input/OWML.ModHelper.Input.csproj
+++ b/OWML.ModHelper.Input/OWML.ModHelper.Input.csproj
@@ -76,6 +76,8 @@
+
+
diff --git a/OWML.ModHelper.Interaction/InterfaceProxyBuilder.cs b/OWML.ModHelper.Interaction/InterfaceProxyBuilder.cs
index 1a255a11..9340eb47 100644
--- a/OWML.ModHelper.Interaction/InterfaceProxyBuilder.cs
+++ b/OWML.ModHelper.Interaction/InterfaceProxyBuilder.cs
@@ -9,16 +9,19 @@ namespace OWML.ModHelper.Interaction
{
internal class InterfaceProxyBuilder
{
- private readonly Type TargetType;
-
- private readonly Type ProxyType;
+ private readonly Type _targetType;
+ private readonly Type _proxyType;
public InterfaceProxyBuilder(string name, ModuleBuilder moduleBuilder, Type interfaceType, Type targetType)
{
if (name == null)
+ {
throw new ArgumentNullException(nameof(name));
+ }
if (targetType == null)
+ {
throw new ArgumentNullException(nameof(targetType));
+ }
var proxyBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class);
proxyBuilder.AddInterfaceImplementation(interfaceType);
@@ -31,20 +34,23 @@ public InterfaceProxyBuilder(string name, ModuleBuilder moduleBuilder, Type inte
{
var targetMethod = targetType.GetMethod(proxyMethod.Name, proxyMethod.GetParameters().Select(a => a.ParameterType).ToArray());
if (targetMethod == null)
+ {
throw new InvalidOperationException($"The {interfaceType.FullName} interface defines method {proxyMethod.Name} which doesn't exist in the API.");
-
- this.ProxyMethod(proxyBuilder, targetMethod, targetField);
+ }
+ ProxyMethod(proxyBuilder, targetMethod, targetField);
}
- this.TargetType = targetType;
- this.ProxyType = proxyBuilder.CreateType();
+ _targetType = targetType;
+ _proxyType = proxyBuilder.CreateType();
}
public object CreateInstance(object targetInstance)
{
- var constructor = this.ProxyType.GetConstructor(new[] { this.TargetType });
+ var constructor = _proxyType.GetConstructor(new[] { _targetType });
if (constructor == null)
- throw new InvalidOperationException($"Couldn't find the constructor for generated proxy type '{this.ProxyType.Name}'."); // should never happen
+ {
+ throw new InvalidOperationException($"Couldn't find the constructor for generated proxy type '{_proxyType.Name}'."); // should never happen
+ }
return constructor.Invoke(new[] { targetInstance });
}
@@ -68,8 +74,10 @@ private void CreateProxyMethodBody(MethodBuilder methodBuilder, MethodInfo targe
il.Emit(OpCodes.Ldfld, instanceField);
// invoke target method on instance
- for (int i = 0; i < argTypes.Length; i++)
+ for (var i = 0; i < argTypes.Length; i++)
+ {
il.Emit(OpCodes.Ldarg, i + 1);
+ }
il.Emit(OpCodes.Call, target);
// return result
diff --git a/OWML.ModHelper.Interaction/InterfaceProxyFactory.cs b/OWML.ModHelper.Interaction/InterfaceProxyFactory.cs
index e83b5d2b..eb30e045 100644
--- a/OWML.ModHelper.Interaction/InterfaceProxyFactory.cs
+++ b/OWML.ModHelper.Interaction/InterfaceProxyFactory.cs
@@ -9,29 +9,34 @@ namespace OWML.ModHelper.Interaction
{
public class InterfaceProxyFactory
{
- private readonly ModuleBuilder ModuleBuilder;
-
- private readonly IDictionary Builders = new Dictionary();
+ private readonly ModuleBuilder _moduleBuilder;
+ private readonly IDictionary _builders = new Dictionary();
public InterfaceProxyFactory()
{
- var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName($"OWMLInteraction.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run);
- this.ModuleBuilder = assemblyBuilder.DefineDynamicModule("OWMLInteraction.Proxies");
+ var assemblyName = new AssemblyName($"OWMLInteraction.Proxies, Version={GetType().Assembly.GetName().Version}, Culture=neutral");
+ var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
+ _moduleBuilder = assemblyBuilder.DefineDynamicModule("OWMLInteraction.Proxies");
}
- public TInterface CreateProxy(object instance, string sourceModID, string targetModID) where TInterface : class
+ public TInterface CreateProxy(object instance, string sourceModName, string targetModName) where TInterface : class
{
if (instance == null)
+ {
throw new InvalidOperationException("Can't proxy access to a null API.");
+ }
if (!typeof(TInterface).IsInterface)
+ {
throw new InvalidOperationException("The proxy type must be an interface, not a class.");
+ }
var targetType = instance.GetType();
- var proxyTypeName = $"OWMLInteraction.Proxies.From<{sourceModID}_{typeof(TInterface).FullName}>_To<{targetModID}_{targetType.FullName}>";
- if (!this.Builders.TryGetValue(proxyTypeName, out InterfaceProxyBuilder builder))
+
+ var proxyTypeName = $"OWMLInteraction.Proxies.From<{sourceModName}_{typeof(TInterface).FullName}>_To<{targetModName}_{targetType.FullName}>";
+ if (!_builders.TryGetValue(proxyTypeName, out InterfaceProxyBuilder builder))
{
- builder = new InterfaceProxyBuilder(proxyTypeName, this.ModuleBuilder, typeof(TInterface), targetType);
- this.Builders[proxyTypeName] = builder;
+ builder = new InterfaceProxyBuilder(proxyTypeName, _moduleBuilder, typeof(TInterface), targetType);
+ _builders[proxyTypeName] = builder;
}
return (TInterface)builder.CreateInstance(instance);
diff --git a/OWML.ModHelper.Interaction/ModInteraction.cs b/OWML.ModHelper.Interaction/ModInteraction.cs
index aed103e1..fa4b6280 100644
--- a/OWML.ModHelper.Interaction/ModInteraction.cs
+++ b/OWML.ModHelper.Interaction/ModInteraction.cs
@@ -7,13 +7,9 @@ namespace OWML.ModHelper.Interaction
public class ModInteraction : IModInteraction
{
private readonly IList _modList;
-
private readonly InterfaceProxyFactory _proxyFactory;
-
private readonly IModManifest _manifest;
-
private Dictionary> _dependantDict = new Dictionary>();
-
private Dictionary> _dependencyDict = new Dictionary>();
public ModInteraction(IList list, InterfaceProxyFactory proxyFactory, IModManifest manifest)
diff --git a/OWML.ModHelper.Menus/ModComboInput.cs b/OWML.ModHelper.Menus/ModComboInput.cs
index 208c533a..779b907f 100644
--- a/OWML.ModHelper.Menus/ModComboInput.cs
+++ b/OWML.ModHelper.Menus/ModComboInput.cs
@@ -1,25 +1,25 @@
using OWML.Common.Menus;
using OWML.ModHelper.Events;
+using OWML.ModHelper.Input;
using UnityEngine;
using UnityEngine.UI;
-using System;
+using OWML.Common;
namespace OWML.ModHelper.Menus
{
public class ModComboInput : ModInput, IModComboInput
{
- private const float ScaleDown = 0.75f;
- private const string XboxPrefix = "xbox_";
-
public IModLayoutButton Button { get; }
protected readonly IModInputMenu InputMenu;
protected readonly TwoButtonToggleElement ToggleElement;
private string _value;
- private HorizontalLayoutGroup _layoutGroup;
+ private readonly HorizontalLayoutGroup _layoutGroup;
+ private readonly IModInputHandler _inputHandler;
- public ModComboInput(TwoButtonToggleElement element, IModMenu menu, IModInputMenu inputMenu) : base(element, menu)
+ public ModComboInput(TwoButtonToggleElement element, IModMenu menu, IModInputMenu inputMenu, IModInputHandler inputHandler) : base(element, menu)
{
+ _inputHandler = inputHandler;
ToggleElement = element;
InputMenu = inputMenu;
Button = new ModLayoutButton(element.GetValue