Decompiled source of EnemySizeRandomizer v1.5.3

BepInEx/plugins/EnemySizeRandomizer.dll

Decompiled 2 months 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 System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using ScalerCore;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("EnemySizeRandomizer")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EnemySizeRandomizer")]
[assembly: AssemblyTitle("EnemySizeRandomizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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 EnemySizeRandomizer
{
	[BepInPlugin("Grungler.EnemySizeRandomizer", "Enemy Size Randomizer", "1.5.3")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class EnemySizeRandomizerPlugin : BaseUnityPlugin
	{
		private class TimerField
		{
			public object Owner;

			public FieldInfo Field;
		}

		private class HuntsmanData
		{
			public EnemyHunter Hunter;

			public Vector3 OriginalGunTipPosition;

			public Vector3 OriginalGunAimPosition;

			public bool HasGunTip;

			public bool HasGunAim;
		}

		private class EnemyData
		{
			public EnemyParent Enemy;

			public ScaleController Controller;

			public float Size;

			public float Speed;

			public Animator[] Animators;

			public AudioSource[] AudioSources;

			public EnemyRigidbody EnemyRigidbody;

			public float OriginalPositionSpeedChase;

			public float OriginalPositionSpeedIdle;

			public float OriginalRotationSpeedChase;

			public float OriginalRotationSpeedIdle;

			public GrabForce OriginalGrabForce;

			public bool HasOriginalRigidbodyValues;

			public HuntsmanData Huntsman;

			public List<TimerField> TimerFields = new List<TimerField>();
		}

		public static ConfigEntry<bool> EnableSizeRandomizer;

		public static ConfigEntry<bool> EnableSpeedRandomizer;

		public static ConfigEntry<float> MinScale;

		public static ConfigEntry<float> MaxScale;

		public static ConfigEntry<float> MinSpeedMultiplier;

		public static ConfigEntry<float> MaxSpeedMultiplier;

		public static ConfigEntry<bool> SpeedAnimations;

		public static ConfigEntry<bool> SpeedAttackTimers;

		public static ConfigEntry<float> MinAudioPitch;

		public static ConfigEntry<float> MaxAudioPitch;

		public static ConfigEntry<float> DamageSizeInfluence;

		public static ConfigEntry<float> MinimumDamageMultiplier;

		public static ConfigEntry<float> MaximumDamageMultiplier;

		private static readonly Dictionary<EnemyParent, EnemyData> Enemies = new Dictionary<EnemyParent, EnemyData>();

		private void Awake()
		{
			EnableSizeRandomizer = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomizers", "EnableSizeRandomizer", true, "Randomize enemy sizes.");
			EnableSpeedRandomizer = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomizers", "EnableSpeedRandomizer", true, "Randomize enemy speed.");
			MinScale = ((BaseUnityPlugin)this).Config.Bind<float>("Size", "MinScale", 0.15f, "Smallest enemy size. 1.0 = normal size.");
			MaxScale = ((BaseUnityPlugin)this).Config.Bind<float>("Size", "MaxScale", 2f, "Largest enemy size. 1.0 = normal size.");
			MinSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "MinSpeedMultiplier", 1f, "Slowest enemy speed. 1.0 = normal speed.");
			MaxSpeedMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "MaxSpeedMultiplier", 5f, "Fastest enemy speed. 1.0 = normal speed.");
			SpeedAnimations = ((BaseUnityPlugin)this).Config.Bind<bool>("SpeedBehavior", "SpeedAnimations", true, "Make supported enemy animations use the random speed multiplier.");
			SpeedAttackTimers = ((BaseUnityPlugin)this).Config.Bind<bool>("SpeedBehavior", "SpeedAttackTimers", true, "Make supported enemy cooldown, delay, charge, recovery and attack timers faster.");
			MinAudioPitch = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "MinAudioPitch", 0.5f, "Lowest pitch allowed for large enemies.");
			MaxAudioPitch = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "MaxAudioPitch", 1.8f, "Highest pitch allowed for small enemies.");
			DamageSizeInfluence = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "DamageSizeInfluence", 0.2f, "How strongly size affects damage. 0.20 means 1.5x size = 1.10x damage.");
			MinimumDamageMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "MinimumDamageMultiplier", 0.8f, "Lowest damage multiplier caused by size.");
			MaximumDamageMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "MaximumDamageMultiplier", 1.2f, "Highest damage multiplier caused by size.");
			Harmony.CreateAndPatchAll(typeof(EnemySizeRandomizerPlugin), (string)null);
			Debug.Log((object)"[EnemySizeRandomizer] Loaded version 1.5.3");
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnemyParent), "SpawnRPC")]
		private static void OnEnemySpawn(EnemyParent __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				Debug.Log((object)"[EnemySizeRandomizer] SpawnRPC detected.");
				if (PhotonNetwork.IsMasterClient)
				{
					((MonoBehaviour)__instance).StartCoroutine(SetupEnemyAfterSpawn(__instance));
				}
			}
		}

		private static IEnumerator SetupEnemyAfterSpawn(EnemyParent enemy)
		{
			yield return null;
			yield return null;
			yield return null;
			if ((Object)(object)enemy == (Object)null)
			{
				yield break;
			}
			ScaleController controller = ((Component)enemy).GetComponentInChildren<ScaleController>(true);
			if ((Object)(object)controller == (Object)null)
			{
				Debug.LogWarning((object)"[EnemySizeRandomizer] Could not find ScalerCore ScaleController.");
				yield break;
			}
			float size = 1f;
			if (EnableSizeRandomizer.Value)
			{
				size = Random.Range(MinScale.Value, MaxScale.Value);
			}
			float speed = 1f;
			if (EnableSpeedRandomizer.Value)
			{
				speed = Random.Range(MinSpeedMultiplier.Value, MaxSpeedMultiplier.Value);
			}
			Animator[] animators = ((Component)enemy).GetComponentsInChildren<Animator>(true);
			AudioSource[] audioSources = ((Component)enemy).GetComponentsInChildren<AudioSource>(true);
			EnemyRigidbody enemyRigidbody = ((Component)enemy).GetComponentInChildren<EnemyRigidbody>(true);
			EnemyData data = new EnemyData
			{
				Enemy = enemy,
				Controller = controller,
				Size = size,
				Speed = speed,
				Animators = animators,
				AudioSources = audioSources,
				EnemyRigidbody = enemyRigidbody
			};
			data.TimerFields = FindTimerFields(enemy);
			if ((Object)(object)enemyRigidbody != (Object)null)
			{
				data.OriginalPositionSpeedChase = enemyRigidbody.positionSpeedChase;
				data.OriginalPositionSpeedIdle = enemyRigidbody.positionSpeedIdle;
				data.OriginalRotationSpeedChase = enemyRigidbody.rotationSpeedChase;
				data.OriginalRotationSpeedIdle = enemyRigidbody.rotationSpeedIdle;
				data.OriginalGrabForce = enemyRigidbody.grabForceNeeded;
				data.HasOriginalRigidbodyValues = true;
			}
			EnemyHunter hunter = ((Component)enemy).GetComponentInChildren<EnemyHunter>(true);
			if ((Object)(object)hunter != (Object)null)
			{
				HuntsmanData huntsman = new HuntsmanData
				{
					Hunter = hunter
				};
				if ((Object)(object)hunter.gunTipTransform != (Object)null)
				{
					huntsman.OriginalGunTipPosition = hunter.gunTipTransform.localPosition;
					huntsman.HasGunTip = true;
				}
				if ((Object)(object)hunter.gunAimTransform != (Object)null)
				{
					huntsman.OriginalGunAimPosition = hunter.gunAimTransform.localPosition;
					huntsman.HasGunAim = true;
				}
				data.Huntsman = huntsman;
				Debug.Log((object)"[EnemySizeRandomizer] Huntsman compatibility enabled.");
			}
			data.TimerFields = FindTimerFields(enemy);
			Enemies[enemy] = data;
			Debug.Log((object)("[EnemySizeRandomizer] Enemy randomized: " + $"Size {size:F2}x | " + $"Speed {speed:F2}x | " + $"Timers {data.TimerFields.Count}"));
			ScaleOptions options = ScaleOptions.Default;
			options.Factor = size;
			options.SpeedFactor = speed;
			options.PreserveMass = true;
			options.AllowedTargets = (ScaleTargets)2;
			options.SuppressVoicePitch = false;
			options.IgnoreBonkExpand = true;
			options.Duration = 0f;
			ScaleManager.Apply(((Component)controller).gameObject, options);
			Debug.Log((object)"[EnemySizeRandomizer] ScalerCore Apply() called.");
			yield return null;
			RestoreSpeedIndependence(data);
			RestoreGrabForce(data);
			ApplyHuntsmanTransformFix(data);
			ApplyAnimationSpeed(data);
			ApplyAudioPitch(data);
		}

		private void Update()
		{
			if (Enemies.Count == 0)
			{
				return;
			}
			List<EnemyParent> list = new List<EnemyParent>();
			foreach (KeyValuePair<EnemyParent, EnemyData> enemy in Enemies)
			{
				EnemyParent key = enemy.Key;
				EnemyData value = enemy.Value;
				if ((Object)(object)key == (Object)null)
				{
					list.Add(key);
					continue;
				}
				RestoreSpeedIndependence(value);
				RestoreGrabForce(value);
				ApplyHuntsmanTransformFix(value);
				if (SpeedAnimations.Value)
				{
					ApplyAnimationSpeed(value);
				}
				ApplyAudioPitch(value);
				if (SpeedAttackTimers.Value)
				{
					AccelerateTimers(value);
				}
			}
			foreach (EnemyParent item in list)
			{
				Enemies.Remove(item);
			}
		}

		private static void RestoreSpeedIndependence(EnemyData data)
		{
			if (data != null && !((Object)(object)data.EnemyRigidbody == (Object)null) && data.HasOriginalRigidbodyValues && EnableSpeedRandomizer.Value)
			{
				data.EnemyRigidbody.positionSpeedChase = data.OriginalPositionSpeedChase * data.Speed;
				data.EnemyRigidbody.positionSpeedIdle = data.OriginalPositionSpeedIdle * data.Speed;
				data.EnemyRigidbody.rotationSpeedChase = data.OriginalRotationSpeedChase * data.Speed;
				data.EnemyRigidbody.rotationSpeedIdle = data.OriginalRotationSpeedIdle * data.Speed;
			}
		}

		private static void RestoreGrabForce(EnemyData data)
		{
			if (data != null && !((Object)(object)data.EnemyRigidbody == (Object)null) && data.HasOriginalRigidbodyValues)
			{
				data.EnemyRigidbody.grabForceNeeded = data.OriginalGrabForce;
			}
		}

		private static void ApplyHuntsmanTransformFix(EnemyData data)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (data == null || data.Huntsman == null)
			{
				return;
			}
			EnemyHunter hunter = data.Huntsman.Hunter;
			if (!((Object)(object)hunter == (Object)null))
			{
				float size = data.Size;
				if (data.Huntsman.HasGunTip && (Object)(object)hunter.gunTipTransform != (Object)null)
				{
					hunter.gunTipTransform.localPosition = data.Huntsman.OriginalGunTipPosition * size;
				}
				if (data.Huntsman.HasGunAim && (Object)(object)hunter.gunAimTransform != (Object)null)
				{
					hunter.gunAimTransform.localPosition = data.Huntsman.OriginalGunAimPosition * size;
				}
			}
		}

		private static void ApplyAnimationSpeed(EnemyData data)
		{
			if (data == null || data.Animators == null)
			{
				return;
			}
			Animator[] animators = data.Animators;
			foreach (Animator val in animators)
			{
				if (!((Object)(object)val == (Object)null) && ((Behaviour)val).enabled)
				{
					val.speed = data.Speed;
				}
			}
		}

		private static void ApplyAudioPitch(EnemyData data)
		{
			if (data == null || data.AudioSources == null)
			{
				return;
			}
			float num = Mathf.Max(0.01f, data.Size);
			float num2 = Mathf.Pow(1f / num, 0.75f);
			num2 = Mathf.Clamp(num2, MinAudioPitch.Value, MaxAudioPitch.Value);
			AudioSource[] audioSources = data.AudioSources;
			foreach (AudioSource val in audioSources)
			{
				if (!((Object)(object)val == (Object)null))
				{
					val.pitch = num2;
				}
			}
		}

		private static List<TimerField> FindTimerFields(EnemyParent enemy)
		{
			List<TimerField> list = new List<TimerField>();
			MonoBehaviour[] componentsInChildren = ((Component)enemy).GetComponentsInChildren<MonoBehaviour>(true);
			MonoBehaviour[] array = componentsInChildren;
			foreach (MonoBehaviour val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Type type = ((object)val).GetType();
				FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				FieldInfo[] array2 = fields;
				foreach (FieldInfo fieldInfo in array2)
				{
					if (!(fieldInfo.FieldType != typeof(float)))
					{
						string name = fieldInfo.Name.ToLowerInvariant();
						if (LooksLikeAttackTimer(name) && !LooksLikeProtectedTimer(name))
						{
							list.Add(new TimerField
							{
								Owner = val,
								Field = fieldInfo
							});
						}
					}
				}
			}
			return list;
		}

		private static bool LooksLikeAttackTimer(string name)
		{
			return name.Contains("cooldown") || name.Contains("delay") || name.Contains("wait") || name.Contains("charge") || name.Contains("attackinterval") || name.Contains("attackcooldown") || name.Contains("recovery") || name.Contains("reload") || name.Contains("casttime") || name.Contains("windup");
		}

		private static bool LooksLikeProtectedTimer(string name)
		{
			return name.Contains("stun") || name.Contains("freeze") || name.Contains("frozen") || name.Contains("invincible") || name.Contains("invulnerability") || name.Contains("death") || name.Contains("dead") || name.Contains("disable") || name.Contains("respawn");
		}

		private static void AccelerateTimers(EnemyData data)
		{
			if (data == null || data.TimerFields == null)
			{
				return;
			}
			float speed = data.Speed;
			if (Mathf.Approximately(speed, 1f))
			{
				return;
			}
			float num = Time.deltaTime * (speed - 1f);
			foreach (TimerField timerField in data.TimerFields)
			{
				if (timerField.Owner == null || timerField.Field == null)
				{
					continue;
				}
				try
				{
					float num2 = (float)timerField.Field.GetValue(timerField.Owner);
					if (!(num2 <= 0f))
					{
						num2 -= num;
						if (num2 < 0f)
						{
							num2 = 0f;
						}
						timerField.Field.SetValue(timerField.Owner, num2);
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPriority(0)]
		[HarmonyPrefix]
		[HarmonyPatch(typeof(HurtCollider), "PlayerHurt")]
		private static void SoftenEnemyDamage(HurtCollider __instance)
		{
			if (!EnableSizeRandomizer.Value || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			EnemyParent componentInParent = ((Component)__instance).GetComponentInParent<EnemyParent>();
			if (!((Object)(object)componentInParent == (Object)null) && Enemies.TryGetValue(componentInParent, out var value))
			{
				float size = value.Size;
				if (!(size <= 0.0001f))
				{
					float num = 1f + (size - 1f) * DamageSizeInfluence.Value;
					num = Mathf.Clamp(num, MinimumDamageMultiplier.Value, MaximumDamageMultiplier.Value);
					float num2 = num / size;
					__instance.playerDamage = Mathf.RoundToInt((float)__instance.playerDamage * num2);
					__instance.playerTumbleImpactHurtDamage = Mathf.RoundToInt((float)__instance.playerTumbleImpactHurtDamage * num2);
				}
			}
		}
	}
}