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 Byname v0.3.2
plugins/Byname.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using Byname.Commands; using Byname.Config; using Byname.Net; using Byname.Stats; using Byname.Titles; using Byname.Titles.Catalog; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("RAGEmedia")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A Valheim mod.")] [assembly: AssemblyFileVersion("0.3.2.0")] [assembly: AssemblyInformationalVersion("0.3.2+3d2524af24af9a6a1a03fed59080c23479836c8c")] [assembly: AssemblyProduct("Byname")] [assembly: AssemblyTitle("Byname")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.2.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 Byname { [BepInPlugin("com.ragemedia.byname", "Byname", "0.3.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class BynamePlugin : BaseUnityPlugin { public const string PluginGuid = "com.ragemedia.byname"; public const string PluginName = "Byname"; public const string PluginVersion = "0.3.2"; internal static BynamePlugin Instance; private readonly Harmony _harmony = new Harmony("com.ragemedia.byname"); private void Awake() { Instance = this; BindConfig(); _harmony.PatchAll(Assembly.GetExecutingAssembly()); VerifyPatches(); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BynameCommand()); LogInfo("Registered /byname."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void BindConfig() { BynameConfig.Bind(((BaseUnityPlugin)this).Config); } private void VerifyPatches() { List<MethodBase> list = _harmony.GetPatchedMethods().ToList(); LogInfo($"Harmony bound {list.Count} target(s):"); foreach (MethodBase item in list) { LogInfo(" bound " + item.DeclaringType?.FullName + "." + item.Name); } int num = 0; foreach (Type item2 in from t in Assembly.GetExecutingAssembly().GetTypes() where t.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Any() select t) { List<HarmonyMethod> list2 = (from HarmonyPatch a in item2.GetCustomAttributes(typeof(HarmonyPatch), inherit: true) select ((HarmonyAttribute)a).info).ToList(); HarmonyMethod val; try { val = HarmonyMethod.Merge(list2); } catch (Exception ex) { LogError(" UNRESOLVED " + item2.Name + ": could not merge patch attributes: " + ex.Message); num++; continue; } if (!(val?.declaringType == null) && !string.IsNullOrEmpty(val.methodName) && AccessTools.Method(val.declaringType, val.methodName, val.argumentTypes, (Type[])null) == null) { num++; LogError(" UNRESOLVED " + item2.Name + " -> " + val.declaringType.Name + "." + val.methodName + " does not exist. This patch will never run. The game version likely changed; re-check the method against the decompiled source."); } } if (num > 0) { LogError(string.Format("{0}: {1} patch target(s) failed to resolve. ", "Byname", num) + "This mod is loaded but not fully functional."); } else if (list.Count == 0) { LogWarning("Byname: loaded, but bound zero patches. Expected if this mod only adds content via Jotunn."); } else { LogInfo("Byname v0.3.2 ready."); } } internal static void LogInfo(string msg) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[Byname] " + msg)); } internal static void LogWarning(string msg) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("[Byname] " + msg)); } internal static void LogError(string msg) { ((BaseUnityPlugin)Instance).Logger.LogError((object)("[Byname] " + msg)); } internal static void LogVerbose(string msg) { if (BynameConfig.VerboseLogging != null && BynameConfig.VerboseLogging.Value) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[Byname] " + msg)); } } } } namespace Byname.Titles { internal static class FragmentCatalog { private static readonly List<TitleFragment> AllFragments = FateFragments.All.Concat(WarFragments.All).Concat(WildFragments.All).Concat(HearthFragments.All) .Concat(BeastFragments.All) .Concat(TierFragments.All) .Concat(EarlyFragments.All) .Concat(DoomFragments.All) .ToList(); private static readonly List<TitleTemplate> Patterns = new List<TitleTemplate> { new TitleTemplate("{epithet} {noun}"), new TitleTemplate("the {epithet}"), new TitleTemplate("the {epithet} {noun}", 1), new TitleTemplate("{noun} of the {domain}", 2), new TitleTemplate("{epithet} {noun} of the {domain}", 4) }; internal static IReadOnlyList<TitleFragment> Fragments => AllFragments; internal static IReadOnlyList<TitleTemplate> Templates => Patterns; internal static IReadOnlyList<PlayerStatType> TrackedStats => AllFragments.SelectMany((TitleFragment f) => f.Reads).Distinct().ToList(); } internal static class TitleEngine { private const float PoolDepth = 1f; private const float SlotCost = 1.5f; private const float RepeatedCategoryCost = 1f; private const float OnlyFallbacksCost = 100f; internal static ComposedTitle Compose(IStatSource stats, long playerId, int epoch) { int seed = Seed(playerId, epoch); List<TitleFragment> qualifying = Qualifying(stats); if (BynameConfig.VerboseLogging.Value) { LogEvaluation(stats, qualifying, seed); } Dictionary<TitleSlot, List<TitleFragment>> ranked = BuildPools(qualifying, seed, epoch); int value = BynameConfig.MaxTitleLength.Value; ComposedTitle composedTitle = null; bool bStrict = false; int bFlourish = 0; int bTie = 0; foreach (TitleTemplate template in FragmentCatalog.Templates) { bool[] array = new bool[2] { true, false }; foreach (bool flag in array) { Dictionary<TitleSlot, TitleFragment> chosen = TryFill(template, ranked, flag, playerId); if (chosen == null) { continue; } string text = template.Render(chosen, playerId); if (text.Length <= value) { List<TitleFragment> list = template.Slots.Select((TitleSlot sl) => chosen[sl]).ToList(); List<string> words = list.Select((TitleFragment f) => f.TextFor(playerId)).ToList(); Rarity rarity = (Rarity)list.Max((TitleFragment f) => (int)f.Rarity); ComposedTitle composedTitle2 = new ComposedTitle(text, rarity, list, words, Score(list)); int num = TieBreak(template.Pattern, seed); if (composedTitle == null || Beats(composedTitle2, flag, template.Flourish, num, composedTitle, bStrict, bFlourish, bTie)) { composedTitle = composedTitle2; bStrict = flag; bFlourish = template.Flourish; bTie = num; } } } } return composedTitle; } private static float Score(IReadOnlyList<TitleFragment> parts) { int num = parts.Count - parts.Select((TitleFragment f) => f.Category).Distinct().Count(); float num2 = parts.Sum((TitleFragment f) => f.Score) - 1.5f * (float)parts.Count - 1f * (float)num; if (!parts.All((TitleFragment f) => f.IsFallback)) { return num2; } return num2 - 100f; } private static bool Beats(ComposedTitle a, bool aStrict, int aFlourish, int aTie, ComposedTitle b, bool bStrict, int bFlourish, int bTie) { if (a.Score > b.Score + 0.001f) { return true; } if (a.Score < b.Score - 0.001f) { return false; } if (aStrict != bStrict) { return aStrict; } if (aFlourish != bFlourish) { return aFlourish > bFlourish; } return aTie < bTie; } private static Dictionary<TitleSlot, List<TitleFragment>> BuildPools(IEnumerable<TitleFragment> qualifying, int seed, int epoch) { Dictionary<TitleSlot, List<TitleFragment>> dictionary = new Dictionary<TitleSlot, List<TitleFragment>>(); foreach (IGrouping<TitleSlot, TitleFragment> item in from f in qualifying group f by f.Slot) { List<TitleFragment> earned = (item.Any((TitleFragment f) => !f.IsFallback) ? item.Where((TitleFragment f) => !f.IsFallback).ToList() : item.ToList()); List<TitleFragment> list = (from f in earned.Where((TitleFragment f) => !earned.Any((TitleFragment g) => g.Rarity > f.Rarity && SameDeed(f, g))).ToList() orderby f.Score descending, TieBreak(f.Id, seed) select f).ToList(); float best = list[0].Score; List<TitleFragment> list2 = list.Where((TitleFragment f) => f.Score >= best - 1f).ToList(); List<TitleFragment> second = list.Skip(list2.Count).ToList(); int count = ((list2.Count > 0) ? ((epoch % list2.Count + list2.Count) % list2.Count) : 0); List<TitleFragment> first = list2.Skip(count).Concat(list2.Take(count)).ToList(); dictionary[item.Key] = first.Concat(second).ToList(); } return dictionary; } private static bool SameDeed(TitleFragment a, TitleFragment b) { if (a.SourceKeys.Count == b.SourceKeys.Count) { return !a.SourceKeys.Except(b.SourceKeys).Any(); } return false; } private static Dictionary<TitleSlot, TitleFragment> TryFill(TitleTemplate template, IReadOnlyDictionary<TitleSlot, List<TitleFragment>> ranked, bool strict, long playerId) { Dictionary<TitleSlot, TitleFragment> dictionary = new Dictionary<TitleSlot, TitleFragment>(); HashSet<TitleCategory> usedCategories = new HashSet<TitleCategory>(); HashSet<string> usedSources = new HashSet<string>(); HashSet<string> usedWords = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (TitleSlot slot in template.Slots) { if (!ranked.TryGetValue(slot, out var value)) { return null; } TitleFragment titleFragment = value.FirstOrDefault((TitleFragment f) => !f.SourceKeys.Any(usedSources.Contains) && !usedWords.Contains(f.TextFor(playerId)) && (!strict || !usedCategories.Contains(f.Category))); if (titleFragment == null) { return null; } dictionary[slot] = titleFragment; usedCategories.Add(titleFragment.Category); usedWords.Add(titleFragment.TextFor(playerId)); foreach (string sourceKey in titleFragment.SourceKeys) { usedSources.Add(sourceKey); } } return dictionary; } internal static List<TitleFragment> Qualifying(IStatSource stats) { return (from f in FragmentCatalog.Fragments where BynameConfig.CategoryEnabled(f.Category) where !BynameConfig.IsBlocked(f.Id) where f.Qualifies(stats) select f).ToList(); } internal static List<KeyValuePair<TitleFragment, float>> NearMisses(IStatSource stats, int count, float floor = 0.25f) { HashSet<string> earned = new HashSet<string>(from f in Qualifying(stats) select f.Id); return (from f in FragmentCatalog.Fragments where !earned.Contains(f.Id) && !f.IsFallback where BynameConfig.CategoryEnabled(f.Category) && !BynameConfig.IsBlocked(f.Id) select new KeyValuePair<TitleFragment, float>(f, f.Progress(stats)) into x where x.Value > floor && x.Value < 1f orderby x.Value descending select x).Take(count).ToList(); } private static void LogEvaluation(IStatSource stats, List<TitleFragment> qualifying, int seed) { BynamePlugin.LogVerbose($"evaluating with seed {seed}: {qualifying.Count} qualifying fragment(s) " + "[" + string.Join(", ", qualifying.Select((TitleFragment f) => f.Id).ToArray()) + "]"); foreach (KeyValuePair<TitleFragment, float> item in NearMisses(stats, 5, 0.5f)) { BynamePlugin.LogVerbose($" near miss: {item.Key.Id} at {item.Value:P0}"); } } private static int Seed(long playerId, int epoch) { return (int)(playerId ^ (playerId >> 32)) * 397 + epoch; } private static int TieBreak(string id, int seed) { return TitleFragment.StableHash(id, seed); } } internal enum TitleSlot { Epithet, Noun, Domain } internal enum TitleCategory { Combat, Bosses, Building, Death, Harvest, Travel, Exploration, Taming, Fishing, Cooking, Crafting, Misc } internal enum Rarity { Common = 1, Uncommon, Rare, Epic, Legendary } internal sealed class TitleFragment { private readonly Func<IStatSource, bool> _qualifies; private readonly Func<IStatSource, float> _progress; private readonly Func<IStatSource, string> _describe; internal string Id { get; } internal TitleSlot Slot { get; } internal TitleCategory Category { get; } internal Rarity Rarity { get; } internal IReadOnlyList<string> Texts { get; } internal string Text => Texts[0]; internal IReadOnlyList<PlayerStatType> Reads { get; } internal bool IsFallback { get; } internal IReadOnlyCollection<string> SourceKeys { get; } internal float Score => (float)Rarity * BynameConfig.Weight(Category); internal unsafe TitleFragment(string id, TitleSlot slot, TitleCategory category, Rarity rarity, string text, Func<IStatSource, bool> qualifies, IReadOnlyList<PlayerStatType> reads, Func<IStatSource, float> progress = null, bool isFallback = false, Func<IStatSource, string> describe = null, IReadOnlyCollection<string> sourceKeys = null) { Id = id; Slot = slot; Category = category; Rarity = rarity; Texts = (from t in text.Split(new char[1] { '|' }) select t.Trim() into t where t.Length > 0 select t).ToArray(); _qualifies = qualifies; Reads = reads ?? Array.Empty<PlayerStatType>(); _progress = progress; IsFallback = isFallback; _describe = describe; SourceKeys = (IReadOnlyCollection<string>)(((object)sourceKeys) ?? ((object)Reads.Select((PlayerStatType r) => ((object)(*(PlayerStatType*)(&r))/*cast due to .constrained prefix*/).ToString()).ToArray())); } internal string TextFor(long playerId) { if (Texts.Count == 1) { return Texts[0]; } int seed = (int)(playerId ^ (playerId >> 32)); return Texts[StableHash(Id, seed) % Texts.Count]; } internal bool Qualifies(IStatSource stats) { try { return _qualifies(stats); } catch (Exception ex) { BynamePlugin.LogError("fragment '" + Id + "' threw while evaluating: " + ex.Message); return false; } } internal float Progress(IStatSource stats) { if (_progress == null) { return 0f; } try { return Math.Max(0f, Math.Min(1f, _progress(stats))); } catch { return 0f; } } internal string Describe(IStatSource stats) { if (_describe == null) { return Text; } try { return _describe(stats); } catch { return Text; } } internal unsafe static string Humanize(PlayerStatType stat) { string text = ((object)(*(PlayerStatType*)(&stat))/*cast due to .constrained prefix*/).ToString(); if (text.StartsWith("BuildCluster")) { return "largest " + text.Substring("BuildCluster".Length).ToLowerInvariant() + " cluster"; } StringBuilder stringBuilder = new StringBuilder(text.Length + 8); for (int i = 0; i < text.Length; i++) { char c = text[i]; if (i > 0 && char.IsUpper(c) && !char.IsUpper(text[i - 1])) { stringBuilder.Append(' '); } stringBuilder.Append(char.ToLowerInvariant(c)); } return stringBuilder.ToString(); } internal static float Scaled(TitleCategory category, float threshold) { if (threshold <= 0f) { return threshold; } return (float)Math.Ceiling(threshold * BynameConfig.ThresholdScale(category)); } internal static int StableHash(string id, int seed) { uint num = (uint)(seed * -2128831035); foreach (char c in id) { num ^= c; num *= 16777619; } return (int)(num & 0x7FFFFFFF); } internal static string Number(float v) { if (!(v >= 1000f)) { return v.ToString("0.##"); } return v.ToString("N0"); } internal static TitleFragment Threshold(string id, TitleSlot slot, TitleCategory category, Rarity rarity, string text, PlayerStatType stat, float threshold) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown PlayerStatType[] reads = (PlayerStatType[])(object)new PlayerStatType[1] { (PlayerStatType)(int)stat }; return new TitleFragment(id, slot, category, rarity, text, (IStatSource s) => s.Get(stat) >= Scaled(category, threshold), reads, (IStatSource s) => Progress(s.Get(stat), Scaled(category, threshold)), isFallback: false, (IStatSource s) => Humanize(stat) + ": " + Number(s.Get(stat)) + " (needed " + Number(Scaled(category, threshold)) + ")"); } internal static TitleFragment Enemy(string id, TitleSlot slot, Rarity rarity, string text, string enemyToken, float threshold) { return new TitleFragment(id, slot, TitleCategory.Combat, rarity, text, (IStatSource s) => s.GetEnemyKills(enemyToken, (KillModifiers)0) >= Scaled(TitleCategory.Combat, threshold), Array.Empty<PlayerStatType>(), (IStatSource s) => Progress(s.GetEnemyKills(enemyToken, (KillModifiers)0), Scaled(TitleCategory.Combat, threshold)), isFallback: false, (IStatSource s) => CreatureName(enemyToken) + " kills: " + Number(s.GetEnemyKills(enemyToken, (KillModifiers)0)) + " (needed " + Number(Scaled(TitleCategory.Combat, threshold)) + ")", (IReadOnlyCollection<string>)(object)new string[1] { enemyToken }); } internal static TitleFragment KilledBy(string id, TitleSlot slot, Rarity rarity, string text, float threshold, params string[] creatureTokens) { return new TitleFragment(id, slot, TitleCategory.Death, rarity, text, (IStatSource s) => Count(s) >= Scaled(TitleCategory.Death, threshold), Array.Empty<PlayerStatType>(), (IStatSource s) => Progress(Count(s), Scaled(TitleCategory.Death, threshold)), isFallback: false, (IStatSource s) => "killed by " + CreatureName(creatureTokens[0]) + ": " + Number(Count(s)) + " (needed " + Number(Scaled(TitleCategory.Death, threshold)) + ")", (IReadOnlyCollection<string>)(object)creatureTokens.Select((string t) => "killedby:" + t).ToArray()); float Count(IStatSource s) { return creatureTokens.Sum((string t) => s.GetDeathsBy(t)); } } internal static TitleFragment DiedIn(string id, TitleSlot slot, Rarity rarity, string text, string biome, float threshold) { return new TitleFragment(id, slot, TitleCategory.Death, rarity, text, (IStatSource s) => s.GetDeathsIn(biome) >= Scaled(TitleCategory.Death, threshold), Array.Empty<PlayerStatType>(), (IStatSource s) => Progress(s.GetDeathsIn(biome), Scaled(TitleCategory.Death, threshold)), isFallback: false, (IStatSource s) => "deaths in the " + BiomeName(biome) + ": " + Number(s.GetDeathsIn(biome)) + " (needed " + Number(Scaled(TitleCategory.Death, threshold)) + ")", (IReadOnlyCollection<string>)(object)new string[1] { "diedin:" + biome }); } private static float Progress(float value, float threshold) { if (!(threshold <= 0f)) { return value / threshold; } return 1f; } private static string CreatureName(string token) { if (!token.StartsWith("$enemy_")) { return token; } return token.Substring("$enemy_".Length).Replace('_', ' '); } private static string BiomeName(string biome) { return biome switch { "BlackForest" => "black forest", "AshLands" => "ashlands", "DeepNorth" => "deep north", _ => biome.ToLowerInvariant(), }; } internal static TitleFragment Always(string id, TitleSlot slot, TitleCategory category, string text) { return new TitleFragment(id, slot, category, Rarity.Common, text, (IStatSource _) => true, Array.Empty<PlayerStatType>(), (IStatSource _) => 1f, isFallback: true, (IStatSource _) => "you have yet to earn anything else"); } } internal sealed class TitleTemplate { internal string Pattern { get; } internal IReadOnlyList<TitleSlot> Slots { get; } internal int Flourish { get; } internal TitleTemplate(string pattern, int flourish = 0) { Pattern = pattern; Flourish = flourish; Slots = ParseSlots(pattern); } internal string Render(IReadOnlyDictionary<TitleSlot, TitleFragment> chosen) { return Render(chosen, (TitleFragment f) => f.Text); } internal string Render(IReadOnlyDictionary<TitleSlot, TitleFragment> chosen, long playerId) { return Render(chosen, (TitleFragment f) => f.TextFor(playerId)); } private string Render(IReadOnlyDictionary<TitleSlot, TitleFragment> chosen, Func<TitleFragment, string> word) { string text = Pattern; foreach (KeyValuePair<TitleSlot, TitleFragment> item in chosen) { text = text.Replace(Token(item.Key), word(item.Value)); } return Capitalize(text); } private static string Capitalize(string text) { if (string.IsNullOrEmpty(text)) { return text; } if (!char.IsLetter(text[0]) || char.IsUpper(text[0])) { return text; } return char.ToUpperInvariant(text[0]) + text.Substring(1); } private static string Token(TitleSlot slot) { return "{" + slot.ToString().ToLowerInvariant() + "}"; } private static IReadOnlyList<TitleSlot> ParseSlots(string pattern) { List<TitleSlot> list = new List<TitleSlot>(); foreach (TitleSlot value in Enum.GetValues(typeof(TitleSlot))) { if (pattern.Contains(Token(value))) { list.Add(value); } } return list; } } internal sealed class ComposedTitle { internal string Text { get; } internal Rarity Rarity { get; } internal IReadOnlyList<TitleFragment> Parts { get; } internal IReadOnlyList<string> Words { get; } internal float Score { get; } internal ComposedTitle(string text, Rarity rarity, IReadOnlyList<TitleFragment> parts, IReadOnlyList<string> words, float score) { Text = text; Rarity = rarity; Parts = parts; Words = words; Score = score; } internal string Describe() { return $"\"{Text}\" [{Rarity}, score {Score:0.##}] from " + string.Join(" + ", Parts.Select((TitleFragment p) => p.Id).ToArray()); } } internal static class CategoryDefaults { internal static float Weight(TitleCategory category) { return category switch { TitleCategory.Building => 0.85f, TitleCategory.Travel => 0.85f, TitleCategory.Harvest => 0.9f, TitleCategory.Death => 1.2f, _ => 1f, }; } } internal static class TitleService { private static readonly Dictionary<Rarity, string> RarityColors = new Dictionary<Rarity, string> { { Rarity.Common, "#C8C8C8" }, { Rarity.Uncommon, "#7FC77F" }, { Rarity.Rare, "#6FA8DC" }, { Rarity.Epic, "#B490D9" }, { Rarity.Legendary, "#E0A44C" } }; internal static void Evaluate(Player player, string reason) { if (!BynameConfig.Enabled.Value || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)Game.instance == (Object)null) { return; } try { EvaluateCore(player, reason); } catch (Exception arg) { BynamePlugin.LogError($"title evaluation failed on '{reason}': {arg}"); } } private static void EvaluateCore(Player player, string reason) { IStatSource stats = BuildStatSource(player); int num = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetDay() : 0); int lastChangeDay = TitleState.GetLastChangeDay(player); int num2 = TitleState.GetEpoch(player); int value = BynameConfig.StalenessDays.Value; bool flag = value > 0 && lastChangeDay >= 0 && num - lastChangeDay >= value; if (flag) { num2++; } ComposedTitle composedTitle = TitleEngine.Compose(stats, player.GetPlayerID(), num2); if (composedTitle == null) { BynamePlugin.LogVerbose(reason + ": no title could be composed"); return; } string title = TitleState.GetTitle(player); bool flag2 = !string.Equals(title, composedTitle.Text, StringComparison.Ordinal); TitleNet.Publish(player, composedTitle.Text, (int)composedTitle.Rarity); if (!flag2 && !flag) { BynamePlugin.LogVerbose(reason + ": title unchanged (" + composedTitle.Describe() + ")"); return; } TitleState.SetEpoch(player, num2); TitleState.SetLastChangeDay(player, num); TitleState.SetTitle(player, composedTitle.Text, (int)composedTitle.Rarity); if (!flag2) { BynamePlugin.LogVerbose(reason + ": stale reroll landed on the same title"); return; } BynamePlugin.LogInfo(reason + ": " + ((title == null) ? "first title" : ("retitled from \"" + title + "\"")) + " -> " + composedTitle.Describe()); Announce(player, composedTitle, title == null); } internal static IStatSource BuildStatSource(Player player) { LifetimeStatSource lifetimeStatSource = new LifetimeStatSource(Game.instance.GetPlayerProfile(), DeathLedger.Read(player, null)); if (BynameConfig.Scope.Value != StatScope.World) { return lifetimeStatSource; } long num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldUID() : 0); IReadOnlyDictionary<PlayerStatType, float> readOnlyDictionary = TitleState.GetBaseline(player, num); if (readOnlyDictionary == null) { TitleState.SetBaseline(player, num, FragmentCatalog.TrackedStats, lifetimeStatSource); readOnlyDictionary = TitleState.GetBaseline(player, num) ?? new Dictionary<PlayerStatType, float>(); BynamePlugin.LogInfo($"World scope: baselined {readOnlyDictionary.Count} stat(s) for world {num}."); } return new WorldStatSource(lifetimeStatSource, readOnlyDictionary, DeathLedger.Read(player, num)); } internal static List<string> Explain(Player player) { List<string> list = new List<string>(); if (!BynameConfig.Enabled.Value) { list.Add("Byname is disabled on this server."); return list; } if ((Object)(object)player == (Object)null || (Object)(object)Game.instance == (Object)null) { list.Add("No character loaded."); return list; } IStatSource stats = BuildStatSource(player); int epoch = TitleState.GetEpoch(player); string title = TitleState.GetTitle(player); ComposedTitle composedTitle = TitleEngine.Compose(stats, player.GetPlayerID(), epoch); if (composedTitle == null) { list.Add("You have no title yet."); return list; } string text = title ?? composedTitle.Text; list.Add($"You are {Decorate(text, TitleState.GetRarity(player))} ({composedTitle.Rarity})"); if (title != null && !string.Equals(title, composedTitle.Text, StringComparison.Ordinal)) { list.Add(" You have earned a new one since: next update makes you " + composedTitle.Text + "."); } list.Add(""); for (int i = 0; i < composedTitle.Parts.Count; i++) { list.Add($" {composedTitle.Words[i],-18} {composedTitle.Parts[i].Describe(stats)}"); } int num = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetDay() : 0); int lastChangeDay = TitleState.GetLastChangeDay(player); int value = BynameConfig.StalenessDays.Value; if (lastChangeDay >= 0 && value > 0) { int num2 = lastChangeDay + value; list.Add(""); list.Add((num2 > num) ? $"Standing since day {lastChangeDay} — rerolls on day {num2} if nothing changes." : $"Standing since day {lastChangeDay} — due to reroll at your next spawn or sleep."); } return list; } private static void Announce(Player player, ComposedTitle composed, bool firstEver) { string text = (firstEver ? "You are known as" : "You are now known as") + " " + Decorate(composed); if (BynameConfig.ShowChangeToast.Value) { ((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false); } if (BynameConfig.ShowChangeInChat.Value) { if ((Object)(object)Chat.instance != (Object)null) { ((Terminal)Chat.instance).AddString(text); } else { BynamePlugin.LogVerbose("chat line skipped: Chat.instance not ready yet"); } } } internal static string Decorate(ComposedTitle composed) { return Decorate(composed.Text, (int)composed.Rarity); } internal static string Decorate(string text, int rarity) { if (string.IsNullOrEmpty(text)) { return text; } if (!BynameConfig.ShowRarityColor.Value) { return text; } Rarity key = (Rarity)Mathf.Clamp(rarity, 1, 5); if (!RarityColors.TryGetValue(key, out var value)) { return text; } return "<color=" + value + ">" + text + "</color>"; } } } namespace Byname.Titles.Catalog { internal static class BeastFragments { internal static IEnumerable<TitleFragment> All => Firsts.Concat(Ladders); private static IEnumerable<TitleFragment> Ladders => Ladder("greyling", "Greyling", "$enemy_greyling", Rarity.Common, 400f, 1000f, 2500f).Concat(Ladder("boar", "Boar", "$enemy_boar", Rarity.Common, 250f, 600f, 1500f)).Concat(Ladder("deer", "Deer", "$enemy_deer", Rarity.Common, 200f, 500f, 1200f)).Concat(Ladder("neck", "Neck", "$enemy_neck", Rarity.Common, 250f, 600f, 1500f)) .Concat(Ladder("greydwarf", "Greydwarf", "$enemy_greydwarf", Rarity.Uncommon, 1500f, 4000f, 10000f)) .Concat(Ladder("skeleton", "Skeleton", "$enemy_skeleton", Rarity.Uncommon, 800f, 2000f, 5000f)) .Concat(Ladder("troll", "Troll", "$enemy_troll", Rarity.Rare, 150f, 400f)) .Concat(Ladder("draugr", "Draugr", "$enemy_draugr", Rarity.Uncommon, 800f, 2000f, 5000f)) .Concat(Ladder("blob", "Blob", "$enemy_blob", Rarity.Uncommon, 500f, 1500f, 4000f)) .Concat(Ladder("surtling", "Surtling", "$enemy_surtling", Rarity.Uncommon, 200f, 600f, 1500f)) .Concat(Ladder("leech", "Leech", "$enemy_leech", Rarity.Rare, 400f, 1000f)) .Concat(Ladder("wraith", "Wraith", "$enemy_wraith", Rarity.Rare, 120f, 300f)) .Concat(Ladder("abomination", "Abomination", "$enemy_abomination", Rarity.Epic, 50f)) .Concat(Ladder("wolf", "Wolf", "$enemy_wolf", Rarity.Rare, 500f, 1200f)) .Concat(Ladder("drake", "Drake", "$enemy_drake", Rarity.Rare, 250f, 700f)) .Concat(Ladder("fenring", "Fenring", "$enemy_fenring", Rarity.Epic, 200f)) .Concat(Ladder("stonegolem", "Golem", "$enemy_stonegolem", Rarity.Epic, 80f)) .Concat(Ladder("ulv", "Ulv", "$enemy_ulv", Rarity.Epic, 100f)) .Concat(Ladder("deathsquito", "Squito", "$enemy_deathsquito", Rarity.Rare, 300f, 800f)) .Concat(Ladder("fuling", "Fuling", "$enemy_goblin", Rarity.Rare, 700f, 2000f)) .Concat(Ladder("lox", "Lox", "$enemy_lox", Rarity.Epic, 150f)) .Concat(Ladder("serpent", "Serpent", "$enemy_serpent", Rarity.Epic, 50f)) .Concat(Ladder("seeker", "Seeker", "$enemy_seeker", Rarity.Epic, 400f)) .Concat(Ladder("seekerbrute", "Soldier", "$enemy_seekerbrute", Rarity.Epic, 80f)) .Concat(Ladder("tick", "Tick", "$enemy_tick", Rarity.Rare, 200f, 500f)) .Concat(Ladder("gjall", "Gjall", "$enemy_gjall", Rarity.Epic, 75f)) .Concat(Ladder("dvergr", "Dvergr", "$enemy_dvergr", Rarity.Epic, 150f)) .Concat(Ladder("charred", "Charred", "$enemy_charred", Rarity.Epic, 600f)) .Concat(Ladder("asksvin", "Asksvin", "$enemy_asksvin", Rarity.Epic, 200f)) .Concat(Ladder("morgen", "Morgen", "$enemy_morgen", Rarity.Epic, 100f)) .Concat(Ladder("valkyrie", "Valkyrie", "$enemy_fallenvalkyrie", Rarity.Epic, 50f)); private static IEnumerable<TitleFragment> Firsts => new TitleFragment[36] { TitleFragment.Enemy("greyling_bane", TitleSlot.Noun, Rarity.Common, "Greyling-Bane|Greyling-Swatter", "$enemy_greyling", 100f), TitleFragment.Enemy("boar_hunter", TitleSlot.Noun, Rarity.Common, "Boar-Hunter|Boar-Sticker|Tusk-Taker", "$enemy_boar", 60f), TitleFragment.Enemy("deer_stalker", TitleSlot.Noun, Rarity.Common, "Deer-Stalker|Stag-Hunter|Antler-Taker", "$enemy_deer", 40f), TitleFragment.Enemy("neck_catcher", TitleSlot.Noun, Rarity.Common, "Neck-Catcher|Neck-Wringer", "$enemy_neck", 80f), TitleFragment.Enemy("greydwarf_bane", TitleSlot.Noun, Rarity.Uncommon, "Greydwarf-Bane|Greydwarf-Breaker", "$enemy_greydwarf", 400f), TitleFragment.Enemy("skeleton_breaker", TitleSlot.Noun, Rarity.Uncommon, "Bone-Breaker|Skull-Cracker", "$enemy_skeleton", 200f), TitleFragment.Enemy("troll_slayer", TitleSlot.Noun, Rarity.Rare, "Troll-Slayer|Troll-Feller|Troll-Toppler", "$enemy_troll", 50f), TitleFragment.Enemy("draugr_bane", TitleSlot.Noun, Rarity.Uncommon, "Draugr-Bane|Draugr-Unmaker|Barrow-Breaker", "$enemy_draugr", 250f), TitleFragment.Enemy("blob_render", TitleSlot.Noun, Rarity.Uncommon, "Blob-Render|Ooze-Popper", "$enemy_blob", 150f), TitleFragment.Enemy("leech_puller", TitleSlot.Noun, Rarity.Rare, "Leech-Puller|Leech-Burner", "$enemy_leech", 120f), TitleFragment.Enemy("wraith_banisher", TitleSlot.Noun, Rarity.Rare, "Wraith-Banisher|Ghost-Cutter", "$enemy_wraith", 40f), TitleFragment.Enemy("abomination_feller", TitleSlot.Noun, Rarity.Epic, "Root-Cutter|Bog-Feller", "$enemy_abomination", 15f), TitleFragment.Enemy("surtling_snuffer", TitleSlot.Noun, Rarity.Uncommon, "Surtling-Snuffer|Ember-Quencher", "$enemy_surtling", 60f), TitleFragment.Enemy("wolf_breaker", TitleSlot.Noun, Rarity.Rare, "Wolf-Breaker|Wolf-Hunter|Pelt-Taker", "$enemy_wolf", 150f), TitleFragment.Enemy("drake_downer", TitleSlot.Noun, Rarity.Rare, "Drake-Downer|Drake-Hunter", "$enemy_drake", 80f), TitleFragment.Enemy("fenring_hunter", TitleSlot.Noun, Rarity.Epic, "Fenring-Hunter|Moon-Hunter", "$enemy_fenring", 60f), TitleFragment.Enemy("stone_golem_breaker", TitleSlot.Noun, Rarity.Epic, "Golem-Breaker|Crystal-Cracker", "$enemy_stonegolem", 25f), TitleFragment.Enemy("ulv_hunter", TitleSlot.Noun, Rarity.Epic, "Ulv-Hunter|Ulv-Bane", "$enemy_ulv", 30f), TitleFragment.Enemy("deathsquito_swatter", TitleSlot.Noun, Rarity.Rare, "Squito-Swatter|Needle-Breaker", "$enemy_deathsquito", 100f), TitleFragment.Enemy("fuling_bane", TitleSlot.Noun, Rarity.Rare, "Fuling-Bane|Fuling-Smasher|Goblin-Bane", "$enemy_goblin", 200f), TitleFragment.Enemy("lox_feller", TitleSlot.Noun, Rarity.Epic, "Lox-Feller|Lox-Toppler", "$enemy_lox", 50f), TitleFragment.Enemy("serpent_slayer", TitleSlot.Noun, Rarity.Epic, "Serpent-Slayer|Wyrm-Gaffer", "$enemy_serpent", 15f), TitleFragment.Enemy("seeker_render", TitleSlot.Noun, Rarity.Epic, "Seeker-Render|Bug-Crusher", "$enemy_seeker", 100f), TitleFragment.Enemy("seeker_brute_breaker", TitleSlot.Noun, Rarity.Epic, "Soldier-Breaker|Carapace-Cracker", "$enemy_seekerbrute", 25f), TitleFragment.Enemy("tick_squasher", TitleSlot.Noun, Rarity.Rare, "Tick-Squasher|Tick-Picker", "$enemy_tick", 50f), TitleFragment.Enemy("gjall_downer", TitleSlot.Noun, Rarity.Epic, "Gjall-Downer|Sky-Gutter", "$enemy_gjall", 25f), TitleFragment.Enemy("dvergr_breaker", TitleSlot.Noun, Rarity.Epic, "Dvergr-Breaker|Dwarf-Wronger", "$enemy_dvergr", 40f), TitleFragment.Enemy("charred_bane", TitleSlot.Noun, Rarity.Epic, "Charred-Bane|Ash-Sweeper", "$enemy_charred", 150f), TitleFragment.Enemy("asksvin_hunter", TitleSlot.Noun, Rarity.Epic, "Asksvin-Hunter|Scale-Taker", "$enemy_asksvin", 50f), TitleFragment.Enemy("morgen_breaker", TitleSlot.Noun, Rarity.Epic, "Morgen-Breaker|Morgen-Bane", "$enemy_morgen", 30f), TitleFragment.Enemy("valkyrie_feller", TitleSlot.Noun, Rarity.Epic, "Valkyrie-Feller|Wing-Clipper", "$enemy_fallenvalkyrie", 15f), TitleFragment.Enemy("troll_worn", TitleSlot.Epithet, Rarity.Rare, "Troll-Worn|Troll-Tested", "$enemy_troll", 30f), TitleFragment.Enemy("wolf_bitten", TitleSlot.Epithet, Rarity.Rare, "Wolf-Bitten|Wolf-Scarred", "$enemy_wolf", 100f), TitleFragment.Enemy("serpent_touched", TitleSlot.Epithet, Rarity.Epic, "Serpent-Touched|Wyrm-Blooded", "$enemy_serpent", 10f), TitleFragment.Enemy("greydwarf_weary", TitleSlot.Epithet, Rarity.Common, "Greydwarf-Weary|Pinecone-Pelted", "$enemy_greydwarf", 150f), TitleFragment.Enemy("draugr_haunted", TitleSlot.Epithet, Rarity.Uncommon, "Draugr-Haunted|Crypt-Hardened", "$enemy_draugr", 120f) }; private static IEnumerable<TitleFragment> Ladder(string stem, string creature, string token, Rarity first, params float[] thresholds) { Rarity rarity = first; foreach (float threshold in thresholds) { if (rarity < Rarity.Legendary) { rarity++; } yield return TitleFragment.Enemy(stem + "_" + rarity.ToString().ToLowerInvariant(), TitleSlot.Noun, rarity, RungWords(creature, rarity), token, threshold); } } private static string RungWords(string creature, Rarity rarity) { return rarity switch { Rarity.Uncommon => creature + "-Harrier|" + creature + "-Culler", Rarity.Rare => creature + "-Scourge|" + creature + "-Butcher", Rarity.Epic => creature + "-Reaper|" + creature + "-Ruin", _ => creature + "-Doom|" + creature + "-Ender", }; } } internal static class DoomFragments { internal static IEnumerable<TitleFragment> All => new TitleFragment[55] { TitleFragment.Threshold("tooth_marked", TitleSlot.Epithet, TitleCategory.Death, Rarity.Uncommon, "Tooth-Marked|Claw-Raked|Oft-Mauled", (PlayerStatType)56, 5f), TitleFragment.Threshold("much_mauled", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Much-Mauled|Beast-Bitten|Monster-Fed", (PlayerStatType)56, 25f), TitleFragment.Threshold("fodder", TitleSlot.Noun, TitleCategory.Death, Rarity.Uncommon, "Fodder|Easy-Meat|Carrion", (PlayerStatType)56, 10f), TitleFragment.Threshold("last_stand", TitleSlot.Domain, TitleCategory.Death, Rarity.Uncommon, "Last Stand|Losing Fight|Red Snow", (PlayerStatType)56, 8f), TitleFragment.DiedIn("meadow_felled", TitleSlot.Epithet, Rarity.Uncommon, "Meadow-Felled|Grass-Stained", "Meadows", 4f), TitleFragment.DiedIn("forest_taken", TitleSlot.Epithet, Rarity.Uncommon, "Forest-Taken|Pine-Shadowed|Moss-Covered", "BlackForest", 3f), TitleFragment.DiedIn("dark_wood", TitleSlot.Domain, Rarity.Rare, "Dark Wood|Black Pines", "BlackForest", 6f), TitleFragment.DiedIn("mire_taken", TitleSlot.Epithet, Rarity.Uncommon, "Mire-Taken|Bog-Claimed|Swamp-Sunk", "Swamp", 3f), TitleFragment.DiedIn("bog_body", TitleSlot.Noun, Rarity.Rare, "Bog-Body|Mire-Wight|Swamp-Thing", "Swamp", 6f), TitleFragment.DiedIn("the_mire", TitleSlot.Domain, Rarity.Uncommon, "Mire|Bog|Rotting Fen", "Swamp", 4f), TitleFragment.DiedIn("peak_fallen", TitleSlot.Epithet, Rarity.Uncommon, "Peak-Fallen|Snow-Buried", "Mountain", 3f), TitleFragment.DiedIn("high_snows", TitleSlot.Domain, Rarity.Rare, "High Snows|Cold Peaks", "Mountain", 6f), TitleFragment.DiedIn("dust_taken", TitleSlot.Epithet, Rarity.Uncommon, "Dust-Taken|Field-Fallen", "Plains", 3f), TitleFragment.DiedIn("yellow_grass", TitleSlot.Domain, Rarity.Rare, "Yellow Grass|Golden Fields", "Plains", 6f), TitleFragment.DiedIn("sea_taken", TitleSlot.Epithet, Rarity.Uncommon, "Sea-Taken|Salt-Buried", "Ocean", 3f), TitleFragment.DiedIn("whale_road", TitleSlot.Domain, Rarity.Rare, "Salt Grave|Cold Sea", "Ocean", 6f), TitleFragment.DiedIn("mist_lost", TitleSlot.Epithet, Rarity.Uncommon, "Mist-Lost|Fog-Swallowed", "Mistlands", 3f), TitleFragment.DiedIn("grey_mist", TitleSlot.Domain, Rarity.Rare, "Grey Mist|Hollow Mist", "Mistlands", 6f), TitleFragment.DiedIn("ash_claimed", TitleSlot.Epithet, Rarity.Rare, "Ash-Claimed|Cinder-Taken", "AshLands", 3f), TitleFragment.DiedIn("ash_wastes", TitleSlot.Domain, Rarity.Epic, "Ash Wastes|Burning Shore", "AshLands", 6f), TitleFragment.DiedIn("ice_claimed", TitleSlot.Epithet, Rarity.Rare, "Ice-Claimed|Frost-Graved", "DeepNorth", 3f), TitleFragment.KilledBy("boar_gored", TitleSlot.Epithet, Rarity.Epic, "Boar-Gored|Pig-Bested", 1f, "$enemy_boar"), TitleFragment.KilledBy("greyling_bested", TitleSlot.Epithet, Rarity.Epic, "Greyling-Bested|Twig-Beaten", 1f, "$enemy_greyling"), TitleFragment.KilledBy("neck_nibbled", TitleSlot.Epithet, Rarity.Epic, "Neck-Nibbled|Lizard-Licked", 1f, "$enemy_neck"), TitleFragment.KilledBy("greydwarf_pelted", TitleSlot.Epithet, Rarity.Uncommon, "Greydwarf-Pelted|Stone-Pelted", 3f, "$enemy_greydwarf", "$enemy_greydwarfbrute", "$enemy_greydwarfshaman"), TitleFragment.KilledBy("troll_stomped", TitleSlot.Epithet, Rarity.Uncommon, "Troll-Stomped|Troll-Flattened|Club-Struck", 2f, "$enemy_troll", "$enemy_trollfrost"), TitleFragment.KilledBy("bone_beaten", TitleSlot.Epithet, Rarity.Uncommon, "Bone-Beaten|Crypt-Taken", 3f, "$enemy_skeleton", "$enemy_skeletonpoison", "$enemy_skeletonfire"), TitleFragment.KilledBy("draugr_fodder", TitleSlot.Epithet, Rarity.Uncommon, "Draugr-Hewn|Draugr-Felled|Barrow-Taken", 3f, "$enemy_draugr", "$enemy_draugrelite"), TitleFragment.KilledBy("draugr_meat", TitleSlot.Noun, Rarity.Rare, "Draugr-Fodder|Draugr-Meat", 6f, "$enemy_draugr", "$enemy_draugrelite"), TitleFragment.KilledBy("ooze_choked", TitleSlot.Epithet, Rarity.Uncommon, "Ooze-Choked|Blob-Smothered|Slime-Drowned", 3f, "$enemy_blob", "$enemy_blobelite"), TitleFragment.KilledBy("leech_drained", TitleSlot.Epithet, Rarity.Uncommon, "Leech-Drained|Leech-Supped", 2f, "$enemy_leech"), TitleFragment.KilledBy("wraith_taken", TitleSlot.Epithet, Rarity.Rare, "Wraith-Taken|Ghost-Chilled", 2f, "$enemy_wraith"), TitleFragment.KilledBy("root_crushed", TitleSlot.Epithet, Rarity.Rare, "Root-Crushed|Log-Walloped", 2f, "$enemy_abomination"), TitleFragment.KilledBy("wolf_eaten", TitleSlot.Epithet, Rarity.Uncommon, "Wolf-Eaten|Wolf-Torn|Pack-Hunted", 3f, "$enemy_wolf", "$enemy_ulv"), TitleFragment.KilledBy("drake_frozen", TitleSlot.Epithet, Rarity.Uncommon, "Drake-Frozen|Ice-Spat", 3f, "$enemy_drake"), TitleFragment.KilledBy("fenring_torn", TitleSlot.Epithet, Rarity.Rare, "Fenring-Torn|Moon-Mauled", 2f, "$enemy_fenring", "$enemy_fenringcultist"), TitleFragment.KilledBy("golem_pounded", TitleSlot.Epithet, Rarity.Rare, "Golem-Pounded|Crystal-Crushed", 2f, "$enemy_stonegolem"), TitleFragment.KilledBy("squito_stung", TitleSlot.Epithet, Rarity.Rare, "Squito-Stung|Needle-Struck", 2f, "$enemy_deathsquito"), TitleFragment.KilledBy("fuling_clubbed", TitleSlot.Epithet, Rarity.Uncommon, "Fuling-Clubbed|Fuling-Felled", 3f, "$enemy_goblin", "$enemy_goblinbrute", "$enemy_goblinshaman"), TitleFragment.KilledBy("lox_trampled", TitleSlot.Epithet, Rarity.Rare, "Lox-Trampled|Lox-Flattened", 2f, "$enemy_lox"), TitleFragment.KilledBy("serpent_swallowed", TitleSlot.Epithet, Rarity.Rare, "Serpent-Eaten|Wyrm-Swallowed", 2f, "$enemy_serpent"), TitleFragment.KilledBy("seeker_stung", TitleSlot.Epithet, Rarity.Uncommon, "Seeker-Stung|Bug-Bitten", 3f, "$enemy_seeker", "$enemy_seekerbrute"), TitleFragment.KilledBy("gjall_bombed", TitleSlot.Epithet, Rarity.Rare, "Gjall-Bombed|Sky-Spat", 2f, "$enemy_gjall"), TitleFragment.KilledBy("tick_drained", TitleSlot.Epithet, Rarity.Rare, "Tick-Drained|Tick-Supped", 2f, "$enemy_tick"), TitleFragment.KilledBy("dvergr_wronged", TitleSlot.Epithet, Rarity.Rare, "Dvergr-Wronged|Dwarf-Blasted", 2f, "$enemy_dvergr", "$enemy_dvergr_mage"), TitleFragment.KilledBy("charred_cut", TitleSlot.Epithet, Rarity.Uncommon, "Charred-Cut|Ash-Felled", 3f, "$enemy_charred", "$enemy_charred_melee", "$enemy_charred_archer", "$enemy_charred_mage", "$enemy_charred_twitcher"), TitleFragment.KilledBy("morgen_mauled", TitleSlot.Epithet, Rarity.Rare, "Morgen-Mauled|Morgen-Torn", 2f, "$enemy_morgen"), TitleFragment.KilledBy("valkyrie_taken", TitleSlot.Epithet, Rarity.Epic, "Valkyrie-Taken|Valkyrie-Chosen", 1f, "$enemy_fallenvalkyrie"), TitleFragment.KilledBy("stag_struck", TitleSlot.Epithet, Rarity.Rare, "Stag-Struck|Antler-Lit", 2f, "$enemy_eikthyr"), TitleFragment.KilledBy("elder_crushed", TitleSlot.Epithet, Rarity.Rare, "Elder-Crushed|Root-Bound", 2f, "$enemy_gdking"), TitleFragment.KilledBy("bonemass_taken", TitleSlot.Epithet, Rarity.Rare, "Bone-Mashed|Rot-Smothered", 2f, "$enemy_bonemass"), TitleFragment.KilledBy("moder_frozen", TitleSlot.Epithet, Rarity.Rare, "Moder-Frozen|Wing-Chilled", 2f, "$enemy_dragon"), TitleFragment.KilledBy("yagluth_burned", TitleSlot.Epithet, Rarity.Epic, "King-Burned|Yagluth-Scorched", 2f, "$enemy_goblinking"), TitleFragment.KilledBy("queen_stung", TitleSlot.Epithet, Rarity.Epic, "Queen-Stung|Hive-Taken", 2f, "$enemy_seekerqueen"), TitleFragment.KilledBy("fader_scorched", TitleSlot.Epithet, Rarity.Epic, "Fader-Scorched|Fire-Unmade", 2f, "$enemy_fader") }; } internal static class EarlyFragments { internal static IEnumerable<TitleFragment> All => new TitleFragment[37] { TitleFragment.Threshold("new_landed", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Common, "New-Landed|Raven-Brought|Fresh-Landed", (PlayerStatType)151, 1f), TitleFragment.Threshold("hearth_lit", TitleSlot.Epithet, TitleCategory.Building, Rarity.Common, "Hearth-Lit|Turf-Roofed|Smoke-Rising", (PlayerStatType)168, 50f), TitleFragment.Threshold("first_footed", TitleSlot.Epithet, TitleCategory.Travel, Rarity.Common, "First-Footed|Trail-Dusted|Mud-Booted", (PlayerStatType)17, 5000f), TitleFragment.Threshold("hungry", TitleSlot.Epithet, TitleCategory.Cooking, Rarity.Common, "Well-Meaning|Broth-Warmed|Crumb-Bearded", (PlayerStatType)49, 50f), TitleFragment.Threshold("hand_worn", TitleSlot.Epithet, TitleCategory.Crafting, Rarity.Common, "Hand-Worn|Blister-Palmed|Callus-Handed", (PlayerStatType)13, 40f), TitleFragment.Threshold("rough_handed", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Common, "Rough-Handed|Bark-Scuffed|Sap-Sticky", (PlayerStatType)27, 300f), TitleFragment.Threshold("wave_shy", TitleSlot.Epithet, TitleCategory.Travel, Rarity.Common, "Wave-Shy|Raft-Faring|Shore-Hugging", (PlayerStatType)19, 2000f), TitleFragment.Threshold("night_worn", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Common, "Night-Worn|Bed-Warmed|Dream-Fed", (PlayerStatType)23, 5f), TitleFragment.Threshold("twice_fallen", TitleSlot.Epithet, TitleCategory.Death, Rarity.Common, "Twice-Fallen|Still-Rising|Ill-Starred", (PlayerStatType)0, 2f), TitleFragment.Threshold("settler", TitleSlot.Noun, TitleCategory.Building, Rarity.Common, "Settler|Homesteader|Crofter", (PlayerStatType)168, 100f), TitleFragment.Threshold("gatherer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Common, "Gatherer|Scrounger|Gleaner", (PlayerStatType)12, 500f), TitleFragment.Threshold("walker", TitleSlot.Noun, TitleCategory.Travel, Rarity.Common, "Walker|Rambler|Footslogger", (PlayerStatType)17, 15000f), TitleFragment.Threshold("digger", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Common, "Digger|Mudlark|Ditch-Digger", (PlayerStatType)37, 500f), TitleFragment.Threshold("apprentice", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Common, "Apprentice|Tinkerer|Journeyman", (PlayerStatType)1, 100f), TitleFragment.Threshold("thrall", TitleSlot.Noun, TitleCategory.Misc, Rarity.Common, "Thrall|Bounder|Hopper", (PlayerStatType)3, 300f), TitleFragment.Threshold("first_camp", TitleSlot.Domain, TitleCategory.Building, Rarity.Common, "First Camp|Lean-To|Rough Camp", (PlayerStatType)168, 80f), TitleFragment.Threshold("near_shore", TitleSlot.Domain, TitleCategory.Travel, Rarity.Common, "Near Shore|Shallows|Home Waters", (PlayerStatType)19, 4000f), TitleFragment.Threshold("open_road", TitleSlot.Domain, TitleCategory.Travel, Rarity.Common, "Open Road|Beaten Path|Muddy Track", (PlayerStatType)17, 20000f), TitleFragment.Threshold("beast_calling", TitleSlot.Epithet, TitleCategory.Taming, Rarity.Common, "Beast-Calling|Feed-Tossing", (PlayerStatType)48, 2f), TitleFragment.Threshold("well_heeded", TitleSlot.Epithet, TitleCategory.Taming, Rarity.Uncommon, "Well-Heeded|Well-Obeyed", (PlayerStatType)192, 200f), TitleFragment.Threshold("soft_spoken", TitleSlot.Epithet, TitleCategory.Taming, Rarity.Epic, "Soft-Spoken|Beast-Beloved", (PlayerStatType)191, 800f), TitleFragment.Threshold("herder", TitleSlot.Noun, TitleCategory.Taming, Rarity.Common, "Herder|Swineherd|Drover", (PlayerStatType)48, 4f), TitleFragment.Threshold("houndmaster", TitleSlot.Noun, TitleCategory.Taming, Rarity.Rare, "Houndmaster|Packleader", (PlayerStatType)192, 500f), TitleFragment.Threshold("warm_stable", TitleSlot.Domain, TitleCategory.Taming, Rarity.Rare, "Warm Stable|Full Pen|Warm Byre", (PlayerStatType)191, 250f), TitleFragment.Threshold("float_watcher", TitleSlot.Noun, TitleCategory.Fishing, Rarity.Common, "Float-Watcher|Bobber-Gazer|Line-Dangler", (PlayerStatType)158, 30f), TitleFragment.Threshold("baited", TitleSlot.Epithet, TitleCategory.Fishing, Rarity.Uncommon, "Bait-Handed|Worm-Fingered", (PlayerStatType)121, 100f), TitleFragment.Threshold("slack_lined", TitleSlot.Epithet, TitleCategory.Fishing, Rarity.Common, "Slack-Lined|Line-Snapped", (PlayerStatType)159, 25f), TitleFragment.Threshold("hoard_keeping", TitleSlot.Epithet, TitleCategory.Building, Rarity.Rare, "Hoard-Keeping|Chest-Proud|Dragon-Hoarding", (PlayerStatType)181, 80f), TitleFragment.Threshold("floor_layer", TitleSlot.Noun, TitleCategory.Building, Rarity.Uncommon, "Floor-Layer|Plank-Setter|Boardwright", (PlayerStatType)174, 350f), TitleFragment.Threshold("floorwright", TitleSlot.Noun, TitleCategory.Building, Rarity.Rare, "Floorwright|Hall-Floorer|Deckwright", (PlayerStatType)174, 800f), TitleFragment.Threshold("gatewarden", TitleSlot.Noun, TitleCategory.Building, Rarity.Uncommon, "Gatewarden|Doorward|Porter", (PlayerStatType)189, 20f), TitleFragment.Threshold("cartwright", TitleSlot.Noun, TitleCategory.Building, Rarity.Rare, "Cartwright|Wainwright", (PlayerStatType)182, 15f), TitleFragment.Threshold("well_stocked", TitleSlot.Epithet, TitleCategory.Building, Rarity.Uncommon, "Well-Stocked|Larder-Proud|Full-Pantried", (PlayerStatType)183, 25f), TitleFragment.Threshold("wreath_hanger", TitleSlot.Epithet, TitleCategory.Building, Rarity.Epic, "Wreath-Hanging|Yule-Merry", (PlayerStatType)190, 20f), TitleFragment.Threshold("workshop", TitleSlot.Domain, TitleCategory.Building, Rarity.Uncommon, "Workshop|Workbench|Tool Loft", (PlayerStatType)172, 20f), TitleFragment.Threshold("homebound", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Uncommon, "Homebound|Hearth-Bound|Door-Hugging", (PlayerStatType)21, 200000f), TitleFragment.Threshold("seldom_home", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Rare, "Seldom-Home|Long-Absent|Hearthless", (PlayerStatType)22, 500000f) }; } internal static class FateFragments { internal static IEnumerable<TitleFragment> All => new TitleFragment[56] { TitleFragment.Always("unproven", TitleSlot.Epithet, TitleCategory.Misc, "Unproven|Untested|Untried"), TitleFragment.Always("stranger", TitleSlot.Noun, TitleCategory.Misc, "Stranger|Newcomer|Outlander"), TitleFragment.Threshold("sodden", TitleSlot.Epithet, TitleCategory.Death, Rarity.Uncommon, "Sodden|Waterlogged|Brine-Lunged", (PlayerStatType)59, 3f), TitleFragment.Threshold("deep_drowned", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Deep-Drowned|Sea-Claimed", (PlayerStatType)59, 15f), TitleFragment.Threshold("timber_struck", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Timber-Struck|Tree-Flattened", (PlayerStatType)68, 1f), TitleFragment.Threshold("oft_felled", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Oft-Felled|Forest-Hated", (PlayerStatType)68, 4f), TitleFragment.Threshold("ember_scarred", TitleSlot.Epithet, TitleCategory.Death, Rarity.Uncommon, "Ember-Scarred|Smoke-Blackened|Twice-Singed", (PlayerStatType)60, 3f), TitleFragment.Threshold("frost_bitten", TitleSlot.Epithet, TitleCategory.Death, Rarity.Uncommon, "Frost-Bitten|Rime-Touched|Blue-Lipped", (PlayerStatType)61, 3f), TitleFragment.Threshold("venom_touched", TitleSlot.Epithet, TitleCategory.Death, Rarity.Uncommon, "Venom-Touched|Poison-Veined|Green-Gilled", (PlayerStatType)62, 4f), TitleFragment.Threshold("smoke_choked", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Smoke-Choked|Soot-Lunged", (PlayerStatType)63, 3f), TitleFragment.Threshold("cliff_fallen", TitleSlot.Epithet, TitleCategory.Death, Rarity.Uncommon, "Cliff-Fallen|Ledge-Leaping|Gravity-Scorned", (PlayerStatType)58, 5f), TitleFragment.Threshold("cart_crushed", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Cart-Crushed|Wheel-Pressed", (PlayerStatType)67, 1f), TitleFragment.Threshold("keel_hauled", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Keel-Hauled|Hull-Struck", (PlayerStatType)72, 1f), TitleFragment.Threshold("self_undone", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Self-Undone|Self-Slain", (PlayerStatType)69, 3f), TitleFragment.Threshold("spike_struck", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Spike-Struck|Ceiling-Speared", (PlayerStatType)73, 1f), TitleFragment.Threshold("lava_kissed", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Lava-Kissed|Magma-Bathed", (PlayerStatType)204, 1f), TitleFragment.Threshold("hollow_eyed", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Hollow-Eyed|Oft-Buried|Grave-Familiar", (PlayerStatType)0, 50f), TitleFragment.Threshold("much_mourned", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Much-Mourned|Hel-Weary", (PlayerStatType)0, 150f), TitleFragment.Threshold("stone_crushed", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Stone-Crushed|Roof-Buried", (PlayerStatType)70, 2f), TitleFragment.Threshold("cinder_burnt", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Cinder-Burnt|Ash-Rained", (PlayerStatType)106, 1f), TitleFragment.Threshold("sea_boiled", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Sea-Boiled|Boil-Drowned", (PlayerStatType)107, 1f), TitleFragment.Threshold("bolt_pinned", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Bolt-Pinned|Ballista-Struck", (PlayerStatType)71, 1f), TitleFragment.Threshold("catapult_struck", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Catapult-Struck|Siege-Flattened", (PlayerStatType)105, 1f), TitleFragment.Threshold("friend_slain", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Friend-Slain|Kin-Struck|Back-Stabbed", (PlayerStatType)57, 1f), TitleFragment.Threshold("worlds_edge", TitleSlot.Epithet, TitleCategory.Death, Rarity.Legendary, "World's-Edge|Edge-Fallen", (PlayerStatType)65, 1f), TitleFragment.Threshold("gate_crushed", TitleSlot.Epithet, TitleCategory.Death, Rarity.Legendary, "Gate-Crushed|Drawbridge-Flat", (PlayerStatType)203, 1f), TitleFragment.Threshold("obliterated", TitleSlot.Epithet, TitleCategory.Death, Rarity.Legendary, "Obliterated|Incinerated", (PlayerStatType)108, 1f), TitleFragment.Threshold("gravewalker", TitleSlot.Noun, TitleCategory.Death, Rarity.Uncommon, "Gravewalker|Corpse-Walker|Grave-Returner", (PlayerStatType)0, 30f), TitleFragment.Threshold("revenant", TitleSlot.Noun, TitleCategory.Death, Rarity.Epic, "Revenant|Wight|Hel-Walker", (PlayerStatType)0, 120f), TitleFragment.Threshold("drowned", TitleSlot.Noun, TitleCategory.Death, Rarity.Rare, "Drowned|Kelp-Crown", (PlayerStatType)59, 8f), TitleFragment.Threshold("graverobber", TitleSlot.Noun, TitleCategory.Death, Rarity.Rare, "Graverobber|Grave-Picker", (PlayerStatType)53, 1f), TitleFragment.Threshold("corpse_runner", TitleSlot.Noun, TitleCategory.Death, Rarity.Uncommon, "Corpse-Runner|Grave-Fetcher|Kit-Chaser", (PlayerStatType)52, 15f), TitleFragment.Threshold("the_deep", TitleSlot.Domain, TitleCategory.Death, Rarity.Uncommon, "Deep|Depths|Undertow", (PlayerStatType)59, 1f), TitleFragment.Threshold("cold_ground", TitleSlot.Domain, TitleCategory.Death, Rarity.Uncommon, "Cold Ground|Shallow Grave|Barrow", (PlayerStatType)0, 20f), TitleFragment.Threshold("long_fall", TitleSlot.Domain, TitleCategory.Death, Rarity.Uncommon, "Long Fall|Sudden Drop", (PlayerStatType)58, 3f), TitleFragment.Threshold("pyre", TitleSlot.Domain, TitleCategory.Death, Rarity.Rare, "Pyre|Bonfire", (PlayerStatType)60, 5f), new TitleFragment("undying", TitleSlot.Epithet, TitleCategory.Death, Rarity.Legendary, "Undying|Hel-Denied", (IStatSource s) => s.Get((PlayerStatType)0) == 0f && s.Get((PlayerStatType)6) >= TitleFragment.Scaled(TitleCategory.Death, 200f), (IReadOnlyList<PlayerStatType>)(object)new PlayerStatType[2] { default(PlayerStatType), (PlayerStatType)6 }, (IStatSource s) => s.Get((PlayerStatType)6) / TitleFragment.Scaled(TitleCategory.Death, 200f), isFallback: false, (IStatSource s) => TitleFragment.Number(s.Get((PlayerStatType)6)) + " kills and not one death (needed " + TitleFragment.Number(TitleFragment.Scaled(TitleCategory.Death, 200f)) + " kills, 0 deaths)"), new TitleFragment("scarce_mourned", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Scarce-Mourned|Death-Dodging", (IStatSource s) => s.Get((PlayerStatType)0) <= 2f && s.Get((PlayerStatType)129) >= TitleFragment.Scaled(TitleCategory.Death, 50f), (IReadOnlyList<PlayerStatType>)(object)new PlayerStatType[2] { default(PlayerStatType), (PlayerStatType)129 }, (IStatSource s) => s.Get((PlayerStatType)129) / TitleFragment.Scaled(TitleCategory.Death, 50f), isFallback: false, (IStatSource s) => TitleFragment.Number(s.Get((PlayerStatType)129)) + " days survived unbroken, only " + TitleFragment.Number(s.Get((PlayerStatType)0)) + " deaths (needed " + TitleFragment.Number(TitleFragment.Scaled(TitleCategory.Death, 50f)) + " days, at most 2)"), TitleFragment.Threshold("long_lived", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Epic, "Long-Lived|Grey-Bearded", (PlayerStatType)129, 100f), TitleFragment.Threshold("well_rested", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Uncommon, "Well-Rested|Sleep-Glad|Bed-Loving", (PlayerStatType)23, 250f), TitleFragment.Threshold("raven_spoken", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Rare, "Raven-Spoken|Munin-Heeding", (PlayerStatType)46, 30f), TitleFragment.Threshold("leap_glad", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Uncommon, "Leap-Glad|Spring-Heeled", (PlayerStatType)3, 5000f), TitleFragment.Threshold("portal_worn", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Uncommon, "Portal-Worn|Rift-Stepping|Gate-Hopping", (PlayerStatType)15, 500f), TitleFragment.Threshold("door_minded", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Rare, "Door-Minded|Draught-Hating", (PlayerStatType)75, 500f), TitleFragment.Threshold("raven_striking", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Epic, "Raven-Striking|Munin-Swatting", (PlayerStatType)45, 1f), TitleFragment.Threshold("trophy_proud", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Uncommon, "Trophy-Proud|Head-Hanging", (PlayerStatType)24, 30f), TitleFragment.Threshold("armor_proud", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Uncommon, "Armor-Proud|Mail-Displaying", (PlayerStatType)25, 20f), TitleFragment.Threshold("tidy_handed", TitleSlot.Epithet, TitleCategory.Misc, Rarity.Uncommon, "Tidy-Handed|Chest-Sorting", (PlayerStatType)82, 300f), TitleFragment.Threshold("skald", TitleSlot.Noun, TitleCategory.Misc, Rarity.Uncommon, "Skald|Saga-Teller|Rune-Reader", (PlayerStatType)46, 10f), TitleFragment.Threshold("hoarder", TitleSlot.Noun, TitleCategory.Misc, Rarity.Rare, "Hoarder|Packrat|Magpie", (PlayerStatType)12, 10000f), TitleFragment.Threshold("sleeper", TitleSlot.Noun, TitleCategory.Misc, Rarity.Rare, "Sleeper|Dreamer|Slugabed", (PlayerStatType)23, 500f), TitleFragment.Threshold("elder", TitleSlot.Noun, TitleCategory.Misc, Rarity.Epic, "Elder|Greybeard|Old-Timer", (PlayerStatType)129, 150f), TitleFragment.Threshold("trophy_keeper", TitleSlot.Noun, TitleCategory.Misc, Rarity.Rare, "Trophy-Keeper|Head-Collector", (PlayerStatType)24, 80f), TitleFragment.Threshold("long_night", TitleSlot.Domain, TitleCategory.Misc, Rarity.Uncommon, "Long Night|Dark Hours", (PlayerStatType)23, 150f), TitleFragment.Threshold("old_ways", TitleSlot.Domain, TitleCategory.Misc, Rarity.Rare, "Old Ways|Old Gods", (PlayerStatType)129, 50f), TitleFragment.Threshold("trophy_wall", TitleSlot.Domain, TitleCategory.Misc, Rarity.Rare, "Trophy Wall|Mounted Heads", (PlayerStatType)24, 50f) }; } internal static class HearthFragments { internal static IEnumerable<TitleFragment> All => new TitleFragment[57] { TitleFragment.Threshold("hall_raising", TitleSlot.Epithet, TitleCategory.Building, Rarity.Uncommon, "Hall-Raising|Beam-Lifting|Ever-Building", (PlayerStatType)168, 3000f), TitleFragment.Threshold("stone_setting", TitleSlot.Epithet, TitleCategory.Building, Rarity.Uncommon, "Wall-Raising|Stone-Setting|Plank-Nailing", (PlayerStatType)175, 350f), TitleFragment.Threshold("rampart_raising", TitleSlot.Epithet, TitleCategory.Building, Rarity.Rare, "Rampart-Raising|Fortress-Minded", (PlayerStatType)175, 900f), TitleFragment.Threshold("high_building", TitleSlot.Epithet, TitleCategory.Building, Rarity.Epic, "High-Building|Tower-Raising|Sky-Reaching", (PlayerStatType)130, 40f), TitleFragment.Threshold("well_lit", TitleSlot.Epithet, TitleCategory.Building, Rarity.Uncommon, "Well-Lit|Torch-Lining|Lamp-Hanging", (PlayerStatType)179, 60f), TitleFragment.Threshold("well_housed", TitleSlot.Epithet, TitleCategory.Building, Rarity.Rare, "Well-Housed|Comfort-Wise|Cushion-Proud", (PlayerStatType)132, 17f), TitleFragment.Threshold("ever_mending", TitleSlot.Epithet, TitleCategory.Building, Rarity.Rare, "Ever-Mending|Never-Satisfied|Oft-Rebuilding", (PlayerStatType)170, 2000f), TitleFragment.Threshold("well_adorned", TitleSlot.Epithet, TitleCategory.Building, Rarity.Rare, "Well-Adorned|Rug-Proud|Banner-Hanging", (PlayerStatType)180, 60f), TitleFragment.Threshold("housecarl", TitleSlot.Noun, TitleCategory.Building, Rarity.Uncommon, "Housecarl|Joiner|Builder", (PlayerStatType)168, 1500f), TitleFragment.Threshold("thatcher", TitleSlot.Noun, TitleCategory.Building, Rarity.Uncommon, "Thatcher|Roofer|Shingler", (PlayerStatType)176, 200f), TitleFragment.Threshold("hallwright", TitleSlot.Noun, TitleCategory.Building, Rarity.Rare, "Hallwright|Roof-Raiser|Rafter-Master", (PlayerStatType)176, 450f), TitleFragment.Threshold("stonewright", TitleSlot.Noun, TitleCategory.Building, Rarity.Rare, "Stonewright|Mason|Pillar-Setter", (PlayerStatType)177, 200f), TitleFragment.Threshold("hearthkeeper", TitleSlot.Noun, TitleCategory.Building, Rarity.Uncommon, "Hearthkeeper|Homemaker|Bench-Carver", (PlayerStatType)178, 60f), TitleFragment.Threshold("wallwarden", TitleSlot.Noun, TitleCategory.Building, Rarity.Rare, "Wallwarden|Palisader|Stake-Setter", (PlayerStatType)186, 150f), TitleFragment.Threshold("stairwright", TitleSlot.Noun, TitleCategory.Building, Rarity.Epic, "Stairwright|Step-Cutter", (PlayerStatType)188, 80f), TitleFragment.Threshold("jarl", TitleSlot.Noun, TitleCategory.Building, Rarity.Legendary, "Jarl|Chieftain|Hall-King", (PlayerStatType)168, 20000f), TitleFragment.Threshold("quiet_hearth", TitleSlot.Domain, TitleCategory.Building, Rarity.Uncommon, "Quiet Hearth|Warm Hall|Long Bench", (PlayerStatType)178, 40f), TitleFragment.Threshold("high_hall", TitleSlot.Domain, TitleCategory.Building, Rarity.Rare, "High Hall|Tall Tower", (PlayerStatType)130, 28f), TitleFragment.Threshold("long_wall", TitleSlot.Domain, TitleCategory.Building, Rarity.Rare, "Long Wall|Great Wall|High Ramparts", (PlayerStatType)175, 700f), TitleFragment.Threshold("bee_tending", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Uncommon, "Bee-Tending|Honey-Handed|Hive-Minded", (PlayerStatType)76, 100f), TitleFragment.Threshold("sap_drawing", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Rare, "Sap-Drawing|Root-Tapping", (PlayerStatType)77, 200f), TitleFragment.Threshold("green_thumbed", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Uncommon, "Green-Thumbed|Soil-Blessed|Seed-Sowing", (PlayerStatType)123, 500f), TitleFragment.Threshold("berry_stained", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Uncommon, "Berry-Stained|Bramble-Scratched", (PlayerStatType)124, 500f), TitleFragment.Threshold("wood_wise", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Rare, "Wood-Wise|Mushroom-Mad|Toadstool-Wise", (PlayerStatType)125, 400f), TitleFragment.Threshold("beekeeper", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Beekeeper|Hive-Warden|Honey-Taker", (PlayerStatType)76, 50f), TitleFragment.Threshold("tiller", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Tiller|Farmer|Ploughman", (PlayerStatType)123, 200f), TitleFragment.Threshold("forager", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Common, "Forager|Berry-Picker", (PlayerStatType)124, 150f), TitleFragment.Threshold("sapwright", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Rare, "Sapwright|Sap-Tapper", (PlayerStatType)77, 100f), TitleFragment.Threshold("limbhewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Limbhewer|Axeman|Lumberjack", (PlayerStatType)27, 5000f), TitleFragment.Threshold("stonebreaker", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Stonebreaker|Rockhewer|Quarryman", (PlayerStatType)38, 1500f), TitleFragment.Threshold("oakfeller", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Epic, "Oakfeller|Oak-Toppler", (PlayerStatType)194, 100f), TitleFragment.Threshold("green_hall", TitleSlot.Domain, TitleCategory.Harvest, Rarity.Rare, "Green Hall|Tilled Field|Turnip Rows", (PlayerStatType)123, 400f), TitleFragment.Threshold("deep_wood", TitleSlot.Domain, TitleCategory.Harvest, Rarity.Uncommon, "Deep Wood|Tall Timber|Stump Field", (PlayerStatType)28, 800f), TitleFragment.Threshold("black_vein", TitleSlot.Domain, TitleCategory.Harvest, Rarity.Rare, "Black Vein|Deep Seam", (PlayerStatType)38, 2500f), TitleFragment.Threshold("well_fed", TitleSlot.Epithet, TitleCategory.Cooking, Rarity.Uncommon, "Well-Fed|Stout-Bellied|Hearty", (PlayerStatType)49, 800f), TitleFragment.Threshold("ale_warmed", TitleSlot.Epithet, TitleCategory.Cooking, Rarity.Rare, "Ale-Warmed|Mead-Soaked", (PlayerStatType)184, 20f), TitleFragment.Threshold("smoke_touched", TitleSlot.Epithet, TitleCategory.Cooking, Rarity.Uncommon, "Smoke-Touched|Spit-Turning|Grease-Fingered", (PlayerStatType)111, 300f), TitleFragment.Threshold("ash_tongued", TitleSlot.Epithet, TitleCategory.Cooking, Rarity.Rare, "Ash-Tongued|Char-Loving|Burnt-Offering", (PlayerStatType)112, 100f), TitleFragment.Threshold("cook", TitleSlot.Noun, TitleCategory.Cooking, Rarity.Common, "Cook|Pot-Stirrer|Broth-Maker", (PlayerStatType)109, 200f), TitleFragment.Threshold("firetender", TitleSlot.Noun, TitleCategory.Cooking, Rarity.Uncommon, "Firetender|Grillmaster|Spit-Turner", (PlayerStatType)111, 150f), TitleFragment.Threshold("meadwright", TitleSlot.Noun, TitleCategory.Cooking, Rarity.Rare, "Meadwright|Brewer|Mead-Brewer", (PlayerStatType)184, 10f), TitleFragment.Threshold("feastgiver", TitleSlot.Noun, TitleCategory.Cooking, Rarity.Epic, "Feastgiver|Feast-Host", (PlayerStatType)185, 8f), TitleFragment.Threshold("long_table", TitleSlot.Domain, TitleCategory.Cooking, Rarity.Rare, "Long Table|Mead Bench", (PlayerStatType)185, 5f), TitleFragment.Threshold("full_board", TitleSlot.Domain, TitleCategory.Cooking, Rarity.Uncommon, "Full Board|Groaning Board|Stewpot", (PlayerStatType)49, 500f), TitleFragment.Threshold("iron_handed", TitleSlot.Epithet, TitleCategory.Crafting, Rarity.Uncommon, "Iron-Handed|Anvil-Ringing", (PlayerStatType)114, 100f), TitleFragment.Threshold("ever_bettering", TitleSlot.Epithet, TitleCategory.Crafting, Rarity.Rare, "Ever-Bettering|Whetstone-Keen", (PlayerStatType)14, 300f), TitleFragment.Threshold("well_girded", TitleSlot.Epithet, TitleCategory.Crafting, Rarity.Uncommon, "Well-Girded|Mail-Stitching", (PlayerStatType)115, 60f), TitleFragment.Threshold("torch_bearing", TitleSlot.Epithet, TitleCategory.Crafting, Rarity.Rare, "Torch-Bearing|Pitch-Stained", (PlayerStatType)120, 200f), TitleFragment.Threshold("smith", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Common, "Smith|Hammerer|Forgehand", (PlayerStatType)13, 300f), TitleFragment.Threshold("weaponwright", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Rare, "Weaponwright|Bladesmith|Edge-Maker", (PlayerStatType)114, 200f), TitleFragment.Threshold("fletcher", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Uncommon, "Fletcher|Arrowsmith", (PlayerStatType)117, 300f), TitleFragment.Threshold("charmwright", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Epic, "Charmwright|Trinket-Maker", (PlayerStatType)116, 25f), TitleFragment.Threshold("toolwright", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Uncommon, "Toolwright|Tool-Maker", (PlayerStatType)119, 40f), TitleFragment.Threshold("refiner", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Uncommon, "Refiner|Alloy-Maker", (PlayerStatType)118, 300f), TitleFragment.Threshold("masterwright", TitleSlot.Noun, TitleCategory.Crafting, Rarity.Legendary, "Masterwright|Master-Smith", (PlayerStatType)1, 2500f), TitleFragment.Threshold("forge", TitleSlot.Domain, TitleCategory.Crafting, Rarity.Uncommon, "Forge|Anvil|Smithy", (PlayerStatType)13, 500f), TitleFragment.Threshold("keen_edge", TitleSlot.Domain, TitleCategory.Crafting, Rarity.Rare, "Keen Edge|Bright Blade", (PlayerStatType)114, 150f) }; } internal static class TierFragments { internal static IEnumerable<TitleFragment> All => new TitleFragment[28] { TitleFragment.Threshold("softwood_hewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Common, "Woodcutter|Kindling-Maker|Log-Splitter", (PlayerStatType)29, 200f), TitleFragment.Threshold("hardwood_hewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Hardwood-Hewer|Hardwood-Feller", (PlayerStatType)31, 150f), TitleFragment.Threshold("ironwood_hewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Rare, "Ironwood-Hewer|Heartwood-Hewer", (PlayerStatType)32, 100f), TitleFragment.Threshold("ancient_hewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Epic, "Ancient-Hewer|Elderwood-Hewer", (PlayerStatType)33, 60f), TitleFragment.Threshold("world_hewer", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Legendary, "World-Hewing|Root-Severing", (PlayerStatType)34, 40f), TitleFragment.Threshold("fir_feller", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Common, "Fir-Feller|Needle-Shedder", (PlayerStatType)193, 150f), TitleFragment.Threshold("birch_breaker", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Birch-Breaker|Birch-Feller", (PlayerStatType)200, 120f), TitleFragment.Threshold("mire_hewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Rare, "Mire-Hewer|Bog-Logger", (PlayerStatType)198, 150f), TitleFragment.Threshold("shoot_hewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Legendary, "Shoot-Hewer|Yggdrasil-Pruner", (PlayerStatType)197, 25f), TitleFragment.Threshold("ash_hewer", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Epic, "Ash-Hewer|Cinderwood-Cutter", (PlayerStatType)196, 80f), TitleFragment.Threshold("pine_feller", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Pine-Feller|Pine-Toppler", (PlayerStatType)195, 150f), TitleFragment.Threshold("snowline", TitleSlot.Domain, TitleCategory.Harvest, Rarity.Rare, "Snowline|Frost Pines", (PlayerStatType)201, 100f), TitleFragment.Threshold("stone_knapper", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Common, "Stoneknapper|Pebble-Cracker", (PlayerStatType)39, 300f), TitleFragment.Threshold("iron_delver", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Uncommon, "Iron-Delver|Ore-Delver", (PlayerStatType)41, 200f), TitleFragment.Threshold("deep_hewer", TitleSlot.Epithet, TitleCategory.Harvest, Rarity.Rare, "Deep-Hewing|Vein-Chasing", (PlayerStatType)42, 150f), TitleFragment.Threshold("blackmetal_delver", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Epic, "Black-Delver|Dark-Delver", (PlayerStatType)43, 100f), TitleFragment.Threshold("flametal_delver", TitleSlot.Noun, TitleCategory.Harvest, Rarity.Legendary, "Flame-Delver|Flametal-Seeker", (PlayerStatType)44, 60f), TitleFragment.Threshold("small_catch", TitleSlot.Noun, TitleCategory.Fishing, Rarity.Common, "Minnow-Taker|Sprat-Catcher", (PlayerStatType)161, 40f), TitleFragment.Threshold("fair_catch", TitleSlot.Epithet, TitleCategory.Fishing, Rarity.Uncommon, "Sure-Handed|Steady-Lined", (PlayerStatType)162, 30f), TitleFragment.Threshold("fine_catch", TitleSlot.Noun, TitleCategory.Fishing, Rarity.Rare, "Deep-Catcher|Fine-Catcher", (PlayerStatType)163, 25f), TitleFragment.Threshold("great_catch", TitleSlot.Noun, TitleCategory.Fishing, Rarity.Epic, "Great-Catcher|Trophy-Angler", (PlayerStatType)164, 15f), TitleFragment.Threshold("legend_catch", TitleSlot.Epithet, TitleCategory.Fishing, Rarity.Legendary, "Deep-Favoured|Njord-Blessed", (PlayerStatType)165, 10f), TitleFragment.Threshold("cold_current", TitleSlot.Domain, TitleCategory.Fishing, Rarity.Rare, "Cold Current|Dark Water", (PlayerStatType)163, 12f), TitleFragment.Threshold("sapling_struck", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Sapling-Struck|Twig-Felled", (PlayerStatType)152, 1f), TitleFragment.Threshold("hardwood_struck", TitleSlot.Epithet, TitleCategory.Death, Rarity.Rare, "Hardwood-Struck|Hardwood-Humbled", (PlayerStatType)154, 1f), TitleFragment.Threshold("ironwood_struck", TitleSlot.Epithet, TitleCategory.Death, Rarity.Epic, "Ironwood-Struck|Heartwood-Humbled", (PlayerStatType)155, 1f), TitleFragment.Threshold("ancient_struck", TitleSlot.Epithet, TitleCategory.Death, Rarity.Legendary, "Ancient-Struck|Root-Crowned", (PlayerStatType)157, 1f), TitleFragment.Threshold("fallen_timber", TitleSlot.Domain, TitleCategory.Death, Rarity.Rare, "Fallen Timber|Toppled Tree", (PlayerStatType)68, 2f) }; } internal static class WarFragments { internal static IEnumerable<TitleFragment> All => new TitleFragment[37] { Style("bare_fisted", Rarity.Legendary, "Bare-Fisted|Knuckle-Bloodied", (KillModifiers)1, "unarmed", 100f), Style("far_shooting", Rarity.Rare, "Far-Shooting|Keen-Eyed|Long-Aiming", (KillModifiers)3, "ranged", 200f), Style("rune_handed", Rarity.Epic, "Rune-Handed|Staff-Wielding|Galdr-Wise", (KillModifiers)2, "magic", 200f), Style("close_fighting", Rarity.Uncommon, "Close-Fighting|Shield-Pressing|Toe-to-Toe", (KillModifiers)4, "melee", 300f), TitleFragment.Threshold("blood_worn", TitleSlot.Epithet, TitleCategory.Combat, Rarity.Uncommon, "Blood-Worn|Blade-Weary|Gore-Spattered", (PlayerStatType)6, 500f), TitleFragment.Threshold("much_feared", TitleSlot.Epithet, TitleCategory.Combat, Rarity.Rare, "Much-Feared|Dread-Named|Grim-Famed", (PlayerStatType)6, 2500f), TitleFragment.Threshold("red_handed", TitleSlot.Epithet, TitleCategory.Combat, Rarity.Epic, "Red-Handed|Slaughter-Glad", (PlayerStatType)6, 7500f), TitleFragment.Threshold("shield_worn", TitleSlot.Epithet, TitleCategory.Combat, Rarity.Uncommon, "Shield-Worn|Dent-Helmed|Bruise-Hided", (PlayerStatType)10, 2000f), TitleFragment.Threshold("arrow_glad", TitleSlot.Epithet, TitleCategory.Combat, Rarity.Uncommon, "Arrow-Glad|Quiver-Light", (PlayerStatType)51, 2000f), TitleFragment.Threshold("bone_calling", TitleSlot.Epithet, TitleCategory.Combat, Rarity.Rare, "Bone-Calling|Grave-Stirring", (PlayerStatType)50, 50f), TitleFragment.Threshold("trap_setting", TitleSlot.Epithet, TitleCategory.Combat, Rarity.Uncommon, "Trap-Setting|Snare-Minded", (PlayerStatType)80, 20f), TitleFragment.Threshold("slayer", TitleSlot.Noun, TitleCategory.Combat, Rarity.Common, "Slayer|Reaver|Brawler", (PlayerStatType)6, 200f), TitleFragment.Threshold("hunter", TitleSlot.Noun, TitleCategory.Combat, Rarity.Uncommon, "Hunter|Stalker|Tracker", (PlayerStatType)7, 500f), TitleFragment.Threshold("bowman", TitleSlot.Noun, TitleCategory.Combat, Rarity.Uncommon, "Bowman|Archer|Bowyer", (PlayerStatType)51, 1000f), TitleFragment.Threshold("shieldbearer", TitleSlot.Noun, TitleCategory.Combat, Rarity.Rare, "Shieldbearer|Shieldwall|Bulwark", (PlayerStatType)10, 5000f), TitleFragment.Threshold("bonecaller", TitleSlot.Noun, TitleCategory.Combat, Rarity.Epic, "Bonecaller|Necromancer", (PlayerStatType)50, 150f), TitleFragment.Threshold("trapper", TitleSlot.Noun, TitleCategory.Combat, Rarity.Rare, "Trapper|Snarer", (PlayerStatType)81, 25f), TitleFragment.Threshold("ballista_keeper", TitleSlot.Noun, TitleCategory.Combat, Rarity.Uncommon, "Ballista-Keeper|Bolt-Loader", (PlayerStatType)78, 200f), TitleFragment.Threshold("kinslayer", TitleSlot.Noun, TitleCategory.Combat, Rarity.Rare, "Kinslayer|Oathbreaker|Backstabber", (PlayerStatType)9, 3f), TitleFragment.Threshold("red_field", TitleSlot.Domain, TitleCategory.Combat, Rarity.Uncommon, "Red Field|Blood Moor|Battlefield", (PlayerStatType)6, 1000f), TitleFragment.Threshold("long_war", TitleSlot.Domain, TitleCategory.Combat, Rarity.Rare, "Long War|Endless Fray", (PlayerStatType)5, 20000f), TitleFragment.Threshold("crown_breaking", TitleSlot.Epithet, TitleCategory.Bosses, Rarity.Rare, "Crown-Breaking|King-Toppling|Throne-Breaking", (PlayerStatType)85, 5f), TitleFragment.Threshold("god_touched", TitleSlot.Epithet, TitleCategory.Bosses, Rarity.Epic, "God-Touched|Power-Wielding|Altar-Blessed", (PlayerStatType)96, 200f), TitleFragment.Threshold("alone_going", TitleSlot.Epithet, TitleCategory.Bosses, Rarity.Epic, "Alone-Going|Lone-Standing|Unaided", (PlayerStatType)147, 3f), TitleFragment.Threshold("oath_bound", TitleSlot.Epithet, TitleCategory.Bosses, Rarity.Uncommon, "Oath-Bound|Kin-Fighting|War-Banded", (PlayerStatType)146, 4f), TitleFragment.Threshold("bane", TitleSlot.Noun, TitleCategory.Bosses, Rarity.Uncommon, "Bane|Horn-Taker|Crown-Taker|Beast-Queller", (PlayerStatType)85, 3f), TitleFragment.Threshold("lone_slayer", TitleSlot.Noun, TitleCategory.Bosses, Rarity.Legendary, "Lone-Slayer|Lone-Wolf", (PlayerStatType)147, 5f), TitleFragment.Threshold("kingsbane", TitleSlot.Noun, TitleCategory.Bosses, Rarity.Legendary, "Kingsbane|Godsbane|Throne-Ender", (PlayerStatType)85, 8f), TitleFragment.Threshold("deathbringer", TitleSlot.Noun, TitleCategory.Bosses, Rarity.Epic, "Deathbringer|Finisher|Last-Striker", (PlayerStatType)86, 3f), TitleFragment.Threshold("nine_crowns", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Legendary, "Nine Crowns|Fallen Gods", (PlayerStatType)85, 8f), TitleFragment.Threshold("stag_horn", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Uncommon, "Stag's Horn|Storm Stag", (PlayerStatType)97, 25f), TitleFragment.Threshold("elder_wood", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Rare, "Elder Wood|Old Root", (PlayerStatType)98, 25f), TitleFragment.Threshold("frozen_peak", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Rare, "Frozen Peak|Dragon's Breath", (PlayerStatType)100, 25f), TitleFragment.Threshold("black_mire", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Rare, "Black Mire|Bone Heap", (PlayerStatType)99, 25f), TitleFragment.Threshold("ashen_crown", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Epic, "Ashen Crown|Fallen King", (PlayerStatType)101, 25f), TitleFragment.Threshold("hive_queen", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Epic, "Hive Queen|Seeker Hive", (PlayerStatType)102, 25f), TitleFragment.Threshold("last_fire", TitleSlot.Domain, TitleCategory.Bosses, Rarity.Epic, "Last Fire|Burning Ring", (PlayerStatType)103, 25f) }; private static TitleFragment Style(string id, Rarity rarity, string text, KillModifiers style, string styleName, float kills) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return new TitleFragment(id, TitleSlot.Epithet, TitleCategory.Combat, rarity, text, (IStatSource s) => s.DominantKillStyle() == style && s.Get((PlayerStatType)6) >= TitleFragment.Scaled(TitleCategory.Combat, kills), (IReadOnlyList<PlayerStatType>)(object)new PlayerStatType[1] { (PlayerStatType)6 }, null, isFallback: false, (IStatSource s) => "most of your " + TitleFragment.Number(s.Get((PlayerStatType)6)) + " kills are " + styleName + " (needed " + TitleFragment.Number(TitleFragment.Scaled(TitleCategory.Combat, kills)) + "+, and a clear majority)"); } } internal static class WildFragments { internal static IEnumerable<TitleFragment> All { get { TitleFragment[] obj = new TitleFragment[37] { TitleFragment.Threshold("far_walking", TitleSlot.Epithet, TitleCategory.Travel, Rarity.Uncommon, "Far-Walking|Long-Striding|Sore-Footed", (PlayerStatType)17, 150000f), TitleFragment.Threshold("swift_footed", TitleSlot.Epithet, TitleCategory.Travel, Rarity.Uncommon, "Swift-Footed|Fleet-Footed|Hare-Quick", (PlayerStatType)18, 120000f), TitleFragment.Threshold("salt_worn", TitleSlot.Epithet, TitleCategory.Travel, Rarity.Rare, "Salt-Worn|Sea-Weathered|Brine-Crusted", (PlayerStatType)19, 120000f), TitleFragment.Threshold("wind_borne", TitleSlot.Epithet, TitleCategory.Travel, Rarity.Rare, "Wind-Borne|Seldom-Grounded", (PlayerStatType)20, 60000f), TitleFragment.Threshold("road_weary", TitleSlot.Epithet, TitleCategory.Travel, Rarity.Rare, "Road-Weary|World-Worn|Mile-Eating", (PlayerStatType)16, 800000f), TitleFragment.Threshold("wanderer", TitleSlot.Noun, TitleCategory.Travel, Rarity.Common, "Wanderer|Roamer|Drifter", (PlayerStatType)16, 50000f), TitleFragment.Threshold("wayfarer", TitleSlot.Noun, TitleCategory.Travel, Rarity.Uncommon, "Wayfarer|Journeyer|Strider", (PlayerStatType)16, 350000f), TitleFragment.Threshold("helmsman", TitleSlot.Noun, TitleCategory.Travel, Rarity.Rare, "Helmsman|Steersman|Skipper", (PlayerStatType)150, 80000f), TitleFragment.Threshold("ferryman", TitleSlot.Noun, TitleCategory.Travel, Rarity.Uncommon, "Ferryman|Oarsman|Boatman", (PlayerStatType)19, 40000f), TitleFragment.Threshold("long_road", TitleSlot.Domain, TitleCategory.Travel, Rarity.Uncommon, "Long Road|Winding Path", (PlayerStatType)18, 80000f), TitleFragment.Threshold("nine_waves", TitleSlot.Domain, TitleCategory.Travel, Rarity.Rare, "Nine Waves|Whale Road|Swan Road", (PlayerStatType)19, 100000f), TitleFragment.Threshold("open_sky", TitleSlot.Domain, TitleCategory.Travel, Rarity.Uncommon, "Open Sky|Empty Air|High Leap", (PlayerStatType)20, 25000f), TitleFragment.Threshold("north_going", TitleSlot.Epithet, TitleCategory.Exploration, Rarity.Rare, "North-Going|North-Bound|Pole-Seeking", (PlayerStatType)138, 8000f), TitleFragment.Threshold("south_going", TitleSlot.Epithet, TitleCategory.Exploration, Rarity.Rare, "South-Going|South-Bound", (PlayerStatType)139, 8000f), TitleFragment.Threshold("east_going", TitleSlot.Epithet, TitleCategory.Exploration, Rarity.Rare, "East-Going|Dawn-Chasing", (PlayerStatType)140, 8000f), TitleFragment.Threshold("west_going", TitleSlot.Epithet, TitleCategory.Exploration, Rarity.Rare, "West-Going|Sunset-Chasing", (PlayerStatType)141, 8000f), null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null }; Func<IStatSource, bool> qualifies = (IStatSource s) => MaxNoMap(s) >= TitleFragment.Scaled(TitleCategory.Exploration, 5000f); PlayerStatType[] array = new PlayerStatType[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); obj[16] = new TitleFragment("chartless", TitleSlot.Epithet, TitleCategory.Exploration, Rarity.Legendary, "Chartless|Map-Scorning", qualifies, (IReadOnlyList<PlayerStatType>)(object)array, (IStatSource s) => MaxNoMap(s) / TitleFragment.Scaled(TitleCategory.Exploration, 5000f), isFallback: false, (IStatSource s) => "time at the world's edge with no map: " + TitleFragment.Number(MaxNoMap(s)) + " (needed " + TitleFragment.Number(TitleFragment.Scaled(TitleCategory.Exploration, 5000f)) + ")"); obj[17] = TitleFragment.Threshold("deep_delving", TitleSlot.Epithet, TitleCategory.Exploration, Rarity.Rare, "Deep-Delving|Crypt-Crawling|Tomb-Haunting", (PlayerStatType)83, 50f); obj[18] = TitleFragment.Threshold("pathfinder", TitleSlot.Noun, TitleCategory.Exploration, Rarity.Uncommon, "Pathfinder|Trailblazer|Scout", (PlayerStatType)135, 15f); obj[19] = TitleFragment.Threshold("delver", TitleSlot.Noun, TitleCategory.Exploration, Rarity.Rare, "Delver|Tomb-Robber|Crypt-Looter", (PlayerStatType)134, 20f); obj[20] = TitleFragment.Threshold("cairnbreaker", TitleSlot.Noun, TitleCategory.Exploration, Rarity.Rare, "Cairnbreaker|Barrow-Digger|Hoard-Finder", (PlayerStatType)133, 25f); obj[21] = TitleFragment.Threshold("leviathan_waker", TitleSlot.Noun, TitleCategory.Exploration, Rarity.Epic, "Leviathan-Waker|Leviathan-Rider", (PlayerStatType)136, 3f); obj[22] = TitleFragment.Threshold("hollow_hills", TitleSlot.Domain, TitleCategory.Exploration, Rarity.Rare, "Hollow Hills|Dark Crypts|Burial Mounds", (PlayerStatType)83, 30f); obj[23] = TitleFragment.Threshold("far_shore", TitleSlot.Domain, TitleCategory.Exploration, Rarity.Rare, "Far Shore|World's Rim", (PlayerStatType)141, 6000f); obj[24] = TitleFragment.Threshold("kindly", TitleSlot.Epithet, TitleCategory.Taming, Rarity.Uncommon, "Kindly|Gentle-Handed|Ear-Scratching", (PlayerStatType)191, 100f); obj[25] = TitleFragment.Threshold("beast_gentling", TitleSlot.Epithet, TitleCategory.Taming, Rarity.Rare, "Beast-Gentling|Wild-Taming", (PlayerStatType)48, 25f); obj[26] = TitleFragment.Threshold("tamer", TitleSlot.Noun, TitleCategory.Taming, Rarity.Uncommon, "Tamer|Wrangler|Beast-Breaker", (PlayerStatType)48, 10f); obj[27] = TitleFragment.Threshold("beastfriend", TitleSlot.Noun, TitleCategory.Taming, Rarity.Rare, "Beastfriend|Wolf-Friend|Lox-Friend", (PlayerStatType)191, 400f); obj[28] = TitleFragment.Threshold("herdmaster", TitleSlot.Noun, TitleCategory.Taming, Rarity.Epic, "Herdmaster|Stockmaster", (PlayerStatType)48, 60f); obj[29] = TitleFragment.Threshold("quiet_herd", TitleSlot.Domain, TitleCategory.Taming, Rarity.Uncommon, "Quiet Herd|Wolf Pack", (PlayerStatType)48, 15f); obj[30] = TitleFragment.Threshold("line_wise", TitleSlot.Epithet, TitleCategory.Fishing, Rarity.Uncommon, "Line-Wise|Hook-Clever", (PlayerStatType)160, 50f); obj[31] = TitleFragment.Threshold("patient", TitleSlot.Epithet, TitleCategory.Fishing, Rarity.Rare, "Patient|Still-Waiting|Tide-Watching", (PlayerStatType)158, 300f); obj[32] = TitleFragment.Threshold("oft_cheated", TitleSlot.Epithet, TitleCategory.Fishing, Rarity.Rare, "Oft-Cheated|Fish-Mocked", (PlayerStatType)159, 100f); obj[33] = TitleFragment.Threshold("angler", TitleSlot.Noun, TitleCategory.Fishing, Rarity.Uncommon, "Angler|Fisher|Rod-Holder", (PlayerStatType)160, 25f); obj[34] = TitleFragment.Threshold("fishwright", TitleSlot.Noun, TitleCategory.Fishing, Rarity.Rare, "Fishwright|Fishmonger", (PlayerStatType)160, 200f); obj[35] = TitleFragment.Threshold("netmaster", TitleSlot.Noun, TitleCategory.Fishing, Rarity.Epic, "Netmaster|Sea-Harvester", (PlayerStatType)160, 500f); obj[36] = TitleFragment.Threshold("still_water", TitleSlot.Domain, TitleCategory.Fishing, Rarity.Uncommon, "Still Water|Quiet Pool|Deep Pool", (PlayerStatType)160, 60f); return obj; } } private static float MaxNoMap(IStatSource s) { return Math.Max(Math.Max(s.Get((PlayerStatType)142), s.Get((PlayerStatType)143)), Math.Max(s.Get((PlayerStatType)144), s.Get((PlayerStatType)145))); } } } namespace Byname.Stats { internal sealed class DeathLedger { private const string Prefix = "byname.died."; private const string ByPart = "by."; private const string InPart = "in."; internal static readonly DeathLedger Empty = new DeathLedger(new Dictionary<string, float>()); private readonly Dictionary<string, float> _counts; private DeathLedger(Dictionary<string, float> counts) { _counts = counts; } internal static DeathLedger Read(Player player, long? worldUid) { if ((Object)(object)player == (Object)null) { return Empty; } string text = Scope(worldUid); Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.Ordinal); foreach (KeyValuePair<string, string> customDatum in player.m_customData) { if (customDatum.Key.StartsWith(text, StringComparison.Ordinal)) { string text2 = customDatum.Key.Substring(text.Length); if ((text2.StartsWith("by.", StringComparison.Ordinal) || text2.StartsWith("in.", StringComparison.Ordinal)) && float.TryParse(customDatum.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { dictionary[text2] = result; } } } return new DeathLedger(dictionary); } internal float By(string creatureToken) { if (string.IsNullOrEmpty(creatureToken)) { return 0f; } if (_counts.TryGetValue("by." + creatureToken, out var value)) { return value; } string text = ((creatureToken[0] == '$') ? creatureToken.Substring(1) : ("$" + creatureToken)); if (!_counts.TryGetValue("by." + text, out var value2)) { return 0f; } return value2; } internal float In(string biome) { if (!_counts.TryGetValue("in." + biome, out var value)) { return 0f; } return value; } internal unsafe static void Record(Player player, long worldUid, string killerToken, Biome biome) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } string[] array = new string[2] { Scope(null), Scope(worldUid) }; foreach (string text in array) { if (!string.IsNullOrEmpty(killerToken)) { Increment(player, text + "by." + killerToken); } if ((int)biome != 0) { Increment(player, text + "in." + ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString()); } } } private static string Scope(long? worldUid) { if (!worldUid.HasValue) { return "byname.died."; } return "byname.died.w" + worldUid.Value.ToString(CultureInfo.InvariantCulture) + "."; } private static void Increment(Player player, string key) { player.m_customData.TryGetValue(key, out var value); float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result); player.m_customData[key] = (result + 1f).ToString(CultureInfo.InvariantCulture); } } internal interface IStatSource { float Get(PlayerStatType stat); float GetEnemyKills(string prefabName, KillModifiers modifier = (KillModifiers)0); string TopEnemy(); KillModifiers DominantKillStyle(float minimumShare = 0.6f); float GetDeathsBy(string creatureToken); float GetDeathsIn(string biome); } internal sealed class LifetimeStatSource : IStatSource { private readonly PlayerStats _stats; private readonly DeathLedger _deaths; internal PlayerStats Raw => _stats; internal LifetimeStatSource(PlayerProfile profile, DeathLedger deaths) { _stats = profile.m_playerStats[0]; _deaths = deaths ?? DeathLedger.Empty; } public float Get(PlayerStatType stat) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (!_stats.m_stats.TryGetValue(stat, out var value)) { return 0f; } return value; } public float GetEnemyKills(string prefabName, KillModifiers modifier = (KillModifiers)0) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)modifier; if (num < 0 || num >= _stats.m_enemyStats.Length) { return 0f; } Dictionary<string, float> dictionary = _stats.m_enemyStats[num]; if (dictionary == null || string.IsNullOrEmpty(prefabName)) { return 0f; } if (dictionary.TryGetValue(prefabName, out var value)) { return value; } string key = ((prefabName[0] == '$') ? prefabName.Substring(1) : ("$" + prefabName)); if (!dictionary.TryGetValue(key, out var value2)) { return 0f; } return value2; } public string TopEnemy() { Dictionary<string, float> dictionary = _stats.m_enemyStats[0]; if (dictionary == null || dictionary.Count == 0) { return null; } string result = null; float num = 0f; foreach (KeyValuePair<string, float> item in dictionary) { if (item.Value > num) { num = item.Value; result = item.Key; } } return result; } public KillModifiers DominantKillStyle(float minimumShare = 0.6f) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) KillModifiers[] array = new KillModifiers[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); Dictionary<KillModifiers, float> dictionary = ((IEnumerable<KillModifiers>)(object)array).ToDictionary((KillModifiers s) => s, (KillModifiers s) => Sum(_stats.m_enemyStats[s])); float num = dictionary.Values.Sum(); if (num <= 0f) { return (KillModifiers)0; } KeyValuePair<KillModifiers, float> keyValuePair = dictionary.OrderByDescending((KeyValuePair<KillModifiers, float> p) => p.Value).First(); if (!(keyValuePair.Value / num >= minimumShare)) { return (KillModifiers)0; } return keyValuePair.Key; } public float GetDeathsBy(string creatureToken) { return _deaths.By(creatureToken); } public float GetDeathsIn(string biome) { return _deaths.In(biome); } private static float Sum(Dictionary<string, float> d) { if (d == null) { return 0f; } float num = 0f; foreach (float value in d.Values) { num += value; } return num; } } internal sealed class WorldStatSource : IStatSource { private readonly LifetimeStatSource _lifetime; private readonly IReadOnlyDictionary<PlayerStatType, float> _baseline; private readonly DeathLedger _worldDeaths; internal WorldStatSource(LifetimeStatSource lifetime, IReadOnlyDictionary<PlayerStatType, float> baseline, DeathLedger worldDeaths) { _lifetime = lifetime; _baseline = baseline; _worldDeaths = worldDeaths ?? DeathLedger.Empty; } public float Get(PlayerStatType stat) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) float num = _lifetime.Get(stat); float value; float num2 = (_baseline.TryGetValue(stat, out value) ? value : 0f); float num3 = num - num2; if (!(num3 > 0f)) { return 0f; } return num3; } public float GetEnemyKills(string prefabName, KillModifiers modifier = (KillModifiers)0) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) return _lifetime.GetEnemyKills(prefabName, modifier); } public string TopEnemy() { return _lifetime.TopEnemy(); } public KillModifiers DominantKillStyle(float minimumShare = 0.6f) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) return _lifetime.DominantKillStyle(minimumShare); } public float GetDeathsBy(string creatureToken) { return _worldDeaths.By(creatureToken); } public float GetDeathsIn(string biome) { return _worldDeaths.In(biome); } } internal static class TitleState { private const string KeyEpoch = "byname.epoch"; private const string KeyDay = "byname.day"; private const string KeyTitle = "byname.title"; private const string KeyRarity = "byname.rarity"; private const string BaselinePrefix = "byname.base."; internal static int GetEpoch(Player player) { return ReadInt(player, "byname.epoch", 0); } internal static void SetEpoch(Player player, int epoch) { Write(player, "byname.epoch", epoch.ToString(CultureInfo.InvariantCulture)); } internal static int GetLastChangeDay(Player player) { return ReadInt(player, "byname.day", -1); } internal static void SetLastChangeDay(Player player, int day) { Write(player, "byname.day", day.ToString(CultureInfo.InvariantCulture)); } internal static string GetTitle(Player player) { if (!((Object)(object)player != (Object)null) || !player.m_customData.TryGetValue("byname.title", out var value)) { return null; } return value; } internal static int GetRarity(Player player) { return ReadInt(player, "byname.rarity", 1); } internal static void SetTitle(Player player, string title, int rarity) { Write(player, "byname.title", title ?? ""); Write(player, "byname.rarity", rarity.ToString(CultureInfo.InvariantCulture)); } internal static IReadOnlyDictionary<PlayerStatType, float> GetBaseline(Player player, long worldUid) { if ((Object)(object)player == (Object)null) { return null; } if (!player.m_customData.TryGetValue("byname.base." + worldUid, out var value)) { return null; } Dictionary<PlayerStatType, float> dictionary = new Dictionary<PlayerStatType, float>(); string[] array = value.Split(new char[1] { ';' }); foreach (string text in array) { if (text.Length != 0) { string[] array2 = text.Split(new char[1] { ':' }); if (array2.Length == 2 && int.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { dictionary[(PlayerStatType)result] = result2; } } } return dictionary; } internal static void SetBaseline(Player player, long worldUid, IEnumerable<PlayerStatType> tracked, LifetimeStatSource lifetime) { if (!((Object)(object)player == (Object)null)) { IEnumerable<string> source = from s in tracked.Distinct() select ((int)s).ToString(CultureInfo.InvariantCulture) + ":" + lifetime.Get(s).ToString("R", CultureInfo.InvariantCulture); Write(player, "byname.base." + worldUid, string.Join(";", source.ToArray())); } } private static int ReadInt(Player player, string key, int fallback) { if ((Object)(object)player == (Object)null) { return fallback; } if (!player.m_customData.TryGetValue(key, out var value) || !int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return fallback; } return result; } private static void Write(Player player, string key, string value) { if (!((Object)(object)player == (Object)null)) { player.m_customData[key] = value; } } } } namespace Byname.Patches { [HarmonyPatch(typeof(InventoryGui), "UpdateCharacterStats")] internal static class CharacterPanelPatch { private static void Postfix(InventoryGui __instance, Player player) { if (BynameConfig.Enabled.Value && BynameConfig.ShowOnCharacterPanel.Value && !((Object)(object)__instance.m_playerName == (Object)null) && !((Object)(object)player == (Object)null)) { int rarity; string text = TitleNet.Read(player, out rarity); if (!string.IsNullOrEmpty(text)) { TMP_Text playerName = __instance.m_playerName; playerName.text = playerName.text + "\n<size=70%>" + TitleService.Decorate(text, rarity) + "</size>"; } } } } [HarmonyPatch(typeof(Character), "ApplyDamage")] internal static class RecentAttackerTracker { internal const float BlameWindowSeconds = 45f; internal static string LastToken; internal static float LastTime = float.NegativeInfinity; private static void Prefix(Character __instance, HitData hit) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && hit != null) { Character attacker = hit.GetAttacker(); if (!((Object)(object)attacker == (Object)null) && !attacker.IsPlayer()) { LastToken = attacker.m_name; LastTime = Time.time; } } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class DeathRecorder { private static void Prefix(Player __instance) { //IL_0033: 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_0058: 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) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || (Object)(object)((Character)__instance).m_nview == (Object)null || !((Character)__instance).m_nview.IsOwner()) { return; } try { string text = ResolveKiller(__instance); Biome currentBiome = __instance.GetCurrentBiome(); long worldUid = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldUID() : 0); DeathLedger.Record(__instance, worldUid, text, currentBiome); RecentAttackerTracker.LastTime = float.NegativeInfinity; BynamePlugin.LogVerbose(string.Format("death recorded: killer {0}, biome {1}", text ?? "<none>", currentBiome)); } catch (Exception ex) { BynamePlugin.LogError("could not record death: " + ex.Message); }