From c068196ca23074e16c3537f8cd3a664c1fac90a8 Mon Sep 17 00:00:00 2001 From: Abbysssal <55982389+Abbysssal@users.noreply.github.com> Date: Sat, 12 Sep 2020 22:53:42 +0700 Subject: [PATCH] Fix a null reference error, start working on RogueLibs.Interactions --- RogueLibs.Interactions/CustomInteraction.cs | 69 ++++++++++++++++ RogueLibs.Interactions/InteractionTemplate.cs | 80 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 36 +++++++++ .../RogueLibs.Interactions.csproj | 68 ++++++++++++++++ .../RogueLibsInteractions.cs | 52 ++++++++++++ .../RogueLibsInteractionsPlugin.cs | 27 +++++++ RogueLibs.sln | 6 ++ RogueLibs/Properties/AssemblyInfo.cs | 4 +- RogueLibs/RogueLibs.cs | 2 +- RogueLibs/RogueLibsPlugin.cs | 4 +- md/4. Changelog.md | 3 + 11 files changed, 346 insertions(+), 5 deletions(-) create mode 100644 RogueLibs.Interactions/CustomInteraction.cs create mode 100644 RogueLibs.Interactions/InteractionTemplate.cs create mode 100644 RogueLibs.Interactions/Properties/AssemblyInfo.cs create mode 100644 RogueLibs.Interactions/RogueLibs.Interactions.csproj create mode 100644 RogueLibs.Interactions/RogueLibsInteractions.cs create mode 100644 RogueLibs.Interactions/RogueLibsInteractionsPlugin.cs diff --git a/RogueLibs.Interactions/CustomInteraction.cs b/RogueLibs.Interactions/CustomInteraction.cs new file mode 100644 index 000000000..a76a82b99 --- /dev/null +++ b/RogueLibs.Interactions/CustomInteraction.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; + +namespace RogueLibsCore.Interactions +{ + public class CustomInteraction + { + internal CustomInteraction(string id, InteractionType type, CustomName name) + { + Id = id; + Type = type; + ButtonName = name; + } + + /// + /// Identifier of this . + /// + public string Id { get; } + /// + /// of this . + /// + public InteractionType Type { get; set; } + + /// + /// Localizable name of this 's button. + /// + public CustomName ButtonName { get; } + + /// + /// Method that will determine when this will be active for an object. + /// arg1 is the interacting player;
arg2 is the object that is being interacted with.
+ ///
+ public Func Condition { get; set; } + /// + /// Method that will determine the cost and the extra text for this 's button. Return to not add anything. + /// arg1 is the interacting player;
arg2 is the object that is being interacted with;
result is a structure containing the cost and the extra text values.
+ ///
+ public Func GetButtonInfo { get; set; } + /// + /// Method that will invoked when interacted with an object directly or via a button. + /// arg1 is the interacting player;
arg2 is the object that is being interacted with;
result determines whether the interaction should be stopped after interacting.
+ ///
+ public Func Action { get; set; } + + + + } + public struct InteractionButtonInfo + { + public InteractionButtonInfo(int cost) + { + Cost = cost; + ExtraText = string.Empty; + } + public InteractionButtonInfo(string extraText) + { + Cost = 0; + ExtraText = extraText; + } + public InteractionButtonInfo(int cost, string extraText) + { + Cost = cost; + ExtraText = extraText; + } + + public int Cost { get; set; } + public string ExtraText { get; set; } + } +} diff --git a/RogueLibs.Interactions/InteractionTemplate.cs b/RogueLibs.Interactions/InteractionTemplate.cs new file mode 100644 index 000000000..7c2fc72cf --- /dev/null +++ b/RogueLibs.Interactions/InteractionTemplate.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using HarmonyLib; + +namespace RogueLibsCore.Interactions +{ + internal class Interaction + where T : PlayfieldObject + { + public void Patch() + { + RoguePatcher patcher = new RoguePatcher(RogueLibsInteractions.PluginInstance, GetType()); + patcher.Prefix(typeof(T), "DetermineButtons"); + patcher.Prefix(typeof(T), "Interact"); + patcher.Prefix(typeof(T), "PressedButton"); + } + public virtual bool DetermineButtons(T __instance) + { + while (__instance.buttonPrices.Count < __instance.buttons.Count) + __instance.buttonPrices.Add(0); + while (__instance.buttonsExtra.Count < __instance.buttons.Count) + __instance.buttonsExtra.Add(string.Empty); + + List allInteractions = RogueLibsInteractions.CustomInteractions.FindAll(i => i.Condition?.Invoke(__instance.interactingAgent, __instance) ?? false); + foreach (CustomInteraction interaction in allInteractions) + { + if ((interaction.Type & InteractionType.Button) != 0) + { + InteractionButtonInfo? info = interaction.GetButtonInfo(__instance.interactingAgent, __instance); + __instance.buttons.Add(interaction.Id); + __instance.buttonPrices.Add(info?.Cost ?? 0); + __instance.buttonsExtra.Add(info?.ExtraText ?? string.Empty); + } + } + return false; + } + public virtual bool Interact(T __instance, Agent agent) + { + MethodInfo baseMethod = AccessTools.Method(__instance.GetType().BaseType, "Interact"); + baseMethod.Invoke(__instance, new object[] { agent }); + + List allInteractions = RogueLibsInteractions.CustomInteractions.FindAll(i => i.Condition?.Invoke(__instance.interactingAgent, __instance) ?? false); + List actionsOnly = allInteractions.FindAll(i => i.Type == InteractionType.Interact); + List possibleButtons = allInteractions.FindAll(i => i.Type == InteractionType.InteractOrButton); + List buttonsOnly = allInteractions.FindAll(i => i.Type == InteractionType.Button); + __instance.DetermineButtons(); + List origButtons = __instance.buttons; + + foreach (CustomInteraction action in actionsOnly) + { + action.Action?.Invoke(__instance.interactingAgent, __instance); + __instance.StopInteraction(); + } + if (possibleButtons.Count == 1 && (buttonsOnly.Count == 0 && origButtons.Count == 0)) + { + possibleButtons[0].Action?.Invoke(__instance.interactingAgent, __instance); + __instance.StopInteraction(); + } + else if (possibleButtons.Count > 0 || (buttonsOnly.Count > 0 || origButtons.Count > 0)) + { + __instance.ShowObjectButtons(); + } + return false; + + } + public virtual bool PressedButton(T __instance, string buttonText, int buttonPrice) + { + CustomInteraction interaction = RogueLibsInteractions.GetCustomInteraction(buttonText); + if (interaction == null) return true; + bool stopInteracting = interaction.Action?.Invoke(__instance.interactingAgent, __instance) ?? false; + if (stopInteracting) __instance.StopInteraction(); + else __instance.RefreshButtons(); + return false; + } + } +} diff --git a/RogueLibs.Interactions/Properties/AssemblyInfo.cs b/RogueLibs.Interactions/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..2f72af056 --- /dev/null +++ b/RogueLibs.Interactions/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RogueLibs.Interactions")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RogueLibs.Interactions")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c933c4df-e3b1-48a5-8c82-a6c53cb56020")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.1.1.0")] +[assembly: AssemblyFileVersion("2.1.1.0")] diff --git a/RogueLibs.Interactions/RogueLibs.Interactions.csproj b/RogueLibs.Interactions/RogueLibs.Interactions.csproj new file mode 100644 index 000000000..b070c19a0 --- /dev/null +++ b/RogueLibs.Interactions/RogueLibs.Interactions.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {C933C4DF-E3B1-48A5-8C82-A6C53CB56020} + Library + Properties + RogueLibsCore.Interactions + RogueLibs.Interactions + v3.5 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\Libraries\0Harmony.dll + + + ..\Libraries\Assembly-CSharp.dll + + + ..\Libraries\BepInEx.dll + + + + ..\Libraries\UnityEngine.dll + + + ..\Libraries\UnityEngine.CoreModule.dll + + + + + + + + + + + + {a1764685-f31f-4864-aab6-99870b10c239} + RogueLibs + + + + + + + \ No newline at end of file diff --git a/RogueLibs.Interactions/RogueLibsInteractions.cs b/RogueLibs.Interactions/RogueLibsInteractions.cs new file mode 100644 index 000000000..df7ea449a --- /dev/null +++ b/RogueLibs.Interactions/RogueLibsInteractions.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using BepInEx; +using BepInEx.Logging; +using UnityEngine; + +namespace RogueLibsCore.Interactions +{ + public static class RogueLibsInteractions + { + public const string pluginGuid = "abbysssal.streetsofrogue.roguelibs.interactions"; + public const string pluginName = "RogueLibs Interactions"; + public const string pluginVersion = "2.1.1"; + + public static RogueLibsInteractionsPlugin PluginInstance; + internal static ManualLogSource Logger; + + public static List CustomInteractions { get; } = new List(); + + public static CustomInteraction GetCustomInteraction(string id) => CustomInteractions.Find(i => i.Id == id); + public static CustomInteraction CreateCustomInteraction(string id, InteractionType type, CustomNameInfo? buttonName, Func condition) + { + CustomInteraction customInteraction = GetCustomInteraction(id); + if (customInteraction != null) + { + string message = string.Concat("A CustomInteraction with Id \"", id, "\" already exists!"); + Logger.LogError(message); + throw new ArgumentException(message, nameof(id)); + } + CustomInteractions.Add(customInteraction = new CustomInteraction(id, type, + buttonName.HasValue ? RogueLibs.CreateCustomName(id, "Interface", buttonName.Value) : null)); + customInteraction.Condition = condition; + + Logger.LogDebug(string.Concat("A CustomInteraction with Id \"", id, "\" (", type.ToString(), ") was created.")); + + return customInteraction; + } + + + + + } + [Flags] public enum InteractionType + { + // Interact only + Interact = 0b_01, + // Button only + Button = 0b_10, + // Button if Interact is not possible; otherwise, Interact + InteractOrButton = 0b_11 + } +} diff --git a/RogueLibs.Interactions/RogueLibsInteractionsPlugin.cs b/RogueLibs.Interactions/RogueLibsInteractionsPlugin.cs new file mode 100644 index 000000000..7cd2cd646 --- /dev/null +++ b/RogueLibs.Interactions/RogueLibsInteractionsPlugin.cs @@ -0,0 +1,27 @@ +using System; +using BepInEx; +using BepInEx.Logging; + +namespace RogueLibsCore.Interactions +{ + [BepInPlugin(RogueLibsInteractions.pluginGuid, RogueLibsInteractions.pluginName, RogueLibsInteractions.pluginVersion)] + [BepInDependency(RogueLibs.pluginGuid, RogueLibsInteractions.pluginVersion)] + public partial class RogueLibsInteractionsPlugin : BaseUnityPlugin + { + protected static ManualLogSource MyLogger; + + public void Awake() + { + RogueLibsInteractions.PluginInstance = this; + RogueLibsInteractions.Logger = MyLogger = Logger; + + RoguePatcher patcher = new RoguePatcher(this, GetType()); + + + + + + } + + } +} diff --git a/RogueLibs.sln b/RogueLibs.sln index 79661f596..1c301009e 100644 --- a/RogueLibs.sln +++ b/RogueLibs.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RogueLibs", "RogueLibs\Rogu EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RogueLibs.Test", "RogueLibs.Test\RogueLibs.Test.csproj", "{D4A59C1A-DCDA-4DE8-80DF-7B802707000F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RogueLibs.Interactions", "RogueLibs.Interactions\RogueLibs.Interactions.csproj", "{C933C4DF-E3B1-48A5-8C82-A6C53CB56020}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {D4A59C1A-DCDA-4DE8-80DF-7B802707000F}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4A59C1A-DCDA-4DE8-80DF-7B802707000F}.Release|Any CPU.ActiveCfg = Release|Any CPU {D4A59C1A-DCDA-4DE8-80DF-7B802707000F}.Release|Any CPU.Build.0 = Release|Any CPU + {C933C4DF-E3B1-48A5-8C82-A6C53CB56020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C933C4DF-E3B1-48A5-8C82-A6C53CB56020}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C933C4DF-E3B1-48A5-8C82-A6C53CB56020}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C933C4DF-E3B1-48A5-8C82-A6C53CB56020}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/RogueLibs/Properties/AssemblyInfo.cs b/RogueLibs/Properties/AssemblyInfo.cs index 9677b4894..395f2b514 100644 --- a/RogueLibs/Properties/AssemblyInfo.cs +++ b/RogueLibs/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // Можно задать все значения или принять номера сборки и редакции по умолчанию // используя "*", как показано ниже: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.1.0.0")] -[assembly: AssemblyFileVersion("2.1.0.0")] +[assembly: AssemblyVersion("2.1.1.0")] +[assembly: AssemblyFileVersion("2.1.1.0")] diff --git a/RogueLibs/RogueLibs.cs b/RogueLibs/RogueLibs.cs index f71cd6a33..105b1e25c 100644 --- a/RogueLibs/RogueLibs.cs +++ b/RogueLibs/RogueLibs.cs @@ -22,7 +22,7 @@ public static class RogueLibs /// /// ' version. Do not use this value in your attribute! /// - public const string pluginVersion = "2.1"; + public const string pluginVersion = "2.1.1"; /// /// Main instance. diff --git a/RogueLibs/RogueLibsPlugin.cs b/RogueLibs/RogueLibsPlugin.cs index 18a2420e7..110f6bbb0 100644 --- a/RogueLibs/RogueLibsPlugin.cs +++ b/RogueLibs/RogueLibsPlugin.cs @@ -465,7 +465,7 @@ protected static bool ScrollingMenu_Setup(ScrollingMenu __instance, ButtonData m myButtonData.scrollingHighlighted4 = (__instance.menuType == "Items" || __instance.menuType == "TraitUnlocks") && myUnlock.notActive; // gray 'not active' highlight myButtonData.highlightedSprite = __instance.solidObjectButton; } - else if ((custom == null && myUnlock.cost != 0) || custom.UnlockCost != null) + else if ((custom == null && myUnlock.cost != 0) || custom?.UnlockCost != null) { // not unlocked, original mutator OR custom and can be purchased myButtonData.buttonText += " - $" + myUnlock.cost; myButtonData.scrollingHighlighted2 = true; // blue 'purchasable' highlight @@ -865,7 +865,7 @@ protected static bool CharacterCreation_Setup(CharacterCreation __instance, Butt myButtonData.scrollingHighlighted4 = false; myButtonData.highlightedSprite = __instance.solidObjectButton; } - else if ((custom == null && myUnlock.cost != 0) || custom.UnlockCost != null) // not unlocked, original mutator OR custom and can be purchased + else if ((custom == null && myUnlock.cost != 0) || custom?.UnlockCost != null) // not unlocked, original mutator OR custom and can be purchased { myButtonData.buttonText = __instance.gc.nameDB.GetName(myUnlock.unlockName, myUnlock.unlockNameType) + " - $" + myUnlock.cost; myButtonData.scrollingHighlighted2 = true; // blue 'purchasable' highlight diff --git a/md/4. Changelog.md b/md/4. Changelog.md index d9a734ffe..b12b3ea73 100644 --- a/md/4. Changelog.md +++ b/md/4. Changelog.md @@ -16,6 +16,9 @@ ## Changelog ## +#### RogueLibs v2.1.1 #### +- Fixed an error, when Mutator Menu didn't open because of a null reference; + #### RogueLibs v2.1 #### - Added `OnEnabled`, `OnDisabled` and `OnChangedState` events to `CustomMutator`;