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 ShowMeter; public static ConfigEntry ShowInfoText; public static ConfigEntry NextBrackenKey; public static ConfigEntry PrevBrackenKey; public static ConfigEntry EnableInitialAnger; public static ConfigEntry 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("UI", "Show Meter", true, "Show anger meter."); ShowInfoText = ((BaseUnityPlugin)this).Config.Bind("UI", "Show Info Text", true, "Show detailed information text."); NextBrackenKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "Next Bracken Key", (Key)67, "Switch to next Bracken."); PrevBrackenKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "Prev Bracken Key", (Key)66, "Switch to previous Bracken."); EnableInitialAnger = ((BaseUnityPlugin)this).Config.Bind("General", "Enable Initial Anger", false, "Apply the Initial Anger Meter Value."); InitialAngerMeterValue = ((BaseUnityPlugin)this).Config.Bind("General", "Initial Anger Meter Value", 0f, new ConfigDescription("Bracken anger meter applied when Anger builds up or detected.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 12f), Array.Empty())); } } [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 trackedBrackens = new Dictionary(); private static ulong? currentTargetId = null; private static Dictionary lastThreats = new Dictionary(); private static HashSet initializedBrackens = new HashSet(); private static List aliveBrackenIds = new List(); 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(); List 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 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.transform.parent); ((Object)meterObject).name = "BrackenAngerMeter"; backgroundmeterObject = Object.Instantiate(gameObject, gameObject.transform.parent); ((Object)backgroundmeterObject).name = "BrackenAngerMeterBackground"; backgroundmeterObject.transform.SetAsFirstSibling(); RectTransform component = gameObject.GetComponent(); RectTransform component2 = meterObject.GetComponent(); RectTransform component3 = backgroundmeterObject.GetComponent(); 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(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(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(HUDManager.Instance.weightCounter, ((TMP_Text)HUDManager.Instance.weightCounter).transform.parent); ((Object)infoText).name = "BrackenAngerText"; RectTransform component4 = ((Component)infoText).GetComponent(); 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(); } }