Initial commit
This commit is contained in:
92
mod/PLibActions/PAction.cs
Normal file
92
mod/PLibActions/PAction.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace PeterHan.PLib.Actions {
|
||||
/// <summary>
|
||||
/// An Action managed by PLib. Actions have key bindings assigned to them.
|
||||
/// </summary>
|
||||
public sealed class PAction {
|
||||
/// <summary>
|
||||
/// The maximum action value (typically used to mean "no action") used in the currently
|
||||
/// running instance of the game.
|
||||
///
|
||||
/// Since Action is compiled to a const int when a mod is built, any changes to the
|
||||
/// Action enum will break direct references to Action.NumActions. Use this property
|
||||
/// instead to always use the intended "no action" value.
|
||||
/// </summary>
|
||||
public static Action MaxAction { get; }
|
||||
|
||||
static PAction() {
|
||||
if (!Enum.TryParse(nameof(Action.NumActions), out Action limit))
|
||||
limit = Action.NumActions;
|
||||
MaxAction = limit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default key binding for this action. Not necessarily the current key binding.
|
||||
/// </summary>
|
||||
internal PKeyBinding DefaultBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The action's non-localized identifier. Something like YOURMOD.CATEGORY.ACTIONNAME.
|
||||
/// </summary>
|
||||
public string Identifier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The action's ID. This ID is assigned internally upon register and used for PLib
|
||||
/// indexing. Even if you somehow obtain it in your mod, it is not to be used!
|
||||
/// </summary>
|
||||
private readonly int id;
|
||||
|
||||
/// <summary>
|
||||
/// The action's title.
|
||||
/// </summary>
|
||||
public LocString Title { get; }
|
||||
|
||||
internal PAction(int id, string identifier, LocString title, PKeyBinding binding) {
|
||||
if (id <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(id));
|
||||
DefaultBinding = binding;
|
||||
Identifier = identifier;
|
||||
this.id = id;
|
||||
Title = title;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return (obj is PAction other) && other.id == id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the Klei action for this PAction.
|
||||
/// </summary>
|
||||
/// <returns>The Klei action for use in game functions.</returns>
|
||||
public Action GetKAction() {
|
||||
return (Action)((int)MaxAction + id);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "PAction[" + Identifier + "]: " + Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
357
mod/PLibActions/PActionManager.cs
Normal file
357
mod/PLibActions/PActionManager.cs
Normal file
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using HarmonyLib;
|
||||
using PeterHan.PLib.Core;
|
||||
using PeterHan.PLib.Detours;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PeterHan.PLib.Actions {
|
||||
/// <summary>
|
||||
/// Manages PAction functionality which must be single instance.
|
||||
/// </summary>
|
||||
public sealed class PActionManager : PForwardedComponent {
|
||||
/// <summary>
|
||||
/// Prototypes the required parameters for new BindingEntry() since they changed in
|
||||
/// U39-489490.
|
||||
/// </summary>
|
||||
private delegate BindingEntry NewEntry(string group, GamepadButton button,
|
||||
KKeyCode key_code, Modifier modifier, Action action);
|
||||
|
||||
/// <summary>
|
||||
/// The category used for all PLib keys.
|
||||
/// </summary>
|
||||
public const string CATEGORY = "PLib";
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new BindingEntry.
|
||||
/// </summary>
|
||||
private static readonly NewEntry NEW_BINDING_ENTRY = typeof(BindingEntry).
|
||||
DetourConstructor<NewEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// The version of this component. Uses the running PLib version.
|
||||
/// </summary>
|
||||
internal static readonly Version VERSION = new Version(PVersion.VERSION);
|
||||
|
||||
/// <summary>
|
||||
/// The instantiated copy of this class.
|
||||
/// </summary>
|
||||
internal static PActionManager Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Assigns the key bindings to each Action when they are needed.
|
||||
/// </summary>
|
||||
private static void AssignKeyBindings() {
|
||||
var allMods = PRegistry.Instance.GetAllComponents(typeof(PActionManager).FullName);
|
||||
if (allMods != null)
|
||||
foreach (var mod in allMods)
|
||||
mod.Process(0, null);
|
||||
}
|
||||
|
||||
private static void CKeyDef_Postfix(KInputController.KeyDef __instance) {
|
||||
if (Instance != null)
|
||||
__instance.mActionFlags = ExtendFlags(__instance.mActionFlags,
|
||||
Instance.GetMaxAction());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extends the action flags array to the new maximum length.
|
||||
/// </summary>
|
||||
/// <param name="oldActionFlags">The old flags array.</param>
|
||||
/// <param name="newMax">The minimum length.</param>
|
||||
/// <returns>The new action flags array.</returns>
|
||||
internal static bool[] ExtendFlags(bool[] oldActionFlags, int newMax) {
|
||||
int n = oldActionFlags.Length;
|
||||
bool[] newActionFlags;
|
||||
if (n < newMax) {
|
||||
newActionFlags = new bool[newMax];
|
||||
Array.Copy(oldActionFlags, newActionFlags, n);
|
||||
} else
|
||||
newActionFlags = oldActionFlags;
|
||||
return newActionFlags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if an action is already bound to a key.
|
||||
/// </summary>
|
||||
/// <param name="currentBindings">The current key bindings.</param>
|
||||
/// <param name="action">The action to look up.</param>
|
||||
/// <returns>true if the action already has a binding assigned, or false otherwise.</returns>
|
||||
private static bool FindKeyBinding(IEnumerable<BindingEntry> currentBindings,
|
||||
Action action) {
|
||||
bool inBindings = false;
|
||||
foreach (var entry in currentBindings)
|
||||
if (entry.mAction == action) {
|
||||
LogKeyBind("Action {0} already exists; assigned to KeyCode {1}".F(action,
|
||||
entry.mKeyCode));
|
||||
inBindings = true;
|
||||
break;
|
||||
}
|
||||
return inBindings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a Klei key binding title.
|
||||
/// </summary>
|
||||
/// <param name="category">The category of the key binding.</param>
|
||||
/// <param name="item">The key binding to retrieve.</param>
|
||||
/// <returns>The Strings entry describing this key binding.</returns>
|
||||
private static string GetBindingTitle(string category, string item) {
|
||||
return "STRINGS.INPUT_BINDINGS." + category.ToUpperInvariant() + "." + item.
|
||||
ToUpperInvariant();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a "localized" (if PLib is localized) description of additional key codes
|
||||
/// from the KKeyCode enumeration, to avoid warning spam on popular keybinds like
|
||||
/// arrow keys, delete, home, and so forth.
|
||||
/// </summary>
|
||||
/// <param name="code">The key code.</param>
|
||||
/// <returns>A description of that key code, or null if no localization is found.</returns>
|
||||
internal static string GetExtraKeycodeLocalized(KKeyCode code) {
|
||||
string localCode = null;
|
||||
switch (code) {
|
||||
case KKeyCode.Home:
|
||||
localCode = PLibStrings.KEY_HOME;
|
||||
break;
|
||||
case KKeyCode.End:
|
||||
localCode = PLibStrings.KEY_END;
|
||||
break;
|
||||
case KKeyCode.Delete:
|
||||
localCode = PLibStrings.KEY_DELETE;
|
||||
break;
|
||||
case KKeyCode.PageDown:
|
||||
localCode = PLibStrings.KEY_PAGEDOWN;
|
||||
break;
|
||||
case KKeyCode.PageUp:
|
||||
localCode = PLibStrings.KEY_PAGEUP;
|
||||
break;
|
||||
case KKeyCode.LeftArrow:
|
||||
localCode = PLibStrings.KEY_ARROWLEFT;
|
||||
break;
|
||||
case KKeyCode.UpArrow:
|
||||
localCode = PLibStrings.KEY_ARROWUP;
|
||||
break;
|
||||
case KKeyCode.RightArrow:
|
||||
localCode = PLibStrings.KEY_ARROWRIGHT;
|
||||
break;
|
||||
case KKeyCode.DownArrow:
|
||||
localCode = PLibStrings.KEY_ARROWDOWN;
|
||||
break;
|
||||
case KKeyCode.Pause:
|
||||
localCode = PLibStrings.KEY_PAUSE;
|
||||
break;
|
||||
case KKeyCode.SysReq:
|
||||
localCode = PLibStrings.KEY_SYSRQ;
|
||||
break;
|
||||
case KKeyCode.Print:
|
||||
localCode = PLibStrings.KEY_PRTSCREEN;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return localCode;
|
||||
}
|
||||
|
||||
private static bool GetKeycodeLocalized_Prefix(KKeyCode key_code, ref string __result) {
|
||||
string newResult = GetExtraKeycodeLocalized(key_code);
|
||||
if (newResult != null)
|
||||
__result = newResult;
|
||||
return newResult == null;
|
||||
}
|
||||
|
||||
private static void IsActive_Prefix(ref bool[] ___mActionState) {
|
||||
if (Instance != null)
|
||||
___mActionState = ExtendFlags(___mActionState, Instance.GetMaxAction());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a message encountered by the PLib key binding system.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
internal static void LogKeyBind(string message) {
|
||||
Debug.LogFormat("[PKeyBinding] {0}", message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning encountered by the PLib key binding system.
|
||||
/// </summary>
|
||||
/// <param name="message">The warning message.</param>
|
||||
internal static void LogKeyBindWarning(string message) {
|
||||
Debug.LogWarningFormat("[PKeyBinding] {0}", message);
|
||||
}
|
||||
|
||||
private static void QueueButtonEvent_Prefix(ref bool[] ___mActionState,
|
||||
KInputController.KeyDef key_def) {
|
||||
if (KInputManager.isFocused && Instance != null) {
|
||||
int max = Instance.GetMaxAction();
|
||||
key_def.mActionFlags = ExtendFlags(key_def.mActionFlags, max);
|
||||
___mActionState = ExtendFlags(___mActionState, max);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetDefaultKeyBindings_Postfix() {
|
||||
Instance?.UpdateMaxAction();
|
||||
}
|
||||
|
||||
public override Version Version => VERSION;
|
||||
|
||||
/// <summary>
|
||||
/// Queued key binds which are resolved on load.
|
||||
/// </summary>
|
||||
private readonly IList<PAction> actions;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum action index of any custom action registered across all mods.
|
||||
/// </summary>
|
||||
private int maxAction;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new action manager used to create and assign custom actions. Due to the
|
||||
/// timing of when the user's key bindings are loaded, all actions must be added in
|
||||
/// OnLoad().
|
||||
/// </summary>
|
||||
public PActionManager() {
|
||||
actions = new List<PAction>(8);
|
||||
maxAction = 0;
|
||||
}
|
||||
|
||||
public override void Bootstrap(Harmony plibInstance) {
|
||||
// GameInputMapping.LoadBindings occurs after mods load but before the rest of the
|
||||
// PLib components are initialized
|
||||
plibInstance.Patch(typeof(GameInputMapping), nameof(GameInputMapping.
|
||||
LoadBindings), prefix: PatchMethod(nameof(AssignKeyBindings)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a PAction with the action manager.
|
||||
///
|
||||
/// This call should occur after PUtil.InitLibrary() during the mod OnLoad(). If called
|
||||
/// earlier, it may fail with InvalidOperationException, and if called later, the
|
||||
/// user's custom key bind (if applicable) will be discarded.
|
||||
/// </summary>
|
||||
/// <param name="identifier">The identifier for this action.</param>
|
||||
/// <param name="title">The action's title. If null, the default value from
|
||||
/// STRINGS.INPUT_BINDINGS.PLIB.identifier will be used instead.</param>
|
||||
/// <param name="binding">The default key binding for this action. If null, no key will
|
||||
/// be bound by default, but the user can set a key bind.</param>
|
||||
/// <returns>The action thus registered.</returns>
|
||||
public PAction CreateAction(string identifier, LocString title,
|
||||
PKeyBinding binding = null) {
|
||||
PAction action;
|
||||
RegisterForForwarding();
|
||||
int curIndex = GetSharedData(1);
|
||||
action = new PAction(curIndex, identifier, title, binding);
|
||||
SetSharedData(curIndex + 1);
|
||||
actions.Add(action);
|
||||
return action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum length of the Action enum, including custom actions. If no
|
||||
/// actions are defined, returns NumActions - 1 since NumActions is reserved in the
|
||||
/// base game.
|
||||
///
|
||||
/// This value will not be accurate until all mods have loaded and key binds
|
||||
/// registered (AfterLayerableLoad or later such as BeforeDbInit).
|
||||
/// </summary>
|
||||
/// <returns>The maximum length required to represent all Actions.</returns>
|
||||
public int GetMaxAction() {
|
||||
int nActions = (int)PAction.MaxAction;
|
||||
return (maxAction > 0) ? (maxAction + nActions) : nActions - 1;
|
||||
}
|
||||
|
||||
public override void Initialize(Harmony plibInstance) {
|
||||
Instance = this;
|
||||
|
||||
// GameInputMapping
|
||||
plibInstance.Patch(typeof(GameInputMapping), nameof(GameInputMapping.
|
||||
SetDefaultKeyBindings), postfix: PatchMethod(nameof(
|
||||
SetDefaultKeyBindings_Postfix)));
|
||||
|
||||
// GameUtil
|
||||
plibInstance.Patch(typeof(GameUtil), nameof(GameUtil.GetKeycodeLocalized),
|
||||
prefix: PatchMethod(nameof(GetKeycodeLocalized_Prefix)));
|
||||
|
||||
// KInputController
|
||||
plibInstance.PatchConstructor(typeof(KInputController.KeyDef), new Type[] {
|
||||
typeof(KKeyCode), typeof(Modifier)
|
||||
}, postfix: PatchMethod(nameof(CKeyDef_Postfix)));
|
||||
plibInstance.Patch(typeof(KInputController), nameof(KInputController.IsActive),
|
||||
prefix: PatchMethod(nameof(IsActive_Prefix)));
|
||||
plibInstance.Patch(typeof(KInputController), nameof(KInputController.
|
||||
QueueButtonEvent), prefix: PatchMethod(nameof(QueueButtonEvent_Prefix)));
|
||||
}
|
||||
|
||||
public override void PostInitialize(Harmony plibInstance) {
|
||||
// Needs to occur after localization
|
||||
Strings.Add(GetBindingTitle(CATEGORY, "NAME"), PLibStrings.KEY_CATEGORY_TITLE);
|
||||
var allMods = PRegistry.Instance.GetAllComponents(ID);
|
||||
if (allMods != null)
|
||||
foreach (var mod in allMods)
|
||||
mod.Process(1, null);
|
||||
}
|
||||
|
||||
public override void Process(uint operation, object _) {
|
||||
int n = actions.Count;
|
||||
if (n > 0) {
|
||||
if (operation == 0)
|
||||
RegisterKeyBindings();
|
||||
else if (operation == 1) {
|
||||
#if DEBUG
|
||||
LogKeyBind("Localizing titles for {0}".F(Assembly.GetExecutingAssembly().
|
||||
GetNameSafe() ?? "?"));
|
||||
#endif
|
||||
foreach (var action in actions)
|
||||
Strings.Add(GetBindingTitle(CATEGORY, action.GetKAction().ToString()),
|
||||
action.Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterKeyBindings() {
|
||||
int n = actions.Count;
|
||||
LogKeyBind("Registering {0:D} key bind(s) for mod {1}".F(n, Assembly.
|
||||
GetExecutingAssembly().GetNameSafe() ?? "?"));
|
||||
var currentBindings = new List<BindingEntry>(GameInputMapping.DefaultBindings);
|
||||
foreach (var action in actions) {
|
||||
var kAction = action.GetKAction();
|
||||
var binding = action.DefaultBinding;
|
||||
if (!FindKeyBinding(currentBindings, kAction)) {
|
||||
if (binding == null)
|
||||
binding = new PKeyBinding();
|
||||
// This constructor changes often enough to be worth detouring
|
||||
currentBindings.Add(NEW_BINDING_ENTRY.Invoke(CATEGORY, binding.
|
||||
GamePadButton, binding.Key, binding.Modifiers, kAction));
|
||||
}
|
||||
}
|
||||
GameInputMapping.SetDefaultKeyBindings(currentBindings.ToArray());
|
||||
UpdateMaxAction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the maximum action for this instance.
|
||||
/// </summary>
|
||||
private void UpdateMaxAction() {
|
||||
maxAction = GetSharedData(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
mod/PLibActions/PKeyBinding.cs
Normal file
71
mod/PLibActions/PKeyBinding.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace PeterHan.PLib.Actions {
|
||||
/// <summary>
|
||||
/// Represents a single key binding to an action.
|
||||
/// </summary>
|
||||
public sealed class PKeyBinding {
|
||||
/// <summary>
|
||||
/// The gamepad button to bind.
|
||||
/// </summary>
|
||||
public GamepadButton GamePadButton { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key code.
|
||||
/// </summary>
|
||||
public KKeyCode Key { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The modifier code.
|
||||
/// </summary>
|
||||
public Modifier Modifiers { get; set; }
|
||||
|
||||
public PKeyBinding(KKeyCode keyCode = KKeyCode.None, Modifier modifiers = Modifier.
|
||||
None, GamepadButton gamePadButton = GamepadButton.NumButtons) {
|
||||
GamePadButton = gamePadButton;
|
||||
Key = keyCode;
|
||||
Modifiers = modifiers;
|
||||
}
|
||||
|
||||
public PKeyBinding(PKeyBinding other) : this() {
|
||||
if (other != null) {
|
||||
GamePadButton = other.GamePadButton;
|
||||
Key = other.Key;
|
||||
Modifiers = other.Modifiers;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is PKeyBinding other && other.Key == Key && other.Modifiers ==
|
||||
Modifiers && other.GamePadButton == GamePadButton;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
// Was auto generated by VS, please do not kill me
|
||||
int hashCode = 1488379021 + GamePadButton.GetHashCode();
|
||||
hashCode = hashCode * -1521134295 + Key.GetHashCode();
|
||||
hashCode = hashCode * -1521134295 + Modifiers.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Modifiers + " " + Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
mod/PLibActions/PLibActions.csproj
Normal file
20
mod/PLibActions/PLibActions.csproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Title>PLib Actions</Title>
|
||||
<AssemblyTitle>PLib.Actions</AssemblyTitle>
|
||||
<Version>4.11.0.0</Version>
|
||||
<UsesPLib>false</UsesPLib>
|
||||
<RootNamespace>PeterHan.PLib.Actions</RootNamespace>
|
||||
<AssemblyVersion>4.11.0.0</AssemblyVersion>
|
||||
<DistributeMod>false</DistributeMod>
|
||||
<PLibCore>true</PLibCore>
|
||||
<Platforms>Vanilla;Mergedown</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DocumentationFile>bin\$(Platform)\Release\PLibActions.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../PLibCore/PLibCore.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
118
mod/PLibActions/PToolMode.cs
Normal file
118
mod/PLibActions/PToolMode.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using PeterHan.PLib.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PeterHan.PLib.Actions {
|
||||
/// <summary>
|
||||
/// A tool mode used in custom tool menus. Shown in the options in the bottom right.
|
||||
/// </summary>
|
||||
public sealed class PToolMode {
|
||||
/// <summary>
|
||||
/// Sets up tool options in the tool parameter menu.
|
||||
/// </summary>
|
||||
/// <param name="menu">The menu to configure.</param>
|
||||
/// <param name="options">The available modes.</param>
|
||||
/// <returns>A dictionary which is updated in real time to contain the actual state of each mode.</returns>
|
||||
public static IDictionary<string, ToolParameterMenu.ToggleState> PopulateMenu(
|
||||
ToolParameterMenu menu, ICollection<PToolMode> options) {
|
||||
if (options == null)
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
var kOpt = new Dictionary<string, ToolParameterMenu.ToggleState>(options.Count);
|
||||
// Add to Klei format, yes it loses the order but it means less of a mess
|
||||
foreach (var option in options) {
|
||||
string key = option.Key;
|
||||
if (!string.IsNullOrEmpty(option.Title))
|
||||
Strings.Add("STRINGS.UI.TOOLS.FILTERLAYERS." + key, option.Title);
|
||||
kOpt.Add(key, option.State);
|
||||
}
|
||||
menu.PopulateMenu(kOpt);
|
||||
return kOpt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a tool with the game. It still must be added to a tool collection to be
|
||||
/// visible.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The tool type to register.</typeparam>
|
||||
/// <param name="controller">The player controller which will be its parent; consider
|
||||
/// using in a postfix on PlayerController.OnPrefabInit.</param>
|
||||
public static void RegisterTool<T>(PlayerController controller) where T : InterfaceTool
|
||||
{
|
||||
if (controller == null)
|
||||
throw new ArgumentNullException(nameof(controller));
|
||||
// Create list so that new tool can be appended at the end
|
||||
var interfaceTools = ListPool<InterfaceTool, PlayerController>.Allocate();
|
||||
interfaceTools.AddRange(controller.tools);
|
||||
var newTool = new UnityEngine.GameObject(typeof(T).Name);
|
||||
var tool = newTool.AddComponent<T>();
|
||||
// Reparent tool to the player controller, then enable/disable to load it
|
||||
newTool.transform.SetParent(controller.gameObject.transform);
|
||||
newTool.gameObject.SetActive(true);
|
||||
newTool.gameObject.SetActive(false);
|
||||
interfaceTools.Add(tool);
|
||||
controller.tools = interfaceTools.ToArray();
|
||||
interfaceTools.Recycle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A unique key used to identify this mode.
|
||||
/// </summary>
|
||||
public string Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current state of this tool mode.
|
||||
/// </summary>
|
||||
public ToolParameterMenu.ToggleState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The title displayed on-screen for this mode.
|
||||
/// </summary>
|
||||
public LocString Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new tool mode entry.
|
||||
/// </summary>
|
||||
/// <param name="key">The key which identifies this tool mode.</param>
|
||||
/// <param name="title">The title to be displayed. If null, the title will be taken
|
||||
/// from the default location in STRINGS.UI.TOOLS.FILTERLAYERS.</param>
|
||||
/// <param name="state">The initial state, default Off.</param>
|
||||
public PToolMode(string key, LocString title, ToolParameterMenu.ToggleState state =
|
||||
ToolParameterMenu.ToggleState.Off) {
|
||||
if (string.IsNullOrEmpty(key))
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
Key = key;
|
||||
State = state;
|
||||
Title = title;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is PToolMode other && other.Key == Key;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return Key.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "{0} ({1})".F(Key, Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user