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 Odins Raven Returns v0.1.3
plugins/OdinsRavenReturns.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("OdinsRavenReturns")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.3.0")] [assembly: AssemblyInformationalVersion("0.1.3+b26d2859c53736a20da2fed9567118fd75a0a4fd")] [assembly: AssemblyProduct("OdinsRavenReturns")] [assembly: AssemblyTitle("OdinsRavenReturns")] [assembly: AssemblyVersion("0.1.3.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace OdinsRavenReturns { internal sealed class ContentsSummary { public int Items; public float Weight; public int Coins; public string Notable = ""; public int NotableCount; public void CopyTo(GraveRecord r) { r.Items = Items; r.Weight = Weight; r.Coins = Coins; r.Notable = Notable; } public static ContentsSummary From(GraveRecord r) { ContentsSummary contentsSummary = new ContentsSummary(); contentsSummary.Items = r.Items; contentsSummary.Weight = r.Weight; contentsSummary.Coins = r.Coins; contentsSummary.Notable = r.Notable; contentsSummary.NotableCount = ((!string.IsNullOrEmpty(r.Notable)) ? r.Notable.Split(new char[1] { ';' }).Length : 0); return contentsSummary; } } internal static class GraveContents { private const int MaxNotableStored = 10; private static readonly Dictionary<ItemType, int> GearRank = new Dictionary<ItemType, int> { [(ItemType)14] = 0, [(ItemType)22] = 0, [(ItemType)3] = 0, [(ItemType)4] = 0, [(ItemType)5] = 1, [(ItemType)7] = 2, [(ItemType)11] = 2, [(ItemType)6] = 2, [(ItemType)17] = 2, [(ItemType)18] = 3, [(ItemType)24] = 3, [(ItemType)19] = 4 }; private static readonly Dictionary<ZDOID, KeyValuePair<uint, ContentsSummary>> Cache = new Dictionary<ZDOID, KeyValuePair<uint, ContentsSummary>>(); private static Inventory _scratch; internal static ContentsSummary FromZdo(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown if (Cache.TryGetValue(zdo.m_uid, out var value) && value.Key == zdo.DataRevision) { return value.Value; } ContentsSummary contentsSummary = null; byte[] byteArray = zdo.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray != null && (Object)(object)ObjectDB.instance != (Object)null) { try { if (_scratch == null) { _scratch = new Inventory("OdinsRavenReturns", (Sprite)null, 8, 8); } _scratch.Load(new ZPackage(byteArray)); contentsSummary = Summarise(_scratch); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Couldn't read a grave's contents: " + ex.Message)); } } contentsSummary = contentsSummary ?? new ContentsSummary(); if (Cache.Count > 64) { Cache.Clear(); } Cache[zdo.m_uid] = new KeyValuePair<uint, ContentsSummary>(zdo.DataRevision, contentsSummary); return contentsSummary; } internal static ContentsSummary Summarise(Inventory inventory) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) ContentsSummary contentsSummary = new ContentsSummary(); if (inventory == null) { return contentsSummary; } contentsSummary.Items = inventory.NrOfItems(); contentsSummary.Weight = inventory.GetTotalWeight(); List<ItemData> list = new List<ItemData>(); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem?.m_shared != null) { if (allItem.m_shared.m_name == "$item_coins") { contentsSummary.Coins += allItem.m_stack; } else if (GearRank.ContainsKey(allItem.m_shared.m_itemType)) { list.Add(allItem); } } } list.Sort(delegate(ItemData a, ItemData b) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) int num2 = GearRank[a.m_shared.m_itemType].CompareTo(GearRank[b.m_shared.m_itemType]); if (num2 != 0) { return num2; } int num3 = b.m_quality.CompareTo(a.m_quality); return (num3 == 0) ? b.m_shared.m_maxDurability.CompareTo(a.m_shared.m_maxDurability) : num3; }); contentsSummary.NotableCount = Mathf.Min(list.Count, 10); List<string> list2 = new List<string>(); for (int num = 0; num < contentsSummary.NotableCount; num++) { list2.Add(Describe(list[num])); } contentsSummary.Notable = string.Join(";", list2); return contentsSummary; } private static string Describe(ItemData item) { string text = ((Localization.instance != null) ? Localization.instance.Localize(item.m_shared.m_name) : item.m_shared.m_name); text = text.Replace(';', ',').Replace('|', '/'); if (item.m_quality <= 1 || item.m_shared.m_maxQuality <= 1) { return text; } return $"{text} (lvl {item.m_quality})"; } } internal class GravePanel : MonoBehaviour { private const float RefreshInterval = 0.5f; private const float RespawnDelay = 2.5f; private GameObject _tooltip; private RectTransform _panel; private TMP_Text _topic; private TMP_Text _text; private readonly Vector3[] _corners = (Vector3[])(object)new Vector3[4]; private CanvasGroup _group; private float _shownAt; private float _fadeOutStart = -1f; private float _fadeOutLength; private float _nextRefresh; private int _seed; private float _respawnSeenAt = -1f; private void LateUpdate() { bool flag = CanShow(); if (flag && ShowKeyDown()) { if (IsOpen() && !IsFading()) { Close(); } else { Open(); } } ShowAfterRespawn(flag); if (!flag || !IsOpen()) { Hide(); } else if (!((Object)(object)_tooltip == (Object)null) || CreateTooltip()) { if (Time.time >= _nextRefresh) { _nextRefresh = Time.time + 0.5f; GraveScanner.Refresh(); _topic.text = GraveText.Title(GraveScanner.Graves); _text.text = GraveText.Body(GraveScanner.Graves, Player.m_localPlayer, _seed); LayoutRebuilder.ForceRebuildLayoutImmediate(_panel); } if (!_tooltip.activeSelf) { _tooltip.SetActive(true); _tooltip.transform.SetAsLastSibling(); } _group.alpha = Alpha(); Place(); } } private void OnDestroy() { if ((Object)(object)_tooltip != (Object)null) { Object.Destroy((Object)(object)_tooltip); } } private bool IsOpen() { return Time.time < _fadeOutStart + _fadeOutLength; } private bool IsFading() { return Time.time >= _fadeOutStart; } private void Open() { float num = (IsOpen() ? Alpha() : 0f); _shownAt = Time.time - num * Plugin.FadeInSeconds.Value; _fadeOutStart = Time.time + Plugin.ShowSeconds.Value; _fadeOutLength = Plugin.FadeOutSeconds.Value; _nextRefresh = 0f; if (num == 0f) { _seed = Random.Range(0, int.MaxValue); } } private void ShowAfterRespawn(bool canShow) { if (!GraveRecords.JustDied) { _respawnSeenAt = -1f; } else { if (!canShow) { return; } if (_respawnSeenAt < 0f) { _respawnSeenAt = Time.time; } if (!(Time.time - _respawnSeenAt < 2.5f)) { GraveRecords.JustDied = false; if (Plugin.ShowAfterRespawn.Value) { GraveScanner.Poke(); Open(); } } } } private void Close() { _fadeOutStart = Time.time; _fadeOutLength = Plugin.FadeInSeconds.Value; } private float Alpha() { float value = Plugin.FadeInSeconds.Value; float num = ((value > 0f) ? Mathf.Clamp01((Time.time - _shownAt) / value) : 1f); float num2 = ((_fadeOutLength > 0f) ? Mathf.Clamp01((Time.time - _fadeOutStart) / _fadeOutLength) : (IsFading() ? 1f : 0f)); return num * (1f - num2); } private static bool CanShow() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || !HudVisible()) { return false; } if (!InventoryGui.IsVisible() && !Menu.IsVisible() && !Minimap.IsOpen() && !TextInput.IsVisible() && !Console.IsVisible()) { if ((Object)(object)Chat.instance != (Object)null) { return !Chat.instance.HasFocus(); } return true; } return false; } private static bool HudVisible() { if ((Object)(object)Hud.instance != (Object)null && !Hud.IsUserHidden()) { return Hud.instance.IsVisible(); } return false; } private static bool ShowKeyDown() { //IL_0005: 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) //IL_000d: 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_0035: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = Plugin.ShowKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !ZInput.GetKeyDown(((KeyboardShortcut)(ref value)).MainKey, false)) { return false; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers) { if (!ZInput.GetKey(modifier, false)) { return false; } } return true; } private void Place() { //IL_0034: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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_009a: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(Plugin.PositionX.Value * (float)Screen.width, Plugin.PositionY.Value * (float)Screen.height); _tooltip.transform.position = Vector2.op_Implicit(val); _panel.GetWorldCorners(_corners); Vector2 val2 = Vector2.op_Implicit((_corners[0] + _corners[2]) / 2f); Transform transform = _tooltip.transform; transform.position += Vector2.op_Implicit(val - val2); Utils.ClampUIToScreen(_panel); } private bool CreateTooltip() { GameObject val = FindTooltipPrefab(); if ((Object)(object)val == (Object)null) { return false; } Canvas componentInParent = ((Component)InventoryGui.instance).GetComponentInParent<Canvas>(); Canvas val2 = ((componentInParent != null) ? componentInParent.rootCanvas : null); if ((Object)(object)val2 == (Object)null) { return false; } _tooltip = Object.Instantiate<GameObject>(val, ((Component)val2).transform); ((Object)_tooltip).name = "OdinsRavenReturns"; ref RectTransform panel = ref _panel; Transform child = _tooltip.transform.GetChild(0); panel = (RectTransform)(object)((child is RectTransform) ? child : null); Transform obj = Utils.FindChild(_tooltip.transform, "Topic", (IterativeSearchType)0); _topic = ((obj != null) ? ((Component)obj).GetComponent<TMP_Text>() : null); Transform obj2 = Utils.FindChild(_tooltip.transform, "Text", (IterativeSearchType)0); _text = ((obj2 != null) ? ((Component)obj2).GetComponent<TMP_Text>() : null); if ((Object)(object)_panel == (Object)null || (Object)(object)_topic == (Object)null || (Object)(object)_text == (Object)null) { Object.Destroy((Object)(object)_tooltip); _tooltip = null; return false; } Graphic[] componentsInChildren = _tooltip.GetComponentsInChildren<Graphic>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].raycastTarget = false; } _group = _tooltip.GetComponent<CanvasGroup>(); if ((Object)(object)_group == (Object)null) { _group = _tooltip.AddComponent<CanvasGroup>(); } _group.interactable = false; _group.blocksRaycasts = false; _group.alpha = 0f; return true; } private static GameObject FindTooltipPrefab() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance == (Object)null) { return null; } UITooltip val = (((Object)(object)instance.m_playerGrid != (Object)null && (Object)(object)instance.m_playerGrid.m_elementPrefab != (Object)null) ? instance.m_playerGrid.m_elementPrefab.GetComponentInChildren<UITooltip>(true) : null); if ((Object)(object)val != (Object)null && (Object)(object)val.m_tooltipPrefab != (Object)null) { return val.m_tooltipPrefab; } UITooltip[] componentsInChildren = ((Component)instance).GetComponentsInChildren<UITooltip>(true); foreach (UITooltip val2 in componentsInChildren) { if ((Object)(object)val2.m_tooltipPrefab != (Object)null) { return val2.m_tooltipPrefab; } } return null; } private void Hide() { if ((Object)(object)_tooltip != (Object)null && _tooltip.activeSelf) { _tooltip.SetActive(false); } } } internal sealed class GraveRecord { public long World; public long Ticks; public Vector3 Position; public string HitType = ""; public string Killer = ""; public string KillerPrefab = ""; public int KillerLevel; public bool KillerIsBoss; public int Items; public float Weight; public int Coins; public string Notable = ""; } internal static class GraveRecords { [HarmonyPatch(typeof(Player), "OnDeath")] private static class OnDeathPatch { private static void Prefix(Player __instance, HitData ___m_lastHit) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } GraveRecord graveRecord = new GraveRecord(); try { if (___m_lastHit != null) { graveRecord.HitType = ((object)Unsafe.As<HitType, HitType>(ref ___m_lastHit.m_hitType)/*cast due to .constrained prefix*/).ToString(); Character attacker = ___m_lastHit.GetAttacker(); if ((Object)(object)attacker != (Object)null && (Object)(object)attacker != (Object)(object)__instance) { Player val = (Player)(object)((attacker is Player) ? attacker : null); graveRecord.Killer = ((val != null) ? val.GetPlayerName() : Localization.instance.Localize(attacker.m_name)); graveRecord.KillerPrefab = ((attacker is Player) ? "Player" : Utils.GetPrefabName(((Component)attacker).gameObject)); graveRecord.KillerLevel = attacker.GetLevel(); graveRecord.KillerIsBoss = attacker.IsBoss(); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Couldn't tell what killed you: " + ex.Message)); } _pendingCause = graveRecord; } } [HarmonyPatch(typeof(TombStone), "Setup")] private static class SetupPatch { private static void Postfix(TombStone __instance, long ownerUID, ZNetView ___m_nview, Container ___m_container) { //IL_00a8: 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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)Game.instance == (Object)null || ownerUID != Game.instance.GetPlayerProfile().GetPlayerID() || (Object)(object)___m_nview == (Object)null || !___m_nview.IsValid()) { return; } GraveRecord r = _pendingCause ?? new GraveRecord(); _pendingCause = null; r.World = CurrentWorld(); r.Ticks = ___m_nview.GetZDO().GetLong(ZDOVars.s_timeOfDeath, 0L); if (r.Ticks != 0L) { r.Position = ((Component)__instance).transform.position; if ((Object)(object)___m_container != (Object)null) { GraveContents.Summarise(___m_container.GetInventory()).CopyTo(r); } List<GraveRecord> list = Load(localPlayer); list.RemoveAll((GraveRecord x) => x.World == r.World && x.Ticks == r.Ticks); list.Add(r); Save(localPlayer, list); JustDied = true; GraveScanner.Poke(); } } } [HarmonyPatch(typeof(TombStone), "Start")] private static class StartPatch { private static void Postfix(TombStone __instance, ZNetView ___m_nview) { if (!((Object)(object)___m_nview == (Object)null) && ___m_nview.IsValid() && !((Object)(object)Game.instance == (Object)null) && ___m_nview.GetZDO().GetLong(ZDOVars.s_owner, 0L) == Game.instance.GetPlayerProfile().GetPlayerID() && (Object)(object)((Component)__instance).GetComponent<RavenGuard>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<RavenGuard>(); } } } private const string Key = "OdinsRavenReturns.Graves"; private const int MaxRecords = 40; private static GraveRecord _pendingCause; internal static bool JustDied; internal static List<GraveRecord> Load(Player player) { List<GraveRecord> list = new List<GraveRecord>(); if ((Object)(object)player == (Object)null || !player.m_customData.TryGetValue("OdinsRavenReturns.Graves", out var value) || string.IsNullOrEmpty(value)) { return list; } string[] array = value.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { GraveRecord graveRecord = Parse(array[i]); if (graveRecord != null) { list.Add(graveRecord); } } return list; } internal static void Save(Player player, List<GraveRecord> records) { if ((Object)(object)player == (Object)null) { return; } records.Sort((GraveRecord a, GraveRecord b) => b.Ticks.CompareTo(a.Ticks)); if (records.Count > 40) { records.RemoveRange(40, records.Count - 40); } StringBuilder stringBuilder = new StringBuilder(); foreach (GraveRecord record in records) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(Format(record)); } if (stringBuilder.Length == 0) { player.m_customData.Remove("OdinsRavenReturns.Graves"); } else { player.m_customData["OdinsRavenReturns.Graves"] = stringBuilder.ToString(); } } internal static void Forget(Player player, long world, long ticks) { List<GraveRecord> list = Load(player); if (list.RemoveAll((GraveRecord r) => r.World == world && r.Ticks == ticks) > 0) { Save(player, list); } } internal static long CurrentWorld() { if (ZNet.World == null) { return 0L; } return ZNet.World.m_uid; } private static string Format(GraveRecord r) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; return string.Join("|", "1", r.World.ToString(invariantCulture), r.Ticks.ToString(invariantCulture), r.Position.x.ToString("R", invariantCulture), r.Position.y.ToString("R", invariantCulture), r.Position.z.ToString("R", invariantCulture), Clean(r.HitType), Clean(r.Killer), Clean(r.KillerPrefab), r.KillerLevel.ToString(invariantCulture), r.KillerIsBoss ? "1" : "0", r.Items.ToString(invariantCulture), r.Weight.ToString("R", invariantCulture), r.Coins.ToString(invariantCulture), Clean(r.Notable)); } private static GraveRecord Parse(string line) { //IL_006f: 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) string[] array = line.Split(new char[1] { '|' }); if (array.Length < 15 || array[0] != "1") { return null; } CultureInfo invariantCulture = CultureInfo.InvariantCulture; try { return new GraveRecord { World = long.Parse(array[1], invariantCulture), Ticks = long.Parse(array[2], invariantCulture), Position = new Vector3(float.Parse(array[3], invariantCulture), float.Parse(array[4], invariantCulture), float.Parse(array[5], invariantCulture)), HitType = array[6], Killer = array[7], KillerPrefab = array[8], KillerLevel = int.Parse(array[9], invariantCulture), KillerIsBoss = (array[10] == "1"), Items = int.Parse(array[11], invariantCulture), Weight = float.Parse(array[12], invariantCulture), Coins = int.Parse(array[13], invariantCulture), Notable = array[14] }; } catch (FormatException) { return null; } } private static string Clean(string s) { return (s ?? "").Replace('|', '/').Replace('\n', ' '); } } internal sealed class GraveInfo { public long Ticks; public Vector3 Position; public bool Live; public bool InWater; public ContentsSummary Contents; public GraveRecord Record; } internal class GraveScanner : MonoBehaviour { private const float RestInterval = 2f; private const int ScansBeforeForgetting = 3; private static GraveScanner _instance; private readonly List<ZDO> _scanning = new List<ZDO>(); private List<ZDO> _found = new List<ZDO>(); private int _index; private float _nextScan; private bool _haveFullScan; private string _prefabName; private int _prefabHash; private readonly Dictionary<long, int> _missing = new Dictionary<long, int>(); internal static readonly List<GraveInfo> Graves = new List<GraveInfo>(); internal static bool Scanned { get { if ((Object)(object)_instance != (Object)null) { return _instance._haveFullScan; } return false; } } internal static void Poke() { if ((Object)(object)_instance != (Object)null) { _instance._nextScan = 0f; } } private void Awake() { _instance = this; } private void Update() { if (ZDOMan.instance == null || (Object)(object)ZNet.instance == (Object)null) { _haveFullScan = false; _found.Clear(); _index = 0; _scanning.Clear(); _missing.Clear(); } else if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Game.instance == (Object)null) && (_index != 0 || !(Time.time < _nextScan))) { if (_prefabName == null) { _prefabName = (((Object)(object)Player.m_localPlayer.m_tombstone != (Object)null) ? ((Object)Player.m_localPlayer.m_tombstone).name : "Player_tombstone"); _prefabHash = StringExtensionMethods.GetStableHashCode(_prefabName); } if (ZDOMan.instance.GetAllZDOsWithPrefabIterative(_prefabName, _scanning, ref _index)) { List<ZDO> found = _found; _found = new List<ZDO>(_scanning); found.Clear(); _scanning.Clear(); _index = 0; _haveFullScan = true; _nextScan = Time.time + 2f; ForgetLootedGraves(); } } } private void ForgetLootedGraves() { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); long num = GraveRecords.CurrentWorld(); HashSet<long> hashSet = new HashSet<long>(); foreach (ZDO item in _found) { if (IsGraveOf(item, playerID)) { hashSet.Add(item.GetLong(ZDOVars.s_timeOfDeath, 0L)); } } List<GraveRecord> list = GraveRecords.Load(localPlayer); bool flag = false; GraveRecord[] array = list.ToArray(); foreach (GraveRecord graveRecord in array) { if (graveRecord.World != num) { continue; } if (hashSet.Contains(graveRecord.Ticks)) { _missing.Remove(graveRecord.Ticks); } else if (ZNet.instance.IsServer() || ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsZoneLoaded(graveRecord.Position))) { _missing.TryGetValue(graveRecord.Ticks, out var value); if (++value < 3) { _missing[graveRecord.Ticks] = value; continue; } _missing.Remove(graveRecord.Ticks); list.Remove(graveRecord); flag = true; } } if (flag) { GraveRecords.Save(localPlayer, list); } } internal static void Refresh() { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) Graves.Clear(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)_instance == (Object)null || (Object)(object)localPlayer == (Object)null || (Object)(object)Game.instance == (Object)null) { return; } long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); long num = GraveRecords.CurrentWorld(); Dictionary<long, GraveRecord> dictionary = new Dictionary<long, GraveRecord>(); foreach (GraveRecord item in GraveRecords.Load(localPlayer)) { if (item.World == num) { dictionary[item.Ticks] = item; } } HashSet<long> hashSet = new HashSet<long>(); foreach (ZDO item2 in _instance._found) { if (_instance.IsGraveOf(item2, playerID)) { long num2 = item2.GetLong(ZDOVars.s_timeOfDeath, 0L); if (hashSet.Add(num2)) { dictionary.TryGetValue(num2, out var value); Graves.Add(new GraveInfo { Ticks = num2, Position = item2.GetPosition(), Live = true, InWater = item2.GetBool(ZDOVars.s_inWater, false), Contents = GraveContents.FromZdo(item2), Record = value }); } } } foreach (GraveRecord value2 in dictionary.Values) { if (!hashSet.Contains(value2.Ticks)) { Graves.Add(new GraveInfo { Ticks = value2.Ticks, Position = value2.Position, Live = false, Contents = ContentsSummary.From(value2), Record = value2 }); } } Graves.Sort((GraveInfo a, GraveInfo b) => b.Ticks.CompareTo(a.Ticks)); } private bool IsGraveOf(ZDO zdo, long owner) { if (zdo.IsValid() && zdo.GetPrefab() == _prefabHash) { return zdo.GetLong(ZDOVars.s_owner, 0L) == owner; } return false; } } internal static class GraveText { private const string Value = "orange"; private const string FlavorColor = "#B9B3A8"; private const string NoteColor = "#B0B0B0"; private const string DangerColor = "#FF6A55"; private const string SafeColor = "#9BD86E"; private static readonly StringBuilder Sb = new StringBuilder(); private static readonly string[] Points = new string[8] { "north", "north-east", "east", "south-east", "south", "south-west", "west", "north-west" }; internal static string Title(List<GraveInfo> graves) { if (graves.Count == 0) { return "Odin's raven"; } if (graves.Count != 1) { return $"Your graves ({graves.Count})"; } return "Your grave"; } internal static string Body(List<GraveInfo> graves, Player player, int seed) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) Sb.Clear(); int num = Mathf.Min(graves.Count, Plugin.MaxGraves.Value); ThreatReport[] array = new ThreatReport[num]; for (int i = 0; i < num; i++) { array[i] = (graves[i].Live ? ThreatRadar.Around(graves[i].Position, player) : new ThreatReport()); } if (Plugin.ShowFlavor.Value) { Sb.Append("<i><color=").Append("#B9B3A8").Append('>') .Append(Remarks.Pick(graves, (num > 0) ? array[0] : null, player, seed)) .Append("</color></i>\n"); } if (graves.Count == 0) { Sb.Append("\n<color=").Append("#B0B0B0").Append(">No graves of yours in this world. The raven only knows the ones dug in this world, by this character.</color>"); return Sb.ToString(); } for (int j = 0; j < num; j++) { AppendGrave(graves[j], j, num, array[j], player); } if (graves.Count > num) { Sb.Append("\n<color=").Append("#B0B0B0").Append(">And ") .Append(graves.Count - num) .Append((graves.Count - num == 1) ? " older grave" : " older graves") .Append(" not listed. See MaxGraves.</color>"); } return Sb.ToString(); } private static void AppendGrave(GraveInfo g, int index, int shown, ThreatReport threat, Player player) { //IL_0053: 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_005a: 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_0060: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) Sb.Append('\n'); if (shown > 1) { Sb.Append("<b>").Append((index == 0) ? "Newest" : ((index == shown - 1) ? "Oldest" : "Older")).Append("</b>\n"); } Vector3 position = ((Component)player).transform.position; Vector3 delta = g.Position - position; delta.y = 0f; float magnitude = ((Vector3)(ref delta)).magnitude; Sb.Append("Where: "); if (magnitude < 6f) { Sb.Append("<color=").Append("orange").Append(">right here</color>"); } else { Sb.Append("<color=").Append("orange").Append('>') .Append(Distance(magnitude)) .Append(' ') .Append(Compass(Bearing(delta))) .Append("</color>"); string text = Side(delta); if (text != null) { Sb.Append(", ").Append(text); } } string text2 = Biome(g.Position); if (text2 != null) { Sb.Append(", in the <color=").Append("orange").Append('>') .Append(text2) .Append("</color>"); } Sb.Append('\n'); Sb.Append("Died: "); if ((Object)(object)ZNet.instance != (Object)null && g.Ticks > 0) { double num = (double)g.Ticks / 10000000.0; Sb.Append("<color=").Append("orange").Append('>') .Append(Ago(ZNet.instance.GetTimeSeconds() - num)) .Append("</color>"); if ((Object)(object)EnvMan.instance != (Object)null) { Sb.Append(" (day ").Append(EnvMan.instance.GetDay(num)).Append(')'); } } else { Sb.Append("<color=").Append("orange").Append(">a while ago</color>"); } string text3 = Cause(g.Record); if (text3 != null) { Sb.Append(". ").Append(text3); } Sb.Append('\n'); if (Plugin.ShowContents.Value && g.Contents != null) { ContentsSummary contents = g.Contents; Sb.Append(g.Live ? "Holds: " : "Held: "); Sb.Append("<color=").Append("orange").Append('>') .Append(contents.Items) .Append((contents.Items == 1) ? " item" : " items") .Append("</color>, ") .Append("<color=") .Append("orange") .Append('>') .Append(Mathf.RoundToInt(contents.Weight)) .Append(" weight</color>"); if (contents.Coins > 0) { Sb.Append(", <color=").Append("orange").Append('>') .Append(contents.Coins) .Append(" coins</color>"); } if (!g.Live) { Sb.Append(" <color=").Append("#B0B0B0").Append(">(when you died)</color>"); } Sb.Append('\n'); int value = Plugin.NotableItems.Value; if (value > 0 && !string.IsNullOrEmpty(contents.Notable)) { string[] array = contents.Notable.Split(new char[1] { ';' }); int num2 = Mathf.Min(value, array.Length); Sb.Append("Gear: <color=").Append("orange").Append('>') .Append(string.Join(", ", array, 0, num2)) .Append("</color>"); if (array.Length > num2) { Sb.Append(" and ").Append(array.Length - num2).Append(" more"); } Sb.Append('\n'); } if (g.Live) { float num3 = ((Humanoid)player).GetInventory().GetTotalWeight() + contents.Weight - player.GetMaxCarryWeight(); Sb.Append("Carry: "); if (num3 <= 0f) { Sb.Append("<color=").Append("#9BD86E").Append(">it all fits</color>\n"); } else { Sb.Append("<color=").Append("#FF6A55").Append('>') .Append(Mathf.CeilToInt(num3)) .Append(" over your limit</color>\n"); } } } Sb.Append("Nearby: "); if (!threat.Known) { Sb.Append("<color=").Append("#B0B0B0").Append(">too far for the raven to report</color>"); } else if (threat.Total == 0) { Sb.Append("<color=").Append("#9BD86E").Append(">nothing hostile</color>"); } else { Sb.Append("<color=").Append("#FF6A55").Append('>'); for (int i = 0; i < threat.Hostiles.Count; i++) { if (i == 3) { Sb.Append(" and more"); break; } if (i > 0) { Sb.Append(", "); } Sb.Append(threat.Hostiles[i].Key); if (threat.Hostiles[i].Value > 1) { Sb.Append(" x").Append(threat.Hostiles[i].Value); } } Sb.Append("</color>"); } Sb.Append('\n'); if (g.InWater) { Sb.Append("<color=").Append("#B0B0B0").Append(">It's floating. Bring a boat.</color>\n"); } } internal static string Cause(GraveRecord r) { if (r == null) { return null; } if (!string.IsNullOrEmpty(r.Killer)) { if (r.KillerPrefab == "Player") { return "Killed by <color=orange>" + r.Killer + "</color>"; } string text = ((r.KillerLevel > 1) ? string.Format(" ({0} star{1})", r.KillerLevel - 1, (r.KillerLevel > 2) ? "s" : "") : ""); if (r.KillerIsBoss) { return "Killed by <color=orange>" + r.Killer + "</color>"; } return "Killed by " + Article(r.Killer) + " <color=orange>" + r.Killer + "</color>" + text; } return r.HitType switch { "EnemyHit" => "Killed by something you didn't see", "PlayerHit" => "Killed by another viking", "Fall" => "Fell from a height", "Drowning" => "Drowned", "Burning" => "Burned to death", "Freezing" => "Froze to death", "Poisoned" => "Poisoned", "Water" => "Taken by the water", "Smoke" => "Choked on smoke", "EdgeOfWorld" => "Fell off the edge of the world", "Impact" => "Hit by something heavy", "Cart" => "Run over by a cart", "Tree" => "Flattened by a falling tree", "Self" => "Killed by your own hand", "Structural" => "Crushed by a falling building", "Turret" => "Shot by a turret", "Boat" => "Hit by a boat", "Stalagtite" => "Struck by a falling stalactite", "Catapult" => "Hit by a catapult", "CinderFire" => "Burned by cinder fire", "AshlandsOcean" => "Boiled in the Ashlands sea", "Incinerator" => "Fed to an incinerator", "DrawBridge" => "Flattened by a drawbridge", "AshlandsLava" => "Fell into lava", "" => null, _ => "Died of causes unknown", }; } private static string Article(string name) { if (name.Length <= 0 || "AEIOUaeiou".IndexOf(name[0]) < 0) { return "a"; } return "an"; } private unsafe static string Biome(Vector3 pos) { //IL_0015: 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_001b: 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) if (WorldGenerator.instance == null || Localization.instance == null) { return null; } Biome biome = WorldGenerator.instance.GetBiome(pos); if ((int)biome == 0) { return null; } string text = Localization.instance.Localize("$biome_" + ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString().ToLower()); if (!text.StartsWith("[")) { return text; } return ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString(); } private static string Distance(float metres) { if (metres < 1000f) { return $"{Mathf.RoundToInt(metres / 5f) * 5} m"; } return $"{metres / 1000f:0.0} km"; } internal static string Ago(double seconds) { if (seconds < 60.0) { return "moments ago"; } int num = Mathf.RoundToInt((float)(seconds / 60.0)); if (num < 60) { return $"{num} min ago"; } if (num < 2880) { if (num % 60 != 0) { return $"{num / 60} h {num % 60} min ago"; } return $"{num / 60} h ago"; } return $"{num / 1440} days ago"; } private static float Bearing(Vector3 delta) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) return Mathf.Repeat(Mathf.Atan2(delta.x, delta.z) * 57.29578f, 360f); } private static string Compass(float bearing) { return Points[Mathf.RoundToInt(bearing / 45f) % 8]; } private static string Side(Vector3 delta) { //IL_0017: 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) //IL_003a: 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) Camera mainCamera = Utils.GetMainCamera(); if ((Object)(object)mainCamera == (Object)null) { return null; } Vector3 forward = ((Component)mainCamera).transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { return null; } float num = Vector3.SignedAngle(forward, delta, Vector3.up); float num2 = Mathf.Abs(num); if (num2 < 25f) { return "straight ahead"; } if (num2 > 150f) { return "behind you"; } if (num2 < 70f) { if (!(num > 0f)) { return "ahead to your left"; } return "ahead to your right"; } if (!(num > 0f)) { return "to your left"; } return "to your right"; } } [BepInPlugin("hevyh2o.valheim.odinsravenreturns", "Odin's Raven Returns", "0.1.3")] public class Plugin : BaseUnityPlugin { public const string Guid = "hevyh2o.valheim.odinsravenreturns"; public const string Name = "Odin's Raven Returns"; public const string Version = "0.1.3"; internal static ManualLogSource Log; internal static ConfigEntry<KeyboardShortcut> ShowKey; internal static ConfigEntry<bool> ShowAfterRespawn; internal static ConfigEntry<float> ShowSeconds; internal static ConfigEntry<float> FadeInSeconds; internal static ConfigEntry<float> FadeOutSeconds; internal static ConfigEntry<float> PositionX; internal static ConfigEntry<float> PositionY; internal static ConfigEntry<int> MaxGraves; internal static ConfigEntry<bool> ShowContents; internal static ConfigEntry<int> NotableItems; internal static ConfigEntry<float> ThreatRadius; internal static ConfigEntry<string> IgnoredCreatures; internal static ConfigEntry<bool> ShowFlavor; internal static ConfigEntry<bool> ShowRaven; internal static ConfigEntry<bool> Caw; internal static ConfigEntry<float> CawDistance; internal static ConfigEntry<float> CawInterval; internal static ConfigEntry<float> CawVolume; internal static ConfigEntry<bool> ShowGlow; private Harmony _harmony; private GravePanel _panel; private GraveScanner _scanner; private void Awake() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Expected O, but got Unknown //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Expected O, but got Unknown //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Expected O, but got Unknown //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Expected O, but got Unknown //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Expected O, but got Unknown //IL_0374: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Expected O, but got Unknown //IL_03a9: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ShowKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Display", "ShowKey", new KeyboardShortcut((KeyCode)104, Array.Empty<KeyCode>()), "Key that opens the raven's report. Press again to close it early, or while it's fading to bring it back. (G is taken: vanilla uses it for the radial menu.)"); ShowAfterRespawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowAfterRespawn", true, "Open the report automatically a couple of seconds after you respawn."); ShowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ShowSeconds", 20f, new ConfigDescription("How long the report stays fully visible before it starts to fade.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 120f), Array.Empty<object>())); FadeInSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "FadeInSeconds", 0.3f, new ConfigDescription("How long the report takes to fade in. Also used when closing it with ShowKey. 0 = instant.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>())); FadeOutSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "FadeOutSeconds", 5f, new ConfigDescription("How long the report takes to fade away once ShowSeconds is up. 0 = vanish at once.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>())); PositionX = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "PositionX", 0.8f, new ConfigDescription("Horizontal position of the report, 0 = left edge, 1 = right edge.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); PositionY = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "PositionY", 0.6f, new ConfigDescription("Vertical position of the report, 0 = bottom, 1 = top.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); MaxGraves = ((BaseUnityPlugin)this).Config.Bind<int>("General", "MaxGraves", 3, new ConfigDescription("How many of your graves to list, newest first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); ShowContents = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowContents", true, "Show what's in each grave: item count, weight, coins and notable gear."); NotableItems = ((BaseUnityPlugin)this).Config.Bind<int>("General", "NotableItems", 4, new ConfigDescription("How many pieces of notable gear (weapons, armour, tools) to name per grave. 0 = none.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 10), Array.Empty<object>())); ThreatRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ThreatRadius", 25f, new ConfigDescription("How close (in metres) to a grave a hostile creature must be for the raven to warn you about it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())); IgnoredCreatures = ((BaseUnityPlugin)this).Config.Bind<string>("General", "IgnoredCreatures", "", "Creatures the raven never warns about, separated by commas. Use the prefab name or the name shown in game."); ShowFlavor = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowFlavor", true, "Show Odin's remark at the top of the report."); ShowRaven = ((BaseUnityPlugin)this).Config.Bind<bool>("Raven", "ShowRaven", true, "Perch one of Odin's ravens on each of your graves. Only you can see it."); Caw = ((BaseUnityPlugin)this).Config.Bind<bool>("Raven", "Caw", true, "The raven caws every so often while you're within CawDistance of your grave, louder the closer you get."); CawDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Raven", "CawDistance", 120f, new ConfigDescription("How far away (in metres) you can hear the raven. Graves only load within about 150 m of you, so higher values make no difference.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 150f), Array.Empty<object>())); CawInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Raven", "CawInterval", 15f, new ConfigDescription("Roughly how many seconds between caws. Each wait varies a little, like a real bird.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 120f), Array.Empty<object>())); CawVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Raven", "CawVolume", 1f, new ConfigDescription("How loud the caw is beside the grave, 1 = the game's own volume. At the edge of CawDistance it's 40% of this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); ShowGlow = ((BaseUnityPlugin)this).Config.Bind<bool>("Raven", "ShowGlow", true, "A soft blue light at each of your graves, so you can spot it at night."); _harmony = new Harmony("hevyh2o.valheim.odinsravenreturns"); _harmony.PatchAll(typeof(Plugin).Assembly); _scanner = ((Component)this).gameObject.AddComponent<GraveScanner>(); _panel = ((Component)this).gameObject.AddComponent<GravePanel>(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Raven Returns 0.1.3 loaded. A raven is waiting for bad news."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)_panel != (Object)null) { Object.Destroy((Object)(object)_panel); } if ((Object)(object)_scanner != (Object)null) { Object.Destroy((Object)(object)_scanner); } } } internal class RavenGuard : MonoBehaviour { private const float FoundDistance = 4f; private const float FarLoudness = 0.4f; private const float EarDistance = 2f; private const float TopCheckInterval = 2f; private const float RotateSpeed = 120f; private static Raven _template; private static bool _lookedForTemplate; private GameObject _raven; private Animator _animator; private Light _glow; private float _top = 1.4f; private float _nextTopCheck; private float _nextCaw; private float _glowPhase; private static bool _reportedCaw; private static GameObject _cawSound; private static bool _lookedForCaw; private void Start() { _glowPhase = Random.value * 10f; _nextCaw = Time.time + 3f + Random.value * 3f; MeasureTop(); } private void LateUpdate() { if (Time.time >= _nextTopCheck) { MeasureTop(); } UpdateRaven(); UpdateGlow(); UpdateCaw(); } private void OnDestroy() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Container component = ((Component)this).GetComponent<Container>(); if ((Object)(object)component != (Object)null && component.GetInventory() != null && component.GetInventory().NrOfItems() == 0 && (Object)(object)_raven != (Object)null && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && (Object)(object)_template != (Object)null && (Object)(object)ZNetScene.instance != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)this).transform.position) < 40f) { _template.m_despawnEffect.Create(_raven.transform.position, _raven.transform.rotation, (Transform)null, 1f, -1, default(ZDOID)); } if ((Object)(object)_raven != (Object)null) { Object.Destroy((Object)(object)_raven); } if ((Object)(object)_glow != (Object)null) { Object.Destroy((Object)(object)((Component)_glow).gameObject); } } private void UpdateRaven() { //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) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ShowRaven.Value) { if ((Object)(object)_raven != (Object)null) { Object.Destroy((Object)(object)_raven); } _raven = null; } else { if ((Object)(object)_raven == (Object)null && !SpawnRaven()) { return; } _raven.transform.position = ((Component)this).transform.position + Vector3.up * _top; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Vector3 val = ((Component)localPlayer).transform.position - _raven.transform.position; val.y = 0f; if (!(((Vector3)(ref val)).sqrMagnitude < 0.01f)) { Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized); float num = Vector3.SignedAngle(_raven.transform.forward, val, Vector3.up); if ((Object)(object)_animator != (Object)null) { _animator.SetFloat("anglevel", (Mathf.Abs(num) > 15f) ? (10f * Mathf.Sign(num)) : 0f, 0.4f, Time.deltaTime); } if (Mathf.Abs(num) > 15f) { _raven.transform.rotation = Quaternion.RotateTowards(_raven.transform.rotation, val2, 120f * Time.deltaTime); } } } } private bool SpawnRaven() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_021e: 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_0215: Unknown result type (might be due to invalid IL or missing references) Raven val = FindTemplate(); if ((Object)(object)val == (Object)null || (Object)(object)val.m_visual == (Object)null) { return false; } GameObject val2 = new GameObject("OdinsRavenReturns_holder"); val2.SetActive(false); GameObject val3 = Object.Instantiate<GameObject>(val.m_visual, val2.transform); string text = PathFrom(val.m_visual.transform, ((Object)(object)val.m_exclamation != (Object)null) ? val.m_exclamation.transform : null); if (text != null) { Transform val4 = val3.transform.Find(text); if (val4 != null) { Object.DestroyImmediate((Object)(object)((Component)val4).gameObject); } } MonoBehaviour[] componentsInChildren = val3.GetComponentsInChildren<MonoBehaviour>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } Collider[] componentsInChildren2 = val3.GetComponentsInChildren<Collider>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } Rigidbody[] componentsInChildren3 = val3.GetComponentsInChildren<Rigidbody>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } GameObject val5 = new GameObject("OdinsRavenReturns_Raven"); Transform transform = ((Component)val).transform; Transform transform2 = val.m_visual.transform; val3.transform.SetParent(val5.transform, false); val3.transform.localPosition = transform.InverseTransformPoint(transform2.position); val3.transform.localRotation = Quaternion.Inverse(transform.rotation) * transform2.rotation; val3.transform.localScale = transform2.lossyScale; val5.transform.position = ((Component)this).transform.position + Vector3.up * _top; Player localPlayer = Player.m_localPlayer; Vector3 val6 = (((Object)(object)localPlayer != (Object)null) ? (((Component)localPlayer).transform.position - val5.transform.position) : ((Component)this).transform.forward); val6.y = 0f; val5.transform.rotation = ((((Vector3)(ref val6)).sqrMagnitude > 0.01f) ? Quaternion.LookRotation(((Vector3)(ref val6)).normalized) : ((Component)this).transform.rotation); Object.Destroy((Object)(object)val2); val3.SetActive(true); _animator = val3.GetComponentInChildren<Animator>(); if ((Object)(object)_animator != (Object)null) { _animator.fireEvents = false; _animator.SetTrigger("teleportin"); } _raven = val5; return true; } private static string PathFrom(Transform root, Transform child) { if ((Object)(object)child == (Object)null || (Object)(object)child == (Object)(object)root) { return null; } string text = ((Object)child).name; Transform parent = child.parent; while ((Object)(object)parent != (Object)null) { if ((Object)(object)parent == (Object)(object)root) { return text; } text = ((Object)parent).name + "/" + text; parent = parent.parent; } return null; } private static Raven FindTemplate() { if ((Object)(object)_template != (Object)null || _lookedForTemplate) { return _template; } if ((Object)(object)Tutorial.instance == (Object)null || (Object)(object)Tutorial.instance.m_ravenPrefab == (Object)null) { return null; } _lookedForTemplate = true; Raven[] componentsInChildren = Tutorial.instance.m_ravenPrefab.GetComponentsInChildren<Raven>(true); foreach (Raven val in componentsInChildren) { if ((Object)(object)_template == (Object)null || (_template.m_isMunin && !val.m_isMunin)) { _template = val; } } if ((Object)(object)_template == (Object)null) { Plugin.Log.LogWarning((object)"Couldn't find Hugin's model; graves get a glow but no raven. A game update may have moved him."); } return _template; } private void UpdateGlow() { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ShowGlow.Value) { if ((Object)(object)_glow != (Object)null) { Object.Destroy((Object)(object)((Component)_glow).gameObject); } _glow = null; return; } if ((Object)(object)_glow == (Object)null) { GameObject val = new GameObject("OdinsRavenReturns_Glow"); _glow = val.AddComponent<Light>(); _glow.type = (LightType)2; _glow.color = new Color(0.55f, 0.75f, 1f); _glow.range = 6f; _glow.shadows = (LightShadows)0; } ((Component)_glow).transform.position = ((Component)this).transform.position + Vector3.up * (_top * 0.6f); float num = Time.time + _glowPhase; _glow.intensity = 1.1f + 0.25f * Mathf.Sin(num * 1.7f) + 0.1f * Mathf.Sin(num * 4.3f); } private void UpdateCaw() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010c: 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_0113: 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_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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_0123: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_016d: Unknown result type (might be due to invalid IL or missing references) if (Time.time < _nextCaw) { return; } _nextCaw = Time.time + Plugin.CawInterval.Value * Random.Range(0.7f, 1.3f); Player localPlayer = Player.m_localPlayer; if (!Plugin.Caw.Value || (Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { return; } float num = Vector3.Distance(((Component)localPlayer).transform.position, ((Component)this).transform.position); float value = Plugin.CawDistance.Value; if (num > value || num < 4f) { return; } GameObject val = FindCawSound(); if (!((Object)(object)val == (Object)null)) { Vector3 val2 = (((Object)(object)_raven != (Object)null) ? _raven.transform.position : (((Component)this).transform.position + Vector3.up * _top)); Camera mainCamera = Utils.GetMainCamera(); Vector3 val3 = (((Object)(object)mainCamera != (Object)null) ? ((Component)mainCamera).transform.position : ((Component)localPlayer).transform.position); Vector3 val4 = val2 - val3; Vector3 val5 = ((((Vector3)(ref val4)).magnitude > 2f) ? (val3 + ((Vector3)(ref val4)).normalized * 2f) : val2); float num2 = Plugin.CawVolume.Value * Mathf.Lerp(1f, 0.4f, Mathf.Sqrt(Mathf.Clamp01(num / value))); GameObject val6 = Object.Instantiate<GameObject>(val, val5, Quaternion.identity); ZSFX[] componentsInChildren = val6.GetComponentsInChildren<ZSFX>(true); foreach (ZSFX obj in componentsInChildren) { obj.m_playOnAwake = true; obj.SetVolumeModifier(num2); } Object.Destroy((Object)(object)val6, 10f); if (!_reportedCaw) { _reportedCaw = true; ((MonoBehaviour)this).StartCoroutine(ReportCaw(val6, num, num2)); } if ((Object)(object)_animator != (Object)null) { _animator.SetTrigger("talk"); } } } private static IEnumerator ReportCaw(GameObject sfx, float distance, float loudness) { yield return (object)new WaitForSeconds(0.3f); if ((Object)(object)sfx == (Object)null) { Plugin.Log.LogInfo((object)$"First caw ({distance:0} m away, loudness {loudness:0.00}): the sound was removed before it could be checked."); yield break; } AudioSource[] componentsInChildren = sfx.GetComponentsInChildren<AudioSource>(true); foreach (AudioSource val in componentsInChildren) { Plugin.Log.LogInfo((object)(string.Format("First caw ({0:0} m away, loudness {1:0.00}): playing={2}, clip={3}, ", distance, loudness, val.isPlaying, ((Object)(object)val.clip != (Object)null) ? ((Object)val.clip).name : "none") + $"volume={val.volume:0.00}, rolloff={val.rolloffMode} {val.minDistance:0}-{val.maxDistance:0} m, spatial={val.spatialBlend:0.0}, " + string.Format("mixer={0}, mute={1}, enabled={2}", ((Object)(object)val.outputAudioMixerGroup != (Object)null) ? ((Object)val.outputAudioMixerGroup).name : "none", val.mute, ((Behaviour)val).enabled && ((Component)val).gameObject.activeInHierarchy))); } } private static GameObject FindCawSound() { //IL_012e: 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) if ((Object)(object)_cawSound != (Object)null || _lookedForCaw) { return _cawSound; } Raven val = FindTemplate(); if ((Object)(object)val == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return null; } _lookedForCaw = true; List<string> list = new List<string>(); GameObject prefab = ZNetScene.instance.GetPrefab("Crow"); RandomFlyingBird val2 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<RandomFlyingBird>() : null); if ((Object)(object)val2 != (Object)null) { _cawSound = FirstAudible(val2.m_randomNoise, "the crow's call", list); } else { list.Add("the crow: not found"); } if ((Object)(object)_cawSound == (Object)null) { _cawSound = FirstAudible(val.m_idleEffect, "Hugin's idle effect", list) ?? FirstAudible(val.m_despawnEffect, "Hugin's departure effect", list, nameMustMatch: true); } if ((Object)(object)_cawSound == (Object)null) { ZSFX[] array = Resources.FindObjectsOfTypeAll<ZSFX>(); foreach (ZSFX val3 in array) { string text = ((Object)val3).name.ToLowerInvariant(); if ((text.Contains("raven") || text.Contains("crow")) && HasClips(val3)) { Scene scene = ((Component)val3).gameObject.scene; if (!((Scene)(ref scene)).IsValid()) { _cawSound = ((Component)val3).gameObject; list.Add("loaded sound '" + ((Object)val3).name + "'"); break; } } } } if ((Object)(object)_cawSound != (Object)null) { Plugin.Log.LogInfo((object)("The raven's caw is '" + ((Object)_cawSound).name + "' (" + string.Join("; ", list) + ").")); } else { Plugin.Log.LogWarning((object)("Couldn't find a bird call for the raven, so it stays quiet. Checked: " + string.Join("; ", list) + ".")); } return _cawSound; } private static GameObject FirstAudible(EffectList effects, string source, List<string> tried, bool nameMustMatch = false) { if (effects?.m_effectPrefabs == null || effects.m_effectPrefabs.Length == 0) { tried.Add(source + ": empty"); return null; } EffectData[] effectPrefabs = effects.m_effectPrefabs; foreach (EffectData val in effectPrefabs) { if (!((Object)(object)val?.m_prefab == (Object)null)) { ZSFX componentInChildren = val.m_prefab.GetComponentInChildren<ZSFX>(true); string text = ((Object)val.m_prefab).name.ToLowerInvariant(); bool flag = (Object)(object)componentInChildren != (Object)null && HasClips(componentInChildren) && (!nameMustMatch || text.Contains("raven") || text.Contains("crow") || text.Contains("caw")); tried.Add(source + ": '" + ((Object)val.m_prefab).name + "'" + (flag ? "" : " (no bird call)")); if (flag) { return ((Component)componentInChildren).gameObject; } } } return null; } private static bool HasClips(ZSFX z) { if (z.m_audioClips != null) { return z.m_audioClips.Length != 0; } return false; } private void MeasureTop() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) _nextTopCheck = Time.time + 2f; float num = float.MinValue; Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren<Collider>(); foreach (Collider val in componentsInChildren) { if (val.enabled && !val.isTrigger) { float num2 = num; Bounds bounds = val.bounds; num = Mathf.Max(num2, ((Bounds)(ref bounds)).max.y); } } if (num > float.MinValue) { _top = Mathf.Clamp(num - ((Component)this).transform.position.y, 0.3f, 3f); } } } internal static class Remarks { private enum Situation { None, Danger, BossDanger, Arrived } private static readonly string[] General = new string[25] { "Your belongings are where you left them. So, presumably, is whatever killed you.", "The raven is guarding your things. It is not guarding your dignity.", "Odin has seen worse deaths. Not many, but some.", "Valhalla isn't taking you yet. Something about paperwork.", "Death is a setback, not a career.", "Odin sent his best raven. The other one was busy.", "Somewhere a skald is already writing this one down. It isn't flattering.", "The corpse run. Oldest tradition in Valheim.", "You came back. Your trousers didn't.", "Odin would like to remind you that armour works better when worn.", "Naked, cold and embarrassed. A classic.", "Hugin keeps the watch. Munin keeps count. It's a big number.", "Your grave isn't going anywhere. Neither is your reputation.", "Fetch your gear. Odin will pretend he didn't see.", "Everything you owned, in one tidy pile. Efficient, in a way.", "The raven asks that you hurry. It has other graves to sit on.", "Run, don't walk. Actually, walk. Running is how this started.", "Go and get your things. Try to stay alive while you do it.", "Odin isn't angry. Odin is disappointed.", "The raven has been told not to peck at anything valuable. It's trying.", "Dying is easy. Getting your stuff back is the real test.", "A warrior's death. Well, a death.", "Eat first. Then go. Odin insists.", "The gods were watching. Some of them laughed.", "Odin keeps a list of how his warriors die. You're on it. Again." }; private static readonly string[] Danger = new string[10] { "Your belongings are where you left them. So, presumably, is whatever killed you.", "Company at the grave. Bring friends, or at least a better plan.", "Something's still at the grave. It may be guarding it. It may be waiting for seconds.", "The raven reports visitors. They don't look like mourners.", "Don't charge in naked. That's how you get two graves.", "There's a crowd at your grave. Very touching. Very dangerous.", "They're still there. They have nothing better to do. You do: survive.", "Odin suggests the long way round, a short fight, or a great many arrows.", "Hostiles at the grave. The raven is keeping its distance. So should you, for now.", "Lure them away, then dash in. Odin has seen it work. Once." }; private static readonly string[] BossDanger = new string[3] { "The thing that killed you is sitting next to your stuff. Plan accordingly.", "A boss is minding your grave. It doesn't take tips.", "Odin admires your ambition. Not your timing." }; private static readonly string[] Clear = new string[5] { "All quiet at the grave. Now's your chance.", "Nothing hostile near the grave. The raven has the place to itself.", "The coast is clear. Odin suggests you go before it isn't.", "Whatever got you has wandered off. Probably bragging.", "No one's at the grave but the raven. Go." }; private static readonly string[] Arrived = new string[5] { "You're standing on it. Press E, not your luck.", "This is the one. The raven would point, but it has wings.", "Here lies you. Briefly.", "Grab it all and go. Mourning is for later.", "Found it. Odin knew you would. Eventually." }; private static readonly string[] Several = new string[6] { "{n} graves. Odin is starting to think you're doing this on purpose.", "Another grave? The raven is going to want a raise.", "{n} graves. You're not dying, you're planting.", "Start with the newest. That's where the good stuff is.", "At this rate Odin will need more ravens.", "{n} graves and counting. Munin has started a tally on the wall." }; private static readonly string[] Floating = new string[2] { "Your grave is floating. Bring a boat, or a very good swimmer.", "Your belongings are bobbing about. Odin finds this funny." }; private static readonly string[] Heavy = new string[3] { "You won't carry all of that. Bring a mead of strength, or a cart.", "More than you can carry. Two trips, or Megingjord.", "Odin admires a full pack. Your legs won't." }; private static readonly string[] Fresh = new string[2] { "Still warm. Odin is sorry for your loss. Your loss of everything.", "That was quick. The raven barely had time to land." }; private static readonly string[] Old = new string[2] { "This one's been waiting a while. The raven has started calling it home.", "An old grave. The raven has named every stone in it." }; private static readonly string[] None = new string[4] { "No graves. Odin is almost proud.", "The raven has nothing to guard. It's sulking.", "Nothing to report. Keep it that way.", "Not a single grave. Are you even trying?" }; private static readonly Dictionary<string, string[]> ByHitType = new Dictionary<string, string[]> { ["Fall"] = new string[4] { "The ground was closer than it looked.", "Gravity: still undefeated.", "Next time, try going down the slow way.", "You fell. The mountain didn't." }, ["Drowning"] = new string[3] { "Vikings sail on the water. Not under it.", "Swimming lessons. Odin is serious.", "The sea gives back your tombstone. Not your pride." }, ["Water"] = new string[2] { "Vikings sail on the water. Not under it.", "The water took you. It's not giving back your boots." }, ["Burning"] = new string[3] { "You were warm, briefly.", "Fire is a tool. You were the kindling.", "Stop, drop and roll. Odin should have mentioned it." }, ["CinderFire"] = new string[2] { "You were warm, briefly.", "Cinders fall up there. Now you know." }, ["AshlandsLava"] = new string[2] { "Lava is not a hot bath. Odin shouldn't have to say this.", "The floor was lava. Literally." }, ["AshlandsOcean"] = new string[2] { "The Ashlands sea boils. Now you know.", "Poached viking. Not on Odin's menu." }, ["Freezing"] = new string[3] { "Wear the wolf. Don't just look at it.", "Cold took you. Next time: a cape, a fire, and mead.", "Frozen solid. Odin could have used you as a table." }, ["Poisoned"] = new string[2] { "Poison: the slow way to lose an argument.", "Bring an antidote. Or a smaller appetite for swamps." }, ["Tree"] = new string[3] { "A tree fell in the forest. You were there.", "Timber is a warning, not a greeting.", "The tree is fine, thank you for asking." }, ["Self"] = new string[2] { "Killed by your own hand. Odin has questions.", "You beat yourself. Odin isn't sure who to congratulate." }, ["Structural"] = new string[2] { "Your own roof fell on you. Build better, or live outside.", "Support beams. Look them up." }, ["Smoke"] = new string[2] { "Open a window. You can build those.", "A chimney would have helped." }, ["EdgeOfWorld"] = new string[2] { "You found the edge of the world. It found you back.", "There's nothing past there. You checked." }, ["Cart"] = new string[1] { "Run over by a cart. The cart has no regrets." }, ["Boat"] = new string[1] { "Hit by a boat. Odin has no idea how. He's impressed." }, ["Stalagtite"] = new string[1] { "Look up in caves. That's where the pointy bits live." }, ["Turret"] = new string[1] { "Killed by a turret. At least it was a professional." }, ["Catapult"] = new string[1] { "Killed by a catapult. Odin calls that being on the wrong end of engineering." }, ["Incinerator"] = new string[1] { "The obliterator was for junk. Odin will say no more." }, ["DrawBridge"] = new string[1] { "It goes up, it comes down. You were underneath." }, ["Impact"] = new string[1] { "Something hit you very hard. Physics wins again." }, ["Undefined"] = new string[1] { "Died of mysterious causes. The raven saw nothing. Suspicious." }, ["PlayerHit"] = new string[1] { "Killed by another viking. Odin won't get involved in family matters." } }; private static readonly string[] Enemy = new string[4] { "Killed by {killer}. It will remember you fondly.", "{killer} won this round. Rounds aren't matches.", "Odin has added a line to {killer}'s file.", "{killer} sends its regards." }; private static readonly string[] BossKiller = new string[2] { "{killer} won. Bosses do that. Come back with better food.", "{killer} one, you nil. Odin's betting on the rematch." }; private static readonly Dictionary<string, string> ByKiller = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["Neck"] = "Killed by a Neck. Odin is laughing. Quietly.", ["Boar"] = "Boar one, viking nil. Bacon gets the last word.", ["Greyling"] = "Killed by a Greyling. The raven is pretending it didn't see.", ["Greydwarf"] = "Killed by a Greydwarf. Odin is choosing not to comment.", ["Troll"] = "Trolls swing logs. You were standing where the log went.", ["Skeleton"] = "The skeleton won. It wasn't even using its muscles.", ["Ghost"] = "Killed by a ghost. Now you know how it feels.", ["Draugr"] = "The draugr have one more friend now. Don't join them properly.", ["Leech"] = "Leeches. Drink less of the swamp.", ["Blob"] = "Killed by a blob. Odin told you not to lick it.", ["Abomination"] = "That wasn't a tree. Odin did try to tell you.", ["Wraith"] = "Wraiths come out at night. So, it turns out, did you.", ["Wolf"] = "Not a good boy after all.", ["Fenring"] = "Fenring: fast, at night, and not sorry.", ["Ulv"] = "Ulvs hunt in packs. You hunted alone.", ["Hatchling"] = "Killed by a drake. It's already telling the others.", ["StoneGolem"] = "Punched by a mountain. It happens.", ["Goblin"] = "The Fulings are celebrating. Loudly.", ["GoblinBrute"] = "The big one. There's always a big one.", ["Deathsquito"] = "A deathsquito. Even Odin flinches.", ["Lox"] = "Gentle giant, they said.", ["Serpent"] = "The serpent. The boat was smaller than it looked.", ["Seeker"] = "Too many legs won.", ["SeekerBrute"] = "The big seeker. It skipped no meals, and neither did it skip you.", ["Tick"] = "Killed by a tick. A tick.", ["Gjall"] = "Gjall. It floats, it spits, it wins.", ["Dverger"] = "You annoyed the dvergr. They're very good at being annoyed.", ["Charred"] = "The Charred. Burnt, bony, and better at this than you.", ["Morgen"] = "A Morgen. Odin tries not to think about those.", ["Asksvin"] = "Asksvin. Fast, toothy, and unbothered by your shield.", ["FallenValkyrie"] = "A fallen valkyrie. Odin knew her. She was always like that.", ["Volture"] = "A volture. It's eyeing your grave too." }; private static readonly string[] Words = new string[11] { "No", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten" }; internal static string Pick(List<GraveInfo> graves, ThreatReport threat, Player player, int seed) { //IL_0078: 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) seed &= 0x7FFFFFFF; if (graves.Count == 0) { return Choose(None, seed, null, 0); } GraveInfo graveInfo = graves[0]; string killer = Killer(graveInfo.Record); if (threat != null && threat.Known && threat.Total > 0) { return Choose(threat.Boss ? BossDanger : Danger, seed, killer, graves.Count); } if ((Object)(object)player != (Object)null && Utils.DistanceXZ(((Component)player).transform.position, graveInfo.Position) < 6f) { return Choose(Arrived, seed, killer, graves.Count); } List<string[]> list = new List<string[]> { General }; if (threat != null && threat.Known) { list.Add(Clear); } if (graveInfo.Record != null) { GraveRecord record = graveInfo.Record; string[] value2; if (!string.IsNullOrEmpty(record.KillerPrefab) && record.KillerPrefab != "Player") { if (ByKiller.TryGetValue(BaseName(record.KillerPrefab), out var value)) { list.Add(new string[1] { value }); } list.Add(record.KillerIsBoss ? BossKiller : Enemy); } else if (ByHitType.TryGetValue(record.HitType ?? "", out value2)) { list.Add(value2); } } if (graves.Count > 1) { list.Add(Several); } if (graveInfo.InWater) { list.Add(Floating); } if ((Object)(object)player != (Object)null && graveInfo.Live && graveInfo.Contents != null && ((Humanoid)player).GetInventory().GetTotalWeight() + graveInfo.Contents.Weight > player.GetMaxCarryWeight()) { list.Add(Heavy); } if ((Object)(object)ZNet.instance != (Object)null && graveInfo.Ticks > 0) { double num = ZNet.instance.GetTimeSeconds() - (double)graveInfo.Ticks / 10000000.0; if (num < 120.0) { list.Add(Fresh); } else if (num > 7200.0) { list.Add(Old); } } return Choose((list.Count > 1 && seed % 4 != 0) ? list[1 + seed / 4 % (list.Count - 1)] : list[seed / 4 % list.Count], seed / 16, killer, graves.Count); } private static string Choose(string[] pool, int seed, string killer, int count) { string text = pool[seed % pool.Length]; text = text.Replace("{killer}", killer ?? "something").Replace("{n}", Number(count)); return char.ToUpperInvariant(text[0]) + text.Substring(1); } private static string Killer(GraveRecord r) { if (r == null || string.IsNullOrEmpty(r.Killer)) { return null; } if (!r.KillerIsBoss && !(r.KillerPrefab == "Player")) { return "the " + r.Killer; } return r.Killer; } private static string BaseName(string prefab) { if (ByKiller.ContainsKey(prefab)) { return prefab; } int num = prefab.IndexOf('_'); if (num <= 0) { return prefab; } return prefab.Substring(0, num); } private static string Number(int n) { if (n < 0 || n >= Words.Length) { return n.ToString(); } return Words[n]; } } internal sealed class ThreatReport { public bool Known; public readonly List<KeyValuePair<string, int>> Hostiles = new List<KeyValuePair<string, int>>(); public int Total; public bool Boss; public string FirstPrefab = ""; } internal static class ThreatRadar { private static string _ignoredSource; private static readonly HashSet<string> IgnoredNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, int> Counts = new Dictionary<string, int>(); internal static ThreatReport Around(Vector3 grave, Player player) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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) ThreatReport threatReport = new ThreatReport(); if ((Object)(object)player == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || !ZoneSystem.instance.IsZoneLoaded(grave)) { return threatReport; } threatReport.Known = true; float value = Plugin.ThreatRadius.Value; float num = value * value; Counts.Clear(); foreach (Character allCharacter in Character.GetAllCharacters()) { if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)player || allCharacter.IsDead()) { continue; } Vector3 val = ((Component)allCharacter).transform.position - grave; if (!(((Vector3)(ref val)).sqrMagnitude > num) && BaseAI.IsEnemy((Character)(object)player, allCharacter) && !IsIgnored(allCharacter)) { string key = ((Localization.instance != null) ? Localization.instance.Localize(allCharacter.m_name) : allCharacter.m_name); Counts.TryGetValue(key, out var value2); Counts[key] = value2 + 1; if (threatReport.Total++ == 0) { threatReport.FirstPrefab = Utils.GetPrefabName(((Component)allCharacter).gameObject); } if (allCharacter.IsBoss()) { threatReport.Boss = true; threatReport.FirstPrefab = Utils.GetPrefabName(((Component)allCharacter).gameObject); } } } threatReport.Hostiles.AddRange(Counts); threatReport.Hostiles.Sort((KeyValuePair<string, int> a, KeyValuePair<string, int> b) => (b.Value == a.Value) ? string.CompareOrdinal(a.Key, b.Key) : b.Value.CompareTo(a.Value)); return threatReport; } private static bool IsIgnored(Character c) { RefreshIgnoredNames(); if (IgnoredNames.Count == 0) { return false; } if (IgnoredNames.Contains(Utils.GetPrefabName(((Component)c).gameObject))) { return true; } if (Localization.instance != null) { return IgnoredNames.Contains(Localization.instance.Localize(c.m_name)); } return false; } private static void RefreshIgnoredNames() { string text = Plugin.IgnoredCreatures.Value ?? string.Empty; if (text == _ignoredSource) { return; } _ignoredSource = text; IgnoredNames.Clear(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length > 0) { IgnoredNames.Add(text2); } } } } internal static class BuildInfo { public const string Version = "0.1.3"; } }