using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Extensions; using Jotunn.Managers; using Jotunn.Utils; using RdMods.PvpKillFeed.Patches; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PvPKillFeed")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("PvP Kill Feed")] [assembly: AssemblyTitle("PvPKillFeed")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] namespace RdMods.PvpKillFeed { public static class ChatCommands { [HarmonyPatch(typeof(Chat), "InputText")] private static class ChatInputPatch { private static bool Prefix(Chat __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)((Terminal)__instance).m_input == (Object)null) { return true; } return !TryHandleChat(((TMP_InputField)((Terminal)__instance).m_input).text); } } private class DelegateCommand : ConsoleCommand { private readonly string _name; private readonly string _help; private readonly Action _run; public override string Name => _name; public override string Help => _help; public override bool IsCheat => false; public DelegateCommand(string name, string help, Action run) { _name = name; _help = help; _run = run; } public override void Run(string[] args) { _run(args); } } public static void Register() { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DelegateCommand("disablekillfeed", "Hide the kill feed on this client for the current session.", delegate { SetFeedHidden(hidden: true); })); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DelegateCommand("enablekillfeed", "Show the kill feed on this client. Rejoin also re-enables it.", delegate { SetFeedHidden(hidden: false); })); } public static bool TryHandleChat(string raw) { if (string.IsNullOrEmpty(raw) || raw[0] != '/') { return false; } string text = raw.Trim(); int num = text.IndexOf(' '); if (num > 0) { text = text.Substring(0, num); } if (text.Equals("/disablekillfeed", StringComparison.OrdinalIgnoreCase)) { SetFeedHidden(hidden: true); return true; } if (text.Equals("/enablekillfeed", StringComparison.OrdinalIgnoreCase)) { SetFeedHidden(hidden: false); return true; } return false; } private static void SetFeedHidden(bool hidden) { if ((Object)(object)Plugin.Instance == (Object)null || (Object)(object)Plugin.Instance.Hud == (Object)null) { Tell("Kill feed is not ready."); return; } if (Plugin.Instance.Hud.LocalHidden == hidden) { Tell(hidden ? "Kill feed is already hidden." : "Kill feed is already shown."); return; } Plugin.Instance.Hud.SetLocalHidden(hidden); Tell(hidden ? "Kill feed hidden. Other players still see your kills. /enablekillfeed to show." : "Kill feed shown."); } private static void Tell(string message) { if ((Object)(object)Chat.instance != (Object)null) { ((Terminal)Chat.instance).AddString(message); } } } public static class GuildColor { public const string GuildsPluginGuid = "org.bepinex.plugins.guilds"; public static readonly Color White = Color.white; public static readonly int WhiteRgb = PvpKillEvent.Pack(Color.white); private static bool _initialized; private static bool _available; private static MethodInfo _getPlayerGuild; private static MethodInfo _getGuilds; private static FieldInfo _generalField; private static FieldInfo _colorField; private static FieldInfo _membersField; public static bool IsAvailable { get { Init(); return _available; } } public static void Init() { if (_initialized) { return; } _initialized = true; if (!Chainloader.PluginInfos.ContainsKey("org.bepinex.plugins.guilds")) { Plugin.Log.LogInfo((object)"Guilds is not loaded. Names will render white."); return; } Type type = FindType("Guilds.API"); Type type2 = FindType("Guilds.Guild"); Type type3 = FindType("Guilds.GuildGeneral"); if (type == null || type2 == null || type3 == null) { Plugin.Log.LogWarning((object)"Guilds is present but its API types could not be resolved."); return; } _getPlayerGuild = type.GetMethod("GetPlayerGuild", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Player) }, null); _getGuilds = type.GetMethod("GetGuilds", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); _generalField = type2.GetField("General", BindingFlags.Instance | BindingFlags.Public); _membersField = type2.GetField("Members", BindingFlags.Instance | BindingFlags.Public); _colorField = type3.GetField("color", BindingFlags.Instance | BindingFlags.Public); _available = _getPlayerGuild != null && _generalField != null && _colorField != null; Plugin.Log.LogInfo((object)(_available ? "Bound to Guilds.API for live guild RGB colors." : "Failed to bind Guilds.API. Names will render white.")); } public static int RgbFor(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return ColorToRgb(ColorFor(player)); } public static int RgbForName(string playerName) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return ColorToRgb(ColorForName(playerName)); } public static Color ColorFor(Player player) { //IL_0047: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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) Init(); if (!_available || (Object)(object)player == (Object)null || _getPlayerGuild == null) { return White; } try { return ReadGuildColor(_getPlayerGuild.Invoke(null, new object[1] { player })); } catch { return White; } } public static Color ColorForName(string playerName) { //IL_000d: 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_0024: Unknown result type (might be due to invalid IL or missing references) Init(); if (string.IsNullOrEmpty(playerName)) { return White; } Player val = FindLivePlayer(playerName); if ((Object)(object)val != (Object)null) { return ColorFor(val); } return ColorFromGuildScan(playerName); } public static int ColorToRgb(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return PvpKillEvent.Pack(color); } private static Color ColorFromGuildScan(string playerName) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_0050: Unknown result type (might be due to invalid IL or missing references) if (_getGuilds == null) { return White; } try { if (!(_getGuilds.Invoke(null, null) is IEnumerable enumerable)) { return White; } foreach (object item in enumerable) { if (GuildContainsPlayer(item, playerName)) { return ReadGuildColor(item); } } } catch { } return White; } private static Color ReadGuildColor(object guild) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (guild == null || _generalField == null || _colorField == null) { return White; } object value = _generalField.GetValue(guild); if (value == null) { return White; } return ParseHtml(_colorField.GetValue(value) as string); } private static bool GuildContainsPlayer(object guild, string playerName) { if (guild == null || _membersField == null) { return false; } if (!(_membersField.GetValue(guild) is IDictionary dictionary)) { return false; } foreach (object key in dictionary.Keys) { if (key != null) { FieldInfo field = key.GetType().GetField("name", BindingFlags.Instance | BindingFlags.Public); if (string.Equals((field != null) ? (field.GetValue(key) as string) : null, playerName, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } private static Player FindLivePlayer(string playerName) { foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && string.Equals(NameResolver.OfPlayer(allPlayer), playerName, StringComparison.OrdinalIgnoreCase)) { return allPlayer; } } return null; } private static Type FindType(string fullName) { Type type = Type.GetType(fullName + ", Guilds"); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == "Guilds") { return assembly.GetType(fullName); } } return null; } private static Color ParseHtml(string html) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(html)) { return White; } string text = html.Trim(); if (text[0] != '#') { text = "#" + text; } Color result = default(Color); if (!ColorUtility.TryParseHtmlString(text, ref result)) { return White; } result.a = 1f; return result; } } public enum HudAnchor { TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, BottomRight } public static class KillFeedAssets { private const string SwordResource = "kill_icon_sword.png"; private static Sprite _sword; public static Sprite Sword => _sword; public static void Load() { _sword = ToSprite(LoadTexture("kill_icon_sword.png")); if ((Object)(object)_sword == (Object)null) { Plugin.Log.LogWarning((object)"Failed to load crossed-swords kill icon."); } } private static Texture2D LoadTexture(string fileName) { try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); if (text == null) { return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = AssetUtils.LoadImage(memoryStream.ToArray()); if ((Object)(object)val != (Object)null) { ((Object)val).name = fileName; ((Texture)val).filterMode = (FilterMode)1; ((Texture)val).wrapMode = (TextureWrapMode)1; } return val; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Kill icon load failed: " + ex.Message)); return null; } } private static Sprite ToSprite(Texture2D texture) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture == (Object)null) { return null; } return Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f); } } public class KillFeedConfig { public ConfigEntry Position; public ConfigEntry OffsetX; public ConfigEntry OffsetY; public ConfigEntry NewestOnTop; public ConfigEntry EntryDurationSeconds; public ConfigEntry FadeOutSeconds; public ConfigEntry MaxEntries; public ConfigEntry FontSize; public ConfigEntry IconSize; public ConfigEntry EntrySpacing; public ConfigEntry BackgroundAlpha; public ConfigEntry BackgroundPadX; public ConfigEntry BackgroundPadY; public KillFeedConfig(ConfigFile cfg) { Position = ConfigFileExtensions.BindConfig(cfg, "HUD", "Position", HudAnchor.TopCenter, "HUD anchor.", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); OffsetX = ConfigFileExtensions.BindConfig(cfg, "HUD", "OffsetX", 0, "Horizontal inset (px).", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); OffsetY = ConfigFileExtensions.BindConfig(cfg, "HUD", "OffsetY", 36, "Vertical inset (px).", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); NewestOnTop = ConfigFileExtensions.BindConfig(cfg, "HUD", "NewestOnTop", true, "New kills appear at the top.", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); EntryDurationSeconds = ConfigFileExtensions.BindConfig(cfg, "HUD", "EntryDurationSeconds", 7f, "Seconds fully visible.", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); FadeOutSeconds = ConfigFileExtensions.BindConfig(cfg, "HUD", "FadeOutSeconds", 2f, "Fade-out seconds.", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); MaxEntries = ConfigFileExtensions.BindConfig(cfg, "HUD", "MaxEntries", 3, "Max lines on screen.", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); FontSize = ConfigFileExtensions.BindConfig(cfg, "HUD", "FontSize", 15, "Name font size.", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); IconSize = ConfigFileExtensions.BindConfig(cfg, "HUD", "IconSize", 20, "Sword icon size (px).", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); EntrySpacing = ConfigFileExtensions.BindConfig(cfg, "HUD", "EntrySpacing", 4, "Gap between lines (px).", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); BackgroundAlpha = ConfigFileExtensions.BindConfig(cfg, "HUD", "BackgroundAlpha", 0.55f, "Bar opacity 0-1.", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); BackgroundPadX = ConfigFileExtensions.BindConfig(cfg, "HUD", "BackgroundPadX", 5, "Bar padding X (px).", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); BackgroundPadY = ConfigFileExtensions.BindConfig(cfg, "HUD", "BackgroundPadY", 1, "Bar padding Y (px).", true, (int?)null, (AcceptableValueBase)null, (Action)null, (ConfigurationManagerAttributes)null); } } public class KillFeedHud : MonoBehaviour { private class EntryView { public GameObject Root; public RectTransform Motion; public CanvasGroup Group; } private class PendingKill { public string Killer; public string Victim; public Color KillerColor; public Color VictimColor; } private const float SlideInSeconds = 0.2f; private KillFeedConfig _cfg; private RectTransform _root; private VerticalLayoutGroup _stack; private Sprite _barSprite; private readonly List _entries = new List(); private readonly Queue _queued = new Queue(); private bool _guiBound; public bool LocalHidden { get; private set; } public void Configure(KillFeedConfig cfg) { _cfg = cfg; } public void BindGui() { if (!_guiBound) { _guiBound = true; GUIManager.OnCustomGUIAvailable += RebuildRoot; if ((Object)(object)GUIManager.CustomGUIFront != (Object)null) { RebuildRoot(); } } } public void UnbindGui() { if (_guiBound) { _guiBound = false; GUIManager.OnCustomGUIAvailable -= RebuildRoot; } } public void SetLocalHidden(bool hidden) { LocalHidden = hidden; ApplyVisibility(); } public void ResetSession() { LocalHidden = false; ApplyVisibility(); } public void RefreshLayout() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) ApplyAnchor(); ApplySpacing(); if ((Object)(object)_stack != (Object)null) { ((LayoutGroup)_stack).childAlignment = ChildAlignmentForAnchor(); } } public void Push(PvpKillEvent ev) { //IL_0048: 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) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (LocalHidden) { return; } string text = NameResolver.Sanitize(ev.Killer); string text2 = NameResolver.Sanitize(ev.Victim); if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2)) { PendingKill pendingKill = new PendingKill { Killer = text, Victim = text2, KillerColor = ev.KillerColor, VictimColor = ev.VictimColor }; if ((Object)(object)_stack == (Object)null) { _queued.Enqueue(pendingKill); } else { Spawn(pendingKill); } } } private void RebuildRoot() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown try { _entries.Clear(); _root = null; _stack = null; GameObject customGUIFront = GUIManager.CustomGUIFront; if (!((Object)(object)customGUIFront == (Object)null)) { EnsureBarSprite(); GameObject val = new GameObject("PvPKillFeed_Root", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); val.layer = 5; val.transform.SetParent(customGUIFront.transform, false); _root = val.GetComponent(); ApplyAnchor(); _stack = val.GetComponent(); ((LayoutGroup)_stack).childAlignment = ChildAlignmentForAnchor(); ((HorizontalOrVerticalLayoutGroup)_stack).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)_stack).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)_stack).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)_stack).childForceExpandHeight = false; ((LayoutGroup)_stack).padding = new RectOffset(0, 0, 0, 0); ApplySpacing(); ContentSizeFitter component = val.GetComponent(); component.horizontalFit = (FitMode)2; component.verticalFit = (FitMode)2; ApplyVisibility(); while (_queued.Count > 0) { Spawn(_queued.Dequeue()); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Kill feed GUI rebuild failed: " + ex.Message)); } } private void ApplyAnchor() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root == (Object)null) && _cfg != null) { HudAnchor value = _cfg.Position.Value; bool flag = IsCenter(value); bool flag2 = IsRight(value); bool flag3 = IsBottom(value); float num = (flag ? 0.5f : (flag2 ? 1f : 0f)); float num2 = (flag3 ? 0f : 1f); _root.anchorMin = new Vector2(num, num2); _root.anchorMax = new Vector2(num, num2); _root.pivot = new Vector2(num, num2); float num3 = _cfg.OffsetX.Value; float num4 = Math.Max(0, _cfg.OffsetY.Value); float num5 = (flag ? num3 : (flag2 ? (0f - Math.Max(0f, num3)) : Math.Max(0f, num3))); _root.anchoredPosition = new Vector2(num5, flag3 ? num4 : (0f - num4)); _root.sizeDelta = Vector2.zero; } } private void ApplySpacing() { if (!((Object)(object)_stack == (Object)null) && _cfg != null) { ((HorizontalOrVerticalLayoutGroup)_stack).spacing = Mathf.Max(0, _cfg.EntrySpacing.Value); } } private TextAnchor ChildAlignmentForAnchor() { if (_cfg == null) { return (TextAnchor)1; } return (TextAnchor)(_cfg.Position.Value switch { HudAnchor.TopLeft => 0, HudAnchor.TopCenter => 1, HudAnchor.BottomLeft => 6, HudAnchor.BottomCenter => 7, HudAnchor.BottomRight => 8, _ => 2, }); } private static bool IsCenter(HudAnchor anchor) { if (anchor != HudAnchor.TopCenter) { return anchor == HudAnchor.BottomCenter; } return true; } private static bool IsRight(HudAnchor anchor) { if (anchor != HudAnchor.TopRight) { return anchor == HudAnchor.BottomRight; } return true; } private static bool IsBottom(HudAnchor anchor) { if (anchor != HudAnchor.BottomLeft && anchor != HudAnchor.BottomCenter) { return anchor == HudAnchor.BottomRight; } return true; } private Vector2 InnerPivot() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) if (_cfg == null || IsCenter(_cfg.Position.Value)) { return new Vector2(0.5f, 0.5f); } if (!IsRight(_cfg.Position.Value)) { return new Vector2(0f, 0.5f); } return new Vector2(1f, 0.5f); } private Vector2 SlideOffset() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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) HudAnchor value = _cfg.Position.Value; if (IsCenter(value)) { if (!IsBottom(value)) { return new Vector2(0f, 18f); } return new Vector2(0f, -18f); } if (!IsRight(value)) { return new Vector2(-24f, 0f); } return new Vector2(24f, 0f); } private void Spawn(PendingKill pending) { if ((Object)(object)_stack == (Object)null || _cfg == null || LocalHidden) { return; } ApplyAnchor(); ApplySpacing(); TrimToMax(); EntryView entryView = BuildRow(pending); if (entryView != null && !((Object)(object)entryView.Root == (Object)null)) { if (_cfg.NewestOnTop.Value) { entryView.Root.transform.SetAsFirstSibling(); } _entries.Add(entryView); LayoutRebuilder.ForceRebuildLayoutImmediate(_root); ((MonoBehaviour)this).StartCoroutine(SafeRunEntry(entryView)); } } private void TrimToMax() { int num = Math.Max(1, _cfg.MaxEntries.Value); while (_entries.Count >= num) { EntryView entryView = _entries[0]; _entries.RemoveAt(0); if (entryView != null && (Object)(object)entryView.Root != (Object)null) { Object.Destroy((Object)(object)entryView.Root); } } _entries.RemoveAll((EntryView e) => e == null || (Object)(object)e.Root == (Object)null); } private EntryView BuildRow(PendingKill pending) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_011e: 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_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0142: 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_018f: Expected O, but got Unknown //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) int num = Math.Max(8, _cfg.IconSize.Value); int num2 = Mathf.Clamp(_cfg.BackgroundPadX.Value, 0, 32); int num3 = Mathf.Clamp(_cfg.BackgroundPadY.Value, 0, 32); GameObject val = new GameObject("KillLine", new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); val.layer = 5; val.transform.SetParent(((Component)_stack).transform, false); LayoutElement component = val.GetComponent(); component.flexibleWidth = 1f; GameObject val2 = new GameObject("Content", new Type[5] { typeof(RectTransform), typeof(CanvasGroup), typeof(HorizontalLayoutGroup), typeof(ContentSizeFitter), typeof(Image) }); val2.layer = 5; val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent(); Vector2 pivot = (component2.anchorMax = (component2.anchorMin = InnerPivot())); component2.pivot = pivot; component2.anchoredPosition = Vector2.zero; HorizontalLayoutGroup component3 = val2.GetComponent(); ((LayoutGroup)component3).childAlignment = (TextAnchor)4; ((HorizontalOrVerticalLayoutGroup)component3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component3).spacing = 8f; ((LayoutGroup)component3).padding = new RectOffset(num2, num2, num3, num3); ContentSizeFitter component4 = val2.GetComponent(); component4.horizontalFit = (FitMode)2; component4.verticalFit = (FitMode)2; Image component5 = val2.GetComponent(); component5.sprite = _barSprite; component5.type = (Type)0; ((Graphic)component5).color = new Color(0f, 0f, 0f, Mathf.Clamp01(_cfg.BackgroundAlpha.Value)); ((Graphic)component5).raycastTarget = false; TextMeshProUGUI killer = CreateName(val2.transform, pending.Killer, pending.KillerColor); CreateIcon(val2.transform, num); TextMeshProUGUI victim = CreateName(val2.transform, pending.Victim, pending.VictimColor); float num4 = (component.preferredHeight = (component.minHeight = MeasureRowHeight(killer, victim, num, num3))); component.flexibleHeight = 0f; LayoutRebuilder.ForceRebuildLayoutImmediate(component2); Rect rect = component2.rect; float height = ((Rect)(ref rect)).height; if (height > num4) { component.minHeight = height; component.preferredHeight = height; } CanvasGroup component6 = val2.GetComponent(); component6.alpha = 0f; component6.blocksRaycasts = false; component6.interactable = false; return new EntryView { Root = val, Motion = component2, Group = component6 }; } private TextMeshProUGUI CreateName(Transform parent, string name, Color color) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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) GameObject val = new GameObject("Name", new Type[4] { typeof(RectTransform), typeof(TextMeshProUGUI), typeof(ContentSizeFitter), typeof(Outline) }) { layer = 5 }; val.transform.SetParent(parent, false); TextMeshProUGUI component = val.GetComponent(); ((Graphic)component).raycastTarget = false; ((TMP_Text)component).text = name; ((Graphic)component).color = color; ((TMP_Text)component).fontSize = Math.Max(8, _cfg.FontSize.Value); ((TMP_Text)component).fontStyle = (FontStyles)1; ((TMP_Text)component).characterSpacing = -1.2f; ((TMP_Text)component).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component).overflowMode = (TextOverflowModes)0; ((TMP_Text)component).alignment = (TextAlignmentOptions)4097; ((TMP_Text)component).enableAutoSizing = false; TMP_FontAsset val2 = ((GUIManager.Instance != null) ? GUIManager.Instance.TMP_AveriaSansLibre : null); if ((Object)(object)val2 != (Object)null) { ((TMP_Text)component).font = val2; } Outline component2 = val.GetComponent(); ((Shadow)component2).effectColor = new Color(0f, 0f, 0f, 0.92f); ((Shadow)component2).effectDistance = new Vector2(1f, -1f); ((Shadow)component2).useGraphicAlpha = true; ContentSizeFitter component3 = val.GetComponent(); component3.horizontalFit = (FitMode)2; component3.verticalFit = (FitMode)2; return component; } private static float MeasureRowHeight(TextMeshProUGUI killer, TextMeshProUGUI victim, int iconSize, int padY) { float num = Mathf.Max(MeasureNameHeight(killer), MeasureNameHeight(victim)); return Mathf.Max((float)iconSize, num) + (float)padY * 2f; } private static float MeasureNameHeight(TextMeshProUGUI tmp) { if ((Object)(object)tmp == (Object)null) { return 0f; } ((TMP_Text)tmp).ForceMeshUpdate(true, false); return Mathf.Max(((TMP_Text)tmp).preferredHeight, ((TMP_Text)tmp).fontSize); } private Image CreateIcon(Transform parent, int size) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0070: 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_0098: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("KillIcon", new Type[3] { typeof(RectTransform), typeof(LayoutElement), typeof(Image) }) { layer = 5 }; val.transform.SetParent(parent, false); LayoutElement component = val.GetComponent(); component.preferredWidth = size; component.preferredHeight = size; component.minWidth = size; component.minHeight = size; val.GetComponent().sizeDelta = new Vector2((float)size, (float)size); Image component2 = val.GetComponent(); ((Graphic)component2).raycastTarget = false; component2.preserveAspect = true; ((Graphic)component2).color = Color.white; component2.sprite = KillFeedAssets.Sword; return component2; } private void EnsureBarSprite() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_001c: 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_005f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_barSprite != (Object)null)) { Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, Color.white); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)0; val.Apply(); _barSprite = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f); } } private IEnumerator SafeRunEntry(EntryView view) { IEnumerator routine; try { routine = AnimateEntry(view); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Kill feed animation failed to start: " + ex.Message)); yield break; } while (true) { bool flag; try { flag = routine.MoveNext(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Kill feed animation error: " + ex2.Message)); break; } if (flag) { yield return routine.Current; continue; } break; } } private IEnumerator AnimateEntry(EntryView view) { if (view == null || (Object)(object)view.Motion == (Object)null || (Object)(object)view.Group == (Object)null) { yield break; } Vector2 slide = SlideOffset(); view.Motion.anchoredPosition = slide; float t = 0f; while (t < 0.2f) { if ((Object)(object)view.Root == (Object)null || (Object)(object)view.Motion == (Object)null) { yield break; } t += Time.unscaledDeltaTime; float num = Mathf.Clamp01(t / 0.2f); float num2 = 1f - (1f - num) * (1f - num); view.Group.alpha = num2; view.Motion.anchoredPosition = Vector2.Lerp(slide, Vector2.zero, num2); yield return null; } if ((Object)(object)view.Root == (Object)null || (Object)(object)view.Motion == (Object)null) { yield break; } view.Group.alpha = 1f; view.Motion.anchoredPosition = Vector2.zero; float num3 = Math.Max(0.1f, _cfg.EntryDurationSeconds.Value); yield return (object)new WaitForSecondsRealtime(num3); float fade = Math.Max(0.05f, _cfg.FadeOutSeconds.Value); t = 0f; while (t < fade) { if ((Object)(object)view.Root == (Object)null || (Object)(object)view.Group == (Object)null) { yield break; } t += Time.unscaledDeltaTime; view.Group.alpha = 1f - Mathf.Clamp01(t / fade); yield return null; } _entries.Remove(view); if ((Object)(object)view.Root != (Object)null) { Object.Destroy((Object)(object)view.Root); } } private void ApplyVisibility() { if (!((Object)(object)_root == (Object)null)) { bool flag = (Object)(object)Player.m_localPlayer != (Object)null && ((Object)(object)Hud.instance == (Object)null || Hud.instance.IsVisible()); bool flag2 = !LocalHidden && flag; if (((Component)_root).gameObject.activeSelf != flag2) { ((Component)_root).gameObject.SetActive(flag2); } } } private void Update() { try { ApplyVisibility(); } catch { } } } public static class NameResolver { public static string OfPlayer(Player player) { if ((Object)(object)player == (Object)null) { return ""; } string playerName = player.GetPlayerName(); if (!string.IsNullOrEmpty(playerName) && playerName != "...") { return Sanitize(playerName); } string hoverName = ((Character)player).GetHoverName(); if (!string.IsNullOrEmpty(hoverName) && hoverName != "...") { return Sanitize(hoverName); } return ""; } public static string Sanitize(string name) { if (string.IsNullOrEmpty(name)) { return ""; } return name.Replace("<", "").Replace(">", "").Trim(); } } public static class Network { [HarmonyPatch(typeof(Game), "Start")] private static class GameStartPatch { private static void Postfix() { if ((Object)(object)Plugin.Instance != (Object)null && (Object)(object)Plugin.Instance.Hud != (Object)null) { Plugin.Instance.Hud.ResetSession(); } ResetSession(); TryRegister(); } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] private static class ZNetDestroyPatch { private static void Prefix() { _registeredRpc = null; ResetSession(); } } public const string RpcName = "PKF"; private const float DedupSeconds = 2.5f; private const int MaxCache = 8; private static ZRoutedRpc _registeredRpc; private static readonly Dictionary _recentVictims = new Dictionary(8); private static readonly List _stale = new List(8); public static void Broadcast(PvpKillEvent ev) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); ev.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PKF", new object[1] { val }); } } public static void OnRpc(long sender, ZPackage pkg) { if (pkg != null && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { PvpKillEvent ev; try { ev = PvpKillEvent.Read(pkg); } catch { return; } if (!IsDuplicate(ev.VictimId)) { Plugin.Instance.OnPvpKillReceived(ev); } } } public static void ResetSession() { _recentVictims.Clear(); _stale.Clear(); PlayerDeathPatch.ResetDebounce(); CharacterDamagePatch.ClearCredit(); } private static bool IsDuplicate(long victimId) { float unscaledTime = Time.unscaledTime; Prune(unscaledTime); if (_recentVictims.TryGetValue(victimId, out var value) && unscaledTime - value < 2.5f) { return true; } _recentVictims[victimId] = unscaledTime; return false; } private static void Prune(float now) { if (_recentVictims.Count == 0) { return; } _stale.Clear(); foreach (KeyValuePair recentVictim in _recentVictims) { if (now - recentVictim.Value >= 2.5f) { _stale.Add(recentVictim.Key); } } for (int i = 0; i < _stale.Count; i++) { _recentVictims.Remove(_stale[i]); } _stale.Clear(); if (_recentVictims.Count > 8) { _recentVictims.Clear(); } } private static void TryRegister() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && _registeredRpc != instance) { instance.Register("PKF", (Action)OnRpc); _registeredRpc = instance; } } } [BepInPlugin("com.rdmods.pvpkillfeed", "PvP Kill Feed", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "com.rdmods.pvpkillfeed"; public const string PluginName = "PvP Kill Feed"; public const string PluginVersion = "1.1.0"; private Harmony _harmony; private ConfigFileWatcher _configWatcher; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public KillFeedConfig FeedConfig { get; private set; } public KillFeedHud Hud { get; private set; } private void Awake() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; FeedConfig = new KillFeedConfig(((BaseUnityPlugin)this).Config); KillFeedAssets.Load(); GuildColor.Init(); Hud = ((Component)this).gameObject.AddComponent(); Hud.Configure(FeedConfig); Hud.BindGui(); ChatCommands.Register(); _configWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config, 1000L); _configWatcher.OnConfigFileReloaded += OnConfigReloaded; SynchronizationManager.OnConfigurationSynchronized += OnConfigSynced; _harmony = new Harmony("com.rdmods.pvpkillfeed"); _harmony.PatchAll(); Log.LogInfo((object)"PvP Kill Feed v1.1.0 loaded."); } private void OnDestroy() { SynchronizationManager.OnConfigurationSynchronized -= OnConfigSynced; if (_configWatcher != null) { _configWatcher.OnConfigFileReloaded -= OnConfigReloaded; } if ((Object)(object)Hud != (Object)null) { Hud.UnbindGui(); } Network.ResetSession(); if (_harmony != null) { _harmony.UnpatchSelf(); } } public void OnPvpKillReceived(PvpKillEvent ev) { if (!((Object)(object)Hud == (Object)null) && !Hud.LocalHidden) { Hud.Push(ev); } } private void OnConfigReloaded() { RefreshHud(); } private static void OnConfigSynced(object sender, ConfigurationSynchronizationEventArgs args) { if ((Object)(object)Instance != (Object)null) { Instance.RefreshHud(); } } private void RefreshHud() { if ((Object)(object)Hud != (Object)null) { Hud.RefreshLayout(); } } } public struct PvpKillEvent { public const int MaxNameChars = 32; public string Killer; public string Victim; public long VictimId; public int KillerRgb; public int VictimRgb; public Color KillerColor => Unpack(KillerRgb); public Color VictimColor => Unpack(VictimRgb); public void Write(ZPackage pkg) { WriteName(pkg, Killer); WriteName(pkg, Victim); pkg.Write(VictimId); pkg.Write(KillerRgb); pkg.Write(VictimRgb); } public static PvpKillEvent Read(ZPackage pkg) { return new PvpKillEvent { Killer = pkg.ReadString(), Victim = pkg.ReadString(), VictimId = pkg.ReadLong(), KillerRgb = pkg.ReadInt(), VictimRgb = pkg.ReadInt() }; } public static int Pack(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Clamp(Mathf.RoundToInt(c.r * 255f), 0, 255); int num2 = Mathf.Clamp(Mathf.RoundToInt(c.g * 255f), 0, 255); int num3 = Mathf.Clamp(Mathf.RoundToInt(c.b * 255f), 0, 255); return (num << 16) | (num2 << 8) | num3; } public static Color Unpack(int rgb) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) return new Color((float)((rgb >> 16) & 0xFF) / 255f, (float)((rgb >> 8) & 0xFF) / 255f, (float)(rgb & 0xFF) / 255f, 1f); } private static void WriteName(ZPackage pkg, string name) { if (string.IsNullOrEmpty(name)) { pkg.Write(""); } else { pkg.Write((name.Length > 32) ? name.Substring(0, 32) : name); } } } } namespace RdMods.PvpKillFeed.Patches { [HarmonyPatch(typeof(Character), "Damage")] public static class CharacterDamagePatch { internal static bool HasConfirmedKiller; internal static string ConfirmedKillerName; internal static int ConfirmedKillerRgb; private static void Prefix(Character __instance, HitData hit, out bool __state) { __state = false; if (!((Object)(object)__instance == (Object)null) && hit != null && __instance.IsPlayer() && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !HasConfirmedKiller && !(__instance.GetHealth() <= 0f)) { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)__instance)) { __state = true; } } } private static void Postfix(Character __instance, HitData hit, bool __state) { if (!__state || HasConfirmedKiller || (Object)(object)__instance == (Object)null || hit == null || __instance.GetHealth() > 0f) { return; } Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)__instance)) { string text = NameResolver.OfPlayer(val); if (!string.IsNullOrEmpty(text)) { HasConfirmedKiller = true; ConfirmedKillerName = text; ConfirmedKillerRgb = GuildColor.RgbFor(val); } } } internal static void ClearCredit() { HasConfirmedKiller = false; ConfirmedKillerName = null; ConfirmedKillerRgb = GuildColor.WhiteRgb; } } [HarmonyPatch(typeof(Player), "OnSpawned")] public static class PlayerSpawnedCreditReset { private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { CharacterDamagePatch.ClearCredit(); } } } [HarmonyPatch(typeof(Player), "OnDeath")] public static class PlayerDeathPatch { private const float BroadcastCooldown = 2f; private static float _lastBroadcastTime = -999f; private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (!CharacterDamagePatch.HasConfirmedKiller || string.IsNullOrEmpty(CharacterDamagePatch.ConfirmedKillerName)) { CharacterDamagePatch.ClearCredit(); return; } float unscaledTime = Time.unscaledTime; if (unscaledTime - _lastBroadcastTime < 2f) { CharacterDamagePatch.ClearCredit(); return; } string text = NameResolver.OfPlayer(__instance); if (string.IsNullOrEmpty(text)) { CharacterDamagePatch.ClearCredit(); return; } _lastBroadcastTime = unscaledTime; Network.Broadcast(new PvpKillEvent { Killer = CharacterDamagePatch.ConfirmedKillerName, Victim = text, VictimId = __instance.GetPlayerID(), KillerRgb = CharacterDamagePatch.ConfirmedKillerRgb, VictimRgb = GuildColor.RgbFor(__instance) }); CharacterDamagePatch.ClearCredit(); } internal static void ResetDebounce() { _lastBroadcastTime = -999f; } } }