Decompiled source of BossAffixByPlayers v1.0.4

BepInEx\plugins\Cjayride.BossGroupAffix.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 System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Cjayride.BossGroupAffix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4+eb1be48ca009e441df618dd16f77a31de42125b9")]
[assembly: AssemblyProduct("Cjayride.BossGroupAffix")]
[assembly: AssemblyTitle("Cjayride.BossGroupAffix")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.4.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 Cjayride.BossGroupAffix
{
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class Character_Awake_Patch
	{
		private static void Postfix(Character __instance)
		{
			if (Object.op_Implicit((Object)(object)__instance) && __instance.IsBoss())
			{
				ConfigEntry<bool> swapAffixByPlayerCount = Plugin.SwapAffixByPlayerCount;
				if (swapAffixByPlayerCount != null && swapAffixByPlayerCount.Value && Cllc.Ready && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<AffixWatcher>()))
				{
					((Component)__instance).gameObject.AddComponent<AffixWatcher>();
				}
			}
		}
	}
	internal sealed class AffixWatcher : MonoBehaviour
	{
		private const string ZdoLocked = "cjayride.BossGroupAffix.Locked";

		private const float Interval = 2f;

		private Character _boss;

		private float _timer;

		private void Awake()
		{
			_boss = ((Component)this).GetComponent<Character>();
		}

		private void Update()
		{
			_timer += Time.deltaTime;
			if (!(_timer < 2f))
			{
				_timer = 0f;
				Check();
			}
		}

		private void Check()
		{
			if (Plugin.SwapAffixByPlayerCount.Value && Cllc.Ready && Plugin.Enabled.Value && Object.op_Implicit((Object)(object)_boss) && !_boss.IsDead() && Object.op_Implicit((Object)(object)_boss.m_nview) && _boss.m_nview.IsValid() && _boss.m_nview.IsOwner())
			{
				ZDO zDO = _boss.m_nview.GetZDO();
				if (zDO.GetBool("cjayride.BossGroupAffix.Locked", false))
				{
					((Behaviour)this).enabled = false;
				}
				else if (_boss.GetHealth() < _boss.GetMaxHealth())
				{
					ApplyAffixForPlayerCount();
					zDO.Set("cjayride.BossGroupAffix.Locked", true);
					((Behaviour)this).enabled = false;
				}
				else
				{
					ApplyAffixForPlayerCount();
				}
			}
		}

		private void ApplyAffixForPlayerCount()
		{
			string text = Cllc.Get(_boss);
			if (!(text == "Twin") && !(text == "Godlike"))
			{
				int num = BossNearbyPlayers.CountLivingNear(_boss);
				string text2 = ((num <= 1) ? Plugin.SoloAffix.Value : Plugin.GroupAffix.Value);
				if (Cllc.IsValid(text2) && !(text2 == text))
				{
					Cllc.Set(_boss, text2);
					Plugin.Log.LogInfo((object)$"Boss Affix By Players: {_boss.m_name} affix {text} -> {text2} ({num} player(s) in range).");
				}
			}
		}
	}
	internal static class BossNearbyPlayers
	{
		private static readonly List<Player> Buffer = new List<Player>();

		internal static int CountLivingNear(Character boss)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)boss))
			{
				return 0;
			}
			return CountLivingNear(((Component)boss).transform.position);
		}

		internal static int CountLivingNear(Vector3 position)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			float num = Plugin.PlayerRange?.Value ?? 0f;
			if (num <= 0f)
			{
				return 0;
			}
			Buffer.Clear();
			Player.GetPlayersInRange(position, num, Buffer);
			int num2 = 0;
			for (int i = 0; i < Buffer.Count; i++)
			{
				Player val = Buffer[i];
				if (Object.op_Implicit((Object)(object)val) && !((Character)val).IsDead())
				{
					num2++;
				}
			}
			Buffer.Clear();
			return num2;
		}
	}
	[HarmonyPatch(typeof(OfferingBowl), "InitiateSpawnBoss")]
	internal static class BossSummonGate_Initiate
	{
		private static readonly FieldInfo InteractUser = AccessTools.Field(typeof(OfferingBowl), "m_interactUser");

		private static bool Prefix(OfferingBowl __instance, Vector3 point, bool removeItemsFromInventory)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<bool> enabled = Plugin.Enabled;
			if (enabled == null || !enabled.Value)
			{
				return true;
			}
			int num = Plugin.MinPlayersToStartFight?.Value ?? 0;
			if (num <= 0 || (Object)(object)__instance == (Object)null || (Object)(object)__instance.m_bossPrefab == (Object)null)
			{
				return true;
			}
			int num2 = BossNearbyPlayers.CountLivingNear(point);
			if (num2 >= num)
			{
				return true;
			}
			object? obj = InteractUser?.GetValue(__instance);
			BossSummonMessages.Show((Humanoid)((obj is Humanoid) ? obj : null), num, num2);
			return false;
		}
	}
	[HarmonyPatch(typeof(OfferingBowl), "RPC_SpawnBoss")]
	internal static class BossSummonGate_Rpc
	{
		private static bool Prefix(OfferingBowl __instance, Vector3 point, bool removeItemsFromInventory)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			ConfigEntry<bool> enabled = Plugin.Enabled;
			if (enabled == null || !enabled.Value)
			{
				return true;
			}
			int num = Plugin.MinPlayersToStartFight?.Value ?? 0;
			if (num <= 0 || (Object)(object)__instance == (Object)null || (Object)(object)__instance.m_bossPrefab == (Object)null)
			{
				return true;
			}
			if (BossNearbyPlayers.CountLivingNear(point) >= num)
			{
				return true;
			}
			return false;
		}
	}
	internal static class BossSummonMessages
	{
		private static float _lastMessageTime;

		internal static void Show(Humanoid user, int required, int present)
		{
			if (!((Object)(object)user == (Object)null) && !((Object)(object)user != (Object)(object)Player.m_localPlayer))
			{
				float num = Plugin.MessageCooldownSeconds?.Value ?? 3f;
				if (!(Time.time - _lastMessageTime < num))
				{
					_lastMessageTime = Time.time;
					string text = string.Format(Plugin.FightBlockedMessage?.Value ?? "Need at least {0} players within {1}m of the boss spawn to summon ({2} nearby).", required, Mathf.RoundToInt(Plugin.PlayerRange?.Value ?? 0f), present);
					((Character)user).Message((MessageType)2, text, 0, (Sprite)null, false);
				}
			}
		}
	}
	[BepInPlugin("cjayride.bossgroupaffix", "Boss Affix By Players", "1.0.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "cjayride.bossgroupaffix";

		public const string PluginName = "Boss Affix By Players";

		public const string PluginVersion = "1.0.4";

		internal const string CllcGuid = "org.bepinex.plugins.creaturelevelcontrol";

		internal static Plugin Instance;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> SwapAffixByPlayerCount;

		internal static ConfigEntry<string> SoloAffix;

		internal static ConfigEntry<string> GroupAffix;

		internal static ConfigEntry<float> PlayerRange;

		internal static ConfigEntry<int> MinPlayersToStartFight;

		internal static ConfigEntry<string> FightBlockedMessage;

		internal static ConfigEntry<float> MessageCooldownSeconds;

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private void Awake()
		{
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "When true, MinPlayersToStartFight and optional affix swapping are active.");
			SwapAffixByPlayerCount = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SwapAffixByPlayerCount", false, "When true, CL&LC boss affix is chosen from nearby player count (solo vs group). When false, affix swapping is off.");
			SoloAffix = ((BaseUnityPlugin)this).Config.Bind<string>("Affix swap", "SoloAffix", "Reflective", "Used only when SwapAffixByPlayerCount is true and one living player is in range. One of: None, Reflective, Shielded, Mending, Summoner, Elementalist, Enraged, Sturdy.");
			GroupAffix = ((BaseUnityPlugin)this).Config.Bind<string>("Affix swap", "GroupAffix", "Summoner", "Used only when SwapAffixByPlayerCount is true and two or more living players are in range.");
			PlayerRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PlayerRange", 180f, "Meters from the boss. Living players inside this radius count toward starting the fight and toward affix swap.");
			MinPlayersToStartFight = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MinPlayersToStartFight", 2, new ConfigDescription("Offering a boss summon requires at least this many living players within PlayerRange of the spawn point. 0 disables the check.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>()));
			FightBlockedMessage = ((BaseUnityPlugin)this).Config.Bind<string>("General", "FightBlockedMessage", "Need at least {0} players within {1}m of the boss spawn to summon ({2} nearby).", "HUD text when a summon is blocked. {0} = required count, {1} = PlayerRange, {2} = current count.");
			MessageCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MessageCooldownSeconds", 3f, new ConfigDescription("Minimum seconds between fight-blocked messages for the local player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 30f), Array.Empty<object>()));
			WarnIfAffixInvalid(SoloAffix);
			WarnIfAffixInvalid(GroupAffix);
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Boss Affix By Players 1.0.4 loaded. Config: " + ((BaseUnityPlugin)this).Config.ConfigFilePath));
			new Harmony("cjayride.bossgroupaffix").PatchAll(Assembly.GetExecutingAssembly());
			if (!Cllc.Init())
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Creature Level & Loot Control API not found; affix swapping is disabled until CL&LC is installed.");
			}
		}

		private static void WarnIfAffixInvalid(ConfigEntry<string> entry)
		{
			if (entry != null && !Cllc.IsValid(entry.Value))
			{
				Log.LogWarning((object)("Config " + ((ConfigEntryBase)entry).Definition.Section + "/" + ((ConfigEntryBase)entry).Definition.Key + " = \"" + entry.Value + "\" is not a valid CL&LC boss affix."));
			}
		}
	}
	internal static class Cllc
	{
		private static Type _affixType;

		private static MethodInfo _get;

		private static MethodInfo _set;

		internal static bool Ready
		{
			get
			{
				if (_get != null)
				{
					return _set != null;
				}
				return false;
			}
		}

		internal static bool Init()
		{
			Type type = AccessTools.TypeByName("CreatureLevelControl.API");
			_affixType = AccessTools.TypeByName("CreatureLevelControl.BossAffix");
			if (type == null || _affixType == null)
			{
				return false;
			}
			_get = AccessTools.Method(type, "GetAffixBoss", new Type[1] { typeof(Character) }, (Type[])null);
			_set = AccessTools.Method(type, "SetAffixBoss", new Type[2]
			{
				typeof(Character),
				_affixType
			}, (Type[])null);
			if (_get != null)
			{
				return _set != null;
			}
			return false;
		}

		internal static string Get(Character boss)
		{
			return _get.Invoke(null, new object[1] { boss })?.ToString() ?? "None";
		}

		internal static void Set(Character boss, string affix)
		{
			_set.Invoke(null, new object[2]
			{
				boss,
				Enum.Parse(_affixType, affix)
			});
		}

		internal static bool IsValid(string affix)
		{
			if (_affixType == null)
			{
				if (!string.IsNullOrEmpty(affix) && affix != "Twin")
				{
					return affix != "Godlike";
				}
				return false;
			}
			if (string.IsNullOrEmpty(affix) || affix == "Twin" || affix == "Godlike")
			{
				return false;
			}
			try
			{
				Enum.Parse(_affixType, affix);
				return true;
			}
			catch (ArgumentException)
			{
				return false;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}