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.0
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 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 Muindor.Windows; 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 Muindor.Windows { public static class CursorRelease { public enum Step { RunVanilla, Release, SkipVanilla } public static Step Decide(bool windowOpen, bool cursorFree) { if (!windowOpen) { return Step.RunVanilla; } if (!cursorFree) { return Step.Release; } return Step.SkipVanilla; } } } 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; public ModConfig(ConfigFile config) { //IL_0035: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: 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 visibility."); DetailKey = config.Bind<KeyboardShortcut>("Detail", "DetailKey", new KeyboardShortcut((KeyCode)292, Array.Empty<KeyCode>()), "Open encounter details."); CompactMode = config.Bind<MeterMode>("Compact", "CompactMode", MeterMode.Off, "Auto retains the latest encounter; Always also shows an empty meter; Off hides it. Recording is always shared."); CompactRows = config.Bind<int>("Compact", "CompactRows", 5, "Visible rows, including your own."); CompactAnchor = config.Bind<ScreenAnchor>("Compact", "CompactAnchor", ScreenAnchor.TopRight, "Screen anchor."); CompactOffset = config.Bind<Vector2>("Compact", "CompactOffset", new Vector2(-28f, -164f), "Offset from the anchor."); CompactWidth = config.Bind<float>("Compact", "CompactWidth", 360f, "Compact width."); CompactScale = config.Bind<float>("Compact", "CompactScale", 1f, "Compact scale."); CompactOpacity = config.Bind<float>("Compact", "CompactOpacity", 0.9f, "Compact background opacity."); CompactCaption = config.Bind<bool>("Compact", "CompactCaption", true, "Show encounter name and duration."); 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), "Detail window size."); Colors = config.Bind<string>("Colours", "Colors", "", "Damage-type overrides, such as Fire=#ff7733."); IsDebug = config.Bind<bool>("General", "IsDebug", false, "Log transport and encounter diagnostics."); 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown if (!File.Exists(path)) { return; } string text = null; bool flag = false; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2); continue; } string[] array2 = text2.Split(new char[1] { '=' }, 2); if (text != null && array2.Length == 2) { ConfigDefinition val = new ConfigDefinition(text, array2[0].Trim()); if (text == "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, array2[1].Trim()); } } } if (flag) { CompactMode.Value = MeterMode.Off; Remember(((ConfigEntryBase)CompactMode).Definition, ((ConfigEntryBase)CompactMode).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() { 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'); } } Storage.Write(path, stringBuilder.ToString()); } 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; } } internal static class Storage { internal static void Write(string path, string text) { 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); } } } [BepInPlugin("tenemo.CombatStats", "Combat stats", "1.0.0")] [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.0"; internal static ManualLogSource Log; internal static ModConfig Settings; internal static DamageCollector Collector; internal static CombatHistory View = new CombatHistory(); internal static DamageChannel Channel; internal static bool Enabled; internal static bool Headless; private Harmony harmony; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); Headless = (int)SystemInfo.graphicsDeviceType == 4; Collector = new DamageCollector(); Channel = ((Component)this).gameObject.AddComponent<DamageChannel>(); harmony = new Harmony("tenemo.CombatStats"); harmony.PatchAll(typeof(Plugin).Assembly); Enabled = true; if (!Headless) { ((Component)this).gameObject.AddComponent<CompactMeter>(); ((Component)this).gameObject.AddComponent<DetailWindow>(); } Log.LogInfo((object)"Combat stats 1.0.0 loaded: shared encounters, 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; } } } 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 Player_TakeInput_Patch { private static void Postfix(ref bool __result) { if (Windows.AnyOpen) { __result = false; } } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] internal static class PlayerController_TakeInput_Patch { private static void Postfix(ref bool __result) { if (Windows.AnyOpen) { __result = false; } } } [HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")] internal static class ZInput_GetMouseScrollWheel_Patch { private static void Postfix(ref float __result) { if (__result != 0f && DetailWindow.IsOpen) { DetailWindow.FeedScrollWheel(__result); __result = 0f; } } } [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] internal static class GameCamera_UpdateMouseCapture_Patch { private static bool Prefix() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 switch (CursorRelease.Decide(Windows.AnyOpen, (int)Cursor.lockState == 0)) { case CursorRelease.Step.RunVanilla: return true; case CursorRelease.Step.Release: ZCursor.LockState = (CursorLockMode)0; break; } ZCursor.Show(); return false; } } [HarmonyPatch(typeof(Menu), "Update")] internal static class Menu_Update_Patch { 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.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 readonly Dictionary<string, Character> tracked = new Dictionary<string, Character>(); 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 (Plugin.Enabled && 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.Channel.FindPlayer(name); if (source2 != null) { source2.Pet = Name(actor); petOwners.Add(actor, source2); return source2; } return new Source { Id = long.MinValue + (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_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0128: 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) 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.Channel.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 = DamageChannel.Now, Target = Key(target), TargetName = Name(target), Actor = source.Id, ActorName = source.Name, Pet = source.Pet, Skill = ((hit == null) ? "Other" : ((object)Unsafe.As<SkillType, SkillType>(ref hit.m_skill)/*cast due to .constrained prefix*/).ToString()) }; 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()) { tracked[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()) { tracked[observation.Enemy] = val; } } return observation; } 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.Channel.Queue(observation); } } } internal void TrackAttacker(Character target, HitData hit) { if (Plugin.Enabled && 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()) { tracked[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.Channel.Queue(observation); } } internal void Death(Character target) { Observation observation = Create(target, null); if (observation != null) { observation.Kind = ObservationKind.Death; Plugin.Channel.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.Channel.Queue(observation); } } } internal void Engagement() { foreach (KeyValuePair<string, Character> item in tracked.ToList()) { Character value = item.Value; if (!Object.op_Implicit((Object)(object)value) || !value.IsOwner()) { tracked.Remove(item.Key); continue; } if (value.IsDead()) { Death(value); continue; } BaseAI baseAI = value.GetBaseAI(); AnimalAI val = (AnimalAI)(object)((baseAI is AnimalAI) ? baseAI : null); Character val2 = ((val != null) ? AnimalTarget.Invoke(val) : (Object.op_Implicit((Object)(object)baseAI) ? baseAI.GetTargetCreature() : null)); Source source = Actor(val2); Observation observation = Create(value, null); observation.Kind = ObservationKind.Engagement; observation.Actor = source.Id; observation.ActorName = source.Name; observation.Engaged = Object.op_Implicit((Object)(object)baseAI) && baseAI.IsAlerted() && Object.op_Implicit((Object)(object)val2) && !val2.IsDead() && source.Id != 0; Plugin.Channel.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.Channel.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.Channel.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_006c: 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, damage.m_damage }.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(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.Net { internal sealed class PlayerIdentity { public int Protocol = 1; public long Id; public long Peer; public string Name = ""; public string Character = ""; public float X; public float Y; public float Z; public double Sent; } internal sealed class ClockReply { public int Protocol = 1; public string Session = ""; public double Echo; public double Time; } internal sealed class CombatBatch { public int Protocol = 1; public string Session = ""; public List<Observation> Events = new List<Observation>(); } internal sealed class CombatFeed { public int Protocol = 1; public string Session = ""; public double Time; public List<Encounter> Fights = new List<Encounter>(); public Dictionary<long, long> Redirects = new Dictionary<long, long>(); public List<PlayerIdentity> Players = new List<PlayerIdentity>(); } internal sealed class DamageChannel : 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 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 EncounterRecorder Recorder => recorder; 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<PlayerIdentity> list = players.Values.Where((PlayerIdentity x) => x.Name == name).ToList(); if (list.Count != 1) { return null; } return Source(list[0]); } internal DamageCollector.Source FindCharacter(string character) { PlayerIdentity playerIdentity = players.Values.FirstOrDefault((PlayerIdentity x) => x.Character == character); if (playerIdentity != null) { return Source(playerIdentity); } return null; } private static DamageCollector.Source Source(PlayerIdentity player) { return new DamageCollector.Source { Id = player.Id, Name = player.Name }; } private void Update() { //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Enabled) { return; } 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) { LoadHistory(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), Peer = ZNet.GetUID(), 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.View.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("Combat channel registered for a new world."); } private void EndSession() { SaveHistory(); historyPath = ""; Plugin.View.Changed -= SaveHistory; Plugin.View = new CombatHistory(); recorder = new EncounterRecorder(); pending.Clear(); players.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 && playerIdentity.Protocol == 1 && 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())) { playerIdentity.Peer = sender; 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 || clockReply.Protocol != 1 || 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.Protocol == 1 && !(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); } 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 = players.Values.ToList(), 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) <= recorder.Radius * recorder.Radius)) && (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.Protocol == 1 && !(combatFeed.Session != session)) { AcceptFeed(combatFeed); } } } private void AcceptFeed(CombatFeed feed) { if (feed == null || historyPath.Length == 0) { return; } if (!ZNet.instance.IsServer()) { players.Clear(); foreach (PlayerIdentity player in feed.Players) { players[player.Peer] = player; } } Plugin.View.Receive(feed.Session, feed.Time, feed.Fights, feed.Redirects); } private void LoadHistory(string path) { //IL_005b: Expected O, but got Unknown SaveHistory(); Plugin.View.Changed -= SaveHistory; Plugin.View = new CombatHistory(); historyPath = path; if (File.Exists(path)) { try { Plugin.View.Saved = JsonConvert.DeserializeObject<SavedHistory>(File.ReadAllText(path), Json) ?? new SavedHistory(); } catch (JsonException ex) { JsonException ex2 = ex; string text = path + ".invalid-" + DateTime.UtcNow.ToString("yyyyMMdd-HHmmss"); File.Copy(path, text); Plugin.Log.LogError((object)("Could not read combat history; preserved it at " + text + ": " + ((Exception)(object)ex2).Message)); } } Plugin.View.Changed += SaveHistory; } private void SaveHistory() { if (historyPath.Length > 0) { Storage.Write(historyPath, Encode(Plugin.View.Saved)); } } 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.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; public bool IsVisible => _alpha > 0.01f; 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.Enabled || 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.View.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_022e: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) if (!EnsureUi() || (Object)(object)_root == (Object)null) { return; } ModConfig settings = Plugin.Settings; float num = Mathf.Max(160f, settings.CompactWidth.Value); bool value = settings.CompactBars.Value; float width = num - 22f; EncounterSnapshot encounterSnapshot = Snapshot(); List<CombatantRow> list = Choose(encounterSnapshot, settings.CompactRows.Value); float leader = ((list.Count > 0) ? list[0].Total : 0f); long num2 = DamageCollector.LocalId(); float num3 = 0f; if ((Object)(object)_caption != (Object)null) { bool value2 = settings.CompactCaption.Value; ((Component)_caption).gameObject.SetActive(value2); if (value2) { _caption.text = Localization.instance.Localize(encounterSnapshot.Name) + " | " + Seconds(encounterSnapshot.Duration) + " | " + encounterSnapshot.Result; UiStyle.PlaceTopStretch(_caption.rectTransform, 0f, 0f, 0f, 14f); num3 += 14f; } } for (int i = 0; i < list.Count; i++) { MeterRow meterRow = RowAt(i); meterRow.SetActive(active: true); meterRow.Apply(list[i], leader, width, value, settings.CompactShare.Value, list[i].Id == num2, string.Empty); meterRow.Place(num3); num3 += MeterRow.Height(value); } for (int j = list.Count; j < _rows.Count; j++) { _rows[j].SetActive(active: false); } if ((Object)(object)_list != (Object)null) { _list.sizeDelta = new Vector2(_list.sizeDelta.x, num3); } _root.sizeDelta = new Vector2(num, 7f + num3 + 9f); ((Transform)_root).localScale = Vector3.one * Mathf.Clamp(settings.CompactScale.Value, 0.5f, 2f); UiStyle.Anchor(_root, settings.CompactAnchor.Value, settings.CompactOffset.Value); if ((Object)(object)_background != (Object)null) { Color color = ((Graphic)_background).color; ((Graphic)_background).color = new Color(color.r, color.g, color.b, settings.CompactOpacity.Value); } } private EncounterSnapshot Snapshot() { return Plugin.View.Snapshot(0, CombatChannel.DamageDealt, DamageCollector.LocalId()); } private List<CombatantRow> Choose(EncounterSnapshot snapshot, int limit) { List<CombatantRow> list = new List<CombatantRow>(); long num = DamageCollector.LocalId(); bool flag = false; for (int i = 0; i < snapshot.Rows.Count; i++) { if (list.Count >= limit) { break; } list.Add(snapshot.Rows[i]); flag |= snapshot.Rows[i].Id == num; } if (flag || list.Count < limit) { return list; } for (int j = limit; j < snapshot.Rows.Count; j++) { if (snapshot.Rows[j].Id == num) { list[list.Count - 1] = snapshot.Rows[j]; break; } } return list; } internal static string Seconds(double seconds) { return (int)seconds / 60 + ":" + ((int)seconds % 60).ToString("00"); } private MeterRow RowAt(int index) { while (_rows.Count <= index) { _rows.Add(MeterRow.Create(_list, detailed: false, null)); } return _rows[index]; } private bool EnsureUi() { if (_failed) { return false; } if ((Object)(object)_root != (Object)null) { return true; } try { Build(); } catch (Exception arg) { _failed = true; if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)((Component)_root).gameObject); _root = null; } Plugin.Log.LogError((object)$"Could not build the compact meter; it stays hidden until restart: {arg}"); return false; } return (Object)(object)_root != (Object)null; } private void Build() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_00a1: 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_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: 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_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown //IL_0260: Unknown result type (might be due to invalid IL or missing references) _rows.Clear(); _list = null; _caption = null; _background = null; _group = null; Transform val = UiStyle.FindCanvas(); if (!((Object)(object)val == (Object)null) && UiStyle.ReadStyle()) { GameObject val2 = new GameObject("CombatStats Compact", new Type[2] { typeof(RectTransform), typeof(CanvasGroup) }); _root = (RectTransform)val2.transform; ((Transform)_root).SetParent(val, false); _root.sizeDelta = new Vector2(Plugin.Settings.CompactWidth.Value, 80f); _group = val2.GetComponent<CanvasGroup>(); _group.alpha = 0f; _group.interactable = false; _group.blocksRaycasts = false; val2.SetActive(false); _background = UiStyle.Box("Background", _root, new Color(0.03f, 0.028f, 0.024f, Plugin.Settings.CompactOpacity.Value)); _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 val3 = new GameObject("List", new Type[1] { typeof(RectTransform) }); _list = (RectTransform)val3.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 sealed class KindLine { public const float Height = 15f; private readonly RectTransform _root; private readonly TMP_Text _name; private readonly TMP_Text _amount; private readonly TMP_Text _share; private KindLine(RectTransform root, TMP_Text name, TMP_Text amount, TMP_Text share) { _root = root; _name = name; _amount = amount; _share = share; } public static KindLine 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_011f: 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_0153: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)new GameObject("Kind", 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, 15f); TMP_Text val2 = UiStyle.Text("Name", val, 12.5f, (TextAlignmentOptions)4097, UiStyle.Dim); val2.richText = false; val2.rectTransform.offsetMin = new Vector2(22f, 0f); val2.rectTransform.offsetMax = new Vector2(-250f, 0f); TMP_Text val3 = UiStyle.Text("Amount", val, 12.5f, (TextAlignmentOptions)4100, UiStyle.Dim); val3.rectTransform.anchorMin = new Vector2(1f, 0f); val3.rectTransform.anchorMax = new Vector2(1f, 1f); val3.rectTransform.offsetMin = new Vector2(-250f, 0f); val3.rectTransform.offsetMax = new Vector2(-152f, 0f); TMP_Text val4 = UiStyle.Text("Share", val, 12.5f, (TextAlignmentOptions)4100, UiStyle.Dim); val4.rectTransform.anchorMin = new Vector2(1f, 0f); val4.rectTransform.anchorMax = new Vector2(1f, 1f); val4.rectTransform.offsetMin = new Vector2(-68f, 0f); val4.rectTransform.offsetMax = new Vector2(0f, 0f); return new KindLine(val, val2, val3, val4); } public void ApplyText(string name, string amount, string share) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) _name.text = Localization.instance.Localize(name); ((Graphic)_name).color = UiStyle.Dim; _amount.text = amount; _share.text = share; } public void Apply(DamageKind kind, float amount, float share) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) _name.text = KindName(kind); ((Graphic)_name).color = UiStyle.Colors[(int)kind]; _amount.text = MeterRow.Format(amount); _share.text = (share * 100f).ToString("0") + "%"; } 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 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 static readonly CombatChannel[] Channels = new CombatChannel[3] { CombatChannel.DamageDealt, CombatChannel.DamageTaken, CombatChannel.Healing }; private readonly List<MeterRow> _rows = new List<MeterRow>(); private readonly List<KindLine> _kindLines = new List<KindLine>(); private readonly List<Button> _windowTabs = new List<Button>(); private readonly List<Button> _channelTabs = new List<Button>(); 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 List<int> _windows = new List<int>(); private int _window; private int _historyIndex; private int _breakdown; private TMP_Text _breakdownLabel; private TMP_Text _rateHeading; private CombatChannel _channel; private float _nextRefresh; private float _scrolled; private bool _failed; private bool _open; private bool _seededExpanded; public static bool IsOpen { get; private set; } public static DetailWindow Instance { get; private set; } public static void FeedScrollWheel(float amount) { DetailWindow instance = Instance; if (!((Object)(object)instance == (Object)null) && instance._open) { instance._scrolled = Mathf.Max(0f, instance._scrolled - Mathf.Sign(amount) * 48f); instance.Layout(); } } public void Toggle() { if (_open) { Close(); } else if (EnsureUi() && !((Object)(object)_panel == (Object)null)) { _open = true; IsOpen = true; _scrolled = 0f; ((Component)_panel).gameObject.SetActive(true); Refresh(); } } public void Close() { _open = false; IsOpen = 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; IsOpen = false; } } private void Update() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (Plugin.Settings != null && Plugin.Enabled) { 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_panel == (Object)null)) { Vector2 value = Plugin.Settings.DetailSize.Value; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(Mathf.Max(600f, value.x), Mathf.Max(280f, value.y)); if (_panel.sizeDelta != val) { _panel.sizeDelta = val; } RefreshTabs(); Layout(); } } private void RefreshTabs() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < _windowTabs.Count; i++) { ((Graphic)UiStyle.LabelOf(_windowTabs[i])).color = ((i == _window) ? UiStyle.Mine : UiStyle.Dim); } for (int j = 0; j < _channelTabs.Count; j++) { ((Graphic)UiStyle.LabelOf(_channelTabs[j])).color = ((Channels[j] == _channel) ? UiStyle.Mine : UiStyle.Dim); } if ((Object)(object)_rateHeading != (Object)null) { _rateHeading.text = ((_channel == CombatChannel.Healing) ? "HPS" : ((_channel == CombatChannel.DamageTaken) ? "Taken/s" : "DPS")); } if ((Object)(object)_breakdownLabel != (Object)null) { _breakdownLabel.text = (new string[4] { "Types", (_channel == CombatChannel.DamageTaken) ? "Sources" : "Targets", "Skills", "Pets" })[_breakdown]; } } private void Layout() { //IL_0105: 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) //IL_04db: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_0525: Unknown result type (might be due to invalid IL or missing references) //IL_054f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_content == (Object)null || (Object)(object)_viewport == (Object)null || (Object)(object)_summary == (Object)null) { return; } EncounterSnapshot encounterSnapshot = Plugin.View.Snapshot(_window, _channel, DamageCollector.LocalId(), _historyIndex); _summary.richText = false; _summary.text = Localization.instance.Localize(encounterSnapshot.Name) + " | " + encounterSnapshot.Result + " | " + CompactMeter.Seconds(encounterSnapshot.Duration) + "\nTotal " + MeterRow.Format(encounterSnapshot.Total) + " | " + ((_channel == CombatChannel.Healing) ? "HPS received " : ((_channel == CombatChannel.DamageTaken) ? "Taken/s " : "DPS ")) + MeterRow.Format(encounterSnapshot.Dps); Rect rect = _viewport.rect; float width = ((Rect)(ref rect)).width; float leader = ((encounterSnapshot.Rows.Count > 0) ? encounterSnapshot.Rows[0].Total : 0f); long num = DamageCollector.LocalId(); 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; } KindLine kindLine = KindAt(num3++); kindLine.SetActive(active: true); kindLine.ApplyText("Hits " + row.Hits + " | Ticks " + row.Ticks + " | Avg/event " + MeterRow.Format(row.Average), "Overkill " + MeterRow.Format(row.Overkill), ""); kindLine.Place(num4); num4 += 15f; if (_breakdown == 0) { for (int i = 0; i < 11; i++) { float num5 = row.ByKind[i]; if (!(num5 <= 0f)) { KindLine kindLine2 = KindAt(num3++); kindLine2.SetActive(active: true); kindLine2.Apply((DamageKind)i, num5, (row.Total > 0f) ? (num5 / row.Total) : 0f); kindLine2.Place(num4); num4 += 15f; } } } else { foreach (KeyValuePair<string, float> item in from x in (_breakdown == 1) ? row.Targets : ((_breakdown == 2) ? row.Skills : row.Pets) where x.Value > 0f orderby x.Value descending select x) { KindLine kindLine3 = KindAt(num3++); kindLine3.SetActive(active: true); kindLine3.ApplyText(item.Key, MeterRow.Format(item.Value), (row.Total > 0f) ? ((100f * item.Value / row.Total).ToString("0") + "%") : ""); kindLine3.Place(num4); num4 += 15f; } } num4 += 4f; } for (int num6 = num2; num6 < _rows.Count; num6++) { _rows[num6].SetActive(active: false); } for (int num7 = num3; num7 < _kindLines.Count; num7++) { _kindLines[num7].SetActive(active: false); } if ((Object)(object)_empty != (Object)null) { ((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 KindLine KindAt(int index) { while (_kindLines.Count <= index) { _kindLines.Add(KindLine.Create(_content)); } return _kindLines[index]; } private bool EnsureUi() { if (_failed) { return false; } if ((Object)(object)_panel != (Object)null) { return true; } try { Build(); } catch (Exception arg) { _failed = true; if ((Object)(object)_panel != (Object)null) { Object.Destroy((Object)(object)((Component)_panel).gameObject); _panel = null; } Plugin.Log.LogError((object)$"Could not build the detail window; it stays closed until restart: {arg}"); return false; } return (Object)(object)_panel != (Object)null; } private void Build() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016b: 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_01f1: 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_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0231: 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_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_026f: 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_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) _rows.Clear(); _kindLines.Clear(); _windowTabs.Clear(); _channelTabs.Clear(); _windows = new List<int>(); _content = null; _viewport = null; _summary = null; _empty = null; Transform val = UiStyle.FindCanvas(); if ((Object)(object)val == (Object)null || !UiStyle.ReadStyle()) { Plugin.Debug("The detail window waits for the game UI"); return; } Vector2 value = Plugin.Settings.DetailSize.Value; GameObject val2 = new GameObject("CombatStats Detail", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(GraphicRaycaster), typeof(Image) }); _panel = (RectTransform)val2.transform; ((Transform)_panel).SetParent(val, false); _panel.anchorMin = new Vector2(0.5f, 0.5f); _panel.anchorMax = new Vector2(0.5f, 0.5f); _panel.pivot = new Vector2(0.5f, 0.5f); _panel.anchoredPosition = Vector2.zero; _panel.sizeDelta = new Vector2(Mathf.Max(600f, value.x), Mathf.Max(280f, value.y)); Canvas component = val2.GetComponent<Canvas>(); component.overrideSorting = true; component.sortingOrder = 100; UiStyle.StylePanel(val2.GetComponent<Image>()); TMP_Text obj = UiStyle.Text("Title", _panel, 20f, (TextAlignmentOptions)4097, UiStyle.FontColor); UiStyle.PlaceTopLeft(obj.rectTransform, 14f, 10f, 300f, 26f); obj.text = "Combat stats"; RectTransform val3 = (RectTransform)((Component)UiStyle.TextButton("Settings", _panel, "settings", 14f, UiStyle.Dim, OpenSettings)).transform; val3.anchorMin = new Vector2(1f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(1f, 1f); val3.anchoredPosition = new Vector2(-14f, -12f); val3.sizeDelta = new Vector2(84f, 22f); BuildWindowTabs(); BuildChannelTabs(); _summary = UiStyle.Text("Summary", _panel, 13f, (TextAlignmentOptions)4097, UiStyle.Dim); UiStyle.PlaceTopStretch(_summary.rectTransform, 14f, 14f, 100f, 36f); TMP_Text obj2 = UiStyle.Text("Header", _panel, 11f, (TextAlignmentOptions)4097, UiStyle.Dim); UiStyle.PlaceTopStretch(obj2.rectTransform, 14f, 14f, 148f, 14f); obj2.text = "Player"; Column("Total", 250f, 152f); Column("DPS", 145f, 75f); _rateHeading = ((Component)((Transform)_panel).Find("ColumnDPS")).GetComponent<TMP_Text>(); Column("Share", 68f, 0f); Image obj3 = UiStyle.Box("Separator", _panel, new Color(0.45f, 0.38f, 0.25f, 0.5f)); obj3.sprite = UiStyle.Flat(); UiStyle.PlaceTopStretch(((Graphic)obj3).rectTransform, 14f, 14f, 164f, 1f); BuildScroll(); _empty = UiStyle.Text("Empty", _viewport, 14f, (TextAlignmentOptions)514, UiStyle.Dim); _empty.text = "No combat recorded for this selection."; ((Component)_empty).gameObject.SetActive(false); val2.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 void Column(string text, float from, float to) { //IL_001b: 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_0051: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) TMP_Text obj = UiStyle.Text("Column" + text, _panel, 11f, (TextAlignmentOptions)4100, UiStyle.Dim); obj.text = text; RectTransform rectTransform = obj.rectTransform; rectTransform.anchorMin = new Vector2(1f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(1f, 1f); rectTransform.offsetMin = new Vector2(0f - (from + 14f), -162f); rectTransform.offsetMax = new Vector2(0f - (to + 14f), -148f); } private void BuildWindowTabs() { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Expected O, but got Unknown //IL_01f1: 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_022c: Expected O, but got Unknown foreach (Button windowTab in _windowTabs) { Object.Destroy((Object)(object)((Component)windowTab).gameObject); } _windowTabs.Clear(); if ((Object)(object)_panel == (Object)null) { return; } _windows = new List<int> { 0, 1, 2, 3 }; float num = 14f; foreach (int window in _windows) { int captured = window; Button val = UiStyle.TextButton("Selection" + window, _panel, Label(window), 14f, UiStyle.Dim, delegate { _window = captured; _scrolled = 0f; Refresh(); }); UiStyle.PlaceTopLeft((RectTransform)((Component)val).transform, num, 42f, 72f, 22f); num += 74f; _windowTabs.Add(val); } UiStyle.PlaceTopLeft((RectTransform)((Component)UiStyle.TextButton("Earlier", _panel, "Earlier", 13f, UiStyle.Dim, delegate { _window = 3; _historyIndex = Math.Min(Plugin.View.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 { _window = 3; _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.View.Reset() && Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowMessage((MessageType)1, "Finish combat before resetting history.", 0, (Sprite)null, false, true); } _historyIndex = 0; Refresh(); })).transform, num + 150f, 42f, 65f, 22f); } private void BuildChannelTabs() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown if ((Object)(object)_panel == (Object)null) { return; } float num = 14f; CombatChannel[] channels = Channels; for (int i = 0; i < channels.Length; i++) { CombatChannel combatChannel = channels[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, 20f); num += 132f; _channelTabs.Add(val); } Button val2 = UiStyle.TextButton("Breakdown", _panel, "Types", 13f, UiStyle.Dim, delegate { _breakdown = (_breakdown + 1) % 4; _scrolled = 0f; Refresh(); }); UiStyle.PlaceTopLeft((RectTransform)((Component)val2).transform, num + 10f, 70f, 100f, 20f); _breakdownLabel = UiStyle.LabelOf(val2); } private void OpenSettings() { (SettingsPanel.Instance ?? ((Component)this).gameObject.AddComponent<SettingsPanel>()).Toggle(); } private static string Label(int selection) { return (new string[4] { "Current", "Last", "Overall", "History" })[selection]; } private static string Label(CombatChannel channel) { return channel switch { CombatChannel.Healing => "Healing received", CombatChannel.DamageTaken => "Damage taken", _ => "Damage done", }; } } internal sealed class MeterRow { public const float LineHeight = 17f; public const float BarHeight = 6f; private const float BarGap = 2f; private const float RowGap = 4f; private static readonly NumberFormatInfo Numbers = BuildNumberFormat(); 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) //IL_00a5: 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_00bb: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown 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); if (onClick != null) { Image val3 = val.AddComponent<Image>(); ((Graphic)val3).color = new Color(1f, 1f, 1f, 0f); Button obj = val.AddComponent<Button>(); ColorBlock colors = ((Selectable)obj).colors; ((ColorBlock)(ref colors)).normalColor = new Color(1f, 1f, 1f, 0f); ((ColorBlock)(ref colors)).highlightedColor = new Color(1f, 1f, 1f, 0.1f); ((ColorBlock)(ref colors)).pressedColor = new Color(1f, 1f, 1f, 0.18f); ((ColorBlock)(ref colors)).selectedColor = new Color(1f, 1f, 1f, 0f); ((ColorBlock)(ref colors)).disabledColor = new Color(1f, 1f, 1f, 0f); ((ColorBlock)(ref colors)).fadeDuration = 0.05f; ((Selectable)obj).colors = colors; ((Selectable)obj).targetGraphic = (Graphic)(object)val3; MeterRow created = null; ((UnityEvent)obj.onClick).AddListener((UnityAction)delegate { if (created != null) { onClick(created.Id); } }); return created = Build(val2, detailed); } return Build(val2, detailed); } public void Apply(CombatantRow row, float leader, float width, bool bars, bool showShare, bool local, string prefix) { //IL_006f: 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) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) Id = row.Id; string text = (string.IsNullOrEmpty(row.Name) ? "..." : row.Name); if (row.Id < -1099511627776L && Localization.instance != null) { text = Localization.instance.Localize(text); } _name.text = prefix + text; ((Graphic)_name).color = (local ? UiStyle.Mine : UiStyle.FontColor); string text2 = Format(row.Total); if (_detailed) { _value.text = text2; if ((Object)(object)_share != (Object)null) { _share.text = Percent(row.Share); } if ((Object)(object)_rate != (Object)null) { _rate.text = Format(row.Dps); } } else { _value.text = (showShare ? (text2 + " " + Format(row.Dps) + "/s <color=#" + ColorUtility.ToHtmlStringRGB(UiStyle.Dim) + ">" + Percent(row.Share) + "</color>") : (text2 + " " + Format(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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: 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_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: 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; Line(val.rectTransform); val.rectTransform.offsetMax = new Vector2(detailed ? (-250f) : (-185f), val.rectTransform.offsetMax.y); TMP_Text val2 = UiStyle.Text("Value", root, 15f, (TextAlignmentOptions)4100, UiStyle.FontColor); Line(val2.rectTransform); TMP_Text val3 = null; TMP_Text val4 = null; if (detailed) { PinRight(val2.rectTransform, 250f, 152f); val3 = UiStyle.Text("Share", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); Line(val3.rectTransform); PinRight(val3.rectTransform, 68f, 0f); val4 = UiStyle.Text("Rate", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); Line(val4.rectTransform); PinRight(val4.rectTransform, 145f, 75f); } else { PinRight(val2.rectTransform, 185f, 0f); } 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, new Color(0f, 0f, 0f, 0.45f)); 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 static void Line(RectTransform rect) { //IL_000b: 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_0035: 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_005f: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = new Vector2(0f, 1f); rect.anchorMax = new Vector2(1f, 1f); rect.pivot = new Vector2(0.5f, 1f); rect.offsetMin = new Vector2(0f, -17f); rect.offsetMax = new Vector2(0f, 0f); } private static void PinRight(RectTransform rect, float from, float to) { //IL_000b: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) rect.anchorMin = new Vector2(1f, 1f); rect.anchorMax = new Vector2(1f, 1f); rect.pivot = new Vector2(1f, 1f); rect.offsetMin = new Vector2(0f - from, -17f); rect.offsetMax = new Vector2(0f - to, 0f); } private static NumberFormatInfo BuildNumberFormat() { NumberFormatInfo obj = (NumberFormatInfo)CultureInfo.InvariantCulture.NumberFormat.Clone(); obj.NumberGroupSeparator = " "; return obj; } public static string Format(float value) { if (!(value >= 10f)) { return value.ToString("0.#", CultureInfo.InvariantCulture); } return value.ToString("#,0", Numbers); } private static string Percent(float share) { return (share * 100f).ToString("0", CultureInfo.InvariantCulture) + "%"; } 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 = 300f; private readonly List<Control> _rows = new List<Control>(); private RectTransform _panel; private bool _failed; private bool _open; public static SettingsPanel Instance { get; private set; } public static bool IsOpen { get; private set; } public void Toggle() { if (_open) { Close(); } else if (EnsureUi() && !((Object)(object)_panel == (Object)null)) { _open = true; IsOpen = true; ((Component)_panel).gameObject.SetActive(true); RefreshValues(); } } public void Close() { _open = false; IsOpen = 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; IsOpen = false; } } private void RefreshValues() { foreach (Control row in _rows) { row.Refresh(); } } private bool EnsureUi() { if (_failed) { return false; } if ((Object)(object)_panel != (Object)null) { return true; } try { Build(); } catch (Exception arg) { _failed = true; if ((Object)(object)_panel != (Object)null) { Object.Destroy((Object)(object)((Component)_panel).gameObject); _panel = null; } Plugin.Log.LogError((object)$"Could not build the settings panel; the config file still works: {arg}"); return false; } return (Object)(object)_panel != (Object)null; } private void Build() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_009b: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_019a: 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_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_04bb: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04ec: Unknown result type (might be due to invalid IL or missing references) _rows.Clear(); Transform val = UiStyle.FindCanvas(); if (!((Object)(object)val == (Object)null) && UiStyle.ReadStyle()) { GameObject val2 = new GameObject("CombatStats Settings", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(GraphicRaycaster), typeof(Image) }); _panel = (RectTransform)val2.transform; ((Transform)_panel).SetParent(val, false); _panel.anchorMin = new Vector2(0.5f, 0.5f); _panel.anchorMax = new Vector2(0.5f, 0.5f); _panel.pivot = new Vector2(0.5f, 0.5f); _panel.anchoredPosition = Vector2.zero; _panel.sizeDelta = new Vector2(680f, 470f); Canvas component = val2.GetComponent<Canvas>(); component.overrideSorting = true; component.sortingOrder = 110; UiStyle.StylePanel(val2.GetComponent<Image>()); TMP_Text obj = UiStyle.Text("Title", _panel, 19f, (TextAlignmentOptions)4097, UiStyle.FontColor); UiStyle.PlaceTopLeft(obj.rectTransform, 16f, 10f, 400f, 24f); obj.text = "Combat Stats - settings"; RectTransform val3 = (RectTransform)((Component)UiStyle.TextButton("Close", _panel, "close", 14f, UiStyle.Dim, Close)).transform; val3.anchorMin = new Vector2(1f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(1f, 1f); val3.anchoredPosition = new Vector2(-16f, -12f); val3.sizeDelta = new Vector2(70f, 22f); ModConfig settings = Plugin.Settings; float x = 16f; float x2 = 356f; float y = 46f; y = Heading("Compact window", x, y); y = Choice(x, y, "When to show", () => Word(settings.CompactMode.Value), delegate(int step) { settings.CompactMode.Value = (MeterMode)Wrap((int)(settings.CompactMode.Value + step), 3); }); y = Number(x, y, "Rows", () => settings.CompactRows.Value.ToString(CultureInfo.InvariantCulture), delegate(int step) { settings.CompactRows.Value = Mathf.Clamp(settings.CompactRows.Value + step, 1, 12); }); y = Choice(x, y, "Cor