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 BowSecondaryMultishot v1.5.2
BowSecondaryMultishot.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("BowSecondaryMultishot")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.5.0.0")] [assembly: AssemblyInformationalVersion("1.5.0")] [assembly: AssemblyProduct("BowSecondaryMultishot")] [assembly: AssemblyTitle("BowSecondaryMultishot")] [assembly: AssemblyVersion("1.5.0.0")] [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 BowSecondaryMultishot { [BepInPlugin("com.example.bowsecondarymultishot", "BowSecondaryMultishot", "1.5.0")] public class BowSecondaryMultishotPlugin : BaseUnityPlugin { internal class ArrowType { public ItemData Item; public int Count; public int Fire; public bool Fired; } public const string PluginGuid = "com.example.bowsecondarymultishot"; public const string PluginName = "BowSecondaryMultishot"; public const string PluginVersion = "1.5.0"; internal static ConfigEntry<int> LevelsPerExtraArrow; internal static ConfigEntry<int> MaxArrows; internal static ConfigEntry<float> Spread; internal static ConfigEntry<float> ArrowSpeedMultiplier; internal static ConfigEntry<float> DamageMultiplier; internal static ConfigEntry<bool> FireAllArrowTypes; private void Awake() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) LevelsPerExtraArrow = ((BaseUnityPlugin)this).Config.Bind<int>("Multishot", "LevelsPerExtraArrow", 25, new ConfigDescription("Every this many bow skill levels adds one more arrow. The multishot always fires at least 2 arrows (1 extra). With 25: skill 0-24 = 2 arrows, 25-49 = 3, 50-74 = 4, 75-99 = 5, 100 = 6.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>())); MaxArrows = ((BaseUnityPlugin)this).Config.Bind<int>("Multishot", "MaxArrows", 20, new ConfigDescription("Hard cap on arrows per shot.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>())); Spread = ((BaseUnityPlugin)this).Config.Bind<float>("Multishot", "Spread", 1.5f, new ConfigDescription("Spread in degrees added to the bow's normal accuracy so the arrows fan out. 0 = as tight as a normal shot.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); ArrowSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Multishot", "ArrowSpeedMultiplier", 0.5f, new ConfigDescription("Arrow speed of the secondary attack relative to a normal shot (1 = same speed, 0.5 = half). Slower arrows drop faster, so the volley is mainly useful at close range.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>())); DamageMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Multishot", "DamageMultiplier", 0.8f, new ConfigDescription("Damage of each multishot arrow relative to a normal shot (1 = same damage, 0.8 = 80%). Scales the total damage, so every damage type of the bow and arrow is reduced equally.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); FireAllArrowTypes = ((BaseUnityPlugin)this).Config.Bind<bool>("Multishot", "FireAllArrowTypes", true, "When on, the multishot fires at least one arrow of every arrow type in your inventory (for example normal, fire, frost and poison arrows together), starting with the arrow type you have equipped. The number of arrows per volley does not change: when it is smaller than the number of arrow types you carry, only the first types are used, and when it is bigger, the extra arrows are all of the equipped type. Every arrow is taken from its own stack."); new Harmony("com.example.bowsecondarymultishot").PatchAll(); } internal static int ArrowCount(Player player) { int num = Mathf.FloorToInt(((Character)player).GetSkillLevel((SkillType)8)); return Mathf.Clamp(2 + num / LevelsPerExtraArrow.Value, 1, MaxArrows.Value); } internal static bool IsBow(SharedData shared) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if (shared != null && (int)shared.m_itemType == 4) { return (int)shared.m_skillType == 8; } return false; } internal static bool IsAmmoFor(ItemData item, SharedData bow) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 if (item != null && item.m_shared != null && (int)item.m_shared.m_itemType != 4) { return item.m_shared.m_ammoType == bow.m_ammoType; } return false; } internal static int CountAmmo(Player player, SharedData bow) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null || bow == null || string.IsNullOrEmpty(bow.m_ammoType)) { return 0; } int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if (IsAmmoFor(allItem, bow)) { num += allItem.m_stack; } } return num; } internal static bool SameArrowType(ItemData a, ItemData b) { if (a != null && b != null && a.m_shared != null && b.m_shared != null && a.m_shared.m_name == b.m_shared.m_name) { return a.m_quality == b.m_quality; } return false; } internal static List<ArrowType> PlanArrowTypes(Player player, SharedData bow, ItemData equipped, int takenByGame, int shots) { List<ArrowType> list = new List<ArrowType>(); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null || equipped == null) { return list; } list.Add(new ArrowType { Item = equipped, Count = takenByGame }); foreach (ItemData allItem in inventory.GetAllItems()) { if (!IsAmmoFor(allItem, bow) || allItem.m_stack <= 0) { continue; } ArrowType arrowType = null; foreach (ArrowType item in list) { if (SameArrowType(item.Item, allItem)) { arrowType = item; break; } } if (arrowType == null) { arrowType = new ArrowType { Item = allItem }; list.Add(arrowType); } arrowType.Count += allItem.m_stack; } int num = 0; foreach (ArrowType item2 in list) { if (num >= shots) { break; } if (item2.Count > 0) { item2.Fire = 1; num++; } } foreach (ArrowType item3 in list) { if (num >= shots) { break; } int num2 = Math.Min(shots - num, item3.Count - item3.Fire); if (num2 > 0) { item3.Fire += num2; num += num2; } } return list; } internal static int RemoveAmmo(Player player, SharedData bow, ItemData preferred, int amount, int reserveOnPreferred, ItemData sameTypeAs = null) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null || bow == null || amount <= 0) { return 0; } int num = 0; if (preferred != null && inventory.ContainsItem(preferred)) { int num2 = Math.Min(amount, Math.Max(0, preferred.m_stack - reserveOnPreferred)); if (num2 > 0) { inventory.RemoveItem(preferred, num2); num += num2; } } if (num < amount) { foreach (ItemData item in new List<ItemData>(inventory.GetAllItems())) { if (num >= amount) { break; } if (item != preferred && IsAmmoFor(item, bow) && (sameTypeAs == null || SameArrowType(item, sameTypeAs))) { int num3 = Math.Min(amount - num, item.m_stack); inventory.RemoveItem(item, num3); num += num3; } } } return num; } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class InputRedirectPatch { private static bool _currentDrawIsMultishot; private static bool _wasHolding; internal static bool PendingBurstIsMultishot; internal static int AmmoCountAtDrawStart = -1; private static void Prefix(Player __instance, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } ItemData currentWeapon = ((Humanoid)__instance).GetCurrentWeapon(); if (currentWeapon == null || !BowSecondaryMultishotPlugin.IsBow(currentWeapon.m_shared)) { _wasHolding = false; _currentDrawIsMultishot = false; PendingBurstIsMultishot = false; return; } if (secondaryAttack && !attack && !attackHold) { _currentDrawIsMultishot = true; AmmoCountAtDrawStart = BowSecondaryMultishotPlugin.CountAmmo(__instance, currentWeapon.m_shared); } else if (attack && !secondaryAttackHold) { _currentDrawIsMultishot = false; } attack |= secondaryAttack; attackHold |= secondaryAttackHold; secondaryAttack = false; secondaryAttackHold = false; if (_wasHolding && !attackHold) { PendingBurstIsMultishot = _currentDrawIsMultishot; } _wasHolding = attackHold; } } [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] internal static class MultishotPatch { internal class BurstState { public int Shots; public int ReserveOnPreferred; public ItemData Ammo; public SharedData Bow; public Player Player; public List<BowSecondaryMultishotPlugin.ArrowType> Types; } private static MethodInfo _fireBurst; private static void Prefix(Attack __instance, out BurstState __state) { __state = null; Traverse val = Traverse.Create((object)__instance); Humanoid value = val.Field("m_character").GetValue<Humanoid>(); Player val2 = (Player)(object)((value is Player) ? value : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2 != (Object)(object)Player.m_localPlayer || !InputRedirectPatch.PendingBurstIsMultishot) { return; } InputRedirectPatch.PendingBurstIsMultishot = false; ItemData value2 = val.Field("m_weapon").GetValue<ItemData>(); if (value2 == null || !BowSecondaryMultishotPlugin.IsBow(value2.m_shared)) { return; } ItemData value3 = val.Field("m_ammoItem").GetValue<ItemData>(); int num = BowSecondaryMultishotPlugin.CountAmmo(val2, value2.m_shared); int ammoCountAtDrawStart = InputRedirectPatch.AmmoCountAtDrawStart; int num2 = ((ammoCountAtDrawStart >= 0) ? Mathf.Clamp(ammoCountAtDrawStart - num, 0, 1) : 0); int num3 = num + num2; int num4 = BowSecondaryMultishotPlugin.ArrowCount(val2); int num5 = Mathf.Max(1, Mathf.Min(num4, num3)); Attack attack = value2.m_shared.m_attack; float value4 = BowSecondaryMultishotPlugin.Spread.Value; __instance.m_projectiles = num5; __instance.m_projectileAccuracy = attack.m_projectileAccuracy + value4; __instance.m_projectileAccuracyMin = attack.m_projectileAccuracyMin + value4; float num6 = Mathf.Round(BowSecondaryMultishotPlugin.DamageMultiplier.Value * 100f) / 100f; __instance.m_damageMultiplier = attack.m_damageMultiplier * num6; float value5 = BowSecondaryMultishotPlugin.ArrowSpeedMultiplier.Value; string[] array = new string[2] { "m_projectileVel", "m_projectileVelMin" }; foreach (string text in array) { FieldInfo fieldInfo = AccessTools.Field(typeof(Attack), text); if (fieldInfo != null && fieldInfo.FieldType == typeof(float)) { fieldInfo.SetValue(__instance, (float)fieldInfo.GetValue(attack) * value5); } } List<BowSecondaryMultishotPlugin.ArrowType> list = null; if (BowSecondaryMultishotPlugin.FireAllArrowTypes.Value && value3 != null) { list = BowSecondaryMultishotPlugin.PlanArrowTypes(val2, value2.m_shared, value3, num2, num5); __instance.m_projectiles = list[0].Fire; } __state = new BurstState { Shots = num5, Types = list, ReserveOnPreferred = ((num2 == 0) ? 1 : 0), Ammo = value3, Bow = value2.m_shared, Player = val2 }; } private static void Postfix(Attack __instance, BurstState __state, object[] __args) { if (__state == null) { return; } if (__state.Types != null) { FireOtherArrowTypes(__instance, __state, __args); return; } int num = __state.Shots - 1; if (num > 0) { BowSecondaryMultishotPlugin.RemoveAmmo(__state.Player, __state.Bow, __state.Ammo, num, __state.ReserveOnPreferred); } } private static void FireOtherArrowTypes(Attack attack, BurstState state, object[] args) { List<BowSecondaryMultishotPlugin.ArrowType> types = state.Types; types[0].Fired = true; if (types.Count > 1) { if (_fireBurst == null) { _fireBurst = AccessTools.Method(typeof(Attack), "FireProjectileBurst", (Type[])null, (Type[])null); } Traverse val = Traverse.Create((object)attack).Field("m_ammoItem"); int projectiles = attack.m_projectiles; try { for (int i = 1; i < types.Count; i++) { if (types[i].Fire > 0) { try { val.SetValue((object)types[i].Item); attack.m_projectiles = types[i].Fire; _fireBurst.Invoke(attack, args); types[i].Fired = true; } catch (Exception ex) { Debug.LogWarning((object)("[BowSecondaryMultishot] Could not fire arrow type " + types[i].Item.m_shared.m_name + ": " + (ex.InnerException ?? ex).Message)); } } } } finally { val.SetValue((object)state.Ammo); attack.m_projectiles = projectiles; } } for (int j = 0; j < types.Count; j++) { BowSecondaryMultishotPlugin.ArrowType arrowType = types[j]; if (!arrowType.Fired || arrowType.Fire <= 0) { continue; } if (j == 0) { if (arrowType.Fire > 1) { BowSecondaryMultishotPlugin.RemoveAmmo(state.Player, state.Bow, state.Ammo, arrowType.Fire - 1, state.ReserveOnPreferred, state.Ammo); } } else { BowSecondaryMultishotPlugin.RemoveAmmo(state.Player, state.Bow, arrowType.Item, arrowType.Fire, 0, arrowType.Item); } } } } }