Decompiled source of Scroll Template v1.0.0

Mods/ScrollTemplate.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HarmonyLib;
using MelonLoader;
using Microsoft.CodeAnalysis;
using ScrollTemplate;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Class1), "Scroll Template", "1.0", "TheMrEvil", null)]
[assembly: MelonGame("Alvios", "Vellum")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ScrollTemplate")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+eb8e544c71b2711defd8bd6dffb5f66af412ac3f")]
[assembly: AssemblyProduct("ScrollTemplate")]
[assembly: AssemblyTitle("ScrollTemplate")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace ScrollTemplate
{
	public class Class1 : MelonMod
	{
		internal const string AugmentName = "Scroll of Ferocity";

		internal const string AugmentDetail = "Double your damage (+100% All Damage).";

		internal static bool HasDamageScroll;

		internal static object CachedAugmentTree;

		private static bool _augmentRegistered;

		private static float _nextAttemptTime;

		private static int _attempts;

		private static bool _templateFallbackUsed;

		private static bool _injected;

		private static bool _dictInjected;

		private static bool _ferocityOwnedCached;

		private static float _nextOwnershipCheck;

		private static object _allDamageEnumValue;

		internal static FieldInfo _effectPropsSourceControlField;

		internal static Type _playerControlType;

		internal static FieldInfo _playerMyInstanceField;

		public override void OnInitializeMelon()
		{
			((MelonBase)this).HarmonyInstance.PatchAll();
			InitReflectionCache();
			TryRegisterCustomAugment(first: true);
		}

		private static void InitReflectionCache()
		{
			try
			{
				Type type = AccessTools.TypeByName("Passive+AbilityValue") ?? AccessTools.TypeByName("Passive/AbilityValue");
				if (type != null)
				{
					try
					{
						_allDamageEnumValue = Enum.Parse(type, "AllDamage");
					}
					catch
					{
						_allDamageEnumValue = null;
					}
				}
				Type type2 = AccessTools.TypeByName("EffectProperties");
				if (type2 != null)
				{
					_effectPropsSourceControlField = type2.GetField("SourceControl", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				_playerControlType = AccessTools.TypeByName("PlayerControl");
				if (_playerControlType != null)
				{
					_playerMyInstanceField = _playerControlType.GetField("myInstance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
			catch
			{
			}
		}

		public override void OnUpdate()
		{
			if (!_augmentRegistered)
			{
				if (Time.realtimeSinceStartup >= _nextAttemptTime && AccessTools.TypeByName("GraphDB") != null)
				{
					TryRegisterCustomAugment(first: false);
					float num = Mathf.Min(5f, 0.5f + (float)_attempts * 0.5f);
					_nextAttemptTime = Time.realtimeSinceStartup + num;
				}
			}
			else if (Time.realtimeSinceStartup >= _nextOwnershipCheck)
			{
				_ferocityOwnedCached = PlayerHasFerocity();
				_nextOwnershipCheck = Time.realtimeSinceStartup + 0.5f;
			}
		}

		public static void SetDamageScroll(bool enabled)
		{
			HasDamageScroll = enabled;
			MelonLogger.Msg("Damage Scroll manual override " + (enabled ? "ENABLED" : "DISABLED"));
		}

		internal static void TryRegisterCustomAugment(bool first)
		{
			if (_augmentRegistered)
			{
				return;
			}
			_attempts++;
			try
			{
				Type type = AccessTools.TypeByName("GraphDB");
				if (type == null)
				{
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(type, "GetAugmentByName", new Type[1] { typeof(string) }, (Type[])null);
				object obj = null;
				if (methodInfo != null)
				{
					try
					{
						obj = methodInfo.Invoke(null, new object[1] { "Scroll of Ferocity" });
					}
					catch
					{
					}
				}
				object obj3 = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null);
				if (obj3 == null)
				{
					return;
				}
				if (CachedAugmentTree == null)
				{
					CachedAugmentTree = obj ?? BuildAugmentWithDiagnostics();
					if (CachedAugmentTree == null)
					{
						return;
					}
				}
				if (obj3.GetType().GetField("PlayerMods", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj3) is IList list)
				{
					int num = FindAugmentIndexByName(list, "Scroll of Ferocity");
					if (num >= 0)
					{
						CachedAugmentTree = list[num];
						_injected = true;
					}
					else if (!_injected)
					{
						list.Add(CachedAugmentTree);
						_injected = true;
						MelonLogger.Msg("[Ferocity] Added to PlayerMods list.");
					}
				}
				Type type2 = CachedAugmentTree.GetType();
				FieldInfo[] fields = obj3.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					Type fieldType = fieldInfo.FieldType;
					if (!fieldType.IsGenericType || fieldType.GetGenericTypeDefinition() != typeof(Dictionary<, >))
					{
						continue;
					}
					Type[] genericArguments = fieldType.GetGenericArguments();
					if (!(genericArguments[0] == typeof(string)) || !(genericArguments[1] == type2))
					{
						continue;
					}
					object value = fieldInfo.GetValue(obj3);
					if (value == null)
					{
						continue;
					}
					bool flag = false;
					try
					{
						flag = (bool)fieldType.GetMethod("ContainsKey").Invoke(value, new object[1] { "Scroll of Ferocity" });
					}
					catch
					{
					}
					if (!flag)
					{
						try
						{
							fieldType.GetMethod("Add").Invoke(value, new object[2] { "Scroll of Ferocity", CachedAugmentTree });
							_dictInjected = true;
							MelonLogger.Msg("[Ferocity] Added to dictionary '" + fieldInfo.Name + "'.");
						}
						catch
						{
						}
					}
					else
					{
						_dictInjected = true;
					}
				}
				bool flag2 = false;
				if (methodInfo != null)
				{
					try
					{
						flag2 = methodInfo.Invoke(null, new object[1] { "Scroll of Ferocity" }) != null;
					}
					catch
					{
					}
				}
				_augmentRegistered = flag2 && _injected;
				if (_augmentRegistered)
				{
					MelonLogger.Msg($"[Ferocity] Registration successful (InjectedList={_injected} Dict={_dictInjected} Fallback={_templateFallbackUsed}).");
				}
			}
			catch
			{
			}
		}

		private static int FindAugmentIndexByName(IList list, string name)
		{
			for (int i = 0; i < list.Count; i++)
			{
				object rootNode = GetRootNode(list[i]);
				if (rootNode == null)
				{
					continue;
				}
				FieldInfo field = rootNode.GetType().GetField("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!(field == null))
				{
					string a = field.GetValue(rootNode) as string;
					if (string.Equals(a, name, StringComparison.OrdinalIgnoreCase))
					{
						return i;
					}
				}
			}
			return -1;
		}

		internal static object BuildAugmentWithDiagnostics()
		{
			try
			{
				Type type = AccessTools.TypeByName("AugmentTree");
				Type type2 = AccessTools.TypeByName("AugmentRootNode");
				Type type3 = AccessTools.TypeByName("ModPassiveNode");
				if (type == null || type2 == null || type3 == null)
				{
					return TemplateFallback();
				}
				object obj = Activator.CreateInstance(type2);
				object obj2 = Activator.CreateInstance(type3);
				SetFieldIfExists(obj, "Name", "Scroll of Ferocity");
				SetFieldIfExists(obj, "Detail", "Double your damage (+100% All Damage).");
				Type type4 = AccessTools.TypeByName("ModType");
				if (type4 != null)
				{
					try
					{
						SetFieldIfExists(obj, "modType", Enum.Parse(type4, "Player"));
					}
					catch
					{
					}
				}
				SetFieldIfExists(obj2, "Multiplier", true);
				SetFieldIfExists(obj2, "Value", 1f);
				Type nestedType = type3.GetNestedType("Category");
				if (nestedType != null)
				{
					try
					{
						SetFieldIfExists(obj2, "category", Enum.Parse(nestedType, "Ability"));
					}
					catch
					{
					}
				}
				FieldInfo field = type3.GetField("abilityPassive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object value = field.GetValue(obj2);
					Type type5 = AccessTools.TypeByName("Passive+AbilityValue") ?? AccessTools.TypeByName("Passive/AbilityValue");
					if (value != null && type5 != null)
					{
						FieldInfo field2 = value.GetType().GetField("Value", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (field2 != null)
						{
							try
							{
								field2.SetValue(value, Enum.Parse(type5, "AllDamage"));
								field.SetValue(obj2, value);
							}
							catch
							{
							}
						}
					}
				}
				FieldInfo field3 = type2.GetField("Passives", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field3 != null)
				{
					(field3.GetValue(obj) as IList)?.Add(obj2);
				}
				object obj6 = null;
				ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (ConstructorInfo constructorInfo in constructors)
				{
					ParameterInfo[] parameters = constructorInfo.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType == type2)
					{
						try
						{
							obj6 = constructorInfo.Invoke(new object[1] { obj });
						}
						catch
						{
							continue;
						}
						break;
					}
				}
				if (obj6 == null)
				{
					try
					{
						obj6 = Activator.CreateInstance(type, nonPublic: true);
					}
					catch
					{
						obj6 = null;
					}
					if (obj6 == null)
					{
						return TemplateFallback();
					}
					bool flag = false;
					FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						if (fieldInfo.FieldType == type2)
						{
							try
							{
								fieldInfo.SetValue(obj6, obj);
								flag = true;
							}
							catch
							{
								continue;
							}
							break;
						}
					}
					if (!flag)
					{
						return TemplateFallback();
					}
				}
				MelonLogger.Msg("[Ferocity] Built standalone augment.");
				return obj6;
			}
			catch
			{
				return TemplateFallback();
			}
		}

		private static object TemplateFallback()
		{
			if (_templateFallbackUsed && CachedAugmentTree != null)
			{
				return CachedAugmentTree;
			}
			_templateFallbackUsed = true;
			try
			{
				Type type = AccessTools.TypeByName("GraphDB");
				Type type2 = AccessTools.TypeByName("ModType");
				if (type == null || type2 == null)
				{
					return null;
				}
				MethodInfo methodInfo = AccessTools.Method(type, "GetValidMods", new Type[1] { type2 }, (Type[])null);
				if (methodInfo == null)
				{
					return null;
				}
				object obj = Enum.Parse(type2, "Player");
				if (!(methodInfo.Invoke(null, new object[1] { obj }) is IList list) || list.Count == 0)
				{
					return null;
				}
				object obj2 = list[0];
				if (obj2 == null)
				{
					return null;
				}
				object obj3 = null;
				try
				{
					obj3 = obj2.GetType().InvokeMember("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, obj2, null);
				}
				catch
				{
					obj3 = obj2;
				}
				object obj5 = GetRootNode(obj3) ?? GetRootNode(obj2);
				if (obj5 != null)
				{
					SetFieldIfExists(obj5, "Name", "Scroll of Ferocity");
					SetFieldIfExists(obj5, "Detail", "Double your damage (+100% All Damage).");
				}
				MelonLogger.Msg("[Ferocity] Using template augment fallback.");
				return obj3;
			}
			catch
			{
				return null;
			}
		}

		internal static object GetRootNode(object augmentTree)
		{
			if (augmentTree == null)
			{
				return null;
			}
			Type type = augmentTree.GetType();
			PropertyInfo property = type.GetProperty("Root", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				try
				{
					return property.GetValue(augmentTree);
				}
				catch
				{
				}
			}
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.Name.Equals("Root", StringComparison.OrdinalIgnoreCase))
				{
					try
					{
						return fieldInfo.GetValue(augmentTree);
					}
					catch
					{
					}
				}
			}
			return null;
		}

		private static void SetFieldIfExists(object obj, string fieldName, object value)
		{
			if (obj == null || value == null)
			{
				return;
			}
			FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(field != null))
			{
				return;
			}
			try
			{
				field.SetValue(obj, value);
			}
			catch
			{
			}
		}

		internal static bool PlayerHasFerocity()
		{
			try
			{
				if (_playerControlType == null || _playerMyInstanceField == null)
				{
					return false;
				}
				object value = _playerMyInstanceField.GetValue(null);
				if (value == null)
				{
					return false;
				}
				object obj = _playerControlType.GetField("Augment", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(value);
				if (obj == null)
				{
					return false;
				}
				if (!(obj.GetType().GetField("trees", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) is IDictionary dictionary))
				{
					return false;
				}
				foreach (object key in dictionary.Keys)
				{
					object obj2 = key;
					string a = obj2.GetType().GetField("Name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2) as string;
					if (string.Equals(a, "Scroll of Ferocity", StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
			}
			catch
			{
			}
			return false;
		}

		internal static bool FerocityActiveFast()
		{
			return HasDamageScroll || _ferocityOwnedCached;
		}

		internal static bool IsAllDamageEnum(object val)
		{
			if (_allDamageEnumValue == null || val == null)
			{
				return false;
			}
			return val.Equals(_allDamageEnumValue);
		}
	}
	[HarmonyPatch]
	internal static class Ferocity_ModifyAbilityPassives_Patch
	{
		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("EffectProperties");
			if (type == null)
			{
				return null;
			}
			Type type2 = AccessTools.TypeByName("Passive+AbilityValue") ?? AccessTools.TypeByName("Passive/AbilityValue");
			if (type2 == null)
			{
				return null;
			}
			return AccessTools.Method(type, "ModifyAbilityPassives", new Type[2]
			{
				type2,
				typeof(float)
			}, (Type[])null);
		}

		private static void Postfix(object valueType, float startVal, ref float __result, object __instance)
		{
			try
			{
				if (!Class1.FerocityActiveFast() || !Class1.IsAllDamageEnum(valueType) || Class1._effectPropsSourceControlField == null || Class1._playerMyInstanceField == null)
				{
					return;
				}
				object value = Class1._effectPropsSourceControlField.GetValue(__instance);
				if (value == null)
				{
					return;
				}
				object value2 = Class1._playerMyInstanceField.GetValue(null);
				if (value2 != null && value == value2)
				{
					if (!(Math.Abs(startVal) < 0.0001f) && __result < startVal * 1.95f)
					{
						__result += startVal;
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("Ferocity passive patch failed: " + ex.Message);
			}
		}
	}
}