Decompiled source of AlertCreatures v1.0.1

AlertCreatures.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using ConditionalConfigSync;
using HarmonyLib;
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: AssemblyCompany("AlertCreatures")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("AlertCreatures")]
[assembly: AssemblyTitle("AlertCreatures")]
[assembly: AssemblyVersion("1.1.0.0")]
[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 AlertCreatures
{
	[BepInPlugin("com.dreich.alertcreatures", "Alert Creatures", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class AlertCreaturesPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.alertcreatures";

		public const string Name = "Alert Creatures";

		public const string Version = "1.0.1";

		private const string ConfigSyncGuid = "_shudnal.ConditionalConfigSync";

		private const string GhettoNetworkGuid = "com.Fire.FiresGhettoNetworkMod";

		private static bool ConfigSyncAvailable;

		internal static ConfigEntry<bool> ModEnabled;

		internal static ConfigEntry<bool> UsePeerPositions;

		internal static ConfigEntry<string> ExcludedCreatures;

		internal static HashSet<string> Excluded = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static int ExcludedVersion;

		internal static ScanSettings Animals;

		internal static ScanSettings Enemies;

		internal static ScanSettings Tamed;

		private void Awake()
		{
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			ConfigSyncAvailable = Chainloader.PluginInfos.ContainsKey("_shudnal.ConditionalConfigSync");
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.Initialize("com.dreich.alertcreatures", "Alert Creatures", "1.0.1");
			}
			ConfigEntry<bool> entry = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Lock Configuration", true, "If on, the configuration is locked and can be changed by server admins only. Requires ConditionalConfigSync.");
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.AddLockingConfigEntry(entry);
			}
			ModEnabled = BindShared("General", "Enabled", defaultValue: true, "Turns the whole mod on or off. When off, every creature looks for targets on the vanilla schedule.");
			UsePeerPositions = BindShared("General", "UsePeerPositionsOnServer", defaultValue: true, "Dedicated server only. Also counts each connected player's reported position when checking whether a player is close, not just the player characters loaded on the server. Matters when the server runs creatures itself, for example with server-side creature ownership from Fire's Ghetto Network Mod.");
			ExcludedCreatures = BindShared("General", "ExcludedCreatures", "Hen,Chicken", "Comma-separated creature prefab names that always use the vanilla schedule, whether tamed or wild. Leave empty to exclude nothing.");
			ExcludedCreatures.SettingChanged += delegate
			{
				ParseExcluded();
			};
			ParseExcluded();
			Animals = BindScanSettings("Animals", "untamed wildlife that flees, such as deer, hares and other creatures driven by AnimalAI", "every 2 seconds");
			Enemies = BindScanSettings("Enemies", "untamed creatures driven by MonsterAI, such as greydwarfs, trolls, and wild boars, wolves and lox", "every 2 seconds with a player within 50 m, otherwise every 6 seconds");
			Tamed = BindScanSettings("Tamed", "tamed creatures of any kind, so they spot threats to you and your base sooner", "every 2 seconds for most tames");
			new Harmony("com.dreich.alertcreatures").PatchAll();
		}

		private static void ParseExcluded()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = (ExcludedCreatures.Value ?? "").Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (text.Trim().Length > 0)
				{
					hashSet.Add(text.Trim());
				}
			}
			Excluded = hashSet;
			ExcludedVersion++;
		}

		private ScanSettings BindScanSettings(string section, string who, string vanilla)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			ScanSettings scanSettings = new ScanSettings();
			scanSettings.Enabled = BindShared(section, "Enabled", defaultValue: true, "Makes " + who + " look for targets more often while a player is close. Vanilla checks " + vanilla + ".");
			scanSettings.PlayerRange = BindShared(section, "PlayerRange", 25f, new ConfigDescription("Distance in metres from the nearest player within which the faster checks are used. This only decides how often the creature looks: it still has to hear or see a target with its own hearing and view ranges to react.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			scanSettings.ScanInterval = BindShared(section, "ScanInterval", 0.5f, new ConfigDescription("Seconds between target checks while a player is within PlayerRange. Values below 0.5 gain little in multiplayer, because a player's noise level is only shared every 0.5 seconds. The game runs creature AI 20 times a second, so 0.05 is the fastest possible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			return scanSettings;
		}

		private ConfigEntry<T> BindShared<T>(string section, string key, T defaultValue, string description)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			return BindShared(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()));
		}

		private ConfigEntry<T> BindShared<T>(string section, string key, T defaultValue, ConfigDescription description)
		{
			ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description);
			if (ConfigSyncAvailable)
			{
				ConfigSyncCompat.AddConfigEntry<T>(val);
			}
			return val;
		}
	}
	internal class ScanSettings
	{
		internal ConfigEntry<bool> Enabled;

		internal ConfigEntry<float> PlayerRange;

		internal ConfigEntry<float> ScanInterval;
	}
	internal static class ConfigSyncCompat
	{
		private static object configSync;

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void Initialize(string guid, string name, string version)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			configSync = (object)new ConfigSync(guid)
			{
				DisplayName = name,
				CurrentVersion = version,
				MinimumRequiredVersion = version,
				ModRequired = true
			};
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void AddLockingConfigEntry(ConfigEntry<bool> entry)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((ConditionalConfigSync)(ConfigSync)configSync).AddLockingConfigEntry<bool>(entry);
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static void AddConfigEntry<T>(ConfigEntry<T> entry)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			((ConditionalConfigSync)(ConfigSync)configSync).AddConfigEntry<T>(entry, (ConfigSyncMode)0);
		}
	}
	internal static class ScanTimer
	{
		private class State
		{
			internal bool HoldsScanWait = true;

			internal bool IsExcluded;

			internal int ExcludedVersion = -1;
		}

		private static readonly ConditionalWeakTable<BaseAI, State> States = new ConditionalWeakTable<BaseAI, State>();

		private static readonly FieldRef<BaseAI, Character> CharacterRef = AccessTools.FieldRefAccess<BaseAI, Character>("m_character");

		internal static void AfterUpdate(BaseAI ai, ref float timer, float before, ScanSettings untamed, float vanillaNear, float vanillaFar)
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			State orCreateValue = States.GetOrCreateValue(ai);
			if (timer > before)
			{
				orCreateValue.HoldsScanWait = Mathf.Approximately(timer, vanillaNear) || Mathf.Approximately(timer, vanillaFar);
			}
			if (!AlertCreaturesPlugin.ModEnabled.Value || !orCreateValue.HoldsScanWait)
			{
				return;
			}
			if (orCreateValue.ExcludedVersion != AlertCreaturesPlugin.ExcludedVersion)
			{
				orCreateValue.ExcludedVersion = AlertCreaturesPlugin.ExcludedVersion;
				orCreateValue.IsExcluded = AlertCreaturesPlugin.Excluded.Contains(Utils.GetPrefabName(((Component)ai).gameObject));
			}
			if (orCreateValue.IsExcluded)
			{
				return;
			}
			Character val = CharacterRef.Invoke(ai);
			ScanSettings scanSettings = (((Object)(object)val != (Object)null && val.IsTamed()) ? AlertCreaturesPlugin.Tamed : untamed);
			if (scanSettings.Enabled.Value)
			{
				float value = scanSettings.ScanInterval.Value;
				if (timer > value && IsPlayerNear(((Component)ai).transform.position, scanSettings.PlayerRange.Value))
				{
					timer = value;
				}
			}
		}

		private static bool IsPlayerNear(Vector3 position, float range)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			if (Player.IsPlayerInRange(position, range))
			{
				return true;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsDedicated() || !AlertCreaturesPlugin.UsePeerPositions.Value)
			{
				return false;
			}
			float num = range * range;
			foreach (ZNetPeer peer in instance.GetPeers())
			{
				Vector3 val = peer.GetRefPos() - position;
				if (((Vector3)(ref val)).sqrMagnitude < num)
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(AnimalAI), "UpdateAI")]
	internal static class AnimalAIScanPatch
	{
		private static readonly FieldRef<AnimalAI, float> Timer = AccessTools.FieldRefAccess<AnimalAI, float>("m_updateTargetTimer");

		private static void Prefix(AnimalAI __instance, out float __state)
		{
			__state = Timer.Invoke(__instance);
		}

		private static void Postfix(AnimalAI __instance, bool __result, float __state)
		{
			if (!__result)
			{
				return;
			}
			try
			{
				ScanTimer.AfterUpdate((BaseAI)(object)__instance, ref Timer.Invoke(__instance), __state, AlertCreaturesPlugin.Animals, 2f, 10f);
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class MonsterAIScanPatch
	{
		private static readonly FieldRef<MonsterAI, float> Timer = AccessTools.FieldRefAccess<MonsterAI, float>("m_updateTargetTimer");

		private static void Prefix(MonsterAI __instance, out float __state)
		{
			__state = Timer.Invoke(__instance);
		}

		private static void Postfix(MonsterAI __instance, bool __result, float __state)
		{
			if (!__result || ((BaseAI)__instance).IsSleeping())
			{
				return;
			}
			try
			{
				ScanTimer.AfterUpdate((BaseAI)(object)__instance, ref Timer.Invoke(__instance), __state, AlertCreaturesPlugin.Enemies, 2f, 6f);
			}
			catch
			{
			}
		}
	}
}