Decompiled source of TowerShieldRebalance v1.0.0

TowerShieldRebalance.dll

Decompiled 4 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TowerShieldRebalance")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TowerShieldRebalance")]
[assembly: AssemblyTitle("TowerShieldRebalance")]
[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 TowerShieldRebalance
{
	internal sealed class ConfigurationManagerAttributes
	{
		public Action<ConfigEntryBase> CustomDrawer;
	}
	[BepInPlugin("org.bepinex.plugins.towershieldrebalance", "Tower Shield Rebalance", "1.0.0")]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	public class TowerShieldRebalancePlugin : BaseUnityPlugin
	{
		private class OriginalData
		{
			public float BlockPower;

			public float BlockPowerPerLevel;

			public float MovementModifier;

			public List<DamageModPair> Modifiers;
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDBAwakePatch
		{
			private static void Postfix()
			{
				ApplyAll();
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class ObjectDBCopyPatch
		{
			private static void Postfix()
			{
				ApplyAll();
			}
		}

		[HarmonyPatch(typeof(Humanoid))]
		[HarmonyPatch("BlockAttack")]
		private static class BlockPatch
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				//IL_0077: Unknown result type (might be due to invalid IL or missing references)
				//IL_007e: Expected O, but got Unknown
				//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ad: Expected O, but got Unknown
				//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c2: Expected O, but got Unknown
				//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d7: Expected O, but got Unknown
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				try
				{
					int num = list.FindLastIndex((CodeInstruction p) => p.opcode == OpCodes.Callvirt && p.operand != null && p.operand.ToString().Contains("Damage(HitData)"));
					if (num > 0)
					{
						MethodInfo methodInfo = AccessTools.Method(typeof(TowerShieldRebalancePlugin), "OnBlockHit", (Type[])null, (Type[])null);
						List<Label> labels = list[num].labels;
						list[num].labels = new List<Label>();
						CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
						val.labels = labels;
						list.RemoveAt(num);
						list.Insert(num, val);
						list.Insert(num + 1, new CodeInstruction(OpCodes.Ldloc_0, (object)null));
						list.Insert(num + 2, new CodeInstruction(OpCodes.Ldarg_1, (object)null));
						list.Insert(num + 3, new CodeInstruction(OpCodes.Call, (object)methodInfo));
						Debug.Log((object)"[TowerShieldRebalance] Patch successful!");
						return list;
					}
					Debug.Log((object)("[TowerShieldRebalance] Patching failed. damageCallIndex: " + num));
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("[TowerShieldRebalance] " + ex.Message));
				}
				return instructions;
			}
		}

		public const string PluginGuid = "org.bepinex.plugins.towershieldrebalance";

		public const string PluginName = "Tower Shield Rebalance";

		public const string PluginVersion = "1.0.0";

		private const float GoldSpeedPenalty = 0.2f;

		private static readonly string[] TowerShields = new string[7] { "ShieldWoodTower", "ShieldBoneTower", "ShieldIronTower", "ShieldSerpentscale", "ShieldBlackmetalTower", "ShieldFlametalTower", "ShieldGoldTower" };

		internal static ConfigEntry<float> BaseBlockScale;

		internal static ConfigEntry<float> UpgradeBlockScale;

		internal static ConfigEntry<bool> DisableKnockback;

		internal static ConfigEntry<bool> EnableThorns;

		internal static ConfigEntry<float> MaxThornsPercent;

		internal static ConfigEntry<float> MinThornsPercent;

		internal static ConfigEntry<float> SkillCurvePower;

		internal static ConfigEntry<bool> EnableShieldBonuses;

		private static readonly Dictionary<string, OriginalData> Originals = new Dictionary<string, OriginalData>();

		private const DamageType Blunt = (DamageType)1;

		private const DamageType Slash = (DamageType)2;

		private const DamageType Pierce = (DamageType)4;

		private const DamageType Fire = (DamageType)32;

		private const DamageType Frost = (DamageType)64;

		private const DamageType Lightning = (DamageType)128;

		private const DamageType Poison = (DamageType)256;

		private const DamageModifier SlightRes = (DamageModifier)7;

		private const DamageModifier Res = (DamageModifier)1;

		private const DamageModifier VeryRes = (DamageModifier)5;

		private const DamageModifier SlightWeak = (DamageModifier)8;

		private const DamageModifier Weak = (DamageModifier)2;

		private static readonly Dictionary<string, DamageModPair[]> Bonuses = new Dictionary<string, DamageModPair[]>
		{
			["ShieldWoodTower"] = (DamageModPair[])(object)new DamageModPair[3]
			{
				M((DamageType)32, (DamageModifier)8),
				M((DamageType)4, (DamageModifier)7),
				M((DamageType)1, (DamageModifier)7)
			},
			["ShieldBoneTower"] = (DamageModPair[])(object)new DamageModPair[3]
			{
				M((DamageType)1, (DamageModifier)8),
				M((DamageType)4, (DamageModifier)1),
				M((DamageType)2, (DamageModifier)7)
			},
			["ShieldIronTower"] = (DamageModPair[])(object)new DamageModPair[3]
			{
				M((DamageType)4, (DamageModifier)7),
				M((DamageType)2, (DamageModifier)7),
				M((DamageType)64, (DamageModifier)8)
			},
			["ShieldSerpentscale"] = (DamageModPair[])(object)new DamageModPair[3]
			{
				M((DamageType)2, (DamageModifier)7),
				M((DamageType)256, (DamageModifier)1),
				M((DamageType)64, (DamageModifier)2)
			},
			["ShieldBlackmetalTower"] = (DamageModPair[])(object)new DamageModPair[6]
			{
				M((DamageType)4, (DamageModifier)1),
				M((DamageType)2, (DamageModifier)7),
				M((DamageType)1, (DamageModifier)7),
				M((DamageType)256, (DamageModifier)8),
				M((DamageType)32, (DamageModifier)8),
				M((DamageType)128, (DamageModifier)8)
			},
			["ShieldFlametalTower"] = (DamageModPair[])(object)new DamageModPair[6]
			{
				M((DamageType)4, (DamageModifier)1),
				M((DamageType)2, (DamageModifier)7),
				M((DamageType)1, (DamageModifier)7),
				M((DamageType)32, (DamageModifier)7),
				M((DamageType)128, (DamageModifier)2),
				M((DamageType)64, (DamageModifier)2)
			},
			["ShieldGoldTower"] = (DamageModPair[])(object)new DamageModPair[7]
			{
				M((DamageType)4, (DamageModifier)5),
				M((DamageType)1, (DamageModifier)1),
				M((DamageType)2, (DamageModifier)1),
				M((DamageType)256, (DamageModifier)2),
				M((DamageType)64, (DamageModifier)2),
				M((DamageType)32, (DamageModifier)2),
				M((DamageType)128, (DamageModifier)2)
			}
		};

		private readonly Harmony _harmony = new Harmony("org.bepinex.plugins.towershieldrebalance");

		private static float R1(float v)
		{
			return Mathf.Round(v * 10f) / 10f;
		}

		private static Action<ConfigEntryBase> Drawer(float min, float max)
		{
			return delegate(ConfigEntryBase entry)
			{
				ConfigEntry<float> val = (ConfigEntry<float>)(object)entry;
				float num = R1(val.Value);
				GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
				float v = GUILayout.HorizontalSlider(num, min, max, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
				GUILayout.Label(num.ToString("F1", CultureInfo.InvariantCulture), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) });
				GUILayout.EndHorizontal();
				v = Mathf.Clamp(R1(v), min, max);
				if (Mathf.Abs(v - val.Value) > 0.001f)
				{
					val.Value = v;
				}
			};
		}

		private static ConfigEntry<float> BindFloat(ConfigFile cfg, string section, string key, float defaultValue, float min, float max, string description)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return cfg.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					CustomDrawer = Drawer(min, max)
				}
			}));
		}

		private static DamageModPair M(DamageType type, DamageModifier mod)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			return new DamageModPair
			{
				m_type = type,
				m_modifier = mod
			};
		}

		private void Awake()
		{
			BaseBlockScale = BindFloat(((BaseUnityPlugin)this).Config, "1. Block", "BaseBlockScale", 20f, 0f, 100f, "BBS. Percentage increase of the base block armor of tower shields (20 = +20%). Formula: original block armor x (1 + BBS / 100).");
			UpgradeBlockScale = BindFloat(((BaseUnityPlugin)this).Config, "1. Block", "UpgradeBlockScale", 4f, 0f, 20f, "UBS. Value added to the block armor gained per upgrade level of tower shields (original value + UBS).");
			DisableKnockback = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Knockback", "DisableKnockback", false, "Disable enemy knockback when blocking (tower shields only).");
			EnableThorns = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Thorns", "EnableThorns", true, "Deal return damage to the attacker when blocking with a tower shield.");
			MaxThornsPercent = BindFloat(((BaseUnityPlugin)this).Config, "3. Thorns", "MaxThornsPercent", 30f, 0f, 100f, "Maximum return damage, % of the shield's block armor (including the Blocking skill).");
			MinThornsPercent = BindFloat(((BaseUnityPlugin)this).Config, "3. Thorns", "MinThornsPercent", 10f, 0f, 100f, "Minimum return damage, % of the shield's block armor (including the Blocking skill).");
			SkillCurvePower = BindFloat(((BaseUnityPlugin)this).Config, "3. Thorns", "SkillCurvePower", 2f, 1f, 5f, "Blocking skill curve power. Higher values make return damage grow faster at low skill levels and slower close to level 100 (1 = linear).");
			EnableShieldBonuses = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Shield bonuses", "EnableShieldBonuses", true, "Enable the changed resistances and weaknesses of tower shields (the Gold Tower Shield also gets a 20% movement speed penalty).");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				ApplyAll();
			};
			_harmony.PatchAll();
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
		}

		internal static void ApplyAll()
		{
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ObjectDB.instance == (Object)null || ObjectDB.instance.m_items == null || ObjectDB.instance.m_items.Count == 0)
			{
				return;
			}
			string[] towerShields = TowerShields;
			foreach (string text in towerShields)
			{
				try
				{
					GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text);
					if ((Object)(object)itemPrefab == (Object)null)
					{
						continue;
					}
					ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
					if ((Object)(object)component == (Object)null)
					{
						continue;
					}
					SharedData shared = component.m_itemData.m_shared;
					if (!Originals.TryGetValue(text, out var value))
					{
						value = new OriginalData
						{
							BlockPower = shared.m_blockPower,
							BlockPowerPerLevel = shared.m_blockPowerPerLevel,
							MovementModifier = shared.m_movementModifier,
							Modifiers = ((shared.m_damageModifiers != null) ? new List<DamageModPair>(shared.m_damageModifiers) : new List<DamageModPair>())
						};
						Originals[text] = value;
					}
					shared.m_blockPower = value.BlockPower * (1f + R1(BaseBlockScale.Value) / 100f);
					shared.m_blockPowerPerLevel = value.BlockPowerPerLevel + R1(UpgradeBlockScale.Value);
					shared.m_movementModifier = value.MovementModifier;
					List<DamageModPair> list = new List<DamageModPair>(value.Modifiers);
					if (EnableShieldBonuses.Value && Bonuses.TryGetValue(text, out var value2))
					{
						DamageModPair[] array = value2;
						for (int j = 0; j < array.Length; j++)
						{
							DamageModPair pair = array[j];
							list.RemoveAll((DamageModPair m) => m.m_type == pair.m_type);
							list.Add(pair);
						}
						if (text == "ShieldGoldTower")
						{
							shared.m_movementModifier = value.MovementModifier - 0.2f;
						}
					}
					shared.m_damageModifiers = list;
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("[TowerShieldRebalance] Error while processing " + text + ": " + ex));
				}
			}
		}

		private static bool IsTowerShield(ItemData item)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return false;
			}
			return Array.IndexOf(TowerShields, ((Object)item.m_dropPrefab).name) >= 0;
		}

		public static void OnBlockHit(Character attacker, HitData pushHit, Humanoid blocker, ItemData currentBlocker, HitData originalHit)
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)attacker == (Object)null || pushHit == null)
			{
				return;
			}
			if (IsTowerShield(currentBlocker))
			{
				if (DisableKnockback.Value)
				{
					pushHit.m_pushForce = 0f;
				}
				if (EnableThorns.Value && (Object)(object)blocker != (Object)null)
				{
					float num = Mathf.Clamp01(((Character)blocker).GetSkillFactor((SkillType)6));
					float blockPower = currentBlocker.GetBlockPower(num);
					float num2 = Mathf.Clamp01(R1(MaxThornsPercent.Value) / 100f);
					float num3 = Mathf.Min(Mathf.Clamp01(R1(MinThornsPercent.Value) / 100f), num2);
					float num4 = 1f - Mathf.Pow(1f - num, R1(SkillCurvePower.Value));
					float num5 = Mathf.Clamp(num3 + (num2 - num3) * num4, num3, num2);
					float num6 = blockPower * num5;
					if (num6 > 0f)
					{
						pushHit.m_damage = default(DamageTypes);
						pushHit.m_damage.m_pierce = num6;
						if ((Object)(object)DamageText.instance != (Object)null && originalHit != null)
						{
							DamageText.instance.ShowText((TextType)0, originalHit.m_point + Vector3.up * 0.5f, num6, false);
						}
					}
				}
			}
			attacker.Damage(pushHit);
		}
	}
}