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 GooCosmetics v1.0.1
BepInEx/plugins/GooCosmetics/GooCosmetics.dll
Decompiled 3 weeks 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.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using GUIFramework; using HarmonyLib; using TMPro; using Unity.Collections; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; using Valheim.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("GooCosmetics")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1+00c009784b5e85fd4acae52dafd6bf559ee16f23")] [assembly: AssemblyProduct("GooCosmetics")] [assembly: AssemblyTitle("GooCosmetics")] [assembly: AssemblyVersion("1.0.1.0")] namespace GooCosmetics; internal enum ItemParticleMode { Donor, Original, None, Selected } internal sealed class Appearance { internal const string ItemKey = "gnls.GooCosmetics.illusion.v1"; internal const int MaxLength = 2100; internal readonly string Original; internal readonly string Donor; internal readonly int Variant; internal readonly int Quality; internal readonly string ParticleDonor; internal readonly int ParticleVariant; internal readonly int ParticleQuality; internal readonly bool PreserveParticles; internal readonly bool NoParticles; internal readonly bool Hidden; internal ItemParticleMode ParticleMode { get { if (!NoParticles) { if (!PreserveParticles) { if (ParticleDonor == null) { return ItemParticleMode.Donor; } return ItemParticleMode.Selected; } return ItemParticleMode.Original; } return ItemParticleMode.None; } } internal CollectedAppearance ParticleLook { get { if (ParticleDonor != null) { return new CollectedAppearance(ParticleDonor, ParticleVariant, ParticleQuality); } return null; } } internal Appearance(string original, string donor, int variant, int quality, bool preserveParticles = false, bool noParticles = false, string particleDonor = null, int particleVariant = 0, int particleQuality = 1) { Original = original; Donor = donor; Variant = variant; Quality = quality; PreserveParticles = preserveParticles; NoParticles = noParticles; ParticleDonor = particleDonor; ParticleVariant = particleVariant; ParticleQuality = particleQuality; } private Appearance(string original) : this(original, original, 0, 1, preserveParticles: false, noParticles: true) { Hidden = true; } internal static Appearance NoLook(string original) { return new Appearance(original); } private static bool ValidName(string name) { if (string.IsNullOrWhiteSpace(name) || name.Length > 160) { return false; } for (int i = 0; i < name.Length; i++) { if (char.IsControl(name[i])) { return false; } } return true; } private static string Encode(string value) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); } internal string Encode() { if (Hidden) { return "4|" + Encode(Original); } string[] obj = new string[12] { (ParticleDonor == null) ? "2|" : "3|", Encode(Original), "|", Encode(Donor), "|", null, null, null, null, null, null, null }; int variant = Variant; obj[5] = variant.ToString(CultureInfo.InvariantCulture); obj[6] = "|"; variant = Quality; obj[7] = variant.ToString(CultureInfo.InvariantCulture); obj[8] = "|"; obj[9] = (PreserveParticles ? "1" : "0"); obj[10] = "|"; obj[11] = (NoParticles ? "1" : "0"); string text = string.Concat(obj); if (ParticleDonor != null) { string[] obj2 = new string[7] { text, "|", Encode(ParticleDonor), "|", null, null, null }; variant = ParticleVariant; obj2[4] = variant.ToString(CultureInfo.InvariantCulture); obj2[5] = "|"; variant = ParticleQuality; obj2[6] = variant.ToString(CultureInfo.InvariantCulture); return string.Concat(obj2); } return text; } internal static bool TryParse(string text, out Appearance result) { result = null; if (string.IsNullOrEmpty(text) || text.Length > 2100) { return false; } string[] array = text.Split(new char[1] { '|' }); if (array.Length == 2 && array[0] == "4") { try { string text2 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(array[1])); if (!ValidName(text2)) { return false; } result = NoLook(text2); return true; } catch (FormatException) { return false; } catch (DecoderFallbackException) { return false; } } bool num = array.Length == 6 && array[0] == "1"; bool flag = array.Length == 7 && array[0] == "2"; bool flag2 = array.Length == 10 && array[0] == "3"; if ((!num && !flag && !flag2) || (array[5] != "0" && array[5] != "1") || ((flag || flag2) && array[6] != "0" && array[6] != "1") || (flag2 && (array[5] != "0" || array[6] != "0"))) { return false; } if (!int.TryParse(array[3], NumberStyles.None, CultureInfo.InvariantCulture, out var result2) || result2 > 65535 || !int.TryParse(array[4], NumberStyles.None, CultureInfo.InvariantCulture, out var result3) || result3 < 1 || result3 > 65535) { return false; } try { UTF8Encoding uTF8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); string text3 = uTF8Encoding.GetString(Convert.FromBase64String(array[1])); string text4 = uTF8Encoding.GetString(Convert.FromBase64String(array[2])); if (!ValidName(text3) || !ValidName(text4)) { return false; } string text5 = null; int result4 = 0; int result5 = 1; if (flag2) { text5 = uTF8Encoding.GetString(Convert.FromBase64String(array[7])); if (!ValidName(text5) || !int.TryParse(array[8], NumberStyles.None, CultureInfo.InvariantCulture, out result4) || result4 > 65535 || !int.TryParse(array[9], NumberStyles.None, CultureInfo.InvariantCulture, out result5) || result5 < 1 || result5 > 65535) { return false; } } result = new Appearance(text3, text4, result2, result3, array[5] == "1", flag && array[6] == "1", text5, result4, result5); return true; } catch (FormatException) { return false; } catch (DecoderFallbackException) { return false; } } } internal static class AppearanceCollection { private sealed class State { internal AppearanceJournal Journal; internal bool Writable; internal bool Dirty; internal AppearanceJournal Granted; internal bool GrantedWritable; internal bool GrantedDirty; internal AppearanceJournal Revoked; internal bool RevokedWritable; internal bool RevokedDirty; internal int Revision; internal int ParticleJournalRevision = -1; internal int ParticleVisualRevision = -1; internal bool HasParticles; internal HashSet<string> Unlocked; internal int UnlockRevision = -1; internal int UnlockVisualRevision = -1; internal Dictionary<string, CollectedAppearance> Held; internal int HeldRevision = -1; internal int HeldVisualRevision = -1; internal readonly int Id = ++lastStateId; } internal sealed class LoadState { internal string Previous; internal string PreviousGranted; internal string PreviousRevoked; } private sealed class Observation { internal bool Valid; internal bool Gear; internal int Definitions; internal int StateId; internal int Revision; internal GameObject Prefab; internal SharedData Shared; internal SharedData Own; internal int MaxStack; internal int Variant; internal int Quality; internal int OwnMaxStack; internal int OwnType; internal string Raw; internal readonly List<Classification.Use> Classified = new List<Classification.Use>(); internal readonly List<string> Normalized = new List<string>(); internal readonly List<bool> Unscaled = new List<bool>(); internal void Note(string prefab, bool unscaled) { if (!Normalized.Contains(prefab)) { Normalized.Add(prefab); Unscaled.Add(unscaled); } } } private static int lastStateId; private static readonly ConditionalWeakTable<Player, State> States = new ConditionalWeakTable<Player, State>(); private static readonly HashSet<Player> Loading = new HashSet<Player>(); internal const string GrantedKey = "gnls.GooCosmetics.granted.v1"; internal const string RevokedKey = "gnls.GooCosmetics.revoked.v1"; private static readonly ConditionalWeakTable<ItemData, Observation> Observations = new ConditionalWeakTable<ItemData, Observation>(); private static Observation noting; internal static readonly string[] FreeLooks = new string[2] { "HelmetOdin", "CapeOdin" }; private static readonly Dictionary<string, string> Companions = new Dictionary<string, string>(StringComparer.Ordinal) { { "SwordIron", "SwordIronFire" } }; private static readonly Dictionary<string, CollectedAppearance> NoneHeld = new Dictionary<string, CollectedAppearance>(); internal static int FullChecks { get; private set; } internal static LoadState BeginLoad(Player player) { if (!Object.op_Implicit((Object)(object)player)) { return null; } player.m_customData.TryGetValue("gnls.GooCosmetics.collection.v1", out var value); player.m_customData.TryGetValue("gnls.GooCosmetics.granted.v1", out var value2); player.m_customData.TryGetValue("gnls.GooCosmetics.revoked.v1", out var value3); LoadState result = new LoadState { Previous = value, PreviousGranted = value2, PreviousRevoked = value3 }; player.m_customData.Remove("gnls.GooCosmetics.collection.v1"); player.m_customData.Remove("gnls.GooCosmetics.granted.v1"); player.m_customData.Remove("gnls.GooCosmetics.revoked.v1"); States.Remove(player); Loading.Add(player); return result; } internal static void EndLoad(Player player, LoadState snapshot, bool succeeded) { if (Object.op_Implicit((Object)(object)player) && snapshot != null) { Loading.Remove(player); States.Remove(player); if (succeeded) { Loaded(player); return; } Restore(player, "gnls.GooCosmetics.collection.v1", snapshot.Previous); Restore(player, "gnls.GooCosmetics.granted.v1", snapshot.PreviousGranted); Restore(player, "gnls.GooCosmetics.revoked.v1", snapshot.PreviousRevoked); } } internal static void Restore(Player player, string key, string previous) { if (previous != null && !player.m_customData.ContainsKey(key)) { player.m_customData[key] = previous; } } private static State Get(Player player) { if (!Object.op_Implicit((Object)(object)player)) { return null; } if (States.TryGetValue(player, out var value)) { return value; } player.m_customData.TryGetValue("gnls.GooCosmetics.collection.v1", out var value2); AppearanceJournal journal; bool flag = AppearanceJournal.TryDecode(value2, out journal); player.m_customData.TryGetValue("gnls.GooCosmetics.granted.v1", out var value3); AppearanceJournal journal2; bool flag2 = AppearanceJournal.TryDecode(value3, out journal2); player.m_customData.TryGetValue("gnls.GooCosmetics.revoked.v1", out var value4); AppearanceJournal journal3; bool flag3 = AppearanceJournal.TryDecode(value4, out journal3); value = new State { Journal = journal, Writable = flag, Granted = journal2, GrantedWritable = flag2, Revoked = journal3, RevokedWritable = flag3 }; States.Add(player, value); if (flag && flag2 && flag3) { Recheat(value); } if (!flag) { Plugin.WarnOnce("Saved appearances", new FormatException("The character's appearance collection could not be read. Its saved value has been preserved.")); } if (!flag2) { Plugin.WarnOnce("Unlocked appearances", new FormatException("The looks added by goocosmetics unlock could not be read. Their saved value has been preserved.")); } if (!flag3) { Plugin.WarnOnce("Locked appearances", new FormatException("The looks taken away by goocosmetics lock could not be read. Their saved value has been preserved.")); } return value; } private static void Recheat(State state) { List<CollectedAppearance> list = null; foreach (CollectedAppearance entry in state.Journal.Entries) { if (MobLooks.TryGet(entry.Prefab, out var look) && look.CommandOnly && !look.Recolor) { (list ?? (list = new List<CollectedAppearance>())).Add(entry); } } if (list == null) { return; } foreach (CollectedAppearance item in list) { if (state.Granted.Contains(item) || state.Granted.Add(item)) { state.Journal.Remove(item); state.GrantedDirty = (state.Dirty = true); } } } internal static int Revision(Player player) { return Get(player)?.Revision ?? 0; } internal static bool Readable(Player player) { return Get(player)?.Writable ?? false; } internal static bool AllReadable(Player player) { State state = Get(player); if (state != null && state.Writable && state.GrantedWritable) { return state.RevokedWritable; } return false; } internal static bool HasParticles(Player player) { State state = Get(player); if (state == null || !state.Writable || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return false; } if (state.ParticleJournalRevision != state.Revision || state.ParticleVisualRevision != Plugin.VisualRevision) { state.HasParticles = false; foreach (CollectedAppearance item in Owned(state)) { if (ParticleSources.Available(item)) { state.HasParticles = true; break; } } if (!state.HasParticles) { foreach (CollectedAppearance item2 in UnlockedLooks(player)) { if (ParticleSources.Available(item2)) { state.HasParticles = true; break; } } } state.ParticleJournalRevision = state.Revision; state.ParticleVisualRevision = Plugin.VisualRevision; } return state.HasParticles; } internal static void Loaded(Player player) { if (Object.op_Implicit((Object)(object)player)) { States.Remove(player); ObserveInventory(player); Flush(player); } } internal static void ObserveInventory(Player player) { if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player) || ((Humanoid)player).GetInventory() == null) { return; } foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems()) { Observe(player, allItem); } } internal static void Observe(Player player, ItemData item) { if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player) || item == null || item.m_stack != 1 || !((Humanoid)player).GetInventory().ContainsItem(item)) { return; } Observation orCreateValue = Observations.GetOrCreateValue(item); string raw = Illusions.Raw(item); if (Unchanged(orCreateValue, player, item, raw)) { return; } orCreateValue.Valid = false; orCreateValue.Classified.Clear(); orCreateValue.Normalized.Clear(); orCreateValue.Unscaled.Clear(); Observation observation = noting; List<Classification.Use> noted = Classification.Noted; noting = orCreateValue; Classification.Noted = orCreateValue.Classified; try { FullChecks++; orCreateValue.Definitions = Classification.Definitions; orCreateValue.Prefab = item.m_dropPrefab; orCreateValue.Shared = item.m_shared; orCreateValue.MaxStack = item.m_shared?.m_maxStackSize ?? 0; orCreateValue.Variant = item.m_variant; orCreateValue.Quality = item.m_quality; orCreateValue.Raw = raw; Check(player, item, raw, orCreateValue); orCreateValue.Valid = true; } finally { noting = observation; Classification.Noted = noted; } } private static bool Unchanged(Observation last, Player player, ItemData item, string raw) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Invalid comparison between Unknown and I4 if (!last.Valid || last.Definitions != Classification.Definitions || last.Prefab != item.m_dropPrefab || last.Shared != item.m_shared || (item.m_shared != null && item.m_shared.m_maxStackSize != last.MaxStack) || last.Variant != item.m_variant || last.Quality != item.m_quality || !string.Equals(last.Raw, raw)) { return false; } if (last.Gear) { if (!States.TryGetValue(player, out var value) || value.Id != last.StateId || value.Revision != last.Revision) { return false; } } else { SharedData val = PrefabShared(item.m_dropPrefab); if (val != last.Own || (val != null && (val.m_maxStackSize != last.OwnMaxStack || (int)val.m_itemType != last.OwnType))) { return false; } } foreach (Classification.Use item2 in last.Classified) { if (Classification.Of(item2.Prefab) != item2.Signature) { return false; } } for (int i = 0; i < last.Normalized.Count; i++) { if (Unscaled(last.Normalized[i]) != last.Unscaled[i]) { return false; } } return true; } private static SharedData PrefabShared(GameObject prefab) { if (!Object.op_Implicit((Object)(object)prefab)) { return null; } return prefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared; } private static void Check(Player player, ItemData item, string raw, Observation last) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (!Classification.Eligible(item) && ParticleSources.Plan(item.m_dropPrefab) == null) { last.Gear = false; last.Own = PrefabShared(item.m_dropPrefab); last.OwnMaxStack = last.Own?.m_maxStackSize ?? 0; SharedData own = last.Own; last.OwnType = ((own != null) ? ((int)own.m_itemType) : 0); } else { State state = Get(player); last.Gear = true; last.StateId = state.Id; Collect(state, item, raw); last.Revision = state.Revision; } } private static void Collect(State state, ItemData item, string raw) { if (!state.Writable) { return; } CollectedAppearance collectedAppearance = NativeLook(item); if (!CommandOnly(collectedAppearance.Prefab)) { Add(state, collectedAppearance); } if (!string.IsNullOrEmpty(raw) && Illusions.TryResolve(raw, StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name), out var appearance, out var _) && !appearance.Hidden) { CollectedAppearance collectedAppearance2 = Normalize(new CollectedAppearance(appearance.Donor, appearance.Variant, appearance.Quality)); if (state.GrantedWritable && state.RevokedWritable && !FromUnlockOnly(state, collectedAppearance2) && !CommandOnly(collectedAppearance2.Prefab)) { Add(state, collectedAppearance2); } } } private static bool CommandOnly(string prefab) { if (MobLooks.TryGet(prefab, out var look)) { return look.CommandOnly; } return false; } private static void Add(State state, CollectedAppearance look) { if (state.Journal.Add(look)) { state.Dirty = true; state.Revision++; if (state.RevokedWritable && state.Revoked.Remove(look)) { state.RevokedDirty = true; } } } private static bool FromUnlockOnly(State state, CollectedAppearance look, bool revoked = true) { if ((state.Granted.Count == 0 && (!revoked || state.Revoked.Count == 0)) || state.Journal.Contains(look)) { return false; } if (state.Granted.Contains(look) || (revoked && state.Revoked.Contains(look))) { return true; } if (!Companions.ContainsValue(look.Prefab)) { return false; } bool flag = false; foreach (CollectedAppearance item in Owned(state)) { if (CompanionOf(item.Prefab) == look.Prefab && look.Equals(Normalize(new CollectedAppearance(look.Prefab, 0, item.Quality)))) { if (state.Journal.Contains(item)) { return false; } flag = true; } } if (flag || !revoked) { return flag; } foreach (CollectedAppearance entry in state.Revoked.Entries) { if (CompanionOf(entry.Prefab) == look.Prefab && look.Equals(Normalize(new CollectedAppearance(look.Prefab, 0, entry.Quality)))) { return true; } } return false; } internal static bool Cheated(Player player, CollectedAppearance look) { State state = Get(player); if (look == null || !look.Valid || state == null || !state.Writable || state.Granted.Count == 0) { return false; } CollectedAppearance collectedAppearance = Normalize(look); if (MobLooks.TryGet(collectedAppearance.Prefab, out var look2)) { if (state.Journal.Contains(collectedAppearance)) { return false; } string[] unlocks = look2.Unlocks; for (int i = 0; i < unlocks.Length; i++) { string text = Native.PrefabItem(unlocks[i])?.m_shared?.m_name; if (text != null && player.IsMaterialKnown(text)) { return false; } } } return FromUnlockOnly(state, collectedAppearance, revoked: false); } internal static bool FirstUnlockItem(Player player, ItemData item) { string text = item?.m_shared?.m_name; if (Object.op_Implicit((Object)(object)player) && text != null && !player.IsMaterialKnown(text) && Object.op_Implicit((Object)(object)item.m_dropPrefab)) { return MobLooks.IsUnlockItem(((Object)item.m_dropPrefab).name); } return false; } internal static void ObserveUnlock(Player player, ItemData item) { if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player)) { return; } object prefab; if (item == null) { prefab = null; } else { GameObject dropPrefab = item.m_dropPrefab; prefab = ((dropPrefab != null) ? ((Object)dropPrefab).name : null); } if (MobLooks.IsUnlockItem((string)prefab)) { State state = Get(player); if (state != null) { state.Revision++; } } } private static HashSet<string> UnlockedSet(Player player, State state) { if (state.Unlocked != null && state.UnlockRevision == state.Revision && state.UnlockVisualRevision == Plugin.VisualRevision) { return state.Unlocked; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); if (Object.op_Implicit((Object)(object)ObjectDB.instance)) { foreach (MobLook item in MobLooks.Catalog) { string[] unlocks = item.Unlocks; for (int i = 0; i < unlocks.Length; i++) { string text = Native.PrefabItem(unlocks[i])?.m_shared?.m_name; if (text != null && player.IsMaterialKnown(text)) { hashSet.Add(item.Prefab); break; } } } } foreach (CollectedAppearance item2 in Owned(state)) { if (MobLooks.TryGet(item2.Prefab, out var _)) { hashSet.Add(item2.Prefab); } } state.Unlocked = hashSet; state.UnlockRevision = state.Revision; state.UnlockVisualRevision = Plugin.VisualRevision; return hashSet; } private static IEnumerable<CollectedAppearance> Owned(State state) { foreach (CollectedAppearance entry in state.Journal.Entries) { yield return entry; } foreach (CollectedAppearance entry2 in state.Granted.Entries) { yield return entry2; } } internal static bool IsFree(string prefab) { if (prefab != null) { return Array.IndexOf(FreeLooks, prefab) >= 0; } return false; } internal static string CompanionOf(string prefab) { if (prefab == null || !Companions.TryGetValue(prefab, out var value)) { return null; } return value; } internal static string CompanionSource(string prefab) { foreach (KeyValuePair<string, string> companion in Companions) { if (companion.Value == prefab) { return companion.Key; } } return null; } internal static bool Unlocked(Player player, string prefab) { State state = Get(player); if (state != null && state.Writable && prefab != null) { if (!IsFree(prefab)) { return UnlockedSet(player, state).Contains(prefab); } return true; } return false; } internal static IEnumerable<CollectedAppearance> UnlockedLooks(Player player) { State state = Get(player); if (state == null || !state.Writable || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { yield break; } HashSet<string> unlocked = UnlockedSet(player, state); foreach (MobLook item in MobLooks.Catalog) { if (unlocked.Contains(item.Prefab) && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(item.Prefab))) { yield return Normalize(new CollectedAppearance(item.Prefab, 0, 1)); } } string[] freeLooks = FreeLooks; foreach (string text in freeLooks) { if (Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(text))) { yield return Normalize(new CollectedAppearance(text, 0, 1)); } } foreach (CollectedAppearance item2 in Owned(state)) { string text2 = CompanionOf(item2.Prefab); if (text2 != null && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(text2))) { yield return Normalize(new CollectedAppearance(text2, 0, item2.Quality)); } } } internal static bool Grant(Player player, IEnumerable<CollectedAppearance> looks, out List<CollectedAppearance> added, out int full) { added = new List<CollectedAppearance>(); full = 0; State state = Get(player); if (state == null || !state.Writable || !state.GrantedWritable || !state.RevokedWritable || Loading.Contains(player)) { return false; } HashSet<CollectedAppearance> hashSet = new HashSet<CollectedAppearance>(); foreach (CollectedAppearance item in Owned(state)) { hashSet.Add(Normalize(item)); } foreach (CollectedAppearance item2 in UnlockedLooks(player)) { hashSet.Add(item2); } foreach (CollectedAppearance look in looks) { CollectedAppearance collectedAppearance = Normalize(look); if (collectedAppearance == null || !collectedAppearance.Valid || !hashSet.Add(collectedAppearance)) { continue; } if (!state.Granted.Add(collectedAppearance)) { full++; continue; } added.Add(collectedAppearance); if (state.Revoked.Remove(collectedAppearance)) { state.RevokedDirty = true; } } if (added.Count > 0) { state.GrantedDirty = true; state.Revision++; Flush(player); } return true; } internal static bool Revoke(Player player, string prefab, out int removed, out int unremembered) { removed = (unremembered = 0); State state = Get(player); if (state == null || !state.GrantedWritable || !state.RevokedWritable || Loading.Contains(player)) { return false; } List<CollectedAppearance> list = new List<CollectedAppearance>(); foreach (CollectedAppearance entry in state.Granted.Entries) { if (prefab == null || entry.Prefab == prefab) { list.Add(entry); } } foreach (CollectedAppearance item in list) { if (!state.Granted.Remove(item)) { continue; } removed++; if (!state.Journal.Contains(item)) { if (state.Revoked.Add(item)) { state.RevokedDirty = true; } else if (!state.Revoked.Contains(item)) { unremembered++; } } } if (removed > 0) { state.GrantedDirty = true; state.Revision++; Flush(player); } return true; } internal static IEnumerable<string> GrantedPrefabs(Player player) { State state = Get(player); if (state == null) { yield break; } HashSet<string> names = new HashSet<string>(StringComparer.Ordinal); foreach (CollectedAppearance entry in state.Granted.Entries) { if (names.Add(entry.Prefab)) { yield return entry.Prefab; } } } internal static bool GrantedOnly(Player player, CollectedAppearance look) { State state = Get(player); if (look == null || state == null || !state.Writable) { return false; } CollectedAppearance entry = Normalize(look); if (state.Granted.Contains(entry)) { return !state.Journal.Contains(entry); } return false; } internal static void Flush(Player player) { if (!Object.op_Implicit((Object)(object)player) || Loading.Contains(player)) { return; } State state = Get(player); if (state == null) { return; } if (state.Writable && state.Dirty) { player.m_customData["gnls.GooCosmetics.collection.v1"] = state.Journal.Encode(); state.Dirty = false; } if (state.GrantedWritable && state.GrantedDirty) { if (state.Granted.Count == 0) { player.m_customData.Remove("gnls.GooCosmetics.granted.v1"); } else { player.m_customData["gnls.GooCosmetics.granted.v1"] = state.Granted.Encode(); } state.GrantedDirty = false; } if (state.RevokedWritable && state.RevokedDirty) { if (state.Revoked.Count == 0) { player.m_customData.Remove("gnls.GooCosmetics.revoked.v1"); } else { player.m_customData["gnls.GooCosmetics.revoked.v1"] = state.Revoked.Encode(); } state.RevokedDirty = false; } } internal static void Saving(Player player) { ObserveInventory(player); Flush(player); } internal static CollectedAppearance Normalize(CollectedAppearance look) { if (look == null) { return null; } bool flag = Unscaled(look.Prefab); noting?.Note(look.Prefab, flag); if (!flag) { return look; } return new CollectedAppearance(look.Prefab, look.Variant, 1); } private static bool Unscaled(string prefab) { ItemData val = Native.PrefabItem(prefab); if (val?.m_shared != null) { return val.m_shared.m_scaleByQuality == 0f; } return false; } internal static CollectedAppearance NativeLook(ItemData item) { if ((Object)(object)item?.m_dropPrefab == (Object)null) { return null; } return Normalize(new CollectedAppearance(((Object)item.m_dropPrefab).name, item.m_variant, item.m_quality)); } internal static CollectedAppearance VisibleLook(ItemData item) { if ((Object)(object)item?.m_dropPrefab == (Object)null) { return null; } if (!Illusions.TryResolve(Illusions.Raw(item), StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name), out var appearance, out var _) || appearance.Hidden) { return NativeLook(item); } return Normalize(new CollectedAppearance(appearance.Donor, appearance.Variant, appearance.Quality)); } internal static bool Contains(Player player, CollectedAppearance look) { State state = Get(player); if (look == null || !look.Valid || state == null || !state.Writable) { return false; } CollectedAppearance collectedAppearance = Normalize(look); if (state.Journal.Contains(collectedAppearance) || state.Granted.Contains(collectedAppearance)) { return true; } if (Unlocked(player, collectedAppearance.Prefab) && collectedAppearance.Equals(Normalize(new CollectedAppearance(collectedAppearance.Prefab, 0, 1)))) { return true; } foreach (CollectedAppearance item in Owned(state)) { if (collectedAppearance.Equals(Normalize(item)) || (CompanionOf(item.Prefab) == collectedAppearance.Prefab && collectedAppearance.Equals(Normalize(new CollectedAppearance(collectedAppearance.Prefab, 0, item.Quality))))) { return true; } } return false; } internal static Dictionary<string, CollectedAppearance> HeldByPrefab(Player player) { State state = Get(player); if (state == null || !state.Writable || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return NoneHeld; } if (state.Held != null && state.HeldRevision == state.Revision && state.HeldVisualRevision == Plugin.VisualRevision) { return state.Held; } Dictionary<string, CollectedAppearance> dictionary = new Dictionary<string, CollectedAppearance>(StringComparer.Ordinal); foreach (CollectedAppearance item in Owned(state)) { Keep(dictionary, Normalize(item)); } foreach (CollectedAppearance item2 in UnlockedLooks(player)) { Keep(dictionary, item2); } state.Held = dictionary; state.HeldRevision = state.Revision; state.HeldVisualRevision = Plugin.VisualRevision; return dictionary; } private static void Keep(Dictionary<string, CollectedAppearance> held, CollectedAppearance look) { if (look != null && look.Valid && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(look.Prefab)) && (!held.TryGetValue(look.Prefab, out var value) || look.Variant < value.Variant || (look.Variant == value.Variant && look.Quality < value.Quality))) { held[look.Prefab] = look; } } internal static List<CollectedAppearance> Matching(Player player, ItemData target, bool particles = false) { if (particles) { return ParticleEffects.Matching(player, target); } List<CollectedAppearance> list = new List<CollectedAppearance>(); State state = Get(player); if (state == null || !state.Writable || !Classification.Eligible(target) || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return list; } HashSet<CollectedAppearance> hashSet = new HashSet<CollectedAppearance>(); List<CollectedAppearance> list2 = new List<CollectedAppearance>(Owned(state)); list2.AddRange(UnlockedLooks(player)); foreach (CollectedAppearance item in list2) { CollectedAppearance collectedAppearance = Normalize(item); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(collectedAppearance.Prefab); if (Object.op_Implicit((Object)(object)itemPrefab) && hashSet.Add(collectedAppearance) && Fits(target, itemPrefab)) { list.Add(collectedAppearance); } } AppearanceOrder.Sort(list); return list; } internal static List<CollectedAppearance> Locked(Player player, ItemData target, bool particles = false) { if (particles) { return ParticleEffects.Locked(player, target); } List<CollectedAppearance> list = new List<CollectedAppearance>(); State state = Get(player); if (state == null || !state.Writable || !Classification.Eligible(target) || !Object.op_Implicit((Object)(object)ObjectDB.instance)) { return list; } HashSet<string> hashSet = UnlockedSet(player, state); foreach (MobLook item in MobLooks.Catalog) { GameObject val = ((hashSet.Contains(item.Prefab) || item.CommandOnly) ? null : ObjectDB.instance.GetItemPrefab(item.Prefab)); CollectedAppearance collectedAppearance = (Object.op_Implicit((Object)(object)val) ? Normalize(new CollectedAppearance(item.Prefab, 0, 1)) : null); if (collectedAppearance != null && Fits(target, val)) { list.Add(collectedAppearance); } } AppearanceOrder.Sort(list); return list; } private static bool Fits(ItemData target, GameObject prefab) { if (Classification.PairPrefabs(target.m_dropPrefab, prefab, out var _)) { return VisualCapabilities.Listable(prefab, Classification.Of(prefab).Kind); } return false; } internal static ItemData Preview(CollectedAppearance look) { if (look == null || !look.Valid || !Object.op_Implicit((Object)(object)ObjectDB.instance) || MobLooks.TryGet(look.Prefab, out var _)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(look.Prefab); ItemData val = ((!Object.op_Implicit((Object)(object)itemPrefab)) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData); if (val?.m_shared == null) { return null; } ItemData obj = val.Clone(); obj.m_customData.Clear(); obj.m_dropPrefab = itemPrefab; obj.m_stack = 1; obj.m_variant = look.Variant; obj.m_quality = look.Quality; obj.m_equipped = false; obj.m_durability = obj.GetMaxDurability(); return obj; } internal static string Name(CollectedAppearance look) { if (look == null) { return ""; } if (MobLooks.TryGet(look.Prefab, out var look2) && !look2.Trophy) { if (!look2.Recolor) { return look2.Name; } return ((look2.Source == "Shadow") ? "Shadow " : "Fallen ") + ItemName(look2.Original); } return ItemName(look.Prefab); } internal static string ItemName(string prefab) { string text = Native.PrefabItem(prefab)?.m_shared?.m_name ?? prefab; if (Localization.instance == null) { return text; } return Localization.instance.Localize(text); } internal static Sprite Icon(CollectedAppearance look) { Sprite badge; return Icon(look, out badge); } internal static Sprite Icon(CollectedAppearance look, out Sprite badge, bool render = true, GearKind kind = GearKind.Unsupported) { badge = null; if (look == null) { return null; } if (!MobLooks.TryGet(look.Prefab, out var look2)) { return PrefabIcon(look.Prefab, look.Variant); } if (look2.Trophy) { return PrefabIcon(look2.Prefab, 0); } Sprite val = UnlockIcon(look2); Sprite val2 = (look2.Recolor ? PrefabIcon(look2.Original, 0) : MonsterIcon(look2, render, kind)); if (!Object.op_Implicit((Object)(object)val2)) { if (!Object.op_Implicit((Object)(object)val)) { return PrefabIcon(look2.Prefab, 0); } return val; } badge = val; return val2; } internal static bool IconPending(CollectedAppearance look, GearKind kind = GearKind.Unsupported) { if (look != null && MobLooks.TryGet(look.Prefab, out var look2) && !look2.Recolor && !look2.Trophy) { return !MobLookIcons.Ready(look2.Prefab, IconFraming.Layout(look2, kind)); } return false; } private static Sprite MonsterIcon(MobLook mob, bool render, GearKind kind) { IconLayout layout = IconFraming.Layout(mob, kind); if (!render && !MobLookIcons.Ready(mob.Prefab, layout)) { return null; } GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(mob.Prefab) : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } return MobLookIcons.Get(val, layout); } private static Sprite PrefabIcon(string name, int variant) { GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(name) : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } return VisualCapabilities.Icon(val, variant); } private static Sprite UnlockIcon(MobLook mob) { Sprite val = null; if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { return null; } string[] unlocks = mob.Unlocks; foreach (string text in unlocks) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text); Sprite val2 = (Object.op_Implicit((Object)(object)itemPrefab) ? VisualCapabilities.Icon(itemPrefab, 0) : null); if (Object.op_Implicit((Object)(object)val2)) { if (!Object.op_Implicit((Object)(object)val)) { val = val2; } string text2 = Native.PrefabItem(text)?.m_shared?.m_name; if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && text2 != null && Player.m_localPlayer.IsMaterialKnown(text2)) { return val2; } } } return val; } internal static bool Describe(CollectedAppearance look, out string topic, out string text) { topic = (text = null); if (look == null || !MobLooks.TryGet(look.Prefab, out var look2)) { return false; } topic = Name(look); List<string> list = new List<string>(); string[] unlocks = look2.Unlocks; foreach (string prefab in unlocks) { list.Add(ItemName(prefab)); } List<string> list2 = new List<string>(); if (look2.Recolor) { list2.Add("the same items as the " + ItemName(look2.Original)); } else { GearKind[] families = look2.Families; foreach (GearKind kind in families) { list2.Add(FamilyRules.Staff(kind) ? ("staffs held like the " + ItemName(look2.HeldLike)) : FamilyRules.Label(kind).ToLowerInvariant()); } } bool flag = Object.op_Implicit((Object)(object)Player.m_localPlayer) && !Unlocked(Player.m_localPlayer, look2.Prefab); text = ((!flag) ? "" : (look2.CommandOnly ? "Not in your collection\n" : "Locked\n")) + (look2.Recolor ? (look2.Source + " recolor") : ((look2.Trophy ? "Trophy: " : (look2.Helmet ? "Monster helmet: " : "Monster weapon: ")) + look2.Source)) + "\nUnlocked by: " + (look2.CommandOnly ? "goocosmetics unlock only" : string.Join(" or ", list)) + "\nFits: " + string.Join(", ", list2); return true; } internal static string UnlockHint(CollectedAppearance look) { if (look == null || !MobLooks.TryGet(look.Prefab, out var look2)) { return ""; } if (look2.CommandOnly) { return "Only goocosmetics unlock gives this look."; } List<string> list = new List<string>(); string[] unlocks = look2.Unlocks; foreach (string prefab in unlocks) { list.Add("the " + ItemName(prefab)); } return "Pick up " + string.Join(" or ", list) + " to unlock this look."; } internal static string RefusedAppearance(ItemData original, string prefab) { if (!MobLooks.IsUnlockItem(prefab)) { return "This item can’t be used as an appearance."; } if (original == null) { return "Choose the item you want to change first."; } if (!Features.Has(Feature.Collection) || !Object.op_Implicit((Object)(object)ObjectDB.instance) || !Classification.Eligible(original)) { return "This item can’t be used as an appearance."; } foreach (MobLook item in MobLooks.Catalog) { if (Array.IndexOf(item.Unlocks, prefab) >= 0) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.Prefab); if (Object.op_Implicit((Object)(object)itemPrefab) && Fits(original, itemPrefab)) { return "Choose what this unlocks in the Collection."; } } } return "This item can’t be used as an appearance."; } } internal static class AppearanceOrder { private static readonly HashSet<string> vanilla = new HashSet<string>(StringComparer.Ordinal); private static readonly Dictionary<string, string[]> recipes = new Dictionary<string, string[]>(StringComparer.Ordinal); private static readonly HashSet<string> craftable = new HashSet<string>(StringComparer.Ordinal); private static readonly Dictionary<string, int> resolved = new Dictionary<string, int>(StringComparer.Ordinal); private static bool captured; private static readonly Regex Formatting = new Regex("<[^>]*>", RegexOptions.CultureInvariant); private static readonly List<CollectedAppearance> trailing = new List<CollectedAppearance>(); internal static bool HasVanillaSnapshot => captured; internal static void CaptureVanilla(ObjectDB database) { if (captured || !Object.op_Implicit((Object)(object)database) || database.m_items == null) { return; } bool flag = false; foreach (GameObject item in database.m_items) { if (Object.op_Implicit((Object)(object)item) && ((Object)item).name == "AxeStone") { flag = true; break; } } if (!flag) { return; } foreach (GameObject item2 in database.m_items) { if (Object.op_Implicit((Object)(object)item2)) { vanilla.Add(((Object)item2).name); } } foreach (Recipe recipe in database.m_recipes) { if (!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item) || recipe.m_resources == null) { continue; } List<string> list = new List<string>(); Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (Object.op_Implicit((Object)(object)val?.m_resItem)) { list.Add(((Object)val.m_resItem).name); } } string name = ((Object)recipe.m_item).name; if (!recipes.ContainsKey(name)) { recipes.Add(name, list.ToArray()); } if (recipe.m_enabled) { craftable.Add(name); } } captured = true; } internal static bool Craftable(string name) { if (captured) { return craftable.Contains(name); } return true; } private static int MaterialRank(string name) { switch (name) { case "Gold": case "GoldBar": case "GoldOre": case "AncientGemstoneBlack": case "AncientGemstoneGreen": case "AncientGemstoneOrange": case "AncientGemstonePurple": return 800; case "AskHide": case "GemstoneRed": case "MorgenHeart": case "FlametalNew": case "GemstoneBlue": case "GemstoneGreen": case "FlametalOreNew": return 700; case "Eitr": case "YggdrasilWood": case "Softtissue": case "Mandible": case "Carapace": case "ScaleHide": case "BlackCore": return 600; case "LoxPelt": case "LinenThread": case "BlackMetal": case "BlackMetalScrap": return 500; case "Crystal": case "FenringHair": case "FenringClaw": case "FreezeGland": case "WolfFang": case "WolfPelt": case "SilverOre": case "Silver": return 400; case "Iron": case "Root": case "IronScrap": case "ElderBark": case "Chain": return 300; case "SurtlingCore": case "RoundLog": case "FineWood": case "TrollHide": case "Bronze": case "Copper": case "Tin": return 200; default: return 100; } } private static int Rank(string name, HashSet<string> path) { int num = AppearanceProgression.Rank(name); if (num > 0) { return num; } if (resolved.TryGetValue(name, out var value)) { return value; } int num2 = MaterialRank(name); if (path.Count >= 32 || !path.Add(name)) { return num2; } if (recipes.TryGetValue(name, out var value2)) { string[] array = value2; foreach (string name2 in array) { num2 = Math.Max(num2, Rank(name2, path) + 1); } } path.Remove(name); resolved[name] = num2; return num2; } internal static bool Last(string prefab) { return MobLooks.IsTrophy(prefab); } internal static void Sort(List<CollectedAppearance> looks) { Dictionary<CollectedAppearance, string> names = new Dictionary<CollectedAppearance, string>(); Dictionary<CollectedAppearance, int> ranks = new Dictionary<CollectedAppearance, int>(); Dictionary<CollectedAppearance, bool> obtainable = new Dictionary<CollectedAppearance, bool>(); Dictionary<CollectedAppearance, bool> last = new Dictionary<CollectedAppearance, bool>(); foreach (CollectedAppearance look in looks) { names[look] = Formatting.Replace(AppearanceCollection.Name(look), ""); ranks[look] = Rank(look.Prefab, new HashSet<string>(StringComparer.Ordinal)); obtainable[look] = Obtainable.Item(look.Prefab); last[look] = Last(look.Prefab); } looks.Sort(delegate(CollectedAppearance left, CollectedAppearance right) { if (last[left] != last[right]) { if (!last[left]) { return -1; } return 1; } if (last[left]) { return AppearanceProgression.Compare(!MobLooks.IsModdedTrophy(left.Prefab), ranks[left], names[left], left, !MobLooks.IsModdedTrophy(right.Prefab), ranks[right], names[right], right); } int num = obtainable[right].CompareTo(obtainable[left]); return (num == 0) ? AppearanceProgression.Compare(IsVanilla(left.Prefab), ranks[left], names[left], left, IsVanilla(right.Prefab), ranks[right], names[right], right) : num; }); } internal static void TrophiesLast(List<CollectedAppearance> looks) { int num = 0; trailing.Clear(); for (int i = 0; i < looks.Count; i++) { CollectedAppearance collectedAppearance = looks[i]; if (collectedAppearance != null && Last(collectedAppearance.Prefab)) { trailing.Add(collectedAppearance); } else { looks[num++] = collectedAppearance; } } if (trailing.Count != 0) { looks.RemoveRange(num, looks.Count - num); looks.AddRange(trailing); trailing.Clear(); } } internal static bool IsVanilla(string name) { if (!captured) { return AppearanceProgression.Known(name); } return vanilla.Contains(name); } internal static IEnumerable<string> VanillaItems() { if (captured) { return vanilla; } List<string> list = new List<string>(); if (Object.op_Implicit((Object)(object)ObjectDB.instance)) { foreach (GameObject item in ObjectDB.instance.m_items) { if (Object.op_Implicit((Object)(object)item)) { list.Add(((Object)item).name); } } } return list; } } internal static class AppearanceProgression { private static readonly Dictionary<string, int> Ranks = Build(); internal static bool Known(string name) { if (name != null) { return Ranks.ContainsKey(name); } return false; } internal static int Rank(string name) { if (name == null || !Ranks.TryGetValue(name, out var value)) { return 0; } return value; } private static void Add(Dictionary<string, int> ranks, int stage, params string[] names) { for (int i = 0; i < names.Length; i++) { ranks[names[i]] = stage * 100 + i; } } private static Dictionary<string, int> Build() { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); Add(dictionary, 1, "AxeStone", "AxeFlint", "Club", "KnifeFlint", "SpearFlint", "Bow", "ShieldWood", "ShieldWoodTower", "SledgeStagbreaker", "ArmorRagsChest", "ArmorRagsLegs", "ArmorLeatherChest", "ArmorLeatherLegs", "HelmetLeather", "CapeDeerHide"); Add(dictionary, 2, "PickaxeAntler", "AxeBronze", "SwordBronze", "MaceBronze", "KnifeCopper", "SpearBronze", "AtgeirBronze", "BowFineWood", "ShieldBronzeBuckler", "ShieldBoneTower", "PickaxeBronze", "ArmorTrollLeatherChest", "ArmorTrollLeatherLegs", "HelmetTrollLeather", "CapeTrollHide", "ArmorBronzeChest", "ArmorBronzeLegs", "HelmetBronze", "HelmetDverger", "HelmetYule", "CapeOdin", "HelmetOdin"); Add(dictionary, 3, "AxeIron", "SwordIron", "SwordIronFire", "MaceIron", "Battleaxe", "AtgeirIron", "SpearElderbark", "KnifeChitin", "SpearChitin", "BowHuntsman", "SledgeIron", "PickaxeIron", "ShieldIronBuckler", "ShieldBanded", "ShieldIronTower", "ShieldSerpentscale", "ArmorRootChest", "ArmorRootLegs", "HelmetRoot", "ArmorIronChest", "ArmorIronLegs", "HelmetIron"); Add(dictionary, 4, "SwordSilver", "MaceSilver", "KnifeSilver", "SpearWolfFang", "BowDraugrFang", "BattleaxeCrystal", "FistFenrirClaw", "ShieldSilver", "ArmorWolfChest", "ArmorWolfLegs", "HelmetDrake", "CapeWolf", "ArmorFenringChest", "ArmorFenringLegs", "HelmetFenring"); Add(dictionary, 5, "AxeBlackMetal", "SwordBlackmetal", "MaceNeedle", "KnifeBlackMetal", "AtgeirBlackmetal", "ShieldBlackmetal", "ShieldBlackmetalTower", "ArmorPaddedCuirass", "ArmorPaddedGreaves", "HelmetPadded", "CapeLox", "CapeLinen"); Add(dictionary, 6, "AxeJotunBane", "SwordMistwalker", "THSwordKrom", "SledgeDemolisher", "AtgeirHimminAfl", "SpearCarapace", "KnifeSkollAndHati", "BowSpineSnap", "CrossbowArbalest", "PickaxeBlackMetal", "ShieldCarapaceBuckler", "ShieldCarapace", "StaffFireball", "StaffIceShards", "StaffShield", "StaffSkeleton", "ArmorCarapaceChest", "ArmorCarapaceLegs", "HelmetCarapace", "ArmorMageChest", "ArmorMageLegs", "HelmetMage", "CapeFeather"); Add(dictionary, 7, "AxeBerzerkr", "AxeBerzerkrBlood", "AxeBerzerkrLightning", "AxeBerzerkrNature", "SwordNiedhogg", "SwordNiedhoggBlood", "SwordNiedhoggLightning", "SwordNiedhoggNature", "THSwordSlayer", "THSwordSlayerBlood", "THSwordSlayerLightning", "THSwordSlayerNature", "SpearSplitner", "SpearSplitner_Blood", "SpearSplitner_Lightning", "SpearSplitner_Nature", "BowAshlands", "BowAshlandsBlood", "BowAshlandsStorm", "BowAshlandsRoot", "CrossbowRipper", "CrossbowRipperBlood", "CrossbowRipperLightning", "CrossbowRipperNature", "MaceEldner", "MaceEldnerBlood", "MaceEldnerLightning", "MaceEldnerNature", "SwordDyrnwyn", "ShieldFlametal", "ShieldFlametalTower", "StaffClusterbomb", "StaffLightning", "StaffRedTroll", "StaffGreenRoots", "ArmorFlametalChest", "ArmorFlametalLegs", "HelmetFlametal", "ArmorAshlandsMediumChest", "ArmorAshlandsMediumlegs", "HelmetAshlandsMediumHood", "ArmorMageChest_Ashlands", "ArmorMageLegs_Ashlands", "HelmetMage_Ashlands", "CapeAsksvin", "CapeAsh"); Add(dictionary, 8, "AxeGold", "AxeGold_BloodLightning", "AxeGold_FrostFire", "BattleaxeGold", "BattleaxeGold_BloodLightning", "BattleaxeGold_FrostFire", "SwordGold", "SwordGold_BloodLightning", "SwordGold_FrostFire", "THSwordGold", "THSwordGold_BloodLightning", "THSwordGold_FrostFire", "MaceGold", "MaceGold_BloodLightning", "MaceGold_FrostFire", "SledgeGold", "SledgeGold_BloodLightning", "SledgeGold_FrostFire", "SpearGold", "SpearGold_BloodLightning", "SpearGold_FrostFire", "AtgeirGold", "AtgeirGold_BloodLightning", "AtgeirGold_FrostFire", "KnifeGold", "KnifeGold_BloodLightning", "KnifeGold_FrostFire", "BowGold", "BowGold_BloodLightning", "BowGold_FrostFire", "CrossbowGold", "CrossbowGold_BloodLightning", "CrossbowGold_FrostFire", "ShieldGold", "ShieldGoldTower", "ShieldGoldBuckler", "StaffFrostOrbs", "StaffThunderBlood", "StaffOrbofAhri", "StaffSpiritCaller", "ArmorDeepNorthHeavyChest", "ArmorDeepNorthHeavylegs", "HelmetDNHeavy", "ArmorDeepNorthMediumChest", "ArmorDeepNorthMediumlegs", "HelmetDNMediumHood", "ArmorDeepNorthMageChest", "ArmorDeepNorthMagelegs", "HelmetDNMage"); Dictionary<int, int> dictionary2 = new Dictionary<int, int>(); foreach (MobLook item in MobLooks.Catalog) { dictionary2.TryGetValue(item.Stage, out var value); dictionary[item.Prefab] = item.Stage * 100 + 80 + value; dictionary2[item.Stage] = value + 1; } return dictionary; } internal static int Compare(bool leftVanilla, int leftRank, string leftName, CollectedAppearance left, bool rightVanilla, int rightRank, string rightName, CollectedAppearance right) { int num = rightVanilla.CompareTo(leftVanilla); if (num == 0 && leftVanilla) { num = leftRank.CompareTo(rightRank); } if (num == 0) { num = StringComparer.CurrentCultureIgnoreCase.Compare(leftName, rightName); } if (num == 0) { num = StringComparer.Ordinal.Compare(left.Prefab, right.Prefab); } if (num == 0) { int variant = left.Variant; num = variant.CompareTo(right.Variant); } if (num == 0) { int variant = left.Quality; num = variant.CompareTo(right.Quality); } return num; } } internal sealed class AppearanceSelection { private CollectedAppearance original; private CollectedAppearance previous; internal CollectedAppearance Current { get; private set; } internal bool IsOriginal { get { if (original != null) { return original.Equals(Current); } return false; } } internal CollectedAppearance SlotLook => Current ?? original; internal bool Dimmed { get { if (Current != null) { return IsOriginal; } return true; } } internal void Reset(CollectedAppearance native) { CollectedAppearance collectedAppearance = (Current = native); original = collectedAppearance; previous = null; } internal void Select(CollectedAppearance look) { Current = look; if (look != null && !look.Equals(original)) { previous = look; } } internal void SetOriginal(bool enabled) { Current = (enabled ? original : previous); } } internal static class ArmorStandLooks { private sealed class StandState { internal ArmorStand Stand; internal ZNetView View; internal ZDO Zdo; internal uint DataRevision; internal readonly string[] Raw = new string[8] { "", "", "", "", "", "", "", "" }; } private static readonly ConditionalWeakTable<VisEquipment, StandState> States = new ConditionalWeakTable<VisEquipment, StandState>(); private static readonly VisSlot[] Slots; internal static string Read(VisEquipment visual, GearSlot slot) { StandState value = States.GetValue(visual, (VisEquipment v) => new StandState { Stand = ((Component)v).GetComponentInParent<ArmorStand>() }); ArmorStand stand = value.Stand; if (!Object.op_Implicit((Object)(object)stand)) { return ""; } if (!Object.op_Implicit((Object)(object)value.View)) { value.View = (Object.op_Implicit((Object)(object)stand.m_netViewOverride) ? stand.m_netViewOverride : ((Component)stand).GetComponent<ZNetView>()); } ZNetView view = value.View; if (!Object.op_Implicit((Object)(object)view) || !view.IsValid()) { return ""; } ZDO zDO = view.GetZDO(); if (zDO != value.Zdo || zDO.DataRevision != value.DataRevision) { Refresh(value, zDO); } return value.Raw[(int)slot]; } private static void Refresh(StandState state, ZDO zdo) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_0094: Expected O, but got Unknown state.Zdo = zdo; state.DataRevision = zdo.DataRevision; for (int i = 0; i < state.Raw.Length; i++) { state.Raw[i] = ""; } List<ArmorStandSlot> slots = state.Stand.m_slots; if (slots == null) { return; } for (int j = 0; j < slots.Count; j++) { int num = ((slots[j] == null) ? (-1) : Array.IndexOf(Slots, slots[j].m_slot)); if (num >= 0 && state.Raw[num].Length <= 0) { try { ItemData val = new ItemData(); ItemDrop.LoadFromZDO(val, zdo, j); string text = Illusions.Raw(val); state.Raw[num] = ((text.Length > 2100) ? "" : text); } catch (Exception error) { Plugin.WarnOnce("Armor stand appearance", error); } } } } static ArmorStandLooks() { VisSlot[] array = new VisSlot[8]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); Slots = (VisSlot[])(object)array; } } internal static class Classification { private sealed class Remembered { internal int Definitions = -1; internal MobLook Look; internal string Recolor; internal GearSignature Signature; private GameObject source; private SharedData shared; private int maxStack; private int stance; private int skill; private int itemType; private string name; private Attack primary; private Attack secondary; private string primaryAnimation; private string secondaryAnimation; private int primaryChain; private int secondaryChain; private int primaryType; private int secondaryType; internal bool Matches(GameObject prefab, SharedData data) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 if (Signature == null || prefab != source || data != shared) { return false; } if (data != null) { if (data.m_maxStackSize == maxStack && (int)data.m_animationState == stance && (int)data.m_skillType == skill && (int)data.m_itemType == itemType && string.Equals(data.m_name, name) && Same(data.m_attack, primary, primaryAnimation, primaryChain, primaryType)) { return Same(data.m_secondaryAttack, secondary, secondaryAnimation, secondaryChain, secondaryType); } return false; } return true; } private static bool Same(Attack attack, Attack kept, string animation, int chain, int type) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 if (attack == kept) { if (attack != null) { if (string.Equals(attack.m_attackAnimation, animation) && attack.m_attackChainLevels == chain) { return (int)attack.m_attackType == type; } return false; } return true; } return false; } internal void Keep(GameObject prefab, SharedData data, GearSignature signature) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected I4, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected I4, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected I4, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) Signature = signature; source = prefab; shared = data; if (data != null) { maxStack = data.m_maxStackSize; stance = (int)data.m_animationState; skill = (int)data.m_skillType; itemType = (int)data.m_itemType; name = data.m_name; primary = data.m_attack; secondary = data.m_secondaryAttack; primaryAnimation = primary?.m_attackAnimation; primaryChain = primary?.m_attackChainLevels ?? 0; Attack obj = primary; primaryType = ((obj != null) ? ((int)obj.m_attackType) : 0); secondaryAnimation = secondary?.m_attackAnimation; secondaryChain = secondary?.m_attackChainLevels ?? 0; Attack obj2 = secondary; secondaryType = ((obj2 != null) ? ((int)obj2.m_attackType) : 0); } } } internal struct Use { internal GameObject Prefab; internal GearSignature Signature; } private sealed class Draft { internal GearKind Kind; internal int Stance; internal int Skill; internal int ItemType; internal string Primary = ""; internal string Secondary = ""; internal int PrimaryChain; internal int SecondaryChain; internal int PrimaryType; internal int SecondaryType; internal int KnownSlot = -1; internal int KnownStance = -1; internal string Evidence = "Unsupported item"; internal string Issue = ""; internal bool IsWeapon => GearSignature.Weapon(Kind); internal GearSignature Done() { return new GearSignature(Kind, Stance, Skill, ItemType, Primary, Secondary, PrimaryChain, SecondaryChain, PrimaryType, SecondaryType, KnownSlot, KnownStance, Evidence, Issue); } } private static readonly HashSet<string> Bucklers = new HashSet<string>(StringComparer.Ordinal) { "ShieldBronzeBuckler", "ShieldIronBuckler", "ShieldCarapaceBuckler", "ShieldGoldBuckler" }; private static readonly HashSet<string> Towers = new HashSet<string>(StringComparer.Ordinal) { "ShieldWoodTower", "ShieldBoneTower", "ShieldIronTower", "ShieldSerpentscale", "ShieldBlackmetalTower", "ShieldFlametalTower", "ShieldGoldTower" }; private static readonly HashSet<string> Rounds = new HashSet<string>(StringComparer.Ordinal) { "ShieldWood", "ShieldBanded", "ShieldSilver", "ShieldBlackmetal", "ShieldCarapace", "ShieldFlametal", "ShieldGold" }; private static readonly GearSignature Unclassified = new GearSignature(); private static readonly ConditionalWeakTable<GameObject, Remembered> Memos = new ConditionalWeakTable<GameObject, Remembered>(); private static ObjectDB database; private static ZNetScene scene; private static int definitions; private static int revision = -1; private static int registrations = -1; private static int items = -1; private static int recipes = -1; private static int scenePrefabs = -1; private static bool allowUnknown; internal static List<Use> Noted; internal static int Definitions { get { ObjectDB instance = ObjectDB.instance; ZNetScene instance2 = ZNetScene.instance; bool num = Object.op_Implicit((Object)(object)instance); bool flag = Object.op_Implicit((Object)(object)instance2); int num2 = ((num && instance.m_items != null) ? instance.m_items.Count : (-1)); int num3 = ((num && instance.m_recipes != null) ? instance.m_recipes.Count : (-1)); int num4 = ((flag && instance2.m_prefabs != null) ? instance2.m_prefabs.Count : (-1)); bool flag2 = AllowUnknown; if (instance == database && instance2 == scene && revision == Plugin.VisualRevision && registrations == Compatibility.Revision && items == num2 && recipes == num3 && scenePrefabs == num4 && allowUnknown == flag2) { return definitions; } database = instance; scene = instance2; revision = Plugin.VisualRevision; registrations = Compatibility.Revision; items = num2; recipes = num3; scenePrefabs = num4; allowUnknown = flag2; return ++definitions; } } internal static bool AllowUnknown => Plugin.AllowUnknownTypes?.Value ?? false; internal static GearSignature Of(ItemData item) { if ((Object)(object)item?.m_dropPrefab == (Object)null) { return Unclassified; } return Of(item.m_dropPrefab); } private static Remembered Remember(GameObject prefab) { Remembered orCreateValue = Memos.GetOrCreateValue(prefab); int num = Definitions; if (orCreateValue.Definitions == num) { return orCreateValue; } orCreateValue.Definitions = num; orCreateValue.Signature = null; orCreateValue.Look = (MobLooks.TryGet(((Object)prefab).name, out var look) ? look : null); orCreateValue.Recolor = orCreateValue.Look?.Original; return orCreateValue; } internal static GearSignature Of(GameObject prefab) { if (!Object.op_Implicit((Object)(object)prefab)) { return Note(prefab, Unclassified); } Remembered remembered = Remember(prefab); GameObject val = ((remembered.Recolor != null && Object.op_Implicit((Object)(object)ObjectDB.instance)) ? ObjectDB.instance.GetItemPrefab(remembered.Recolor) : null); if (!Object.op_Implicit((Object)(object)val)) { val = prefab; } SharedData val2 = val.GetComponent<ItemDrop>()?.m_itemData?.m_shared; if (!remembered.Matches(val, val2)) { remembered.Keep(val, val2, OfPrefab(val, val2).Done()); } return Note(prefab, remembered.Signature); } private static GearSignature Note(GameObject prefab, GearSignature signature) { if (Noted == null) { return signature; } foreach (Use item in Noted) { if (item.Prefab == prefab) { return signature; } } Noted.Add(new Use { Prefab = prefab, Signature = signature }); return signature; } private static Draft OfPrefab(GameObject prefab, SharedData shared) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected I4, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Invalid comparison between Unknown and I4 //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Invalid comparison between Unknown and I4 Draft draft = new Draft(); if (shared == null || shared.m_maxStackSize != 1) { return draft; } draft.Stance = (int)shared.m_animationState; draft.Skill = (int)shared.m_skillType; draft.ItemType = (int)shared.m_itemType; draft.Primary = shared.m_attack?.m_attackAnimation ?? ""; draft.Secondary = shared.m_secondaryAttack?.m_attackAnimation ?? ""; draft.PrimaryChain = shared.m_attack?.m_attackChainLevels ?? 0; draft.SecondaryChain = shared.m_secondaryAttack?.m_attackChainLevels ?? 0; Attack attack = shared.m_attack; draft.PrimaryType = ((attack == null) ? 3 : ((int)attack.m_attackType)); Attack secondaryAttack = shared.m_secondaryAttack; draft.SecondaryType = ((secondaryAttack == null) ? 3 : ((int)secondaryAttack.m_attackType)); draft.KnownSlot = (WeaponSlot(shared.m_itemType) ? draft.ItemType : (-1)); draft.KnownStance = (Enum.IsDefined(typeof(AnimationState), shared.m_animationState) ? draft.Stance : (-1)); string text = InvalidLooks.Reason(prefab); if (text != null) { return Block(draft, text); } Compatibility.Gear(((Object)prefab).name, out var rule); GearKind gearKind = Kind(((Object)prefab).name, shared); GearKind gearKind2 = StanceKind(shared.m_animationState); if (gearKind == GearKind.Unsupported && !Enum.IsDefined(typeof(ItemType), shared.m_itemType) && draft.Primary.Length > 0 && gearKind2 != GearKind.Unsupported && KnownWeaponSkill(shared.m_skillType) && SkillFits(gearKind2, shared.m_skillType)) { gearKind = gearKind2; } draft.Kind = gearKind; draft.Evidence = "Native equipment data"; if (gearKind != GearKind.Unsupported) { if (!Fits(gearKind, shared) || (rule != null && !FamilyRules.SameFamily(rule.Family, gearKind))) { return Block(draft, "Conflicting native equipment data or family registration"); } } else if (rule != null) { if (!Fits(rule.Family, shared) || (GearSignature.Weapon(rule.Family) && (draft.Primary.Length == 0 || !SkillFits(rule.Family, shared.m_skillType)))) { return Block(draft, "Registration conflicts with equipment data"); } draft.Kind = rule.Family; draft.Evidence = "Explicit compatibility registration"; } else if ((int)shared.m_itemType == 5) { GearKind kind = WeaponNameClues.Read(((Object)prefab).name, shared.m_name).Resolve(shared); if (!Shield(kind)) { return Block(draft, "Shield category could not be identified"); } draft.Kind = kind; draft.Evidence = "Shield slot and name clues"; } else { GearKind gearKind3 = StanceKind(shared.m_animationState); bool flag = !Enum.IsDefined(typeof(ItemType), shared.m_itemType) && gearKind3 != GearKind.Unsupported; if ((!WeaponSlot(shared.m_itemType) && !flag) || draft.Primary.Length == 0) { return Block(draft, "No supported weapon slot and attack setup"); } if (KnownWeaponSkill(shared.m_skillType)) { return Block(draft, "Weapon skill conflicts with equipment data"); } GearKind gearKind4 = WeaponNameClues.Read(((Object)prefab).name, shared.m_name).Resolve(shared); if (gearKind4 == GearKind.Unsupported || (gearKind4 != GearKind.UnknownWeapon && !Fits(gearKind4, shared))) { return Block(draft, "Name clues are ambiguous or conflict with equipment data"); } if (gearKind3 != GearKind.Unsupported && gearKind4 != GearKind.UnknownWeapon && gearKind3 != gearKind4) { return Block(draft, "Weapon stance and name clues disagree"); } draft.Kind = ((gearKind3 != GearKind.Unsupported) ? gearKind3 : gearKind4); if (draft.Kind == GearKind.UnknownWeapon && (int)shared.m_itemType == 4) { draft.Kind = GearKind.Bow; } if (!Fits(draft.Kind, shared)) { return Block(draft, "Weapon family conflicts with equipment position"); } draft.Evidence = ((draft.Kind == GearKind.UnknownWeapon) ? "Unresolved weapon family" : ((gearKind3 != GearKind.Unsupported) ? "Weapon slot, attack and stance" : "Weapon slot, attack and name clues")); } if (rule != null && draft.Issue.Length == 0) { draft.Evidence = "Explicit compatibility registration; native boundaries checked"; } if (rule != null && rule.Moveset != null && draft.IsWeapon && draft.Primary.Length > 0) { draft.Primary = "registered:" + rule.Moveset + ":primary"; if (draft.Secondary.Length > 0) { draft.Secondary = "registered:" + rule.Moveset + ":secondary"; } } return draft; } private static Draft Block(Draft signature, string issue) { signature.Kind = GearKind.Unsupported; signature.Issue = issue; signature.Evidence = "Unresolved or conflicting data"; return signature; } private static bool Shield(GearKind kind) { if (kind != GearKind.Buckler && kind != GearKind.RoundShield) { return kind == GearKind.TowerShield; } return true; } private static bool WeaponSlot(ItemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if ((int)type != 3 && (int)type != 14 && (int)type != 22) { return (int)type == 4; } return true; } private static bool KnownWeaponSkill(SkillType skill) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown switch (skill - 1) { case 0: case 1: case 2: case 3: case 4: case 6: case 7: case 8: case 9: case 10: case 11: case 13: return true; default: return false; } } private static bool SkillFits(GearKind kind, SkillType skill) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected I4, but got Unknown switch (skill - 1) { case 0: if (kind != GearKind.Sword1H) { return kind == GearKind.Sword2H; } return true; case 6: if (kind != GearKind.Axe1H && kind != GearKind.Axe2H) { return kind == GearKind.DualAxes; } return true; case 2: if (kind != GearKind.Mace1H) { return kind == GearKind.Sledge; } return true; case 1: if (kind != GearKind.Knife) { return kind == GearKind.DualKnives; } return true; case 4: return kind == GearKind.Spear; case 3: return kind == GearKind.Atgeir; case 10: return kind == GearKind.Fists; case 7: return kind == GearKind.Bow; case 13: return kind == GearKind.Crossbow; case 8: case 9: return FamilyRules.Staff(kind); case 11: return kind == GearKind.Pickaxe; default: return true; } } private static GearKind StanceKind(AnimationState stance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected I4, but got Unknown if ((int)stance != 2) { if ((int)stance != 3) { return (stance - 7) switch { 6 => GearKind.Sword2H, 0 => GearKind.Atgeir, 8 => GearKind.DualAxes, 4 => GearKind.DualKnives, 3 => GearKind.Crossbow, _ => GearKind.Unsupported, }; } return GearKind.Bow; } return GearKind.Sledge; } private static bool Fits(GearKind kind, SharedData shared) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Invalid comparison between Unknown and I4 //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Invalid comparison between Unknown and I4 //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Invalid comparison between Unknown and I4 //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Invalid comparison between Unknown and I4 //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Invalid comparison between Unknown and I4 //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Expected I4, but got Unknown if (Shield(kind)) { return (int)shared.m_itemType == 5; } switch (kind) { case GearKind.Chest: return (int)shared.m_itemType == 7; case GearKind.Legs: return (int)shared.m_itemType == 11; case GearKind.Cape: return (int)shared.m_itemType == 17; case GearKind.Helmet: return (int)shared.m_itemType == 6; default: { if (!GearSignature.Weapon(kind) || (!WeaponSlot(shared.m_itemType) && Enum.IsDefined(typeof(ItemType), shared.m_itemType))) { return false; } bool flag = (int)shared.m_itemType == 3; bool flag2 = (int)shared.m_itemType == 14 || (int)shared.m_itemType == 22; if ((int)shared.m_animationState == 1 && flag2) { return false; } if ((int)shared.m_animationState == 1) { flag = true; } if (flag2 && (kind == GearKind.Sword1H || kind == GearKind.Axe1H || kind == GearKind.Mace1H || kind == GearKind.Knife)) { return false; } if (flag && (kind == GearKind.Sword2H || kind == GearKind.Axe2H || kind == GearKind.Sledge || kind == GearKind.DualAxes || kind == GearKind.DualKnives)) { return false; } if ((int)shared.m_itemType == 4 && kind != GearKind.Bow && kind != GearKind.Crossbow) { return false; } GearKind gearKind = StanceKind(shared.m_animationState); if (gearKind != GearKind.Unsupported && gearKind != kind && !FamilyRules.Staff(kind)) { return false; } AnimationState animationState = shared.m_animationState; switch (animationState - 4) { case 0: case 1: case 2: case 5: return false; case 4: if (kind != GearKind.Axe2H && kind != GearKind.Pickaxe) { return kind == GearKind.UnknownWeapon; } return true; case 8: if (!FamilyRules.Staff(kind)) { return kind == GearKind.UnknownWeapon; } return true; default: return true; } } } } private static GearKind Kind(string prefab, SharedData shared) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_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_002b: Expected I4, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Invalid comparison between Unknown and I4 //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Invalid comparison between Unknown and I4 //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected I4, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Invalid comparison between Unknown and I4 //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Invalid comparison between Unknown and I4 //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Invalid comparison between Unknown and I4 //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Invalid comparison between Unknown and I4 //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Invalid comparison between Unknown and I4 //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Invalid comparison between Unknown and I4 //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Invalid comparison between Unknown and I4 //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected I4, but got Unknown ItemType itemType = shared.m_itemType; switch (itemType - 5) { default: if ((int)itemType != 17) { break; } return GearKind.Cape; case 2: return GearKind.Chest; case 6: return GearKind.Legs; case 1: return GearKind.Helmet; case 0: if (!Bucklers.Contains(prefab)) { if (!Towers.Contains(prefab)) { if (!Rounds.Contains(prefab)) { return GearKind.Unsupported; } return GearKind.RoundShield; } return GearKind.TowerShield; } return GearKind.Buckler; case 3: case 4: case 5: break; } if ((int)shared.m_itemType != 3 && (int)shared.m_itemType != 14 && (int)shared.m_itemType != 22 && (int)shared.m_itemType != 4) { return GearKind.Unsupported; } SkillType skillType = shared.m_skillType; switch (skillType - 8) { case 0: return GearKind.Bow; case 6: return GearKind.Crossbow; case 1: return GearKind.ElementalStaff; case 2: return GearKind.BloodStaff; case 4: return GearKind.Pickaxe; default: { AnimationState animationState = shared.m_animationState; if ((int)animationState != 11) { if ((int)animationState == 15) { if ((int)shared.m_skillType != 7) { return GearKind.Unsupported; } return GearKind.DualAxes; } bool flag = (int)shared.m_itemType == 14 || (int)shared.m_itemType == 22; skillType = shared.m_skillType; switch (skillType - 1) { case 0: if (!flag) { return GearKind.Sword1H; } return GearKind.Sword2H; case 6: if (!flag) { return GearKind.Axe1H; } return GearKind.Axe2H; case 2: if (!flag) { return GearKind.Mace1H; } return GearKind.Sledge; case 1: if (!flag) { return GearKind.Knife; } return GearKind.Unsupported; case 4: return GearKind.Spear; case 3: return GearKind.Atgeir; case 10: return GearKind.Fists; default: return GearKind.Unsupported; } } if ((int)shared.m_skillType != 2) { return GearKind.Unsupported; } return GearKind.DualKnives; } } } internal static bool Eligible(ItemData item) { if (item == null || item.m_stack != 1 || item.m_shared == null || item.m_shared.m_maxStackSize != 1) { return false; } return Of(item).Kind != GearKind.Unsupported; } internal static bool Pair(ItemData original, ItemData donor, out string reason) { if (!Eligible(original) || !Eligible(donor)) { reason = "This item can’t be used as an appearance."; return false; } return PairPrefabs(original.m_dropPrefab, donor.m_dropPrefab, out reason); } internal static bool PairPrefabs(GameObject original, GameObject donor, out string reason, bool savedAppearance = false) { MobLook mobLook = (Object.op_Implicit((Object)(object)donor) ? Remember(donor).Look : null); if (mobLook != null && !mobLook.Recolor) { return PairMob(original, mobLook, out reason); } GearSignature gearSignature = Of(original); GearSignature gearSignature2 = Of(donor); if ((!Object.op_Implicit((Object)(object)original) || !((Object)(object)original == (Object)(object)donor) || gearSignature.Kind == GearKind.Unsupported) && !FamilyRules.Compatible(gearSignature, gearSignature2, savedAppearance || AllowUnknown)) { reason = ((gearSignature.Kind == GearKind.Unsupported || gearSignature2.Kind == GearKind.Unsupported) ? "This item’s type could not be matched safely. Choose a different appearance." : ((gearSignature.Kind == GearKind.UnknownWeapon && gearSignature2.Kind == GearKind.UnknownWeapon) ? ((!savedAppearance && !AllowUnknown) ? "These weapon types are unknown. Enable Allow mixing of unknown types in settings to use them together." : "These weapons use different equipment positions. Choose a different appearance.") : ((!FamilyRules.SameFamily(gearSignature.Kind, gearSignature2.Kind)) ? FamilyRules.Requirement(gearSignature.Kind) : ((FamilyRules.Staff(gearSignature.Kind) && (gearSignature.ItemType != gearSignature2.ItemType || gearSignature.Stance != gearSignature2.Stance)) ? "These staffs are held differently. Choose a different appearance." : "These weapons use different attacks. Choose a different appearance.")))); return false; } reason = ""; return true; } private static bool PairMob(GameObject original, MobLook mob, out string reason) { GearSignature gearSignature = Of(original); if (!mob.Dresses(gearSignature.Kind)) { List<string> list = new List<string>(); GearKind[] families = mob.Families; foreach (GearKind kind in families) { list.Add(FamilyRules.Label(kind).ToLowerInvariant()); } reason = "This appearance only fits: " + string.Join(", ", list) + "."; return false; } if (FamilyRules.Staff(gearSignature.Kind)) { GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(mob.HeldLike) : null); if (!Object.op_Implicit((Object)(object)val) || !FamilyRules.Compatible(gearSignature, Of(val))) { reason = "This appearance only fits staffs held like the " + AppearanceCollection.ItemName(mob.HeldLike) + "."; return false; } } reason = ""; return true; } } internal sealed class CollectedAppearance : IEquatable<CollectedAppearance> { internal readonly string Prefab; internal readonly int Variant; internal readonly int Quality; internal bool Valid { get { if (string.IsNullOrWhiteSpace(Prefab) || Prefab.Length > 160 || Variant < 0 || Variant > 65535 || Quality < 1 || Quality > 65535) { return false; } string prefab = Prefab; for (int i = 0; i < prefab.Length; i++) { if (char.IsControl(prefab[i])) { return false; } } return true; } } internal CollectedAppearance(string prefab, int variant, int quality) { Prefab = prefab; Variant = variant; Quality = quality; } public bool Equals(CollectedAppearance other) { if (other != null && Prefab == other.Prefab && Variant == other.Variant) { return Quality == other.Quality; } return false; } public override bool Equals(object other) { return Equals(other as CollectedAppearance); } public override int GetHashCode() { return (((((Prefab != null) ? StringComparer.Ordinal.GetHashCode(Prefab) : 0) * 397) ^ Variant) * 397) ^ Quality; } internal string Encode() { string[] obj = new string[5] { Convert.ToBase64String(Encoding.UTF8.GetBytes(Prefab)), "|", null, null, null }; int variant = Variant; obj[2] = variant.ToString(CultureInfo.InvariantCulture); obj[3] = "|"; variant = Quality; obj[4] = variant.ToString(CultureInfo.InvariantCulture); return string.Concat(obj); } internal static bool TryDecode(string line, out CollectedAppearance value) { value = null; string[] array = line.Split(new char[1] { '|' }); if (array.Length != 3 || array[0].Length > 860 || !int.TryParse(array[1], NumberStyles.None, CultureInfo.InvariantCulture, out var result) || !int.TryParse(array[2], NumberStyles.None, CultureInfo.InvariantCulture, out var result2)) { return false; } try { CollectedAppearance collectedAppearance = new CollectedAppearance(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true).GetString(Convert.FromBase64String(array[0])), result, result2); if (!collectedAppearance.Valid) { return false; } value = collectedAppearance; return true; } catch (FormatException) { return false; } catch (DecoderFallbackException) { return false; } } } internal sealed class AppearanceJournal { internal const string Key = "gnls.GooCosmetics.collection.v1"; internal const int MaxEntries = 8192; internal const int MaxLength = 2097152; private readonly HashSet<CollectedAppearance> entries = new HashSet<CollectedAppearance>(); private int encodedLength = 2; internal IEnumerable<CollectedAppearance> Entries => entries; internal int Count => entries.Count; internal bool Contains(CollectedAppearance entry) { if (entry != null) { return entries.Contains(entry); } return false; } internal bool Add(CollectedAppearance entry) { if (entry == null || !entry.Valid || entries.Count >= 8192 || entries.Contains(entry)) { return false; } int num = entry.Encode().Length + ((entries.Count != 0) ? 1 : 0); if (encodedLength + num > 2097152) { return false; } entries.Add(entry); encodedLength += num; return true; } internal bool Remove(CollectedAppearance entry) { if (entry == null || !entries.Remove(entry)) { return false; } encodedLength -= entry.Encode().Length + ((entries.Count != 0) ? 1 : 0); return true; } internal string Encode() { List<string> list = new List<string>(); foreach (CollectedAppearance entry in entries) { list.Add(entry.Encode()); } list.Sort(StringComparer.Ordinal); return "1\n" + string.Join("\n", list); } internal static bool TryDecode(string raw, out AppearanceJournal journal) { journal = new AppearanceJournal(); if (string.IsNullOrEmpty(raw)) { return true; } if (raw.Length > 2097152 || !raw.StartsWith("1\n", StringComparison.Ordinal)) { return false; } if (raw.Length == 2) { return true; } string[] array = raw.Substring(2).Split(new char[1] { '\n' }); if (array.Length > 8192) { return false; } AppearanceJournal appearanceJournal = new AppearanceJournal(); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { if (!CollectedAppearance.TryDecode(array2[i], out var value)) { return false; } appearanceJournal.Add(value); } journal = appearanceJournal; return true; } } internal static class CollectionHooks { internal static void CheckContracts() { Native.Method(typeof(Player), "Load", typeof(ZPackage)); Native.Method(typeof(Player), "Save", typeof(ZPackage)); Native.Method(typeof(Player), "AddKnownItem", typeof(ItemData)); Native.Method(typeof(ObjectDB), "Awake"); Native.Method(typeof(ObjectDB), "UpdateRegisters"); if (AccessTools.Field(typeof(Player), "m_customData") == null) { throw new MissingFieldException("Player.m_customData"); } } } [HarmonyPatch(typeof(Player), "Load", new Type[] { typeof(ZPackage) })] internal static class CollectionLoadPatch { internal sealed class Snapshot { internal AppearanceCollection.LoadState Collection; internal string Favorites; internal string NewLooks; internal bool FavoritesStarted; internal bool NewLooksStarted; } [HarmonyPrefix] private static void Prefix(Player __instance, out Snapshot __state) { __state = new Snapshot(); ModdedTrophies.Discover(ObjectDB.instance); try { __state.Collection = AppearanceCollection.BeginLoad(__instance); } catch (Exception error) { Plugin.WarnOnce("Prepare saved appearances", error); } try { __state.Favorites = FavoritesStore.BeginLoad(__instance); __state.FavoritesStarted = true; } catch (Exception error2) { Plugin.WarnOnce("Prepare saved favorites", error2); } try { __state.NewLooks = NewLooks.BeginLoad(__instance); __state.NewLooksStarted = true; } catch (Exception error3) { Plugin.WarnOnce("Prepare new monster looks", error3); } } [HarmonyFinalizer] private static Exception Finalizer(Player __instance, Snapshot __state, Exception __exception) { try { AppearanceCollection.EndLoad(__instance, __state?.Collection, __exception == null); } catch (Exception error) { Plugin.WarnOnce("Load saved appearances", error); } try { if (__state != null && __state.FavoritesStarted) { FavoritesStore.EndLoad(__instance, __state.Favorites, __exception == null); } } catch (Exception error2) { Plugin.WarnOnce("Load saved favorites", error2); } try { if (__state != null && __state.NewLooksStarted) { NewLooks.EndLoad(__instance, __state.NewLooks, __exception == null); } } catch (Exception error3) { Plugin.WarnOnce("Load new monster looks", error3); } return __exception; } } [HarmonyPatch(typeof(Player), "Save", new Type[] { typeof(ZPackage) })] internal static class CollectionSavePatch { [HarmonyPrefix] private static void Prefix(Player __instance) { try { AppearanceCollection.Saving(__instance); } catch (Exception error) { Plugin.WarnOnce("Save appearances", error); } try { FavoritesStore.Saving(__instance); } catch (Exception error2) { Plugin.WarnOnce("Save favorites", error2); } } } [HarmonyPatch(typeof(Player), "AddKnownItem", new Type[] { typeof(ItemData) })] internal static class CollectionObtainPatch { internal struct Pickup { internal List<MobLook> Fresh; internal bool FirstUnlockItem; } [HarmonyPrefix] private static void Prefix(Player __instance, ItemData item, out Pickup __state) { __state = default(Pickup); try { __state.FirstUnlockItem = AppearanceCollection.FirstUnlockItem(__instance, item); if (__state.FirstUnlockItem && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { __state.Fresh = NewLooks.Unlocking(__instance, item); } } catch (Exception error) { Plugin.WarnOnce("Detect new monster looks", error); } } [HarmonyPostfix] private static void Postfix(Player __instance, ItemData item, Pickup __state) { try { AppearanceCollection.Observe(__instance, item); } catch (Exception error) { Plugin.WarnOnce("Collect appearance", error); } try { if (__state.FirstUnlockItem) { AppearanceCollection.ObserveUnlock(__instance, item); } } catch (Exception error2) { Plugin.WarnOnce("Unlock monster appearance", error2); } if (__state.Fresh == null) { return; } try { NewLooks.Add(__instance, __state.Fresh); UnlockNotices.Queue(__instance, __state.Fresh); } catch (Exception error3) { Plugin.WarnOnce("Announce new monster looks", error3); } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class VanillaAppearanceCatalogPatch { [HarmonyPrefix] [HarmonyPriority(800)] private static void Prefix(ObjectDB __instance) { try { AppearanceOrder.CaptureVanilla(__instance); } catch (Exception error) { Plugin.WarnOnce("Vanilla appearance order", error); } } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { ModdedTrophies.Discover(__instance); } } [HarmonyPatch(typeof(ObjectDB), "UpdateRegisters")] internal static class ModdedTrophyCatalogPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { ModdedTrophies.Discover(__instance); } } internal static class CollectionPages { internal const int Columns = 4; internal const int Rows = 6; internal const int Size = 24; internal static int Count(int items) { return Math.Max(1, (Math.Max(0, items) + 24 - 1) / 24); } internal static int Clamp(int page, int items) { return Math.Max(0, Math.Min(page, Count(items) - 1)); } internal static int Index(int page, int cell) { return page * 24 + cell; } } internal static class CollectionWindow { private sealed class Panel { private readonly bool particleCollection; private GameObject root; private WindowDrag drag; private NativeItemCell[] cells; private TMP_InputField search; private Toggle favorites; private Toggle showEnemyGear; private TMP_Text subtitle; private TMP_Text pageLabel; private TMP_Text empty; private Button previous; private Button next; private List<CollectedAppearance> entries = new List<CollectedAppearance>(); private List<CollectedAppearance> matching = new List<CollectedAppearance>(); private List<CollectedAppearance> locked = new List<CollectedAppearance>(); private readonly HashSet<CollectedAppearance> lockedLooks = new HashSet<CollectedAppearance>(); private readonly HashSet<string> shownNew = new HashSet<string>(StringComparer.Ordinal); private readonly Dictionary<CollectedAppearance, string> names = new Dictionary<CollectedAppearance, string>(); private int page; private int journalRevision = -1; private int visualRevision = -1; private int wardrobeRevision = -1; private bool allowUnknown; private bool lockedShown; private bool enemyShown; private bool enemyHidden; private bool iconsPending; private ItemData target; internal FocusLayer Layer; private CollectedAppearance Selected { get { if (!particleCollection) { return IllusionWindow.Selected; } return IllusionWindow.SelectedParticles; } } internal bool IsOpen => Object.op_Implicit((Object)(object)root); internal int Depth { get { if (!Object.op_Implicit((Object)(object)root)) { return -1; } return root.transform.GetSiblingIndex(); } } private GearKind TargetKind { get { if (target == null) { return GearKind.Unsupported; } return Classification.Of(target).Kind; } } internal Panel(bool particles) { particleCollection = particles; } internal bool CoversPointer(Vector2 position) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)drag)) { return drag.Contains(position); } return false; } internal void Toggle() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_0374: Unkn