Decompiled source of CowardlyGreydwarfs v1.0.0

CowardlyGreydwarfs.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CowardlyGreydwarfs")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CowardlyGreydwarfs")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("efb8d134-6ac1-4d17-ae04-edff6144bb93")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CowardlyGreydwarfs;

[BepInPlugin("com.robit.cowardlygreydwarfs", "Cowardly Greydwarfs", "1.0.0")]
public class CowardlyGreydwarfsPlugin : BaseUnityPlugin
{
	private static class DamagePatch
	{
		internal static void Prefix(Character __instance, ref float __state)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				__state = 0f;
			}
			else
			{
				__state = __instance.GetHealth();
			}
		}

		internal static void Postfix(Character __instance, float __state)
		{
			if ((Object)(object)__instance == (Object)null || !IsAffectedCreature(__instance))
			{
				return;
			}
			float health = __instance.GetHealth();
			if (health <= 0f)
			{
				return;
			}
			float num = __state - health;
			if (num <= 0f)
			{
				return;
			}
			float maxHealth = __instance.GetMaxHealth();
			if (maxHealth <= 0f)
			{
				return;
			}
			float num2 = num / maxHealth;
			float value = BigHitThreshold.Value;
			if (value <= 0f || num2 < value)
			{
				return;
			}
			float value2 = PanicDuration.Value;
			if (!(value2 <= 0f))
			{
				int instanceID = ((Object)__instance).GetInstanceID();
				float num3 = Time.time + value2;
				if (PanicUntil.TryGetValue(instanceID, out var value3))
				{
					PanicUntil[instanceID] = Mathf.Max(value3, num3);
				}
				else
				{
					PanicUntil.Add(instanceID, num3);
				}
				if (DebugLogging.Value)
				{
					string prefabName = GetPrefabName(((Component)__instance).gameObject);
					Log.LogInfo((object)(prefabName + " panicked! Hit removed " + (num2 * 100f).ToString("0.0") + "% of max HP. Fleeing for " + value2.ToString("0.0") + " seconds."));
				}
			}
		}
	}

	private static class AIPatch
	{
		internal struct FleeState
		{
			internal bool Applied;

			internal float OldFleeIfLowHealth;

			internal float OldFleeTimeSinceHurt;
		}

		internal static void Prefix(MonsterAI __instance, ref FleeState __state)
		{
			__state = new FleeState
			{
				Applied = false
			};
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Character component = ((Component)__instance).GetComponent<Character>();
			if ((Object)(object)component == (Object)null || !IsAffectedCreature(component))
			{
				return;
			}
			float health = component.GetHealth();
			float maxHealth = component.GetMaxHealth();
			if (health <= 0f || maxHealth <= 0f)
			{
				return;
			}
			int instanceID = ((Object)component).GetInstanceID();
			bool flag = false;
			if (PanicUntil.TryGetValue(instanceID, out var value))
			{
				if (Time.time < value)
				{
					flag = true;
				}
				else
				{
					PanicUntil.Remove(instanceID);
				}
			}
			float num = health / maxHealth;
			float value2 = LowHealthThreshold.Value;
			bool flag2 = value2 > 0f && num <= value2;
			if (flag || flag2)
			{
				__state.OldFleeIfLowHealth = (float)FleeIfLowHealthField.GetValue(__instance);
				__state.OldFleeTimeSinceHurt = (float)FleeTimeSinceHurtField.GetValue(__instance);
				__state.Applied = true;
				FleeIfLowHealthField.SetValue(__instance, 1.01f);
				FleeTimeSinceHurtField.SetValue(__instance, float.MaxValue);
			}
		}

		internal static void Postfix(MonsterAI __instance, FleeState __state)
		{
			if (!((Object)(object)__instance == (Object)null) && __state.Applied)
			{
				FleeIfLowHealthField.SetValue(__instance, __state.OldFleeIfLowHealth);
				FleeTimeSinceHurtField.SetValue(__instance, __state.OldFleeTimeSinceHurt);
			}
		}
	}

	private Harmony _harmony;

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> EnableGreylings;

	internal static ConfigEntry<bool> EnableGreydwarfs;

	internal static ConfigEntry<bool> EnableShamans;

	internal static ConfigEntry<bool> EnableBrutes;

	internal static ConfigEntry<float> BigHitThreshold;

	internal static ConfigEntry<float> LowHealthThreshold;

	internal static ConfigEntry<float> PanicDuration;

	internal static ConfigEntry<bool> DebugLogging;

	internal static FieldInfo FleeIfLowHealthField;

	internal static FieldInfo FleeTimeSinceHurtField;

	internal static readonly Dictionary<int, float> PanicUntil = new Dictionary<int, float>();

	private static readonly List<int> ExpiredPanicEntries = new List<int>();

	private float _nextCleanupTime;

	private void Awake()
	{
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Expected O, but got Unknown
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Expected O, but got Unknown
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Expected O, but got Unknown
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_0242: Expected O, but got Unknown
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Expected O, but got Unknown
		//IL_027b: Expected O, but got Unknown
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b5: Expected O, but got Unknown
		//IL_02b5: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		EnableGreylings = ((BaseUnityPlugin)this).Config.Bind<bool>("Creatures", "Enable Greylings", true, "Allow Greylings to flee.");
		EnableGreydwarfs = ((BaseUnityPlugin)this).Config.Bind<bool>("Creatures", "Enable Greydwarfs", true, "Allow normal Greydwarfs to flee.");
		EnableShamans = ((BaseUnityPlugin)this).Config.Bind<bool>("Creatures", "Enable Shamans", false, "Allow Greydwarf Shamans to flee.");
		EnableBrutes = ((BaseUnityPlugin)this).Config.Bind<bool>("Creatures", "Enable Brutes", false, "Allow Greydwarf Brutes to flee.");
		BigHitThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Fleeing", "Big Hit Threshold", 0.5f, new ConfigDescription("Fraction of the creature's maximum health that must be lost from ONE hit to cause panic. 0.50 = 50%. Set to 0 to disable.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		LowHealthThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Fleeing", "Low Health Threshold", 0.2f, new ConfigDescription("Creature will flee while its health is at or below this fraction. 0.20 = 20%. Set to 0 to disable.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		PanicDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Fleeing", "Panic Duration", 15f, new ConfigDescription("How many seconds a creature flees after receiving a big hit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>()));
		DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug Logging", false, "Log panic triggers to the BepInEx console.");
		FleeIfLowHealthField = AccessTools.Field(typeof(MonsterAI), "m_fleeIfLowHealth");
		FleeTimeSinceHurtField = AccessTools.Field(typeof(MonsterAI), "m_fleeTimeSinceHurt");
		if (FleeIfLowHealthField == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Could not find MonsterAI.m_fleeIfLowHealth. Cowardly Greydwarfs cannot start.");
			return;
		}
		if (FleeTimeSinceHurtField == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Could not find MonsterAI.m_fleeTimeSinceHurt. Cowardly Greydwarfs cannot start.");
			return;
		}
		MethodInfo methodInfo = AccessTools.Method(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(MonsterAI), "UpdateAI", (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Could not find Character.RPC_Damage. Cowardly Greydwarfs cannot start.");
			return;
		}
		if (methodInfo2 == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Could not find MonsterAI.UpdateAI. Cowardly Greydwarfs cannot start.");
			return;
		}
		_harmony = new Harmony("com.robit.cowardlygreydwarfs");
		_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DamagePatch), "Prefix", (Type[])null), new HarmonyMethod(typeof(DamagePatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		_harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(AIPatch), "Prefix", (Type[])null), new HarmonyMethod(typeof(AIPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Cowardly Greydwarfs v1.0.0 loaded.");
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}

	private void Update()
	{
		if (Time.time < _nextCleanupTime)
		{
			return;
		}
		_nextCleanupTime = Time.time + 10f;
		ExpiredPanicEntries.Clear();
		foreach (KeyValuePair<int, float> item in PanicUntil)
		{
			if (Time.time >= item.Value)
			{
				ExpiredPanicEntries.Add(item.Key);
			}
		}
		foreach (int expiredPanicEntry in ExpiredPanicEntries)
		{
			PanicUntil.Remove(expiredPanicEntry);
		}
	}

	internal static bool IsAffectedCreature(Character character)
	{
		if ((Object)(object)character == (Object)null)
		{
			return false;
		}
		return GetPrefabName(((Component)character).gameObject) switch
		{
			"Greyling" => EnableGreylings.Value, 
			"Greydwarf" => EnableGreydwarfs.Value, 
			"Greydwarf_Shaman" => EnableShamans.Value, 
			"Greydwarf_Elite" => EnableBrutes.Value, 
			_ => false, 
		};
	}

	internal static string GetPrefabName(GameObject gameObject)
	{
		if ((Object)(object)gameObject == (Object)null)
		{
			return string.Empty;
		}
		string text = ((Object)gameObject).name;
		int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
		if (num >= 0)
		{
			text = text.Substring(0, num);
		}
		return text.Trim();
	}
}