using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Buff_Timers")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a0d73d031984068578c6541d86bc307c982e9d87")] [assembly: AssemblyProduct("Buff_Timers")] [assembly: AssemblyTitle("Buff_Timers")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 Ocewen.BuffTimers { public enum AnchorPreset { BottomCenter, BottomLeft, BottomRight, Center, TopCenter, TopLeft, TopRight } internal static class ModConfig { internal static float CachedFontSize = 8f; internal static Color CachedColor = Color.white; internal static AnchorPreset CachedPosition = AnchorPreset.BottomCenter; internal static float CachedOverlayOpacity = 0f; private static readonly List> _labels = new List>(); internal static ConfigEntry FontSize { get; private set; } = null; internal static ConfigEntry TextColor { get; private set; } = null; internal static ConfigEntry Position { get; private set; } = null; internal static ConfigEntry ShowOnPlayer { get; private set; } = null; internal static ConfigEntry ShowOnTarget { get; private set; } = null; internal static ConfigEntry OverlayOpacity { get; private set; } = null; internal static void Bind(ConfigFile config) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown FontSize = config.Bind("Appearance", "FontSize", 8f, new ConfigDescription("Timer text size", (AcceptableValueBase)(object)new AcceptableValueRange(4f, 24f), Array.Empty())); TextColor = config.Bind("Appearance", "TextColor", "#FFFFFF", "Timer text color as HTML hex (e.g. #FFFFFF, #FFD700)"); Position = config.Bind("Appearance", "Position", AnchorPreset.BottomCenter, "Where to display the timer within the icon"); ShowOnPlayer = config.Bind("Visibility", "ShowOnPlayer", true, "Show timers on your own buff/debuff icons"); ShowOnTarget = config.Bind("Visibility", "ShowOnTarget", true, "Show timers on target buff/debuff icons"); OverlayOpacity = config.Bind("Appearance", "OverlayOpacity", 0f, new ConfigDescription("Darkness of the shadow behind the timer (0 = none, 1 = fully black)", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ParseColor(); CachedFontSize = FontSize.Value; CachedPosition = Position.Value; CachedOverlayOpacity = OverlayOpacity.Value; FontSize.SettingChanged += delegate { CachedFontSize = FontSize.Value; RefreshAll(); }; TextColor.SettingChanged += delegate { ParseColor(); RefreshAll(); }; Position.SettingChanged += delegate { CachedPosition = Position.Value; RefreshAll(); }; OverlayOpacity.SettingChanged += delegate { CachedOverlayOpacity = OverlayOpacity.Value; }; } internal static void TrackLabel(TextMeshProUGUI label) { _labels.Add(new WeakReference(label)); } internal static void ApplyStyle(TextMeshProUGUI label) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) ((TMP_Text)label).fontSize = CachedFontSize; ((Graphic)label).color = CachedColor; ApplyAnchor(((TMP_Text)label).rectTransform, label, CachedPosition); } private static void RefreshAll() { _labels.RemoveAll((WeakReference wr) => !wr.TryGetTarget(out TextMeshProUGUI _)); foreach (WeakReference label in _labels) { if (label.TryGetTarget(out var target) && (Object)(object)target != (Object)null) { ApplyStyle(target); } } } private static void ParseColor() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) Color white = default(Color); if (!ColorUtility.TryParseHtmlString(TextColor.Value, ref white)) { white = Color.white; } CachedColor = white; } private static void ApplyAnchor(RectTransform rect, TextMeshProUGUI label, AnchorPreset preset) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: 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) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) var (anchorMin, anchorMax, alignment) = preset switch { AnchorPreset.BottomLeft => (new Vector2(0f, 0f), new Vector2(0.5f, 0.5f), (TextAlignmentOptions)1025), AnchorPreset.BottomRight => (new Vector2(0.5f, 0f), new Vector2(1f, 0.5f), (TextAlignmentOptions)1028), AnchorPreset.Center => (new Vector2(0f, 0.25f), new Vector2(1f, 0.75f), (TextAlignmentOptions)514), AnchorPreset.TopLeft => (new Vector2(0f, 0.5f), new Vector2(0.5f, 1f), (TextAlignmentOptions)257), AnchorPreset.TopCenter => (new Vector2(0f, 0.5f), new Vector2(1f, 1f), (TextAlignmentOptions)258), AnchorPreset.TopRight => (new Vector2(0.5f, 0.5f), new Vector2(1f, 1f), (TextAlignmentOptions)260), _ => (new Vector2(0f, 0f), new Vector2(1f, 0.5f), (TextAlignmentOptions)1026), }; rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; ((TMP_Text)label).alignment = alignment; } } [BepInPlugin("Ocewen.Buff_Timers", "Buff Timers", "1.2.1")] public class Plugin : BaseUnityPlugin { private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) ModConfig.Bind(((BaseUnityPlugin)this).Config); new Harmony("Ocewen.Buff_Timers").PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Buff Timers 1.2.1 loaded."); } } internal static class TimerFormatter { internal static string FormatTime(float seconds) { if (seconds <= 0f) { return ""; } if (seconds < 60f) { return $"{(int)Math.Ceiling(seconds)}s"; } if (seconds < 3600f) { return $"{(int)(seconds / 60f)}m"; } return $"{(int)(seconds / 3600f)}h"; } } internal static class TimerLabel { private sealed class TickRecord { internal float LastSeenDuration; internal float RecordedAt; } internal const string LabelName = "BuffTimerLabel"; internal const string OverlayName = "BuffTimerOverlay"; private static readonly ConditionalWeakTable _tickRecords = new ConditionalWeakTable(); internal static TextMeshProUGUI CreateLabel(GameObject parent) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_00a5: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("BuffTimerOverlay"); val.transform.SetParent(parent.transform, false); ((Graphic)val.AddComponent()).color = new Color(0f, 0f, 0f, 0f); RectTransform component = val.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; GameObject val2 = new GameObject("BuffTimerLabel"); val2.transform.SetParent(parent.transform, false); TextMeshProUGUI obj = val2.AddComponent(); ((TMP_Text)obj).outlineWidth = 0.2f; ((TMP_Text)obj).outlineColor = new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue); ModConfig.ApplyStyle(obj); ModConfig.TrackLabel(obj); return obj; } internal static void ClearLabel(TextMeshProUGUI label) { ((TMP_Text)label).text = ""; SetOverlayAlpha(label, 0f); } internal static void UpdateLabel(TextMeshProUGUI label, StatusEffect? effect) { if ((Object)(object)effect?.Effect == (Object)null || effect.Effect.SpellDurationInTicks == 0 || ((object)Unsafe.As(ref effect.Effect.Line)/*cast due to .constrained prefix*/).ToString().StartsWith("Aura_")) { ClearLabel(label); return; } ((TMP_Text)label).text = TimerFormatter.FormatTime(GetInterpolatedSeconds(effect)); SetOverlayAlpha(label, ModConfig.CachedOverlayOpacity); } private static void SetOverlayAlpha(TextMeshProUGUI label, float alpha) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((TMP_Text)label).transform.parent; Transform val = ((parent != null) ? parent.Find("BuffTimerOverlay") : null); if (!((Object)(object)val == (Object)null)) { Image component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { ((Graphic)component).color = new Color(0f, 0f, 0f, alpha); } } } private static float GetInterpolatedSeconds(StatusEffect effect) { float duration = effect.Duration; float unscaledTime = Time.unscaledTime; if (!_tickRecords.TryGetValue(effect, out TickRecord value)) { value = new TickRecord { LastSeenDuration = duration, RecordedAt = unscaledTime }; _tickRecords.Add(effect, value); } else if (value.LastSeenDuration != duration) { value.LastSeenDuration = duration; value.RecordedAt = unscaledTime; } return Math.Max(0f, duration * 3f - (unscaledTime - value.RecordedAt)); } } } namespace Ocewen.BuffTimers.Patches { [HarmonyPatch(typeof(StatusEffectIcon), "Start")] internal static class StatusEffectIcon_Start_Patch { [HarmonyPostfix] private static void Postfix(StatusEffectIcon __instance) { TimerLabel.CreateLabel(((Component)__instance).gameObject); } } [HarmonyPatch(typeof(StatusEffectIcon), "Update")] internal static class StatusEffectIcon_Update_Patch { [HarmonyPostfix] private static void Postfix(StatusEffectIcon __instance) { Transform val = ((Component)__instance).transform.Find("BuffTimerLabel"); if ((Object)(object)val == (Object)null) { return; } TextMeshProUGUI component = ((Component)val).GetComponent(); if ((Object)(object)component == (Object)null) { return; } if (!ModConfig.ShowOnPlayer.Value || !((Behaviour)__instance.Icon).enabled) { TimerLabel.ClearLabel(component); return; } Stats readStats = __instance.ReadStats; object obj; if (readStats == null) { obj = null; } else { StatusEffect[] statusEffects = readStats.StatusEffects; obj = ((statusEffects != null) ? statusEffects[__instance.SlotIndex] : null); } StatusEffect effect = (StatusEffect)obj; TimerLabel.UpdateLabel(component, effect); } } internal static class TargetStatusIconState { internal static readonly ConditionalWeakTable LiveEffects = new ConditionalWeakTable(); } [HarmonyPatch(typeof(TargetStatusIcon), "Awake")] internal static class TargetStatusIcon_Awake_Patch { [HarmonyPostfix] private static void Postfix(TargetStatusIcon __instance) { TimerLabel.CreateLabel(((Component)__instance).gameObject); } } [HarmonyPatch(typeof(TargetStatusIcon), "PopulateData")] internal static class TargetStatusIcon_PopulateData_Patch { [HarmonyPostfix] private static void Postfix(TargetStatusIcon __instance, StatusEffect _SE) { TargetStatusIconState.LiveEffects.Remove(__instance); TargetStatusIconState.LiveEffects.Add(__instance, _SE); } } [HarmonyPatch(typeof(TargetStatusIcon), "Update")] internal static class TargetStatusIcon_Update_Patch { [HarmonyPostfix] private static void Postfix(TargetStatusIcon __instance) { Transform val = ((Component)__instance).transform.Find("BuffTimerLabel"); if ((Object)(object)val == (Object)null) { return; } TextMeshProUGUI component = ((Component)val).GetComponent(); if (!((Object)(object)component == (Object)null)) { if (!ModConfig.ShowOnTarget.Value || __instance.IsEmpty() || !TargetStatusIconState.LiveEffects.TryGetValue(__instance, out StatusEffect value)) { TimerLabel.ClearLabel(component); } else { TimerLabel.UpdateLabel(component, value); } } } } }