Decompiled source of GrizzlyBulwark v0.1.0

BepInEx/plugins/GrizzlyBulwark/GrizzlyBulwark.dll

Decompiled 3 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GrizzlyBulwark.Configuration;
using GrizzlyBulwark.Core;
using GrizzlyBulwark.Interface;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
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: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: AssemblyCompany("GrizzlyBulwark")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+b539c7c6e7ce2d071f9c9aa17df23c502b74f1aa")]
[assembly: AssemblyProduct("GrizzlyBulwark")]
[assembly: AssemblyTitle("GrizzlyBulwark")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.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 GrizzlyBulwark
{
	[BepInPlugin("com.grizzlyendeavors.bulwark", "GrizzlyBulwark", "0.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	public class BulwarkPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.grizzlyendeavors.bulwark";

		public const string Name = "GrizzlyBulwark";

		public const string Version = "0.1.0";

		private Harmony harmony;

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			Log.Bind(((BaseUnityPlugin)this).Logger);
			ModSettings.Bind(((BaseUnityPlugin)this).Config);
			Strings.Register();
			harmony = new Harmony("com.grizzlyendeavors.bulwark");
			harmony.PatchAll();
			int num = harmony.GetPatchedMethods().Count();
			Log.Info(string.Format("{0} {1} ready, {2} game methods patched.", "GrizzlyBulwark", "0.1.0", num));
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
}
namespace GrizzlyBulwark.Interface
{
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class SetTooltip
	{
		[HarmonyPostfix]
		private static void Postfix(ItemData item, ref string __result)
		{
			try
			{
				TankSet tankSet = TankSets.Of(item);
				if (ModSettings.IsActive(tankSet))
				{
					StringBuilder stringBuilder = new StringBuilder(__result);
					stringBuilder.AppendFormat("\n\n{0} (<color=orange>3</color> {1}): <color=orange>{2}</color>", "$grizzlybulwark_seteffect", "$grizzlybulwark_parts", tankSet.DisplayName);
					stringBuilder.AppendFormat("\n{0}: <color=orange>-{1:0}%</color>", "$grizzlybulwark_resist", ModSettings.ResistanceOf(tankSet) * 100f);
					float num = ModSettings.StaggerResistanceOf(tankSet);
					if (num > 0f)
					{
						stringBuilder.AppendFormat("\n{0}: <color=orange>-{1:0}%</color>", "$grizzlybulwark_stagger", num * 100f);
					}
					if (ModSettings.AggroPull.Value)
					{
						stringBuilder.AppendFormat("\n{0}", "$grizzlybulwark_aggro");
					}
					__result = stringBuilder.ToString();
				}
			}
			catch (Exception error)
			{
				Log.Failure("describing the heavy armour set bonus", error);
			}
		}
	}
	internal static class Strings
	{
		public const string SetEffect = "$grizzlybulwark_seteffect";

		public const string Parts = "$grizzlybulwark_parts";

		public const string Resist = "$grizzlybulwark_resist";

		public const string Stagger = "$grizzlybulwark_stagger";

		public const string Aggro = "$grizzlybulwark_aggro";

		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{
					"$grizzlybulwark_seteffect".TrimStart(new char[1] { '$' }),
					"Bulwark"
				},
				{
					"$grizzlybulwark_parts".TrimStart(new char[1] { '$' }),
					"parts"
				},
				{
					"$grizzlybulwark_resist".TrimStart(new char[1] { '$' }),
					"Blunt, slash and pierce taken"
				},
				{
					"$grizzlybulwark_stagger".TrimStart(new char[1] { '$' }),
					"Stagger buildup"
				},
				{
					"$grizzlybulwark_aggro".TrimStart(new char[1] { '$' }),
					"Draws enemies off nearby allies"
				}
			});
		}
	}
}
namespace GrizzlyBulwark.Features.Stagger
{
	[HarmonyPatch(typeof(Character), "AddStaggerDamage")]
	internal static class StaggerResistance
	{
		[HarmonyPrefix]
		private static void Prefix(Character __instance, ref float damage)
		{
			try
			{
				if (damage <= 0f)
				{
					return;
				}
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)((Character)val).m_nview == (Object)null) && ((Character)val).m_nview.IsOwner())
				{
					float num = ModSettings.StaggerResistanceOf(TankSets.Worn(val));
					if (!(num <= 0f))
					{
						damage *= 1f - num;
					}
				}
			}
			catch (Exception error)
			{
				Log.Failure("applying heavy armour stagger resistance", error);
			}
		}
	}
}
namespace GrizzlyBulwark.Features.Resistance
{
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class PhysicalResistance
	{
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(Character __instance, HitData hit)
		{
			try
			{
				if (hit == null)
				{
					return;
				}
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)((Character)val).m_nview == (Object)null) && ((Character)val).m_nview.IsOwner())
				{
					float num = ModSettings.ResistanceOf(TankSets.Worn(val));
					if (!(num <= 0f))
					{
						float num2 = 1f - num;
						hit.m_damage.m_blunt *= num2;
						hit.m_damage.m_slash *= num2;
						hit.m_damage.m_pierce *= num2;
					}
				}
			}
			catch (Exception error)
			{
				Log.Failure("applying heavy armour resistance", error);
			}
		}
	}
}
namespace GrizzlyBulwark.Features.Aggro
{
	[HarmonyPatch(typeof(BaseAI), "FindEnemy")]
	internal static class AggroPull
	{
		[HarmonyPostfix]
		private static void Postfix(BaseAI __instance, ref Character __result)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!ModSettings.AggroPull.Value)
				{
					return;
				}
				Character obj = __result;
				Player val = (Player)(object)((obj is Player) ? obj : null);
				if (val == null)
				{
					return;
				}
				Character character = __instance.m_character;
				if ((Object)(object)character == (Object)null || character.IsTamed() || TankSets.Worn(val) != null)
				{
					return;
				}
				float value = ModSettings.AggroRadius.Value;
				if (value <= 0f)
				{
					return;
				}
				Vector3 position = ((Component)val).transform.position;
				Vector3 position2 = ((Component)__instance).transform.position;
				Player val2 = null;
				float num = float.MaxValue;
				foreach (Player allPlayer in Player.GetAllPlayers())
				{
					if (!((Object)(object)allPlayer == (Object)null) && !((Object)(object)allPlayer == (Object)(object)val) && !((Character)allPlayer).IsDead() && !((Character)allPlayer).m_aiSkipTarget && ModSettings.IsActive(TankSets.Worn(allPlayer)) && !(Vector3.Distance(((Component)allPlayer).transform.position, position) > value) && BaseAI.IsEnemy(character, (Character)(object)allPlayer) && __instance.CanSenseTarget((Character)(object)allPlayer))
					{
						float num2 = Vector3.Distance(((Component)allPlayer).transform.position, position2);
						if (num2 < num)
						{
							num = num2;
							val2 = allPlayer;
						}
					}
				}
				if ((Object)(object)val2 != (Object)null)
				{
					__result = (Character)(object)val2;
				}
			}
			catch (Exception error)
			{
				Log.Failure("pulling aggro onto heavy armour", error);
			}
		}
	}
}
namespace GrizzlyBulwark.Core
{
	internal static class Log
	{
		private static ManualLogSource sink;

		public static void Bind(ManualLogSource source)
		{
			sink = source;
		}

		public static void Info(string message)
		{
			ManualLogSource obj = sink;
			if (obj != null)
			{
				obj.LogInfo((object)message);
			}
		}

		public static void Warning(string message)
		{
			ManualLogSource obj = sink;
			if (obj != null)
			{
				obj.LogWarning((object)message);
			}
		}

		public static void Failure(string context, Exception error)
		{
			ManualLogSource obj = sink;
			if (obj != null)
			{
				obj.LogError((object)$"{context}: {error}");
			}
		}
	}
	internal sealed class TankSet
	{
		public string Id { get; }

		public string DisplayName { get; }

		public string Helmet { get; }

		public string Chest { get; }

		public string Legs { get; }

		public int Tier { get; internal set; }

		public TankSet(string id, string displayName, string helmet, string chest, string legs)
		{
			Id = id;
			DisplayName = displayName;
			Helmet = helmet;
			Chest = chest;
			Legs = legs;
		}
	}
	internal static class TankSets
	{
		private static readonly TankSet[] Sets;

		private static readonly Dictionary<string, TankSet> ByPiece;

		private static readonly Dictionary<int, TankSet> ByPieceHash;

		public static IReadOnlyList<TankSet> All => Sets;

		public static int TopTier => Sets.Length - 1;

		static TankSets()
		{
			Sets = new TankSet[7]
			{
				new TankSet("Bronze", "Bronze", "HelmetBronze", "ArmorBronzeChest", "ArmorBronzeLegs"),
				new TankSet("Iron", "Iron", "HelmetIron", "ArmorIronChest", "ArmorIronLegs"),
				new TankSet("Wolf", "Wolf", "HelmetDrake", "ArmorWolfChest", "ArmorWolfLegs"),
				new TankSet("Padded", "Padded", "HelmetPadded", "ArmorPaddedCuirass", "ArmorPaddedGreaves"),
				new TankSet("Carapace", "Carapace", "HelmetCarapace", "ArmorCarapaceChest", "ArmorCarapaceLegs"),
				new TankSet("Flametal", "Flametal", "HelmetFlametal", "ArmorFlametalChest", "ArmorFlametalLegs"),
				new TankSet("DeepNorthHeavy", "Deep North heavy", "HelmetDNHeavy", "ArmorDeepNorthHeavyChest", "ArmorDeepNorthHeavylegs")
			};
			ByPiece = new Dictionary<string, TankSet>(StringComparer.OrdinalIgnoreCase);
			ByPieceHash = new Dictionary<int, TankSet>();
			for (int i = 0; i < Sets.Length; i++)
			{
				TankSet tankSet = Sets[i];
				tankSet.Tier = i;
				ByPiece[tankSet.Helmet] = tankSet;
				ByPiece[tankSet.Chest] = tankSet;
				ByPiece[tankSet.Legs] = tankSet;
				ByPieceHash[StringExtensionMethods.GetStableHashCode(tankSet.Helmet)] = tankSet;
				ByPieceHash[StringExtensionMethods.GetStableHashCode(tankSet.Chest)] = tankSet;
				ByPieceHash[StringExtensionMethods.GetStableHashCode(tankSet.Legs)] = tankSet;
			}
		}

		public static TankSet Worn(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			if (((Humanoid)player).m_helmetItem == null)
			{
				return FromNetwork(player);
			}
			return FromItems(player);
		}

		private static TankSet FromItems(Player player)
		{
			TankSet tankSet = SetOf(((Humanoid)player).m_helmetItem);
			if (tankSet == null)
			{
				return null;
			}
			if (tankSet != SetOf(((Humanoid)player).m_chestItem) || tankSet != SetOf(((Humanoid)player).m_legItem))
			{
				return null;
			}
			return tankSet;
		}

		private static TankSet FromNetwork(Player player)
		{
			ZDO val = (((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid()) ? ((Character)player).m_nview.GetZDO() : null);
			if (val == null)
			{
				return null;
			}
			TankSet tankSet = SetOf(val.GetInt(ZDOVars.s_helmetItem, 0));
			if (tankSet == null)
			{
				return null;
			}
			if (tankSet != SetOf(val.GetInt(ZDOVars.s_chestItem, 0)) || tankSet != SetOf(val.GetInt(ZDOVars.s_legItem, 0)))
			{
				return null;
			}
			return tankSet;
		}

		private static TankSet SetOf(int prefabHash)
		{
			if (prefabHash == 0 || !ByPieceHash.TryGetValue(prefabHash, out var value))
			{
				return null;
			}
			return value;
		}

		public static TankSet Of(ItemData item)
		{
			return SetOf(item);
		}

		private static TankSet SetOf(ItemData item)
		{
			if ((Object)(object)item?.m_dropPrefab == (Object)null)
			{
				return null;
			}
			if (!ByPiece.TryGetValue(((Object)item.m_dropPrefab).name, out var value))
			{
				return null;
			}
			return value;
		}
	}
}
namespace GrizzlyBulwark.Configuration
{
	internal static class ModSettings
	{
		private const string Resistance = "Physical Resistance";

		private const string Stagger = "Stagger";

		private const string Aggro = "Aggro";

		private const float BaseResistance = 8f;

		private const float ResistancePerTier = 2f;

		private static readonly Dictionary<string, ConfigEntry<float>> PhysicalResistance = new Dictionary<string, ConfigEntry<float>>();

		public static ConfigEntry<float> StaggerResistance;

		public static ConfigEntry<bool> AggroPull;

		public static ConfigEntry<float> AggroRadius;

		public static void Bind(ConfigFile config)
		{
			foreach (TankSet item in TankSets.All)
			{
				float value = 8f + 2f * (float)item.Tier;
				PhysicalResistance[item.Id] = Synced(config, "Physical Resistance", item.Id, value, "Blunt, slash and pierce damage the " + item.DisplayName + " set turns away, as a percentage, when its helmet, chest and legs are all worn. Elemental damage is deliberately untouched. Note that the game applies resistance before armour and armour is quadratic in damage, so the reduction you actually feel is larger than the number here: 20 here lands around 36% against a hit big enough to matter. Set to 0 to drop this set from the mod entirely, stagger guard and aggro pull included.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f));
			}
			StaggerResistance = Synced(config, "Stagger", "StaggerResistance", 50f, "Stagger buildup the heaviest set shrugs off, as a percentage, scaled down for earlier sets in the same progression order as resistance. This is what lets a heavy set hold its ground and keep swinging instead of being knocked out of every animation. It changes nothing about damage taken.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 90f));
			AggroPull = Synced(config, "Aggro", "AggroPull", value: true, "Creatures picking a target prefer a player in a heavy set over the friend they would otherwise have gone for. It only redirects a target the creature had already settled on, so it never picks a fight that was not already happening.");
			AggroRadius = Synced(config, "Aggro", "AggroRadius", 15f, "How far in meters a heavy-set player can be from the creature's intended target and still pull it onto themselves. Keeps the pull to fights you are actually standing in.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f));
		}

		public static float ResistanceOf(TankSet set)
		{
			if (set == null || !PhysicalResistance.TryGetValue(set.Id, out var value))
			{
				return 0f;
			}
			return Clamp01(value.Value / 100f);
		}

		public static bool IsActive(TankSet set)
		{
			return ResistanceOf(set) > 0f;
		}

		public static float StaggerResistanceOf(TankSet set)
		{
			if (!IsActive(set))
			{
				return 0f;
			}
			return Clamp01(StaggerResistance.Value / 100f) * ((float)set.Tier + 1f) / ((float)TankSets.TopTier + 1f);
		}

		private static float Clamp01(float value)
		{
			if (value < 0f)
			{
				return 0f;
			}
			if (!(value > 1f))
			{
				return value;
			}
			return 1f;
		}

		private static ConfigEntry<T> Synced<T>(ConfigFile config, string section, string key, T value, string help, AcceptableValueBase range = null)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			return config.Bind<T>(section, key, value, new ConfigDescription(help, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}