Decompiled source of BrackenAngerMeter v1.0.2

BrackenAngerMeter.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("BrackenAngerMeter")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BrackenAngerMeter")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a5e42522-7099-4e31-a6a9-8f83387caecc")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BrackenAngerMeter;

[BepInPlugin("azumi.brackenangermeter", "BrackenAngerMeter", "1.0.2")]
public class Plugin : BaseUnityPlugin
{
	public static ConfigEntry<bool> ShowMeter;

	public static ConfigEntry<bool> ShowInfoText;

	public static ConfigEntry<Key> NextBrackenKey;

	public static ConfigEntry<Key> PrevBrackenKey;

	public static ConfigEntry<bool> EnableInitialAnger;

	public static ConfigEntry<float> InitialAngerMeterValue;

	internal static Plugin Instance;

	internal static ManualLogSource Log;

	private Harmony harmony;

	private void Awake()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		harmony = new Harmony("azumi.brackenangermeter");
		harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"BrackenAngerMeter loaded.");
		ShowMeter = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Show Meter", true, "Show anger meter.");
		ShowInfoText = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "Show Info Text", true, "Show detailed information text.");
		NextBrackenKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Controls", "Next Bracken Key", (Key)67, "Switch to next Bracken.");
		PrevBrackenKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Controls", "Prev Bracken Key", (Key)66, "Switch to previous Bracken.");
		EnableInitialAnger = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable Initial Anger", false, "Apply the Initial Anger Meter Value.");
		InitialAngerMeterValue = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Initial Anger Meter Value", 0f, new ConfigDescription("Bracken anger meter applied when Anger builds up or detected.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 12f), Array.Empty<object>()));
	}
}
[HarmonyPatch(typeof(StartOfRound))]
internal class StartOfRoundPatch
{
	private static GameObject meterObject;

	private static Image meterImage;

	private static TextMeshProUGUI infoText;

	private static GameObject backgroundmeterObject;

	private static Image backgroundmeterImage;

	private const float MaxAnger = 12f;

	private static readonly FieldInfo threatField = typeof(FlowermanAI).GetField("timesThreatened", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static Dictionary<ulong, FlowermanAI> trackedBrackens = new Dictionary<ulong, FlowermanAI>();

	private static ulong? currentTargetId = null;

	private static Dictionary<ulong, int> lastThreats = new Dictionary<ulong, int>();

	private static HashSet<ulong> initializedBrackens = new HashSet<ulong>();

	private static List<ulong> aliveBrackenIds = new List<ulong>();

	private static int currentIndex = 0;

	[HarmonyPatch("LateUpdate")]
	[HarmonyPostfix]
	private static void LateUpdatePostfix(StartOfRound __instance)
	{
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a3: 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_02aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_030f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0318: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)GameNetworkManager.Instance == (Object)null)
			{
				return;
			}
			PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController;
			if ((Object)(object)localPlayerController == (Object)null)
			{
				return;
			}
			CreateUI(localPlayerController);
			FlowermanAI[] source = Object.FindObjectsOfType<FlowermanAI>();
			List<FlowermanAI> list = source.Where((FlowermanAI b) => (Object)(object)b != (Object)null && !((EnemyAI)b).isEnemyDead).ToList();
			trackedBrackens.Clear();
			foreach (FlowermanAI item in list)
			{
				if ((Object)(object)((EnemyAI)item).thisNetworkObject == (Object)null)
				{
					continue;
				}
				ulong networkObjectId = ((EnemyAI)item).thisNetworkObject.NetworkObjectId;
				trackedBrackens[networkObjectId] = item;
				if (initializedBrackens.Contains(networkObjectId) || !((Object)(object)NetworkManager.Singleton != (Object)null) || !NetworkManager.Singleton.IsHost)
				{
					continue;
				}
				if (Plugin.EnableInitialAnger.Value)
				{
					float num = (item.angerMeter = Mathf.Clamp(Plugin.InitialAngerMeterValue.Value, 0f, 12f));
					if (threatField != null)
					{
						threatField.SetValue(item, Mathf.RoundToInt(num));
					}
					Plugin.Log.LogInfo((object)$"Initialized Bracken {networkObjectId} anger={num}");
				}
				initializedBrackens.Add(networkObjectId);
			}
			aliveBrackenIds = (from kv in trackedBrackens
				where (Object)(object)kv.Value != (Object)null && !((EnemyAI)kv.Value).isEnemyDead
				orderby kv.Key
				select kv.Key).ToList();
			if (aliveBrackenIds.Count == 0)
			{
				currentIndex = 0;
			}
			else if (currentIndex >= aliveBrackenIds.Count)
			{
				currentIndex = 0;
			}
			if (Keyboard.current != null)
			{
				Key value = Plugin.NextBrackenKey.Value;
				Key value2 = Plugin.PrevBrackenKey.Value;
				if ((int)value != 0 && ((ButtonControl)Keyboard.current[value]).wasPressedThisFrame && aliveBrackenIds.Count > 0)
				{
					currentIndex = (currentIndex + 1) % aliveBrackenIds.Count;
					currentTargetId = aliveBrackenIds[currentIndex];
				}
				if ((int)value2 != 0 && ((ButtonControl)Keyboard.current[value2]).wasPressedThisFrame && aliveBrackenIds.Count > 0)
				{
					currentIndex--;
					if (currentIndex < 0)
					{
						currentIndex = aliveBrackenIds.Count - 1;
					}
					currentTargetId = aliveBrackenIds[currentIndex];
				}
			}
			if (list.Count == 0)
			{
				HideUI();
				return;
			}
			List<ulong> list2 = (from kv in trackedBrackens
				where (Object)(object)kv.Value == (Object)null || ((EnemyAI)kv.Value).isEnemyDead
				select kv.Key).ToList();
			foreach (ulong item2 in list2)
			{
				trackedBrackens.Remove(item2);
				initializedBrackens.Remove(item2);
			}
			FlowermanAI val = null;
			if (currentTargetId.HasValue && trackedBrackens.TryGetValue(currentTargetId.Value, out var value3))
			{
				val = value3;
			}
			else
			{
				val = trackedBrackens.Values.OrderByDescending((FlowermanAI b) => b.angerMeter).FirstOrDefault();
				if ((Object)(object)((EnemyAI)(val?)).thisNetworkObject != (Object)null)
				{
					currentTargetId = ((EnemyAI)val).thisNetworkObject.NetworkObjectId;
					currentIndex = aliveBrackenIds.IndexOf(currentTargetId.Value);
					if (currentIndex < 0)
					{
						currentIndex = 0;
					}
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				HideUI();
				return;
			}
			float angerMeter = val.angerMeter;
			int currentBehaviourStateIndex = ((EnemyAI)val).currentBehaviourStateIndex;
			if ((Object)(object)meterObject != (Object)null)
			{
				meterObject.SetActive(Plugin.ShowMeter.Value);
			}
			if ((Object)(object)backgroundmeterObject != (Object)null)
			{
				backgroundmeterObject.SetActive(Plugin.ShowMeter.Value);
			}
			if ((Object)(object)infoText != (Object)null)
			{
				((Component)infoText).gameObject.SetActive(Plugin.ShowInfoText.Value);
			}
			if (Plugin.ShowMeter.Value)
			{
				UpdateMeter(angerMeter);
			}
			if (Plugin.ShowInfoText.Value)
			{
				UpdateText(val, angerMeter, currentBehaviourStateIndex);
			}
		}
		catch (Exception ex)
		{
			Plugin.Log.LogError((object)ex);
		}
	}

	private static void CreateUI(PlayerControllerB player)
	{
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Invalid comparison between Unknown and I4
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Invalid comparison between Unknown and I4
		//IL_025f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0313: Unknown result type (might be due to invalid IL or missing references)
		//IL_032a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0376: Unknown result type (might be due to invalid IL or missing references)
		//IL_0380: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)meterObject != (Object)null && (Object)(object)backgroundmeterObject != (Object)null && (Object)(object)infoText != (Object)null)
		{
			return;
		}
		Image sprintMeterUI = player.sprintMeterUI;
		if ((Object)(object)sprintMeterUI == (Object)null)
		{
			return;
		}
		GameObject gameObject = ((Component)sprintMeterUI).gameObject;
		meterObject = Object.Instantiate<GameObject>(gameObject, gameObject.transform.parent);
		((Object)meterObject).name = "BrackenAngerMeter";
		backgroundmeterObject = Object.Instantiate<GameObject>(gameObject, gameObject.transform.parent);
		((Object)backgroundmeterObject).name = "BrackenAngerMeterBackground";
		backgroundmeterObject.transform.SetAsFirstSibling();
		RectTransform component = gameObject.GetComponent<RectTransform>();
		RectTransform component2 = meterObject.GetComponent<RectTransform>();
		RectTransform component3 = backgroundmeterObject.GetComponent<RectTransform>();
		component2.anchoredPosition = new Vector2(component.anchoredPosition.x + 3f, component.anchoredPosition.y - 4f);
		((Transform)component2).localScale = ((Transform)component).localScale * 1.2f;
		component3.anchoredPosition = component2.anchoredPosition;
		((Transform)component3).localScale = ((Transform)component2).localScale;
		Image[] componentsInChildren = meterObject.GetComponentsInChildren<Image>(true);
		Image[] array = componentsInChildren;
		foreach (Image val in array)
		{
			Plugin.Log.LogInfo((object)$"Image: {((Object)val).name} Type: {val.type}");
		}
		Image[] array2 = componentsInChildren;
		foreach (Image val2 in array2)
		{
			if ((int)val2.type == 3)
			{
				meterImage = val2;
				Plugin.Log.LogInfo((object)("Bracken meter image found: " + ((Object)val2).name));
				break;
			}
		}
		Image[] componentsInChildren2 = backgroundmeterObject.GetComponentsInChildren<Image>(true);
		Image[] array3 = componentsInChildren2;
		foreach (Image val3 in array3)
		{
			if ((int)val3.type == 3)
			{
				backgroundmeterImage = val3;
				break;
			}
		}
		if ((Object)(object)backgroundmeterImage != (Object)null)
		{
			backgroundmeterImage.fillAmount = 0.9101f;
			((Graphic)backgroundmeterImage).color = new Color(0f, 0f, 0f, 0.35f);
		}
		if ((Object)(object)HUDManager.Instance != (Object)null && (Object)(object)HUDManager.Instance.weightCounter != (Object)null)
		{
			infoText = Object.Instantiate<TextMeshProUGUI>(HUDManager.Instance.weightCounter, ((TMP_Text)HUDManager.Instance.weightCounter).transform.parent);
			((Object)infoText).name = "BrackenAngerText";
			RectTransform component4 = ((Component)infoText).GetComponent<RectTransform>();
			component4.anchorMin = new Vector2(0.5f, 1f);
			component4.anchorMax = new Vector2(0.5f, 1f);
			component4.pivot = new Vector2(0.5f, 1f);
			component4.anchoredPosition = new Vector2(40f, -120f);
			((TMP_Text)infoText).fontSize = 12f;
			((TMP_Text)infoText).alignment = (TextAlignmentOptions)513;
			((TMP_Text)infoText).enableWordWrapping = false;
			((TMP_Text)infoText).overflowMode = (TextOverflowModes)0;
			component4.sizeDelta = new Vector2(300f, component4.sizeDelta.y);
		}
	}

	private static void UpdateMeter(float anger)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)meterImage == (Object)null))
		{
			float num = Mathf.Clamp01(anger / 12f);
			meterImage.fillAmount = Mathf.Lerp(0.2976f, 0.9101f, num);
			if (anger < 2f)
			{
				((Graphic)meterImage).color = Color.green;
			}
			else if (anger < 5f)
			{
				((Graphic)meterImage).color = Color.yellow;
			}
			else if (anger < 8f)
			{
				((Graphic)meterImage).color = new Color(1f, 0.5f, 0f);
			}
			else
			{
				((Graphic)meterImage).color = Color.red;
			}
		}
	}

	private static void UpdateText(FlowermanAI bracken, float anger, int state)
	{
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		string text = "UNKNOWN";
		string text2 = "NONE";
		string text3 = "--";
		int num = -1;
		if ((Object)(object)bracken != (Object)null && threatField != null)
		{
			num = (int)threatField.GetValue(bracken);
		}
		if (num < 0)
		{
			Plugin.Log.LogWarning((object)"Threat value not found!");
		}
		if ((Object)(object)bracken != (Object)null && (Object)(object)((EnemyAI)bracken).thisNetworkObject != (Object)null)
		{
			ulong networkObjectId = ((EnemyAI)bracken).thisNetworkObject.NetworkObjectId;
			if (!lastThreats.ContainsKey(networkObjectId))
			{
				lastThreats[networkObjectId] = num;
			}
			else
			{
				int num2 = lastThreats[networkObjectId];
				if (num2 != num)
				{
					Plugin.Log.LogInfo((object)$"[Bracken {networkObjectId}] Threat changed: {num2} -> {num} | Anger: {anger:F2}");
					lastThreats[networkObjectId] = num;
				}
			}
		}
		if ((Object)(object)bracken != (Object)null && (Object)(object)((EnemyAI)bracken).targetPlayer != (Object)null)
		{
			text2 = ((EnemyAI)bracken).targetPlayer.playerUsername;
			float num3 = Vector3.Distance(((Component)bracken).transform.position, ((Component)((EnemyAI)bracken).targetPlayer).transform.position);
			text3 = $"{num3:F1}m";
		}
		if ((Object)(object)bracken != (Object)null && (Object)(object)((EnemyAI)bracken).thisNetworkObject != (Object)null)
		{
			text = $"BRACKEN #{((EnemyAI)bracken).thisNetworkObject.NetworkObjectId}";
		}
		if (!((Object)(object)infoText == (Object)null))
		{
			string text4 = state switch
			{
				0 => "SNEAKING", 
				1 => "EVADING", 
				2 => "ANGRY", 
				_ => "UNKNOWN", 
			};
			float num4 = Mathf.Clamp(0.09f * anger, 0f, 0.99f);
			int num5 = Mathf.RoundToInt(num4 * 100f);
			string text5 = "";
			if (state == 2)
			{
				text5 = $"ANGER TIME LEFT: {anger:F1}s\n";
			}
			float num6 = anger / 12f * 100f;
			float num7 = ((num >= 0) ? ((float)num / 1.75f) : 0f);
			((TMP_Text)infoText).text = "DETAILED INFORMATION\n" + text + "\n" + $"ANGER: {anger:F2} / {12f:F0} ({num6:F1}%)\n" + $"ANGER CHANCE: {num5}%\n" + text5 + "STATE: " + text4 + "\n" + $"ANGER STACKS: {num}\n" + $"ANGER BONUS: +{num7:F2}\n" + "TARGET: " + text2 + "\nDISTANCE: " + text3 + "\n";
		}
	}

	private static void HideUI()
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)meterObject != (Object)null)
		{
			meterObject.SetActive(false);
		}
		if ((Object)(object)backgroundmeterObject != (Object)null)
		{
			backgroundmeterObject.SetActive(false);
		}
		if ((Object)(object)infoText != (Object)null)
		{
			((TMP_Text)infoText).text = "";
			((Component)infoText).gameObject.SetActive(false);
		}
		if ((Object)(object)meterImage != (Object)null)
		{
			meterImage.fillAmount = 0.2976f;
			((Graphic)meterImage).color = Color.green;
		}
	}

	[HarmonyPatch("Start")]
	[HarmonyPostfix]
	private static void StartPostfix()
	{
		initializedBrackens.Clear();
	}
}