Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CombatStats v1.0.8
plugins/Tenemo.CombatStats.dll
Decompiled 16 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CombatStats.Collect; using CombatStats.Model; using CombatStats.Net; using CombatStats.Stats; using CombatStats.Ui; using HarmonyLib; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyVersion("0.0.0.0")] [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 CombatStats { internal sealed class ModConfig { public readonly ConfigEntry<KeyboardShortcut> CompactKey; public readonly ConfigEntry<KeyboardShortcut> DetailKey; public readonly ConfigEntry<MeterMode> CompactMode; public readonly ConfigEntry<int> CompactRows; public readonly ConfigEntry<ScreenAnchor> CompactAnchor; public readonly ConfigEntry<Vector2> CompactOffset; public readonly ConfigEntry<Vector2> DetailSize; public readonly ConfigEntry<float> CompactWidth; public readonly ConfigEntry<float> CompactScale; public readonly ConfigEntry<float> CompactOpacity; public readonly ConfigEntry<bool> CompactCaption; public readonly ConfigEntry<bool> CompactShare; public readonly ConfigEntry<bool> CompactBars; public readonly ConfigEntry<bool> IsDebug; public readonly ConfigEntry<string> Colors; private readonly ConfigFile config; private readonly SortedDictionary<string, SortedDictionary<string, string>> personal = new SortedDictionary<string, SortedDictionary<string, string>>(); private readonly string path; private bool writable = true; public ModConfig(ConfigFile config) { //IL_003c: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_021f: Unknown result type (might be due to invalid IL or missing references) this.config = config; config.SaveOnConfigSet = false; CompactKey = config.Bind<KeyboardShortcut>("Compact", "CompactKey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Cycle compact meter visibility."); DetailKey = config.Bind<KeyboardShortcut>("Detail", "DetailKey", new KeyboardShortcut((KeyCode)292, Array.Empty<KeyCode>()), "Open fight details."); CompactMode = config.Bind<MeterMode>("Compact", "CompactMode", MeterMode.Off, "Auto shows the running or latest fight; Always also shows an empty meter; Off hides it."); CompactRows = config.Bind<int>("Compact", "CompactRows", 5, new ConfigDescription("Visible rows, including your own.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 12), Array.Empty<object>())); CompactAnchor = config.Bind<ScreenAnchor>("Compact", "CompactAnchor", ScreenAnchor.TopRight, "Screen corner or edge."); CompactOffset = config.Bind<Vector2>("Compact", "CompactOffset", new Vector2(28f, 164f), "Distance in pixels from the chosen corner towards the middle of the screen; middle edges centre the meter vertically."); CompactWidth = config.Bind<float>("Compact", "CompactWidth", 360f, new ConfigDescription("Width in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(260f, 700f), Array.Empty<object>())); CompactScale = config.Bind<float>("Compact", "CompactScale", 1f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.75f, 2f), Array.Empty<object>())); CompactOpacity = config.Bind<float>("Compact", "CompactOpacity", 0.9f, new ConfigDescription("", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>())); CompactCaption = config.Bind<bool>("Compact", "CompactCaption", true, "Show the fight's name, duration and result."); CompactShare = config.Bind<bool>("Compact", "CompactShare", true, "Show contribution percentages."); CompactBars = config.Bind<bool>("Compact", "CompactBars", true, "Show damage-type bars."); DetailSize = config.Bind<Vector2>("Detail", "DetailSize", new Vector2(700f, 480f), "Width and height in pixels."); Colors = config.Bind<string>("Colours", "Colors", "", "Damage-type overrides, such as Fire=#ff7733."); IsDebug = config.Bind<bool>("General", "IsDebug", false, "Enable diagnostic logging."); path = Path.Combine(Paths.BepInExRootPath, "combat-stats-state", "settings.dat"); LoadPersonal(); config.SettingChanged += delegate(object _, SettingChangedEventArgs change) { Remember(change.ChangedSetting.Definition, change.ChangedSetting.GetSerializedValue()); Save(); }; } private void LoadPersonal() { //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown string problem; string text = Storage.Read(path, out problem); if (problem != null) { writable = false; Plugin.Log.LogError((object)(problem + ". Display changes are not saved this session.")); } if (text == null) { return; } string text2 = null; bool flag = false; string[] array = text.Split('\n'); for (int i = 0; i < array.Length; i++) { string text3 = array[i].Trim(); if (text3.Length == 0 || text3.StartsWith("#")) { continue; } if (text3.StartsWith("[") && text3.EndsWith("]")) { text2 = text3.Substring(1, text3.Length - 2); continue; } string[] array2 = text3.Split(new char[1] { '=' }, 2); if (text2 != null && array2.Length == 2) { ConfigDefinition val = new ConfigDefinition(text2, array2[0].Trim()); if (text2 == "General" && val.Key == "Enabled" && bool.TryParse(array2[1].Trim(), out var result)) { flag = !result; } if (config.ContainsKey(val)) { config[val].SetSerializedValue(array2[1].Trim()); Remember(val, config[val].GetSerializedValue()); } } } if (flag) { CompactMode.Value = MeterMode.Off; Remember(((ConfigEntryBase)CompactMode).Definition, ((ConfigEntryBase)CompactMode).GetSerializedValue()); } Vector2 value = CompactOffset.Value; if (value.x < 0f || value.y < 0f) { CompactOffset.Value = new Vector2(Mathf.Abs(value.x), Mathf.Abs(value.y)); Remember(((ConfigEntryBase)CompactOffset).Definition, ((ConfigEntryBase)CompactOffset).GetSerializedValue()); } } private void Remember(ConfigDefinition definition, string value) { if (!personal.TryGetValue(definition.Section, out var value2)) { value2 = (personal[definition.Section] = new SortedDictionary<string, string>()); } value2[definition.Key] = value; } private void Save() { if (!writable) { return; } StringBuilder stringBuilder = new StringBuilder("## Personal combat meter display settings. Gale does not replace this file.\n"); foreach (KeyValuePair<string, SortedDictionary<string, string>> item in personal) { stringBuilder.Append("\n[").Append(item.Key).Append("]\n"); foreach (KeyValuePair<string, string> item2 in item.Value) { stringBuilder.Append(item2.Key).Append(" = ").Append(item2.Value) .Append('\n'); } } string text = Storage.Write(path, stringBuilder.ToString()); if (text != null) { Plugin.Log.LogWarning((object)text); } } public Color[] ParseColors() { //IL_0089: 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) Color[] array = (Color[])(object)new Color[11]; for (int i = 0; i < array.Length; i++) { ColorUtility.TryParseHtmlString(DamageKinds.ColorHex((DamageKind)i), ref array[i]); } string[] array2 = Colors.Value.Split(','); Color val = default(Color); for (int j = 0; j < array2.Length; j++) { string[] array3 = array2[j].Split('='); if (array3.Length == 2 && Enum.TryParse<DamageKind>(array3[0].Trim(), ignoreCase: true, out var result) && result >= DamageKind.Blunt && (int)result < array.Length && ColorUtility.TryParseHtmlString(array3[1].Trim(), ref val)) { array[(int)result] = val; } } return array; } } [BepInPlugin("tenemo.CombatStats", "Combat stats", "1.0.8")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("muindor.CombatStats")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Id = "tenemo.CombatStats"; public const string Version = "1.0.8"; internal static ManualLogSource Log; internal static ModConfig Settings; internal static DamageCollector Collector; internal static CombatHistory History = new CombatHistory(); internal static FightSync Sync; private Harmony harmony; private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); Collector = new DamageCollector(); harmony = new Harmony("tenemo.CombatStats"); try { harmony.PatchAll(typeof(Plugin).Assembly); } catch { harmony.UnpatchSelf(); throw; } Sync = ((Component)this).gameObject.AddComponent<FightSync>(); if ((int)SystemInfo.graphicsDeviceType != 4) { ((Component)this).gameObject.AddComponent<CompactMeter>(); ((Component)this).gameObject.AddComponent<DetailWindow>(); } Log.LogInfo((object)"Combat stats 1.0.8 loaded: shared fights, whole-fight DPS and effect attribution."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } internal static void Debug(string text) { if (Settings.IsDebug.Value) { Log.LogInfo((object)text); } } } internal static class Shortcut { public static bool IsPressed(KeyboardShortcut shortcut) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!IsTyping()) { return WentDown(shortcut); } return false; } public static bool IsTyping() { if (!Console.IsVisible() && !TextInput.IsVisible()) { if ((Object)(object)Chat.instance != (Object)null) { return Chat.instance.HasFocus(); } return false; } return true; } private static bool WentDown(KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, false)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { if (!ZInput.GetKey(modifier, false)) { return false; } } return true; } } internal static class Storage { internal static string Read(string path, out string problem) { problem = null; try { return File.Exists(path) ? File.ReadAllText(path) : null; } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { problem = "Could not read " + path + ": " + ex.Message; return null; } } internal static string Write(string path, string text) { try { Directory.CreateDirectory(Path.GetDirectoryName(path)); string text2 = path + ".tmp"; File.WriteAllText(text2, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); if (File.Exists(path)) { File.Replace(text2, path, null); } else { File.Move(text2, path); } return null; } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException) { return "Could not write " + path + ": " + ex.Message; } } } } namespace CombatStats.Patches { internal static class Windows { public static bool AnyOpen { get { if (!DetailWindow.IsOpen) { return SettingsPanel.IsOpen; } return true; } } } [HarmonyPatch(typeof(Player), "TakeInput")] internal static class BlockPlayerInput { private static void Postfix(ref bool __result) { if (Windows.AnyOpen) { __result = false; } } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] internal static class BlockControllerInput { private static void Postfix(ref bool __result) { if (Windows.AnyOpen) { __result = false; } } } [HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")] internal static class CaptureScrollWheel { private static void Postfix(ref float __result) { if (__result != 0f && DetailWindow.IsOpen) { DetailWindow.FeedScrollWheel(__result); __result = 0f; } } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] internal static class KeepCursorFree { private static bool Prefix() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!Windows.AnyOpen) { return true; } if ((int)Cursor.lockState != 0) { ZCursor.LockState = (CursorLockMode)0; } ZCursor.Show(); return false; } } [HarmonyPatch(typeof(Menu), "Update")] internal static class CloseWindowsOnEscape { private static bool Prefix() { if (!Windows.AnyOpen || Menu.IsVisible()) { return true; } if (!ZInput.GetKeyDown((KeyCode)27, false)) { return true; } if (SettingsPanel.IsOpen) { SettingsPanel.Instance?.Close(); return false; } DetailWindow.Instance?.Close(); return false; } } } namespace CombatStats.Net { internal sealed class PlayerIdentity { public long Id; public string Name = ""; public string Character = ""; public float X; public float Y; public float Z; public double Sent; } internal sealed class KnownPlayer { public long Id; public string Name = ""; public string Character = ""; } internal sealed class ClockReply { public string Session = ""; public double Echo; public double Time; } internal sealed class CombatBatch { public string Session = ""; public List<Observation> Events = new List<Observation>(); } internal sealed class CombatFeed { public string Session = ""; public double Time; public List<Encounter> Fights = new List<Encounter>(); public Dictionary<long, long> Redirects = new Dictionary<long, long>(); public List<KnownPlayer> Players = new List<KnownPlayer>(); } internal sealed class FightSync : MonoBehaviour { private const string HelloRpc = "Tenemo.CombatStats.Hello.1"; private const string ClockRpc = "Tenemo.CombatStats.Clock.1"; private const string EventsRpc = "Tenemo.CombatStats.Events.1"; private const string FeedRpc = "Tenemo.CombatStats.Feed.1"; private static readonly Stopwatch Clock = Stopwatch.StartNew(); private static readonly Func<ZRoutedRpc, long> ServerPeerOf = AccessTools.MethodDelegate<Func<ZRoutedRpc, long>>(AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null), (object)null, true); private static readonly JsonSerializerSettings Json = new JsonSerializerSettings { MaxDepth = 20 }; private static double offset; private ZRoutedRpc bound; private readonly List<Observation> pending = new List<Observation>(); private readonly Dictionary<long, PlayerIdentity> players = new Dictionary<long, PlayerIdentity>(); private List<KnownPlayer> known = new List<KnownPlayer>(); private readonly Dictionary<long, Dictionary<long, long>> published = new Dictionary<long, Dictionary<long, long>>(); private EncounterRecorder recorder = new EncounterRecorder(); private string session = ""; private string historyPath = ""; private long sequence; private double nextSend; private double nextHello; private bool synchronized; internal static double Now => Clock.Elapsed.TotalSeconds + offset; internal void Queue(Observation report) { if (bound != null) { report.Sequence = ++sequence; pending.Add(report); } } internal DamageCollector.Source FindPlayer(string name) { if (string.IsNullOrEmpty(name)) { return null; } List<KnownPlayer> list = known.Where((KnownPlayer x) => x.Name == name).ToList(); if (list.Count != 1) { return null; } return Source(list[0]); } internal DamageCollector.Source FindCharacter(string character) { KnownPlayer knownPlayer = known.FirstOrDefault((KnownPlayer x) => x.Character == character); if (knownPlayer != null) { return Source(knownPlayer); } return null; } private static DamageCollector.Source Source(KnownPlayer player) { return new DamageCollector.Source { Id = player.Id, Name = player.Name }; } private void Update() { //IL_0111: 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_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) ZNet instance = ZNet.instance; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (!Object.op_Implicit((Object)(object)instance) || instance2 == null) { if (bound != null) { EndSession(); } return; } if (bound != instance2) { Bind(instance2, instance); } if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { string text = Path.Combine(Paths.BepInExRootPath, "combat-stats-state", "world-" + instance.GetWorldUID() + "-player-" + DamageCollector.LocalId() + ".dat"); if (text != historyPath) { OpenHistory(text); } if (Clock.Elapsed.TotalSeconds >= nextHello) { nextHello = Clock.Elapsed.TotalSeconds + (synchronized ? 2.0 : 0.25); Player localPlayer = Player.m_localPlayer; Vector3 position = ((Component)localPlayer).transform.position; PlayerIdentity value = new PlayerIdentity { Id = localPlayer.GetPlayerID(), Name = localPlayer.GetPlayerName(), Character = DamageCollector.Key((Character)(object)localPlayer), Sent = Clock.Elapsed.TotalSeconds, X = position.x, Y = position.y, Z = position.z }; if (instance.IsServer()) { players[ZNet.GetUID()] = value; } else { instance2.InvokeRoutedRPC(ServerPeerOf(instance2), "Tenemo.CombatStats.Hello.1", new object[1] { Encode(value) }); } } } if (!(Clock.Elapsed.TotalSeconds < nextSend)) { nextSend = Clock.Elapsed.TotalSeconds + 0.25; Plugin.Collector.Engagement(); Flush(instance, instance2); if (instance.IsServer()) { recorder.Tick(Now); Publish(instance, instance2); } else if (synchronized) { Plugin.History.Now = Now; } } } private void Bind(ZRoutedRpc rpc, ZNet net) { EndSession(); bound = rpc; session = (net.IsServer() ? Guid.NewGuid().ToString("N") : ""); synchronized = net.IsServer(); offset = 0.0; rpc.Register<string>("Tenemo.CombatStats.Hello.1", (Action<long, string>)Hello); rpc.Register<string>("Tenemo.CombatStats.Clock.1", (Action<long, string>)SetClock); rpc.Register<string>("Tenemo.CombatStats.Events.1", (Action<long, string>)Events); rpc.Register<string>("Tenemo.CombatStats.Feed.1", (Action<long, string>)Feed); Plugin.Debug("Fight sync registered for a new world."); } private void EndSession() { CloseHistory(); recorder = new EncounterRecorder(); pending.Clear(); players.Clear(); known.Clear(); published.Clear(); sequence = 0L; bound = null; synchronized = false; nextHello = 0.0; nextSend = 0.0; Plugin.Collector?.Clear(); DetailWindow.Instance?.Close(); } private void Hello(long sender, string json) { if (ZNet.instance.IsServer()) { PlayerIdentity playerIdentity = Decode<PlayerIdentity>(json); ZNetPeer peer = ZNet.instance.GetPeer(sender); if (playerIdentity != null && peer != null && playerIdentity.Id != 0L && playerIdentity.Name != null && playerIdentity.Name.Length <= 160 && !(playerIdentity.Character != ((object)Unsafe.As<ZDOID, ZDOID>(ref peer.m_characterID)/*cast due to .constrained prefix*/).ToString())) { players[sender] = playerIdentity; bound.InvokeRoutedRPC(sender, "Tenemo.CombatStats.Clock.1", new object[1] { Encode(new ClockReply { Session = session, Echo = playerIdentity.Sent, Time = Now }) }); } } } private void SetClock(long sender, string json) { if (ZNet.instance.IsServer() || sender != ServerPeerOf(bound)) { return; } ClockReply clockReply = Decode<ClockReply>(json); if (clockReply == null || string.IsNullOrEmpty(clockReply.Session)) { return; } double totalSeconds = Clock.Elapsed.TotalSeconds; double num = totalSeconds - clockReply.Echo; if (num < 0.0 || num > 5.0 || double.IsNaN(clockReply.Time) || double.IsInfinity(clockReply.Time)) { return; } double num2 = clockReply.Time + num * 0.5 - totalSeconds; foreach (Observation item in pending) { item.Time += num2 - offset; } offset = num2; session = clockReply.Session; synchronized = true; } private void Flush(ZNet net, ZRoutedRpc rpc) { if (pending.Count == 0 || (!net.IsServer() && !synchronized)) { return; } while (pending.Count > 0) { CombatBatch combatBatch = new CombatBatch { Session = session, Events = pending.Take(128).ToList() }; pending.RemoveRange(0, combatBatch.Events.Count); if (net.IsServer()) { recorder.Receive(ZNet.GetUID(), combatBatch.Events, Now); continue; } rpc.InvokeRoutedRPC(ServerPeerOf(rpc), "Tenemo.CombatStats.Events.1", new object[1] { Encode(combatBatch) }); } } private void Events(long sender, string json) { if (ZNet.instance.IsServer() && players.ContainsKey(sender) && ZNet.instance.GetPeer(sender) != null) { CombatBatch combatBatch = Decode<CombatBatch>(json); if (combatBatch != null && !(combatBatch.Session != session) && combatBatch.Events != null && combatBatch.Events.Count <= 128 && !combatBatch.Events.Any((Observation x) => x == null)) { recorder.Receive(sender, combatBatch.Events, Now); } } } private void Publish(ZNet net, ZRoutedRpc rpc) { HashSet<long> connected = new HashSet<long>(from x in net.GetPeers() select x.m_uid) { ZNet.GetUID() }; foreach (long item in players.Keys.Where((long x) => !connected.Contains(x)).ToList()) { players.Remove(item); published.Remove(item); recorder.ForgetPeer(item); } known = players.Values.Select((PlayerIdentity x) => new KnownPlayer { Id = x.Id, Name = x.Name, Character = x.Character }).ToList(); foreach (KeyValuePair<long, PlayerIdentity> item2 in players.ToList()) { PlayerIdentity player = item2.Value; if (!published.TryGetValue(item2.Key, out var revisions)) { published[item2.Key] = (revisions = new Dictionary<long, long>()); } long value; CombatFeed combatFeed = new CombatFeed { Session = session, Time = Now, Redirects = recorder.Redirects, Players = known, Fights = recorder.Fights.Where((Encounter x) => (x.Participants.ContainsKey(player.Id) || revisions.ContainsKey(x.Id) || (x.Active && EncounterRecorder.Distance(x.X, x.Y, x.Z, player.X, player.Y, player.Z) <= 9216f)) && (x.Active || !revisions.TryGetValue(x.Id, out value) || value != x.Revision)).ToList() }; string text = Encode(combatFeed); if (item2.Key == ZNet.GetUID()) { AcceptFeed(Decode<CombatFeed>(text)); } else { rpc.InvokeRoutedRPC(item2.Key, "Tenemo.CombatStats.Feed.1", new object[1] { text }); } foreach (Encounter fight in combatFeed.Fights) { revisions[fight.Id] = fight.Revision; } foreach (long item3 in revisions.Keys.Where((long x) => recorder.Fights.All((Encounter f) => f.Id != x)).ToList()) { revisions.Remove(item3); } } } private void Feed(long sender, string json) { if (!ZNet.instance.IsServer() && sender == ServerPeerOf(bound) && synchronized) { CombatFeed combatFeed = Decode<CombatFeed>(json); if (combatFeed != null && !(combatFeed.Session != session)) { AcceptFeed(combatFeed); } } } private void AcceptFeed(CombatFeed feed) { if (feed != null && historyPath.Length != 0) { if (!ZNet.instance.IsServer()) { known = feed.Players ?? new List<KnownPlayer>(); } Plugin.History.Receive(feed.Session, feed.Time, feed.Fights, feed.Redirects); } } private void OpenHistory(string path) { CloseHistory(); historyPath = path; Plugin.History = CombatHistory.Load(path, out var problem); if (problem != null) { Plugin.Log.LogError((object)problem); } Plugin.History.Changed += SaveHistory; } private void CloseHistory() { SaveHistory(); Plugin.History.Changed -= SaveHistory; Plugin.History = new CombatHistory(); historyPath = ""; } private static void SaveHistory() { string text = Plugin.History.Save(); if (text != null) { Plugin.Log.LogWarning((object)text); } } internal static string Encode(object value) { return JsonConvert.SerializeObject(value, Json); } private static T Decode<T>(string value) where T : class { //IL_002a: Expected O, but got Unknown if (value == null || value.Length > 1048576) { return null; } try { return JsonConvert.DeserializeObject<T>(value, Json); } catch (JsonException ex) { JsonException ex2 = ex; Plugin.Log.LogWarning((object)("Invalid combat packet: " + ((Exception)(object)ex2).Message)); return null; } } } } namespace CombatStats.Collect { internal sealed class DamageCollector { internal sealed class Source { internal long Id; internal string Name = ""; internal string Pet = ""; } internal sealed class DamageState { internal float Health; internal bool Valid; internal float[] Raw; } internal sealed class DotState { internal float Before; internal float Amount; internal int Kind; } internal sealed class RpcContext { internal Character Target; internal HitData Hit; } private sealed class EffectKinds { internal readonly HashSet<int> Values = new HashSet<int>(); } private sealed class Tracked { internal Character Character; internal bool Reported; internal long Actor; internal double Sent; } private readonly Dictionary<string, Tracked> tracked = new Dictionary<string, Tracked>(); private readonly ConditionalWeakTable<Character, Source> petOwners = new ConditionalWeakTable<Character, Source>(); private readonly ConditionalWeakTable<StatusEffect, EffectKinds> effectKinds = new ConditionalWeakTable<StatusEffect, EffectKinds>(); internal static RpcContext CurrentHit; internal static StatusEffect CurrentEffect; private static readonly FieldRef<StatusEffect, Character> EffectCharacter = AccessTools.FieldRefAccess<StatusEffect, Character>("m_character"); private static readonly FieldRef<SE_Burning, float> FireLeft = AccessTools.FieldRefAccess<SE_Burning, float>("m_fireDamageLeft"); private static readonly FieldRef<SE_Burning, float> SpiritLeft = AccessTools.FieldRefAccess<SE_Burning, float>("m_spiritDamageLeft"); private static readonly FieldRef<SE_Poison, float> PoisonLeft = AccessTools.FieldRefAccess<SE_Poison, float>("m_damageLeft"); private static readonly FieldRef<AnimalAI, Character> AnimalTarget = AccessTools.FieldRefAccess<AnimalAI, Character>("m_target"); internal static long LocalId() { if (!Object.op_Implicit((Object)(object)Player.m_localPlayer)) { return 0L; } return Player.m_localPlayer.GetPlayerID(); } internal static string Key(Character character) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) return ((object)character.GetZDOID()/*cast due to .constrained prefix*/).ToString(); } internal static string Name(Character character) { Player val = (Player)(object)((character is Player) ? character : null); if (val == null) { return character.m_name; } return val.GetPlayerName(); } internal static bool Owns(Character character) { if (Object.op_Implicit((Object)(object)character)) { return character.IsOwner(); } return false; } private Source Actor(Character actor) { if (!Object.op_Implicit((Object)(object)actor)) { return new Source(); } Player val = (Player)(object)((actor is Player) ? actor : null); if (val != null) { return new Source { Id = val.GetPlayerID(), Name = val.GetPlayerName() }; } if (!actor.IsTamed()) { return new Source(); } BaseAI baseAI = actor.GetBaseAI(); MonsterAI val2 = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); GameObject val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetFollowTarget() : null); Player val4 = (Object.op_Implicit((Object)(object)val3) ? val3.GetComponent<Player>() : null); if (Object.op_Implicit((Object)(object)val4)) { Source source = new Source { Id = val4.GetPlayerID(), Name = val4.GetPlayerName(), Pet = Name(actor) }; petOwners.Remove(actor); petOwners.Add(actor, source); return source; } if (petOwners.TryGetValue(actor, out var value)) { return value; } ZNetView component = ((Component)actor).GetComponent<ZNetView>(); string name = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO().GetString("follow", "") : ""); Source source2 = Plugin.Sync.FindPlayer(name); if (source2 != null) { source2.Pet = Name(actor); petOwners.Add(actor, source2); return source2; } return new Source { Id = Combatant.UnownedPetId((uint)StringExtensionMethods.GetStableHashCode(Key(actor))), Name = Name(actor), Pet = Name(actor) }; } private Observation Create(Character target, HitData hit) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (!Owns(target) || (!target.IsPlayer() && target.IsTamed())) { return null; } Character val = ((hit != null) ? hit.GetAttacker() : null); if (Object.op_Implicit((Object)(object)val) && val.IsPlayer() && target.IsPlayer()) { return null; } Source source = Actor(val); if (!Object.op_Implicit((Object)(object)val) && hit != null && !((ZDOID)(ref hit.m_attacker)).IsNone()) { Source source2 = Plugin.Sync.FindCharacter(((object)Unsafe.As<ZDOID, ZDOID>(ref hit.m_attacker)/*cast due to .constrained prefix*/).ToString()); if (source2 != null) { source = source2; } } Vector3 position = ((Component)target).transform.position; Observation obj = new Observation { Time = FightSync.Now, Target = Key(target), TargetName = Name(target), Actor = source.Id, ActorName = source.Name, Pet = source.Pet, Skill = SkillOf(hit, val) }; Player val2 = (Player)(object)((target is Player) ? target : null); obj.Victim = ((val2 != null) ? val2.GetPlayerID() : 0); obj.X = position.x; obj.Y = position.y; obj.Z = position.z; obj.Boss = target.IsBoss(); Observation observation = obj; if (!target.IsPlayer()) { Track(observation.Target, target); } else if (Object.op_Implicit((Object)(object)val) && !val.IsPlayer() && !val.IsTamed()) { observation.Enemy = Key(val); observation.EnemyName = Name(val); observation.Boss = val.IsBoss(); if (val.IsOwner()) { Track(observation.Enemy, val); } } return observation; } private static string SkillOf(HitData hit, Character attacker) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (hit == null) { return "Other"; } if ((int)hit.m_skill != 0) { return ((object)Unsafe.As<SkillType, SkillType>(ref hit.m_skill)/*cast due to .constrained prefix*/).ToString(); } if (!Object.op_Implicit((Object)(object)attacker) || attacker.IsPlayer()) { return "Other"; } return "Creature attack"; } private void Track(string key, Character character) { if (!tracked.TryGetValue(key, out var value) || (Object)(object)value.Character != (Object)(object)character) { tracked[key] = new Tracked { Character = character }; } } internal void Activity(Character target, HitData hit) { if (Object.op_Implicit((Object)(object)target) && !target.IsDead() && !target.IsTeleporting() && hit != null) { Observation observation = Create(target, hit); if (observation != null && (observation.Actor != 0L || observation.Enemy.Length != 0)) { observation.Kind = ObservationKind.Activity; Plugin.Sync.Queue(observation); } } } internal void TrackAttacker(Character target, HitData hit) { if (Object.op_Implicit((Object)(object)target) && hit != null && (target.IsPlayer() || target.IsTamed())) { Character attacker = hit.GetAttacker(); if (Object.op_Implicit((Object)(object)attacker) && !attacker.IsPlayer() && !attacker.IsTamed() && attacker.IsOwner()) { Track(Key(attacker), attacker); } } } internal void Applied(Character target, HitData hit, DamageState state) { if (!state.Valid || hit == null) { return; } Observation observation = Create(target, hit); if (observation == null) { return; } float[] array = Values(hit); float num = array.Sum(); if (num <= 0.1f) { return; } float num2 = Math.Min(num, Math.Max(0f, state.Health)); float num3 = num2 / num; for (int i = 0; i < array.Length; i++) { array[i] *= num3; } observation.Kind = ObservationKind.Damage; observation.Amounts = array; observation.Overkill = num - num2; observation.Periodic = CurrentEffect is SE_Burning || CurrentEffect is SE_Poison; if (observation.Actor != 0L || observation.Victim != 0L || observation.Periodic) { if (observation.Periodic) { observation.Consumed = state.Raw; } Plugin.Sync.Queue(observation); } } internal void Death(Character target) { Observation observation = Create(target, null); if (observation != null) { observation.Kind = ObservationKind.Death; Plugin.Sync.Queue(observation); tracked.Remove(observation.Target); } } internal void Healed(Character target, float before) { if (!Owns(target)) { return; } Player val = (Player)(object)((target is Player) ? target : null); if (val != null) { float num = target.GetHealth() - before; if (!(num <= 0f)) { Observation observation = Create(target, null); observation.Kind = ObservationKind.Healing; observation.ActorName = val.GetPlayerName(); observation.Amounts[10] = num; Plugin.Sync.Queue(observation); } } } private Source EngagedActor(Character target) { BaseAI baseAI = target.GetBaseAI(); if (!Object.op_Implicit((Object)(object)baseAI) || !baseAI.IsAlerted()) { return null; } AnimalAI val = (AnimalAI)(object)((baseAI is AnimalAI) ? baseAI : null); Character val2 = ((val != null) ? AnimalTarget.Invoke(val) : baseAI.GetTargetCreature()); if (!Object.op_Implicit((Object)(object)val2) || val2.IsDead()) { return null; } Source source = Actor(val2); if (source.Id == 0L) { return null; } return source; } internal void Engagement() { foreach (Character allCharacter in Character.GetAllCharacters()) { if (Owns(allCharacter) && !allCharacter.IsPlayer() && !allCharacter.IsTamed() && !allCharacter.IsDead()) { string key = Key(allCharacter); if (!tracked.ContainsKey(key) && EngagedActor(allCharacter) != null) { Track(key, allCharacter); } } } double now = FightSync.Now; foreach (KeyValuePair<string, Tracked> item in tracked.ToList()) { Character character = item.Value.Character; if (!Owns(character) || character.IsTamed()) { tracked.Remove(item.Key); continue; } if (character.IsDead()) { Death(character); continue; } Source source = EngagedActor(character); long num = source?.Id ?? 0; if (!item.Value.Reported || num != item.Value.Actor || !(now - item.Value.Sent < 1.0)) { item.Value.Reported = true; item.Value.Actor = num; item.Value.Sent = now; Observation observation = Create(character, null); observation.Kind = ObservationKind.Engagement; observation.Actor = num; observation.ActorName = source?.Name ?? ""; observation.Engaged = source != null; Plugin.Sync.Queue(observation); if (!observation.Engaged) { tracked.Remove(item.Key); } } } } internal DotState BeforeDot(StatusEffect effect, float amount, int kind) { return new DotState { Before = Remaining(effect, kind), Amount = amount, Kind = kind }; } private static float Remaining(StatusEffect effect, int kind) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown SE_Poison val = (SE_Poison)(object)((effect is SE_Poison) ? effect : null); if (val == null) { if (kind != 5) { return SpiritLeft.Invoke((SE_Burning)effect); } return FireLeft.Invoke((SE_Burning)effect); } return PoisonLeft.Invoke(val); } internal void AddedDot(StatusEffect effect, DotState state, bool accepted) { Character val = EffectCharacter.Invoke(effect); if (accepted && Owns(val)) { HitData hit = ((CurrentHit != null && (Object)(object)CurrentHit.Target == (Object)(object)val) ? CurrentHit.Hit : null); Observation observation = Create(val, hit); if (observation != null) { observation.Kind = ObservationKind.DotAdded; observation.DotKind = state.Kind; observation.DotBefore = state.Before; observation.ReplaceDot = effect is SE_Poison; observation.DotAmount = (observation.ReplaceDot ? Remaining(effect, state.Kind) : Math.Max(0f, Remaining(effect, state.Kind) - state.Before)); effectKinds.GetOrCreateValue(effect).Values.Add(state.Kind); Plugin.Sync.Queue(observation); } } } internal void RemovedDot(StatusEffect effect) { if (!Object.op_Implicit((Object)(object)effect) || !effectKinds.TryGetValue(effect, out var value)) { return; } Character target = EffectCharacter.Invoke(effect); foreach (int value2 in value.Values) { Observation observation = Create(target, null); if (observation != null) { observation.Kind = ObservationKind.DotCleared; observation.DotKind = value2; Plugin.Sync.Queue(observation); } } effectKinds.Remove(effect); } internal void Clear() { tracked.Clear(); } internal static float[] Values(HitData hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) DamageTypes damage = hit.m_damage; return new float[11] { damage.m_blunt, damage.m_slash, damage.m_pierce, damage.m_chop, damage.m_pickaxe, damage.m_fire, damage.m_frost, damage.m_lightning, damage.m_poison, damage.m_spirit, Math.Max(0f, damage.m_damage) + Math.Max(0f, damage.m_nonPlayer) }.Select((float x) => Math.Max(0f, x)).ToArray(); } } [HarmonyPatch(typeof(Character), "Damage")] internal static class ObserveOutgoing { private static void Prefix(Character __instance, HitData hit) { Plugin.Collector.TrackAttacker(__instance, hit); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class ObserveContact { private static void Prefix(Character __instance, HitData hit, out DamageCollector.RpcContext __state) { __state = DamageCollector.CurrentHit; if (DamageCollector.Owns(__instance)) { DamageCollector.CurrentHit = new DamageCollector.RpcContext { Target = __instance, Hit = hit }; Plugin.Collector.Activity(__instance, hit); } } private static void Finalizer(DamageCollector.RpcContext __state) { DamageCollector.CurrentHit = __state; } } [HarmonyPatch(typeof(Character), "ApplyDamage")] internal static class ObserveDamage { [HarmonyPriority(0)] private static void Prefix(Character __instance, HitData hit, out DamageCollector.DamageState __state) { __state = new DamageCollector.DamageState { Valid = (DamageCollector.Owns(__instance) && !__instance.IsDead() && !__instance.IsTeleporting() && !__instance.InCutscene() && !__instance.IsDebugFlying() && !CinematicsManager.IsPlaying()), Health = __instance.GetHealth(), Raw = ((hit == null) ? new float[11] : DamageCollector.Values(hit)) }; } private static void Postfix(Character __instance, HitData hit, DamageCollector.DamageState __state) { Plugin.Collector.Applied(__instance, hit, __state); } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class ObserveDeath { private static void Prefix(Character __instance) { Plugin.Collector.Death(__instance); } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class ObservePlayerDeath { private static void Prefix(Player __instance) { Plugin.Collector.Death((Character)(object)__instance); } } [HarmonyPatch(typeof(Character), "RPC_Heal")] internal static class ObserveHealing { private static void Prefix(Character __instance, out float __state) { __state = __instance.GetHealth(); } private static void Postfix(Character __instance, float __state) { Plugin.Collector.Healed(__instance, __state); } } [HarmonyPatch] internal static class ObserveTick { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(SE_Burning), "UpdateStatusEffect", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(SE_Poison), "UpdateStatusEffect", (Type[])null, (Type[])null); } private static void Prefix(StatusEffect __instance, out StatusEffect __state) { __state = DamageCollector.CurrentEffect; DamageCollector.CurrentEffect = __instance; } private static void Finalizer(StatusEffect __state) { DamageCollector.CurrentEffect = __state; } } [HarmonyPatch(typeof(SE_Burning), "AddFireDamage")] internal static class ObserveFire { private static void Prefix(SE_Burning __instance, float __0, out DamageCollector.DotState __state) { __state = Plugin.Collector.BeforeDot((StatusEffect)(object)__instance, __0, 5); } private static void Postfix(SE_Burning __instance, bool __result, DamageCollector.DotState __state) { Plugin.Collector.AddedDot((StatusEffect)(object)__instance, __state, __result); } } [HarmonyPatch(typeof(SE_Burning), "AddSpiritDamage")] internal static class ObserveSpirit { private static void Prefix(SE_Burning __instance, float __0, out DamageCollector.DotState __state) { __state = Plugin.Collector.BeforeDot((StatusEffect)(object)__instance, __0, 9); } private static void Postfix(SE_Burning __instance, bool __result, DamageCollector.DotState __state) { Plugin.Collector.AddedDot((StatusEffect)(object)__instance, __state, __result); } } [HarmonyPatch(typeof(SE_Poison), "AddDamage")] internal static class ObservePoison { private static void Prefix(SE_Poison __instance, float __0, out DamageCollector.DotState __state) { __state = Plugin.Collector.BeforeDot((StatusEffect)(object)__instance, __0, 8); } private static void Postfix(SE_Poison __instance, DamageCollector.DotState __state) { Plugin.Collector.AddedDot((StatusEffect)(object)__instance, __state, __state.Amount >= __state.Before); } } [HarmonyPatch(typeof(SEMan), "RemoveStatusEffect", new Type[] { typeof(StatusEffect), typeof(bool) })] internal static class ObserveEffectRemoval { private static void Postfix(StatusEffect __0, bool __result) { if (__result) { Plugin.Collector.RemovedDot(__0); } } } } namespace CombatStats.Ui { internal sealed class CompactMeter : MonoBehaviour { private const float RefreshInterval = 0.25f; private const float FadeSeconds = 0.4f; private const float PadLeft = 11f; private const float PadTop = 7f; private const float PadBottom = 9f; private const float CaptionHeight = 14f; private readonly List<MeterRow> _rows = new List<MeterRow>(); private RectTransform _root; private RectTransform _list; private CanvasGroup _group; private Image _background; private TMP_Text _caption; private float _nextRefresh; private float _alpha; private bool _failed; private void Update() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings != null) { if (Shortcut.IsPressed(Plugin.Settings.CompactKey.Value)) { Cycle(); } bool flag = Wanted(); if (flag && Time.time >= _nextRefresh) { _nextRefresh = Time.time + 0.25f; Refresh(); } Fade(flag); } } private void Cycle() { ConfigEntryCycle(); string text = Plugin.Settings.CompactMode.Value switch { MeterMode.Always => "Combat meter: always shown", MeterMode.Auto => "Combat meter: shows the latest fight", _ => "Combat meter: off", }; MessageHud instance = MessageHud.instance; if ((Object)(object)instance != (Object)null) { instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true); } Plugin.Debug(text); } private void ConfigEntryCycle() { switch (Plugin.Settings.CompactMode.Value) { case MeterMode.Auto: Plugin.Settings.CompactMode.Value = MeterMode.Always; break; case MeterMode.Always: Plugin.Settings.CompactMode.Value = MeterMode.Off; break; default: Plugin.Settings.CompactMode.Value = MeterMode.Auto; break; } } private bool Wanted() { if (Plugin.Settings.CompactMode.Value == MeterMode.Off) { return false; } if ((Object)(object)Player.m_localPlayer == (Object)null || Hud.IsUserHidden() || Menu.IsVisible() || Minimap.IsOpen()) { return false; } if (Plugin.Settings.CompactMode.Value == MeterMode.Always) { return true; } return Plugin.History.Current(DamageCollector.LocalId()) != null; } private void Fade(bool wanted) { float num = (wanted ? 1f : 0f); if (Mathf.Approximately(_alpha, num)) { if ((Object)(object)_group != (Object)null && ((Component)_group).gameObject.activeSelf != _alpha > 0.01f) { ((Component)_group).gameObject.SetActive(_alpha > 0.01f); } return; } _alpha = Mathf.MoveTowards(_alpha, num, Time.unscaledDeltaTime / 0.4f); if (!((Object)(object)_group == (Object)null)) { _group.alpha = _alpha; bool flag = _alpha > 0.01f; if (((Component)_group).gameObject.activeSelf != flag) { ((Component)_group).gameObject.SetActive(flag); } } } private void Refresh() { //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_root == (Object)null) { _root = UiStyle.Build("CombatStats Compact", ref _failed, Build); if ((Object)(object)_root == (Object)null) { return; } } ModConfig settings = Plugin.Settings; float value = settings.CompactWidth.Value; bool value2 = settings.CompactBars.Value; float width = value - 22f; long num = DamageCollector.LocalId(); EncounterSnapshot encounterSnapshot = Plugin.History.Snapshot(FightView.Current, CombatChannel.DamageDealt, num); List<CombatantRow> list = encounterSnapshot.Visible(settings.CompactRows.Value, num); float leader = ((list.Count > 0) ? list[0].Total : 0f); float num2 = 0f; bool flag = settings.CompactCaption.Value || list.Count == 0; ((Component)_caption).gameObject.SetActive(flag); if (flag) { _caption.text = ((list.Count == 0) ? MeterFormat.EmptyText(encounterSnapshot, CombatChannel.DamageDealt) : MeterFormat.Caption(encounterSnapshot, MeterFormat.Plain(Localization.instance.Localize(encounterSnapshot.Name)))); UiStyle.PlaceTopStretch(_caption.rectTransform, 0f, 0f, 0f, 14f); num2 += 14f; } for (int i = 0; i < list.Count; i++) { MeterRow meterRow = RowAt(i); meterRow.SetActive(active: true); meterRow.Apply(list[i], leader, width, value2, settings.CompactShare.Value, list[i].Id == num, string.Empty); meterRow.Place(num2); num2 += MeterRow.Height(value2); } for (int j = list.Count; j < _rows.Count; j++) { _rows[j].SetActive(active: false); } _list.sizeDelta = new Vector2(_list.sizeDelta.x, num2); _root.sizeDelta = new Vector2(value, 7f + num2 + 9f); ((Transform)_root).localScale = Vector3.one * settings.CompactScale.Value; UiStyle.Anchor(_root, settings.CompactAnchor.Value, settings.CompactOffset.Value); Color meterBackground = UiStyle.MeterBackground; meterBackground.a = settings.CompactOpacity.Value; ((Graphic)_background).color = meterBackground; } private MeterRow RowAt(int index) { while (_rows.Count <= index) { _rows.Add(MeterRow.Create(_list, detailed: false, null)); } return _rows[index]; } private void Build(RectTransform root) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: 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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Expected O, but got Unknown //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Expected O, but got Unknown //IL_01bc: Unknown result type (might be due to invalid IL or missing references) _rows.Clear(); root.sizeDelta = new Vector2(Plugin.Settings.CompactWidth.Value, 80f); _group = ((Component)root).gameObject.AddComponent<CanvasGroup>(); _group.alpha = 0f; _group.interactable = false; _group.blocksRaycasts = false; ((Component)root).gameObject.SetActive(false); _background = UiStyle.Box("Background", root, UiStyle.MeterBackground); _background.sprite = UiStyle.Fade(); UiStyle.Stretch(((Graphic)_background).rectTransform, 0f, 0f, 0f, 0f); Image obj = UiStyle.Box("Hairline", root, UiStyle.Hairline); obj.sprite = UiStyle.Flat(); ((Graphic)obj).rectTransform.anchorMin = new Vector2(0f, 1f); ((Graphic)obj).rectTransform.anchorMax = new Vector2(1f, 1f); ((Graphic)obj).rectTransform.pivot = new Vector2(0.5f, 1f); ((Graphic)obj).rectTransform.sizeDelta = new Vector2(0f, 1f); ((Graphic)obj).rectTransform.anchoredPosition = Vector2.zero; GameObject val = new GameObject("List", new Type[1] { typeof(RectTransform) }); _list = (RectTransform)val.transform; ((Transform)_list).SetParent((Transform)(object)root, false); UiStyle.PlaceTopStretch(_list, 11f, 11f, 7f, 40f); _caption = UiStyle.Text("Caption", _list, 12f, (TextAlignmentOptions)4097, UiStyle.Dim); _caption.richText = false; Plugin.Debug("Compact meter built"); } } internal sealed class DetailWindow : MonoBehaviour { private enum Breakdown { Types, Targets, Skills, Pets } private sealed class BreakdownLine { public const float Height = 16f; private readonly RectTransform _root; private readonly TMP_Text _name; private readonly TMP_Text _amount; private readonly TMP_Text _share; private BreakdownLine(RectTransform root, TMP_Text name, TMP_Text amount, TMP_Text share) { _root = root; _name = name; _amount = amount; _share = share; } public static BreakdownLine Create(RectTransform parent) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)new GameObject("Breakdown", new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent((Transform)(object)parent, false); val.anchorMin = new Vector2(0f, 1f); val.anchorMax = new Vector2(1f, 1f); val.pivot = new Vector2(0f, 1f); val.sizeDelta = new Vector2(0f, 16f); TMP_Text val2 = UiStyle.Text("Name", val, 13f, (TextAlignmentOptions)4097, UiStyle.Dim); val2.richText = false; val2.rectTransform.offsetMin = new Vector2(22f, 0f); TMP_Text val3 = UiStyle.Text("Amount", val, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); UiStyle.PinRight(val3.rectTransform, 250f, 152f, 0f, 16f); TMP_Text val4 = UiStyle.Text("Share", val, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); UiStyle.PinRight(val4.rectTransform, 68f, 0f, 0f, 16f); return new BreakdownLine(val, val2, val3, val4); } public void ApplySummary(string text) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) SetName(text, UiStyle.Dim, 0f); _amount.text = ""; _share.text = ""; } public void ApplyText(string name, string amount, string share) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) SetName(MeterFormat.Plain(Localization.instance.Localize(name)), UiStyle.Dim, 250f); _amount.text = amount; _share.text = share; } public void Apply(DamageKind kind, float amount, float share, bool healing) { //IL_0023: 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) SetName(healing ? "Healing" : KindName(kind), healing ? UiStyle.Dim : UiStyle.Colors[(int)kind], 250f); _amount.text = MeterFormat.Number(amount); _share.text = MeterFormat.Percent(share); } public void SetActive(bool active) { if (((Component)_root).gameObject.activeSelf != active) { ((Component)_root).gameObject.SetActive(active); } } public void Place(float y) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) _root.anchoredPosition = new Vector2(0f, 0f - y); } private void SetName(string text, Color color, float right) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) _name.text = text; ((Graphic)_name).color = color; _name.rectTransform.offsetMax = new Vector2(0f - right, 0f); } private static string KindName(DamageKind kind) { string text = DamageKinds.LocalizationKey(kind); Localization instance = Localization.instance; if (instance == null) { return DamageKinds.EnglishName(kind); } string text2 = instance.Localize(text); if (!string.IsNullOrEmpty(text2) && !text2.StartsWith("[", StringComparison.Ordinal) && !text2.Contains("$")) { return text2; } return DamageKinds.EnglishName(kind); } } private const float RefreshInterval = 0.25f; private const float Padding = 14f; private const float ScrollStep = 48f; private const float ScreenMargin = 20f; private static readonly Vector2 MinimumSize = new Vector2(600f, 280f); private static readonly FightView[] Views = new FightView[4] { FightView.Current, FightView.Last, FightView.Overall, FightView.History }; private readonly List<MeterRow> _rows = new List<MeterRow>(); private readonly List<BreakdownLine> _lines = new List<BreakdownLine>(); private readonly List<Button> _viewTabs = new List<Button>(); private readonly List<Button> _channelTabs = new List<Button>(); private readonly List<GameObject> _columnHeaders = new List<GameObject>(); private readonly HashSet<long> _expanded = new HashSet<long>(); private RectTransform _panel; private RectTransform _content; private RectTransform _viewport; private TMP_Text _summary; private TMP_Text _empty; private TMP_Text _rateHeading; private Button _breakdownButton; private FightView _view; private int _historyIndex; private Breakdown _breakdown; private CombatChannel _channel; private float _nextRefresh; private float _scrolled; private int _scrollFrame = -1; private bool _failed; private bool _open; private bool _seededExpanded; public static bool IsOpen { get { if ((Object)(object)Instance != (Object)null) { return Instance._open; } return false; } } public static DetailWindow Instance { get; private set; } public static void FeedScrollWheel(float amount) { DetailWindow instance = Instance; if (IsOpen && !((Object)(object)instance._panel == (Object)null) && instance._scrollFrame != Time.frameCount) { instance._scrollFrame = Time.frameCount; instance._scrolled = Mathf.Max(0f, instance._scrolled - Mathf.Sign(amount) * 48f); instance.Layout(); } } public void Toggle() { if (_open) { Close(); return; } if ((Object)(object)_panel == (Object)null) { _panel = UiStyle.Build("CombatStats Detail", ref _failed, Build); if ((Object)(object)_panel == (Object)null) { return; } } _open = true; _scrolled = 0f; ((Component)_panel).gameObject.SetActive(true); Refresh(); } public void Close() { _open = false; SettingsPanel.Instance?.Close(); if ((Object)(object)_panel != (Object)null) { ((Component)_panel).gameObject.SetActive(false); } } private void Awake() { Instance = this; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void Update() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings != null) { if (Shortcut.IsPressed(Plugin.Settings.DetailKey.Value)) { Toggle(); } if (_open && (Object)(object)_panel == (Object)null) { Close(); } if (_open && Time.time >= _nextRefresh) { _nextRefresh = Time.time + 0.25f; Refresh(); } } } private void Refresh() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) Vector2 value = Plugin.Settings.DetailSize.Value; Rect rect = ((RectTransform)((Transform)_panel).parent).rect; Vector2 val = ((Rect)(ref rect)).size - 40f * Vector2.one; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(Mathf.Min(Mathf.Max(MinimumSize.x, value.x), val.x), Mathf.Min(Mathf.Max(MinimumSize.y, value.y), val.y)); if (_panel.sizeDelta != val2) { _panel.sizeDelta = val2; } for (int i = 0; i < _viewTabs.Count; i++) { UiStyle.MarkSelected(_viewTabs[i], Views[i] == _view); } for (int j = 0; j < _channelTabs.Count; j++) { UiStyle.MarkSelected(_channelTabs[j], CombatChannels.All[j] == _channel); } _rateHeading.text = ((_channel == CombatChannel.Healing) ? "HPS" : ((_channel == CombatChannel.DamageTaken) ? "Taken/s" : "DPS")); ((Component)_breakdownButton).gameObject.SetActive(_channel != CombatChannel.Healing); UiStyle.LabelOf(_breakdownButton).text = Label(_breakdown, _channel); Layout(); } private void Layout() { //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_0672: Unknown result type (might be due to invalid IL or missing references) //IL_0677: Unknown result type (might be due to invalid IL or missing references) //IL_0694: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Unknown result type (might be due to invalid IL or missing references) //IL_06bc: Unknown result type (might be due to invalid IL or missing references) //IL_06e6: Unknown result type (might be due to invalid IL or missing references) long num = DamageCollector.LocalId(); EncounterSnapshot encounterSnapshot = Plugin.History.Snapshot(_view, _channel, num, _historyIndex); int historyCount = Plugin.History.HistoryCount; string text = ((_view == FightView.History && historyCount > 0) ? ("Fight " + (Math.Min(_historyIndex, historyCount - 1) + 1) + " of " + historyCount + " | ") : ""); _summary.richText = false; _summary.text = (encounterSnapshot.HasEncounter ? (text + MeterFormat.Caption(encounterSnapshot, MeterFormat.Plain(Localization.instance.Localize(encounterSnapshot.Name)))) : ""); if (encounterSnapshot.Rows.Count > 0) { TMP_Text summary = _summary; summary.text = summary.text + "\nTotal " + MeterFormat.Number(encounterSnapshot.Total) + " | " + ((_channel == CombatChannel.Healing) ? "HPS received " : ((_channel == CombatChannel.DamageTaken) ? "Taken/s " : "DPS ")) + MeterFormat.Number(encounterSnapshot.Dps); } foreach (GameObject columnHeader in _columnHeaders) { columnHeader.SetActive(encounterSnapshot.Rows.Count > 0); } ((Component)_breakdownButton).gameObject.SetActive(encounterSnapshot.Rows.Count > 0 && _channel != CombatChannel.Healing); _empty.text = MeterFormat.EmptyText(encounterSnapshot, _channel); Rect rect = _viewport.rect; float width = ((Rect)(ref rect)).width; float leader = ((encounterSnapshot.Rows.Count > 0) ? encounterSnapshot.Rows[0].Total : 0f); Breakdown breakdown = ((_channel != CombatChannel.Healing) ? _breakdown : Breakdown.Types); if (!_seededExpanded && num != 0L) { _seededExpanded = true; _expanded.Add(num); } int num2 = 0; int num3 = 0; float num4 = 0f; foreach (CombatantRow row in encounterSnapshot.Rows) { MeterRow meterRow = RowAt(num2++); bool flag = _expanded.Contains(row.Id); meterRow.SetActive(active: true); meterRow.Apply(row, leader, width, bars: true, showShare: false, row.Id == num, flag ? "- " : "+ "); meterRow.Place(num4); num4 += MeterRow.Height(bars: true); if (!flag) { continue; } BreakdownLine breakdownLine = LineAt(num3++); breakdownLine.SetActive(active: true); breakdownLine.ApplySummary((_channel == CombatChannel.Healing) ? ("Heals " + row.Hits + " | Largest " + MeterFormat.Number(row.Max) + " | Avg/heal " + MeterFormat.Number(row.Average)) : ("Hits " + row.Hits + ((row.Ticks > 0) ? (" | Ticks " + row.Ticks) : "") + " | Largest " + MeterFormat.Number(row.Max) + ((row.Ticks > 0) ? " | Avg/hit or tick " : " | Avg/hit ") + MeterFormat.Number(row.Average) + ((row.Overkill > 0f) ? (" | Overkill " + MeterFormat.Number(row.Overkill)) : ""))); breakdownLine.Place(num4); num4 += 16f; if (_channel == CombatChannel.Healing) { continue; } if (breakdown == Breakdown.Types) { for (int i = 0; i < 11; i++) { float num5 = row.ByKind[i]; if (!(num5 <= 0f)) { BreakdownLine breakdownLine2 = LineAt(num3++); breakdownLine2.SetActive(active: true); breakdownLine2.Apply((DamageKind)i, num5, (row.Total > 0f) ? (num5 / row.Total) : 0f, _channel == CombatChannel.Healing); breakdownLine2.Place(num4); num4 += 16f; } } } else { foreach (KeyValuePair<string, float> item in from x in breakdown switch { Breakdown.Skills => row.Skills, Breakdown.Targets => row.Targets, _ => row.Pets, } where x.Value > 0f orderby x.Value descending select x) { BreakdownLine breakdownLine3 = LineAt(num3++); breakdownLine3.SetActive(active: true); breakdownLine3.ApplyText((breakdown == Breakdown.Skills) ? MeterFormat.Skill(item.Key) : item.Key, MeterFormat.Number(item.Value), (row.Total > 0f) ? MeterFormat.Percent(item.Value / row.Total) : ""); breakdownLine3.Place(num4); num4 += 16f; } } num4 += 4f; } for (int num6 = num2; num6 < _rows.Count; num6++) { _rows[num6].SetActive(active: false); } for (int num7 = num3; num7 < _lines.Count; num7++) { _lines[num7].SetActive(active: false); } ((Component)_empty).gameObject.SetActive(num2 == 0); float num8 = num4; rect = _viewport.rect; float num9 = Mathf.Max(num8, ((Rect)(ref rect)).height); _content.sizeDelta = new Vector2(0f, num9); float scrolled = _scrolled; rect = _viewport.rect; _scrolled = Mathf.Clamp(scrolled, 0f, Mathf.Max(0f, num9 - ((Rect)(ref rect)).height)); _content.anchoredPosition = new Vector2(0f, _scrolled); } private void OnRowClicked(long id) { if (!_expanded.Remove(id)) { _expanded.Add(id); } Layout(); } private MeterRow RowAt(int index) { while (_rows.Count <= index) { _rows.Add(MeterRow.Create(_content, detailed: true, OnRowClicked)); } return _rows[index]; } private BreakdownLine LineAt(int index) { while (_lines.Count <= index) { _lines.Add(BreakdownLine.Create(_content)); } return _lines[index]; } private void Build(RectTransform panel) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) _panel = panel; _rows.Clear(); _lines.Clear(); _viewTabs.Clear(); _channelTabs.Clear(); _columnHeaders.Clear(); UiStyle.Panel(panel, Plugin.Settings.DetailSize.Value, 100, "Combat stats"); UiStyle.CornerButton(panel, "Close", 14f, 70f, Close); UiStyle.CornerButton(panel, "Settings", 88f, 84f, OpenSettings); BuildViewTabs(); BuildChannelTabs(); _summary = UiStyle.Text("Summary", panel, 13f, (TextAlignmentOptions)4097, UiStyle.Dim); UiStyle.PlaceTopStretch(_summary.rectTransform, 14f, 14f, 100f, 36f); TMP_Text val = UiStyle.Text("Header", panel, 12f, (TextAlignmentOptions)4097, UiStyle.Dim); UiStyle.PlaceTopStretch(val.rectTransform, 14f, 14f, 148f, 14f); val.text = "Player"; _columnHeaders.Add(((Component)val).gameObject); Column("Total", 250f, 152f); _rateHeading = Column("DPS", 145f, 75f); Column("Share", 68f, 0f); Image val2 = UiStyle.Line("Separator", panel); UiStyle.PlaceTopStretch(((Graphic)val2).rectTransform, 14f, 14f, 164f, 1f); _columnHeaders.Add(((Component)val2).gameObject); BuildScroll(); _empty = UiStyle.Text("Empty", _viewport, 14f, (TextAlignmentOptions)514, UiStyle.Dim); _empty.text = "No fights recorded for this view."; ((Component)_empty).gameObject.SetActive(false); ((Component)panel).gameObject.SetActive(false); Plugin.Debug($"Detail window built ({panel.sizeDelta.x}x{panel.sizeDelta.y})"); } private void BuildScroll() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012a: 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_0154: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Viewport", new Type[3] { typeof(RectTransform), typeof(RectMask2D), typeof(Image) }); _viewport = (RectTransform)val.transform; ((Transform)_viewport).SetParent((Transform)(object)_panel, false); UiStyle.Stretch(_viewport, 14f, 14f, 172f, 14f); Image component = val.GetComponent<Image>(); ((Graphic)component).color = new Color(0f, 0f, 0f, 0f); ((Graphic)component).raycastTarget = true; GameObject val2 = new GameObject("Content", new Type[1] { typeof(RectTransform) }); _content = (RectTransform)val2.transform; ((Transform)_content).SetParent((Transform)(object)_viewport, false); _content.anchorMin = new Vector2(0f, 1f); _content.anchorMax = new Vector2(1f, 1f); _content.pivot = new Vector2(0f, 1f); _content.anchoredPosition = Vector2.zero; _content.sizeDelta = new Vector2(0f, 0f); } private TMP_Text Column(string text, float from, float to) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) TMP_Text val = UiStyle.Text("Column" + text, _panel, 12f, (TextAlignmentOptions)4100, UiStyle.Dim); val.text = text; _columnHeaders.Add(((Component)val).gameObject); UiStyle.PinRight(val.rectTransform, from + 14f, to + 14f, 148f, 14f); return val; } private void BuildViewTabs() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Expected O, but got Unknown float num = 14f; FightView[] views = Views; for (int i = 0; i < views.Length; i++) { FightView fightView = views[i]; FightView captured = fightView; Button val = UiStyle.TextButton("View" + fightView, _panel, fightView.ToString(), 14f, UiStyle.Dim, delegate { _view = captured; _scrolled = 0f; Refresh(); }); UiStyle.PlaceTopLeft((RectTransform)((Component)val).transform, num, 42f, 72f, 22f); num += 74f; _viewTabs.Add(val); } UiStyle.PlaceTopLeft((RectTransform)((Component)UiStyle.TextButton("Earlier", _panel, "Earlier", 13f, UiStyle.Dim, delegate { _view = FightView.History; _historyIndex = Math.Min(Plugin.History.HistoryCount - 1, _historyIndex + 1); _scrolled = 0f; Refresh(); })).transform, num + 8f, 42f, 65f, 22f); UiStyle.PlaceTopLeft((RectTransform)((Component)UiStyle.TextButton("Later", _panel, "Later", 13f, UiStyle.Dim, delegate { _view = FightView.History; _historyIndex = Math.Max(0, _historyIndex - 1); _scrolled = 0f; Refresh(); })).transform, num + 76f, 42f, 65f, 22f); UiStyle.PlaceTopLeft((RectTransform)((Component)UiStyle.TextButton("Reset", _panel, "Reset", 13f, UiStyle.Dim, delegate { if (!Plugin.History.Reset() && Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowMessage((MessageType)1, "Finish the fight before resetting history.", 0, (Sprite)null, false, true); } _historyIndex = 0; Refresh(); })).transform, num + 150f, 42f, 65f, 22f); } private void BuildChannelTabs() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Expected O, but got Unknown float num = 14f; CombatChannel[] all = CombatChannels.All; for (int i = 0; i < all.Length; i++) { CombatChannel combatChannel = all[i]; CombatChannel captured = combatChannel; Button val = UiStyle.TextButton("Channel" + combatChannel, _panel, Label(combatChannel), 13f, UiStyle.Dim, delegate { _channel = captured; _scrolled = 0f; Refresh(); }); UiStyle.PlaceTopLeft((RectTransform)((Component)val).transform, num, 70f, 130f, 22f); num += 132f; _channelTabs.Add(val); } _breakdownButton = UiStyle.TextButton("Breakdown", _panel, "Types", 13f, UiStyle.Dim, delegate { _breakdown = (Breakdown)((int)(_breakdown + 1) % 4); _scrolled = 0f; Refresh(); }); UiStyle.PlaceTopLeft((RectTransform)((Component)_breakdownButton).transform, num + 10f, 70f, 100f, 22f); } private void OpenSettings() { (SettingsPanel.Instance ?? ((Component)this).gameObject.AddComponent<SettingsPanel>()).Toggle(); } private static string Label(CombatChannel channel) { return channel switch { CombatChannel.Healing => "Healing received", CombatChannel.DamageTaken => "Damage taken", _ => "Damage done", }; } private static string Label(Breakdown breakdown, CombatChannel channel) { switch (breakdown) { case Breakdown.Targets: if (channel != CombatChannel.DamageTaken) { return "Targets"; } return "Sources"; case Breakdown.Skills: return "Skills"; case Breakdown.Pets: return "Pets"; default: return "Types"; } } } internal sealed class MeterRow { public const float LineHeight = 17f; public const float BarHeight = 6f; public const float CompactValueWidth = 185f; private const float BarGap = 2f; private const float RowGap = 4f; private readonly TMP_Text _name; private readonly TMP_Text _value; private readonly TMP_Text _share; private readonly TMP_Text _rate; private readonly RectTransform _bar; private readonly Image[] _segments = (Image[])(object)new Image[11]; private readonly bool _detailed; public RectTransform Root { get; } public long Id { get; private set; } private MeterRow(RectTransform root, bool detailed, TMP_Text name, TMP_Text value, TMP_Text share, TMP_Text rate, RectTransform bar) { Root = root; _detailed = detailed; _name = name; _value = value; _share = share; _rate = rate; _bar = bar; } public static float Height(bool bars) { if (!bars) { return 21f; } return 29f; } public static MeterRow Create(RectTransform parent, bool detailed, Action<long> onClick) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Row", new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)parent, false); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(1f, 1f); val2.pivot = new Vector2(0f, 1f); MeterRow row = null; if (onClick != null) { UiStyle.Clickable(val, delegate { onClick(row.Id); }); } row = Build(val2, detailed); return row; } public void Apply(CombatantRow row, float leader, float width, bool bars, bool showShare, bool local, string prefix) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) Id = row.Id; string text = (string.IsNullOrEmpty(row.Name) ? "..." : row.Name); if (Combatant.IsUnownedPet(row.Id) && Localization.instance != null) { text = MeterFormat.Plain(Localization.instance.Localize(text)); } _name.text = prefix + text; ((Graphic)_name).color = (local ? UiStyle.Mine : UiStyle.FontColor); string text2 = MeterFormat.Number(row.Total); if (_detailed) { _value.text = text2; _share.text = MeterFormat.Percent(row.Share); _rate.text = MeterFormat.Number(row.Dps); } else { _value.text = (showShare ? (text2 + " " + MeterFormat.Number(row.Dps) + "/s <color=#" + ColorUtility.ToHtmlStringRGB(UiStyle.Dim) + ">" + MeterFormat.Percent(row.Share) + "</color>") : (text2 + " " + MeterFormat.Number(row.Dps) + "/s")); } ((Component)_bar).gameObject.SetActive(bars); if (bars) { Layout(row, leader, width); } Root.sizeDelta = new Vector2(0f, Height(bars)); } public void SetActive(bool active) { if (((Component)Root).gameObject.activeSelf != active) { ((Component)Root).gameObject.SetActive(active); } } public void Place(float y) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) Root.anchoredPosition = new Vector2(0f, 0f - y); } private static MeterRow Build(RectTransform root, bool detailed) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) TMP_Text val = UiStyle.Text("Name", root, 15f, (TextAlignmentOptions)4097, UiStyle.FontColor); val.richText = false; val.rectTransform.anchorMin = new Vector2(0f, 1f); val.rectTransform.anchorMax = new Vector2(1f, 1f); val.rectTransform.pivot = new Vector2(0.5f, 1f); val.rectTransform.offsetMin = new Vector2(0f, -17f); val.rectTransform.offsetMax = new Vector2(detailed ? (-250f) : (-185f), 0f); TMP_Text val2 = UiStyle.Text("Value", root, 15f, (TextAlignmentOptions)4100, UiStyle.FontColor); TMP_Text val3 = null; TMP_Text val4 = null; if (detailed) { UiStyle.PinRight(val2.rectTransform, 250f, 152f, 0f, 17f); val3 = UiStyle.Text("Share", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); UiStyle.PinRight(val3.rectTransform, 68f, 0f, 0f, 17f); val4 = UiStyle.Text("Rate", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); UiStyle.PinRight(val4.rectTransform, 145f, 75f, 0f, 17f); } else { UiStyle.PinRight(val2.rectTransform, 185f, 0f, 0f, 17f); } RectTransform val5 = (RectTransform)new GameObject("Bar", new Type[1] { typeof(RectTransform) }).transform; ((Transform)val5).SetParent((Transform)(object)root, false); val5.anchorMin = new Vector2(0f, 1f); val5.anchorMax = new Vector2(1f, 1f); val5.pivot = new Vector2(0f, 1f); val5.anchoredPosition = new Vector2(0f, -19f); val5.sizeDelta = new Vector2(0f, 6f); Image obj = UiStyle.Box("Back", val5, UiStyle.BarTrack); obj.sprite = UiStyle.Flat(); UiStyle.Stretch(((Graphic)obj).rectTransform, 0f, 0f, 0f, 0f); MeterRow meterRow = new MeterRow(root, detailed, val, val2, val3, val4, val5); for (int i = 0; i < 11; i++) { Image val6 = UiStyle.Box("Kind" + i, val5, Color.white); val6.sprite = UiStyle.Flat(); RectTransform rectTransform = ((Graphic)val6).rectTransform; rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(0f, 1f); rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.offsetMin = new Vector2(rectTransform.offsetMin.x, 0f); rectTransform.offsetMax = new Vector2(rectTransform.offsetMax.x, 0f); ((Component)val6).gameObject.SetActive(false); meterRow._segments[i] = val6; } return meterRow; } private void Layout(CombatantRow row, float leader, float width) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) float num = ((leader > 0f) ? (Mathf.Clamp01(row.Total / leader) * width) : 0f); Color[] colors = UiStyle.Colors; float num2 = 0f; for (int i = 0; i < 11; i++) { Image val = _segments[i]; float num3 = row.ByKind[i]; if (num3 <= 0f || row.Total <= 0f || num <= 0f) { if (((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(false); } continue; } float num4 = num * (num3 / row.Total); RectTransform rectTransform = ((Graphic)val).rectTransform; rectTransform.anchoredPosition = new Vector2(num2, 0f); rectTransform.sizeDelta = new Vector2(num4, 0f); ((Graphic)val).color = colors[i]; if (!((Component)val).gameObject.activeSelf) { ((Component)val).gameObject.SetActive(true); } num2 += num4; } } } internal sealed class SettingsPanel : MonoBehaviour { private sealed class Control { private readonly TMP_Text _value; private readonly Func<string> _read; public Control(TMP_Text value, Func<string> read) { _value = value; _read = read; } public void Refresh() { _value.text = _read(); } } private const float Padding = 16f; private const float RowHeight = 24f; private const float ColumnWidth = 320f; private const float ValueWidth = 110f; private readonly List<Control> _rows = new List<Control>(); private RectTransform _panel; private TMP_Text _hint; private bool _failed; private bool _open; public static SettingsPanel Instance { get; private set; } public static bool IsOpen { get { if ((Object)(object)Instance != (Object)null) { return Instance._open; } return false; } } public void Toggle() { if (_open) { Close(); return; } if ((Object)(object)_panel == (Object)null) { _panel = UiStyle.Build("CombatStats Settings", ref _failed, Build); if ((Object)(object)_panel == (Object)null) { return; } } _open = true; ((Component)_panel).gameObject.SetActive(true); RefreshValues(); } public void Close() { _open = false; if ((Object)(object)_panel != (Object)null) { ((Component)_panel).gameObject.SetActive(false); } } private void Awake() { Instance = this; } private void Update() { if (_open && (Object)(object)_panel == (Object)null) { Close(); } } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void RefreshValues() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) foreach (Control row in _rows) { row.Refresh(); } ModConfig settings = Plugin.Settings; _hint.text = ((object)settings.CompactKey.Value/*cast due to .constrained prefix*/).ToString() + " Meter visibility " + ((object)settings.DetailKey.Value/*cast due to .constrained prefix*/).ToString() + " Fight details"; } private void Build(RectTransform panel) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) _panel = panel; _rows.Clear(); UiStyle.Panel(panel, new Vector2(720f, 470f), 110, "Combat stats settings"); UiStyle.CornerButton(panel, "Close", 16f, 70f, Close); ModConfig settings = Plugin.Settings; float x = 16f; float x2 = 384f; float y = 46f; y = Heading("Compact meter", x, y); y = Row(x, y, "When to show", () => Word(settings.CompactMode.Value), delegate(int step) { settings.CompactMode.Value = (MeterMode)Wrap((int)(settings.CompactMode.Value + step), 3); }); y = Row(x, y, "Rows", () => settings.CompactRows.Value.ToString(CultureInfo.InvariantCulture), delegate(int step) { ConfigEntry<int> compactRows = settings.CompactRows; compactRows.Value += step; }); y = Row(x, y, "Position", () => Word(settings.CompactAnchor.Value), delegate(int step) { settings.CompactAnchor.Value = (ScreenAnchor)Wrap((int)(settings.CompactAnchor.Value + step), 6); }); y = Row(x, y, "Horizontal inset", () => settings.CompactOffset.Value.x.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_001b: 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_0048: Unknown result type (might be due to invalid IL or missing references) settings.CompactOffset.Value = new Vector2(Mathf.Max(0f, settings.CompactOffset.Value.x + (float)step * 10f), settings.CompactOffset.Value.y); }); y = Row(x, y, "Vertical inset", () => settings.CompactOffset.Value.y.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) settings.CompactOffset.Value = new Vector2(settings.CompactOffset.Value.x, Mathf.Max(0f, settings.CompactOffset.Value.y + (float)step * 10f)); }); y = Row(x, y, "Width", () => settings.CompactWidth.Value.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { ConfigEntry<float> compactWidth = settings.CompactWidth; compactWidth.Value += (float)step * 20f; }); y = Row(x, y, "Scale", () => settings.CompactScale.Value.ToString("0.00", CultureInfo.InvariantCulture), delegate(int step) { ConfigEntry<float> compactScale = settings.CompactScale; compactScale.Value += (float)step * 0.05f; }); y = Row(x, y, "Background opacity", () => settings.CompactOpacity.Value.ToString("0.00", CultureInfo.InvariantCulture), delegate(int step) { ConfigEntry<float> compactOpacity = settings.CompactOpacity; compactOpacity.Value += (float)step * 0.05f; }); y = Toggle(x, y, "Caption line", () => settings.CompactCaption.Value, delegate(bool value) { settings.CompactCaption.Value = value; }); y = Toggle(x, y, "Percentages", () => settings.CompactShare.Value, delegate(bool value) { settings.CompactShare.Value = value; }); Toggle(x, y, "Bars", () => settings.CompactBars.Value, delegate(bool value) { settings.CompactBars.Value = value; }); y = 46f; y = Heading("Detail window", x2, y); y = Row(x2, y, "Width", () => settings.DetailSize.Value.x.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) settings.DetailSize.Value = new Vector2(Mathf.Clamp(settings.DetailSize.Value.x + (float)step * 20f, 600f, 1400f), settings.DetailSize.Value.y); }); y = Row(x2, y, "Height", () => settings.DetailSize.Value.y.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) settings.DetailSize.Value = new Vector2(settings.DetailSize.Value.x, Mathf.Clamp(settings.DetailSize.Value.y + (float)step * 20f, 280f, 1000f)); }); y += 16f; TMP_Text obj = UiStyle.Text("RecordingRules", panel, 13f, (TextAlignmentOptions)257, UiStyle.Dim); UiStyle.PlaceTopLeft(obj.rectTransform, x2, y, 320f, 44f); obj.text = "Pets and summons count for their owner.\nDPS uses the whole fight."; _hint = UiStyle.Text("Hint", panel, 12f, (TextAlignmentOptions)4097, UiStyle.Dim); UiStyle.PlaceTopLeft(_hint.rectTransform, 16f, panel.sizeDelta.y - 30f, panel.sizeDelta.x - 32f, 18f); ((Component)panel).gameObject.SetActive(false); Plugin.Debug("Settings panel built"); } private float Heading(string text, float x, float y) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) TMP_Text obj = UiStyle.Text("Heading", _panel, 13f, (TextAlignmentOptions)4097, UiStyle.Mine); UiStyle.PlaceTopLeft(obj.rectTransform, x, y, 320f, 20f); obj.text = text; UiStyle.PlaceTopLeft(((Graphic)UiStyle.Line("HeadingLine", _panel)).rectTransform, x, y + 19f, 320f, 1f); return y + 26f; } private float Toggle(float x, float y, string label, Func<bool> read, Action<bool> write) { return Row(x, y, label, () => (!read()) ? "off" : "on", delegate(int step) { if (step != 0) { write(!read()); } }); } private float Row(float x, float y, string label, Func<string> read, Action<int> step) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)new GameObject("Setting", new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent((Transform)(object)_panel, false); UiStyle.PlaceTopLeft(val, x, y, 320f, 24f); TMP_Text obj = UiStyle.Text("Label", val, 14f, (TextAlignmentOptions)4097, UiStyle.FontColor); obj.rectTransform.offsetMax = new Vector2(-162f, 0f); obj.text = label; TMP_Text obj2 = UiStyle.Text("Value", val, 14f, (TextAlignmentOptions)514, UiStyle.Dim); obj2.rectTransform.anchorMin = new Vector2(1f, 0f); obj2.rectTransform.anchorMax = new Vector2(1f, 1f); obj2.rectTransform.offsetMin = new Vector2(-134f, 0f); obj2.rectTransform.offsetMax = new Vector2(-24f, 0f); Control control = new Control(obj2, read); Place(UiStyle.TextButton("Less", val, "<", 15f, UiStyle.Dim, delegate { step(-1); RefreshValues(); }), -158f, -136f); Place(UiStyle.TextButton("More", val, ">", 15f, UiStyle.Dim, delegate { step(1); RefreshValues(); }), -22f, 0f); control.Refresh(); _rows.Add(control); return y + 24f; } private static void Place(Button button, float from, float to) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005