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<WeakReference<TextMeshProUGUI>> _labels = new List<WeakReference<TextMeshProUGUI>>();
internal static ConfigEntry<float> FontSize { get; private set; } = null;
internal static ConfigEntry<string> TextColor { get; private set; } = null;
internal static ConfigEntry<AnchorPreset> Position { get; private set; } = null;
internal static ConfigEntry<bool> ShowOnPlayer { get; private set; } = null;
internal static ConfigEntry<bool> ShowOnTarget { get; private set; } = null;
internal static ConfigEntry<float> 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<float>("Appearance", "FontSize", 8f, new ConfigDescription("Timer text size", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 24f), Array.Empty<object>()));
TextColor = config.Bind<string>("Appearance", "TextColor", "#FFFFFF", "Timer text color as HTML hex (e.g. #FFFFFF, #FFD700)");
Position = config.Bind<AnchorPreset>("Appearance", "Position", AnchorPreset.BottomCenter, "Where to display the timer within the icon");
ShowOnPlayer = config.Bind<bool>("Visibility", "ShowOnPlayer", true, "Show timers on your own buff/debuff icons");
ShowOnTarget = config.Bind<bool>("Visibility", "ShowOnTarget", true, "Show timers on target buff/debuff icons");
OverlayOpacity = config.Bind<float>("Appearance", "OverlayOpacity", 0f, new ConfigDescription("Darkness of the shadow behind the timer (0 = none, 1 = fully black)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
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<TextMeshProUGUI>(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<TextMeshProUGUI> wr) => !wr.TryGetTarget(out TextMeshProUGUI _));
foreach (WeakReference<TextMeshProUGUI> 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<StatusEffect, TickRecord> _tickRecords = new ConditionalWeakTable<StatusEffect, TickRecord>();
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<Image>()).color = new Color(0f, 0f, 0f, 0f);
RectTransform component = val.GetComponent<RectTransform>();
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<TextMeshProUGUI>();
((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<SpellLine, SpellLine>(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<Image>();
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<TextMeshProUGUI>();
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<TargetStatusIcon, StatusEffect> LiveEffects = new ConditionalWeakTable<TargetStatusIcon, StatusEffect>();
}
[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<TextMeshProUGUI>();
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);
}
}
}
}
}