Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LoyalSpears v1.0.0
BepInEx/plugins/LoyalSpears.dll
Decompiled a week agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Extensions; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("LoyalSpears")] [assembly: AssemblyDescription("Spears that are loyal, Return on throw and do not get picked up by others.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("LoyalSpears")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("C00EDC1A-636F-4221-834A-B22A0174B582")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LoyalSpears { [BepInPlugin("com.loyalspears.mod", "Loyal Spears", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] internal class LoyalSpearsPlugin : BaseUnityPlugin { private const string Guid = "com.loyalspears.mod"; private const string Name = "Loyal Spears"; private const string Version = "1.0.0"; private void Awake() { PluginConfig.Init((BaseUnityPlugin)(object)this); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loyal Spears v1.0.0 loaded"); } } internal class LoyaltyComponent : WeightReserverComponent { private float _ownerDeathCountOnThrow; private ItemDrop _attachedItemDrop; public void Setup(ItemDrop attachedItemDrop, Player originalOwner, float ownerDeathCountOnThrow) { _attachedItemDrop = attachedItemDrop; _ownerDeathCountOnThrow = ownerDeathCountOnThrow; Setup(attachedItemDrop.m_itemData, originalOwner); } protected override void StartTimer() { ((MonoBehaviour)this).StartCoroutine("ReturnInABit"); } public override void StopTimer() { ((MonoBehaviour)this).StopCoroutine("ReturnInABit"); } private IEnumerator ReturnInABit() { float value = PluginConfig.ReturnAfterSeconds.Value; if (value > 0f) { yield return (object)new WaitForSeconds(value); } if ((Object)(object)base.OriginalOwner != (Object)null && (Object)(object)_attachedItemDrop != (Object)null && _attachedItemDrop.CanPickup(true)) { ZNetView nview = SpearPatches.GetNview((Character)(object)base.OriginalOwner); ZNetView nview2 = SpearPatches.GetNview(_attachedItemDrop); nview.InvokeRPC("RPC_PickupLoyaltySpear", new object[2] { nview2.GetZDO().m_uid, _ownerDeathCountOnThrow }); } Object.Destroy((Object)(object)this); } } internal class PlayerWeightReserverTrackerComponent : MonoBehaviour { public readonly List<WeightReserverComponent> WeightReservers = new List<WeightReserverComponent>(); } public static class PluginConfig { public static ConfigEntry<bool> EnableBlockAutoPickup { get; private set; } public static ConfigEntry<float> ReturnAfterSeconds { get; private set; } public static ConfigEntry<bool> BlockReturnIfOverburdened { get; private set; } public static ConfigEntry<bool> ReserveThrowWeight { get; private set; } public static ConfigEntry<float> MaxReservationSeconds { get; private set; } public static ConfigEntry<float> FlightDistanceUntilAutoReturn { get; private set; } public static void Init(BaseUnityPlugin plugin) { EnableBlockAutoPickup = ConfigFileExtensions.BindConfig<bool>(plugin.Config, "General", "EnableBlockAutoPickup", true, "Prevent other players from auto-picking up your thrown spears.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ReturnAfterSeconds = ConfigFileExtensions.BindConfig<float>(plugin.Config, "General", "ReturnAfterSeconds", 5f, "Seconds until a thrown spear returns to its owner after hitting the ground. 0 or negative disables.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); BlockReturnIfOverburdened = ConfigFileExtensions.BindConfig<bool>(plugin.Config, "General", "BlockReturnIfOverburdened", false, "Skip auto-return if picking it back up would overburden the owner.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ReserveThrowWeight = ConfigFileExtensions.BindConfig<bool>(plugin.Config, "General", "ReserveThrowWeight", true, "Count thrown spear weight as reserved during auto-pickup checks.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); MaxReservationSeconds = ConfigFileExtensions.BindConfig<float>(plugin.Config, "General", "MaxReservationSeconds", 60f, "Safety cap so a lost spear doesn't reserve weight forever.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); FlightDistanceUntilAutoReturn = ConfigFileExtensions.BindConfig<float>(plugin.Config, "General", "FlightDistanceUntilAutoReturn", -1f, "If a thrown spear travels further than this distance from the owner without hitting the ground, it auto-returns. Negative disables.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); } } [HarmonyPatch] internal class SpearPatches { private static readonly FieldInfo FieldOwner = AccessTools.Field(typeof(Projectile), "m_owner"); private static readonly FieldInfo FieldNview = AccessTools.Field(typeof(Character), "m_nview"); private static readonly FieldInfo FieldInventory = AccessTools.Field(typeof(Humanoid), "m_inventory"); private static readonly FieldInfo FieldPlayerProfile = AccessTools.Field(typeof(Game), "m_playerProfile"); private static readonly FieldInfo FieldItemNview = AccessTools.Field(typeof(ItemDrop), "m_nview"); private static readonly FieldInfo FieldSpawnItem = AccessTools.Field(typeof(Projectile), "m_spawnItem"); public static bool IsSpear(ItemData itemData) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 if (itemData != null && itemData.m_shared != null) { return (int)itemData.m_shared.m_skillType == 5; } return false; } public static bool IsWeapon(ItemData itemData) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 if (itemData == null || itemData.m_shared == null) { return false; } ItemType itemType = itemData.m_shared.m_itemType; if ((int)itemType == 3 || (int)itemType == 14 || (int)itemType == 22) { return true; } return false; } public static bool IsCompatibleThrowableWeaponModInstalled() { return Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"); } private static bool IsIncompatibleThrowableWeaponModInstalled() { return Chainloader.PluginInfos.ContainsKey("neobotics.valheim_mod.maxaxe"); } public static bool IsPotentiallyThrowable(ItemData itemData) { if (IsSpear(itemData)) { return true; } if (!IsIncompatibleThrowableWeaponModInstalled() && IsCompatibleThrowableWeaponModInstalled()) { return IsWeapon(itemData); } return false; } public static ZNetView GetNview(Character character) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (ZNetView)FieldNview.GetValue(character); } public static ZNetView GetNview(ItemDrop itemDrop) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (ZNetView)FieldItemNview.GetValue(itemDrop); } public static Inventory GetInventory(Humanoid humanoid) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (Inventory)FieldInventory.GetValue(humanoid); } public static PlayerProfile GetPlayerProfile(Game game) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (PlayerProfile)FieldPlayerProfile.GetValue(game); } public static Character GetProjectileOwner(Projectile projectile) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (Character)FieldOwner.GetValue(projectile); } public static ItemData GetSpawnItem(Projectile projectile) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown return (ItemData)FieldSpawnItem.GetValue(projectile); } public static float GetPlayerDeathCount() { return GetPlayerProfile(Game.instance).m_playerStats[0].m_stats[(PlayerStatType)0]; } [HarmonyPatch(typeof(Player), "Awake")] [HarmonyPostfix] private static void AddLoyaltySpearRPC(Player __instance) { GetNview((Character)(object)__instance).Register<ZDOID, float>("RPC_PickupLoyaltySpear", (Action<long, ZDOID, float>)delegate(long player, ZDOID item, float deaths) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) RPC_PickupLoyaltySpear(__instance, item, deaths); }); } private static void RPC_PickupLoyaltySpear(Player player, ZDOID item, float deathCountOnThrow) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) GameObject val = ZNetScene.instance.FindInstance(item); if ((Object)(object)val == (Object)null) { return; } ItemDrop component = val.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null)) { Inventory inventory = GetInventory((Humanoid)(object)player); if (inventory.CanAddItem(component.m_itemData, -1) && (!PluginConfig.BlockReturnIfOverburdened.Value || !(component.m_itemData.GetWeight(-1) + inventory.GetTotalWeight() > player.GetMaxCarryWeight())) && (!(deathCountOnThrow >= 0f) || !(GetPlayerDeathCount() > deathCountOnThrow))) { component.Pickup((Humanoid)(object)player); } } } private static ItemDrop AddLoyaltySpearPickupComponent(ItemDrop item, Projectile projectile) { Character projectileOwner = GetProjectileOwner(projectile); Player val = (Player)(object)((projectileOwner is Player) ? projectileOwner : null); if (val != null && (Object)(object)val == (Object)(object)Player.m_localPlayer && (Object)(object)item != (Object)null && IsPotentiallyThrowable(item.m_itemData)) { if (IsSpear(item.m_itemData) && PluginConfig.ReturnAfterSeconds.Value >= 0f) { LoyaltyComponent loyaltyComponent = default(LoyaltyComponent); if (((Component)item).gameObject.TryGetComponent<LoyaltyComponent>(ref loyaltyComponent)) { loyaltyComponent.StopTimer(); } else { loyaltyComponent = ((Component)item).gameObject.AddComponent<LoyaltyComponent>(); } float playerDeathCount = GetPlayerDeathCount(); loyaltyComponent.Setup(item, val, playerDeathCount); } else if (PluginConfig.ReserveThrowWeight.Value && PluginConfig.MaxReservationSeconds.Value >= 0f) { WeightReserverComponent weightReserverComponent = default(WeightReserverComponent); if (((Component)item).gameObject.TryGetComponent<WeightReserverComponent>(ref weightReserverComponent)) { weightReserverComponent.StopTimer(); } else { weightReserverComponent = ((Component)item).gameObject.AddComponent<WeightReserverComponent>(); } weightReserverComponent.Setup(item.m_itemData, val); } } return item; } [HarmonyPatch(typeof(Projectile), "Setup")] [HarmonyPostfix] public static void Projectile_Setup_Postfix(Projectile __instance) { ItemData spawnItem = GetSpawnItem(__instance); if (!PluginConfig.ReserveThrowWeight.Value || PluginConfig.MaxReservationSeconds.Value < 0f) { return; } Character projectileOwner = GetProjectileOwner(__instance); Player val = (Player)(object)((projectileOwner is Player) ? projectileOwner : null); if (val != null && (Object)(object)val == (Object)(object)Player.m_localPlayer && spawnItem != null && IsPotentiallyThrowable(spawnItem)) { WeightReserverComponent weightReserverComponent = default(WeightReserverComponent); if (((Component)__instance).gameObject.TryGetComponent<WeightReserverComponent>(ref weightReserverComponent)) { weightReserverComponent.StopTimer(); } else { weightReserverComponent = ((Component)__instance).gameObject.AddComponent<WeightReserverComponent>(); } weightReserverComponent.Setup(spawnItem, val); } } [HarmonyPatch(typeof(Projectile), "SpawnOnHit")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Projectile_SpawnOnHit_Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo DropItemMethod = AccessTools.DeclaredMethod(typeof(ItemDrop), "DropItem", (Type[])null, (Type[])null); MethodInfo AddLoyaltyToSpearMethod = AccessTools.DeclaredMethod(typeof(SpearPatches), "AddLoyaltySpearPickupComponent", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { yield return instruction; if (instruction.opcode == OpCodes.Call && CodeInstructionExtensions.OperandIs(instruction, (MemberInfo)DropItemMethod)) { yield return new CodeInstruction(OpCodes.Ldarg_0, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)AddLoyaltyToSpearMethod); } } } [HarmonyPatch(typeof(Projectile), "LateUpdate")] [HarmonyPostfix] public static void Projectile_LateUpdate_Postfix(Projectile __instance) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) ItemData spawnItem = GetSpawnItem(__instance); Character projectileOwner = GetProjectileOwner(__instance); if (!(projectileOwner is Player) || spawnItem == null || !IsSpear(spawnItem)) { return; } float value = PluginConfig.FlightDistanceUntilAutoReturn.Value; if (!(value < 0f)) { Vector3 val = ((Component)projectileOwner).transform.position - ((Component)__instance).transform.position; if (((Vector3)(ref val)).sqrMagnitude > value * value) { ZNetView nview = GetNview(ItemDrop.DropItem(spawnItem, 0, ((Component)__instance).transform.position, ((Component)__instance).transform.rotation)); GetNview(projectileOwner).InvokeRPC("RPC_PickupLoyaltySpear", new object[2] { nview.GetZDO().m_uid, -1f }); FieldSpawnItem.SetValue(__instance, null); ZNetScene.instance.Destroy(((Component)__instance).gameObject); } } } [HarmonyPatch(typeof(Player), "AutoPickup")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> AutoPickupTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown FieldInfo autoPickupField = AccessTools.DeclaredField(typeof(ItemDrop), "m_autoPickup"); MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(SpearPatches), "ShouldAutoPickup", (Type[])null, (Type[])null); return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction instruction) => CodeInstructionExtensions.LoadsField(instruction, autoPickupField, false)), (string)null) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Dup, (object)null) }).Advance(1) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldarg_0, (object)null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Call, (object)methodInfo) }) .Instructions(); } public static bool ShouldAutoPickup(ItemDrop itemDrop, bool isAutoPickupable, Player player) { if (!Object.op_Implicit((Object)(object)itemDrop) || itemDrop.m_itemData == null) { return isAutoPickupable; } if (!isAutoPickupable) { return isAutoPickupable; } if (IsPotentiallyThrowable(itemDrop.m_itemData)) { ZNetView nview = GetNview(itemDrop); if ((Object)(object)nview != (Object)null) { return nview.IsOwner(); } return true; } return !BlockAutoPickupDueToReservedWeight(itemDrop, player); } private static bool BlockAutoPickupDueToReservedWeight(ItemDrop itemDrop, Player player) { if (!PluginConfig.ReserveThrowWeight.Value || PluginConfig.MaxReservationSeconds.Value < 0f) { return false; } float num = 0f; PlayerWeightReserverTrackerComponent playerWeightReserverTrackerComponent = default(PlayerWeightReserverTrackerComponent); if (((Component)player).TryGetComponent<PlayerWeightReserverTrackerComponent>(ref playerWeightReserverTrackerComponent)) { for (int num2 = playerWeightReserverTrackerComponent.WeightReservers.Count - 1; num2 >= 0; num2--) { WeightReserverComponent weightReserverComponent = playerWeightReserverTrackerComponent.WeightReservers[num2]; if ((Object)(object)weightReserverComponent != (Object)null && weightReserverComponent.AttachedItemData != null) { num += weightReserverComponent.AttachedItemData.GetWeight(-1); } else { playerWeightReserverTrackerComponent.WeightReservers.RemoveAt(num2); } } } if (num <= 0f) { return false; } Inventory inventory = GetInventory((Humanoid)(object)player); return itemDrop.m_itemData.GetWeight(-1) + inventory.GetTotalWeight() > player.GetMaxCarryWeight() - num; } [HarmonyPatch(typeof(Player), "TeleportTo")] [HarmonyPostfix] private static void TeleportTo_Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } Projectile[] array = Object.FindObjectsByType<Projectile>((FindObjectsSortMode)0); foreach (Projectile val in array) { Character projectileOwner = GetProjectileOwner(val); Player val2 = (Player)(object)((projectileOwner is Player) ? projectileOwner : null); if (val2 != null && (Object)(object)val2 == (Object)(object)__instance) { LoyaltyComponent component = ((Component)val).GetComponent<LoyaltyComponent>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } WeightReserverComponent component2 = ((Component)val).GetComponent<WeightReserverComponent>(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } } } LoyaltyComponent[] array2 = Object.FindObjectsByType<LoyaltyComponent>((FindObjectsSortMode)0); foreach (LoyaltyComponent loyaltyComponent in array2) { if ((Object)(object)loyaltyComponent.OriginalOwner == (Object)(object)__instance) { Object.Destroy((Object)(object)loyaltyComponent); } } } [HarmonyPatch(typeof(Player), "OnDestroy")] [HarmonyPostfix] private static void OnDestroy_Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } LoyaltyComponent[] array = Object.FindObjectsByType<LoyaltyComponent>((FindObjectsSortMode)0); foreach (LoyaltyComponent loyaltyComponent in array) { if ((Object)(object)loyaltyComponent.OriginalOwner == (Object)(object)__instance) { Object.Destroy((Object)(object)loyaltyComponent); } } WeightReserverComponent[] array2 = Object.FindObjectsByType<WeightReserverComponent>((FindObjectsSortMode)0); foreach (WeightReserverComponent weightReserverComponent in array2) { if ((Object)(object)weightReserverComponent.OriginalOwner == (Object)(object)__instance) { Object.Destroy((Object)(object)weightReserverComponent); } } } } internal class WeightReserverComponent : MonoBehaviour { protected ItemData _attachedItemData; protected Player _originalOwner; public ItemData AttachedItemData => _attachedItemData; public Player OriginalOwner => _originalOwner; public void Setup(ItemData attachedItemData, Player originalOwner) { if ((Object)(object)originalOwner == (Object)null) { Object.Destroy((Object)(object)this); return; } _attachedItemData = attachedItemData; _originalOwner = originalOwner; PlayerWeightReserverTrackerComponent playerWeightReserverTrackerComponent = default(PlayerWeightReserverTrackerComponent); if (!((Component)originalOwner).TryGetComponent<PlayerWeightReserverTrackerComponent>(ref playerWeightReserverTrackerComponent)) { playerWeightReserverTrackerComponent = ((Component)originalOwner).gameObject.AddComponent<PlayerWeightReserverTrackerComponent>(); } playerWeightReserverTrackerComponent.WeightReservers.Add(this); StartTimer(); } protected virtual void StartTimer() { ((MonoBehaviour)this).StartCoroutine("UnreserveInABit"); } public virtual void StopTimer() { ((MonoBehaviour)this).StopCoroutine("UnreserveInABit"); } private IEnumerator UnreserveInABit() { float value = PluginConfig.MaxReservationSeconds.Value; if (value > 0f) { yield return (object)new WaitForSeconds(value); } Object.Destroy((Object)(object)this); } public void OnDestroy() { PlayerWeightReserverTrackerComponent playerWeightReserverTrackerComponent = default(PlayerWeightReserverTrackerComponent); if (((Component)_originalOwner).TryGetComponent<PlayerWeightReserverTrackerComponent>(ref playerWeightReserverTrackerComponent)) { playerWeightReserverTrackerComponent.WeightReservers.Remove(this); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "LoyalSpears"; public const string PLUGIN_NAME = "LoyalSpears"; public const string PLUGIN_VERSION = "1.0.0"; } }