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 Scry v0.1.0
Scry.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; 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.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SoftReferenceableAssets; using UnityEngine; using UnityEngine.Animations; using UnityEngine.Playables; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [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("isimp")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 isimp")] [assembly: AssemblyDescription("Browse every prefab in the game and preview it: look at creatures, items and pieces, play sounds and effects, fly projectiles and show status effects, without anything entering the world.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0+771480105364c4addacd2cc32d4da17ed7fc2913")] [assembly: AssemblyProduct("Scry")] [assembly: AssemblyTitle("Scry")] [assembly: AssemblyVersion("0.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Scry { internal static class CatalogBuilder { private sealed class Found { public GameObject Prefab; public PrefabTraits Traits; public string Token; public object Icon; public int ExtraLevels; public bool HasWear; public readonly HashSet<string> Users = new HashSet<string>(StringComparer.Ordinal); public readonly List<Origin> UserOrigins = new List<Origin>(); public readonly HashSet<string> Components = new HashSet<string>(StringComparer.Ordinal); public readonly HashSet<string> Fields = new HashSet<string>(StringComparer.Ordinal); } internal struct ListOn { public string Field; public string Label; public EffectList List; } private static readonly Dictionary<Type, FieldInfo[]> EffectFieldsByType; private static readonly HashSet<string> FailedKinds; private static Dictionary<string, List<Recipe>> _recipes; private static readonly Dictionary<string, string> Localized; private static readonly Dictionary<Type, FieldInfo> NamingFields; private static readonly Dictionary<Type, (FieldInfo, string, string)[]> OwnListsByType; private static readonly Dictionary<Type, (FieldInfo, (FieldInfo, string, string)[])[]> NestedListsByType; static CatalogBuilder() { EffectFieldsByType = new Dictionary<Type, FieldInfo[]>(); FailedKinds = new HashSet<string>(); Localized = new Dictionary<string, string>(StringComparer.Ordinal); NamingFields = new Dictionary<Type, FieldInfo>(); OwnListsByType = new Dictionary<Type, (FieldInfo, string, string)[]>(); NestedListsByType = new Dictionary<Type, (FieldInfo, (FieldInfo, string, string)[])[]>(); WorldCaches.Register("CatalogBuilder", Forget); } public static List<Entry> Build() { CatalogJob catalogJob = new CatalogJob(); while (!catalogJob.Advance(double.MaxValue)) { } if (catalogJob.Entries == null) { throw new InvalidOperationException(catalogJob.Failure ?? "the catalog could not be read"); } return catalogJob.Entries; } internal static IEnumerator<string> Steps(CatalogJob job) { CatalogTiming.Reset(); int collections = GC.CollectionCount(0); long started = CatalogTiming.Start(); try { Compatibility.Check(); } catch (Exception ex) { Faults.Tell("the startup check", ex); } CatalogTiming.Add("startup check", started); yield return "Reading the prefabs"; started = CatalogTiming.Start(); ZNetScene instance = ZNetScene.instance; Dictionary<string, Found> registered = new Dictionary<string, Found>(StringComparer.Ordinal); Dictionary<string, Found> effects = new Dictionary<string, Found>(StringComparer.Ordinal); EffectLinks.Clear(); Localized.Clear(); List<GameObject>[] array = new List<GameObject>[2] { instance.m_prefabs, instance.m_nonNetViewPrefabs }; for (int i = 0; i < array.Length; i++) { foreach (GameObject item in array[i]) { if (!((Object)(object)item == (Object)null) && !registered.ContainsKey(((Object)item).name)) { registered[((Object)item).name] = new Found { Prefab = item }; } } } List<GameObject> prefabs = registered.Values.Select((Found f) => f.Prefab).ToList(); CatalogTiming.Add("setup", started); yield return "Reading recipes"; started = CatalogTiming.Start(); Knowledge.Begin(); Relations.Begin(registered.Keys); IndexRecipes(); CatalogTiming.Add("setup", started); yield return $"Reading prefabs: 0 of {registered.Count:N0}"; List<Component> components = new List<Component>(); List<Leftover> leftovers = new List<Leftover>(); int read = 0; string progress = ""; foreach (KeyValuePair<string, Found> item2 in registered) { job.Piece = item2.Key; ReadPrefab(item2.Key, item2.Value, effects, components, leftovers); int i = read + 1; read = i; if (i % 16 == 1) { progress = $"Reading prefabs: {read:N0} of {registered.Count:N0}"; } yield return progress; } components.Clear(); foreach (string item3 in Knowledge.Finish(prefabs)) { yield return "Reading " + item3; } started = CatalogTiming.Start(); List<Entry> entries = new List<Entry>(); ObjectDB instance2 = ObjectDB.instance; if ((Object)(object)instance2 != (Object)null) { foreach (StatusEffect statusEffect in instance2.m_StatusEffects) { if (!((Object)(object)statusEffect == (Object)null)) { try { Origin origin = Origins.StatusEffects.Of(((Object)statusEffect).name); string text = Localize(statusEffect.m_name); Gather(statusEffect, "status effect " + ((Object)statusEffect).name, origin, effects, "se:" + ((Object)statusEffect).name, (text.Length > 0) ? text : ((Object)statusEffect).name); Relations.ReadStatusEffect(statusEffect); entries.Add(new Entry { Name = ((Object)statusEffect).name, DisplayName = text, Kind = Kind.StatusEffect, Origin = origin, Source = statusEffect, Icon = statusEffect.m_icon, Components = new string[1] { ((object)statusEffect).GetType().Name }, ModName = Knowledge.ModName(((Object)statusEffect).name) }); } catch (Exception ex2) { Failed("status effect", ((Object)statusEffect).name, ex2); } } } } CatalogTiming.Add("status effects", started); yield return "Reading the interface's sounds"; started = CatalogTiming.Start(); GatherInterface(effects); CatalogTiming.Add("interface", started); yield return "Reading effects"; HashSet<string> described = new HashSet<string>(StringComparer.Ordinal); int walked = 0; bool helpersRead = false; bool grew; do { grew = false; foreach (Found item4 in new List<Found>(effects.Values)) { string name = ((Object)item4.Prefab).name; if (!registered.ContainsKey(name) && described.Add(name)) { started = CatalogTiming.Start(); components.Clear(); Describe(item4, name, Provenance.Combine(item4.UserOrigins), effects, components); CatalogTiming.Add("describe effects", started); grew = true; int i = walked + 1; walked = i; if (i % 4 == 0) { yield return $"Reading effects: {walked:N0}"; } } } if (grew || helpersRead) { continue; } helpersRead = true; List<Relations.Helper> helpers = new List<Relations.Helper>(); Relations.TakeHelpers(helpers); for (int i2 = 0; i2 < helpers.Count; i2++) { started = CatalogTiming.Start(); GatherHelper(helpers[i2], registered, effects, components); CatalogTiming.Add("helpers", started); if (i2 % 8 == 7) { yield return $"Reading what prefabs spawn: {i2 + 1:N0} of {helpers.Count:N0}"; } } grew = true; } while (grew); components.Clear(); int made = 0; int total = registered.Count + effects.Count; foreach (KeyValuePair<string, Found> item5 in registered) { MakeEntry(entries, item5.Key, item5.Value, effects, registeredOrigin: true); int i = made + 1; made = i; if (i % 16 == 0) { yield return $"Making entries: {made:N0} of about {total:N0}"; } } foreach (KeyValuePair<string, Found> item6 in effects) { if (!registered.ContainsKey(item6.Key)) { MakeEntry(entries, item6.Key, item6.Value, effects, registeredOrigin: false); int i = made + 1; made = i; if (i % 16 == 0) { yield return $"Making entries: {made:N0} of about {total:N0}"; } } } yield return "Pairing leftovers"; started = CatalogTiming.Start(); try { Leftovers.Pair(entries, leftovers); } catch (Exception arg) { Plugin.Log.LogWarning((object)$"Scry could not pair what things leave behind, and leaves them unpaired: {arg}"); } CatalogTiming.Add("leftovers", started); yield return "Linking entries"; started = CatalogTiming.Start(); LinkBook book = null; try { book = Relations.Finish(prefabs); } catch (Exception arg2) { Plugin.Log.LogWarning((object)$"Scry could not link entries, and shows them without links: {arg2}"); } CatalogTiming.Add("links finished", started); yield return "Linking entries"; started = CatalogTiming.Start(); IEnumerator<int> linking = null; try { if (book != null) { linking = book.ApplyInSteps(entries, 400).GetEnumerator(); } } catch (Exception arg3) { Plugin.Log.LogWarning((object)$"Scry could not link entries, and shows them without links: {arg3}"); } CatalogTiming.Add("links applied", started); while (linking != null) { started = CatalogTiming.Start(); bool flag = false; try { flag = linking.MoveNext(); } catch (Exception arg4) { Plugin.Log.LogWarning((object)$"Scry could not link all entries, and shows some without links: {arg4}"); } CatalogTiming.Add("links applied", started); if (!flag) { break; } yield return $"Linking entries: {linking.Current:N0}"; } yield return "Grouping entries"; IEnumerator<int> grouping = null; try { grouping = Grouping.Apply(entries, 400).GetEnumerator(); } catch (Exception arg5) { Plugin.Log.LogWarning((object)$"Scry could not group entries, and lists them ungrouped: {arg5}"); } while (grouping != null) { started = CatalogTiming.Start(); bool flag2 = false; try { flag2 = grouping.MoveNext(); } catch (Exception arg6) { Plugin.Log.LogWarning((object)$"Scry could not group all entries, and lists some ungrouped: {arg6}"); } CatalogTiming.Add("grouping", started); if (!flag2) { break; } yield return $"Grouping entries: {grouping.Current:N0}"; } job.Entries = entries; Plugin.Note($"Scry's catalog, by part (ms): {CatalogTiming.Report()}; {GC.CollectionCount(0) - collections} garbage collections meanwhile."); Faults.TellSkipped(); } private static void ReadPrefab(string name, Found found, Dictionary<string, Found> effects, List<Component> components, List<Leftover> leftovers) { components.Clear(); long started = CatalogTiming.Start(); Describe(found, name, Origins.Prefabs.Of(name), effects, components); CatalogTiming.Add("describe", started); if (components.Count != 0) { Knowledge.Read(found.Prefab, components); started = CatalogTiming.Start(); Relations.Read(found.Prefab, components); CatalogTiming.Add("links", started); started = CatalogTiming.Start(); FindLeftovers(found.Prefab, leftovers); CatalogTiming.Add("leftovers", started); } } private static void GatherHelper(Relations.Helper helper, Dictionary<string, Found> registered, Dictionary<string, Found> effects, List<Component> components) { GameObject prefab = helper.Prefab; if ((Object)(object)prefab == (Object)null || registered.ContainsKey(((Object)prefab).name) || effects.ContainsKey(((Object)prefab).name)) { return; } string user = helper.User; if (user.StartsWith("se:", StringComparison.Ordinal)) { return; } components.Clear(); try { prefab.GetComponentsInChildren<Component>(true, components); } catch (Exception ex) { Faults.Skip("effects of what prefabs spawn", ((Object)prefab).name, ex); return; } Origin ownerOrigin = Origins.Prefabs.Of(user); foreach (Component component in components) { if (!((Object)(object)component == (Object)null)) { try { Gather(component, user, ownerOrigin, effects, user, user, helper.Part); } catch (Exception ex2) { Faults.Skip("effects of what prefabs spawn", ((Object)prefab).name, ex2); } } } } private static void MakeEntry(List<Entry> entries, string name, Found found, Dictionary<string, Found> effects, bool registeredOrigin) { long started = CatalogTiming.Start(); try { entries.Add(ToEntry(name, found, effects, registeredOrigin)); } catch (Exception ex) { Failed("entry", name, ex); } CatalogTiming.Add("entries", started); } public static void Forget() { _recipes = null; FailedKinds.Clear(); } private static void Failed(string what, string name, Exception ex) { if (Trouble.IsGameChange(ex)) { Faults.Skip(what + " entries", name, ex); } else if (FailedKinds.Add(what + "|" + ex.GetType().Name + "|" + ex.Message)) { Plugin.Log.LogWarning((object)$"Scry leaves the {what} {name} out of its catalog (said once for this kind of failure): {ex}"); } } private static Entry ToEntry(string name, Found found, Dictionary<string, Found> effects, bool registeredOrigin) { effects.TryGetValue(name, out var value); if (value != null) { found.Traits.FromEffectList = true; } Entry entry = new Entry { Name = name, DisplayName = Localize(found.Token), Kind = Kinds.Of(found.Traits), Group = Kinds.GroupLabel(Kinds.GroupOf(found.Traits)), GroupOrder = (int)Kinds.GroupOf(found.Traits), PlayedIn = ((value != null) ? value.Fields.ToArray() : new string[0]), Empty = Kinds.IsEmpty(found.Traits), ExtraLevels = found.ExtraLevels, HasWear = found.HasWear, Source = found.Prefab, Icon = found.Icon, Origin = (registeredOrigin ? Origins.Prefabs.Of(name) : Provenance.Combine(found.UserOrigins)), Registered = registeredOrigin, Components = found.Components.ToArray(), Biomes = Knowledge.Biomes(name), ModName = Knowledge.ModName(name) }; if (entry.Kind == Kind.Other) { Group obj = Groups.Role(found.Traits); entry.Group = obj.Name; entry.GroupOrder = obj.Order; } if ((Object)(object)found.Prefab != (Object)null) { GameObject prefab = found.Prefab; entry.LooksFrom(delegate { try { int defaultLook; return (Names: Variants.Of(prefab, out defaultLook), Default: defaultLook); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Scry could not work out the looks of " + name + ", and offers none: " + ex.Message)); throw; } }); entry.Stations = StationsOf(found.Prefab); } if (value != null) { entry.UsedBy.AddRange(value.Users); entry.UsedBy.Sort(StringComparer.OrdinalIgnoreCase); } return entry; } private static StationUse[] StationsOf(GameObject prefab) { List<StationUse> list = new List<StationUse>(); if ((Object)(object)prefab.GetComponent<ItemDrop>() != (Object)null && _recipes != null && _recipes.TryGetValue(((Object)prefab).name, out var value)) { foreach (Recipe item in value) { list.Add(((Object)(object)item.m_craftingStation != (Object)null) ? new StationUse(((Object)((Component)item.m_craftingStation).gameObject).name, Localize(item.m_craftingStation.m_name), Math.Max(1, item.m_minStationLevel)) : new StationUse("hand", "By hand", 1)); } } Piece component = prefab.GetComponent<Piece>(); if ((Object)(object)component != (Object)null && (Object)(object)component.m_craftingStation != (Object)null) { list.Add(new StationUse(((Object)((Component)component.m_craftingStation).gameObject).name, Localize(component.m_craftingStation.m_name), 1)); } foreach (Making making in Knowledge.MadeOf(((Object)prefab).name)) { if (!list.Exists((StationUse u) => u.Name == making.Station)) { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(making.Station) : null); string shown = (((Object)(object)val != (Object)null) ? Localize(val.GetComponent<Piece>()?.m_name) : ""); list.Add(new StationUse(making.Station, shown, 1)); } } return list.ToArray(); } private static void IndexRecipes() { _recipes = new Dictionary<string, List<Recipe>>(StringComparer.Ordinal); ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return; } foreach (Recipe recipe in instance.m_recipes) { if (!((Object)(object)recipe == (Object)null) && recipe.m_enabled && !((Object)(object)recipe.m_item == (Object)null)) { string name = ((Object)((Component)recipe.m_item).gameObject).name; if (!_recipes.TryGetValue(name, out var value)) { value = (_recipes[name] = new List<Recipe>()); } value.Add(recipe); } } } private static void Describe(Found found, string owner, Origin ownerOrigin, Dictionary<string, Found> effects, List<Component> components) { PrefabTraits traits = (found.Traits = new PrefabTraits()); try { found.Prefab.GetComponentsInChildren<Component>(true, components); } catch (Exception ex) { components.Clear(); Faults.Skip("effect lists", owner, ex); return; } foreach (Component component in components) { if ((Object)(object)component == (Object)null) { continue; } try { Note(component, found, traits); found.Components.Add(((object)component).GetType().Name); Gather(component, owner, ownerOrigin, effects, owner, owner); if (component is Humanoid) { foreach (GameObject item in Relations.CarriedItems(found.Prefab)) { SharedData val = item.GetComponent<ItemDrop>()?.m_itemData?.m_shared; if (val != null) { string text = AttackName(item); Gather(val, owner, ownerOrigin, effects, owner, owner, text); if (val.m_attack != null) { Gather(val.m_attack, owner, ownerOrigin, effects, owner, owner, text); } if (val.m_secondaryAttack != null) { Gather(val.m_secondaryAttack, owner, ownerOrigin, effects, owner, owner, text + " (second)"); } } } } ItemDrop val2 = (ItemDrop)(object)((component is ItemDrop) ? component : null); if (val2 != null && val2.m_itemData?.m_shared != null) { SharedData shared = val2.m_itemData.m_shared; Gather(shared, owner, ownerOrigin, effects, owner, owner); if (shared.m_attack != null) { Gather(shared.m_attack, owner, ownerOrigin, effects, owner, owner, "Attack"); } if (shared.m_secondaryAttack != null) { Gather(shared.m_secondaryAttack, owner, ownerOrigin, effects, owner, owner, "Second attack"); } } } catch (Exception ex2) { Faults.Skip("effect lists", owner, ex2); } } } private static void Note(Component component, Found found, PrefabTraits traits) { if (component is Interactable) { traits.IsUsable = true; } if (!(component is OfferingBowl)) { if (!(component is Ragdoll)) { if (!(component is Container)) { Character val = (Character)(object)((component is Character) ? component : null); if (val == null) { ItemDrop val2 = (ItemDrop)(object)((component is ItemDrop) ? component : null); if (val2 == null) { Piece val3 = (Piece)(object)((component is Piece) ? component : null); if (val3 == null) { if (!(component is Projectile)) { if (!(component is ParticleSystem)) { if (component is ParticleSystemRenderer) { return; } if (!(component is Renderer)) { if (!(component is Light)) { if (!(component is AudioSource) && !(component is ZSFX)) { Collider val4 = (Collider)(object)((component is Collider) ? component : null); if (val4 == null) { if (!(component is TreeBase)) { if (!(component is TreeLog)) { if (!(component is MineRock) && !(component is MineRock5)) { if (!(component is Pickable) && !(component is PickableItem)) { if (!(component is Plant)) { Destructible val5 = (Destructible)(object)((component is Destructible) ? component : null); if (val5 == null) { DropOnDestroyed val6 = (DropOnDestroyed)(object)((component is DropOnDestroyed) ? component : null); if (val6 == null) { if (!(component is SpawnArea) && !(component is CreatureSpawner)) { LevelEffects val7 = (LevelEffects)(object)((component is LevelEffects) ? component : null); if (val7 == null) { WearNTear val8 = (WearNTear)(object)((component is WearNTear) ? component : null); if (val8 != null) { found.HasWear |= ((Object)(object)val8.m_worn != (Object)null && (Object)(object)val8.m_worn != (Object)(object)val8.m_new) || ((Object)(object)val8.m_broken != (Object)null && (Object)(object)val8.m_broken != (Object)(object)val8.m_new); } } else { found.ExtraLevels = Math.Max(found.ExtraLevels, val7.m_levelSetups?.Count ?? 0); } } else { traits.HasSpawner = true; } } else if (val6.m_dropWhenDestroyed?.m_drops != null && val6.m_dropWhenDestroyed.m_drops.Count > 0) { traits.HasDrops = true; } } else { traits.HasDestructible = true; if ((Object)(object)Knowledge.MinedInside(val5.m_spawnWhenDestroyed) != (Object)null) { traits.BreaksIntoResource = true; } } } else { traits.HasResource = true; traits.HasPlant = true; } } else { traits.HasResource = true; traits.IsPicked = true; } } else { traits.HasResource = true; traits.IsMined = true; } } else { traits.HasResource = true; traits.IsLog = true; } } else { traits.HasResource = true; traits.IsTree = true; } } else if (!val4.isTrigger) { traits.HasSolidCollider = true; } } else { traits.HasAudio = true; } } else { traits.HasLight = true; } } else { traits.HasRenderer = true; } } else { traits.HasParticles = true; } } else { traits.HasProjectile = true; } } else { if (((Behaviour)val3).enabled) { traits.HasPiece = true; } found.Token = found.Token ?? val3.m_name; found.Icon = found.Icon ?? val3.m_icon; } return; } traits.HasItemDrop = true; SharedData val9 = val2.m_itemData?.m_shared; if (val9 != null) { found.Token = found.Token ?? val9.m_name; if (found.Icon == null && val9.m_icons != null && val9.m_icons.Length != 0) { found.Icon = val9.m_icons[0]; } } } else { traits.HasCharacter = true; found.Token = found.Token ?? val.m_name; } } else { traits.HasContainer = true; } } else { traits.HasRagdoll = true; } } else { traits.IsAltar = true; } } internal static string GameName(GameObject item) { string text = ((!((Object)(object)item != (Object)null)) ? null : item.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name); if (string.IsNullOrEmpty(text) || !text.StartsWith("$", StringComparison.Ordinal)) { return null; } string text2 = Localize(text); if (text2.Length <= 0 || text2.StartsWith("$", StringComparison.Ordinal)) { return null; } return text2; } internal static string AttackName(GameObject item) { SharedData val = item.GetComponent<ItemDrop>()?.m_itemData?.m_shared; string text = ((val != null) ? Localize(val.m_name) : ""); if (text.Length <= 0 || text.StartsWith("$", StringComparison.Ordinal)) { return ((Object)item).name; } return text; } private static void FindLeftovers(GameObject prefab, List<Leftover> found) { if ((Object)(object)prefab == (Object)null) { return; } try { Character component = prefab.GetComponent<Character>(); if ((Object)(object)component != (Object)null) { Thrown(component.m_deathEffects, prefab, "remains", ragdolls: true); } TreeBase component2 = prefab.GetComponent<TreeBase>(); if ((Object)(object)component2 != (Object)null) { Add(component2.m_logPrefab, prefab, "log"); Add(component2.m_stubPrefab, prefab, "stump"); Thrown(component2.m_destroyedEffect, prefab, "debris", ragdolls: false); } TreeLog component3 = prefab.GetComponent<TreeLog>(); if ((Object)(object)component3 != (Object)null) { Add(component3.m_subLogPrefab, prefab, "half"); Thrown(component3.m_destroyedEffect, prefab, "debris", ragdolls: false); } Destructible component4 = prefab.GetComponent<Destructible>(); if ((Object)(object)component4 != (Object)null) { Add(component4.m_spawnWhenDestroyed, prefab, "broken"); Thrown(component4.m_destroyedEffect, prefab, "debris", ragdolls: false); } Thrown(prefab.GetComponent<WearNTear>()?.m_destroyedEffect, prefab, "debris", ragdolls: false); Thrown(prefab.GetComponent<MineRock>()?.m_destroyedEffect, prefab, "debris", ragdolls: false); Thrown(prefab.GetComponent<MineRock5>()?.m_destroyedEffect, prefab, "debris", ragdolls: false); } catch (Exception ex) { Faults.Skip("what things leave behind", ((Object)prefab).name, ex); } void Add(GameObject thing, GameObject owner, string role) { if ((Object)(object)thing != (Object)null && (Object)(object)owner != (Object)null) { found.Add(new Leftover(((Object)thing).name, ((Object)owner).name, role)); } } void Thrown(EffectList list, GameObject owner, string role, bool ragdolls) { if (list?.m_effectPrefabs != null) { EffectData[] effectPrefabs = list.m_effectPrefabs; for (int i = 0; i < effectPrefabs.Length; i++) { GameObject val = effectPrefabs[i]?.m_prefab; if (!((Object)(object)val == (Object)null)) { if (ragdolls && (Object)(object)val.GetComponent<Ragdoll>() != (Object)null) { Add(val, owner, "ragdoll"); } else if (Ghost.IsDebris(val)) { Add(val, owner, role); } } } } } } public static string Localize(string token) { if (string.IsNullOrEmpty(token)) { return ""; } if (Localized.TryGetValue(token, out var value)) { return value; } string text; try { text = Naming.Plain((Localization.instance != null) ? Localization.instance.Localize(token) : token).Trim(); if (string.IsNullOrEmpty(text) || text.StartsWith("[", StringComparison.Ordinal) || text.StartsWith("$", StringComparison.Ordinal)) { text = ""; } } catch { text = ""; } if (Localized.Count > 20000) { Localized.Clear(); } Localized[token] = text; return text; } private static void GatherInterface(Dictionary<string, Found> effects) { HashSet<Transform> hashSet = new HashSet<Transform>(); AddRoot((Component)(object)InventoryGui.instance, hashSet); AddRoot((Component)(object)Hud.instance, hashSet); AddRoot((Component)(object)StoreGui.instance, hashSet); AddRoot((Component)(object)Minimap.instance, hashSet); AddRoot((Component)(object)MessageHud.instance, hashSet); AddRoot((Component)(object)Menu.instance, hashSet); AddRoot((Component)(object)Chat.instance, hashSet); foreach (Transform item in hashSet) { Component[] componentsInChildren = ((Component)item).GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { try { Gather(val, "(interface)", Origin.Vanilla, effects, null, "(interface)"); } catch (Exception ex) { Faults.Skip("interface sounds", "(interface)", ex); } } } } } private static void AddRoot(Component part, HashSet<Transform> roots) { if ((Object)(object)part != (Object)null) { roots.Add(part.transform.root); } } private static void Gather(object owner, string ownerName, Origin ownerOrigin, Dictionary<string, Found> effects, string ownerKey, string shown, string part = null) { foreach (ListOn item in ListsOn(owner)) { EffectList list = item.List; if (list.m_effectPrefabs == null || list.m_effectPrefabs.Length == 0) { continue; } string text = item.Label; if (part != null) { text = part + ": " + text.ToLowerInvariant(); } EffectLinks.Note(list, shown, ownerKey, text); EffectData[] effectPrefabs = list.m_effectPrefabs; for (int i = 0; i < effectPrefabs.Length; i++) { GameObject val = effectPrefabs[i]?.m_prefab; if (!((Object)(object)val == (Object)null)) { if (!effects.TryGetValue(((Object)val).name, out var value)) { value = new Found { Prefab = val }; effects[((Object)val).name] = value; } if (value.Users.Add(ownerName)) { value.UserOrigins.Add(ownerOrigin); } value.Fields.Add(((ownerName == "(interface)") ? "ui:" : (ownerName.StartsWith("status effect ", StringComparison.Ordinal) ? "se:" : "")) + item.Field); } } } } internal static IEnumerable<ListOn> ListsOn(object owner) { (FieldInfo Field, string Name, string Label)[] array = OwnLists(owner.GetType()); for (int i = 0; i < array.Length; i++) { (FieldInfo Field, string Name, string Label) tuple = array[i]; FieldInfo item = tuple.Field; string item2 = tuple.Name; string item3 = tuple.Label; object? value = item.GetValue(owner); EffectList val = (EffectList)((value is EffectList) ? value : null); if (val != null) { yield return new ListOn { Field = item2, Label = item3, List = val }; } } (FieldInfo Outer, (FieldInfo Field, string Name, string Label)[] Inner)[] array2 = NestedLists(owner.GetType()); for (int i = 0; i < array2.Length; i++) { (FieldInfo, (FieldInfo, string, string)[]) tuple2 = array2[i]; FieldInfo outer = tuple2.Item1; array = tuple2.Item2; object value2 = outer.GetValue(owner); if (value2 == null) { continue; } (FieldInfo Field, string Name, string Label)[] array3; if (value2 is IEnumerable enumerable) { foreach (object item4 in enumerable) { if (item4 == null) { continue; } string of = WhatFor(item4); array3 = array; for (int j = 0; j < array3.Length; j++) { (FieldInfo Field, string Name, string Label) tuple3 = array3[j]; FieldInfo item5 = tuple3.Field; string item6 = tuple3.Name; string item7 = tuple3.Label; object? value3 = item5.GetValue(item4); EffectList val2 = (EffectList)((value3 is EffectList) ? value3 : null); if (val2 != null) { yield return new ListOn { Field = item6, Label = ((of.Length > 0) ? Naming.NestedListLabel(outer.Name, item5.Name, of) : item7), List = val2 }; } } } continue; } array3 = array; for (int j = 0; j < array3.Length; j++) { (FieldInfo Field, string Name, string Label) tuple4 = array3[j]; FieldInfo item8 = tuple4.Field; string item9 = tuple4.Name; string item10 = tuple4.Label; object? value4 = item8.GetValue(value2); EffectList val3 = (EffectList)((value4 is EffectList) ? value4 : null); if (val3 != null) { yield return new ListOn { Field = item9, Label = item10, List = val3 }; } } } } private static string WhatFor(object item) { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown Type type = item.GetType(); if (!NamingFields.TryGetValue(type, out var value)) { value = null; try { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsNotSerialized && (fieldInfo.IsPublic || ((MemberInfo)fieldInfo).GetCustomAttribute<SerializeField>() != null) && (fieldInfo.FieldType.IsEnum || typeof(Object).IsAssignableFrom(fieldInfo.FieldType))) { value = fieldInfo; break; } } } catch (Exception ex) { Faults.Skip("reading of a type's fields", type.Name, ex); } NamingFields[type] = value; } if (value == null) { return ""; } object value2 = value.GetValue(item); ItemDrop val = (ItemDrop)((value2 is ItemDrop) ? value2 : null); Component val2; Object val3; if (val == null) { val2 = (Component)((value2 is Component) ? value2 : null); if (val2 == null) { val3 = (Object)((value2 is Object) ? value2 : null); if (val3 != null) { goto IL_019c; } if (!(value2 is Enum obj)) { goto IL_0203; } if (Convert.ToInt64(obj) == 0L) { return ""; } return string.Join(", ", obj.ToString().Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries).Select(Naming.FieldLabel)); } } else { if ((Object)(object)val != (Object)null) { string text = Naming.Plain(Localize(val.m_itemData?.m_shared?.m_name ?? "")); if (text.Length <= 0 || text.StartsWith("$", StringComparison.Ordinal)) { return ((Object)((Component)val).gameObject).name; } return text; } val2 = (Component)value2; } if (!((Object)(object)val2 != (Object)null)) { val3 = (Object)value2; goto IL_019c; } return ((Object)val2.gameObject).name; IL_0203: return ""; IL_019c: if (val3 != (Object)null) { return val3.name; } goto IL_0203; } private static (FieldInfo Field, string Name, string Label)[] OwnLists(Type type) { if (OwnListsByType.TryGetValue(type, out var value)) { return value; } value = (from f in EffectFields(type) select (f: f, Name: f.Name, Naming.EffectListLabel(f.Name))).ToArray(); OwnListsByType[type] = value; return value; } private static (FieldInfo Outer, (FieldInfo Field, string Name, string Label)[] Inner)[] NestedLists(Type type) { if (NestedListsByType.TryGetValue(type, out var value)) { return value; } List<(FieldInfo, (FieldInfo, string, string)[])> list = new List<(FieldInfo, (FieldInfo, string, string)[])>(); try { Type type2 = type; while (type2 != null && type2 != typeof(object) && type2 != typeof(MonoBehaviour) && type2 != typeof(Component) && type2 != typeof(ScriptableObject)) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo outer in fields) { if (outer.IsNotSerialized || (!outer.IsPublic && ((MemberInfo)outer).GetCustomAttribute<SerializeField>() == null)) { continue; } Type fieldType = outer.FieldType; Type type3 = (fieldType.IsArray ? fieldType.GetElementType() : ((fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>)) ? fieldType.GetGenericArguments()[0] : fieldType)); if (HoldsLists(type3)) { (FieldInfo, string, string)[] array = (from f in EffectFields(type3) select (f: f, outer.Name + "." + f.Name, Naming.NestedListLabel(outer.Name, f.Name))).ToArray(); if (array.Length != 0) { list.Add((outer, array)); } } } type2 = type2.BaseType; } } catch (Exception ex) { Faults.Skip("reading of a type's fields", type.Name, ex); list.Clear(); } value = list.ToArray(); NestedListsByType[type] = value; return value; } private static bool HoldsLists(Type type) { if (type == null || type.IsPrimitive || type.IsEnum || type == typeof(string) || !type.IsSerializable || type.Namespace != null) { return false; } if (typeof(Object).IsAssignableFrom(type)) { return false; } if (type != typeof(EffectList) && type != typeof(DropTable) && type != typeof(Attack) && type != typeof(ItemData)) { return type != typeof(SharedData); } return false; } internal static FieldInfo[] EffectFields(Type type) { if (EffectFieldsByType.TryGetValue(type, out var value)) { return value; } List<FieldInfo> list = new List<FieldInfo>(); try { Type type2 = type; while (type2 != null && type2 != typeof(object)) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(EffectList)) { list.Add(fieldInfo); } } type2 = type2.BaseType; } } catch (Exception ex) { Faults.Skip("reading of a type's fields", type.Name, ex); list.Clear(); } value = list.ToArray(); EffectFieldsByType[type] = value; return value; } } internal sealed class CatalogJob { private readonly ZNetScene _scene = ZNetScene.instance; private readonly IEnumerator<string> _steps; private readonly FrameShare _share = new FrameShare(4.0); private readonly Stopwatch _all = new Stopwatch(); private const double LongPieceMs = 8.0; private readonly List<(string What, double Ms)> _long = new List<(string, double)>(); internal string Piece; public List<Entry> Entries { get; internal set; } public ZNetScene Scene => _scene; public string Failure { get; private set; } public string Progress { get; private set; } = "Reading the catalog"; public bool Done { get { if (Entries == null) { return Failure != null; } return true; } } public double WorkMs { get; private set; } public int Frames { get; private set; } public double ElapsedMs => _all.Elapsed.TotalMilliseconds; public CatalogJob() { _steps = CatalogBuilder.Steps(this); } public bool Advance(double budgetMs) { if (Done) { return true; } if (ZNetScene.instance != _scene) { Failure = "the world was left"; return true; } _all.Start(); Stopwatch stopwatch = Stopwatch.StartNew(); int num = 0; try { while (_share.MayBegin(stopwatch.Elapsed.TotalMilliseconds, num, budgetMs)) { double totalMilliseconds = stopwatch.Elapsed.TotalMilliseconds; string progress = Progress; Piece = null; bool num2 = _steps.MoveNext(); double num3 = stopwatch.Elapsed.TotalMilliseconds - totalMilliseconds; _share.Took(num3); if (num3 > 8.0 && Plugin.LogPreviews) { _long.Add(((Piece != null) ? ("the prefab " + Piece) : progress, num3)); } num++; if (!num2) { if (Entries == null) { Failure = "it ended without a catalog"; } break; } Progress = _steps.Current; } } catch (Exception ex) { Failure = ex.ToString(); } WorkMs += stopwatch.Elapsed.TotalMilliseconds; Frames++; if (Done) { _all.Stop(); _steps.Dispose(); if (_long.Count > 0) { Plugin.Note(string.Format("Scry's catalog pieces over {0:0} ms ({1}): {2}.", 8.0, _long.Count, string.Join(", ", from p in _long.OrderByDescending(((string What, double Ms) p) => p.Ms).Take(25) select $"{p.What} {p.Ms:0}"))); } } return Done; } } internal static class CatalogTiming { private static readonly Dictionary<string, double> Parts = new Dictionary<string, double>(); public static long Start() { return Stopwatch.GetTimestamp(); } public static void Add(string part, long started) { double num = (double)(Stopwatch.GetTimestamp() - started) * 1000.0 / (double)Stopwatch.Frequency; Parts.TryGetValue(part, out var value); Parts[part] = value + num; } public static void Reset() { Parts.Clear(); } public static string Report() { return string.Join(", ", from p in Parts orderby p.Value descending select $"{p.Key} {p.Value:0}"); } } internal static class EffectLinks { private static readonly Dictionary<string, List<EffectUse>> Uses; private static readonly HashSet<(EffectList, string)> Noted; private static readonly Dictionary<string, List<PlaysInRow>> Rows; static EffectLinks() { Uses = new Dictionary<string, List<EffectUse>>(StringComparer.Ordinal); Noted = new HashSet<(EffectList, string)>(); Rows = new Dictionary<string, List<PlaysInRow>>(StringComparer.Ordinal); WorldCaches.Register("EffectLinks", Clear); } public static void Clear() { Uses.Clear(); Noted.Clear(); Rows.Clear(); } public static void Note(EffectList list, string owner, string ownerKey, string label) { if (list?.m_effectPrefabs == null || !Noted.Add((list, owner))) { return; } List<string> list2 = new List<string>(); EffectData[] effectPrefabs = list.m_effectPrefabs; foreach (EffectData val in effectPrefabs) { if (val != null && val.m_enabled && (Object)(object)val.m_prefab != (Object)null) { list2.Add(((Object)val.m_prefab).name); } } if (list2.Count == 0) { return; } EffectUse effectUse = new EffectUse(owner, ownerKey, label, list2, list); string[] members = effectUse.Members; foreach (string key in members) { if (!Uses.TryGetValue(key, out var value)) { value = new List<EffectUse>(); Uses[key] = value; } value.Add(effectUse); } } public static List<PlaysInRow> For(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return new List<PlaysInRow>(); } if (Rows.TryGetValue(prefabName, out var value)) { return value; } value = (Uses.TryGetValue(prefabName, out var value2) ? PlaysIn.Rows(value2) : new List<PlaysInRow>()); Rows[prefabName] = value; return value; } } internal static class Grouping { private static readonly HashSet<string> Told; private static readonly HashSet<string> UsedByLinks; private static Dictionary<PieceCategory, string> _categoryNames; static Grouping() { Told = new HashSet<string>(); UsedByLinks = new HashSet<string>(StringComparer.Ordinal) { "Footstep of", "Played by an animation of", "Spawned by", "Carried by", "Shot from" }; WorldCaches.Register("Grouping", Forget); } public static IEnumerable<int> Apply(List<Entry> entries, int slice) { Dictionary<string, Entry> byName = new Dictionary<string, Entry>(StringComparer.Ordinal); foreach (Entry entry2 in entries) { if (entry2.Kind != Kind.StatusEffect && !byName.ContainsKey(entry2.Name)) { byName[entry2.Name] = entry2; } } HashSet<string> weather = new HashSet<string>(StringComparer.Ordinal); try { Weather(weather); } catch (Exception ex) { Tell("the weather", ex); } Dictionary<string, Group> menus; try { menus = Menus(); } catch (Exception ex2) { Tell("the build menus", ex2); menus = new Dictionary<string, Group>(); } for (int i = 0; i < entries.Count; i++) { Entry entry = entries[i]; try { Group? obj = Of(entry, byName, menus, weather); if (obj.HasValue) { entry.Group = obj.Value.Name; entry.GroupOrder = obj.Value.Order; } } catch (Exception ex3) { Tell("an entry", ex3); } if ((i + 1) % slice == 0) { yield return i + 1; } } try { Groups.FollowSpawners(entries, "Spawned by", Groups.Projectile(new Shooter[0]).Name); } catch (Exception ex4) { Tell("projectiles spawned by others", ex4); } try { Leftovers.JoinOwnersGroups(entries); } catch (Exception ex5) { Tell("what is left behind", ex5); } if (Plugin.LogPreviews) { Report(entries); } } private static void Report(List<Entry> entries) { foreach (IGrouping<Kind, Entry> item in from e in entries where e.Group.Length > 0 group e by e.Kind) { IEnumerable<string> values = from e in item group e by e.Group into g orderby g.First().GroupOrder select $"{g.Key} {g.Count()}"; Plugin.Note("Scry groups its " + Kinds.Label(item.Key).ToLowerInvariant() + ": " + string.Join(", ", values) + "."); } List<string> list = (from e in entries where (e.Kind == Kind.Effect || e.Kind == Kind.Sound) && e.GroupOrder == Groups.Purpose(new string[0], footstep: false, animation: false).Order select e.Name).OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase).ToList(); List<string> list2 = (from e in entries where e.Kind == Kind.Projectile && e.GroupOrder == Groups.Projectile(new Shooter[0]).Order select e.Name + ((e.Links.Count > 0) ? (" (" + string.Join("/", e.Links.Select((Link l) => l.Group).Distinct()) + ")") : "")).OrderBy<string, string>((string n) => n, StringComparer.OrdinalIgnoreCase).ToList(); if (list2.Count > 0) { Plugin.Note(string.Format("Scry found nothing that fires {0} projectiles, among them: {1}.", list2.Count, string.Join(", ", list2.Take(100)))); } if (list.Count > 0) { Plugin.Note(string.Format("Scry found nothing that plays {0} effects and sounds, among them: {1}.", list.Count, string.Join(", ", list.Take(150)))); } } private static Group Item(Entry entry, GameObject prefab) { ItemDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null); SharedData val2 = ((!((Object)(object)val != (Object)null)) ? null : val.m_itemData?.m_shared); bool carriedByCreature = entry.Links.Any((Link l) => l.Group == "Carried by"); bool obtainable = entry.Stations.Length != 0 || Knowledge.SourceLines(entry.Name).Count > 0 || entry.FoundIn.Length != 0 || Knowledge.IsPlacedByWorld(entry.Name) || entry.Links.Any((Link l) => l.Group == "Spawned by"); return Groups.Item(((object)Unsafe.As<ItemType, ItemType>(ref val2?.m_itemType)/*cast due to .constrained prefix*/).ToString(), ((object)Unsafe.As<SkillType, SkillType>(ref val2?.m_skillType)/*cast due to .constrained prefix*/).ToString(), carriedByCreature, obtainable); } public static int FoundInLocations(IEnumerable<Entry> entries) { int order = Groups.CarriedByCreatures.Order; int num = 0; foreach (Entry entry in entries) { if (entry.Kind != Kind.Item || entry.GroupOrder != order || entry.FoundIn.Length == 0) { continue; } try { object source = entry.Source; Group obj = Item(entry, (GameObject)((source is GameObject) ? source : null)); if (obj.Order != order) { entry.Group = obj.Name; entry.GroupOrder = obj.Order; num++; } } catch (Exception ex) { Tell("an entry", ex); } } return num; } private static Group? Of(Entry entry, Dictionary<string, Entry> byName, Dictionary<string, Group> menus, HashSet<string> weather) { object source = entry.Source; GameObject val = (GameObject)((source is GameObject) ? source : null); switch (entry.Kind) { case Kind.Item: return Item(entry, val); case Kind.Creature: { Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null); return ((Object)(object)val2 != (Object)null) ? Groups.Creature(((object)Unsafe.As<Faction, Faction>(ref val2.m_faction)/*cast due to .constrained prefix*/).ToString(), val2.m_boss) : Groups.Creature(null, boss: false); } case Kind.Piece: { Group value2; return menus.TryGetValue(entry.Name, out value2) ? value2 : Groups.InNoMenu; } case Kind.Effect: case Kind.Sound: { bool footstep = entry.Links.Any((Link l) => l.Group == "Footstep of"); bool animation = entry.Links.Any((Link l) => l.Group == "Played by an animation of"); List<string> list = entry.PlayedIn.ToList(); if (weather.Contains(entry.Name)) { list.Add("weather"); } if (Knowledge.IsPlacedByWorld(entry.Name)) { list.Add("ambience"); } if (entry.Links.Any((Link l) => l.Group == "Spawned by" && l.Target.StartsWith("se:", StringComparison.Ordinal))) { list.Add("se:spawned"); } if (entry.LeftBy.Count > 0) { list.Add("left when destroyed"); } Group value = Groups.Purpose(list, footstep, animation); if (value.Order == Groups.Purpose(new string[0], footstep: false, animation: false).Order && Users(entry).Any()) { value = Groups.Purpose(new string[1] { "" }, footstep: false, animation: false); } return value; } case Kind.Projectile: return Groups.Projectile(Shooters(entry, byName)); case Kind.StatusEffect: return Groups.StatusEffect(Givers(entry, byName)); default: return null; } } private static IEnumerable<string> Users(Entry entry) { foreach (string item in entry.UsedBy) { yield return item; } foreach (Link link in entry.Links) { if (UsedByLinks.Contains(link.Group)) { yield return link.Target; } } } private static IEnumerable<Giver> Givers(Entry entry, Dictionary<string, Entry> byName) { foreach (Link link in entry.Links) { if (link.Group != "Given by") { continue; } Entry value; Kind kind = (byName.TryGetValue(link.Target, out value) ? value.Kind : Kind.Other); if (link.Notes.Count == 0) { yield return new Giver(kind, ""); } foreach (string note in link.Notes) { yield return new Giver(kind, note); } } } private static void Weather(HashSet<string> names) { List<EnvSetup> list = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.m_environments : null); if (list == null) { return; } foreach (EnvSetup item in list) { if (item == null) { continue; } if ((Object)(object)item.m_envObject != (Object)null) { names.Add(((Object)item.m_envObject).name); } if (item.m_psystems == null) { continue; } GameObject[] psystems = item.m_psystems; foreach (GameObject val in psystems) { if ((Object)(object)val != (Object)null) { names.Add(((Object)val).name); } } } } private static IEnumerable<Shooter> Shooters(Entry entry, Dictionary<string, Entry> byName) { foreach (Link link in entry.Links) { if ((!(link.Group != "Shot from") || !(link.Group != "Spawned by")) && byName.TryGetValue(link.Target, out var value)) { object source = value.Source; GameObject val = (GameObject)((source is GameObject) ? source : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); SharedData val3 = ((!((Object)(object)val2 != (Object)null)) ? null : val2.m_itemData?.m_shared); string skill = ((val3 != null) ? ((object)Unsafe.As<SkillType, SkillType>(ref val3.m_skillType)/*cast due to .constrained prefix*/).ToString() : ""); string ammoType = ((val3 != null && ((int)val3.m_itemType == 9 || (int)val3.m_itemType == 23)) ? val3.m_ammoType : ""); bool carriedByCreature = value.Links.Any((Link l) => l.Group == "Carried by") && value.Stations.Length == 0; yield return new Shooter(value.Kind, skill, carriedByCreature, ammoType); } } } private static Dictionary<string, Group> Menus() { //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, Group> dictionary = new Dictionary<string, Group>(StringComparer.Ordinal); List<GameObject> list = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.m_items : null); if (list == null) { return dictionary; } List<(string, bool, PieceTable)> list2 = new List<(string, bool, PieceTable)>(); foreach (GameObject item2 in list) { SharedData val = ((!((Object)(object)item2 != (Object)null)) ? null : item2.GetComponent<ItemDrop>()?.m_itemData?.m_shared); PieceTable val2 = val?.m_buildPieces; if (!((Object)(object)val2 == (Object)null) && val2.m_pieces != null) { bool flag = ((Object)item2).name == "Hammer"; (string, bool, PieceTable) item = (CatalogBuilder.Localize(val.m_name), flag, val2); if (flag) { list2.Insert(0, item); } else { list2.Add(item); } } } for (int i = 0; i < list2.Count; i++) { var (tool, mainTool, val3) = list2[i]; foreach (Piece item3 in (from p in val3.m_pieces where (Object)(object)p != (Object)null select p.GetComponent<Piece>() into p where (Object)(object)p != (Object)null select p).ToList()) { string name = ((Object)((Component)item3).gameObject).name; if (!dictionary.ContainsKey(name)) { int num = ((val3.m_categories != null) ? val3.m_categories.IndexOf(item3.m_category) : (-1)); string text = ((num >= 0 && val3.m_categoryLabels != null && num < val3.m_categoryLabels.Count) ? CatalogBuilder.Localize(val3.m_categoryLabels[num]) : ""); if (text.Length == 0) { text = CategoryName(item3.m_category); } dictionary[name] = Groups.Piece(tool, i, mainTool, text, (num >= 0) ? num : (500 + item3.m_category)); } } } return dictionary; } private unsafe static string CategoryName(PieceCategory category) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Invalid comparison between Unknown and I4 //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (_categoryNames == null) { _categoryNames = new Dictionary<PieceCategory, string>(); try { string[] names = Enum.GetNames(typeof(PieceCategory)); Array values = Enum.GetValues(typeof(PieceCategory)); for (int i = 0; i < names.Length && i < values.Length; i++) { PieceCategory key = (PieceCategory)values.GetValue(i); if (!_categoryNames.ContainsKey(key)) { _categoryNames[key] = names[i]; } } } catch (Exception ex) { Tell("the build tabs' names", ex); } } if ((int)category == 100) { return "Every tab"; } string value; string text = (_categoryNames.TryGetValue(category, out value) ? value : ((object)(*(PieceCategory*)(&category))/*cast due to .constrained prefix*/).ToString()); if (text.IndexOf(' ') < 0) { return Naming.FieldLabel(text); } return text; } public static void Forget() { _categoryNames = null; } private static void Tell(string what, Exception ex) { if (Told.Add(what + "|" + ex.GetType().Name + "|" + ex.Message)) { Plugin.Log.LogWarning((object)("Scry could not group " + what + ", and lists it ungrouped (said once): " + ex.Message)); } } } internal struct Source { public string Text; public string Prefab; public Source(string text, string prefab) { Text = text; Prefab = prefab; } } internal struct Summon { public string Boss; public string Item; public int Count; public string Place; public string PlacePrefab; public static Summon Of(OfferingBowl bowl, string place, string placePrefab) { return new Summon { Boss = (((Object)(object)bowl.m_bossPrefab != (Object)null) ? ((Object)bowl.m_bossPrefab).name : null), Item = (((Object)(object)bowl.m_bossItem != (Object)null) ? ((Object)((Component)bowl.m_bossItem).gameObject).name : null), Count = Math.Max(1, bowl.m_bossItems), Place = place, PlacePrefab = placePrefab }; } } internal static class Knowledge { private static readonly Dictionary<string, List<Source>> Where; private static readonly Dictionary<string, HashSet<string>> BiomesOf; private static readonly Dictionary<string, string> ModOf; private static readonly Dictionary<string, List<Source>> ComesFrom; private static readonly HashSet<string> PlacedByWorld; private static readonly Dictionary<Type, FieldInfo[]> DropTableFields; private static readonly Dictionary<string, List<Source>> SpawnPointLines; private static readonly Dictionary<string, List<Source>> DropLines; private static readonly MakerBook Made; private static readonly Dictionary<GameObject, string> ShownNames; private static readonly Dictionary<string, GameObject> Bosses; private static readonly Dictionary<string, List<GameObject>> Turned; private static readonly List<Summon> Altars; private static readonly UseBook Uses; private static readonly Dictionary<Type, FieldInfo[]> FuelFieldsByType; private const string JotunnGuid = "com.jotunn.jotunn"; private static readonly Dictionary<(Type, string), PropertyInfo> Properties; private static FieldRef<List<SpawnSystem>> _spawnSystems; private static bool _spawnSystemsTried; private static readonly List<(GameObject Point, GameObject Creature, SpawnFacts Spawn)> SpawnPointsLeft; private static readonly Dictionary<Type, FieldInfo[]> ConversionFields; private static readonly List<(string Prefab, string Effect, string How)> GiverList; private static readonly Dictionary<Type, FieldInfo[]> EffectRefFields; public static string UpgradeStation { get; private set; } public static string UpgradeStationName { get; private set; } static Knowledge() { Where = new Dictionary<string, List<Source>>(StringComparer.Ordinal); BiomesOf = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal); ModOf = new Dictionary<string, string>(StringComparer.Ordinal); ComesFrom = new Dictionary<string, List<Source>>(StringComparer.Ordinal); PlacedByWorld = new HashSet<string>(StringComparer.Ordinal); DropTableFields = new Dictionary<Type, FieldInfo[]>(); SpawnPointLines = new Dictionary<string, List<Source>>(StringComparer.Ordinal); DropLines = new Dictionary<string, List<Source>>(StringComparer.Ordinal); Made = new MakerBook(); ShownNames = new Dictionary<GameObject, string>(); Bosses = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); Turned = new Dictionary<string, List<GameObject>>(StringComparer.Ordinal); Altars = new List<Summon>(); Uses = new UseBook(); FuelFieldsByType = new Dictionary<Type, FieldInfo[]>(); Properties = new Dictionary<(Type, string), PropertyInfo>(); SpawnPointsLeft = new List<(GameObject, GameObject, SpawnFacts)>(); ConversionFields = new Dictionary<Type, FieldInfo[]>(); GiverList = new List<(string, string, string)>(); EffectRefFields = new Dictionary<Type, FieldInfo[]>(); WorldCaches.Register("Knowledge", Begin); } public static IReadOnlyList<Source> SourceLines(string item) { if (!ComesFrom.TryGetValue(item, out var value)) { return Array.Empty<Source>(); } return value; } public static IReadOnlyList<Source> WhereLines(string prefab) { if (!Where.TryGetValue(prefab, out var value)) { return Array.Empty<Source>(); } return value; } public static string[] Biomes(string prefab) { if (!BiomesOf.TryGetValue(prefab, out var value)) { return new string[0]; } return value.ToArray(); } public static bool IsPlacedByWorld(string prefab) { return PlacedByWorld.Contains(prefab); } public static string ModName(string name) { if (!ModOf.TryGetValue(name, out var value)) { return ""; } return value; } public static IReadOnlyList<Making> MadeOf(string item) { return Made.Of(item); } public static IReadOnlyList<Making> MadeAt(string station) { return Made.At(station); } public static IReadOnlyList<GameObject> TurnedFrom(string prefab) { if (!Turned.TryGetValue(prefab, out var value)) { return Array.Empty<GameObject>(); } return value; } public static GameObject MinedInside(GameObject broken) { if ((Object)(object)broken == (Object)null) { return null; } if (!((Object)(object)broken.GetComponent<MineRock5>() != (Object)null) && !((Object)(object)broken.GetComponent<MineRock>() != (Object)null)) { return null; } return broken; } private static void Turns(GameObject prefab, List<Component> components) { foreach (Component component in components) { if ((Object)(object)component == (Object)null || (Object)(object)component.gameObject != (Object)(object)prefab) { continue; } TreeBase val = (TreeBase)(object)((component is TreeBase) ? component : null); if (val == null) { TreeLog val2 = (TreeLog)(object)((component is TreeLog) ? component : null); if (val2 == null) { Destructible val3 = (Destructible)(object)((component is Destructible) ? component : null); if (val3 != null) { Note(MinedInside(val3.m_spawnWhenDestroyed)); } } else { Note(val2.m_subLogPrefab); } } else { Note(val.m_logPrefab); } } void Note(GameObject into) { if (!((Object)(object)into == (Object)null)) { if (!Turned.TryGetValue(((Object)into).name, out var value)) { value = (Turned[((Object)into).name] = new List<GameObject>()); } if (!value.Contains(prefab)) { value.Add(prefab); } } } } public static IEnumerable<Summon> Summons() { return Altars.Concat(Locations.Summons); } public static string BossOf(string key) { if (key == null || !Bosses.TryGetValue(key, out var value)) { return null; } return ShownName(value); } public static string BossPrefabOf(string key) { if (key == null || !Bosses.TryGetValue(key, out var value)) { return null; } return ((Object)value).name; } public static IReadOnlyList<UseGroup> UsesOf(string item) { return Uses.Of(item); } public static void Begin() { Where.Clear(); BiomesOf.Clear(); ModOf.Clear(); ComesFrom.Clear(); PlacedByWorld.Clear(); GiverList.Clear(); SpawnPointLines.Clear(); DropLines.Clear(); Made.Clear(); ShownNames.Clear(); Uses.Clear(); Bosses.Clear(); Altars.Clear(); Turned.Clear(); SpawnPointsLeft.Clear(); UpgradeStation = null; UpgradeStationName = null; } public static void Read(GameObject prefab, List<Component> components) { long started = CatalogTiming.Start(); try { SpawnPoints(prefab, components); } catch (Exception ex) { Failed("nests and spawn points", prefab, ex); } CatalogTiming.Add("spawn points", started); started = CatalogTiming.Start(); try { Drops(prefab, components); } catch (Exception ex2) { Failed("drops", prefab, ex2); } CatalogTiming.Add("drops", started); started = CatalogTiming.Start(); try { Makers(prefab, components); } catch (Exception ex3) { Failed("makers", prefab, ex3); } CatalogTiming.Add("makers", started); started = CatalogTiming.Start(); try { Givers(prefab, components); } catch (Exception ex4) { Failed("status effect givers", prefab, ex4); } CatalogTiming.Add("givers", started); started = CatalogTiming.Start(); try { UsesIn(prefab, components); } catch (Exception ex5) { Failed("uses of items", prefab, ex5); } CatalogTiming.Add("uses", started); started = CatalogTiming.Start(); try { Turns(prefab, components); } catch (Exception ex6) { Failed("what things turn into", prefab, ex6); } CatalogTiming.Add("turns into", started); foreach (Component component in components) { Character val = (Character)(object)((component is Character) ? component : null); if (val != null && !string.IsNullOrEmpty(val.m_defeatSetGlobalKey) && !Bosses.ContainsKey(val.m_defeatSetGlobalKey)) { Bosses[val.m_defeatSetGlobalKey] = prefab; } OfferingBowl val2 = (OfferingBowl)(object)((component is OfferingBowl) ? component : null); if (val2 != null) { Summon item = Summon.Of(val2, Shown(prefab), ((Object)prefab).name); if (item.Boss != null) { Altars.Add(item); } } } } private static void UsesIn(GameObject prefab, List<Component> components) { foreach (Component component in components) { Component val = component; if (val == null) { continue; } Piece val2 = (Piece)(object)((val is Piece) ? val : null); if (val2 == null) { CraftingStation val3 = (CraftingStation)(object)((val is CraftingStation) ? val : null); if (val3 == null) { MonsterAI val4 = (MonsterAI)(object)((val is MonsterAI) ? val : null); if (val4 != null && val4.m_consumeItems != null) { foreach (ItemDrop consumeItem in val4.m_consumeItems) { if ((Object)(object)consumeItem != (Object)null) { Uses.Add(((Object)((Component)consumeItem).gameObject).name, UseKind.EatenBy, ((Object)prefab).name, 0); } } } } else if (val3.m_upgrader && UpgradeStation == null) { UpgradeStation = ((Object)prefab).name; string text = CatalogBuilder.Localize(val3.m_name); UpgradeStationName = ((text.Length > 0) ? text : ((Object)prefab).name); } } else if (val2.m_enabled && val2.m_resources != null) { string place = (((Object)(object)val2.m_craftingStation != (Object)null) ? ((Object)((Component)val2.m_craftingStation).gameObject).name : null); Requirement[] resources = val2.m_resources; foreach (Requirement val5 in resources) { if ((Object)(object)val5?.m_resItem != (Object)null) { Uses.Add(((Object)((Component)val5.m_resItem).gameObject).name, UseKind.Builds, ((Object)prefab).name, val5.m_amount, place); } } } FieldInfo[] array = FuelFields(((object)component).GetType()); for (int i = 0; i < array.Length; i++) { object value = array[i].GetValue(component); ItemDrop val6 = (ItemDrop)((value is ItemDrop) ? value : null); if (val6 != null) { if ((Object)(object)val6 != (Object)null) { Uses.Add(((Object)((Component)val6).gameObject).name, UseKind.Fuels, ((Object)prefab).name, 0); } } else { if (!(value is IEnumerable<ItemDrop> enumerable)) { continue; } foreach (ItemDrop item in enumerable) { if ((Object)(object)item != (Object)null) { Uses.Add(((Object)((Component)item).gameObject).name, UseKind.Fuels, ((Object)prefab).name, 0); } } } } } } public static bool IsToldAsUse(Type type, FieldInfo field) { if (Array.IndexOf(FuelFields(type), field) < 0) { return Array.IndexOf(Conversions(type), field) >= 0; } return true; } private static FieldInfo[] FuelFields(Type type) { if (FuelFieldsByType.TryGetValue(type, out var value)) { return value; } List<FieldInfo> list = new List<FieldInfo>(); try { Type type2 = type; while (type2 != null && type2 != typeof(object) && type2 != typeof(MonoBehaviour)) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { Type fieldType = fieldInfo.FieldType; if ((fieldType == typeof(ItemDrop) || fieldType == typeof(ItemDrop[]) || fieldType == typeof(List<ItemDrop>)) && fieldInfo.Name.IndexOf("fuel", StringComparison.OrdinalIgnoreCase) >= 0) { list.Add(fieldInfo); } } type2 = type2.BaseType; } } catch (Exception ex) { Faults.Skip("reading of a type's fields", type.Name, ex); list.Clear(); } value = list.ToArray(); FuelFieldsByType[type] = value; return value; } private static void Recipes() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return; } foreach (Recipe recipe in instance.m_recipes) { if ((Object)(object)recipe == (Object)null || !recipe.m_enabled || (Object)(object)recipe.m_item == (Object)null || recipe.m_resources == null) { continue; } string place = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)((Component)recipe.m_craftingStation).gameObject).name : "hand"); int num = recipe.m_item.m_itemData?.m_shared?.m_maxQuality ?? 1; Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (!((Object)(object)val?.m_resItem == (Object)null)) { if (!val.m_upgraderResource) { Uses.Add(((Object)((Component)val.m_resItem).gameObject).name, UseKind.Crafts, ((Object)((Component)recipe.m_item).gameObject).name, val.m_amount, place); } else if (num > 1) { Uses.Add(((Object)((Component)val.m_resItem).gameObject).name, UseKind.UpgradesPastTop, ((Object)((Component)recipe.m_item).gameObject).name, val.GetAmount(num + 1)); } } } } } private static void Failed(string what, GameObject prefab, Exception ex) { Faults.Skip(what, ((Object)prefab).name, ex); } public static IEnumerable<string> Finish(List<GameObject> prefabs) { Try("world spawners", WorldSpawners); Try("raids", Raids); Try("spawn points", TellSpawnPoints); Try("nests and spawn points", delegate { Merge(SpawnPointLines, delegate(string prefab, Source line) { Add(prefab, line.Text, line.Prefab); }); }); Try("vegetation", Vegetation); yield return "where things live"; Try("drops", delegate { Merge(DropLines, From); }); Try("recipes items go into", Recipes); Try("traders", Traders); yield return "what makes things"; Try("Jotunn's registry", JotunnMods); yield return "which mod added what"; foreach (string item in BundleMods(prefabs)) { yield return item; } } private static void Merge(Dictionary<string, List<Source>> lines, Action<string, Source> add) { foreach (KeyValuePair<string, List<Source>> line in lines) { foreach (Source item in line.Value) { add(line.Key, item); } } } private static void Try(string what, Action act) { long started = CatalogTiming.Start(); Stopwatch stopwatch = Stopwatch.StartNew(); try { act(); if (stopwatch.ElapsedMilliseconds >= 50) { Plugin.Note($"Scry read {what} in {stopwatch.ElapsedMilliseconds} ms."); } } catch (Exception ex) { if (Trouble.IsGameChange(ex)) { Faults.Skip(what, "this world", ex); } else { Plugin.Log.LogWarning((object)("Scry could not read " + what + ", and leaves it out: " + ex.Message)); } } CatalogTiming.Add(what, started); } private static void Each<T>(IEnumerable<T> list, string what, Func<T, string> name, Action<T> read) where T : class { if (list == null) { return; } foreach (T item in list) { if (item == null) { continue; } try { read(item); } catch (Exception ex) { string prefab; try { prefab = name(item); } catch { prefab = typeof(T).Name; } Faults.Skip(what, prefab, ex); } } } private static void From(string item, Source line) { if (!ComesFrom.TryGetValue(item, out var value)) { value = (ComesFrom[item] = new List<Source>()); } if (!value.Exists((Source l) => l.Text == line.Text)) { value.Add(line); } } private static void Keep(Dictionary<string, List<Source>> lines, GameObject item, string line, string target) { if (!((Object)(object)item == (Object)null)) { if (!lines.TryGetValue(((Object)item).name, out var value)) { value = (lines[((Object)item).name] = new List<Source>()); } value.Add(new Source(line, target)); } } private static void JotunnMods() { Type registry; if (Chainloader.PluginInfos.TryGetValue("com.jotunn.jotunn", out var value) && !((Object)(object)((value != null) ? value.Instance : null) == (Object)null)) { registry = ((object)value.Instance).GetType().Assembly.GetType("Jotunn.Utils.ModRegistry", throwOnError: false); if (!(registry == null)) { Read("GetPrefabs", "Prefab"); Read("GetItems", "ItemPrefab"); Read("GetPieces", "PiecePrefab"); Read("GetStatusEffects", "StatusEffect"); } } static PropertyInfo Property(Type type, string name) { if (!Properties.TryGetValue((type, name), out var value2)) { value2 = (Properties[(type, name)] = AccessTools.Property(type, name)); } return value2; } void Read(string method, string property) { MethodInfo methodInfo = AccessTools.Method(registry, method, new Type[0], (Type[])null); if (methodInfo == null || !(methodInfo.Invoke(null, null) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item != null) { object? obj = Property(item.GetType(), "SourceMod")?.GetValue(item); BepInPlugin val = (BepInPlugin)((obj is BepInPlugin) ? obj : null); object? obj2 = Property(item.GetType(), property)?.GetValue(item); Object val2 = (Object)((obj2 is Object) ? obj2 : null); if (val != null && !(val2 == (Object)null)) { ModOf[val2.name] = val.Name; } } } } } private static IEnumerable<string> BundleMods(List<GameObject> prefabs) { long started = CatalogTiming.Start(); HashSet<string> wanted = new HashSet<string>(from p in prefabs where (Object)(object)p != (Object)null && !ModOf.ContainsKey(((Object)p).name) && Origins.Prefabs.Of(((Object)p).name) == Origin.Mod select ((Object)p).name.ToLowerInvariant()); if (wanted.Count == 0) { yield break; } Dictionary<string, string> bundleOf = new Dictionary<string, string>(); List<AssetBundle> list; try { list = AssetBundle.GetAllLoadedAssetBundles().ToList(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Scry could not read asset bundles: " + ex.Message)); yield break; } foreach (AssetBundle item in list) { try { if ((Object)(object)item == (Object)null || item.isStreamedSceneAssetBundle) { continue; } string[] allAssetNames = item.GetAllAssetNames(); for (int num = 0; num < allAssetNames.Length; num++) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(allAssetNames[num]); if (wanted.Contains(fileNameWithoutExtension) && !bundleOf.ContainsKey(fileNameWithoutExtension)) { bundleOf[fileNameWithoutExtension] = Leaf(((Object)item).name); } } goto IL_018a; } catch (Exception ex2) { Faults.Skip("which mod added what", "a bundle", ex2); goto IL_018a; } IL_018a: CatalogTiming.Add("asset bundles", started); yield return "which mod added what: asset bundles"; started = CatalogTiming.Start(); } if (bundleOf.Count == 0) { yield break; } Dictionary<string, string> modOfBundle = new Dictionary<string, string>(); HashSet<string> needed = new HashSet<string>(bundleOf.Values); foreach (PluginInfo item2 in Chainloader.PluginInfos.Values.ToList()) { object obj; if (item2 == null) { obj = null; } else { BepInPlugin metadata = item2.Metadata; obj = ((metadata != null) ? metadata.Name : null); } string text = (string)obj; if (string.IsNullOrEmpty(text)) { continue; } started = CatalogTiming.Start(); try { Assembly assembly = (((Object)(object)item2.Instance != (Object)null) ? ((object)item2.Instance).GetType().Assembly : null); if (assembly != null) { string[] allAssetNames = assembly.GetManifestResourceNames(); for (int num = 0; num < allAssetNames.Length; num++) { string text2 = allAssetNames[num].ToLowerInvariant(); foreach (string item3 in needed) { if (text2 == item3 || text2.EndsWith("." + item3)) { modOfBundle[item3] = text; } } } } string directoryName = Path.GetDirectoryName(item2.Location); if (!string.IsNullOrEmpty(directoryName) && Directory.Exists(directoryName) && !IsPluginsRoot(directoryName)) { string[] allAssetNames = Directory.GetFiles(directoryName, "*", SearchOption.AllDirectories); foreach (string path in allAssetNames) { string text3 = Path.GetFileName(path).ToLowerInvariant(); string text4 = Path.GetFileNameWithoutExtension(path).ToLowerInvariant(); if (needed.Contains(text3)) { modOfBundle[text3] = text; } else if (needed.Contains(text4)) { modOfBundle[text4] = text; } } } } catch (Exception ex3) { Faults.Skip("which mod added what", text, ex3); } CatalogTiming.Add("asset bundles", started); yield return "which mod added what: mods' files"; } foreach (GameObject prefab in prefabs) { if (!((Object)(object)prefab == (Object)null) && !ModOf.ContainsKey(((Object)prefab).name) && bundleOf.TryGetValue(((Object)prefab).name.ToLowerInvariant(), out var value) && modOfBundle.TryGetValue(value, out var value2)) { ModOf[((Object)prefab).name] = value2; } } } private static string Leaf(string bundleName) { string text = (bundleName ?? "").Replace('\\', '/'); int num = text.LastIndexOf('/'); return ((num >= 0) ? text.Substring(num + 1) : text).ToLowerInvariant(); } private static bool IsPluginsRoot(string folder) { return string.Equals(Path.GetFullPath(folder).TrimEnd('\\', '/'), Path.GetFullPath(Paths.PluginPath).TrimEnd('\\', '/'), StringComparison.OrdinalIgnoreCase); } private static void Add(string prefab, string line, string target = null) { if (!string.IsNullOrEmpty(prefab)) { if (!Where.TryGetValue(prefab, out var value)) { value = (Where[prefab] = new List<Source>()); } if (!value.Exists((Source l) => l.Text == line)) { value.Add(new Source(line, target)); } } } private unsafe static void AddBiomes(string prefab, Biome biome) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!BiomesOf.TryGetValue(prefab, out var value)) { value = (BiomesOf[prefab] = new HashSet<string>()); } foreach (Biome item in Singles(biome)) { value.Add(((object)(*(Biome*)(&item))/*cast due to .constrained prefix*/).ToString()); } } private static IEnumerable<Biome> Singles(Biome biome) { //IL_0008: 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) foreach (Biome value in Enum.GetValues(typeof(Biome))) { int num = (int)value; if (num != 0 && (num & (num - 1)) == 0 && (biome & value) != 0) { yield return value; } } } public static string BiomeName(string biome) { string text = CatalogBuilder.Localize("$biome_" + (biome ?? "").ToLowerInvariant()); if (text.Length <= 0) { return Naming.FieldLabel(biome ?? ""); } return text; } public unsafe static string BiomeNames(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) List<Biome> list = Singles(biome).ToList(); int num = Enum.GetValues(typeof(Biome)).Cast<Biome>().Count(delegate(Biome b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num2 = (int)b; return num2 != 0 && (num2 & (num2 - 1)) == 0; }); if (list.Count == 0) { return "no biome"; } if (list.Count >= num) { return "every biome"; } return string.Join(", ", list.Select((Biome b) => BiomeName(((object)(*(Biome*)(&b))/*cast due to .constrained prefix*/).ToString()))); } private static List<SpawnSystem> SpawnSystems() { if (!_spawnSystemsTried) { _spawnSystemsTried = true; FieldInfo fieldInfo = AccessTools.Field(typeof(SpawnSystem), "m_instances"); if (fieldInfo != null) { _spawnSystems = AccessTools.StaticFieldRefAccess<List<SpawnSystem>>(fieldInfo); } } FieldRef<List<SpawnSystem>> spawnSystems = _spawnSystems; if (spawnSystems == null) { return null; } return spawnSystems.Invoke(); } private static void WorldSpawners() { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) HashSet<SpawnSystemList> hashSet = new HashSet<SpawnSystemList>(); foreach (SpawnSystem item in SpawnSystems() ?? new List<SpawnSystem>()) { if ((Object)(object)item == (Object)null) { continue; } foreach (SpawnSystemList spawnList in item.m_spawnLists) { if ((Object)(object)spawnList != (Object)null) { hashSet.Add(spawnList); } } } if (hashSet.Count == 0) { SpawnSystemList[] array = Resources.FindObjectsOfTypeAll<SpawnSystemList>(); foreach (SpawnSystemList val in array) { if ((Object)(object)val != (Object)null) { Scene scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { hashSet.Add(val); } } } } foreach (SpawnSystemList item2 in hashSet) { Each(item2.m_spawners, "world spawns", (SpawnData d) => (d.m_name.Length <= 0) ? ((!((Object)(object)d.m_prefab != (Object)null)) ? "a spawn" : ((Object)d.m_prefab).name) : d.m_name, delegate(SpawnData data) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)data.m_prefab == (Object)null) && data.m_enabled) { string name = ((Object)data.m_prefab).name; AddBiomes(name, data.m_biome); PlacedByWorld.Add(name); SpawnFacts spawn = new SpawnFacts { Biomes = BiomeNames(data.m_biome), AtNight = data.m_spawnAtNight, AtDay = data.m_spawnAtDay, MinLevel = data.m_minLevel, MaxLevel = data.m_maxLevel, GroupMin = data.m_groupSizeMin, GroupMax = data.m_groupSizeMax, InForest = data.m_inForest, OutsideForest = data.m_outsideForest, Weather = (data.m_requiredEnvironments ?? new List<string>()).Where((string e) => !string.IsNullOrEmpty(e)).Select(Naming.FieldLabel).ToArray(), Keys = new string[1] { data.m_requiredGlobalKey } }; Add(name, SpawnWords.Line("Spawns in", spawn, BossOf), BossPrefabOf(data.m_requiredGlobalKey)); } }); } } private static void Raids() { List<RandomEvent> list = RandEventSystem.instance?.m_events; if (list == null) { return; } bool byPlayer = (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)17); Each(list, "raids", (RandomEvent r) => r.m_name, delegate(RandomEvent raid) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) if (raid.m_spawn == null || !raid.m_enabled) { return; } string text = CatalogBuilder.Localize(raid.m_startMessage); string text2 = "Comes in the raid \"" + ((text.Length > 0) ? text : raid.m_name) + "\""; bool flag = byPlayer && (Count<string>(raid.m_altRequiredPlayerKeysAny) > 0 || Count<string>(raid.m_altRequiredPlayerKeysAll) > 0 || Count<ItemDrop>(raid.m_altRequiredKnownItems) > 0 || Count<ItemDrop>(raid.m_altRequiredNotKnownItems) > 0 || Count<string>(raid.m_altNotRequiredPlayerKeys) > 0); SpawnFacts spawnFacts = new SpawnFacts { Biomes = (((int)raid.m_biome != 0) ? BiomeNames(raid.m_biome) : ""), Keys = ((flag || raid.m_requiredGlobalKeys == null) ? new string[0] : raid.m_requiredGlobalKeys.ToArray()), NotKeys = ((flag || raid.m_notRequiredGlobalKeys == null) ? new string[0] : raid.m_notRequiredGlobalKeys.ToArray()) }; string text3 = SpawnWords.Line((spawnFacts.Biomes.Length > 0) ? (text2 + ", in") : text2, spawnFacts, BossOf); if (flag) { text3 += ", for a player whose own progress calls for it"; } foreach (SpawnData item in raid.m_spawn) { if (!((Object)(object)item?.m_prefab == (Object)null)) { Add(((Object)item.m_prefab).name, text3, BossPrefabOf(spawnFacts.Keys.FirstOrDefault())); } } }); static int Count<T>(List<T> keys) { return keys?.Count ?? 0; } } private static void TellSpawnPoints() { foreach (var (val, item, spawn) in SpawnPointsLeft) { Keep(SpawnPointLines, item, SpawnWords.Line("In dungeons or locations, from the spawn point " + Shown(val), spawn, BossOf), ((Object)val).name); } SpawnPointsLeft.Clear(); } private static void SpawnPoints(GameObject prefab, List<Component> components) { foreach (Component component in components) { SpawnArea val = (SpawnArea)(object)((component is SpawnArea) ? component : null); if (val == null || val.m_prefabs == null) { continue; } foreach (SpawnData prefab2 in val.m_prefabs) { if (!((Object)(object)prefab2?.m_prefab == (Object)null)) { Keep(SpawnPointLines, prefab2.m_prefab, "Comes from " + Shown(prefab) + ", " + SpawnWords.Stars(prefab2.m_minLevel, prefab2.m_maxLevel), ((Object)prefab).name); } } } foreach (Component component2 in components) { CreatureSpawner val2 = (CreatureSpawner)(object)((component2 is CreatureSpawner) ? component2 : null); if (val2 != null && !((Object)(object)val2.m_creaturePrefab == (Object)null)) { SpawnFacts spawnFacts = new SpawnFacts(); spawnFacts.AtNight = val2.m_spawnAtNight; spawnFacts.AtDay = val2.m_spawnAtDay; spawnFacts.MinLevel = val2.m_minLevel; spawnFacts.MaxLevel = val2.m_maxLevel; spawnFacts.Keys = new string[1] { val2.m_requiredGlobalKey }; spawnFacts.NotKeys = new string[1] { val2.m_blockingGlobalKey }; SpawnFacts item = spawnFacts; SpawnPointsLeft.Add((prefab, val2.m_creaturePrefab, item)); } } } private static void Vegetation() { List<ZoneVegetation> list = ZoneSystem.instance?.m_vegetation; if (list == null) { return; } Each(list, "vegetation", (ZoneVegetation v) => v.m_name, delegate(ZoneVegetation veg) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)veg.m_prefab == (Object)null) && veg.m_enable) { string name = ((Object)veg.m_prefab).name; AddBiomes(name, veg.m_biome); PlacedByWorld.Add(name); string text = "Grows in " + BiomeNames(veg.m_biome); if (veg.m_minAltitude > -1000f || veg.m_maxAltitude < 1000f) { text += ((veg.m_maxAltitude < 1000f) ? (", " + DropWords.Range(Mathf.RoundToInt(veg.m_minAltitude), Mathf.RoundToInt(veg.m_maxAltitude)) + " m up") : $", from {Mathf.RoundToInt(veg.m_minAltitude)} m up"); } if (veg.m_minOceanDepth > 0f || veg.m_maxOceanDepth > 0f) { text += ", in the sea"; } if (veg.m_inForest) { text += ", in forests"; } string text2 = SpawnWords.Group(veg.m_groupSizeMin, veg.m_groupSizeMax); if (text2 != null) { text = text + ", " + text2; } Add(name, text); } }); } private static void Drops(GameObject prefab, List<Component> components) { //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) foreach (Component component in components) { if ((Object)(object)component == (Object)null) { continue; } CharacterDrop val = (CharacterDrop)(object)((component is CharacterDrop) ? component : null); if (val != null) { if (val.m_drops == null) { continue; } foreach (Drop drop in val.m_drops) { if (!((Object)(object)drop?.m_prefab == (Object)null)) { string text = DropWords.CreatureAmount(drop.m_amountMin, drop.m_amountMax, drop.m_onePerPlayer); string text2 = ((drop.m_chance < 1f) ? $" ({Mathf.RoundToInt(drop.m_chance * 100f)}%)" : ""); Keep(DropLines, drop.m_prefab, "Dropped by " + Shown(prefab) + ", " + text + text2, ((Object)prefab).name); } } continue; } Pickable val2 = (Pickable)(object)((component is Pickable) ? component : null); if (val2 != null) { Keep(DropLines, val2.m_itemPrefab, "Picked from " + Shown(prefab), ((Object)prefab).name); } Plant val3 = (Plant)(object)((component is Plant) ? component : null); if (val3 != null && val3.m_grownPrefabs != null) { GameObject[] grownPrefabs = val3.m_grownPrefabs; foreach (GameObject item in grownPrefabs) { Keep(DropLines, item, "Grows from " + Shown(prefab), ((Object)prefab).name); } } FieldInfo[] array = DropTables(((object)component).GetType()); if (array.Length == 0) { continue; } string text3 = null; FieldInfo[] array2 = array; for (int i = 0; i < array2.Length; i++) { object? value = array2[i].GetValue(component); DropTable val4 = (DropTable)((value is DropTable) ? value : null); if (val4 == null || val4.m_drops == null) { continue; } text3 = text3 ?? (FromVerb(component) + " " + Shown(prefab)); foreach (DropData drop2 in val4.m_drops) { Keep(DropLines, drop2.m_item, text3, ((Object)prefab).name); } } } } private static void Makers(GameObject prefab, List<Component> components) { //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) foreach (Component component in components) { if ((Object)(object)component == (Object)null) { continue; } FieldInfo[] array = Conversions(((object)component).GetType()); for (int i = 0; i < array.Length; i++) { if (!(array[i].GetValue(component) is IEnumerable enumerable)) { continue; } foreach (object item in enumerable) { if (item != null) { Type type = item.GetType(); object? obj = type.GetField("m_from")?.GetValue(item); ItemDrop val = (ItemDrop)((obj is ItemDrop) ? obj : null); object? obj2 = type.GetField("m_to")?.GetValue(item); ItemDrop val2 = (ItemDrop)((obj2 is ItemDrop) ? obj2 : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { int makes = ((!(type.GetField("m_producedItems")?.GetValue(item) is int num) || num <= 1) ? 1 : num); Making making = new Making { Station = ((Object)prefab).name, Output = ((Object)((Component)val2).gameObject).name, Makes = makes }; making.Inputs.Add((((Object)((Component)val).gameObject).name, 1)); Made.Add(making); Uses.Add(((Object)((Component)val).gameObject).name, UseKind.TurnsInto, ((Object)((Component)val2).gameObject).name, 0, ((Object)prefab).name); } } } } Beehive val3 = (Beehive)(object)((component is Beehive) ? component : null); if (val3 != null && (Object)(object)val3.m_honeyItem != (Object)null) { string text = (((int)val3.m_biome != 0) ? (", in " + BiomeNames(val3.m_biome)) : ""); Keep(DropLines, ((Component)val3.m_honeyItem).gameObject, $"Made by {Shown(prefab)}, one every {Naming.Duration(val3.m_secPerUnit)}, holding up to {val3.m_maxHoney}{text}", ((Object)prefab).name); } SapCollector val4 = (SapCollector)(object)((component is SapCollector) ? component : null); if (val4 != null && (Object)(object)val4.m_spawnItem != (Object)null) { Keep(DropLines, ((Component)val4.m_spawnItem).gameObject, $"Made by {Shown(prefab)}, one every {Naming.Duration(val4.m_secPerUnit)}, holding up to {val4.m_maxLevel}", ((Object)prefab).name); } Incinerator val5 = (Incinerator)(object)((component is Incinerator) ? component : null); if (val5 == null || val5.m_conversions == null) { continue; } foreach (IncineratorConversion conversion in val5.m_conversions) { if ((Object)(object)conversion?.m_result == (Object)null || conversion.m_requirements == null) { continue; } Making making2 = new Making { Station = ((Object)prefab).name, Output = ((Object)((Component)conversion.m_result).gameObject).name, Makes = Math.Max(1, conversion.m_resultAmount), AnyOne = conversion.m_requireOnlyOneIngredient }; foreach (Requirement requirement in conversion.m_requirements) { if (!((Object)(object)requirement?.m_resItem == (Object)null)) { making2.Inputs.Add((((Object)((Component)requirement.m_resItem).gameObject).name, Math.Max(1, requirement.m_amount))); Uses.Add(((Object)((Component)requirement.m_resItem).gameObject).name, UseKind.TurnsInto, making2.Output, 0, ((Object)prefab).name); } } Made.Add(making2); } } } private static string FromVerb(Component component) { if (!(component is TreeBase)) { if (!(component is TreeLog)) { if (!(component is MineRock) && !(component is MineRock5)) { if (!(component is Container)) { if (!(component is Pickable)) { if (component is DropOnDestroyed) { return "Broken out of"; } return "Comes out of"; } return "Also picked from"; } return "Found in"; } return "Mined from"; } return "Chopped from"; } return "Felled from"; } private static void Traders() { Each(Resources.FindObjectsOfTypeAll<Trader>(), "traders", (Trader t) => ((Object)t).name, delegate(Trader trader) { if (trader.m_items == null) { return; } string text = CatalogBuilder.Localize(trader.m_name); if (text.Length == 0) { text = ((Object)((Component)trader).gameObject).name; } string prefab = ((Object)((Component)((Component)trader).transform.root).gameObject).name.Replace("(Clone)", "").Trim(); foreach (TradeItem item in trader.m_items) { if (!((Object)(object)item?.m_prefab == (Object)null)) { string text2 = ((item.m_stack > 1) ? $"{item.m_stack} for " : ""); string text3 = (string.IsNullOrEmpty(item.m_requiredGlobalKey) ? "" : (", " + SpawnWords.Once(item.m_requiredGlobalKey, BossOf))); From(((Object)((Component)item.m_prefab).gameObject).name, new Source($"Sold by {text}, {text2}{item.m_price} coins{text3}", prefab)); } } }); } private static FieldInfo[] Conversions(Type type) { if (ConversionFields.TryGetValue(type, out var value)) { return value; } List<FieldInfo> list = new List<FieldInfo>(); try { Type type2 = type; while (type2 != null && type2 != typeof(object) && type2 != typeof(MonoBehaviour)) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { Type type3 = (fieldInfo.FieldType.IsArray ? fieldInfo.FieldType.GetElementType() : ((fieldInfo.FieldType.IsGenericType && fieldInfo.FieldType.GetGenericTypeDefinition() == typeof(List<>)) ? fieldInfo.FieldType.GetGenericArguments()[0] : null)); if (!(type3 == null) && type3.GetField("m_from")?.FieldType == typeof(ItemDrop) && type3.GetField("m_to")?.FieldType == typeof(ItemDrop)) { list.Add(fieldInfo); } } type2 = type2.BaseType; } } catch (Exception ex) { Faults.Skip("reading of a type's fields", type.Name, ex); list.Clear(); } value = list.ToArray(); ConversionFields[type] = value; return value; } public static IReadOnlyList<(string Prefab, string Effect, string How)> Givers() { return GiverList; } private static void Givers(GameObject prefab, List<Component> components) { foreach (Component component in components) { if ((Object)(object)component == (Object)null) { continue; } Note(component); SharedData val = ((ItemDrop)(((component is ItemDrop) ? component : null)?)).m_itemData?.m_shared; if (val != null) { Note(val); if (val.m_attack != null) { Note(val.m_attack); } if (val.m_secondaryAttack != null) { Note(val.m_secondaryAttack); } } } void Note(object owner) { FieldInfo[] array = EffectRefs(owner.GetType()); foreach (FieldInfo fieldInfo in array) { string text = null; object value = fieldInfo.GetValue(owner); StatusEffect val2 = (StatusEffect)((value is StatusEffect) ? value : null); if (val2 != null && (Object)(object)val2 != (Object)null) { text = ((Object)val2).name; } else if (value is string { Length: >0 } text2) { text = text2; } if (text != null) {