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.3
plugins/CombatStats/CombatStats.dll
Decompiled 2 days 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; 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 Microsoft.CodeAnalysis; using Muindor.ServerConfig; using Muindor.Windows; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Egor Fadeev")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A combat meter: who dealt how much damage and of which damage type, over the last 30 seconds, 5, 10 or 30 minutes. A compact window that shows itself when a fight starts, and a detail window with the breakdown.")] [assembly: AssemblyFileVersion("1.0.3.0")] [assembly: AssemblyInformationalVersion("1.0.3+363c85e6df7e472b60ad6278f7edd7e425d81ecc")] [assembly: AssemblyProduct("Combat Stats")] [assembly: AssemblyTitle("CombatStats")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace 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 Muindor.ServerConfig { public static class ConfigChannel { private static ConfigSync? _sync; private static ManualLogSource? _log; private static string _rpcName = string.Empty; public static void Activate(ConfigSync sync, ManualLogSource log) { _sync = sync; _log = log; _rpcName = sync.PluginGuid + ".ServerConfig"; sync.Changed += Broadcast; } internal static void OnNewConnection(ZNet net, ZNetPeer peer) { if (_sync != null && !net.IsServer() && peer != null && peer.m_server && peer.m_rpc != null) { _sync.Clear(); peer.m_rpc.Register<string>(_rpcName, (Action<ZRpc, string>)Receive); } } internal static void OnPeerInfo(ZNet net, ZRpc rpc) { if (_sync == null || !net.IsServer()) { return; } ZNetPeer peer = net.GetPeer(rpc); if (peer != null && peer.IsReady() && peer.m_rpc != null) { peer.m_rpc.Invoke(_rpcName, new object[1] { _sync.BuildPayload().Serialize() }); ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)("Sent the configuration to " + peer.m_playerName + " (ConfigPriority " + (_sync.Priority.Value ? "on" : "off") + ")")); } } } internal static void OnSessionEnd() { _sync?.Clear(); } internal static void PatchFailed(string patch, Exception exception) { ManualLogSource? log = _log; if (log != null) { log.LogError((object)$"{patch} failed, the configuration channel may be inactive: {exception}"); } } private static void Broadcast() { ZNet instance = ZNet.instance; if (_sync == null || (Object)(object)instance == (Object)null || !instance.IsServer()) { return; } string text = _sync.BuildPayload().Serialize(); int num = 0; foreach (ZNetPeer peer in instance.GetPeers()) { if (peer.IsReady() && peer.m_rpc != null) { peer.m_rpc.Invoke(_rpcName, new object[1] { text }); num++; } } if (num > 0) { ManualLogSource? log = _log; if (log != null) { log.LogInfo((object)$"A setting changed; sent the configuration to {num} player(s)"); } } } private static void Receive(ZRpc rpc, string text) { if (_sync == null) { return; } ZNet instance = ZNet.instance; ConfigPayload payload; string error; if ((Object)(object)instance != (Object)null && instance.IsServer()) { ManualLogSource? log = _log; if (log != null) { log.LogWarning((object)"Ignored a configuration sent to this server by a client"); } } else if (!ConfigPayload.TryParse(text, out payload, out error)) { ManualLogSource? log2 = _log; if (log2 != null) { log2.LogWarning((object)("Ignored the configuration from the server, keeping the local settings: " + error)); } } else { _sync.Apply(payload); } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal static class ZNet_OnNewConnection_Patch { private static void Postfix(ZNet __instance, ZNetPeer peer) { try { ConfigChannel.OnNewConnection(__instance, peer); } catch (Exception exception) { ConfigChannel.PatchFailed("OnNewConnection", exception); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class ZNet_RPC_PeerInfo_Patch { private static void Postfix(ZNet __instance, ZRpc rpc) { try { ConfigChannel.OnPeerInfo(__instance, rpc); } catch (Exception exception) { ConfigChannel.PatchFailed("RPC_PeerInfo", exception); } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] internal static class ZNet_OnDestroy_Patch { private static void Postfix() { try { ConfigChannel.OnSessionEnd(); } catch (Exception exception) { ConfigChannel.PatchFailed("OnDestroy", exception); } } } public sealed class ConfigPayload { public const string Header = "muindor.config/1"; public string ModVersion { get; } public bool Priority { get; } public IList<KeyValuePair<string, string>> Values { get; } public ConfigPayload(string modVersion, bool priority, IEnumerable<KeyValuePair<string, string>>? values = null) { ModVersion = modVersion ?? string.Empty; Priority = priority; List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(); if (values != null) { list.AddRange(values); } Values = list; } public string Serialize() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("muindor.config/1").Append('\n'); stringBuilder.Append(Escape(ModVersion, escapeEquals: false)).Append('\n'); stringBuilder.Append(Priority ? "true" : "false").Append('\n'); foreach (KeyValuePair<string, string> value in Values) { stringBuilder.Append(Escape(value.Key, escapeEquals: true)).Append('=').Append(Escape(value.Value ?? string.Empty, escapeEquals: false)) .Append('\n'); } return stringBuilder.ToString(); } public static bool TryParse(string? text, out ConfigPayload? payload, out string error) { payload = null; if (string.IsNullOrEmpty(text)) { error = "the payload is empty"; return false; } string[] array = text.Split(new char[1] { '\n' }); if (array.Length < 3) { error = "the payload is truncated"; return false; } if (Trim(array[0]) != "muindor.config/1") { error = "unknown format \"" + Trim(array[0]) + "\", expected \"muindor.config/1\""; return false; } string modVersion = Unescape(Trim(array[1])); string text2 = Trim(array[2]); if (text2 != "true" && text2 != "false") { error = "\"" + text2 + "\" is not a valid priority flag"; return false; } List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(); for (int i = 3; i < array.Length; i++) { string text3 = Trim(array[i]); if (text3.Length != 0) { int num = IndexOfSeparator(text3); if (num < 0) { error = $"line {i + 1} has no key/value separator"; return false; } string text4 = Unescape(text3.Substring(0, num)); if (text4.Length == 0) { error = $"line {i + 1} has an empty key"; return false; } list.Add(new KeyValuePair<string, string>(text4, Unescape(text3.Substring(num + 1)))); } } payload = new ConfigPayload(modVersion, text2 == "true", list); error = string.Empty; return true; } private static string Trim(string line) { if (!line.EndsWith("\r", StringComparison.Ordinal)) { return line; } return line.Substring(0, line.Length - 1); } private static int IndexOfSeparator(string line) { for (int i = 0; i < line.Length; i++) { if (line[i] == '\\') { i++; } else if (line[i] == '=') { return i; } } return -1; } private static string Escape(string value, bool escapeEquals) { StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { switch (c) { case '\\': stringBuilder.Append("\\\\"); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\r': stringBuilder.Append("\\r"); continue; case '=': if (escapeEquals) { stringBuilder.Append("\\="); continue; } break; } stringBuilder.Append(c); } return stringBuilder.ToString(); } private static string Unescape(string value) { if (value.IndexOf('\\') < 0) { return value; } StringBuilder stringBuilder = new StringBuilder(value.Length); for (int i = 0; i < value.Length; i++) { if (value[i] != '\\' || i + 1 >= value.Length) { stringBuilder.Append(value[i]); continue; } char c = value[++i]; switch (c) { case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; default: stringBuilder.Append(c); break; } } return stringBuilder.ToString(); } } public sealed class ConfigSync { private const string SyncedNote = "[synced] "; private readonly Dictionary<string, ISyncedEntry> _byKey = new Dictionary<string, ISyncedEntry>(StringComparer.Ordinal); private readonly List<ISyncedEntry> _entries = new List<ISyncedEntry>(); private readonly HashSet<ConfigEntryBase> _watched = new HashSet<ConfigEntryBase>(); private readonly ConfigFile _config; private readonly ManualLogSource _log; public string PluginGuid { get; } public string ModName { get; } public string ModVersion { get; } public ConfigEntry<bool> Priority { get; } public bool Locked { get; private set; } public string? ServerVersion { get; private set; } public int LockedCount { get; private set; } public event Action? Changed; public ConfigSync(ConfigFile config, string pluginGuid, string modName, string modVersion, ManualLogSource log) { _config = config; _log = log; PluginGuid = pluginGuid; ModName = modName; ModVersion = modVersion; Priority = config.Bind<bool>("Server", "ConfigPriority", false, "Server only, and off by default. Turn it on in the configuration of a dedicated server, or of the player hosting the session, to have every client that also has the mod run on the settings marked [synced] below instead of its own. The clients' own config files are never touched: their settings come back the moment they disconnect. This never makes the mod required on either side - players without it are unaffected, and a player with it can still join a server that does not have it."); _watched.Add((ConfigEntryBase)(object)Priority); config.SettingChanged += OnSettingChanged; } public SyncedEntry<T> Bind<T>(string section, string key, T defaultValue, string description) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } public SyncedEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription description) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown ConfigDescription val = new ConfigDescription("[synced] " + description.Description, description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = _config.Bind<T>(section, key, defaultValue, val); SyncedEntry<T> syncedEntry = new SyncedEntry<T>(val2); _byKey.Add(syncedEntry.Key, syncedEntry); _entries.Add(syncedEntry); _watched.Add((ConfigEntryBase)(object)val2); return syncedEntry; } public ConfigPayload BuildPayload() { bool value = Priority.Value; List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>(value ? _entries.Count : 0); if (value) { foreach (ISyncedEntry entry in _entries) { try { list.Add(new KeyValuePair<string, string>(entry.Key, entry.LocalAsString())); } catch (Exception ex) { _log.LogWarning((object)(entry.Key + " could not be sent to the clients: " + ex.Message)); } } } return new ConfigPayload(ModVersion, value, list); } public void Apply(ConfigPayload payload) { ClearValues(); if (!payload.Priority) { Locked = false; ServerVersion = null; LockedCount = 0; _log.LogInfo((object)("The server runs " + ModName + " " + payload.ModVersion + " with ConfigPriority off; keeping the local settings")); return; } int num = 0; List<string> list = new List<string>(); foreach (KeyValuePair<string, string> value2 in payload.Values) { string error; if (!_byKey.TryGetValue(value2.Key, out ISyncedEntry value)) { list.Add(value2.Key); } else if (value.ApplyServerValue(value2.Value, out error)) { num++; } else { _log.LogWarning((object)("Keeping the local " + value2.Key + ": the server sent a value this version cannot read, " + error)); } } Locked = true; ServerVersion = payload.ModVersion; LockedCount = num; int num2 = _entries.Count - num; string text = ((list.Count > 0) ? string.Format(", {0} unknown here ({1})", list.Count, string.Join(", ", list.ToArray())) : string.Empty); string text2 = ((num2 > 0) ? $", {num2} kept local" : string.Empty); _log.LogInfo((object)$"The server ({ModName} {payload.ModVersion}) decides the settings: {num} applied{text2}{text}"); } public void Clear() { if (Locked || LockedCount != 0) { ClearValues(); Locked = false; ServerVersion = null; LockedCount = 0; _log.LogInfo((object)"Back to the local settings"); } } public string Describe() { if (!Locked) { return "local settings"; } return $"settings from the server ({ModName} {ServerVersion}, {LockedCount} values)"; } private void ClearValues() { foreach (ISyncedEntry entry in _entries) { entry.ClearServerValue(); } } private void OnSettingChanged(object sender, SettingChangedEventArgs args) { if (this.Changed != null) { ConfigEntryBase changedSetting = args.ChangedSetting; if (changedSetting != null && _watched.Contains(changedSetting)) { this.Changed(); } } } } public interface ISyncedEntry { string Key { get; } string LocalAsString(); bool ApplyServerValue(string text, out string error); void ClearServerValue(); } public sealed class SyncedEntry<T> : ISyncedEntry { private T _serverValue; private bool _hasServerValue; public ConfigEntry<T> Local { get; } public string Key { get; } public T Value { get { if (!_hasServerValue) { return Local.Value; } return _serverValue; } } public T LocalValue { get { return Local.Value; } set { Local.Value = value; } } public bool IsLocked => _hasServerValue; internal SyncedEntry(ConfigEntry<T> local) { Local = local; Key = ((ConfigEntryBase)local).Definition.Section + "." + ((ConfigEntryBase)local).Definition.Key; } public string LocalAsString() { return TomlTypeConverter.ConvertToString((object)Local.Value, typeof(T)); } public bool ApplyServerValue(string text, out string error) { object obj; try { obj = TomlTypeConverter.ConvertToValue(text, typeof(T)); } catch (Exception ex) { error = "\"" + text + "\" is not a valid " + typeof(T).Name + " (" + ex.Message + ")"; return false; } ConfigDescription description = ((ConfigEntryBase)Local).Description; AcceptableValueBase val = ((description != null) ? description.AcceptableValues : null); if (val != null && !val.IsValid(obj)) { obj = val.Clamp(obj); } _serverValue = (T)obj; _hasServerValue = true; error = string.Empty; return true; } public void ClearServerValue() { _hasServerValue = false; _serverValue = default(T); } public override string ToString() { T value = Value; return ((value != null) ? value.ToString() : null) ?? string.Empty; } } } namespace CombatStats { public sealed class ModConfig { public const string DefaultWindows = "30, 300, 600, 1800"; private readonly ConfigFile _config; public ConfigSync Sync { get; } public SyncedEntry<bool> Enabled { get; } public ConfigEntry<bool> IsDebug { get; } public ConfigEntry<int> HistoryMinutes { get; } public ConfigEntry<bool> CountDamageTaken { get; } public ConfigEntry<bool> CountHealing { get; } public ConfigEntry<bool> CountObjectDamage { get; } public ConfigEntry<bool> CountPets { get; } public ConfigEntry<KeyboardShortcut> CompactKey { get; } public ConfigEntry<MeterMode> CompactMode { get; } public ConfigEntry<int> CompactWindow { get; } public ConfigEntry<int> CompactRows { get; } public ConfigEntry<float> CompactHideAfter { get; } public ConfigEntry<ScreenAnchor> CompactAnchor { get; } public ConfigEntry<Vector2> CompactOffset { get; } public ConfigEntry<float> CompactWidth { get; } public ConfigEntry<float> CompactScale { get; } public ConfigEntry<float> CompactOpacity { get; } public ConfigEntry<bool> CompactCaption { get; } public ConfigEntry<bool> CompactShare { get; } public ConfigEntry<bool> CompactBars { get; } public ConfigEntry<KeyboardShortcut> DetailKey { get; } public ConfigEntry<Vector2> DetailSize { get; } public ConfigEntry<string> DetailWindows { get; } public SyncedEntry<bool> ShareDamage { get; } public SyncedEntry<float> ShareRadius { get; } public ConfigEntry<float> ShareInterval { get; } public ConfigEntry<string> Colors { get; } public int HistorySeconds => Math.Max(60, HistoryMinutes.Value * 60); public int RunningHistorySeconds { get { if (Plugin.HistorySeconds <= 0) { return HistorySeconds; } return Plugin.HistorySeconds; } } public ModConfig(ConfigFile config) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Expected O, but got Unknown //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Expected O, but got Unknown //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Expected O, but got Unknown //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Expected O, but got Unknown //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Expected O, but got Unknown //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_0401: Expected O, but got Unknown //IL_0430: Unknown result type (might be due to invalid IL or missing references) //IL_043a: Expected O, but got Unknown _config = config; Sync = new ConfigSync(config, "muindor.CombatStats", "Combat Stats", "1.0.3", Plugin.Log); Enabled = Sync.Bind("General", "Enabled", defaultValue: true, "Record combat and show the windows. With this off the mod does nothing at all."); IsDebug = config.Bind<bool>("General", "IsDebug", false, "Log every recorded event and every packet. Noisy; for working out why a number looks wrong."); HistoryMinutes = config.Bind<int>("General", "HistoryMinutes", 30, new ConfigDescription("How far back the meter remembers, in minutes. This is also the longest window the detail view can show.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 60), Array.Empty<object>())); CountDamageTaken = config.Bind<bool>("Meters", "CountDamageTaken", false, "Also record the damage players take, on its own tab of the detail window."); CountHealing = config.Bind<bool>("Meters", "CountHealing", false, "Also record healing: food, potions and magic."); CountObjectDamage = config.Bind<bool>("Meters", "CountObjectDamage", false, "Also record damage to trees, ore, buildings and ships, on its own tab, so felling a forest does not drown the combat numbers."); CountPets = config.Bind<bool>("Meters", "CountPets", false, "Count what tamed creatures and summons deal. Their damage goes to the row of the player they follow, or to a row named after the creature when no owner can be found."); CompactKey = config.Bind<KeyboardShortcut>("Compact", "CompactKey", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Cycles the compact window: shown while fighting, always shown, off. Modifiers are allowed, e.g. \"F10 + LeftShift\"."); CompactMode = config.Bind<MeterMode>("Compact", "CompactMode", MeterMode.Auto, "Auto: the window appears with the first hit and fades out once the fight goes quiet. Always: it stays on screen. Off: it is never shown, but the detail window still has the numbers."); CompactWindow = config.Bind<int>("Compact", "CompactWindow", 30, new ConfigDescription("Seconds the compact window adds up.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 600), Array.Empty<object>())); CompactRows = config.Bind<int>("Compact", "CompactRows", 5, new ConfigDescription("How many players the compact window lists. Your own row is always among them.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 12), Array.Empty<object>())); CompactHideAfter = config.Bind<float>("Compact", "CompactHideAfter", 5f, new ConfigDescription("Seconds without a single event before the compact window fades out.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 120f), Array.Empty<object>())); CompactAnchor = config.Bind<ScreenAnchor>("Compact", "CompactAnchor", ScreenAnchor.TopRight, "Which corner or edge of the screen the compact window is pinned to. The offset below is measured from there."); CompactOffset = config.Bind<Vector2>("Compact", "CompactOffset", new Vector2(-28f, -164f), "Position of the compact window from its anchor, in UI pixels (x right, y up). The default puts it under the minimap."); CompactWidth = config.Bind<float>("Compact", "CompactWidth", 320f, new ConfigDescription("Width of the compact window in UI pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(160f, 700f), Array.Empty<object>())); CompactScale = config.Bind<float>("Compact", "CompactScale", 1f, new ConfigDescription("Size of the compact window, 1 being the size the game's own text is.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>())); CompactOpacity = config.Bind<float>("Compact", "CompactOpacity", 0.9f, new ConfigDescription("How solid the backing of the compact window is. It fades towards the bottom from this value.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>())); CompactCaption = config.Bind<bool>("Compact", "CompactCaption", true, "Show the dim caption line (\"damage - 30 s\") above the rows."); CompactShare = config.Bind<bool>("Compact", "CompactShare", true, "Show each player's share of the total, in percent, next to their damage."); CompactBars = config.Bind<bool>("Compact", "CompactBars", true, "Draw the bar under each row, split into the damage kinds it is made of."); DetailKey = config.Bind<KeyboardShortcut>("Detail", "DetailKey", new KeyboardShortcut((KeyCode)100, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Opens and closes the detail window. Modifiers are allowed; set it to \"None\" to disable the window."); DetailSize = config.Bind<Vector2>("Detail", "DetailSize", new Vector2(660f, 480f), "Width and height of the detail window in UI pixels."); DetailWindows = config.Bind<string>("Detail", "DetailWindows", "30, 300, 600, 1800", "The windows the tabs of the detail view offer, in seconds, comma-separated. Anything longer than HistoryMinutes is capped to it."); ShareDamage = Sync.Bind("Sharing", "ShareDamage", defaultValue: true, "Send what this client sees to the other players who have the mod, so everyone's meter is complete. With this off you still see your own damage and everything your own client is host of."); ShareRadius = Sync.Bind("Sharing", "ShareRadius", 96f, new ConfigDescription("Events sent from farther away than this many metres are ignored, so a fight on the other side of the map stays out of your meter.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(16f, 256f), Array.Empty<object>())); ShareInterval = config.Bind<float>("Sharing", "ShareInterval", 0.5f, new ConfigDescription("Seconds between two packets. Events are collected in between and sent together.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); Colors = config.Bind<string>("Colours", "Colors", "", "Overrides for the damage kind colours, comma-separated, for example: Fire=#ff7733, Poison=#66aa44. Kinds left out keep the muted defaults."); } public void Reload() { _config.Reload(); } public List<int> ParseWindows() { List<int> list = new List<int>(); string[] array = (DetailWindows.Value ?? string.Empty).Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 1) { Plugin.Log.LogWarning((object)("DetailWindows: '" + text + "' is not a number of seconds, ignoring it.")); continue; } int item = Math.Min(result, RunningHistorySeconds); if (!list.Contains(item)) { list.Add(item); } } if (list.Count == 0) { list.Add(Math.Min(30, RunningHistorySeconds)); list.Add(RunningHistorySeconds); } list.Sort(); return list; } public Color[] ParseColors() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_013b: 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++) { array[i] = ParseColor(DamageKinds.ColorHex(DamageKinds.All[i]), Color.white); } string[] array2 = (Colors.Value ?? string.Empty).Split(new char[1] { ',' }); Color val = default(Color); for (int j = 0; j < array2.Length; j++) { string text = array2[j].Trim(); if (text.Length == 0) { continue; } int num = text.IndexOf('='); if (num <= 0) { Plugin.Log.LogWarning((object)("Colors: '" + text + "' is not a \"Kind=#rrggbb\" pair, ignoring it.")); continue; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); if (!Enum.TryParse<DamageKind>(text2, ignoreCase: true, out var result)) { Plugin.Log.LogWarning((object)("Colors: '" + text2 + "' is not a damage kind, ignoring it.")); } else if (!ColorUtility.TryParseHtmlString(text3.StartsWith("#", StringComparison.Ordinal) ? text3 : ("#" + text3), ref val)) { Plugin.Log.LogWarning((object)("Colors: '" + text3 + "' is not a colour, ignoring it.")); } else { array[(int)result] = val; } } return array; } public string Describe() { //IL_00d6: 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) CultureInfo invariantCulture = CultureInfo.InvariantCulture; List<string> list = new List<string>(); if (CountDamageTaken.Value) { list.Add("damage taken"); } if (CountHealing.Value) { list.Add("healing"); } if (CountObjectDamage.Value) { list.Add("objects"); } if (CountPets.Value) { list.Add("pets"); } return string.Format("{0}, {1} min of history, ", Enabled.Value ? "enabled" : "disabled", HistoryMinutes.Value) + $"compact window {CompactMode.Value} over {CompactWindow.Value} s ({CompactKey.Value}), " + $"detail window {DetailKey.Value}, " + "sharing " + (ShareDamage.Value ? "on" : "off") + " within " + ShareRadius.Value.ToString("0.#", invariantCulture) + " m, also counting: " + ((list.Count > 0) ? string.Join(", ", list.ToArray()) : "nothing") + ", " + Sync.Describe(); } private static Color ParseColor(string hex, Color fallback) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Color result = default(Color); if (!ColorUtility.TryParseHtmlString(hex, ref result)) { return fallback; } return result; } } [BepInPlugin("muindor.CombatStats", "Combat Stats", "1.0.3")] public sealed class Plugin : BaseUnityPlugin { private Harmony? _harmony; public static ManualLogSource Log { get; private set; } public static ModConfig Settings { get; private set; } internal static DamageCollector Collector { get; private set; } internal static CompactMeter? Compact { get; private set; } internal static DetailWindow? Detail { get; private set; } public static bool Enabled { get { if (Settings != null) { return Settings.Enabled.Value; } return false; } } public static bool Headless { get; private set; } public static int HistorySeconds { get; private set; } private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Invalid comparison between Unknown and I4 //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Settings = new ModConfig(((BaseUnityPlugin)this).Config); ConfigChannel.Activate(Settings.Sync, Log); Headless = (int)SystemInfo.graphicsDeviceType == 4; HistorySeconds = Settings.HistorySeconds; Collector = new DamageCollector(HistorySeconds); if (!Headless) { ((Component)this).gameObject.AddComponent<DamageChannel>(); Compact = ((Component)this).gameObject.AddComponent<CompactMeter>(); Detail = ((Component)this).gameObject.AddComponent<DetailWindow>(); } _harmony = new Harmony("muindor.CombatStats"); _harmony.PatchAll(typeof(Plugin).Assembly); Log.LogInfo((object)("Combat Stats 1.0.3 loaded: " + Settings.Describe())); if (Headless) { Log.LogInfo((object)"No screen on this machine: the meter itself stays off and only the configuration channel runs."); } } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public static void Debug(string message) { if (Settings != null && Settings.IsDebug.Value) { Log.LogInfo((object)message); } } } 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; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "muindor.CombatStats"; public const string PLUGIN_NAME = "Combat Stats"; public const string PLUGIN_VERSION = "1.0.3"; } } 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: shown while fighting", _ => "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; } StatsRecorder statsRecorder = Plugin.Collector.RecorderIfAny(CombatChannel.DamageDealt); if (statsRecorder != null) { return (double)Time.time - statsRecorder.LastEventTime <= (double)Plugin.Settings.CompactHideAfter.Value; } return false; } 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_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0291: 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; WindowSnapshot windowSnapshot = Snapshot(); List<CombatantRow> list = Choose(windowSnapshot, 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 = "damage - " + Seconds(windowSnapshot.WindowSeconds); 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 WindowSnapshot Snapshot() { StatsRecorder statsRecorder = Plugin.Collector.RecorderIfAny(CombatChannel.DamageDealt); if (statsRecorder != null) { return statsRecorder.Snapshot(Time.time, Plugin.Settings.CompactWindow.Value, Plugin.Collector.NameOf); } return WindowSnapshot.Empty; } private List<CombatantRow> Choose(WindowSnapshot 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; } private static string Seconds(int seconds) { if (seconds < 60) { return seconds + " s"; } if (seconds % 60 != 0) { return seconds / 60 + " min " + seconds % 60 + " s"; } return seconds / 60 + " min"; } 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); 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: 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.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(-148f, 0f); val4.rectTransform.offsetMax = new Vector2(-104f, 0f); return new KindLine(val, val2, val3, val4); } 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[4] { CombatChannel.DamageDealt, CombatChannel.DamageTaken, CombatChannel.Healing, CombatChannel.ObjectDamage }; 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 = 30; 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(420f, value.x), Mathf.Max(280f, value.y)); if (_panel.sizeDelta != val) { _panel.sizeDelta = val; } RefreshTabs(); Layout(); } } private void RefreshTabs() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) List<int> windows = Plugin.Settings.ParseWindows(); if (!SameWindows(windows)) { _windows = windows; BuildWindowTabs(); } if (!_windows.Contains(_window)) { _window = ((_windows.Count > 0) ? _windows[0] : 30); } for (int i = 0; i < _windowTabs.Count && i < _windows.Count; i++) { ((Graphic)UiStyle.LabelOf(_windowTabs[i])).color = ((_windows[i] == _window) ? UiStyle.Mine : UiStyle.Dim); } for (int j = 0; j < _channelTabs.Count; j++) { CombatChannel combatChannel = Channels[j]; bool active = Available(combatChannel); ((Component)_channelTabs[j]).gameObject.SetActive(active); ((Graphic)UiStyle.LabelOf(_channelTabs[j])).color = ((combatChannel == _channel) ? UiStyle.Mine : UiStyle.Dim); } if (!Available(_channel)) { _channel = CombatChannel.DamageDealt; } } private static bool Available(CombatChannel channel) { return channel switch { CombatChannel.DamageTaken => Plugin.Settings.CountDamageTaken.Value, CombatChannel.Healing => Plugin.Settings.CountHealing.Value, CombatChannel.ObjectDamage => Plugin.Settings.CountObjectDamage.Value, _ => true, }; } private bool SameWindows(List<int> windows) { if (_windows.Count != windows.Count) { return false; } for (int i = 0; i < windows.Count; i++) { if (_windows[i] != windows[i]) { return false; } } return true; } private void Layout() { //IL_008b: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: 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; } StatsRecorder statsRecorder = Plugin.Collector.RecorderIfAny(_channel); WindowSnapshot windowSnapshot = ((statsRecorder == null) ? WindowSnapshot.Empty : statsRecorder.Snapshot(Time.time, _window, Plugin.Collector.NameOf)); _summary.text = ((windowSnapshot.Hits == 0) ? "nothing in this window" : ("total <color=#" + ColorUtility.ToHtmlStringRGB(UiStyle.FontColor) + ">" + MeterRow.Format(windowSnapshot.Total) + "</color>" + $" hits <color=#{ColorUtility.ToHtmlStringRGB(UiStyle.FontColor)}>{windowSnapshot.Hits}</color>" + " average <color=#" + ColorUtility.ToHtmlStringRGB(UiStyle.FontColor) + ">" + MeterRow.Format(windowSnapshot.Average) + "</color> largest <color=#" + ColorUtility.ToHtmlStringRGB(UiStyle.FontColor) + ">" + MeterRow.Format(windowSnapshot.Max) + "</color>")); Rect rect = _viewport.rect; float width = ((Rect)(ref rect)).width; float leader = ((windowSnapshot.Rows.Count > 0) ? windowSnapshot.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 windowSnapshot.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; } for (int i = 0; i < 11; i++) { float num5 = row.ByKind[i]; if (!(num5 <= 0f)) { KindLine kindLine = KindAt(num3++); kindLine.SetActive(active: true); kindLine.Apply((DamageKind)i, num5, (row.Total > 0f) ? (num5 / row.Total) : 0f); kindLine.Place(num4); num4 += 15f; } } num4 += 4f; } for (int j = num2; j < _rows.Count; j++) { _rows[j].SetActive(active: false); } for (int k = num3; k < _kindLines.Count; k++) { _kindLines[k].SetActive(active: false); } if ((Object)(object)_empty != (Object)null) { ((Component)_empty).gameObject.SetActive(num2 == 0); } float num6 = num4; rect = _viewport.rect; float num7 = Mathf.Max(num6, ((Rect)(ref rect)).height); _content.sizeDelta = new Vector2(0f, num7); float scrolled = _scrolled; rect = _viewport.rect; _scrolled = Mathf.Clamp(scrolled, 0f, Mathf.Max(0f, num7 - ((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_0389: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_042f: 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(420f, 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, 18f); TMP_Text obj2 = UiStyle.Text("Header", _panel, 11f, (TextAlignmentOptions)4097, UiStyle.Dim); UiStyle.PlaceTopStretch(obj2.rectTransform, 14f, 14f, 122f, 14f); obj2.text = "player"; Column("damage", 250f, 152f); Column("share", 148f, 104f); Column("hits", 100f, 52f); Column("avg", 48f, 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, 138f, 1f); BuildScroll(); _empty = UiStyle.Text("Empty", _viewport, 14f, (TextAlignmentOptions)514, UiStyle.Dim); _empty.text = "Nothing recorded in this window yet."; ((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, 146f, 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), -136f); rectTransform.offsetMax = new Vector2(0f - (to + 14f), -122f); } private void BuildWindowTabs() { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0125: 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; } if (_windows.Count == 0) { _windows = Plugin.Settings.ParseWindows(); _window = ((_windows.Count > 0) ? _windows[0] : 30); } float num = 14f; foreach (int window in _windows) { int captured = window; Button val = UiStyle.TextButton("Window" + window, _panel, Label(window), 14f, UiStyle.Dim, delegate { _window = captured; Refresh(); }); UiStyle.PlaceTopLeft((RectTransform)((Component)val).transform, num, 42f, 72f, 22f); num += 74f; _windowTabs.Add(val); } } private void BuildChannelTabs() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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, 86f, 20f); num += 88f; _channelTabs.Add(val); } } private void OpenSettings() { (SettingsPanel.Instance ?? ((Component)this).gameObject.AddComponent<SettingsPanel>()).Toggle(); } private static string Label(int seconds) { if (seconds < 60) { return seconds + " s"; } if (seconds % 60 != 0) { return ((float)seconds / 60f).ToString("0.#") + " min"; } return seconds / 60 + " min"; } private static string Label(CombatChannel channel) { return channel switch { CombatChannel.DamageTaken => "taken", CombatChannel.Healing => "healing", CombatChannel.ObjectDamage => "objects", _ => "damage", }; } } 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? _hits; private readonly TMP_Text? _average; 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? hits, TMP_Text? average, RectTransform bar) { Root = root; _detailed = detailed; _name = name; _value = value; _share = share; _hits = hits; _average = average; _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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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) Id = row.Id; string text = (string.IsNullOrEmpty(row.Name) ? "..." : row.Name); _name.text = prefix + (row.Estimated ? ("~" + text) : 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)_hits != (Object)null) { _hits.text = row.Hits.ToString(CultureInfo.InvariantCulture); } if ((Object)(object)_average != (Object)null) { _average.text = Format(row.Average); } } else { _value.text = (showShare ? (text2 + " <color=#" + ColorUtility.ToHtmlStringRGB(UiStyle.Dim) + ">" + Percent(row.Share) + "</color>") : text2); } ((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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown //IL_01a6: 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_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) TMP_Text val = UiStyle.Text("Name", root, 15f, (TextAlignmentOptions)4097, UiStyle.FontColor); Line(val.rectTransform); val.rectTransform.offsetMax = new Vector2(detailed ? (-250f) : (-120f), 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; TMP_Text val5 = null; if (detailed) { PinRight(val2.rectTransform, 250f, 152f); val3 = UiStyle.Text("Share", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); Line(val3.rectTransform); PinRight(val3.rectTransform, 148f, 104f); val4 = UiStyle.Text("Hits", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); Line(val4.rectTransform); PinRight(val4.rectTransform, 100f, 52f); val5 = UiStyle.Text("Average", root, 13f, (TextAlignmentOptions)4100, UiStyle.Dim); Line(val5.rectTransform); PinRight(val5.rectTransform, 48f, 0f); } else { PinRight(val2.rectTransform, 120f, 0f); } RectTransform val6 = (RectTransform)new GameObject("Bar", new Type[1] { typeof(RectTransform) }).transform; ((Transform)val6).SetParent((Transform)(object)root, false); val6.anchorMin = new Vector2(0f, 1f); val6.anchorMax = new Vector2(1f, 1f); val6.pivot = new Vector2(0f, 1f); val6.anchoredPosition = new Vector2(0f, -19f); val6.sizeDelta = new Vector2(0f, 6f); Image obj = UiStyle.Box("Back", val6, 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, val6); for (int i = 0; i < 11; i++) { Image val7 = UiStyle.Box("Kind" + i, val6, Color.white); val7.sprite = UiStyle.Flat(); RectTransform rectTransform = ((Graphic)val7).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)val7).gameObject.SetActive(false); meterRow._segments[i] = val7; } 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_05d1: Unknown result type (might be due to invalid IL or missing references) //IL_05ec: Unknown result type (might be due to invalid IL or missing references) //IL_0602: 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, "Window", () => settings.CompactWindow.Value + " s", delegate(int step) { settings.CompactWindow.Value = Mathf.Clamp(settings.CompactWindow.Value + step * 5, 5, 600); }); 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 = Number(x, y, "Hide after", () => settings.CompactHideAfter.Value.ToString("0", CultureInfo.InvariantCulture) + " s", delegate(int step) { settings.CompactHideAfter.Value = Mathf.Clamp(settings.CompactHideAfter.Value + (float)step, 1f, 120f); }); y = Choice(x, y, "Corner", () => Word(settings.CompactAnchor.Value), delegate(int step) { settings.CompactAnchor.Value = (ScreenAnchor)Wrap((int)(settings.CompactAnchor.Value + step), 6); }); y = Number(x, y, "Offset across", () => settings.CompactOffset.Value.x.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_0016: 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_003e: Unknown result type (might be due to invalid IL or missing references) settings.CompactOffset.Value = new Vector2(settings.CompactOffset.Value.x + (float)step * 10f, settings.CompactOffset.Value.y); }); y = Number(x, y, "Offset down", () => settings.CompactOffset.Value.y.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) settings.CompactOffset.Value = new Vector2(settings.CompactOffset.Value.x, settings.CompactOffset.Value.y + (float)step * 10f); }); y = Number(x, y, "Width", () => settings.CompactWidth.Value.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { settings.CompactWidth.Value = Mathf.Clamp(settings.CompactWidth.Value + (float)step * 20f, 160f, 700f); }); y = Number(x, y, "Size", () => settings.CompactScale.Value.ToString("0.00", CultureInfo.InvariantCulture), delegate(int step) { settings.CompactScale.Value = Mathf.Clamp(settings.CompactScale.Value + (float)step * 0.05f, 0.5f, 2f); }); y = Number(x, y, "Backing", () => settings.CompactOpacity.Value.ToString("0.00", CultureInfo.InvariantCulture), delegate(int step) { settings.CompactOpacity.Value = Mathf.Clamp(settings.CompactOpacity.Value + (float)step * 0.05f, 0.1f, 1f); }); y = Toggle(x, y, "Caption line", () => settings.CompactCaption.Value, delegate(bool value) { settings.CompactCaption.Value = value; }); y = Toggle(x, y, "Percentages", () => settings.CompactShare.Value, delegate(bool value) { settings.CompactShare.Value = value; }); Toggle(x, y, "Bars", () => settings.CompactBars.Value, delegate(bool value) { settings.CompactBars.Value = value; }); y = 46f; y = Heading("Detail window", x2, y); y = Number(x2, y, "Width", () => settings.DetailSize.Value.x.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) settings.DetailSize.Value = new Vector2(Mathf.Clamp(settings.DetailSize.Value.x + (float)step * 20f, 420f, 1400f), settings.DetailSize.Value.y); }); y = Number(x2, y, "Height", () => settings.DetailSize.Value.y.ToString("0", CultureInfo.InvariantCulture), delegate(int step) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) settings.DetailSize.Value = new Vector2(settings.DetailSize.Value.x, Mathf.Clamp(settings.DetailSize.Value.y + (float)step * 20f, 280f, 1000f)); }); y += 10f; y = Heading("Also count", x2, y); y = Toggle(x2, y, "Damage taken", () => settings.CountDamageTaken.Value, delegate(bool value) { settings.CountDamageTaken.Value = value; }); y = Toggle(x2, y, "Healing", () => settings.CountHealing.Value, delegate(bool value) { settings.CountHealing.Value = value; }); y = Toggle(x2, y, "Trees, ore, buildings", () => settings.CountObjectDamage.Value, delegate(bool value) { settings.CountObjectDamage.Value = value; }); y = Toggle(x2, y, "Pets and summons", () => settings.CountPets.Value, delegate(bool value) { settings.CountPets.Value = value; }); y += 10f; y = Heading("Sharing", x2, y); Toggle(x2, y, "Send my numbers", () => settings.ShareDamage.Value, delegate(bool value) { settings.ShareDamage.LocalValue = value; }, () => settings.ShareDamage.IsLocked); TMP_Text obj2 = UiStyle.Text("Hint", _panel, 12f, (TextAlignmentOptions)4097, UiStyle.Dim); UiStyle.PlaceTopLeft(obj2.rectTransform, 16f, _panel.sizeDelta.y - 30f, _panel.sizeDelta.x - 32f, 18f); obj2.text = "Keys, colours and the share radius live in the config file. A setting a server decides cannot be changed here."; val2.SetActive(false); Plugin.Debug("Settings panel built"); } } private float Heading(string text, float x, float y) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) TMP_Text obj = UiStyle.Text("Heading", _panel, 13f, (TextAlignmentOptions)4097, UiStyle.Mine); UiStyle.PlaceTopLeft(obj.rectTransform, x, y, 300f, 20f); obj.text = text; Image obj2 = UiStyle.Box("HeadingLine", _panel, new Color(0.45f, 0.38f, 0.25f, 0.4f)); obj2.sprite = UiStyle.Flat(); UiStyle.PlaceTopLeft(((Graphic)obj2).rectTransform, x, y + 19f, 300f, 1f); return y + 26f; } private float Toggle(float x, float y, string label, Func<bool> read, Action<bool> write, Func<bool>? locked = null) { return Row(x, y, label, () => (!read()) ? "off" : "on", delegate(int step) { if (step != 0) { write(!read()); } }, locked); } private float Choice(float x, float y, string label, Func<string> read, Action<int> step) { return Row(x, y, label, read, step); } private float Number(float x, float y, string label, Func<string> read, Action<int> step) { return Row(x, y, label, read, step); } private float Row(float x, float y, string label, Func<string> read, Action<int> step, Func<bool>? locked = null) { //IL_003d: 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_004d: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: 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_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)new GameObject("Setting", new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent((Transform)(object)_panel, false); UiStyle.PlaceTopLeft(val, x, y, 300f, 24f); TMP_Text obj = UiStyle.Text("Label", val, 13.5f, (TextAlignmentOptions)4097, UiStyle.FontColor); obj.rectTransform.offsetMax = new Vector2(-120f, 0f); obj.text = label; TMP_Text val2 = UiStyle.Text("Value", val, 13.5f, (TextAlignmentOptions)514, UiStyle.Dim); val2.rectTransform.anchorMin = new Vector2(1f, 0f); val2.rectTransform.anchorMax = new Vector2(1f, 1f); val2.rectTransform.offsetMin = new Vector2(-92f, 0f); val2.rectTransform.offsetMax = new Vector2(-24f, 0f); Func<string> read2 = ((locked == null) ? read : ((Func<string>)(() => (!locked()) ? read() : (read() + " (server)")))); Action<int> stepping = ((locked == null) ? step : ((Action<int>)delegate(int amount) { if (locked()) { Plugin.Log.LogInfo((object)"That setting is decided by the server while you are connected to it."); } else { step(amount); } })); Control row = new Control(val2, read2); Place(UiStyle.TextButton("Less", val, "<", 15f, UiStyle.Dim, delegate { stepping(-1); Apply(row); }), -116f, -94f); Place(UiStyle.TextButton("More", val, ">", 15f, UiStyle.Dim, delegate { stepping(1); Apply(row); }), -22f, 0f); row.Refresh(); _rows.Add(row); return y + 24f; } private void Apply(Control row) { row.Refresh(); RefreshValues(); } private static void Place(Button button, float from, float to) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)((Component)button).transform; val.anchorMin = new Vector2(1f, 0f); val.anchorMax = new Vector2(1f, 1f); val.pivot = new Vector2(0.5f, 0.5f); val.offsetMin = new Vector2(from, 2f); val.offsetMax = new Vector2(to, -2f); } private static int Wrap(int value, int count) { return (value % count + count) % count; } private static string Word(MeterMode mode) { return mode switch { MeterMode.Always => "alw