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 ValheimKillFeed v1.0.0
plugins/ValheimKillFeed.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using ValheimKillFeed.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("ValheimKillFeed")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ValheimKillFeed")] [assembly: AssemblyTitle("ValheimKillFeed")] [assembly: AssemblyVersion("1.0.0.0")] namespace ValheimKillFeed { public static class CauseResolver { public struct Resolved { public string Section; public string Weapon; public string Distance; public bool HasAttacker; } public static Resolved Resolve(Player victim, HitData hit) { //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_004e: 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_0076: Expected I4, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Invalid comparison between Unknown and I4 //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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) Resolved resolved = default(Resolved); resolved.Section = ""; resolved.Weapon = ""; resolved.Distance = ""; resolved.HasAttacker = false; Resolved result = resolved; if (hit == null) { result.Section = "fall"; return result; } HitType hitType = hit.m_hitType; switch (hitType - 3) { default: if ((int)hitType != 14) { if ((int)hitType != 21) { break; } goto case 1; } result.Section = "fireplace"; return result; case 0: result.Section = "fall"; return result; case 1: case 5: result.Section = "drown"; return result; case 6: result.Section = "smoke"; return result; case 7: result.Section = "oob"; return result; case 2: case 3: case 4: break; } Character attacker = hit.GetAttacker(); if ((Object)(object)attacker != (Object)null && (Object)(object)victim != (Object)null && (Object)(object)attacker == (Object)(object)victim) { result.Section = "fireplace"; return result; } result.HasAttacker = (Object)(object)attacker != (Object)null || hit.HaveAttacker(); result.Section = DamageTypeToSection(((DamageTypes)(ref hit.m_damage)).GetMajorityDamageType()); if ((int)hit.m_skill != 0) { result.Weapon = ((object)(SkillType)(ref hit.m_skill)).ToString(); } if ((Object)(object)attacker != (Object)null && (Object)(object)victim != (Object)null) { float num = Vector3.Distance(((Component)attacker).transform.position, ((Component)victim).transform.position); result.Distance = Mathf.RoundToInt(num).ToString(); } return result; } private static string DamageTypeToSection(DamageType t) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_0009: 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_0021: Expected I4, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if ((int)t <= 32) { if ((int)t <= 8) { switch (t - 1) { default: if ((int)t != 8) { break; } return "chop"; case 0: return "blunt"; case 1: return "slash"; case 3: return "pierce"; case 2: break; } } else { if ((int)t == 16) { return "pickaxe"; } if ((int)t == 32) { return "fire"; } } } else if ((int)t <= 128) { if ((int)t == 64) { return "frost"; } if ((int)t == 128) { return "lightning"; } } else { if ((int)t == 256) { return "poison"; } if ((int)t == 512) { return "spirit"; } } return "blunt"; } } public class KillEvent { public string Victim; public string Killer; public string Section; public string Weapon; public string Distance; public void Write(ZPackage pkg) { pkg.Write(Victim ?? ""); pkg.Write(Killer ?? ""); pkg.Write(Section ?? ""); pkg.Write(Weapon ?? ""); pkg.Write(Distance ?? ""); } public static KillEvent Read(ZPackage pkg) { return new KillEvent { Victim = pkg.ReadString(), Killer = pkg.ReadString(), Section = pkg.ReadString(), Weapon = pkg.ReadString(), Distance = pkg.ReadString() }; } } public static class NameResolver { public static string Of(Character c) { if ((Object)(object)c == (Object)null) { return ""; } Player val = (Player)(object)((c is Player) ? c : null); if (val != null) { string playerName = val.GetPlayerName(); if (!string.IsNullOrEmpty(playerName) && playerName != "...") { return playerName; } } string hoverName = c.GetHoverName(); if (!string.IsNullOrEmpty(hoverName)) { return hoverName; } if (!string.IsNullOrEmpty(c.m_name)) { return c.m_name; } return "something"; } } public static class Network { [HarmonyPatch(/*Could not decode attribute arguments.*/)] private static class ZRoutedRpc_Ctor_Patch { private static void Postfix() { ZRoutedRpc.instance.Register<ZPackage>("ValheimKillFeed_KillEvent", (Action<long, ZPackage>)OnRpc); } } public const string RpcName = "ValheimKillFeed_KillEvent"; public static void Broadcast(KillEvent 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, "ValheimKillFeed_KillEvent", new object[1] { val }); } } private static void OnRpc(long sender, ZPackage pkg) { if (pkg != null && !((Object)(object)Player.m_localPlayer == (Object)null)) { KillEvent ev; try { ev = KillEvent.Read(pkg); } catch { return; } Plugin.Instance?.OnKillEventReceived(ev); } } } public class PhraseTable { private readonly Dictionary<string, List<string>> _sections = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); private readonly Random _rng = new Random(); public int SectionCount => _sections.Count; public int TotalLines { get { int num = 0; foreach (KeyValuePair<string, List<string>> section in _sections) { num += section.Value.Count; } return num; } } public void Load(string path) { _sections.Clear(); if (!File.Exists(path)) { return; } string text = null; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2).Trim().ToLowerInvariant(); if (!_sections.TryGetValue(text, out var _)) { _sections[text] = new List<string>(); } } else if (text != null) { _sections[text].Add(text2); } } } public string Pick(string section, string victim, string killer, string weapon, string distance) { if (!_sections.TryGetValue(section, out var value) || value.Count == 0) { return victim + " died."; } bool HasKiller = !string.IsNullOrEmpty(killer); bool HasWeapon = !string.IsNullOrEmpty(weapon); bool HasDistance = !string.IsNullOrEmpty(distance); List<string> list = new List<string>(); foreach (string item in value) { if (Compatible(item)) { list.Add(item); } } if (list.Count == 0) { list = value; } return list[_rng.Next(list.Count)].Replace("{victim}", victim ?? "").Replace("{killer}", killer ?? "").Replace("{weapon}", weapon ?? "") .Replace("{distance}", distance ?? ""); bool Compatible(string t) { if ((HasKiller || !t.Contains("{killer}")) && (HasWeapon || !t.Contains("{weapon}"))) { if (!HasDistance) { return !t.Contains("{distance}"); } return true; } return false; } } } [BepInPlugin("tarbaby.valheim-kill-feed", "ValheimKillFeed", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "tarbaby.valheim-kill-feed"; public const string PluginName = "ValheimKillFeed"; public const string PluginVersion = "1.0.0"; private Harmony _harmony; private KillFeedConfig _config; private KillFeedHud _hud; private PhraseTable _phrases; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _config = new KillFeedConfig(((BaseUnityPlugin)this).Config); _phrases = new PhraseTable(); string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? "", "phrasings.txt"); EnsurePhrasingsFile(text); _phrases.Load(text); Log.LogInfo((object)$"Loaded phrasings: {_phrases.SectionCount} sections, {_phrases.TotalLines} lines from {text}"); GameObject val = new GameObject("ValheimKillFeed_Hud"); Object.DontDestroyOnLoad((Object)(object)val); _hud = val.AddComponent<KillFeedHud>(); _hud.Configure(_config); _harmony = new Harmony("tarbaby.valheim-kill-feed"); _harmony.PatchAll(); Log.LogInfo((object)"ValheimKillFeed v1.0.0 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } public void OnKillEventReceived(KillEvent ev) { if (ev != null && !((Object)(object)_hud == (Object)null) && _phrases != null) { string text = _phrases.Pick(ev.Section, ev.Victim, ev.Killer, ev.Weapon, ev.Distance); _hud.Push(text); } } private static void EnsurePhrasingsFile(string targetPath) { if (File.Exists(targetPath)) { return; } Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = executingAssembly.GetName().Name + ".phrasings.txt"; using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { Log.LogWarning((object)("Embedded phrasings resource '" + text + "' not found.")); return; } using FileStream destination = File.Create(targetPath); stream.CopyTo(destination); Log.LogInfo((object)("Wrote default phrasings to " + targetPath)); } } } namespace ValheimKillFeed.UI { public enum HudAnchor { TopLeft, TopRight, BottomLeft, BottomRight } public class KillFeedConfig { public ConfigEntry<HudAnchor> Position; public ConfigEntry<float> EntryDurationSeconds; public ConfigEntry<int> MaxEntries; public ConfigEntry<int> FontSize; public KillFeedConfig(ConfigFile cfg) { Position = cfg.Bind<HudAnchor>("HUD", "Position", HudAnchor.TopRight, "Where on the screen the kill feed appears."); EntryDurationSeconds = cfg.Bind<float>("HUD", "EntryDurationSeconds", 6f, "How long each entry stays on screen before fading out."); MaxEntries = cfg.Bind<int>("HUD", "MaxEntries", 6, "Maximum number of entries displayed at once."); FontSize = cfg.Bind<int>("HUD", "FontSize", 18, "Pixel size of kill feed text."); } } public class KillFeedHud : MonoBehaviour { private class Entry { public string Text; public float SpawnedAt; } private readonly LinkedList<Entry> _entries = new LinkedList<Entry>(); private KillFeedConfig _cfg; private GUIStyle _style; private Texture2D _bgTex; private const string Prefix = "☠ "; public void Configure(KillFeedConfig cfg) { _cfg = cfg; } public void Push(string text) { if (!string.IsNullOrEmpty(text)) { _entries.AddLast(new Entry { Text = "☠ " + text, SpawnedAt = Time.unscaledTime }); TrimToMax(); } } private void TrimToMax() { int num = _cfg?.MaxEntries.Value ?? 6; while (_entries.Count > num) { _entries.RemoveFirst(); } } private void Update() { if (_cfg != null) { float value = _cfg.EntryDurationSeconds.Value; float unscaledTime = Time.unscaledTime; while (_entries.First != null && unscaledTime - _entries.First.Value.SpawnedAt > value + 1f) { _entries.RemoveFirst(); } } } private void EnsureStyle() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0032: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (_style == null) { _style = new GUIStyle(GUI.skin.label) { richText = true, wordWrap = false, alignment = (TextAnchor)3 }; } _style.fontSize = _cfg?.FontSize.Value ?? 18; if ((Object)(object)_bgTex == (Object)null) { _bgTex = new Texture2D(1, 1, (TextureFormat)4, false); _bgTex.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.55f)); _bgTex.Apply(); } } private void OnGUI() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Expected O, but got Unknown //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0208: 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_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0245: 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_0269: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) if (_cfg == null || _entries.Count == 0 || (Object)(object)Player.m_localPlayer == (Object)null) { return; } EnsureStyle(); float value = _cfg.EntryDurationSeconds.Value; float unscaledTime = Time.unscaledTime; float num = (float)Screen.height * 0.25f; List<float> list = new List<float>(_entries.Count); float num2 = 0f; foreach (Entry entry in _entries) { float num3 = _style.CalcSize(new GUIContent(entry.Text)).y + 12f; list.Add(num3); num2 += num3 + 4f; } if (num2 > 0f) { num2 -= 4f; } float num4 = Screen.width; float num5 = Screen.height; float num6 = Mathf.Min(560f, num4 * 0.45f); bool flag = _cfg.Position.Value == HudAnchor.TopRight || _cfg.Position.Value == HudAnchor.BottomRight; float num7 = ((_cfg.Position.Value == HudAnchor.TopLeft || _cfg.Position.Value == HudAnchor.TopRight) ? num : (num5 - 120f - num2)); int num8 = 0; foreach (Entry entry2 in _entries) { float num9 = unscaledTime - entry2.SpawnedAt; float num10 = ((num9 < value) ? 1f : ((!(num9 < value + 1f)) ? 0f : (1f - (num9 - value)))); if (num10 <= 0f) { num8++; num7 += list[num8 - 1] + 4f; continue; } GUIContent val = new GUIContent(entry2.Text); Vector2 val2 = _style.CalcSize(val); float num11 = Mathf.Min(num6, val2.x + 20f); float num12 = list[num8]; float num13 = (flag ? (num4 - 16f - num11) : 16f); Color color = GUI.color; GUI.color = new Color(1f, 1f, 1f, num10); GUI.DrawTexture(new Rect(num13, num7, num11, num12), (Texture)(object)_bgTex); GUI.color = new Color(1f, 1f, 1f, num10); GUI.Label(new Rect(num13 + 10f, num7 + 6f, num11 - 20f, num12 - 12f), val, _style); GUI.color = color; num7 += num12 + 4f; num8++; } } } } namespace ValheimKillFeed.Patches { public class AttackerSnapshot { public string KillerName; public CauseResolver.Resolved Resolved; } [HarmonyPatch(typeof(Character), "Damage")] public static class CharacterDamagePatch { public static readonly ConditionalWeakTable<Player, AttackerSnapshot> LastHitByVictim = new ConditionalWeakTable<Player, AttackerSnapshot>(); private static void Postfix(Character __instance, HitData hit) { if ((Object)(object)__instance == (Object)null || hit == null) { return; } Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null) { CauseResolver.Resolved resolved = CauseResolver.Resolve(val, hit); string killerName = ""; Character attacker = hit.GetAttacker(); if ((Object)(object)attacker != (Object)null) { killerName = NameResolver.Of(attacker); } AttackerSnapshot value = new AttackerSnapshot { KillerName = killerName, Resolved = resolved }; LastHitByVictim.Remove(val); LastHitByVictim.Add(val, value); } } } [HarmonyPatch(typeof(Player), "OnDeath")] public static class PlayerDeathPatch { private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)null) { return; } string victim = NameResolver.Of((Character)(object)__instance); string killer = ""; CharacterDamagePatch.LastHitByVictim.TryGetValue(__instance, out var value); CauseResolver.Resolved resolved; if (value != null) { killer = value.KillerName ?? ""; resolved = value.Resolved; } else { HitData value2 = Traverse.Create((object)__instance).Field<HitData>("m_lastHit").Value; resolved = CauseResolver.Resolve(__instance, value2); Character val = ((value2 != null) ? value2.GetAttacker() : null); if ((Object)(object)val != (Object)null) { killer = NameResolver.Of(val); } } Network.Broadcast(new KillEvent { Victim = victim, Killer = killer, Section = resolved.Section, Weapon = resolved.Weapon, Distance = resolved.Distance }); } } }