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);
}
}
}
}