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 Lur v1.0.0
plugins/Lur/Lur.dll
Decompiled 3 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Ezomic.Core; using Ezomic.Shared; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Thijssen Software")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")] [assembly: AssemblyDescription("Sound a horn in one of Hildir's dungeons and its mini-boss wakes again.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+ed4086fb33547df831bf919c89c899dc9da769d4")] [assembly: AssemblyProduct("Lur")] [assembly: AssemblyTitle("Lur")] [assembly: AssemblyVersion("1.0.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 Ezomic.Shared { public static class Prefabs { private sealed class Kept { internal string Name; internal Func<GameObject> Build; internal bool Item; internal string Tool; internal GameObject Prefab; internal int Failures; internal bool Abandoned; } private static ManualLogSource _log; private static GameObject _holder; private static readonly List<Kept> Standing = new List<Kept>(); private const int MaxFailures = 5; private static readonly HashSet<string> Complained = new HashSet<string>(); private static FieldRef<ZNetScene, Dictionary<int, GameObject>> _named; private static MethodInfo _updateRegisters; public static ManualLogSource Log { get { return _log ?? (_log = Logger.CreateLogSource("Prefabs")); } set { _log = value; } } public static Transform Holder { get { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown if ((Object)(object)_holder == (Object)null) { _holder = new GameObject(Log.SourceName + "Prefabs"); _holder.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_holder); } return _holder.transform; } } public static void Keep(string name, Func<GameObject> build, bool item = false, string buildTool = null) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } if (build == null) { throw new ArgumentNullException("build"); } foreach (Kept item2 in Standing) { if (!(item2.Name != name)) { item2.Build = build; item2.Item = item; item2.Tool = buildTool; item2.Failures = 0; item2.Abandoned = false; return; } } Standing.Add(new Kept { Name = name, Build = build, Item = item, Tool = buildTool }); Log.LogInfo((object)("Keeping " + name + " registered.")); } public static void Drop(string name) { Standing.RemoveAll((Kept k) => k.Name == name); } public static void Tick() { if (Standing.Count == 0) { return; } ZNetScene instance = ZNetScene.instance; if (!((Object)(object)instance == (Object)null)) { for (int i = 0; i < Standing.Count; i++) { Apply(Standing[i], instance); } } } private static void Apply(Kept kept, ZNetScene scene) { if (!kept.Abandoned && (!((Object)(object)kept.Prefab == (Object)null) || TryBuild(kept))) { if ((Object)(object)scene.GetPrefab(kept.Name) == (Object)null) { Register(kept.Prefab); } if (kept.Item) { RegisterItem(kept.Prefab); } if (!string.IsNullOrEmpty(kept.Tool)) { AddToTool(kept.Prefab, kept.Tool); } } } private static bool TryBuild(Kept kept) { GameObject val = null; try { val = kept.Build(); } catch (Exception ex) { Log.LogWarning((object)("Building " + kept.Name + " failed: " + ex.Message)); } if ((Object)(object)val != (Object)null) { kept.Prefab = val; kept.Failures = 0; if (((Object)val).name != kept.Name) { ((Object)val).name = kept.Name; } return true; } if (++kept.Failures < 5) { return false; } kept.Abandoned = true; Log.LogError((object)(kept.Name + " could not be built after " + 5 + " attempts and will not be retried. Anything already built with it in a world is untouched; it simply will not be placeable this session.")); return false; } public static bool Known(string name) { if ((Object)(object)ZNetScene.instance != (Object)null) { return (Object)(object)ZNetScene.instance.GetPrefab(name) != (Object)null; } return false; } public static GameObject Clone(GameObject source, string name) { if ((Object)(object)source == (Object)null) { return null; } bool forceDisableInit = ZNetView.m_forceDisableInit; ZNetView.m_forceDisableInit = true; GameObject val; try { val = Object.Instantiate<GameObject>(source, Holder); } finally { ZNetView.m_forceDisableInit = forceDisableInit; } ((Object)val).name = name; return val; } public static GameObject Donor(string commaSeparated, out string chosen) { chosen = null; if ((Object)(object)ZNetScene.instance == (Object)null) { return null; } string[] array = (commaSeparated ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { GameObject prefab = ZNetScene.instance.GetPrefab(text); if (!((Object)(object)prefab == (Object)null)) { chosen = text; return prefab; } } } return null; } public static bool Register(GameObject prefab) { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null) { return false; } if ((Object)(object)instance.GetPrefab(((Object)prefab).name) != (Object)null) { return true; } if (!instance.m_prefabs.Contains(prefab)) { instance.m_prefabs.Add(prefab); } try { NamedPrefabs(instance)[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = prefab; } catch (Exception ex) { ComplainOnce("scene:" + ((Object)prefab).name, "Could not register " + ((Object)prefab).name + " with ZNetScene: " + ex.Message); return false; } Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ZNetScene.")); return true; } public static bool RegisterItem(GameObject prefab) { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || (Object)(object)prefab == (Object)null) { return false; } if (instance.m_items == null || instance.m_items.Count == 0) { return false; } if ((Object)(object)instance.GetItemPrefab(((Object)prefab).name) != (Object)null) { return true; } if (!instance.m_items.Contains(prefab)) { instance.m_items.Add(prefab); } try { UpdateRegisters(instance); } catch (Exception ex) { ComplainOnce("db:" + ((Object)prefab).name, "Could not refresh ObjectDB for " + ((Object)prefab).name + ": " + ex.Message); return false; } Log.LogInfo((object)("Registered " + ((Object)prefab).name + " with ObjectDB.")); return true; } public static PieceTable ToolPieces(string toolPrefab) { if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(toolPrefab)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(toolPrefab); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val == (Object)null || val.m_itemData == null || val.m_itemData.m_shared == null) { return null; } PieceTable buildPieces = val.m_itemData.m_shared.m_buildPieces; if (!((Object)(object)buildPieces != (Object)null) || buildPieces.m_pieces == null) { return null; } return buildPieces; } public static bool InTool(GameObject prefab, string toolPrefab) { PieceTable val = ToolPieces(toolPrefab); if ((Object)(object)val != (Object)null && (Object)(object)prefab != (Object)null) { return val.m_pieces.Contains(prefab); } return false; } public static bool AddToTool(GameObject prefab, string toolPrefab) { if ((Object)(object)prefab == (Object)null) { return false; } PieceTable val = ToolPieces(toolPrefab); if ((Object)(object)val == (Object)null) { return false; } if (val.m_pieces.Contains(prefab)) { return true; } val.m_pieces.Add(prefab); Log.LogInfo((object)(((Object)prefab).name + " added to the " + toolPrefab + ".")); return true; } private static void ComplainOnce(string key, string message) { if (Complained.Add(key)) { Log.LogError((object)message); } } private static Dictionary<int, GameObject> NamedPrefabs(ZNetScene scene) { if (_named == null) { _named = AccessTools.FieldRefAccess<ZNetScene, Dictionary<int, GameObject>>("m_namedPrefabs"); } return _named.Invoke(scene); } private static void UpdateRegisters(ObjectDB db) { if (_updateRegisters == null) { _updateRegisters = AccessTools.Method(typeof(ObjectDB), "UpdateRegisters", (Type[])null, (Type[])null); } if (_updateRegisters == null) { throw new MissingMethodException("ObjectDB.UpdateRegisters"); } _updateRegisters.Invoke(db, null); } } } namespace Lur { internal static class Dungeons { private const Theme Trio = (Theme)3584; internal unsafe static DungeonGenerator Containing(Vector3 point) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) DungeonGenerator val = null; foreach (DungeonGenerator item in Live()) { if (Enabled(item.m_themes) && Inside(item, point)) { if ((Object)(object)val != (Object)null) { ManualLogSource log = LurPlugin.Log; Vector3 val2 = point; log.LogWarning((object)("Two dungeons overlap at " + ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString() + " - refusing to guess which.")); return null; } val = item; } } return val; } internal static IEnumerable<DungeonGenerator> Live() { Scene activeScene = SceneManager.GetActiveScene(); GameObject[] roots = ((Scene)(ref activeScene)).GetRootGameObjects(); DungeonGenerator val2 = default(DungeonGenerator); ZNetView val3 = default(ZNetView); foreach (GameObject val in roots) { if (!((Object)(object)val == (Object)null) && val.TryGetComponent<DungeonGenerator>(ref val2) && !((Object)(object)val2 == (Object)null) && val.TryGetComponent<ZNetView>(ref val3) && !((Object)(object)val3 == (Object)null) && val3.IsValid()) { yield return val2; } } } internal static bool Enabled(Theme themes) { //IL_0000: 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if ((themes & 0xE00) == 0) { return false; } if ((themes & 0x200) != 0) { return LurConfig.WakeCrypt.Value; } if ((themes & 0x400) != 0) { return LurConfig.WakeCave.Value; } if ((themes & 0x800) != 0) { return LurConfig.WakeTower.Value; } return false; } internal static string Describe(DungeonGenerator dg) { if ((Object)(object)dg == (Object)null) { return "<none>"; } return Utils.GetPrefabName(((Component)dg).gameObject) + " [" + ((object)Unsafe.As<Theme, Theme>(ref dg.m_themes)/*cast due to .constrained prefix*/).ToString() + "]"; } internal static bool Inside(DungeonGenerator dg, Vector3 point) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //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_0068: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)dg == (Object)null) { return false; } float num = Mathf.Max(0f, LurConfig.RoomPadding.Value); Room[] componentsInChildren = ((Component)dg).GetComponentsInChildren<Room>(); foreach (Room val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = Quaternion.Inverse(((Component)val).transform.rotation) * (point - ((Component)val).transform.position); Vector3 val3 = Vector3Int.op_Implicit(val.m_size) * 0.5f; if (Mathf.Abs(val2.x) <= val3.x + num && Mathf.Abs(val2.y) <= val3.y + num && Mathf.Abs(val2.z) <= val3.z + num) { return true; } } } if (!LurConfig.UseLocationRadiusFallback.Value) { return false; } Location location = Location.GetLocation(((Component)dg).transform.position, true); if ((Object)(object)location == (Object)null) { return false; } return location.IsInside(point, 0f, false); } internal static bool Ready(DungeonGenerator dg) { if ((Object)(object)dg == (Object)null) { return false; } return ((Component)dg).GetComponentsInChildren<Room>().Length != 0; } internal static List<CreatureSpawner> Spawners(DungeonGenerator dg) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) List<CreatureSpawner> list = new List<CreatureSpawner>(); if ((Object)(object)dg == (Object)null) { return list; } Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); CreatureSpawner val2 = default(CreatureSpawner); ZNetView val3 = default(ZNetView); foreach (GameObject val in rootGameObjects) { if (!((Object)(object)val == (Object)null) && val.TryGetComponent<CreatureSpawner>(ref val2) && !((Object)(object)val2 == (Object)null) && val.TryGetComponent<ZNetView>(ref val3) && !((Object)(object)val3 == (Object)null) && val3.IsValid() && Inside(dg, val.transform.position)) { list.Add(val2); } } return list; } internal static List<CreatureSpawner> BossSpawners(DungeonGenerator dg) { List<CreatureSpawner> list = new List<CreatureSpawner>(); foreach (CreatureSpawner item in Spawners(dg)) { if (IsBoss(item)) { list.Add(item); } } return list; } internal static CreatureSpawner Boss(DungeonGenerator dg) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) List<CreatureSpawner> list = new List<CreatureSpawner>(); foreach (CreatureSpawner item in Spawners(dg)) { if (IsBoss(item)) { list.Add(item); } } if (list.Count == 0) { return null; } if (list.Count == 1) { return list[0]; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return list[0]; } CreatureSpawner result = list[0]; float num = float.MaxValue; foreach (CreatureSpawner item2 in list) { float num2 = Vector3.Distance(((Component)localPlayer).transform.position, ((Component)item2).transform.position); if (!(num2 >= num)) { num = num2; result = item2; } } return result; } internal static bool IsBoss(CreatureSpawner spawner) { if ((Object)(object)spawner == (Object)null) { return false; } if ((Object)(object)spawner.m_creaturePrefab == (Object)null) { return false; } Character val = default(Character); if (!spawner.m_creaturePrefab.TryGetComponent<Character>(ref val)) { return false; } if ((Object)(object)val == (Object)null) { return false; } string defeatSetGlobalKey = val.m_defeatSetGlobalKey; if (string.IsNullOrEmpty(defeatSetGlobalKey)) { return false; } if (val.m_boss) { return true; } return !VanillaKey(defeatSetGlobalKey); } private static bool VanillaKey(string key) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) try { GlobalKeys result; return Enum.TryParse<GlobalKeys>(key, ignoreCase: true, out result) && Enum.IsDefined(typeof(GlobalKeys), result); } catch { return false; } } internal static List<CreatureSpawner> Groupmates(CreatureSpawner boss, List<CreatureSpawner> candidates) { //IL_0071: 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) List<CreatureSpawner> list = new List<CreatureSpawner>(); if ((Object)(object)boss == (Object)null || candidates == null) { return list; } if (boss.m_spawnGroupRadius <= 0f || boss.m_maxGroupSpawned < 1) { return list; } foreach (CreatureSpawner candidate in candidates) { if (!((Object)(object)candidate == (Object)null) && !((Object)(object)candidate == (Object)(object)boss) && candidate.m_spawnGroupID == boss.m_spawnGroupID) { float num = boss.m_spawnGroupRadius + candidate.m_spawnGroupRadius; if (!(Vector3.Distance(((Component)boss).transform.position, ((Component)candidate).transform.position) > num)) { list.Add(candidate); } } } return list; } internal static bool HoldsTombstone(DungeonGenerator dg) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); TombStone val2 = default(TombStone); foreach (GameObject val in rootGameObjects) { if (!((Object)(object)val == (Object)null) && val.TryGetComponent<TombStone>(ref val2) && !((Object)(object)val2 == (Object)null) && Inside(dg, val.transform.position)) { return true; } } return false; } } internal static class Icons { public static Sprite Load(string fileName, string label) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00c1: 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) if (string.IsNullOrEmpty(fileName)) { return null; } string path = Path.Combine(Path.GetDirectoryName(typeof(Icons).Assembly.Location), fileName); if (!File.Exists(path)) { LurPlugin.LogOnce("No " + fileName + " beside the dll - " + label + " will use the donor's icon, which is someone else's picture."); return null; } try { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1 }; if (!LoadPng(val, File.ReadAllBytes(path))) { return null; } ((Object)val).name = label + "_icon"; ((Object)val).hideFlags = (HideFlags)61; return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } catch (Exception ex) { LurPlugin.Log.LogError((object)("Could not read " + fileName + ": " + ex.Message)); return null; } } public static string For(string modelFile) { if (string.IsNullOrEmpty(modelFile)) { return null; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(modelFile); if (!string.IsNullOrEmpty(fileNameWithoutExtension)) { return fileNameWithoutExtension + "_icon.png"; } return null; } private static bool LoadPng(Texture2D texture, byte[] data) { Type type = AccessTools.TypeByName("UnityEngine.ImageConversion"); if (type == null) { LurPlugin.LogOnce("UnityEngine.ImageConversion is missing - cannot read icons."); return false; } MethodInfo methodInfo = AccessTools.Method(type, "LoadImage", new Type[2] { typeof(Texture2D), typeof(byte[]) }, (Type[])null) ?? AccessTools.Method(type, "LoadImage", new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }, (Type[])null); if (methodInfo == null) { LurPlugin.LogOnce("No LoadImage overload found on UnityEngine.ImageConversion."); return false; } object[] parameters = ((methodInfo.GetParameters().Length != 3) ? new object[2] { texture, data } : new object[3] { texture, data, false }); return (bool)methodInfo.Invoke(null, parameters); } } internal static class LurConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> WakeCrypt; internal static ConfigEntry<bool> WakeCave; internal static ConfigEntry<bool> WakeTower; internal static ConfigEntry<float> CooldownDays; internal static ConfigEntry<float> RoomPadding; internal static ConfigEntry<float> WatchSeconds; internal static ConfigEntry<bool> UseLocationRadiusFallback; internal static ConfigEntry<int> Price; internal static ConfigEntry<int> Stack; internal static ConfigEntry<string> SoldAfterKey; internal static ConfigEntry<string> ItemName; internal static ConfigEntry<string> ItemDonor; internal static ConfigEntry<string> SoundEffectPrefab; internal static ConfigEntry<bool> Diagnose; internal static ConfigEntry<bool> Verbose; internal static void Bind(ConfigFile cfg) { Enabled = cfg.Bind<bool>("Lur", "Enabled", true, "Off leaves the plugin loaded and changing nothing. The horn still exists in any inventory that holds one; sounding it simply does nothing."); ItemName = cfg.Bind<string>("Lur", "ItemName", "Lur", "The horn's display name. This is the label only. The prefab name is fixed in code and can never change: ZNetScene keys on it and discards the ZDO of anything it cannot resolve, so a rename would delete every horn already sitting in a chest, silently."); WakeCrypt = cfg.Bind<bool>("Dungeons", "WakeCrypt", true, "Wake the Smouldering Tomb (Hildir's Black Forest crypt)."); WakeCave = cfg.Bind<bool>("Dungeons", "WakeCave", true, "Wake the Howling Cavern (Hildir's mountain cave)."); WakeTower = cfg.Bind<bool>("Dungeons", "WakeTower", true, "Wake the Sealed Tower (Hildir's plains fortress). This one is a ground location rather than a sky interior. It needs no separate code path - Lur deletes nothing and so has no height test anywhere - but it is the one of the three least proven in play."); CooldownDays = cfg.Bind<float>("Dungeons", "CooldownDays", 5f, "In-game days before the same dungeon can be woken again. 0 leaves the horn's price as the only cost. Measured on the world clock, so time spent with nobody online does not count."); RoomPadding = cfg.Bind<float>("Dungeons", "RoomPadding", 1f, "Metres of slack added to each room's box when deciding what is inside the dungeon. Raise it if a boss refuses to wake and the log shows its spawner just outside."); UseLocationRadiusFallback = cfg.Bind<bool>("Dungeons", "UseLocationRadiusFallback", false, "Also count anything inside the Location's own radius as part of the dungeon, not just the generator's rooms. Off by default because it is looser and can reach a spawner standing outside the tower wall. Turn it on only if the diagnostic log shows the tower's boss spawner is not in any room - that is the single case it exists for."); WatchSeconds = cfg.Bind<float>("Dungeons", "WatchSeconds", 30f, "How long to wait for something to actually spawn before giving up and putting the spawners back as they were. On a timeout the horn is not consumed. Must comfortably exceed the spawners' own interval."); Price = cfg.Bind<int>("Store", "Price", 500, "What Hildir charges, in coins. Her own stock runs a few hundred to somewhat over a thousand, so this sits mid-range. Together with CooldownDays it is the entire cost of a rematch."); Stack = cfg.Bind<int>("Store", "Stack", 1, "How many horns one purchase buys. Vanilla clamps this to the item's own max stack size at the till."); SoldAfterKey = cfg.Bind<string>("Store", "SoldAfterKey", "", "A global key Hildir requires before she will stock the horn. Empty means always on sale, which is the recommended setting: sounding it already refuses in a dungeon whose boss is still alive. 'bosshildir1' is the value for a host who wants the shop gated as well. An unknown key is not an error - the row simply never appears, so a typo reads as 'she does not sell it'."); ItemDonor = cfg.Bind<string>("Lur", "ItemDonor", "SurtlingCore", "Vanilla item to take the horn's machinery from. Its appearance is not used."); SoundEffectPrefab = cfg.Bind<string>("Lur", "SoundEffectPrefab", "", "Name of a vanilla effect prefab to play where the horn is sounded. Empty for none. Resolved through ZNetScene; a name that does not exist is logged and skipped, so a rename in a game update costs the noise and nothing else."); Diagnose = cfg.Bind<bool>("Diagnostics", "Diagnose", false, "Log a full census of every Hildir dungeon as it loads: its generator, its themes, its rooms, and every spawner near it with an inside-or-outside verdict. This is how the open questions about the Sealed Tower get answered, and it is the first thing to turn on if the horn does nothing."); Verbose = cfg.Bind<bool>("Diagnostics", "Verbose", false, "Write what was found and what was changed to BepInEx/LogOutput.log. Note that exceptions land in Player.log instead, not here."); } } internal static class LurItem { public const string Name = "Lur"; private const string Mesh = "lur.obj"; private const string Icon = "lur.png"; internal static GameObject Build() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) ModelData modelData = ObjMesh.Load(Path.Combine(Path.GetDirectoryName(typeof(LurItem).Assembly.Location), "lur.obj")); if (modelData == null) { LurPlugin.Log.LogError((object)"No lur.obj beside the dll - cannot build Lur. The mod will keep retrying, and Hildir will not stock the horn until it succeeds."); return null; } GameObject val = Donor(); if ((Object)(object)val == (Object)null) { return null; } GameObject val2 = Prefabs.Clone(val, "Lur"); if ((Object)(object)val2 == (Object)null) { return null; } val2.transform.localRotation = Quaternion.identity; Visual(val2, modelData); ItemDrop component = val2.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { LurPlugin.Log.LogError((object)("Donor " + ((Object)val).name + " has no usable ItemDrop.")); return null; } SharedData shared = component.m_itemData.m_shared; shared.m_name = LurConfig.ItemName.Value; shared.m_description = "A coiled horn of bone and iron, worn smooth at the mouth."; shared.m_itemType = (ItemType)1; shared.m_maxStackSize = 10; shared.m_weight = 1f; shared.m_teleportable = true; shared.m_questItem = false; Sprite val3 = Icons.Load("lur.png", "Lur"); if ((Object)(object)val3 != (Object)null) { shared.m_icons = (Sprite[])(object)new Sprite[1] { val3 }; } component.m_itemData.m_stack = 1; component.m_itemData.m_dropPrefab = val2; LurPlugin.Log.LogInfo((object)("Built Lur from " + ((Object)val).name + ".")); return val2; } private static GameObject Donor() { ZNetScene instance = ZNetScene.instance; string[] array = new string[3] { LurConfig.ItemDonor.Value, "SurtlingCore", "Wood" }; foreach (string text in array) { if (!string.IsNullOrEmpty(text)) { GameObject prefab = instance.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { return prefab; } LurPlugin.LogOnce("Item donor '" + text + "' does not exist."); } } return null; } private static void Visual(GameObject clone, ModelData model) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) MeshRenderer[] componentsInChildren = clone.GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { MeshFilter component = ((Component)val).GetComponent<MeshFilter>(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } Object.DestroyImmediate((Object)(object)val); } } Light[] componentsInChildren2 = clone.GetComponentsInChildren<Light>(true); foreach (Light val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)val2); } } ParticleSystem[] componentsInChildren3 = clone.GetComponentsInChildren<ParticleSystem>(true); foreach (ParticleSystem val3 in componentsInChildren3) { if ((Object)(object)val3 != (Object)null) { Object.DestroyImmediate((Object)(object)val3); } } ParticleSystemRenderer[] componentsInChildren4 = clone.GetComponentsInChildren<ParticleSystemRenderer>(true); foreach (ParticleSystemRenderer val4 in componentsInChildren4) { if ((Object)(object)val4 != (Object)null) { Object.DestroyImmediate((Object)(object)val4); } } GameObject val5 = new GameObject("lur_visual"); val5.transform.SetParent(clone.transform, false); val5.AddComponent<MeshFilter>().sharedMesh = model.Mesh; ((Renderer)val5.AddComponent<MeshRenderer>()).sharedMaterials = Skins.Skin(model.Groups); Skins.Remap(model.Mesh, model.Groups); Ground(clone, model); } private static void Ground(GameObject clone, ModelData model) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)clone.GetComponentInChildren<Collider>(true) != (Object)null) && !((Object)(object)model.Mesh == (Object)null)) { Bounds bounds = model.Mesh.bounds; BoxCollider obj = clone.AddComponent<BoxCollider>(); obj.center = ((Bounds)(ref bounds)).center; obj.size = ((Bounds)(ref bounds)).size; LurPlugin.LogOnce("Donor left no collider on Lur - added one from the mesh bounds so it does not fall through the world."); } } } internal static class LurPatches { [HarmonyPrefix] [HarmonyPatch(typeof(Humanoid), "UseItem")] private static bool UseItem(Humanoid __instance, ItemData item, bool fromInventoryGui) { try { if (!LurConfig.Enabled.Value) { return true; } if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (fromInventoryGui) { return true; } if (item == null || (Object)(object)item.m_dropPrefab == (Object)null) { return true; } if (((Object)item.m_dropPrefab).name != "Lur") { return true; } Sounding.Sound(Player.m_localPlayer); return false; } catch (Exception ex) { LurPlugin.Log.LogError((object)("Sounding the horn threw, so it did nothing: " + ex)); return true; } } [HarmonyPostfix] [HarmonyPatch(typeof(DungeonGenerator), "Awake")] private static void GeneratorAwake(DungeonGenerator __instance) { try { if (!((Object)(object)__instance == (Object)null)) { ((MonoBehaviour)__instance).StartCoroutine(Census(__instance)); } } catch (Exception ex) { LurPlugin.Log.LogError((object)("Dungeon census threw: " + ex)); } } private static IEnumerator Census(DungeonGenerator dg) { yield return (object)new WaitForSeconds(5f); if ((Object)(object)dg == (Object)null || (dg.m_themes & 0xE00) == 0) { yield break; } Room[] componentsInChildren = ((Component)dg).GetComponentsInChildren<Room>(); Vector3 position = ((Component)dg).transform.position; LurPlugin.Log.LogInfo((object)$"Hildir dungeon: {Utils.GetPrefabName(((Component)dg).gameObject)} themes={dg.m_themes} at {position} interior={Character.InInterior(position)} rooms={componentsInChildren.Length} enabled={Dungeons.Enabled(dg.m_themes)}"); if (!LurConfig.Diagnose.Value) { yield break; } for (int i = 0; i < componentsInChildren.Length; i++) { Room val = componentsInChildren[i]; if (!((Object)(object)val == (Object)null)) { LurPlugin.Log.LogInfo((object)$" room {i}: {((Object)val).name} size={val.m_size} at {((Component)val).transform.position}"); } } List<CreatureSpawner> list = Dungeons.Spawners(dg); LurPlugin.Log.LogInfo((object)(" spawners inside: " + list.Count)); foreach (CreatureSpawner item in list) { ZNetView component = ((Component)item).GetComponent<ZNetView>(); ZDO val2 = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); LurPlugin.Log.LogInfo((object)string.Format(" {0} respawn={1} trigger={2} day={3} night={4} req='{5}' block='{6}' group='{7}' connection={8}", Utils.GetPrefabName(((Component)item).gameObject), item.m_respawnTimeMinuts, item.m_triggerDistance, item.m_spawnAtDay, item.m_spawnAtNight, item.m_requiredGlobalKey, item.m_blockingGlobalKey, item.m_spawnGroupID, (val2 != null) ? ((object)val2.GetConnectionType()/*cast due to .constrained prefix*/).ToString() : "<no zdo>")); } } [HarmonyPostfix] [HarmonyPatch(typeof(Trader), "GetAvailableItems")] private static void AvailableItems(Trader __instance, List<TradeItem> __result) { try { Store.Offer(__instance, __result); } catch (Exception ex) { LurPlugin.Log.LogError((object)("Offering the horn threw, so it is not stocked: " + ex)); } } } [BepInPlugin("ezomic.valheim.lur", "Lur", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class LurPlugin : BaseUnityPlugin { public const string PluginGuid = "ezomic.valheim.lur"; public const string PluginName = "Lur"; public const string PluginVersion = "1.0.0"; public const string PluginAuthor = "Robbin Thijssen"; private const string CoreGuid = "ezomic.valheim.core"; internal static ManualLogSource Log; internal static bool CorePresent; private Harmony _harmony; private static readonly HashSet<string> Said = new HashSet<string>(); private void Awake() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; LurConfig.Bind(((BaseUnityPlugin)this).Config); TryRegisterWithCore(); Prefabs.Log = ((BaseUnityPlugin)this).Logger; Prefabs.Keep("Lur", LurItem.Build, item: true); _harmony = new Harmony("ezomic.valheim.lur"); _harmony.PatchAll(typeof(LurPatches)); Log.LogInfo((object)"Lur 1.0.0 by Robbin Thijssen - ready."); } private void TryRegisterWithCore() { CorePresent = Chainloader.PluginInfos.ContainsKey("ezomic.valheim.core"); if (!CorePresent) { Log.LogInfo((object)"Core not installed - running standalone, without the version gate."); } else { RegisterWithCore(); } } [MethodImpl(MethodImplOptions.NoInlining)] private void RegisterWithCore() { Suite.Register("ezomic.valheim.lur", "Lur", "1.0.0", ((BaseUnityPlugin)this).Config, (Requirement)0, (Assembly)null); Suite.Sync((ConfigEntryBase[])(object)new ConfigEntryBase[11] { (ConfigEntryBase)LurConfig.Enabled, (ConfigEntryBase)LurConfig.WakeCrypt, (ConfigEntryBase)LurConfig.WakeCave, (ConfigEntryBase)LurConfig.WakeTower, (ConfigEntryBase)LurConfig.CooldownDays, (ConfigEntryBase)LurConfig.RoomPadding, (ConfigEntryBase)LurConfig.WatchSeconds, (ConfigEntryBase)LurConfig.UseLocationRadiusFallback, (ConfigEntryBase)LurConfig.Price, (ConfigEntryBase)LurConfig.Stack, (ConfigEntryBase)LurConfig.SoldAfterKey }); Suite.Local((ConfigEntryBase[])(object)new ConfigEntryBase[2] { (ConfigEntryBase)LurConfig.Diagnose, (ConfigEntryBase)LurConfig.Verbose }); } private void Update() { Prefabs.Tick(); } internal static void LogOnce(string message) { if (Said.Add(message)) { Log.LogWarning((object)message); } } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal sealed class ModelData { public Mesh Mesh; public string[] Groups; } internal static class ObjMesh { public static ModelData Load(string path) { if (!File.Exists(path)) { LurPlugin.Log.LogWarning((object)("No model at " + path)); return null; } try { return Parse(File.ReadAllLines(path), Path.GetFileNameWithoutExtension(path)); } catch (Exception ex) { LurPlugin.Log.LogError((object)("Could not read " + path + ": " + ex.Message)); return null; } } private static ModelData Parse(string[] lines, string name) { //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) List<Vector3> list = new List<Vector3>(); List<Vector2> list2 = new List<Vector2>(); List<Vector3> list3 = new List<Vector3>(); List<Color> list4 = new List<Color>(); List<string> list5 = new List<string>(); List<List<int>> list6 = new List<List<int>>(); Dictionary<string, int> index = new Dictionary<string, int>(); Dictionary<string, int> lookup = new Dictionary<string, int>(); List<Vector3> list7 = new List<Vector3>(); List<Vector2> list8 = new List<Vector2>(); List<Vector3> list9 = new List<Vector3>(); List<Color> outShades = new List<Color>(); CultureInfo invariantCulture = CultureInfo.InvariantCulture; List<int> triangles = Bucket("", list5, list6, index); for (int i = 0; i < lines.Length; i++) { string text = lines[i].Trim(); if (text.Length == 0 || text[0] == '#') { continue; } string[] array = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length < 2) { continue; } switch (array[0]) { case "v": if (array.Length >= 4) { list.Add(new Vector3(float.Parse(array[1], invariantCulture), float.Parse(array[2], invariantCulture), float.Parse(array[3], invariantCulture))); if (array.Length >= 7) { list4.Add(new Color(float.Parse(array[4], invariantCulture), float.Parse(array[5], invariantCulture), float.Parse(array[6], invariantCulture), 1f)); } } break; case "vt": if (array.Length >= 3) { list2.Add(new Vector2(float.Parse(array[1], invariantCulture), float.Parse(array[2], invariantCulture))); } break; case "vn": if (array.Length >= 4) { list3.Add(new Vector3(float.Parse(array[1], invariantCulture), float.Parse(array[2], invariantCulture), float.Parse(array[3], invariantCulture))); } break; case "usemtl": triangles = Bucket(array[1].Trim(), list5, list6, index); break; case "f": AddFace(array, list, list2, list3, list4, lookup, list7, list8, list9, outShades, triangles); break; } } for (int num = list6.Count - 1; num >= 0; num--) { if (list6[num].Count == 0) { list6.RemoveAt(num); list5.RemoveAt(num); } } if (list7.Count == 0 || list6.Count == 0) { LurPlugin.Log.LogError((object)("Model " + name + " has no geometry.")); return null; } Mesh val = new Mesh { name = name }; if (list7.Count > 65535) { val.indexFormat = (IndexFormat)1; LurPlugin.Log.LogInfo((object)(name + " needs 32 bit indices (" + list7.Count + " verts).")); } val.SetVertices(list7); if (list8.Count == list7.Count) { val.SetUVs(0, list8); } val.subMeshCount = list6.Count; for (int j = 0; j < list6.Count; j++) { val.SetTriangles(list6[j], j); } if (list9.Count == list7.Count) { val.SetNormals(list9); } else { val.RecalculateNormals(); } Color[] array2 = (Color[])(object)new Color[list7.Count]; for (int k = 0; k < array2.Length; k++) { array2[k] = Color.white; } val.colors = array2; val.RecalculateBounds(); val.RecalculateTangents(); int num2 = 0; for (int l = 0; l < list6.Count; l++) { num2 += list6[l].Count; } LurPlugin.Log.LogInfo((object)string.Format("Loaded {0}: {1} verts, {2} tris, parts [{3}]", name, list7.Count, num2 / 3, string.Join(", ", list5.ToArray()))); return new ModelData { Mesh = val, Groups = list5.ToArray() }; } private static List<int> Bucket(string material, List<string> names, List<List<int>> buckets, Dictionary<string, int> index) { if (index.TryGetValue(material, out var value)) { return buckets[value]; } index[material] = buckets.Count; names.Add(material); buckets.Add(new List<int>()); return buckets[buckets.Count - 1]; } private static void AddFace(string[] parts, List<Vector3> positions, List<Vector2> uvs, List<Vector3> normals, List<Color> shades, Dictionary<string, int> lookup, List<Vector3> outPositions, List<Vector2> outUvs, List<Vector3> outNormals, List<Color> outShades, List<int> triangles) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) List<int> list = new List<int>(parts.Length - 1); for (int i = 1; i < parts.Length; i++) { string text = parts[i]; if (!lookup.TryGetValue(text, out var value)) { string[] fields = text.Split(new char[1] { '/' }); int num = Index(fields, 0, positions.Count); if (num < 0) { continue; } outPositions.Add(positions[num]); outShades.Add((num < shades.Count) ? shades[num] : Color.white); int num2 = Index(fields, 1, uvs.Count); outUvs.Add((num2 >= 0) ? uvs[num2] : Vector2.zero); int num3 = Index(fields, 2, normals.Count); outNormals.Add((num3 >= 0) ? normals[num3] : Vector3.up); value = (lookup[text] = outPositions.Count - 1); } list.Add(value); } for (int j = 2; j < list.Count; j++) { triangles.Add(list[0]); triangles.Add(list[j - 1]); triangles.Add(list[j]); } } private static int Index(string[] fields, int slot, int count) { if (slot >= fields.Length) { return -1; } string text = fields[slot]; if (string.IsNullOrEmpty(text)) { return -1; } if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return -1; } if (result > 0) { result--; } else if (result < 0) { result = count + result; } if (result < 0 || result >= count) { return -1; } return result; } } internal static class PropIndex { private static Dictionary<string, GameObject> _index; private static readonly string[] Chaff = new string[5] { "destruction", "broken", "lod", "vfx", "sfx" }; public static GameObject Find(string name) { if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(name); if ((Object)(object)prefab != (Object)null) { return prefab; } } if (_index == null) { BuildIndex(); } if (!_index.TryGetValue(name, out var value)) { return null; } return value; } private static void BuildIndex() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) _index = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); GameObject[] array = Resources.FindObjectsOfTypeAll<GameObject>(); foreach (GameObject val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.transform.parent != (Object)null || (Object)(object)val.GetComponentInChildren<MeshRenderer>(true) == (Object)null) { continue; } Scene scene = val.scene; bool flag = ((Scene)(ref scene)).IsValid(); if (_index.TryGetValue(((Object)val).name, out var value)) { if (flag) { continue; } scene = value.scene; if (!((Scene)(ref scene)).IsValid()) { continue; } } _index[((Object)val).name] = val; } LurPlugin.Log.LogInfo((object)("Prop index built: " + _index.Count + " candidates with meshes.")); } public static void Search(string keywords) { if (string.IsNullOrEmpty(keywords)) { return; } if (_index == null) { BuildIndex(); } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (string key in _index.Keys) { hashSet.Add(key); } if ((Object)(object)ZNetScene.instance != (Object)null) { foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab != (Object)null) { hashSet.Add(((Object)prefab).name); } } } string[] array = keywords.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } List<string> list = new List<string>(); foreach (string item in hashSet) { if (item.IndexOf(text, StringComparison.OrdinalIgnoreCase) < 0) { continue; } string text2 = item.ToLowerInvariant(); string text3 = text.ToLowerInvariant(); bool flag = false; string[] chaff = Chaff; foreach (string value in chaff) { if (text2.Contains(value) && !text3.Contains(value)) { flag = true; } } if (!flag) { list.Add(item); } } list.Sort(); LurPlugin.Log.LogInfo((object)("Prefabs matching '" + text + "' (" + list.Count + "): " + string.Join(", ", list.GetRange(0, Math.Min(40, list.Count)).ToArray()))); } } } internal static class Skins { private const string GlowGroup = "core"; private const string Emission = "_EmissionColor"; private static readonly Dictionary<string, string[]> Donors = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase) { { "bark", new string[3] { "wood_wall", "wood_beam", "piece_chest_wood" } }, { "wood", new string[3] { "wood_wall", "wood_beam", "piece_chest_wood" } }, { "seed", new string[2] { "wood_beam", "wood_wall" } }, { "stone", new string[3] { "stone_wall_2x1", "piece_stonecutter", "smelter" } }, { "bone", new string[3] { "BoneFragments", "TrophyDeer", "wood_beam" } }, { "iron", new string[3] { "Iron", "IronScrap", "stone_wall_2x1" } }, { "moss", new string[5] { "sapling_carrot", "sapling_turnip", "Bush01", "shrub_2", "piece_beehive" } }, { "cap", new string[4] { "Pickable_Mushroom", "Pickable_Mushroom_yellow", "Pickable_Mushroom_blue", "sapling_carrot" } }, { "bloom", new string[2] { "Pickable_Dandelion", "sapling_carrot" } }, { "cloth", new string[4] { "rug_lox", "rug_deer", "rug_fur", "wood_wall" } }, { "meal", new string[2] { "BoneFragments", "stone_wall_2x1" } }, { "rope", new string[2] { "wood_wall", "wood_beam" } }, { "bud", new string[3] { "Pickable_Thistle", "Pickable_Dandelion", "sapling_carrot" } } }; private static readonly Dictionary<string, Material> Cache = new Dictionary<string, Material>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Rect> Atlas = new Dictionary<string, Rect>(StringComparer.OrdinalIgnoreCase); public static void Invalidate() { Cache.Clear(); Atlas.Clear(); } public static Material For(string group) { if (Cache.TryGetValue(group, out var value)) { return value; } if (group == "core") { Material val = Pick(group, mustEmit: true); if ((Object)(object)val != (Object)null) { return val; } } return Pick(group, mustEmit: false); } private static Material Pick(string group, bool mustEmit) { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) string[] array = DonorsFor(group); foreach (string text in array) { GameObject val = PropIndex.Find(text); if ((Object)(object)val == (Object)null) { continue; } MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val2 in componentsInChildren) { Material sharedMaterial = ((Renderer)val2).sharedMaterial; if (!((Object)(object)sharedMaterial == (Object)null) && !((Object)(object)sharedMaterial.shader == (Object)null) && sharedMaterial.HasProperty("_MainTex") && !((Object)(object)sharedMaterial.GetTexture("_MainTex") == (Object)null) && (!mustEmit || sharedMaterial.HasProperty("_EmissionColor"))) { Cache[group] = sharedMaterial; Atlas[group] = UvRegion((Renderer)(object)val2); LurPlugin.Log.LogInfo((object)string.Format("'{0}' skinned with {1} from {2} (shader {3}){4}, atlas {5}.", group, ((Object)sharedMaterial).name, text, ((Object)sharedMaterial.shader).name, mustEmit ? " [emissive]" : "", Atlas[group])); return sharedMaterial; } } } if (mustEmit) { return null; } LurPlugin.Log.LogWarning((object)("No material found for group '" + group + "'.")); Cache[group] = null; return null; } private static string[] DonorsFor(string group) { if (!Donors.TryGetValue(group, out var value)) { return Donors["wood"]; } return value; } public static Material[] Skin(string[] groups) { Material[] array = (Material[])(object)new Material[groups.Length]; for (int i = 0; i < groups.Length; i++) { array[i] = For(groups[i]); } return array; } private static Rect UvRegion(Renderer renderer) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, 1f, 1f); MeshFilter val2 = (((Object)(object)renderer != (Object)null) ? ((Component)renderer).GetComponent<MeshFilter>() : null); Mesh val3 = (((Object)(object)val2 != (Object)null) ? val2.sharedMesh : null); if ((Object)(object)val3 == (Object)null) { return val; } Vector2[] uv; int[] triangles; try { if (!val3.isReadable) { return val; } uv = val3.uv; triangles = val3.triangles; } catch { return val; } if (uv == null || uv.Length == 0 || triangles == null || triangles.Length < 3) { return val; } float num = 0f; Rect result = val; for (int i = 0; i + 2 < triangles.Length; i += 3) { int num2 = triangles[i]; int num3 = triangles[i + 1]; int num4 = triangles[i + 2]; if (num2 >= uv.Length || num3 >= uv.Length || num4 >= uv.Length) { continue; } float num5 = Mathf.Min(uv[num2].x, Mathf.Min(uv[num3].x, uv[num4].x)); float num6 = Mathf.Max(uv[num2].x, Mathf.Max(uv[num3].x, uv[num4].x)); float num7 = Mathf.Min(uv[num2].y, Mathf.Min(uv[num3].y, uv[num4].y)); float num8 = Mathf.Max(uv[num2].y, Mathf.Max(uv[num3].y, uv[num4].y)); float num9 = num6 - num5; float num10 = num8 - num7; if (!(num9 <= 0.005f) && !(num10 <= 0.005f) && !(num9 > 1f) && !(num10 > 1f)) { float num11 = num9 * num10; if (!(num11 <= num)) { num = num11; ((Rect)(ref result))..ctor(num5, num7, num9, num10); } } } if (!(num > 0f)) { return val; } return result; } public static void Remap(Mesh mesh, string[] groups) { //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mesh == (Object)null || groups == null) { return; } Vector2[] uv = mesh.uv; if (uv == null || uv.Length == 0) { return; } int num = Mathf.Min(groups.Length, mesh.subMeshCount); bool[] array = new bool[uv.Length]; for (int i = 0; i < num; i++) { if (!Atlas.TryGetValue(groups[i], out var value) || (((Rect)(ref value)).width >= 0.999f && ((Rect)(ref value)).height >= 0.999f)) { continue; } int[] triangles = mesh.GetTriangles(i); foreach (int num2 in triangles) { if (num2 >= 0 && num2 < uv.Length && !array[num2]) { array[num2] = true; uv[num2] = new Vector2(((Rect)(ref value)).x + Mathf.Clamp01(uv[num2].x) * ((Rect)(ref value)).width, ((Rect)(ref value)).y + Mathf.Clamp01(uv[num2].y) * ((Rect)(ref value)).height); } } } mesh.uv = uv; } } internal static class Sounding { internal struct Cleared { internal ZDO Zdo; internal ConnectionType Type; internal ZDOID Target; } internal static readonly int SoundedKey = StringExtensionMethods.GetStableHashCode("ezomic_lur_sounded"); internal static bool Sound(Player player) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return false; } Vector3 position = ((Component)player).transform.position; DungeonGenerator val = Dungeons.Containing(position); if ((Object)(object)val == (Object)null) { Refuse(player, "Nothing sleeps here."); return false; } if (!Dungeons.Ready(val)) { Refuse(player, "The stone is still settling."); return false; } ZNetView component = ((Component)val).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { Refuse(player, "Nothing sleeps here."); return false; } ZDO zDO = component.GetZDO(); if (zDO == null) { Refuse(player, "Nothing sleeps here."); return false; } if (!OffCooldown(zDO, player)) { return false; } if (Dungeons.HoldsTombstone(val)) { Refuse(player, "Someone lies unclaimed here."); return false; } List<Cleared> list = Wake(val, player); if (list.Count == 0) { return false; } CreatureSpawner val2 = Dungeons.Boss(val); GameObject val3 = (((Object)(object)val2 != (Object)null) ? val2.m_creaturePrefab : null); ManualLogSource log = LurPlugin.Log; string[] obj = new string[11] { "Sounded in ", Dungeons.Describe(val), " - re-armed ", list.Count.ToString(), " spawner(s), boss spawner ", ((Object)(object)val2 != (Object)null) ? Utils.GetPrefabName(((Component)val2).gameObject) : "<none>", " at ", null, null, null, null }; object obj2; if (!((Object)(object)val2 != (Object)null)) { obj2 = "-"; } else { Vector3 position2 = ((Component)val2).transform.position; obj2 = ((Vector3)(ref position2)).ToString("F1"); } obj[7] = (string)obj2; obj[8] = " holding "; obj[9] = (((Object)(object)val3 != (Object)null) ? ((Object)val3).name : "<none>"); obj[10] = ", watching."; log.LogInfo((object)string.Concat(obj)); Effect(position); Watch watch = ((Component)val).gameObject.AddComponent<Watch>(); watch.Observe = Observable(Dungeons.BossSpawners(val), Dungeons.Spawners(val)); watch.Begin(list, zDO, player, ((Object)(object)val3 != (Object)null) ? StringExtensionMethods.GetStableHashCode(((Object)val3).name) : 0); return true; } private static bool OffCooldown(ZDO generator, Player player) { float num = Mathf.Max(0f, LurConfig.CooldownDays.Value); if (num <= 0f) { return true; } long num2 = generator.GetLong(SoundedKey, 0L); if (num2 == 0L) { return true; } long num3 = (long)ZNet.instance.GetTimeSeconds(); long num4 = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.m_dayLengthSec : 1200); long num5 = (long)(num * (float)num4) - (num3 - num2); if (num5 <= 0) { return true; } long num6 = (num5 + num4 - 1) / num4; Refuse(player, (num6 <= 1) ? "This place has not settled. Not long now." : ("This place has not settled. " + num6 + " days yet.")); return false; } private static List<Cleared> Wake(DungeonGenerator dg, Player player) { //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) List<Cleared> list = new List<Cleared>(); List<CreatureSpawner> list2 = Dungeons.Spawners(dg); List<CreatureSpawner> list3 = Dungeons.BossSpawners(dg); if ((Object)(object)Dungeons.Boss(dg) == (Object)null) { LurPlugin.Log.LogWarning((object)(Dungeons.Describe(dg) + ": no boss spawner found among " + list2.Count + " spawner(s). Turn on Diagnose and revisit to see what each of them holds.")); Refuse(player, "Nothing sleeps here."); return list; } List<CreatureSpawner> list4 = new List<CreatureSpawner>(); foreach (CreatureSpawner item in list3) { if (!list4.Contains(item)) { list4.Add(item); } foreach (CreatureSpawner item2 in Dungeons.Groupmates(item, list2)) { if (!list4.Contains(item2)) { list4.Add(item2); } } } string text = "Nothing sleeps here."; bool flag = false; foreach (CreatureSpawner item3 in list4) { if (!Clearable(item3, out var refusal)) { text = refusal; continue; } flag = true; break; } if (!flag) { LurPlugin.Log.LogInfo((object)(Dungeons.Describe(dg) + ": none of " + list4.Count + " boss/group spawner(s) can be cleared - " + text)); Refuse(player, text); return list; } foreach (CreatureSpawner item4 in list4) { ZDO val = ZdoOf(item4); if (val != null) { ConnectionType connectionType = val.GetConnectionType(); ZDOID connectionZDOID = val.GetConnectionZDOID((ConnectionType)3); if (Clearable(item4, out var _)) { val.SetOwner(ZDOMan.GetSessionID()); val.Set(ZDOVars.s_aliveTime, 0L); val.SetConnection((ConnectionType)0, ZDOID.None); list.Add(new Cleared { Zdo = val, Type = connectionType, Target = connectionZDOID }); } } } LurPlugin.Log.LogInfo((object)$"{Dungeons.Describe(dg)}: {list2.Count} spawner(s) present, {list3.Count} hold a boss, {list4.Count} in the group, cleared {list.Count}."); if (list.Count == 0) { Refuse(player, "Nothing sleeps here."); } return list; } private static bool Clearable(CreatureSpawner spawner, out string refusal) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) refusal = "Nothing sleeps here."; ZDO val = ZdoOf(spawner); if (val == null) { return false; } if ((int)val.GetConnectionType() == 0) { refusal = "It is already awake."; return false; } ZDOID connectionZDOID = val.GetConnectionZDOID((ConnectionType)3); if (!((ZDOID)(ref connectionZDOID)).IsNone() && ZDOMan.instance.GetZDO(connectionZDOID) != null) { refusal = "It is already awake."; return false; } if (!spawner.m_spawnInPlayerBase && (Object)(object)EffectArea.IsPointInsideArea(((Component)spawner).transform.position, (Type)4, 0f) != (Object)null) { refusal = "Something built here keeps it away."; return false; } if (Blocked(spawner)) { refusal = "Nothing here answers the horn."; return false; } return true; } private static ZDO ZdoOf(CreatureSpawner spawner) { if ((Object)(object)spawner == (Object)null) { return null; } ZNetView component = ((Component)spawner).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return null; } return component.GetZDO(); } private static List<ZDO> Observable(List<CreatureSpawner> bosses, List<CreatureSpawner> all) { List<CreatureSpawner> list = new List<CreatureSpawner>(); foreach (CreatureSpawner boss in bosses) { if (!list.Contains(boss)) { list.Add(boss); } foreach (CreatureSpawner item in Dungeons.Groupmates(boss, all)) { if (!list.Contains(item)) { list.Add(item); } } } List<ZDO> list2 = new List<ZDO>(); foreach (CreatureSpawner item2 in list) { ZDO val = ZdoOf(item2); if (val != null) { list2.Add(val); } } return list2; } private static bool Blocked(CreatureSpawner spawner) { List<string> globalKeys = ZoneSystem.instance.GetGlobalKeys(); if (!string.IsNullOrEmpty(spawner.m_blockingGlobalKey) && globalKeys.Contains(spawner.m_blockingGlobalKey)) { return true; } if (!string.IsNullOrEmpty(spawner.m_requiredGlobalKey)) { return !globalKeys.Contains(spawner.m_requiredGlobalKey); } return false; } internal static void Succeeded(ZDO generator, Player player) { if (generator != null) { generator.Set(SoundedKey, (long)ZNet.instance.GetTimeSeconds()); } LurPlugin.Log.LogInfo((object)"Something woke - the horn is spent."); if (!((Object)(object)player == (Object)null)) { Consume(player); ((Character)player).Message((MessageType)2, "Something wakes.", 0, (Sprite)null); } } internal static void TimedOut(List<Cleared> cleared, Player player) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) foreach (Cleared item in cleared) { if (item.Zdo != null) { item.Zdo.SetConnection(item.Type, item.Target); } } LurPlugin.Log.LogInfo((object)("Nothing stirred - put " + cleared.Count + " spawner(s) back as they were. The horn was not spent.")); if (!((Object)(object)player == (Object)null)) { ((Character)player).Message((MessageType)2, "Nothing stirred.", 0, (Sprite)null); } } private static void Consume(Player player) { Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { ItemData item = inventory.GetItem("Lur", -1, true); if (item != null) { inventory.RemoveOneItem(item); } } } private static void Effect(Vector3 at) { //IL_0040: 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) string value = LurConfig.SoundEffectPrefab.Value; if (!string.IsNullOrEmpty(value)) { GameObject prefab = ZNetScene.instance.GetPrefab(value); if ((Object)(object)prefab == (Object)null) { LurPlugin.LogOnce("Sound effect prefab '" + value + "' does not exist - the horn is silent. Anything else about it is unaffected."); } else { Object.Instantiate<GameObject>(prefab, at, Quaternion.identity); } } } private static void Refuse(Player player, string message) { if (!((Object)(object)player == (Object)null)) { ((Character)player).Message((MessageType)2, message, 0, (Sprite)null); } } } internal class Watch : MonoBehaviour { private List<Sounding.Cleared> _cleared; private ZDO _generator; private Player _player; private int _wanted; internal List<ZDO> Observe; internal void Begin(List<Sounding.Cleared> cleared, ZDO generator, Player player, int wantedPrefabHash) { _cleared = cleared; _generator = generator; _player = player; _wanted = wantedPrefabHash; ((MonoBehaviour)this).StartCoroutine(Run()); } private IEnumerator Run() { float deadline = Time.time + Mathf.Max(1f, LurConfig.WatchSeconds.Value); while (Time.time < deadline) { foreach (ZDO item in (Observe != null && Observe.Count > 0) ? Observe : Cleared()) { if (item != null && (int)item.GetConnectionType() == 3) { ZDOID connectionZDOID = item.GetConnectionZDOID((ConnectionType)3); if (!((ZDOID)(ref connectionZDOID)).IsNone() && IsWanted(item, connectionZDOID)) { Sounding.Succeeded(_generator, _player); Done(); yield break; } } } yield return (object)new WaitForSeconds(0.5f); } Sounding.TimedOut(_cleared, _player); Done(); } private List<ZDO> Cleared() { List<ZDO> list = new List<ZDO>(); foreach (Sounding.Cleared item in _cleared) { if (item.Zdo != null) { list.Add(item.Zdo); } } return list; } private bool IsWanted(ZDO zdo, ZDOID target) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (_wanted == 0) { return true; } ZDO zDO = ZDOMan.instance.GetZDO(target); if (zDO == null) { return true; } if (zDO.GetPrefab() == _wanted) { return true; } ManualLogSource log = LurPlugin.Log; string[] obj = new string[7] { "Something spawned, but not what the horn was for - expected prefab hash ", _wanted.ToString(), ", got ", zDO.GetPrefab().ToString(), " at ", null, null }; Vector3 position = zDO.GetPosition(); obj[5] = ((Vector3)(ref position)).ToString("F1"); obj[6] = ". The horn was not spent. This means the boss spawner was mis-identified, so turn on Diagnose and report the census."; log.LogError((object)string.Concat(obj)); if ((Object)(object)_player != (Object)null) { ((Character)_player).Message((MessageType)2, "Something else stirs.", 0, (Sprite)null); } return false; } private void Done() { _cleared = null; _generator = null; _player = null; Object.Destroy((Object)(object)this); } } internal static class Store { private const string Merchant = "Hildir"; private static TradeItem _row; private static ItemDrop _drop; private static TradeItem Row() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown if (_row != null && (Object)(object)_drop != (Object)null) { return _row; } if ((Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab("Lur"); if ((Object)(object)prefab == (Object)null) { return null; } ItemDrop component = prefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { return null; } _drop = component; _row = new TradeItem { m_prefab = component, m_stack = Mathf.Max(1, LurConfig.Stack.Value), m_price = Mathf.Max(0, LurConfig.Price.Value), m_requiredGlobalKey = (LurConfig.SoldAfterKey.Value ?? "") }; LurPlugin.Log.LogInfo((object)("Hildir will stock Lur at " + _row.m_price + " coins.")); return _row; } internal static void Offer(Trader trader, List<TradeItem> items) { if (!LurConfig.Enabled.Value || (Object)(object)trader == (Object)null || items == null || Utils.GetPrefabName(((Component)trader).gameObject) != "Hildir") { return; } TradeItem val = Row(); if (val != null) { val.m_price = Mathf.Max(0, LurConfig.Price.Value); val.m_stack = Mathf.Max(1, LurConfig.Stack.Value); val.m_requiredGlobalKey = LurConfig.SoldAfterKey.Value ?? ""; if ((string.IsNullOrEmpty(val.m_requiredGlobalKey) || ZoneSystem.instance.GetGlobalKey(val.m_requiredGlobalKey)) && !items.Contains(val)) { items.Add(val); } } } } }