Decompiled source of BetterTombstoneHints v1.0.0

BetterTombstoneHints.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BetterTombstoneHints.Features;
using BetterTombstoneHints.Infrastructure;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BetterTombstoneHints")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+808f97ed19ef8b65c5b49ca9c6bfee53c795e7ec")]
[assembly: AssemblyProduct("BetterTombstoneHints")]
[assembly: AssemblyTitle("BetterTombstoneHints")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[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 BetterTombstoneHints
{
	[BepInPlugin("JevMods.BetterTombstoneHints", "BetterTombstoneHints", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "JevMods.BetterTombstoneHints";

		public const string PluginName = "BetterTombstoneHints";

		private IFeature[] _features;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			GameEvents events = (GameEvents.Instance = new GameEvents());
			_features = CreateFeatures(events);
			IFeature[] features = _features;
			for (int i = 0; i < features.Length; i++)
			{
				features[i].Enable();
			}
			_harmony = new Harmony("JevMods.BetterTombstoneHints");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if (_features != null)
			{
				IFeature[] features = _features;
				for (int i = 0; i < features.Length; i++)
				{
					features[i].Disable();
				}
			}
			GameEvents.Instance = null;
		}

		private static IFeature[] CreateFeatures(IGameEvents events)
		{
			TombstoneNotes notes = new TombstoneNotes();
			NoteRenderer renderer = new NoteRenderer(new Translator(new EmbeddedTranslations()), new GameLanguage());
			return new IFeature[3]
			{
				new NoteWritingFeature(events, new LocalPlayerDeath(), notes),
				new NoteDisplayFeature(events, notes, renderer),
				new NameplateFeature(events, new TombstoneNameplates(notes, renderer))
			};
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "1.0.0";
	}
}
namespace BetterTombstoneHints.Patches
{
	internal static class TombStonePatches
	{
		[HarmonyPatch(typeof(TombStone), "Setup")]
		private static class Setup
		{
			private static void Postfix(TombStone __instance)
			{
				GameEvents.Instance?.RaiseTombstoneCreated(__instance);
			}
		}

		[HarmonyPatch(typeof(TombStone), "Start")]
		private static class Start
		{
			private static void Postfix(TombStone __instance)
			{
				GameEvents.Instance?.RaiseTombstoneStarted(__instance);
			}
		}

		[HarmonyPatch(typeof(TombStone), "GetHoverText")]
		private static class GetHoverText
		{
			private static void Postfix(TombStone __instance, ref string __result)
			{
				GameEvents.Instance?.RaiseHoverTextBuilt(__instance, ref __result);
			}
		}
	}
}
namespace BetterTombstoneHints.Infrastructure
{
	public sealed class EmbeddedTranslations : ITranslationSource
	{
		public string Read(string language)
		{
			using Stream stream = typeof(EmbeddedTranslations).Assembly.GetManifestResourceStream(language + ".txt");
			if (stream == null)
			{
				return null;
			}
			using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
			return streamReader.ReadToEnd();
		}
	}
	public sealed class GameEvents : IGameEvents
	{
		internal static GameEvents Instance { get; set; }

		public event Action<TombStone> TombstoneCreated;

		public event Action<TombStone> TombstoneStarted;

		public event HoverTextBuiltHandler HoverTextBuilt;

		internal void RaiseTombstoneCreated(TombStone tombstone)
		{
			this.TombstoneCreated?.Invoke(tombstone);
		}

		internal void RaiseTombstoneStarted(TombStone tombstone)
		{
			this.TombstoneStarted?.Invoke(tombstone);
		}

		internal void RaiseHoverTextBuilt(TombStone tombstone, ref string text)
		{
			this.HoverTextBuilt?.Invoke(tombstone, ref text);
		}
	}
	public sealed class GameLanguage : IGameLanguage
	{
		private const string DefaultLanguage = "English";

		public string Current
		{
			get
			{
				Localization instance = Localization.instance;
				return ((instance != null) ? instance.GetSelectedLanguage() : null) ?? "English";
			}
		}

		public string Localize(string text, params string[] words)
		{
			if (Localization.instance == null)
			{
				return text;
			}
			return Localization.instance.Localize(text, words);
		}
	}
	public sealed class LocalPlayerDeath : IDeathSource
	{
		public DeathNote Latest()
		{
			//IL_0040: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)EnvMan.instance == (Object)null)
			{
				return null;
			}
			int currentDay = EnvMan.instance.GetCurrentDay();
			HitData lastHit = ((Character)localPlayer).m_lastHit;
			if (lastHit == null)
			{
				return new DeathNote("died", currentDay);
			}
			HitType hitType = lastHit.m_hitType;
			if (hitType - 1 <= 1)
			{
				return SlainBy(lastHit.GetAttacker(), currentDay);
			}
			return new DeathNote(CauseKeys.ForHitType(((object)Unsafe.As<HitType, HitType>(ref lastHit.m_hitType)/*cast due to .constrained prefix*/).ToString()), currentDay);
		}

		private static DeathNote SlainBy(Character attacker, int day)
		{
			if ((Object)(object)attacker == (Object)null)
			{
				return new DeathNote("slain", day);
			}
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if (val != null)
			{
				return new DeathNote("slain_by", val.GetPlayerName(), day);
			}
			return new DeathNote(attacker.IsBoss() ? "slain_by" : "slain_by_creature", attacker.m_name, day);
		}
	}
	public sealed class TombstoneNameplate : MonoBehaviour
	{
		private const float RefreshSeconds = 0.5f;

		private TombStone _tombstone;

		private ITombstoneNotes _notes;

		private INoteRenderer _renderer;

		private TextWrappingModes _wrappingMode;

		private float _nextRefresh;

		public void Initialize(TombStone tombstone, ITombstoneNotes notes, INoteRenderer renderer)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			_tombstone = tombstone;
			_notes = notes;
			_renderer = renderer;
			_wrappingMode = tombstone.m_worldText.textWrappingMode;
			tombstone.m_worldText.textWrappingMode = (TextWrappingModes)0;
		}

		private void Update()
		{
			if (!(Time.time < _nextRefresh))
			{
				_nextRefresh = Time.time + 0.5f;
				Show(BuildText());
			}
		}

		private void OnDestroy()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_tombstone != (Object)null)
			{
				_tombstone.m_worldText.textWrappingMode = _wrappingMode;
				Show(_tombstone.GetOwnerName());
			}
		}

		private string BuildText()
		{
			string ownerName = _tombstone.GetOwnerName();
			DeathNote deathNote = _notes.Read(_tombstone);
			if (deathNote != null)
			{
				return ownerName + "\n" + _renderer.Render(deathNote);
			}
			return ownerName;
		}

		private void Show(string text)
		{
			if (_tombstone.m_worldText.text != text)
			{
				_tombstone.m_worldText.text = text;
			}
		}
	}
	public sealed class TombstoneNameplates : INameplates
	{
		private readonly ITombstoneNotes _notes;

		private readonly INoteRenderer _renderer;

		private readonly List<TombstoneNameplate> _attached = new List<TombstoneNameplate>();

		public TombstoneNameplates(ITombstoneNotes notes, INoteRenderer renderer)
		{
			_notes = notes;
			_renderer = renderer;
		}

		public void Attach(TombStone tombstone)
		{
			if (!((Object)(object)((Component)tombstone).GetComponent<TombstoneNameplate>() != (Object)null))
			{
				_attached.RemoveAll((TombstoneNameplate nameplate) => (Object)(object)nameplate == (Object)null);
				TombstoneNameplate tombstoneNameplate = ((Component)tombstone).gameObject.AddComponent<TombstoneNameplate>();
				tombstoneNameplate.Initialize(tombstone, _notes, _renderer);
				_attached.Add(tombstoneNameplate);
			}
		}

		public void DetachAll()
		{
			foreach (TombstoneNameplate item in _attached)
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.Destroy((Object)(object)item);
				}
			}
			_attached.Clear();
		}
	}
	public sealed class TombstoneNotes : ITombstoneNotes
	{
		private static readonly int CauseKey = StringExtensionMethods.GetStableHashCode("JevMods_Cause");

		private static readonly int KillerKey = StringExtensionMethods.GetStableHashCode("JevMods_Killer");

		private static readonly int DayKey = StringExtensionMethods.GetStableHashCode("JevMods_Day");

		public void Write(TombStone tombstone, DeathNote note)
		{
			if (tombstone.m_nview.IsValid())
			{
				ZDO zDO = tombstone.m_nview.GetZDO();
				zDO.Set(CauseKey, note.Cause);
				zDO.Set(KillerKey, note.Killer);
				zDO.Set(DayKey, note.Day, false);
			}
		}

		public DeathNote Read(TombStone tombstone)
		{
			if (!tombstone.m_nview.IsValid())
			{
				return null;
			}
			ZDO zDO = tombstone.m_nview.GetZDO();
			string text = zDO.GetString(CauseKey, "");
			if (text.Length == 0)
			{
				return null;
			}
			return new DeathNote(text, zDO.GetString(KillerKey, ""), zDO.GetInt(DayKey, 0));
		}
	}
}
namespace BetterTombstoneHints.Features
{
	public static class CauseKeys
	{
		public const string Died = "died";

		public const string Slain = "slain";

		public const string SlainBy = "slain_by";

		public const string SlainByCreature = "slain_by_creature";

		public static readonly IReadOnlyDictionary<string, string> ByHitType = new Dictionary<string, string>
		{
			{ "Fall", "fall" },
			{ "Drowning", "drowning" },
			{ "Burning", "burning" },
			{ "Freezing", "freezing" },
			{ "Poisoned", "poisoned" },
			{ "Water", "water" },
			{ "Smoke", "smoke" },
			{ "EdgeOfWorld", "edge_of_world" },
			{ "Impact", "impact" },
			{ "Cart", "cart" },
			{ "Tree", "tree" },
			{ "Self", "self" },
			{ "Structural", "structural" },
			{ "Turret", "turret" },
			{ "Boat", "boat" },
			{ "Stalagtite", "stalactite" },
			{ "Catapult", "catapult" },
			{ "CinderFire", "cinder_fire" },
			{ "AshlandsOcean", "ashlands_ocean" },
			{ "AshlandsLava", "ashlands_lava" },
			{ "Incinerator", "incinerator" },
			{ "DrawBridge", "drawbridge" }
		};

		public static string ForHitType(string hitType)
		{
			if (!ByHitType.TryGetValue(hitType, out var value))
			{
				return "died";
			}
			return value;
		}
	}
	public sealed class DeathNote
	{
		public string Cause { get; }

		public string Killer { get; }

		public int Day { get; }

		public DeathNote(string cause, int day)
			: this(cause, string.Empty, day)
		{
		}

		public DeathNote(string cause, string killer, int day)
		{
			Cause = cause;
			Killer = killer;
			Day = day;
		}
	}
	public static class HoverNote
	{
		public static string Insert(string hoverText, string note)
		{
			if (hoverText.Length == 0 || note.Length == 0)
			{
				return hoverText;
			}
			int num = hoverText.IndexOf('\n');
			if (num >= 0)
			{
				return hoverText.Insert(num, "\n" + note);
			}
			return hoverText + "\n" + note;
		}
	}
	public delegate void HoverTextBuiltHandler(TombStone tombstone, ref string text);
	public interface IDeathSource
	{
		DeathNote Latest();
	}
	public interface IFeature
	{
		void Enable();

		void Disable();
	}
	public interface IGameEvents
	{
		event Action<TombStone> TombstoneCreated;

		event Action<TombStone> TombstoneStarted;

		event HoverTextBuiltHandler HoverTextBuilt;
	}
	public interface IGameLanguage
	{
		string Current { get; }

		string Localize(string text, params string[] words);
	}
	public interface INameplates
	{
		void Attach(TombStone tombstone);

		void DetachAll();
	}
	public interface INoteRenderer
	{
		string Render(DeathNote note);
	}
	public interface ITombstoneNotes
	{
		void Write(TombStone tombstone, DeathNote note);

		DeathNote Read(TombStone tombstone);
	}
	public interface ITranslationSource
	{
		string Read(string language);
	}
	public sealed class NameplateFeature : IFeature
	{
		private readonly IGameEvents _events;

		private readonly INameplates _nameplates;

		public NameplateFeature(IGameEvents events, INameplates nameplates)
		{
			_events = events;
			_nameplates = nameplates;
		}

		public void Enable()
		{
			_events.TombstoneStarted += _nameplates.Attach;
		}

		public void Disable()
		{
			_events.TombstoneStarted -= _nameplates.Attach;
			_nameplates.DetachAll();
		}
	}
	public sealed class NoteDisplayFeature : IFeature
	{
		private readonly IGameEvents _events;

		private readonly ITombstoneNotes _notes;

		private readonly INoteRenderer _renderer;

		public NoteDisplayFeature(IGameEvents events, ITombstoneNotes notes, INoteRenderer renderer)
		{
			_events = events;
			_notes = notes;
			_renderer = renderer;
		}

		public void Enable()
		{
			_events.HoverTextBuilt += OnHoverTextBuilt;
		}

		public void Disable()
		{
			_events.HoverTextBuilt -= OnHoverTextBuilt;
		}

		private void OnHoverTextBuilt(TombStone tombstone, ref string text)
		{
			DeathNote deathNote = ((text.Length == 0) ? null : _notes.Read(tombstone));
			if (deathNote != null)
			{
				text = HoverNote.Insert(text, _renderer.Render(deathNote));
			}
		}
	}
	public sealed class NoteRenderer : INoteRenderer
	{
		private const string DayToken = "$msg_newday";

		private const string Separator = ", ";

		private readonly Translator _translator;

		private readonly IGameLanguage _language;

		public NoteRenderer(Translator translator, IGameLanguage language)
		{
			_translator = translator;
			_language = language;
		}

		public string Render(DeathNote note)
		{
			string text = _language.Localize("$msg_newday", note.Day.ToString(CultureInfo.InvariantCulture));
			return RenderCause(note) + ", " + text;
		}

		private static string Article(string name)
		{
			if (name.Length <= 0 || "AEIOUaeiou".IndexOf(name[0]) < 0)
			{
				return "a";
			}
			return "an";
		}

		private string RenderCause(DeathNote note)
		{
			string text = _language.Localize(note.Killer);
			return _translator.Translate(_language.Current, note.Cause).Replace("{a}", Article(text)).Replace("$1", text);
		}
	}
	public sealed class NoteWritingFeature : IFeature
	{
		private readonly IGameEvents _events;

		private readonly IDeathSource _deaths;

		private readonly ITombstoneNotes _notes;

		public NoteWritingFeature(IGameEvents events, IDeathSource deaths, ITombstoneNotes notes)
		{
			_events = events;
			_deaths = deaths;
			_notes = notes;
		}

		public void Enable()
		{
			_events.TombstoneCreated += OnTombstoneCreated;
		}

		public void Disable()
		{
			_events.TombstoneCreated -= OnTombstoneCreated;
		}

		private void OnTombstoneCreated(TombStone tombstone)
		{
			DeathNote deathNote = _deaths.Latest();
			if (deathNote != null)
			{
				_notes.Write(tombstone, deathNote);
			}
		}
	}
	public sealed class Translator
	{
		private const string FallbackLanguage = "English";

		private readonly ITranslationSource _source;

		private readonly Dictionary<string, Dictionary<string, string>> _tables = new Dictionary<string, Dictionary<string, string>>();

		public Translator(ITranslationSource source)
		{
			_source = source;
		}

		public string Translate(string language, string key)
		{
			if (Load(language).TryGetValue(key, out var value) || Load("English").TryGetValue(key, out value))
			{
				return value;
			}
			return key;
		}

		private static Dictionary<string, string> Parse(string text)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			if (text == null)
			{
				return dictionary;
			}
			string[] array = text.Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				int num = text2.IndexOf('=');
				if (num > 0)
				{
					string key = text2.Substring(0, num).Trim();
					dictionary[key] = text2.Substring(num + 1).Trim();
				}
			}
			return dictionary;
		}

		private Dictionary<string, string> Load(string language)
		{
			if (!_tables.TryGetValue(language, out var value))
			{
				value = Parse(_source.Read(language));
				_tables[language] = value;
			}
			return value;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}