Decompiled source of Odins Weapon Memories v0.1.1

plugins/OdinsWeaponMemories.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
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 UnityEngine;

[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("OdinsWeaponMemories")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+9e3cfe7ec14bbc94408ce317c554af6a14d4fa95")]
[assembly: AssemblyProduct("OdinsWeaponMemories")]
[assembly: AssemblyTitle("OdinsWeaponMemories")]
[assembly: AssemblyVersion("0.1.1.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 OdinsWeaponMemories
{
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class Commands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			public static Func<KeyValuePair<string, string>, bool> <>9__1_0;

			public static Func<string, bool> <>9__1_1;

			internal void <Postfix>b__0_0(ConsoleEventArgs args)
			{
				string[] array = Report();
				foreach (string text in array)
				{
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString(text);
					}
					Plugin.Log.LogInfo((object)text);
				}
			}

			internal bool <Report>b__1_0(KeyValuePair<string, string> kv)
			{
				return WeaponMemory.IsOurKey(kv.Key);
			}

			internal bool <Report>b__1_1(string o)
			{
				return o != "hevyh2o.valheim.odinsweaponmemories";
			}
		}

		private static void Postfix()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					string[] array = Report();
					foreach (string text in array)
					{
						Terminal context = args.Context;
						if (context != null)
						{
							context.AddString(text);
						}
						Plugin.Log.LogInfo((object)text);
					}
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("weaponmemory", "Odin's Weapon Memories: show what the mod sees on the weapon in your hands.", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static string[] Report()
		{
			List<string> list = new List<string>();
			list.Add(string.Format("{0} {1}, enabled: {2}, style: {3}", "Odin's Weapon Memories", "0.1.1", Plugin.Enabled.Value, Plugin.NameStyle.Value));
			List<string> list2 = list;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return list2.Concat(new string[1] { "No player loaded." }).ToArray();
			}
			ItemData val = ((Humanoid)localPlayer).GetCurrentWeapon();
			if (val == null || !((Humanoid)localPlayer).GetInventory().ContainsItem(val))
			{
				val = ((Humanoid)localPlayer).LeftItem;
			}
			if (val == null || !((Humanoid)localPlayer).GetInventory().ContainsItem(val))
			{
				return list2.Concat(new string[1] { "Hold a weapon or shield first." }).ToArray();
			}
			WeaponKind weaponKind = WeaponKinds.Of(val);
			list2.Add($"Item: {Localization.instance.Localize(val.m_shared.m_name)} (quality {val.m_quality}), kind {weaponKind}, " + "switched " + (WeaponKinds.IsEnabled(weaponKind) ? "on" : "OFF") + " in [Weapons]");
			list2.Add("Vanilla crafter: " + (string.IsNullOrEmpty(val.m_crafterName) ? "(none)" : val.m_crafterName));
			List<KeyValuePair<string, string>> list3 = val.m_customData?.Where((KeyValuePair<string, string> kv) => WeaponMemory.IsOurKey(kv.Key)).ToList();
			if (list3 == null || list3.Count == 0)
			{
				list2.Add("No memories stored on this item.");
			}
			else
			{
				foreach (KeyValuePair<string, string> item in list3)
				{
					list2.Add("  " + item.Key + " = " + ((item.Value.Length > 200) ? (item.Value.Substring(0, 200) + "...") : item.Value));
				}
				WeaponMemory weaponMemory = WeaponMemory.Read(val);
				list2.Add($"Maker: {weaponMemory.MakerName}, namer: {weaponMemory.NamerName}, kills: {weaponMemory.All.Kills} (namer's {weaponMemory.Namer.Kills})");
			}
			list2.Add("Shown as: " + (Names.Display(val) ?? "(its plain name)"));
			MethodBase[] array = new MethodBase[3]
			{
				AccessTools.Method(typeof(ItemData), "GetTooltip", new Type[6]
				{
					typeof(ItemData),
					typeof(int),
					typeof(bool),
					typeof(float),
					typeof(int),
					typeof(bool)
				}, (Type[])null),
				AccessTools.Method(typeof(InventoryGrid), "CreateItemTooltip", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(ItemDrop), "GetHoverText", (Type[])null, (Type[])null)
			};
			foreach (MethodBase methodBase in array)
			{
				Patches patchInfo = Harmony.GetPatchInfo(methodBase);
				List<string> list4 = ((patchInfo != null) ? patchInfo.Owners.Where((string o) => o != "hevyh2o.valheim.odinsweaponmemories").ToList() : null);
				if (list4 != null && list4.Count > 0)
				{
					list2.Add("Also patched by other mods: " + methodBase.DeclaringType.Name + "." + methodBase.Name + " <- " + string.Join(", ", list4));
				}
			}
			return list2.ToArray();
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
	internal static class CraftingPatch
	{
		internal struct State
		{
			public ItemData OldItem;

			public Dictionary<string, string> Memories;

			public long MakerId;

			public string MakerName;

			public HashSet<ItemData> ItemsBefore;
		}

		private static void Prefix(Player player, ItemData ___m_craftUpgradeItem, out State __state)
		{
			__state = default(State);
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			__state.ItemsBefore = new HashSet<ItemData>(((Humanoid)player).GetInventory().GetAllItems());
			if (___m_craftUpgradeItem != null)
			{
				__state.OldItem = ___m_craftUpgradeItem;
				__state.Memories = ___m_craftUpgradeItem.m_customData?.Where((KeyValuePair<string, string> kv) => WeaponMemory.IsOurKey(kv.Key)).ToDictionary((KeyValuePair<string, string> kv) => kv.Key, (KeyValuePair<string, string> kv) => kv.Value) ?? new Dictionary<string, string>();
				WeaponMemory weaponMemory = WeaponMemory.Read(___m_craftUpgradeItem);
				bool flag = weaponMemory != null && !string.IsNullOrEmpty(weaponMemory.MakerName);
				__state.MakerId = (flag ? weaponMemory.MakerId : ___m_craftUpgradeItem.m_crafterID);
				__state.MakerName = (flag ? weaponMemory.MakerName : ___m_craftUpgradeItem.m_crafterName);
			}
		}

		private static void Postfix(Player player, State __state)
		{
			if ((Object)(object)player == (Object)null || __state.ItemsBefore == null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			ItemData oldItem = __state.OldItem;
			if (oldItem != null)
			{
				if (inventory.ContainsItem(oldItem))
				{
					return;
				}
				ItemData itemAt = inventory.GetItemAt(oldItem.m_gridPos.x, oldItem.m_gridPos.y);
				if (itemAt == null || itemAt == oldItem || itemAt.m_shared.m_name != oldItem.m_shared.m_name || WeaponMemory.Has(itemAt) || (__state.Memories.Count == 0 && !WeaponMemory.CanRemember(itemAt)))
				{
					return;
				}
				if (itemAt.m_customData == null)
				{
					itemAt.m_customData = new Dictionary<string, string>();
				}
				foreach (KeyValuePair<string, string> memory in __state.Memories)
				{
					itemAt.m_customData[memory.Key] = memory.Value;
				}
				WeaponMemory.StampMaker(itemAt, __state.MakerId, __state.MakerName);
				if (Plugin.LogKills.Value)
				{
					Plugin.Log.LogInfo((object)$"Carried memories of {oldItem.m_shared.m_name} across the upgrade to quality {itemAt.m_quality}; engraved by {__state.MakerName}.");
				}
				return;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!__state.ItemsBefore.Contains(allItem) && WeaponMemory.CanRemember(allItem) && !string.IsNullOrEmpty(allItem.m_crafterName))
				{
					WeaponMemory.StampMaker(allItem, allItem.m_crafterID, allItem.m_crafterName);
					if (Plugin.LogKills.Value)
					{
						Plugin.Log.LogInfo((object)("Engraved " + allItem.m_crafterName + " on a new " + allItem.m_shared.m_name + "."));
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int),
		typeof(bool)
	})]
	internal static class TooltipPatch
	{
		private static void Postfix(ItemData item, bool crafting, ref string __result)
		{
			if (!crafting && item != null)
			{
				string text = Epithets.TooltipSection(item);
				if (text != null)
				{
					__result += text;
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "CreateItemTooltip")]
	internal static class InventoryTitlePatch
	{
		private static void Postfix(ItemData item, UITooltip tooltip, RectTransform ___m_tooltipAnchor)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			string text = Names.Display(item);
			if (text != null && (Object)(object)tooltip != (Object)null)
			{
				tooltip.Set(text, tooltip.m_text, ___m_tooltipAnchor, default(Vector2));
			}
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "GetHoverText")]
	internal static class GroundHoverTextPatch
	{
		private static void Postfix(ItemDrop __instance, ref string __result)
		{
			ItemData itemData = __instance.m_itemData;
			string text = Names.Display(itemData);
			if (text == null || string.IsNullOrEmpty(__result))
			{
				return;
			}
			string text2 = Localization.instance.Localize(itemData.m_shared.m_name);
			if (!__result.StartsWith(text2))
			{
				return;
			}
			int num = text2.Length;
			string text3 = "";
			if (num < __result.Length && __result[num] == '[')
			{
				int num2 = __result.IndexOf(']', num);
				if (num2 > 0)
				{
					text3 = " " + __result.Substring(num, num2 + 1 - num);
					num = num2 + 1;
				}
			}
			__result = text + text3 + __result.Substring(num).TrimStart(new char[1] { ' ' });
		}
	}
	[HarmonyPatch(typeof(ItemDrop), "GetHoverName")]
	internal static class GroundHoverNamePatch
	{
		private static void Postfix(ItemDrop __instance, ref string __result)
		{
			__result = Names.Display(__instance.m_itemData) ?? __result;
		}
	}
	internal enum NameStyle
	{
		Legend,
		Epithet
	}
	internal enum LaterBearers
	{
		KeepNamersName,
		EvolveWithNewBearer
	}
	internal static class Names
	{
		internal static string Display(ItemData item)
		{
			if (!Plugin.Enabled.Value || !WeaponMemory.CanRemember(item))
			{
				return null;
			}
			WeaponMemory weaponMemory = WeaponMemory.Read(item);
			if (weaponMemory != null)
			{
				return Announced(item, weaponMemory);
			}
			return null;
		}

		internal static string Announced(ItemData item, WeaponMemory memory)
		{
			if (Plugin.NameStyle.Value == NameStyle.Legend)
			{
				return Legend.For(item, memory);
			}
			string text = Epithets.Headline(memory.All);
			if (text != null)
			{
				return Localization.instance.Localize(item.m_shared.m_name) + ", " + text;
			}
			return null;
		}
	}
	internal static class Epithets
	{
		private struct Tier
		{
			public int Kills;

			public string Title;
		}

		internal static Func<string, string> Localize = (string text) => (Localization.instance == null) ? text : Localization.instance.Localize(text);

		private static List<Tier> Tiers()
		{
			List<Tier> list = new List<Tier>(3);
			Add(list, Plugin.Tier1Kills.Value, Plugin.Tier1Title.Value);
			Add(list, Plugin.Tier2Kills.Value, Plugin.Tier2Title.Value);
			Add(list, Plugin.Tier3Kills.Value, Plugin.Tier3Title.Value);
			list.Sort((Tier a, Tier b) => a.Kills.CompareTo(b.Kills));
			return list;
		}

		private static void Add(List<Tier> tiers, int kills, string title)
		{
			if (kills > 0 && !string.IsNullOrWhiteSpace(title))
			{
				tiers.Add(new Tier
				{
					Kills = kills,
					Title = title
				});
			}
		}

		internal static int TierFor(int kills)
		{
			return Tiers().Count((Tier t) => kills >= t.Kills);
		}

		internal static string Title(int tier, string creature)
		{
			List<Tier> list = Tiers();
			if (tier <= 0 || tier > list.Count)
			{
				return null;
			}
			string text = CreatureName(creature);
			try
			{
				return string.Format(list[tier - 1].Title, text);
			}
			catch (FormatException)
			{
				return list[tier - 1].Title.Replace("{0}", text);
			}
		}

		internal static string CreatureName(string creature)
		{
			return Localize(creature);
		}

		internal static string Headline(Tally t)
		{
			string creature = null;
			int num = 0;
			foreach (KeyValuePair<string, int> item in t.Ranked("kill:"))
			{
				int num2 = TierFor(item.Value);
				if (num2 > num)
				{
					num = num2;
					creature = item.Key;
				}
			}
			if (num <= 0)
			{
				return null;
			}
			return Title(num, creature);
		}

		internal static string TooltipSection(ItemData item)
		{
			if (!Plugin.Enabled.Value || !WeaponMemory.CanRemember(item))
			{
				return null;
			}
			WeaponMemory weaponMemory = WeaponMemory.Read(item);
			if (weaponMemory == null)
			{
				return null;
			}
			Tally all = weaponMemory.All;
			bool flag = WeaponKinds.Of(item) == WeaponKind.Shields;
			bool flag2 = Plugin.NameStyle.Value == NameStyle.Legend;
			StringBuilder stringBuilder = new StringBuilder();
			if (all.IsEmpty)
			{
				string text = (Plugin.ShowEngraving.Value ? Legend.Engraving(item, weaponMemory) : null);
				if (text != null)
				{
					return "\n\n<i>" + text + "</i>";
				}
				return null;
			}
			stringBuilder.Append("\n\n<color=#D9A441>Weapon memories</color>");
			if (flag2 && Names.Display(item) != null)
			{
				stringBuilder.Append("\nForged as: <color=orange>").Append(Localization.instance.Localize(item.m_shared.m_name)).Append("</color>");
			}
			if (flag)
			{
				stringBuilder.Append("\nBlows turned aside: <color=orange>").Append(all["block"]).Append("</color>");
				AppendFoes(stringBuilder, item, all.Ranked("blocked:"), legend: true);
				AppendDeeds(stringBuilder, all);
				AppendBearers(stringBuilder, item, weaponMemory, flag2);
				return stringBuilder.ToString();
			}
			stringBuilder.Append("\nKills: <color=orange>").Append(all.Kills).Append("</color>");
			int num = all["attack"];
			if (num >= 10)
			{
				int num2 = Math.Min(all["attackhit"], num);
				stringBuilder.Append("   Struck true: <color=orange>").Append(num2 * 100 / num).Append("%</color>");
			}
			AppendFoes(stringBuilder, item, all.Ranked("kill:"), flag2);
			AppendDeeds(stringBuilder, all);
			if (!flag2)
			{
				string text2 = NextGoal(all);
				if (text2 != null)
				{
					stringBuilder.Append("\nNext: ").Append(text2);
				}
			}
			AppendBearers(stringBuilder, item, weaponMemory, flag2);
			return stringBuilder.ToString();
		}

		private static void AppendFoes(StringBuilder sb, ItemData item, List<KeyValuePair<string, int>> ranked, bool legend)
		{
			int num = Math.Min(ranked.Count, Plugin.TopFoes.Value);
			for (int i = 0; i < num; i++)
			{
				KeyValuePair<string, int> keyValuePair = ranked[i];
				sb.Append("\n  ").Append(CreatureName(keyValuePair.Key)).Append(": <color=orange>")
					.Append(keyValuePair.Value)
					.Append("</color>");
				int num2 = TierFor(keyValuePair.Value);
				if (num2 > 0 && !legend)
				{
					sb.Append(" <color=#D9A441>").Append(Title(num2, keyValuePair.Key)).Append("</color>");
				}
				float num3 = DamageMultiplier(item, keyValuePair.Key) - 1f;
				if (num3 > 0f)
				{
					sb.Append(" <color=#8FBF6A>+").Append(Math.Round(num3 * 100f, 1)).Append("% dmg</color>");
				}
			}
			if (ranked.Count > num)
			{
				sb.Append("\n  and ").Append(ranked.Count - num).Append((ranked.Count - num == 1) ? " other kind" : " other kinds");
			}
		}

		private static void AppendDeeds(StringBuilder sb, Tally t)
		{
			string text = Feats(t);
			if (text != null)
			{
				sb.Append("\nDeeds: ").Append(text);
			}
		}

		private static string Feats(Tally t)
		{
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, int> item in t.Ranked("boss:"))
			{
				list.Add("felled " + CreatureName(item.Key));
			}
			foreach (KeyValuePair<string, int> item2 in t.Ranked("wardboss:"))
			{
				list.Add("stood against " + CreatureName(item2.Key));
			}
			Count(list, t["sneak"], "unseen kill", "unseen kills");
			Count(list, t["oneshot"], "single-blow kill", "single-blow kills");
			Count(list, t["clutch"], "kill at death's door", "kills at death's door");
			Count(list, t["starred"], "starred foe", "starred foes");
			if (t["longshot"] >= 20)
			{
				list.Add("longest shot " + t["longshot"] + " m");
			}
			Count(list, t["parry"], "perfect parry", "perfect parries");
			Count(list, t["block"], "block", "blocks");
			Count(list, t["chop"], "axe-stroke on wood", "axe-strokes on wood");
			Count(list, t["mine"], "strike on stone", "strikes on stone");
			Count(list, t["death"], "bearer fallen", "bearers fallen");
			if (list.Count != 0)
			{
				return string.Join(", ", list);
			}
			return null;
		}

		private static void Count(List<string> parts, int n, string one, string many)
		{
			if (n > 0)
			{
				parts.Add(n + " " + ((n == 1) ? one : many));
			}
		}

		private static void AppendBearers(StringBuilder sb, ItemData item, WeaponMemory memory, bool legend)
		{
			if (Plugin.ShowEngraving.Value)
			{
				string text = Legend.Engraving(item, memory);
				if (text != null)
				{
					sb.Append("\n<i>").Append(text).Append("</i>");
				}
			}
			if (!legend || string.IsNullOrEmpty(memory.NamerName))
			{
				return;
			}
			string text2 = Legend.NameFrom(WeaponProfile.Of(item), memory.MakerName, memory.Namer);
			if (memory.NamerName != memory.MakerName && text2 != null)
			{
				sb.Append("\nNamed in the hands of <color=orange>").Append(memory.NamerName).Append("</color>");
			}
			if (Plugin.LaterBearers.Value == LaterBearers.KeepNamersName)
			{
				int num = memory.All.Kills - memory.Namer.Kills;
				if (num > 0 && text2 != null)
				{
					sb.Append("\nKills since it left ").Append(memory.NamerName).Append("'s hands: <color=orange>")
						.Append(num)
						.Append("</color>");
				}
			}
			else if (text2 != null && text2 != Legend.For(item, memory))
			{
				sb.Append("\nOnce called <color=orange>").Append(text2).Append("</color>");
			}
		}

		private static string NextGoal(Tally t)
		{
			List<Tier> list = Tiers();
			string text = null;
			int tier = 0;
			int num = int.MaxValue;
			foreach (KeyValuePair<string, int> item in t.Ranked("kill:"))
			{
				int num2 = TierFor(item.Value);
				if (num2 < list.Count)
				{
					int num3 = list[num2].Kills - item.Value;
					if (num3 < num)
					{
						num = num3;
						text = item.Key;
						tier = num2 + 1;
					}
				}
			}
			if (text == null)
			{
				return null;
			}
			return Title(tier, text) + " in " + num + ((num == 1) ? " kill" : " kills");
		}

		internal static float DamageMultiplier(ItemData item, string creature)
		{
			if (!Plugin.Enabled.Value || !Plugin.DamageBonus.Value)
			{
				return 1f;
			}
			WeaponMemory weaponMemory = WeaponMemory.Read(item);
			if (weaponMemory == null)
			{
				return 1f;
			}
			int num = TierFor(weaponMemory.All.KillsOf(creature));
			return 1f + (float)num * Plugin.DamageBonusPerTier.Value / 100f;
		}
	}
	internal static class KillTracker
	{
		private sealed class Pending
		{
			public Character Target;

			public ZNetView View;

			public ItemData Weapon;

			public string Creature;

			public string Biome;

			public bool Boss;

			public bool Starred;

			public bool Unseen;

			public bool FromFullHealth;

			public int Distance;

			public float Time;

			public bool LooksLethal;

			public bool OwnedHere;
		}

		internal enum SourceKind
		{
			Melee,
			Projectile,
			Splash
		}

		internal sealed class Source
		{
			public ItemData Item;

			public SourceKind Kind;

			public bool StruckSomething;
		}

		private const float RemoteKillWindow = 4f;

		private const float ForgetAfter = 120f;

		private const float ClutchHealth = 0.2f;

		private static readonly Dictionary<ZDOID, Pending> PendingHits = new Dictionary<ZDOID, Pending>();

		private static Source _source;

		internal static Source Enter(Character owner, ItemData item, SourceKind kind)
		{
			Source source = _source;
			_source = new Source
			{
				Item = (((Object)(object)owner != (Object)null && (Object)(object)owner == (Object)(object)Player.m_localPlayer) ? item : null),
				Kind = kind
			};
			return source;
		}

		internal static void Exit(Source previous)
		{
			Source source = _source;
			_source = previous;
			if (source?.Item == null)
			{
				return;
			}
			if (previous != null && previous.Item == source.Item)
			{
				previous.StruckSomething |= source.StruckSomething;
			}
			if (source.Kind == SourceKind.Melee)
			{
				bool struck = source.StruckSomething;
				WeaponMemory.Record(source.Item, delegate(Tally t)
				{
					t.Add("attack");
					if (struck)
					{
						t.Add("attackhit");
					}
				});
			}
			else if (source.Kind == SourceKind.Projectile && source.StruckSomething)
			{
				WeaponMemory.Record(source.Item, delegate(Tally t)
				{
					t.Add("attackhit");
				});
			}
		}

		private static ItemData WeaponBehind(HitData hit)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Plugin.Enabled.Value || hit == null || (Object)(object)localPlayer == (Object)null || hit.m_attacker != ((Character)localPlayer).GetZDOID())
			{
				return null;
			}
			ItemData val = ((_source != null) ? _source.Item : CurrentWeaponFor(localPlayer, hit));
			if (!WeaponMemory.CanRemember(val) || !((Humanoid)localPlayer).GetInventory().ContainsItem(val))
			{
				return null;
			}
			if (_source != null)
			{
				_source.StruckSomething = true;
			}
			return val;
		}

		private static ItemData CurrentWeaponFor(Player local, HitData hit)
		{
			//IL_0010: 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)
			ItemData currentWeapon = ((Humanoid)local).GetCurrentWeapon();
			if (currentWeapon == null || currentWeapon.m_shared.m_skillType != hit.m_skill)
			{
				return null;
			}
			return currentWeapon;
		}

		internal static void OnHit(Character target, HitData hit)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target == (Object)null || target.IsPlayer())
			{
				return;
			}
			ItemData val = WeaponBehind(hit);
			string text = Tally.CleanKey(target.m_name);
			ZDOID zDOID = target.GetZDOID();
			if (val != null && text != null && !((ZDOID)(ref zDOID)).IsNone())
			{
				float num = Epithets.DamageMultiplier(val, text);
				if (num > 1f)
				{
					hit.ApplyModifier(num);
				}
				Player localPlayer = Player.m_localPlayer;
				BaseAI baseAI = target.GetBaseAI();
				PendingHits[zDOID] = new Pending
				{
					Target = target,
					View = ((Component)target).GetComponent<ZNetView>(),
					Weapon = val,
					Creature = text,
					Biome = ((object)Heightmap.FindBiome(((Component)target).transform.position)/*cast due to .constrained prefix*/).ToString(),
					Boss = target.IsBoss(),
					Starred = (target.GetLevel() > 1),
					Unseen = ((Object)(object)baseAI != (Object)null && !baseAI.IsAlerted()),
					FromFullHealth = (target.GetHealth() >= target.GetMaxHealth() - 0.5f),
					Distance = (hit.m_ranged ? Mathf.RoundToInt(Vector3.Distance(((Component)localPlayer).transform.position, ((Component)target).transform.position)) : 0),
					Time = Time.time,
					LooksLethal = LooksLethal(target, hit),
					OwnedHere = target.IsOwner()
				};
				WeaponMemory.Record(val, delegate(Tally t)
				{
					t.Add("hit");
				});
			}
		}

		internal static void OnWorkHit(HitData hit, string stat)
		{
			ItemData val = WeaponBehind(hit);
			if (val != null)
			{
				WeaponMemory.Record(val, delegate(Tally t)
				{
					t.Add(stat);
				});
			}
		}

		internal static void OnShot(Humanoid shooter, ItemData weapon)
		{
			if (Plugin.Enabled.Value && (Object)(object)shooter != (Object)null && (Object)(object)shooter == (Object)(object)Player.m_localPlayer)
			{
				WeaponMemory.Record(weapon, delegate(Tally t)
				{
					t.Add("attack");
				});
			}
		}

		private static bool LooksLethal(Character target, HitData hit)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			float health = target.GetHealth();
			if (health <= 0f)
			{
				return true;
			}
			HitData val = hit.Clone();
			DamageModifier val2 = default(DamageModifier);
			val.ApplyResistance(target.GetDamageModifiers((WeakSpot)null), ref val2);
			if ((Object)(object)Game.instance != (Object)null)
			{
				val.ApplyModifier(Game.instance.GetDifficultyDamageScaleEnemy(((Component)target).transform.position) * Game.m_playerDamageRate);
			}
			DamageTypes damage = val.m_damage;
			float num = ((DamageTypes)(ref damage)).GetTotalDamage() - damage.m_fire - damage.m_poison - damage.m_spirit;
			BaseAI baseAI = target.GetBaseAI();
			if ((Object)(object)baseAI != (Object)null && !baseAI.IsAlerted() && hit.m_backstabBonus > 1f)
			{
				num *= hit.m_backstabBonus;
			}
			if (target.IsStaggering())
			{
				num *= 2f;
			}
			return num >= health * 0.9f;
		}

		internal static void OnDeath(Character dead, HitData lastHit, ZNetView view)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)dead == (Object)null || dead.IsPlayer() || (Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner())
			{
				return;
			}
			ZDOID zDOID = dead.GetZDOID();
			if (!PendingHits.TryGetValue(zDOID, out var value))
			{
				return;
			}
			PendingHits.Remove(zDOID);
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || lastHit == null || lastHit.m_attacker != ((Character)localPlayer).GetZDOID())
			{
				if (Plugin.LogKills.Value)
				{
					Plugin.Log.LogInfo((object)(dead.m_name + " died to someone else's blow; not counted."));
				}
			}
			else
			{
				Credit(value, "killing blow");
			}
		}

		internal static void Tick()
		{
			//IL_0065: 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)
			if (PendingHits.Count == 0)
			{
				return;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				PendingHits.Clear();
				return;
			}
			float time = Time.time;
			foreach (KeyValuePair<ZDOID, Pending> item in PendingHits.ToList())
			{
				Pending value = item.Value;
				if ((Object)(object)value.Target == (Object)null)
				{
					PendingHits.Remove(item.Key);
					if (!value.OwnedHere && value.LooksLethal && time - value.Time <= 4f)
					{
						Credit(value, "lethal hit, creature owned by another client");
					}
					continue;
				}
				if ((Object)(object)value.View != (Object)null && value.View.IsValid())
				{
					value.OwnedHere = value.View.IsOwner();
				}
				if (time - value.Time > 120f)
				{
					PendingHits.Remove(item.Key);
				}
			}
		}

		private static void Credit(Pending p, string how)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			bool clutch = ((Character)localPlayer).GetHealth() <= ((Character)localPlayer).GetMaxHealth() * 0.2f;
			bool night = EnvMan.IsNight();
			bool oneShot = p.FromFullHealth && p.LooksLethal;
			WeaponMemory.Record(p.Weapon, delegate(Tally t)
			{
				t.Add("kill:" + p.Creature);
				t.Add("biome:" + p.Biome);
				if (p.Boss)
				{
					t.Add("boss:" + p.Creature);
				}
				if (p.Starred)
				{
					t.Add("starred");
				}
				if (p.Unseen)
				{
					t.Add("sneak");
				}
				if (oneShot)
				{
					t.Add("oneshot");
				}
				if (clutch)
				{
					t.Add("clutch");
				}
				if (night)
				{
					t.Add("night");
				}
				if (p.Distance > 0)
				{
					t.Max("longshot", p.Distance);
				}
			});
			if (Plugin.LogKills.Value)
			{
				Plugin.Log.LogInfo((object)(p.Weapon.m_shared.m_name + " remembers " + p.Creature + " in " + p.Biome + " (" + how + ")" + (p.Boss ? ", boss" : "") + (p.Unseen ? ", unseen" : "") + (oneShot ? ", one blow" : "") + (clutch ? ", at death's door" : "") + (night ? ", night" : "") + ((p.Distance > 0) ? $", {p.Distance} m" : "") + "."));
			}
		}

		internal static void OnBearerDeath(Player player)
		{
			if (Plugin.Enabled.Value && !((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				WeaponMemory.Record(((Humanoid)player).GetCurrentWeapon(), delegate(Tally t)
				{
					t.Add("death");
				});
			}
		}

		internal static void OnBlocked(Humanoid blocker, ItemData item, bool perfect, Character attacker)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled.Value || (Object)(object)blocker == (Object)null || (Object)(object)blocker != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			string foe = (((Object)(object)attacker != (Object)null && !attacker.IsPlayer()) ? Tally.CleanKey(attacker.m_name) : null);
			bool boss = foe != null && attacker.IsBoss();
			string biome = ((object)Heightmap.FindBiome(((Component)blocker).transform.position)/*cast due to .constrained prefix*/).ToString();
			WeaponMemory.Record(item, delegate(Tally t)
			{
				t.Add("block");
				t.Add("wardbiome:" + biome);
				if (perfect)
				{
					t.Add("parry");
				}
				if (foe != null)
				{
					t.Add("blocked:" + foe);
				}
				if (boss)
				{
					t.Add("wardboss:" + foe);
				}
			});
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class CharacterDamagePatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			KillTracker.OnHit(__instance, hit);
		}
	}
	[HarmonyPatch(typeof(Character), "OnDeath")]
	internal static class CharacterOnDeathPatch
	{
		private static void Prefix(Character __instance, HitData ___m_lastHit, ZNetView ___m_nview)
		{
			KillTracker.OnDeath(__instance, ___m_lastHit, ___m_nview);
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerOnDeathPatch
	{
		private static void Prefix(Player __instance, ZNetView ___m_nview)
		{
			if ((Object)(object)___m_nview != (Object)null && ___m_nview.IsValid() && ___m_nview.IsOwner())
			{
				KillTracker.OnBearerDeath(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
	internal static class BlockPatch
	{
		private static readonly FieldRef<Humanoid, ItemData> LeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_leftItem");

		private static ItemData Blocker(Humanoid h)
		{
			return LeftItem.Invoke(h) ?? h.GetCurrentWeapon();
		}

		private static void Prefix(Humanoid __instance, float ___m_blockTimer, out bool __state)
		{
			ItemData val = Blocker(__instance);
			__state = val != null && val.m_shared.m_timedBlockBonus > 1f && ___m_blockTimer != -1f && ___m_blockTimer < 0.25f;
		}

		private static void Postfix(Humanoid __instance, Character attacker, bool __result, bool __state)
		{
			if (__result)
			{
				KillTracker.OnBlocked(__instance, Blocker(__instance), __state, attacker);
			}
		}
	}
	[HarmonyPatch(typeof(TreeBase), "Damage")]
	internal static class TreeHitPatch
	{
		private static void Prefix(HitData hit)
		{
			KillTracker.OnWorkHit(hit, "chop");
		}
	}
	[HarmonyPatch(typeof(TreeLog), "Damage")]
	internal static class LogHitPatch
	{
		private static void Prefix(HitData hit)
		{
			KillTracker.OnWorkHit(hit, "chop");
		}
	}
	[HarmonyPatch(typeof(MineRock), "Damage")]
	internal static class RockHitPatch
	{
		private static void Prefix(HitData hit)
		{
			KillTracker.OnWorkHit(hit, "mine");
		}
	}
	[HarmonyPatch(typeof(MineRock5), "Damage")]
	internal static class BigRockHitPatch
	{
		private static void Prefix(HitData hit)
		{
			KillTracker.OnWorkHit(hit, "mine");
		}
	}
	[HarmonyPatch(typeof(Attack), "DoMeleeAttack")]
	internal static class MeleeAttackPatch
	{
		private static void Prefix(Humanoid ___m_character, ItemData ___m_weapon, out KillTracker.Source __state)
		{
			__state = KillTracker.Enter((Character)(object)___m_character, ___m_weapon, KillTracker.SourceKind.Melee);
		}

		private static void Finalizer(KillTracker.Source __state)
		{
			KillTracker.Exit(__state);
		}
	}
	[HarmonyPatch(typeof(Attack), "DoAreaAttack")]
	internal static class AreaAttackPatch
	{
		private static void Prefix(Humanoid ___m_character, ItemData ___m_weapon, out KillTracker.Source __state)
		{
			__state = KillTracker.Enter((Character)(object)___m_character, ___m_weapon, KillTracker.SourceKind.Melee);
		}

		private static void Finalizer(KillTracker.Source __state)
		{
			KillTracker.Exit(__state);
		}
	}
	[HarmonyPatch(typeof(Attack), "FireProjectileBurst")]
	internal static class FireProjectilePatch
	{
		private static void Prefix(Humanoid ___m_character, ItemData ___m_weapon)
		{
			KillTracker.OnShot(___m_character, ___m_weapon);
		}
	}
	[HarmonyPatch(typeof(Projectile), "OnHit")]
	internal static class ProjectileHitPatch
	{
		private static void Prefix(Character ___m_owner, ItemData ___m_weapon, out KillTracker.Source __state)
		{
			__state = KillTracker.Enter(___m_owner, ___m_weapon, KillTracker.SourceKind.Projectile);
		}

		private static void Finalizer(KillTracker.Source __state)
		{
			KillTracker.Exit(__state);
		}
	}
	[HarmonyPatch(typeof(Projectile), "DoAOE")]
	internal static class ProjectileAoePatch
	{
		private static void Prefix(Character ___m_owner, ItemData ___m_weapon, out KillTracker.Source __state)
		{
			__state = KillTracker.Enter(___m_owner, ___m_weapon, KillTracker.SourceKind.Splash);
		}

		private static void Finalizer(KillTracker.Source __state)
		{
			KillTracker.Exit(__state);
		}
	}
	[HarmonyPatch(typeof(Aoe), "OnHit", new Type[]
	{
		typeof(Collider),
		typeof(Vector3)
	})]
	internal static class AoeHitPatch
	{
		private static void Prefix(Character ___m_owner, ItemData ___m_itemData, out KillTracker.Source __state)
		{
			__state = KillTracker.Enter(___m_owner, ___m_itemData, KillTracker.SourceKind.Splash);
		}

		private static void Finalizer(KillTracker.Source __state)
		{
			KillTracker.Exit(__state);
		}
	}
	internal static class Legend
	{
		private static readonly (int Kills, string Word)[] Renown = new(int, string)[5]
		{
			(500, "Legendary"),
			(200, "Fabled"),
			(75, "Storied"),
			(25, "Seasoned"),
			(5, "Blooded")
		};

		private static readonly (int Blocks, string Word)[] ShieldRenown = new(int, string)[5]
		{
			(1000, "Legendary"),
			(400, "Fabled"),
			(150, "Storied"),
			(50, "Battered"),
			(10, "Dented")
		};

		private static readonly string[] Prey = new string[7] { "$enemy_deer", "$enemy_boar", "$enemy_boarpiggy", "$enemy_neck", "$enemy_hare", "$enemy_chicken", "$enemy_hen" };

		private static readonly string[] Engravings = new string[4] { "A faint engraving reads \"{0}\"", "A strange inscription: \"{0}\"", "Scratched runes spell out \"{0}\"", "Worn letters near the grip read \"{0}\"" };

		internal static string For(ItemData item, WeaponMemory memory)
		{
			Tally t = ((Plugin.LaterBearers.Value == LaterBearers.KeepNamersName) ? memory.Namer : memory.All);
			return NameFrom(WeaponProfile.Of(item), memory.MakerName, t);
		}

		internal static string NameFrom(WeaponProfile w, string maker, Tally t)
		{
			bool flag = w.Kind == WeaponKind.Shields;
			string text = (flag ? ShieldPrefix(t) : Prefix(w, t));
			string text2 = (flag ? ShieldSuffix(t) : Suffix(t));
			if (text == null && text2 == null)
			{
				return null;
			}
			string text3 = (flag ? LandWord(t.Ranked("wardbiome:"), 25, t["block"]) : Element(w, t));
			return string.Join(" ", new string[4]
			{
				text,
				text3,
				BaseWord(w, Seed(w, maker)),
				text2
			}.Where((string s) => !string.IsNullOrEmpty(s)));
		}

		internal static bool HasEarnedName(ItemData item, Tally t)
		{
			if (!t.IsEmpty)
			{
				return NameFrom(WeaponProfile.Of(item), "", t) != null;
			}
			return false;
		}

		private static string Prefix(WeaponProfile w, Tally t)
		{
			int kills = t.Kills;
			if (kills == 0)
			{
				if (t["hit"] + t["chop"] + t["mine"] + t["block"] < 10)
				{
					return null;
				}
				return "Unblooded";
			}
			string item = Renown.FirstOrDefault(((int Kills, string Word) r) => kills >= r.Kills).Word;
			if (kills >= 200)
			{
				return item;
			}
			return Trait(w, t, kills) ?? item;
		}

		private static string Trait(WeaponProfile w, Tally t, int kills)
		{
			int num = t["attack"];
			int num2 = t["attackhit"];
			bool flag = w.Kind == WeaponKind.ElementalMagic || w.Kind == WeaponKind.BloodMagic;
			if (t["death"] >= 3 && t["death"] * 5 >= kills)
			{
				return "Ill-fated";
			}
			if (t["clutch"] >= 5)
			{
				return "Defiant";
			}
			if (t["parry"] >= 25)
			{
				return "Unyielding";
			}
			if (kills >= 20 && t["sneak"] * 4 >= kills)
			{
				return "Silent";
			}
			if (kills >= 20 && t["oneshot"] * 4 >= kills)
			{
				return "Merciless";
			}
			if (!flag && num >= 50 && num2 * 100 >= num * 85)
			{
				return "Unerring";
			}
			if (!flag && num >= 50 && num2 * 100 < num * 35)
			{
				return "Wayward";
			}
			if (kills >= 20 && t["night"] * 10 >= kills * 6)
			{
				return "Moonlit";
			}
			if (kills >= 20 && t["starred"] * 2 >= kills)
			{
				return "Proven";
			}
			if (t["block"] >= 50)
			{
				return "Steadfast";
			}
			return null;
		}

		private static string ShieldPrefix(Tally t)
		{
			int blocks = t["block"];
			string item = ShieldRenown.FirstOrDefault(((int Blocks, string Word) r) => blocks >= r.Blocks).Word;
			if (blocks >= 400)
			{
				return item;
			}
			if (t["death"] >= 3 && t["death"] * 20 >= blocks)
			{
				return "Ill-fated";
			}
			if (t["parry"] >= 25 && t["parry"] * 3 >= blocks)
			{
				return "Unyielding";
			}
			return item;
		}

		private static string Element(WeaponProfile w, Tally t)
		{
			(float, string) tuple = new(float, string)[5]
			{
				(w.Fire, "Ember"),
				(w.Frost, "Frost"),
				(w.Lightning, "Storm"),
				(w.Poison, "Venom"),
				(w.Spirit, "Hallowed")
			}.OrderByDescending(((float Amount, string Word) e) => e.Amount).First();
			if (tuple.Item1 > 0f)
			{
				return tuple.Item2;
			}
			return LandWord(t.Ranked("biome:"), 10, t.Kills);
		}

		private static string LandWord(List<KeyValuePair<string, int>> biomes, int atLeast, int total)
		{
			if (biomes.Count == 0 || biomes[0].Value < atLeast || biomes[0].Value * 2 < total)
			{
				return null;
			}
			return biomes[0].Key switch
			{
				"Meadows" => "Verdant", 
				"BlackForest" => "Shadow", 
				"Swamp" => "Mire", 
				"Mountain" => "Rime", 
				"Plains" => "Sunscorched", 
				"Mistlands" => "Mist", 
				"AshLands" => "Ashen", 
				"DeepNorth" => "Glacial", 
				"Ocean" => "Tidal", 
				_ => null, 
			};
		}

		private static string BaseWord(WeaponProfile w, uint seed)
		{
			string[] array;
			switch (w.Kind)
			{
			case WeaponKind.Swords:
				array = ((!w.TwoHanded) ? new string[3] { "Sword", "Blade", "Brand" } : new string[2] { "Greatsword", "Greatblade" });
				break;
			case WeaponKind.Knives:
				array = new string[3] { "Knife", "Seax", "Dagger" };
				break;
			case WeaponKind.Clubs:
				array = ((!w.TwoHanded) ? new string[3] { "Club", "Mace", "Cudgel" } : new string[2] { "Maul", "Sledge" });
				break;
			case WeaponKind.Axes:
				array = ((!w.TwoHanded) ? new string[3] { "Axe", "Hewer", "Bearded Axe" } : new string[2] { "Greataxe", "Battleaxe" });
				break;
			case WeaponKind.Spears:
				array = new string[2] { "Spear", "Javelin" };
				break;
			case WeaponKind.Polearms:
				array = new string[3] { "Atgeir", "Halberd", "Glaive" };
				break;
			case WeaponKind.Bows:
				array = new string[3] { "Bow", "Longbow", "Warbow" };
				break;
			case WeaponKind.Crossbows:
				array = new string[2] { "Crossbow", "Arbalest" };
				break;
			case WeaponKind.ElementalMagic:
				array = new string[2] { "Staff", "Rod" };
				break;
			case WeaponKind.BloodMagic:
				array = new string[2] { "Staff", "Sceptre" };
				break;
			case WeaponKind.Pickaxes:
				array = new string[2] { "Pick", "Pickaxe" };
				break;
			case WeaponKind.Unarmed:
				array = new string[2] { "Knuckles", "Claws" };
				break;
			case WeaponKind.Torches:
				array = new string[2] { "Torch", "Brand" };
				break;
			case WeaponKind.Shields:
				array = ShieldWords(w.Prefab);
				break;
			default:
				return w.PlainName;
			}
			return array[seed % (uint)array.Length];
		}

		private static string[] ShieldWords(string prefab)
		{
			string text = (prefab ?? "").ToLowerInvariant();
			if (!text.Contains("tower"))
			{
				if (!text.Contains("buckler"))
				{
					return new string[3] { "Shield", "Targe", "Linden-board" };
				}
				return new string[2] { "Buckler", "Targe" };
			}
			return new string[2] { "Tower Shield", "Wall" };
		}

		private static string Suffix(Tally t)
		{
			List<KeyValuePair<string, int>> list = t.Ranked("boss:");
			if (list.Count > 0)
			{
				return "of " + Epithets.CreatureName(list[0].Key) + "'s Fall";
			}
			int num = t["hit"];
			if (t["chop"] >= 100 && t["chop"] >= num * 3)
			{
				if (t["chop"] < 1000)
				{
					return "of the Woodpile";
				}
				return "of the Felled Forest";
			}
			if (t["mine"] >= 100 && t["mine"] >= num * 3)
			{
				if (t["mine"] < 1000)
				{
					return "of the Quarry";
				}
				return "of the Deep Delve";
			}
			int kills = t.Kills;
			int num2 = Prey.Sum((string p) => t.KillsOf(p));
			int num3 = Epithets.TierFor(num2);
			if (num3 > 0 && num2 * 10 >= kills * 6)
			{
				if (num3 < 3)
				{
					if (num3 != 2)
					{
						return "of the Hunt";
					}
					return "of the Long Hunt";
				}
				return "of the Wild Hunt";
			}
			int bestTier;
			string text = BestByTier(t, "kill:", out bestTier);
			if (text != null)
			{
				string text2 = Epithets.CreatureName(text);
				if (bestTier < 3)
				{
					if (bestTier != 2)
					{
						return "of " + text2 + "-Felling";
					}
					return "of the " + text2 + "'s Bane";
				}
				return "of the " + text2 + "-Scourge";
			}
			if (t["longshot"] >= 60)
			{
				return "of the Far Mark";
			}
			if (t["parry"] >= 50)
			{
				return "of the Turned Blade";
			}
			if (t["death"] >= 10)
			{
				return "of Many Graves";
			}
			return null;
		}

		private static string ShieldSuffix(Tally t)
		{
			List<KeyValuePair<string, int>> list = t.Ranked("wardboss:");
			if (list.Count > 0)
			{
				return "of the " + Epithets.CreatureName(list[0].Key) + "-Ward";
			}
			int bestTier;
			string text = BestByTier(t, "blocked:", out bestTier);
			if (text != null)
			{
				string text2 = Epithets.CreatureName(text);
				if (bestTier < 3)
				{
					if (bestTier != 2)
					{
						return "of the " + text2 + "-Ward";
					}
					return "of the " + text2 + "-Wall";
				}
				return "of the " + text2 + "'s Despair";
			}
			if (t["parry"] >= 50)
			{
				return "of the Turned Blade";
			}
			if (t["block"] >= 500)
			{
				return "of the Unbroken Wall";
			}
			if (t["death"] >= 10)
			{
				return "of Many Graves";
			}
			return null;
		}

		private static string BestByTier(Tally t, string prefix, out int bestTier)
		{
			string result = null;
			bestTier = 0;
			foreach (KeyValuePair<string, int> item in t.Ranked(prefix))
			{
				int num = Epithets.TierFor(item.Value);
				if (num > bestTier)
				{
					bestTier = num;
					result = item.Key;
				}
			}
			return result;
		}

		internal static string Engraving(ItemData item, WeaponMemory memory)
		{
			if (!string.IsNullOrEmpty(memory.MakerName))
			{
				return EngravingFor(WeaponProfile.Of(item), memory.MakerName);
			}
			return null;
		}

		internal static string EngravingFor(WeaponProfile w, string maker)
		{
			return string.Format(Engravings[Seed(w, maker) / 7 % (uint)Engravings.Length], maker);
		}

		private static uint Seed(WeaponProfile w, string maker)
		{
			string text = maker + "/" + w.Prefab;
			uint num = 2166136261u;
			string text2 = text;
			foreach (char c in text2)
			{
				num ^= c;
				num *= 16777619;
			}
			return num;
		}
	}
	[BepInPlugin("hevyh2o.valheim.odinsweaponmemories", "Odin's Weapon Memories", "0.1.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "hevyh2o.valheim.odinsweaponmemories";

		public const string Name = "Odin's Weapon Memories";

		public const string Version = "0.1.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> AnnounceEpithets;

		internal static ConfigEntry<int> TopFoes;

		internal static ConfigEntry<NameStyle> NameStyle;

		internal static ConfigEntry<LaterBearers> LaterBearers;

		internal static ConfigEntry<bool> ShowEngraving;

		internal static ConfigEntry<int> Tier1Kills;

		internal static ConfigEntry<string> Tier1Title;

		internal static ConfigEntry<int> Tier2Kills;

		internal static ConfigEntry<string> Tier2Title;

		internal static ConfigEntry<int> Tier3Kills;

		internal static ConfigEntry<string> Tier3Title;

		internal static ConfigEntry<bool> DamageBonus;

		internal static ConfigEntry<float> DamageBonusPerTier;

		internal static ConfigEntry<bool> LogKills;

		internal static readonly Dictionary<WeaponKind, ConfigEntry<bool>> KindEnabled = new Dictionary<WeaponKind, ConfigEntry<bool>>();

		private Harmony _harmony;

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("hevyh2o.valheim.odinsweaponmemories");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Odin's Weapon Memories 0.1.1 loaded. Odin is listening to the blades.");
		}

		internal static void BindConfig(ConfigFile config)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: 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_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Turn the whole mod on or off. When off, weapons stop counting and show no epithets, but the memories already on them are kept.");
			AnnounceEpithets = config.Bind<bool>("General", "AnnounceEpithets", true, "Show a message in the middle of the screen when a weapon earns a new name or epithet.");
			TopFoes = config.Bind<int>("General", "TopFoes", 3, new ConfigDescription("How many kinds of creature the tooltip lists, most kills first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			NameStyle = config.Bind<NameStyle>("Naming", "NameStyle", OdinsWeaponMemories.NameStyle.Legend, "Legend: the weapon earns a whole name from how it was used, e.g. \"Storied Frost Greataxe of the Troll's Bane\". Epithet: the item keeps its name with one epithet added, e.g. \"Iron Sword, Troll-Feller\".");
			LaterBearers = config.Bind<LaterBearers>("Naming", "LaterBearers", OdinsWeaponMemories.LaterBearers.KeepNamersName, "What happens to a legend name when the weapon changes hands. KeepNamersName: it stays as its first bearer left it; new bearers' deeds are still counted and shown. EvolveWithNewBearer: everyone's deeds keep shaping the name, and the tooltip remembers what its first bearer called it. The maker's engraving stays either way.");
			ShowEngraving = config.Bind<bool>("Naming", "ShowEngraving", true, "Show the maker's name as an engraving in the tooltip. The maker is the crafter, or the first bearer for found weapons.");
			Tier1Kills = config.Bind<int>("Epithets", "Tier1Kills", 10, new ConfigDescription("Kills of one kind of creature for the first tier: \"X-Felling\" in legend names, Tier1Title in epithet style. 0 turns this tier off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>()));
			Tier1Title = config.Bind<string>("Epithets", "Tier1Title", "{0}-Feller", "First epithet, used when NameStyle is Epithet. {0} is the creature's name, e.g. \"{0}-Feller\" gives \"Troll-Feller\".");
			Tier2Kills = config.Bind<int>("Epithets", "Tier2Kills", 50, new ConfigDescription("Kills of one kind of creature needed for the second epithet. 0 turns this tier off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>()));
			Tier2Title = config.Bind<string>("Epithets", "Tier2Title", "Bane of the {0}", "Second epithet, used when NameStyle is Epithet. {0} is the creature's name.");
			Tier3Kills = config.Bind<int>("Epithets", "Tier3Kills", 150, new ConfigDescription("Kills of one kind of creature needed for the third epithet. 0 turns this tier off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), Array.Empty<object>()));
			Tier3Title = config.Bind<string>("Epithets", "Tier3Title", "Scourge of the {0}", "Third epithet, used when NameStyle is Epithet. {0} is the creature's name.");
			DamageBonus = config.Bind<bool>("Bonus", "DamageBonus", false, "Off by default, so the mod stays purely cosmetic. When on, a weapon hits a little harder against kinds of creature it has earned an epithet for.");
			DamageBonusPerTier = config.Bind<float>("Bonus", "DamageBonusPerTier", 2f, new ConfigDescription("Extra damage in percent per epithet tier earned against that kind of creature (so 2 gives +2%, +4%, +6%). Only used when DamageBonus is on.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			LogKills = config.Bind<bool>("Debug", "LogKills", false, "Write a line to the BepInEx log for every kill a weapon remembers. Handy for testing.");
			BindKind(config, WeaponKind.Swords, on: true, "Swords");
			BindKind(config, WeaponKind.Axes, on: true, "Axes and battleaxes, including woodcutting axes");
			BindKind(config, WeaponKind.Clubs, on: true, "Clubs, maces and sledges");
			BindKind(config, WeaponKind.Spears, on: true, "Spears (stabbing; thrown spears leave your hands and aren't counted)");
			BindKind(config, WeaponKind.Polearms, on: true, "Atgeirs and other polearms");
			BindKind(config, WeaponKind.Knives, on: true, "Knives and daggers");
			BindKind(config, WeaponKind.Shields, on: true, "Shields, which remember the blows they turned aside rather than kills");
			BindKind(config, WeaponKind.Bows, on: true, "Bows");
			BindKind(config, WeaponKind.Crossbows, on: true, "Crossbows");
			BindKind(config, WeaponKind.ElementalMagic, on: true, "Elemental magic staffs");
			BindKind(config, WeaponKind.BloodMagic, on: true, "Blood magic staffs");
			BindKind(config, WeaponKind.Pickaxes, on: false, "Pickaxes");
			BindKind(config, WeaponKind.Unarmed, on: false, "Fist weapons (knuckles, claws)");
			BindKind(config, WeaponKind.Torches, on: false, "Torches");
			BindKind(config, WeaponKind.Other, on: false, "Any other weapon, e.g. from other mods, that trains a skill not listed here");
		}

		private static void BindKind(ConfigFile config, WeaponKind kind, bool on, string what)
		{
			KindEnabled[kind] = config.Bind<bool>("Weapons", kind.ToString(), on, what + ": remember their deeds and earn names. When off they stop counting and show their plain name; memories already on them are kept.");
		}

		private void Update()
		{
			KillTracker.Tick();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal enum WeaponKind
	{
		None,
		Swords,
		Axes,
		Clubs,
		Spears,
		Polearms,
		Knives,
		Shields,
		Bows,
		Crossbows,
		ElementalMagic,
		BloodMagic,
		Pickaxes,
		Unarmed,
		Torches,
		Other
	}
	internal struct WeaponProfile
	{
		public WeaponKind Kind;

		public bool TwoHanded;

		public float Fire;

		public float Frost;

		public float Lightning;

		public float Poison;

		public float Spirit;

		public string Prefab;

		public string PlainName;

		internal static WeaponProfile Of(ItemData item)
		{
			//IL_0001: 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)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			DamageTypes damage = item.GetDamage();
			return new WeaponProfile
			{
				Kind = WeaponKinds.Of(item),
				TwoHanded = ((int)item.m_shared.m_itemType == 14),
				Fire = damage.m_fire,
				Frost = damage.m_frost,
				Lightning = damage.m_lightning,
				Poison = damage.m_poison,
				Spirit = damage.m_spirit,
				Prefab = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name),
				PlainName = Localization.instance.Localize(item.m_shared.m_name)
			};
		}
	}
	internal static class WeaponKinds
	{
		internal static WeaponKind Of(ItemData item)
		{
			//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)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected I4, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			if (item?.m_shared == null)
			{
				return WeaponKind.None;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((int)itemType <= 5)
			{
				if (itemType - 3 > 1)
				{
					if ((int)itemType != 5)
					{
						goto IL_0042;
					}
					return WeaponKind.Shields;
				}
			}
			else if ((int)itemType != 14)
			{
				if ((int)itemType == 15)
				{
					return WeaponKind.Torches;
				}
				if ((int)itemType != 22)
				{
					goto IL_0042;
				}
			}
			if (item.m_shared.m_maxStackSize > 1)
			{
				return WeaponKind.None;
			}
			SkillType skillType = item.m_shared.m_skillType;
			return (skillType - 1) switch
			{
				0 => WeaponKind.Swords, 
				6 => WeaponKind.Axes, 
				2 => WeaponKind.Clubs, 
				4 => WeaponKind.Spears, 
				3 => WeaponKind.Polearms, 
				1 => WeaponKind.Knives, 
				7 => WeaponKind.Bows, 
				13 => WeaponKind.Crossbows, 
				8 => WeaponKind.ElementalMagic, 
				9 => WeaponKind.BloodMagic, 
				11 => WeaponKind.Pickaxes, 
				10 => WeaponKind.Unarmed, 
				_ => WeaponKind.Other, 
			};
			IL_0042:
			return WeaponKind.None;
		}

		internal static bool IsEnabled(WeaponKind kind)
		{
			if (kind != WeaponKind.None && Plugin.KindEnabled.TryGetValue(kind, out var value))
			{
				return value.Value;
			}
			return false;
		}
	}
	internal static class Stat
	{
		internal const string Kill = "kill:";

		internal const string Biome = "biome:";

		internal const string Boss = "boss:";

		internal const string Hit = "hit";

		internal const string Attack = "attack";

		internal const string AttackHit = "attackhit";

		internal const string Chop = "chop";

		internal const string Mine = "mine";

		internal const string Death = "death";

		internal const string Sneak = "sneak";

		internal const string OneShot = "oneshot";

		internal const string Clutch = "clutch";

		internal const string Night = "night";

		internal const string Starred = "starred";

		internal const string LongShot = "longshot";

		internal const string Block = "block";

		internal const string Parry = "parry";

		internal const string Blocked = "blocked:";

		internal const string WardBiome = "wardbiome:";

		internal const string WardBoss = "wardboss:";
	}
	internal sealed class Tally
	{
		private const string FormatVersion = "v2";

		private readonly Dictionary<string, int> _counts = new Dictionary<string, int>(StringComparer.Ordinal);

		internal int this[string key]
		{
			get
			{
				if (!_counts.TryGetValue(key, out var value))
				{
					return 0;
				}
				return value;
			}
		}

		internal bool IsEmpty => _counts.Count == 0;

		internal int Kills => Sum("kill:");

		internal void Add(string key, int n = 1)
		{
			_counts[key] = this[key] + n;
		}

		internal void Max(string key, int value)
		{
			if (value > this[key])
			{
				_counts[key] = value;
			}
		}

		internal List<KeyValuePair<string, int>> Ranked(string prefix)
		{
			return (from kv in _counts
				where kv.Key.StartsWith(prefix, StringComparison.Ordinal)
				select new KeyValuePair<string, int>(kv.Key.Substring(prefix.Length), kv.Value) into kv
				orderby kv.Value descending
				select kv).ThenBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> kv) => kv.Key, StringComparer.Ordinal).ToList();
		}

		internal int Sum(string prefix)
		{
			return _counts.Where((KeyValuePair<string, int> kv) => kv.Key.StartsWith(prefix, StringComparison.Ordinal)).Sum((KeyValuePair<string, int> kv) => kv.Value);
		}

		internal int KillsOf(string creature)
		{
			return this["kill:" + creature];
		}

		internal string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder("v2");
			foreach (KeyValuePair<string, int> item in _counts.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> kv) => kv.Key, StringComparer.Ordinal))
			{
				stringBuilder.Append('|').Append(item.Key).Append('=')
					.Append(item.Value);
			}
			return stringBuilder.ToString();
		}

		internal static Tally Parse(string raw)
		{
			Tally tally = new Tally();
			if (string.IsNullOrEmpty(raw))
			{
				return tally;
			}
			string[] array = raw.Split(new char[1] { '|' });
			string text = ((array[0] == "v1") ? "kill:" : ((array[0] == "v2") ? "" : null));
			if (text == null)
			{
				return tally;
			}
			for (int i = 1; i < array.Length; i++)
			{
				int num = array[i].LastIndexOf('=');
				if (num > 0 && int.TryParse(array[i].Substring(num + 1), out var result) && result > 0)
				{
					tally._counts[text + array[i].Substring(0, num)] = result;
				}
			}
			return tally;
		}

		internal static string CleanKey(string name)
		{
			if (!string.IsNullOrEmpty(name))
			{
				return name.Replace("|", "").Replace("=", "").Trim();
			}
			return null;
		}
	}
	internal sealed class WeaponMemory
	{
		internal const string DataKey = "hevyh2o.weaponmemories";

		private const string NamerKey = "hevyh2o.weaponmemories.namer";

		private const string WhoKey = "hevyh2o.weaponmemories.who";

		internal Tally All = new Tally();

		internal Tally Namer = new Tally();

		internal long MakerId;

		internal string MakerName = "";

		internal long NamerId;

		internal string NamerName = "";

		internal static bool CanRemember(ItemData item)
		{
			return WeaponKinds.IsEnabled(WeaponKinds.Of(item));
		}

		internal static bool Has(ItemData item)
		{
			if (item?.m_customData != null)
			{
				return item.m_customData.ContainsKey("hevyh2o.weaponmemories");
			}
			return false;
		}

		internal static bool IsOurKey(string key)
		{
			if (!(key == "hevyh2o.weaponmemories"))
			{
				return key.StartsWith("hevyh2o.weaponmemories.", StringComparison.Ordinal);
			}
			return true;
		}

		internal static WeaponMemory Read(ItemData item)
		{
			if (!Has(item))
			{
				return null;
			}
			Dictionary<string, string> customData = item.m_customData;
			WeaponMemory weaponMemory = new WeaponMemory
			{
				All = Tally.Parse(customData["hevyh2o.weaponmemories"])
			};
			if (customData.TryGetValue("hevyh2o.weaponmemories.namer", out var value))
			{
				weaponMemory.Namer = Tally.Parse(value);
			}
			if (customData.TryGetValue("hevyh2o.weaponmemories.who", out var value2))
			{
				string[] array = value2.Split(new char[1] { '|' });
				if (array.Length == 4)
				{
					long.TryParse(array[0], out weaponMemory.MakerId);
					weaponMemory.MakerName = array[1];
					long.TryParse(array[2], out weaponMemory.NamerId);
					weaponMemory.NamerName = array[3];
				}
			}
			else
			{
				weaponMemory.Namer = Tally.Parse(customData["hevyh2o.weaponmemories"]);
			}
			return weaponMemory;
		}

		internal void Write(ItemData item)
		{
			if (item.m_customData == null)
			{
				item.m_customData = new Dictionary<string, string>();
			}
			item.m_customData["hevyh2o.weaponmemories"] = All.Serialize();
			item.m_customData["hevyh2o.weaponmemories.namer"] = Namer.Serialize();
			item.m_customData["hevyh2o.weaponmemories.who"] = string.Join("|", MakerId.ToString(), Clean(MakerName), NamerId.ToString(), Clean(NamerName));
		}

		private static string Clean(string name)
		{
			return (name ?? "").Replace("|", "");
		}

		internal static void StampMaker(ItemData item, long makerId, string makerName)
		{
			if (!string.IsNullOrEmpty(makerName))
			{
				WeaponMemory weaponMemory = Read(item) ?? new WeaponMemory();
				if (string.IsNullOrEmpty(weaponMemory.MakerName))
				{
					weaponMemory.MakerId = makerId;
					weaponMemory.MakerName = makerName;
					weaponMemory.Write(item);
				}
			}
		}

		internal static void Record(ItemData weapon, Action<Tally> change)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && CanRemember(weapon) && ((Humanoid)localPlayer).GetInventory().ContainsItem(weapon))
			{
				WeaponMemory weaponMemory = Read(weapon) ?? new WeaponMemory();
				string text = Names.Announced(weapon, weaponMemory);
				long playerID = localPlayer.GetPlayerID();
				string playerName = localPlayer.GetPlayerName();
				if (string.IsNullOrEmpty(weaponMemory.MakerName))
				{
					bool flag = !string.IsNullOrEmpty(weapon.m_crafterName);
					weaponMemory.MakerId = (flag ? weapon.m_crafterID : playerID);
					weaponMemory.MakerName = (flag ? weapon.m_crafterName : playerName);
				}
				if (weaponMemory.NamerId == 0L)
				{
					weaponMemory.NamerId = playerID;
					weaponMemory.NamerName = playerName;
				}
				else if (weaponMemory.NamerId != playerID && !Legend.HasEarnedName(weapon, weaponMemory.Namer))
				{
					weaponMemory.NamerId = playerID;
					weaponMemory.NamerName = playerName;
					weaponMemory.Namer = new Tally();
				}
				change(weaponMemory.All);
				if (weaponMemory.NamerId == playerID)
				{
					change(weaponMemory.Namer);
				}
				weaponMemory.Write(weapon);
				string text2 = Names.Announced(weapon, weaponMemory);
				if (text2 != null && text2 != text && Plugin.AnnounceEpithets.Value)
				{
					((Character)localPlayer).Message((MessageType)2, "Odin names your weapon\n" + text2, 0, (Sprite)null, false);
				}
			}
		}
	}
	internal static class BuildInfo
	{
		public const string Version = "0.1.1";
	}
}