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 Kynda v1.0.2
plugins/Kynda/Kynda.dll
Decompiled 13 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Ezomic.Core; using HarmonyLib; using Microsoft.CodeAnalysis; using SoftReferenceableAssets; 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("Load smelters, kilns and fires several at a time, and build upgrades to make them hold more.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+df6ae78643f784eea9d41d4d1ef029efbac06ea6")] [assembly: AssemblyProduct("Kynda")] [assembly: AssemblyTitle("Kynda")] [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 Kynda { internal static class BatchAdd { private static readonly MethodInfo SmelterGetFuel = AccessTools.Method(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null); private static readonly MethodInfo SmelterGetQueueSize = AccessTools.Method(typeof(Smelter), "GetQueueSize", (Type[])null, (Type[])null); private static readonly MethodInfo SmelterFindCookable = AccessTools.Method(typeof(Smelter), "FindCookableItem", (Type[])null, (Type[])null); private static readonly FieldInfo SmelterNView = AccessTools.Field(typeof(Smelter), "m_nview"); private static readonly FieldInfo FireplaceNView = AccessTools.Field(typeof(Fireplace), "m_nview"); private static bool Ready { get { if (SmelterGetFuel != null && SmelterGetQueueSize != null && SmelterFindCookable != null && SmelterNView != null) { return FireplaceNView != null; } return false; } } public static bool Verify() { List<string> list = new List<string>(); if (SmelterGetFuel == null) { list.Add("Smelter.GetFuel"); } if (SmelterGetQueueSize == null) { list.Add("Smelter.GetQueueSize"); } if (SmelterFindCookable == null) { list.Add("Smelter.FindCookableItem"); } if (SmelterNView == null) { list.Add("Smelter.m_nview"); } if (FireplaceNView == null) { list.Add("Fireplace.m_nview"); } if (list.Count == 0) { return true; } KyndaPlugin.Log.LogError((object)("Game members this mod reflects on are missing - batching is disabled: " + string.Join(", ", list.ToArray()))); return false; } private static int Extra(int perAdd) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) KeyCode value = KyndaConfig.BatchModifier.Value; if ((int)value != 0 && !Input.GetKey(value)) { return 0; } return Mathf.Max(0, perAdd - 1); } internal unsafe static string BatchHint(int perAdd) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (perAdd <= 1) { return ""; } KeyCode value = KyndaConfig.BatchModifier.Value; if ((int)value != 0) { return "\n[<color=yellow><b>" + ((object)(*(KeyCode*)(&value))/*cast due to .constrained prefix*/).ToString() + "</b></color>] x" + perAdd; } return "\n<color=grey>x" + perAdd + " per press</color>"; } [HarmonyPostfix] [HarmonyPatch(typeof(Smelter), "OnAddFuel")] private static void BatchFuel(Smelter __instance, bool __result, Humanoid user) { if (!__result || (Object)(object)user == (Object)null || !Ready) { return; } int num = Extra(KyndaConfig.SmelterItemsPerAdd.Value); if (num <= 0) { return; } object? value = SmelterNView.GetValue(__instance); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return; } Inventory inventory = user.GetInventory(); if (inventory == null || (Object)(object)__instance.m_fuelItem == (Object)null) { return; } string name = __instance.m_fuelItem.m_itemData.m_shared.m_name; float num2 = (float)SmelterGetFuel.Invoke(__instance, null) + 1f; int num3 = 0; for (int i = 0; i < num; i++) { if (num2 > (float)(__instance.m_maxFuel - 1)) { break; } if (!inventory.HaveItem(name, true)) { break; } inventory.RemoveItem(name, 1, -1, true); val.InvokeRPC("RPC_AddFuel", Array.Empty<object>()); num2 += 1f; num3++; } Report(__instance.m_name, "fuel", num3); } [HarmonyPostfix] [HarmonyPatch(typeof(Smelter), "OnAddOre")] private static void BatchOre(Smelter __instance, bool __result, Humanoid user) { if (!__result || (Object)(object)user == (Object)null || !Ready) { return; } int num = Extra(KyndaConfig.SmelterItemsPerAdd.Value); if (num <= 0) { return; } object? value = SmelterNView.GetValue(__instance); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return; } Inventory inventory = user.GetInventory(); if (inventory == null) { return; } int num2 = (int)SmelterGetQueueSize.Invoke(__instance, null) + 1; int num3 = 0; for (int i = 0; i < num; i++) { if (num2 >= __instance.m_maxOre) { break; } object? obj = SmelterFindCookable.Invoke(__instance, new object[1] { inventory }); ItemData val2 = (ItemData)((obj is ItemData) ? obj : null); if (val2 == null || (Object)(object)val2.m_dropPrefab == (Object)null) { break; } inventory.RemoveItem(val2, 1); val.InvokeRPC("RPC_AddOre", new object[1] { ((Object)val2.m_dropPrefab).name }); num2++; num3++; } Report(__instance.m_name, "ore", num3); } [HarmonyPostfix] [HarmonyPatch(typeof(Fireplace), "UseItem")] private static void BatchFireplaceUseItem(Fireplace __instance, bool __result, Humanoid user, ItemData item) { if (__result && !((Object)(object)user == (Object)null) && item != null && !((Object)(object)__instance.m_fuelItem == (Object)null) && !(item.m_shared.m_name != __instance.m_fuelItem.m_itemData.m_shared.m_name)) { TopUpFire(__instance, user); } } [HarmonyPostfix] [HarmonyPatch(typeof(Fireplace), "Interact")] private static void BatchFireplaceInteract(Fireplace __instance, bool __result, Humanoid user, bool hold, bool alt) { if (!(!__result || hold) && !((Object)(object)user == (Object)null) && Ready) { object? value = FireplaceNView.GetValue(__instance); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if (!((Object)(object)val == (Object)null) && val.IsValid() && (!__instance.m_canTurnOff || alt || !(val.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > 0f))) { TopUpFire(__instance, user); } } } private static void TopUpFire(Fireplace fireplace, Humanoid user) { int num = Extra(KyndaConfig.FireplaceItemsPerAdd.Value); if (num <= 0 || !Ready || fireplace.m_infiniteFuel || !fireplace.m_canRefill) { return; } object? value = FireplaceNView.GetValue(fireplace); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return; } Inventory inventory = user.GetInventory(); if (inventory == null || (Object)(object)fireplace.m_fuelItem == (Object)null) { return; } string name = fireplace.m_fuelItem.m_itemData.m_shared.m_name; float num2 = val.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) + 1f; int num3 = 0; for (int i = 0; i < num; i++) { if ((float)Mathf.CeilToInt(num2) >= fireplace.m_maxFuel) { break; } if (!inventory.HaveItem(name, true)) { break; } inventory.RemoveItem(name, 1, -1, true); val.InvokeRPC("RPC_AddFuel", Array.Empty<object>()); num2 += 1f; num3++; } Report(fireplace.m_name, "logs", num3); } private static void Report(string station, string what, int added) { if (KyndaConfig.Verbose.Value && added != 0) { KyndaPlugin.Log.LogInfo((object)(station + ": batched " + added + " extra " + what + ".")); } } } internal static class HoverHint { [HarmonyPostfix] [HarmonyPatch(typeof(Smelter), "OnHoverAddFuel")] private static void SmelterFuelHover(Smelter __instance, ref string __result) { if (__instance.m_maxFuel > 0) { __result += BatchAdd.BatchHint(KyndaConfig.SmelterItemsPerAdd.Value); } } [HarmonyPostfix] [HarmonyPatch(typeof(Smelter), "OnHoverAddOre")] private static void SmelterOreHover(Smelter __instance, ref string __result) { if (__instance.m_maxOre > 0) { __result += BatchAdd.BatchHint(KyndaConfig.SmelterItemsPerAdd.Value); } } [HarmonyPostfix] [HarmonyPatch(typeof(Fireplace), "GetHoverText")] private static void FireplaceHover(Fireplace __instance, ref string __result) { if (!string.IsNullOrEmpty(__result) && __instance.m_canRefill && !__instance.m_infiniteFuel) { __result += BatchAdd.BatchHint(KyndaConfig.FireplaceItemsPerAdd.Value); } } public static void Apply(Harmony harmony) { try { harmony.PatchAll(typeof(HoverHint)); } catch (Exception ex) { KyndaPlugin.Log.LogWarning((object)("Could not add the batch hint to hover text - batching still works, it just will not announce itself: " + ex.Message)); } } } internal static class IconRender { private const int Size = 512; private const int Layer = 31; public static Sprite Shoot(GameObject prefab, string name) { //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00ae: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: 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) //IL_01a9: 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_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Expected O, but got Unknown //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) GameObject val = null; GameObject val2 = null; RenderTexture val3 = null; RenderTexture active = RenderTexture.active; try { bool forceDisableInit = ZNetView.m_forceDisableInit; ZNetView.m_forceDisableInit = true; try { val = Object.Instantiate<GameObject>(prefab); } finally { ZNetView.m_forceDisableInit = forceDisableInit; } ((Object)val).name = name + "_icon_subject"; val.transform.position = new Vector3(0f, -8000f, 0f); val.transform.rotation = Quaternion.identity; SetLayer(val, 31); Bounds val4 = Framing(val); if (((Bounds)(ref val4)).size == Vector3.zero) { return null; } val2 = new GameObject(name + "_icon_rig"); val2.transform.position = ((Bounds)(ref val4)).center; val2.transform.rotation = Quaternion.Euler(22f, 32f, 0f); Camera val5 = val2.AddComponent<Camera>(); val5.orthographic = true; val5.clearFlags = (CameraClearFlags)2; val5.cullingMask = int.MinValue; ((Behaviour)val5).enabled = false; Vector3 extents = ((Bounds)(ref val4)).extents; float magnitude = ((Vector3)(ref extents)).magnitude; val5.orthographicSize = magnitude * 1.06f; val5.nearClipPlane = 0.01f; val5.farClipPlane = magnitude * 8f; ((Component)val5).transform.position = ((Bounds)(ref val4)).center - ((Component)val5).transform.forward * magnitude * 4f; Light(val2.transform, magnitude); val3 = (val5.targetTexture = RenderTexture.GetTemporary(512, 512, 24, (RenderTextureFormat)0, (RenderTextureReadWrite)2)); bool fog = RenderSettings.fog; AmbientMode ambientMode = RenderSettings.ambientMode; Color ambientLight = RenderSettings.ambientLight; float ambientIntensity = RenderSettings.ambientIntensity; Color[] array; Color[] array2; try { RenderSettings.fog = false; RenderSettings.ambientMode = (AmbientMode)3; RenderSettings.ambientLight = new Color(0.34f, 0.35f, 0.38f); RenderSettings.ambientIntensity = 1f; array = Expose(val5, val3, Color.black); array2 = Expose(val5, val3, Color.white); } finally { RenderSettings.fog = fog; RenderSettings.ambientMode = ambientMode; RenderSettings.ambientLight = ambientLight; RenderSettings.ambientIntensity = ambientIntensity; } Color[] array3 = (Color[])(object)new Color[array.Length]; int num = 0; for (int i = 0; i < array3.Length; i++) { float num2 = 1f - (array2[i].r - array[i].r); num2 = Mathf.Clamp01(Mathf.Max(num2, Mathf.Max(1f - (array2[i].g - array[i].g), 1f - (array2[i].b - array[i].b)))); array3[i] = ((num2 <= 0.004f) ? new Color(0f, 0f, 0f, 0f) : new Color(array[i].r / num2, array[i].g / num2, array[i].b / num2, num2)); if (num2 > 0.5f) { num++; } } Texture2D val6 = new Texture2D(512, 512, (TextureFormat)4, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, name = name + "_icon", hideFlags = (HideFlags)61 }; val6.SetPixels(array3); val6.Apply(); KyndaPlugin.Log.LogInfo((object)$"Icon for {name}: {512}px, subject {((Bounds)(ref val4)).size.x:0.00}x{((Bounds)(ref val4)).size.y:0.00}x{((Bounds)(ref val4)).size.z:0.00}m, {(float)num * 100f / (float)array3.Length:0}% of the frame covered."); Dump(val6, name); return Sprite.Create(val6, new Rect(0f, 0f, 512f, 512f), new Vector2(0.5f, 0.5f)); } catch (Exception ex) { KyndaPlugin.Log.LogWarning((object)("Could not photograph " + name + " for its icon: " + ex.Message + " - falling back to the png beside the dll.")); return null; } finally { RenderTexture.active = active; if ((Object)(object)val3 != (Object)null) { RenderTexture.ReleaseTemporary(val3); } if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)val2); } if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)val); } } } private static Color[] Expose(Camera camera, RenderTexture target, Color background) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_0040: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) camera.backgroundColor = new Color(background.r, background.g, background.b, 1f); camera.Render(); RenderTexture.active = target; Texture2D val = new Texture2D(512, 512, (TextureFormat)4, false); try { val.ReadPixels(new Rect(0f, 0f, 512f, 512f), 0, 0); val.Apply(); return val.GetPixels(); } finally { Object.DestroyImmediate((Object)(object)val); } } private static void Dump(Texture2D texture, string name) { if (KyndaConfig.Verbose == null || !KyndaConfig.Verbose.Value) { return; } try { Type type = AccessTools.TypeByName("UnityEngine.ImageConversion"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "EncodeToPNG", new Type[1] { typeof(Texture2D) }, (Type[])null) : null); if (!(methodInfo == null) && methodInfo.Invoke(null, new object[1] { texture }) is byte[] bytes) { string text = Path.Combine(Path.GetDirectoryName(typeof(IconRender).Assembly.Location), name + "_rendered.png"); File.WriteAllBytes(text, bytes); KyndaPlugin.Log.LogInfo((object)("Wrote " + text + " to look at.")); } } catch (Exception ex) { KyndaPlugin.Log.LogWarning((object)("Could not dump the icon: " + ex.Message)); } } private static Bounds Framing(GameObject subject) { //IL_0002: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_003c: 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) Bounds result = default(Bounds); bool flag = false; Renderer[] componentsInChildren = subject.GetComponentsInChildren<Renderer>(false); foreach (Renderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && val.enabled && !(val is ParticleSystemRenderer)) { if (!flag) { result = val.bounds; flag = true; } else { ((Bounds)(ref result)).Encapsulate(val.bounds); } } } if (!flag) { return new Bounds(subject.transform.position, Vector3.zero); } return result; } private static void Light(Transform rig, float reach) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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) //IL_0098: 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) Transform transform = new GameObject("key").transform; transform.SetParent(rig, false); transform.localRotation = Quaternion.Euler(28f, -22f, 0f); Light obj = ((Component)transform).gameObject.AddComponent<Light>(); obj.type = (LightType)1; obj.color = new Color(1f, 0.96f, 0.88f); obj.intensity = 1.35f; obj.cullingMask = int.MinValue; Transform transform2 = new GameObject("fill").transform; transform2.SetParent(rig, false); transform2.localRotation = Quaternion.Euler(-14f, 158f, 0f); Light obj2 = ((Component)transform2).gameObject.AddComponent<Light>(); obj2.type = (LightType)1; obj2.color = new Color(0.72f, 0.78f, 0.92f); obj2.intensity = 0.55f; obj2.cullingMask = int.MinValue; } private static void SetLayer(GameObject root, int layer) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) root.layer = layer; foreach (Transform item in root.transform) { SetLayer(((Component)item).gameObject, layer); } } } internal static class KyndaConfig { public static ConfigEntry<int> SmelterItemsPerAdd; public static ConfigEntry<int> FireplaceItemsPerAdd; public static ConfigEntry<KeyCode> BatchModifier; public static ConfigEntry<bool> Verbose; public static ConfigEntry<string> DonorCarrierLocations; public static ConfigEntry<bool> Enabled; public static ConfigEntry<string> Donor; public static ConfigEntry<string> Station; public static ConfigEntry<float> Range; public static ConfigEntry<int> MaxPerStation; public static ConfigEntry<float> TexelsPerMetre; public static ConfigEntry<bool> ShowLink; public static ConfigEntry<float> LinkHeight; public static ConfigEntry<string> PrefabSearch; public static ConfigEntry<bool> TestMode; public static ConfigEntry<bool> VariantMode; public static ConfigEntry<string> SkinTrials; public static ConfigEntry<bool> DumpShader; private const string TestCost = "Wood:1"; public static string CostNow(UpgradeDef def) { if (!TestMode.Value && def.Cost != null) { return def.Cost.Value; } return "Wood:1"; } public static void Bind(ConfigFile config) { TestMode = config.Bind<bool>("Diagnostics", "TestMode", false, "Makes both upgrades cost one wood, so they can be built and looked at without bronze. Announced in the log on startup so it is hard to leave on."); DonorCarrierLocations = config.Bind<string>("Diagnostics", "DonorCarrierLocations", "Vendor,Hildir", "Fallback only, and normally unused. A missing donor material is now loaded straight out of its own bundle by name, so no location has to be summoned for it. These location prefabs are soft-ref loaded only if that direct load finds nothing under the wanted name - a game update renaming an asset, or one of the few assets that exist solely inside a containing prefab. Comma-separated name fragments; blank turns the fallback off."); Verbose = config.Bind<bool>("Diagnostics", "Verbose", false, "Log each batched add and why it stopped."); VariantMode = config.Bind<bool>("Diagnostics", "VariantMode", false, "Put every candidate model on the hammer as its own piece at one wood each, named 'var: ...', so they can be built side by side and compared. DESTRUCTIVE WHEN TURNED OFF: anything built from a variant vanishes with it, because its prefab name stops existing. Build them somewhere you do not mind losing."); SkinTrials = config.Bind<string>("Diagnostics", "SkinTrials", "", "Comma-separated donor prefabs. Puts one copy of each upgrade on the hammer per donor, named 'skin: ...', so the same model can be seen wearing every candidate surface side by side. Empty turns it off. DESTRUCTIVE WHEN TURNED OFF, exactly as VariantMode is: anything built from a trial vanishes when its prefab name stops existing."); DumpShader = config.Bind<bool>("Diagnostics", "DumpShader", false, "List every property of each borrowed material's shader, with its type. Needed before our own texture can be written into one."); PrefabSearch = config.Bind<string>("Diagnostics", "PrefabSearch", "", "Comma-separated words. Every loaded prefab whose name contains one is listed in the log, which is how to find a prefab worth borrowing a material from. Empty turns it off. Scans everything loaded, so empty it again when you are done."); Enabled = config.Bind<bool>("Upgrades", "Enabled", true, "Add the two buildable upgrades that raise a nearby station's capacity. They register prefabs, so a world that later loads without this mod discards every one already built rather than reporting an error - which is what the version gate exists to prevent."); Donor = config.Bind<string>("Upgrades", "Donor", "piece_chest_barrel", "Prefab cloned for its machinery - ZNetView, Piece, WearNTear, placement rules. Its look, collision and icon are all replaced, so this is not a visual choice. Falls back to piece_chest_wood. Needs a restart."); Station = config.Bind<string>("Upgrades", "Station", "forge", "Prefab name of the crafting station you must stand near to build these. The forge, because both upgrades are held together with nails and a workbench could never have made them. Empty or an unknown name leaves the donor's, which is the workbench."); Range = config.Bind<float>("Upgrades", "Range", 4f, "How close an upgrade must be to the station it feeds."); MaxPerStation = config.Bind<int>("Upgrades", "MaxPerStation", 1, "How many upgrades of one kind count for a single station. One, because these are a one-time improvement rather than something to stack - the capacity figures are chosen to land on a round number exactly once. Raising it stacks them again; a bin that is not counting says so when you look at it."); TexelsPerMetre = config.Bind<float>("Upgrades", "TexelsPerMetre", 28f, "How coarse the borrowed texture is drawn, in texels per metre. Vanilla's props and piles run 24 to 54; its structural pieces run far finer and are the wrong thing to match. Higher is finer and eventually reads as flat colour, because the grain becomes smaller than a pixel on screen. A group too big for its donor's slice of the atlas is drawn coarser than this rather than tiled - the log says when."); ShowLink = config.Bind<bool>("Upgrades", "ShowLink", true, "Draw the game's own station-link effect from an upgrade to the station it feeds when you look at it - the same run of motes a chopping block draws to its workbench. Off is silent."); LinkHeight = config.Bind<float>("Upgrades", "LinkHeight", 0.8f, "How far up the upgrade the link starts, in metres. The default leaves it around the top of both pieces; at 0 it comes out of the ground."); UpgradePrefabs.Trough.Name = config.Bind<string>("Trough", "Name", "Tun", "Name shown on the hammer and when you look at one."); UpgradePrefabs.Trough.Stations = config.Bind<string>("Trough", "Stations", "smelter", "Station prefabs this upgrades, comma separated. A station not named here is left alone even if it is the right kind, and a bin standing next to one says it is feeding nothing rather than pretending."); UpgradePrefabs.Trough.Cost = config.Bind<string>("Trough", "Cost", "FineWood:20,IronNails:15", "Build cost, as Item:Amount pairs. The iron nails put it a biome beyond the smelter it upgrades, so it is an improvement you return to make rather than part of the original build."); UpgradePrefabs.Trough.Model = config.Bind<string>("Trough", "Model", "kynda_tun_camp.obj", "The OBJ loaded from beside the DLL. Its .col sidecar supplies the collision and its _icon.png the hammer icon, both matched by name - so dropping in a new model brings its own shape and picture with it."); UpgradePrefabs.Trough.Scale = config.Bind<float>("Trough", "Scale", 1f, "Overall size of the trough. Scales the collision with it, since the boxes are children of the piece."); UpgradePrefabs.Trough.SkinDonors = config.Bind<string>("Trough", "SkinDonors", "@fi_village_wood:keep,coal=@coal_pile:keep,ore=@copper_ore:0.02/0.30/0.46/0.22", "Which vanilla prefab this piece borrows its surface from. A bare prefab name covers the whole piece, which is the usual case and matches how vanilla builds a piece - one texture carrying every substance it is made of. group=prefab pairs override a single group. Empty uses the general list. Ore and coal keep their own surfaces either way: they are what is in the piece rather than what it is made of, and vanilla gives a smelter's ore heap its own material too."); UpgradePrefabs.Trough.OreCapacity = config.Bind<int>("Trough", "OreCapacity", 20, "Extra ore a smelter or furnace holds per trough. Vanilla's 10 becomes 30."); UpgradePrefabs.Trough.FuelCapacity = config.Bind<int>("Trough", "FuelCapacity", 40, "Extra coal a smelter or furnace holds per trough. Vanilla's 20 becomes 60. Twice the ore figure on purpose - a smelter burns two coal per ore, so matching them would run the fuel out before the ore."); UpgradePrefabs.Woodrack.Stations = config.Bind<string>("Woodrack", "Stations", "charcoal_kiln", "Station prefabs this upgrades, comma separated. A station not named here is left alone even if it is the right kind."); UpgradePrefabs.Woodrack.Name = config.Bind<string>("Woodrack", "Name", "Woodrack", "Name shown on the hammer and when you look at one."); UpgradePrefabs.Woodrack.Cost = config.Bind<string>("Woodrack", "Cost", "FineWood:25,DeerHide:20,BronzeNails:25", "Build cost, as Item:Amount pairs. The bronze nails put it a tier behind the charcoal kiln it serves, so it is something you come back and add rather than raise alongside the kiln itself."); UpgradePrefabs.Woodrack.Model = config.Bind<string>("Woodrack", "Model", "kynda_rack_camp.obj", "The OBJ loaded from beside the DLL, with its .col and _icon.png matched by name."); UpgradePrefabs.Woodrack.Scale = config.Bind<float>("Woodrack", "Scale", 1f, "Overall size of the woodrack. Scales the collision with it, since the boxes are children of the piece."); UpgradePrefabs.Woodrack.SkinDonors = config.Bind<string>("Woodrack", "SkinDonors", "@wood_item:keep,frame=@woodwall:keep,roof=@straw_roof:keep,roofalpha=@straw_roof_alpha:keep", "Which vanilla prefab this piece borrows its surface from. A bare prefab name covers the whole piece; group=prefab pairs override a single group. Empty uses the general list."); UpgradePrefabs.Woodrack.OreCapacity = config.Bind<int>("Woodrack", "OreCapacity", 25, "Extra wood a charcoal kiln holds per woodrack. Vanilla's 25 becomes 50."); BatchModifier = config.Bind<KeyCode>("Batching", "BatchModifier", (KeyCode)304, "Hold this while interacting to add a batch instead of one. Plain use stays vanilla, so nothing is taken away - the batch is an option you reach for."); SmelterItemsPerAdd = config.Bind<int>("Batching", "SmelterItemsPerAdd", 3, "Ore or coal added per press at a smelter, kiln, blast furnace, windmill, spinning wheel or eitr refinery. Stops early at the station's capacity or when you run out. 1 restores vanilla."); FireplaceItemsPerAdd = config.Bind<int>("Batching", "FireplaceItemsPerAdd", 3, "Logs added per press at a campfire, hearth or torch. 1 restores vanilla."); } } [BepInPlugin("ezomic.valheim.kynda", "Kynda", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class KyndaPlugin : BaseUnityPlugin { public const string PluginGuid = "ezomic.valheim.kynda"; public const string PluginName = "Kynda"; public const string PluginVersion = "1.0.2"; public const string PluginAuthor = "Robbin Thijssen"; private const string CoreGuid = "ezomic.valheim.core"; internal static bool CorePresent; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; KyndaConfig.Bind(((BaseUnityPlugin)this).Config); SoftAssets.MakeEverythingLoadable(); TryRegisterWithCore(); _harmony = new Harmony("ezomic.valheim.kynda"); _harmony.PatchAll(typeof(ScenePatches)); if (BatchAdd.Verify()) { _harmony.PatchAll(typeof(BatchAdd)); HoverHint.Apply(_harmony); } Log.LogInfo((object)"Kynda 1.0.2 by Robbin Thijssen - ready."); if (KyndaConfig.TestMode.Value) { Log.LogWarning((object)"TEST MODE: both upgrades cost one wood. Turn TestMode off in the config before playing for real."); } } private void TryRegisterWithCore() { CorePresent = Chainloader.PluginInfos.ContainsKey("ezomic.valheim.core"); if (!CorePresent) { Log.LogWarning((object)"Core is not installed, so there is no version gate. The upgrades still work, but a world loaded without this mod discards every one already built - and nothing will stop that happening."); } else { RegisterWithCore(); } } [MethodImpl(MethodImplOptions.NoInlining)] private void RegisterWithCore() { Suite.Register("ezomic.valheim.kynda", "Kynda", "1.0.2", ((BaseUnityPlugin)this).Config, (Requirement)0, (Assembly)null); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } private void Update() { if (!((Object)(object)ZNetScene.instance == (Object)null) && !((Object)(object)ObjectDB.instance == (Object)null)) { UpgradePrefabs.Register(); Skins.Tick(); UpgradePrefabs.RefreshIcons(); } } } internal sealed class ModelData { public Mesh Mesh; public string[] Groups; } internal static class ObjMesh { public static ModelData Load(string path) { if (!File.Exists(path)) { KyndaPlugin.Log.LogWarning((object)("No model at " + path)); return null; } try { return Parse(File.ReadAllLines(path), Path.GetFileNameWithoutExtension(path)); } catch (Exception ex) { KyndaPlugin.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) { KyndaPlugin.Log.LogError((object)("Model " + name + " has no geometry.")); return null; } Mesh val = new Mesh { name = name }; if (list7.Count > 65535) { val.indexFormat = (IndexFormat)1; KyndaPlugin.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; } KyndaPlugin.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; 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; } public static void Forget() { _index = null; } 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; } KyndaPlugin.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(); } 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 (KeyValuePair<string, GameObject> item in _index) { if (item.Key.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { string text2 = item.Key.ToLowerInvariant(); if (!text2.Contains("destruction") && !text2.Contains("broken") && !text2.Contains("lod") && !text2.Contains("vfx") && !text2.Contains("sfx")) { list.Add(item.Key); } } } list.Sort(); KyndaPlugin.Log.LogInfo((object)("Prefabs matching '" + text + "' (" + list.Count + "): " + string.Join(", ", list.GetRange(0, Math.Min(40, list.Count)).ToArray()))); } } } internal static class ScenePatches { [HarmonyPostfix] [HarmonyPatch(typeof(ZNetScene), "Awake")] private static void OnSceneAwake() { int num = SmelterCapacity.AttachToPrefabs(); if (num > 0) { KyndaPlugin.Log.LogInfo((object)("Capacity component added to " + num + " station prefab(s).")); } UpgradePrefabs.Register(); PropIndex.Search(KyndaConfig.PrefabSearch.Value); } [HarmonyPostfix] [HarmonyPatch(typeof(ObjectDB), "Awake")] private static void OnObjectDbAwake() { Skins.Invalidate(); UpgradeBin.ForgetConnectionPrefab(); UpgradePrefabs.Register(); } [HarmonyPostfix] [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static void OnObjectDbCopy() { Skins.Invalidate(); UpgradeBin.ForgetConnectionPrefab(); UpgradePrefabs.Register(); } } internal static class Skins { private sealed class LateSkin { public MeshRenderer Renderer; public Mesh Mesh; public Vector2[] OriginalUv; public string[] Groups; public IDictionary<string, string> Overrides; } private static readonly Dictionary<string, string[]> Donors = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase) { { "wood", new string[3] { "piece_chest_wood", "wood_wall_log", "darkwood_beam" } }, { "frame", new string[3] { "wood_beam", "wood_wall_log", "wood_pole" } }, { "iron", new string[3] { "piece_artisanstation", "forge", "piece_cauldron" } }, { "stone", new string[3] { "stone_wall_2x1", "piece_stonecutter", "smelter" } }, { "coal", new string[3] { "coal_pile", "Coal", "charcoal_kiln" } }, { "ore", new string[3] { "CopperOre", "TinOre", "IronOre" } } }; 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); private static readonly Dictionary<string, int> TexPx = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private const string EndSuffix = "_end"; public const string Everything = "*"; private static readonly HashSet<string> Contents = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "ore", "coal" }; private static float _nextSummon; private static string _summonFault; private static readonly HashSet<string> FlipV = new HashSet<string>(StringComparer.Ordinal); private static readonly List<LateSkin> _late = new List<LateSkin>(); private static float _nextLate; private static readonly HashSet<string> _dumped = new HashSet<string>(StringComparer.Ordinal); public static void Invalidate() { Cache.Clear(); Atlas.Clear(); TexPx.Clear(); PropIndex.Forget(); } private static string Named(string group, IDictionary<string, string> overrides) { if (overrides == null) { return null; } if (overrides.TryGetValue(group, out var value) && !string.IsNullOrEmpty(value)) { return value; } string text = Base(group); if (overrides.TryGetValue(text, out value) && !string.IsNullOrEmpty(value)) { return value; } if (Contents.Contains(text)) { return null; } if (!overrides.TryGetValue("*", out value) || string.IsNullOrEmpty(value)) { return null; } return value; } private static bool IsEnd(string group) { if (group != null && group.Length > "_end".Length) { return group.EndsWith("_end", StringComparison.OrdinalIgnoreCase); } return false; } private static string Base(string group) { if (!IsEnd(group)) { return group; } return group.Substring(0, group.Length - "_end".Length); } private static string Key(string group, IDictionary<string, string> overrides) { string text = Named(group, overrides); if (!string.IsNullOrEmpty(text)) { return group + "|" + text; } return group; } private static Material FindMaterial(string name) { if (string.IsNullOrEmpty(name)) { return null; } Material val = null; Material[] array = Resources.FindObjectsOfTypeAll<Material>(); foreach (Material val2 in array) { if ((Object)(object)val2 == (Object)null || (Object)(object)val2.shader == (Object)null) { continue; } if (((Object)val2).name == name) { return val2; } if (!((Object)(object)val != (Object)null)) { if (string.Equals(((Object)val2).name, name, StringComparison.OrdinalIgnoreCase)) { val = val2; } else if (((Object)val2).name.StartsWith(name, StringComparison.OrdinalIgnoreCase)) { val = val2; } } } if ((Object)(object)val == (Object)null) { Material val3 = SoftAssets.LoadMaterial(name); if ((Object)(object)val3 != (Object)null) { return val3; } List<string> list = new List<string>(); array = Resources.FindObjectsOfTypeAll<Material>(); foreach (Material val4 in array) { if (!((Object)(object)val4 == (Object)null) && ((Object)val4).name != null && ((Object)val4).name.IndexOf(name.Split(new char[1] { '_' })[0], StringComparison.OrdinalIgnoreCase) >= 0) { if (!list.Contains(((Object)val4).name)) { list.Add(((Object)val4).name); } if (list.Count >= 12) { break; } } } KyndaPlugin.Log.LogWarning((object)("No loaded material called '" + name + "'. It only exists once something using it has loaded. Loaded names sharing its prefix: " + ((list.Count == 0) ? "none" : string.Join(", ", list.ToArray())))); SummonDonorCarriers(); } return val; } private static void SummonDonorCarriers() { try { SummonDonorCarriersInner(); } catch (Exception ex) { if (_summonFault == null) { _summonFault = ex.ToString(); KyndaPlugin.Log.LogError((object)("Carrier summon failed: " + _summonFault)); } } } private static void SummonDonorCarriersInner() { if (Time.time < _nextSummon) { return; } ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || instance.m_locations == null) { return; } _nextSummon = Time.time + 30f; string[] array = (KyndaConfig.DonorCarrierLocations.Value ?? "").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return; } int num = 0; int num2 = 0; foreach (ZoneLocation location in instance.m_locations) { if (location == null) { continue; } num++; string prefabName = location.m_prefabName; if (string.IsNullOrEmpty(prefabName)) { continue; } string[] array2 = array; foreach (string text in array2) { if (prefabName.IndexOf(text.Trim(), StringComparison.OrdinalIgnoreCase) < 0) { continue; } num2++; try { if (location.m_prefab.IsValid && !location.m_prefab.IsLoaded && !location.m_prefab.IsLoading) { location.m_prefab.LoadAsync(); KyndaPlugin.Log.LogInfo((object)("Summoned location asset '" + prefabName + "' for its donor materials.")); } } catch (Exception) { } break; } } KyndaPlugin.Log.LogInfo((object)("Carrier summon pass: " + num + " locations, " + num2 + " matched.")); } public static Material[] SkinAndWatch(MeshRenderer renderer, Mesh mesh, string[] groups, IDictionary<string, string> overrides) { Material[] array = Skin(groups, overrides); for (int i = 0; i < groups.Length; i++) { if (!((Object)(object)array[i] != (Object)null) && Key(groups[i], overrides).Contains("@")) { _late.Add(new LateSkin { Renderer = renderer, Mesh = mesh, OriginalUv = (((Object)(object)mesh != (Object)null) ? mesh.uv : null), Groups = groups, Overrides = overrides }); KyndaPlugin.Log.LogInfo((object)"A skin donor is not loaded yet - it will be applied when its location streams in."); break; } } return array; } public static void Tick() { if (_late.Count == 0 || Time.realtimeSinceStartup < _nextLate) { return; } _nextLate = Time.realtimeSinceStartup + 5f; for (int num = _late.Count - 1; num >= 0; num--) { LateSkin lateSkin = _late[num]; if ((Object)(object)lateSkin.Renderer == (Object)null || (Object)(object)lateSkin.Mesh == (Object)null) { _late.RemoveAt(num); } else { Material[] array = Skin(lateSkin.Groups, lateSkin.Overrides); bool flag = false; for (int i = 0; i < lateSkin.Groups.Length; i++) { if ((Object)(object)array[i] == (Object)null && Key(lateSkin.Groups[i], lateSkin.Overrides).Contains("@")) { flag = true; } } if (!flag) { if (lateSkin.OriginalUv != null) { lateSkin.Mesh.uv = lateSkin.OriginalUv; } ((Renderer)lateSkin.Renderer).sharedMaterials = array; Remap(lateSkin.Mesh, lateSkin.Groups, lateSkin.Overrides); MeshRenderer[] array2 = Object.FindObjectsOfType<MeshRenderer>(); foreach (MeshRenderer val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)lateSkin.Renderer)) { MeshFilter component = ((Component)val).GetComponent<MeshFilter>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.sharedMesh != (Object)(object)lateSkin.Mesh)) { ((Renderer)val).sharedMaterials = array; } } } _late.RemoveAt(num); KyndaPlugin.Log.LogInfo((object)"A late skin donor arrived and was applied, standing pieces included."); } } } } public static Material[] Skin(string[] groups, IDictionary<string, string> overrides) { Material[] array = (Material[])(object)new Material[groups.Length]; for (int i = 0; i < groups.Length; i++) { array[i] = For(groups[i], overrides); } return array; } public unsafe static Material For(string group, IDictionary<string, string> overrides) { //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_041c: Unknown result type (might be due to invalid IL or missing references) //IL_0437: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_044e: Unknown result type (might be due to invalid IL or missing references) //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) string text = Key(group, overrides); if (Cache.TryGetValue(text, out var value)) { return value; } string text2 = Base(group); string text3 = Named(group, overrides); string[] value2; if (!string.IsNullOrEmpty(text3)) { value2 = new string[1] { text3 }; } else if (!Donors.TryGetValue(text2, out value2)) { value2 = Donors["wood"]; } string[] array = value2; Rect val = default(Rect); foreach (string text4 in array) { string text5 = text4.Trim(); if (text5.StartsWith("@")) { string text6 = text5.Substring(1); ((Rect)(ref val))..ctor(0f, 0f, 1f, 1f); if (text6.EndsWith(":keep", StringComparison.OrdinalIgnoreCase)) { Material val2 = FindMaterial(text6.Substring(0, text6.Length - 5)); if (!((Object)(object)val2 == (Object)null)) { Texture texture = val2.GetTexture("_MainTex"); if (!((Object)(object)texture == (Object)null)) { Cache[text] = val2; Atlas.Remove(text); FlipV.Remove(text); TexPx[text] = Mathf.Max(1, texture.width); KyndaPlugin.Log.LogInfo((object)("'" + text + "' skinned with the material " + ((Object)val2).name + " found by name, vanilla UVs kept, " + texture.width + "px.")); return val2; } } continue; } int num = text6.IndexOf(':'); bool flag = false; if (num > 0) { string[] array2 = text6.Substring(num + 1).Split(new char[1] { '/' }); if (array2.Length == 4 && array2[3].StartsWith("~")) { flag = true; array2[3] = array2[3].Substring(1); } if (array2.Length == 4 && float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3) && float.TryParse(array2[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) && result3 > 0f && result4 > 0f) { ((Rect)(ref val))..ctor(result, result2, result3, result4); } else { KyndaPlugin.Log.LogWarning((object)("Could not read the rect in '" + text5 + "' - expected @name:x/y/w/h. Using the whole sheet, which will look like every tile at once.")); } text6 = text6.Substring(0, num); } Material val3 = FindMaterial(text6); if ((Object)(object)val3 == (Object)null) { continue; } Texture texture2 = val3.GetTexture("_MainTex"); if (!((Object)(object)texture2 == (Object)null)) { Cache[text] = val3; Atlas[text] = val; TexPx[text] = Mathf.Max(1, texture2.width); if (flag) { FlipV.Add(text); } else { FlipV.Remove(text); } ManualLogSource log = KyndaPlugin.Log; string[] obj = new string[12] { "'", text, "' skinned with the material ", ((Object)val3).name, " found by name (shader ", ((Object)val3.shader).name, "), rect ", null, null, null, null, null }; Rect val4 = val; obj[7] = ((object)(*(Rect*)(&val4))/*cast due to .constrained prefix*/).ToString(); obj[8] = (flag ? ", V flipped" : ""); obj[9] = ", "; obj[10] = texture2.width.ToString(); obj[11] = "px."; log.LogInfo((object)string.Concat(obj)); return val3; } continue; } GameObject val5 = PropIndex.Find(text5); if (!((Object)(object)val5 == (Object)null)) { MeshRenderer val6 = MainRenderer(val5); if ((Object)(object)val6 != (Object)null) { Material sharedMaterial = ((Renderer)val6).sharedMaterial; Texture texture3 = sharedMaterial.GetTexture("_MainTex"); Regions((Renderer)(object)val6, out var side, out var cap); Rect value3 = (IsEnd(group) ? cap : ((!IsMetal(text2)) ? side : MetalRegion(texture3, text4, side))); Cache[text] = sharedMaterial; Atlas[text] = value3; TexPx[text] = Mathf.Max(1, texture3.width); KyndaPlugin.Log.LogInfo((object)$"'{text}' skinned with {((Object)sharedMaterial).name} from {text4}/{((Object)val6).name} (shader {((Object)sharedMaterial.shader).name}), atlas {Atlas[text]}, {TexPx[text]}px."); DumpShader(sharedMaterial); return sharedMaterial; } } } KyndaPlugin.Log.LogWarning((object)("No material found for group '" + text + "'.")); if (!text.Contains("@")) { Cache[text] = null; } return null; } private unsafe static void DumpShader(Material material) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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) //IL_00ad: Invalid comparison between Unknown and I4 //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Invalid comparison between Unknown and I4 //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Invalid comparison between Unknown and I4 if (!KyndaConfig.DumpShader.Value || (Object)(object)material == (Object)null || (Object)(object)material.shader == (Object)null || !_dumped.Add(((Object)material.shader).name)) { return; } Shader shader = material.shader; int propertyCount = shader.GetPropertyCount(); KyndaPlugin.Log.LogInfo((object)("SHADER " + ((Object)shader).name + ": " + propertyCount + " properties.")); for (int i = 0; i < propertyCount; i++) { string propertyName = shader.GetPropertyName(i); ShaderPropertyType propertyType = shader.GetPropertyType(i); string text = ""; if ((int)propertyType == 4) { Texture texture = material.GetTexture(propertyName); text = (((Object)(object)texture == (Object)null) ? " (unset)" : (" = " + ((Object)texture).name + " " + texture.width + "x" + texture.height)); } else if ((int)propertyType == 0) { text = " = " + ((object)material.GetColor(propertyName)/*cast due to .constrained prefix*/).ToString(); } else if ((int)propertyType == 2 || (int)propertyType == 3) { text = " = " + material.GetFloat(propertyName); } KyndaPlugin.Log.LogInfo((object)(" " + propertyName + " (" + ((object)(*(ShaderPropertyType*)(&propertyType))/*cast due to .constrained prefix*/).ToString() + ")" + text)); } } private static List<List<int>> Islands(int[] indices, Vector2[] uv) { Dictionary<int, int> dictionary = new Dictionary<int, int>(); foreach (int num in indices) { if (num >= 0 && num < uv.Length && !dictionary.ContainsKey(num)) { dictionary[num] = num; } } for (int j = 0; j + 2 < indices.Length; j += 3) { int num2 = indices[j]; int num3 = indices[j + 1]; int num4 = indices[j + 2]; if (dictionary.ContainsKey(num2) && dictionary.ContainsKey(num3) && dictionary.ContainsKey(num4)) { Join(dictionary, num2, num3); Join(dictionary, num2, num4); } } List<int> list = new List<int>(dictionary.Keys); Dictionary<int, List<int>> dictionary2 = new Dictionary<int, List<int>>(); foreach (int item in list) { int key = Find(dictionary, item); if (!dictionary2.TryGetValue(key, out var value)) { value = (dictionary2[key] = new List<int>()); } value.Add(item); } return new List<List<int>>(dictionary2.Values); } private static int Find(Dictionary<int, int> parent, int v) { while (parent[v] != v) { parent[v] = parent[parent[v]]; v = parent[v]; } return v; } private static void Join(Dictionary<int, int> parent, int a, int b) { int num = Find(parent, a); int num2 = Find(parent, b); if (num != num2) { parent[num] = num2; } } private static Rect Cluster(List<Rect> rects) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016a: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, 0f, 0f); if (rects == null || rects.Count == 0) { return val; } List<Rect> list = new List<Rect>(rects); int num = 0; bool flag; do { flag = false; num++; for (int i = 0; i < list.Count; i++) { if (flag) { break; } for (int j = i + 1; j < list.Count; j++) { Rect val2 = list[i]; Rect val3 = list[j]; if (!(((Rect)(ref val2)).xMin > ((Rect)(ref val3)).xMax + 1f / 128f) && !(((Rect)(ref val3)).xMin > ((Rect)(ref val2)).xMax + 1f / 128f) && !(((Rect)(ref val2)).yMin > ((Rect)(ref val3)).yMax + 1f / 128f) && !(((Rect)(ref val3)).yMin > ((Rect)(ref val2)).yMax + 1f / 128f)) { float num2 = Mathf.Min(((Rect)(ref val2)).xMin, ((Rect)(ref val3)).xMin); float num3 = Mathf.Min(((Rect)(ref val2)).yMin, ((Rect)(ref val3)).yMin); list[i] = new Rect(num2, num3, Mathf.Max(((Rect)(ref val2)).xMax, ((Rect)(ref val3)).xMax) - num2, Mathf.Max(((Rect)(ref val2)).yMax, ((Rect)(ref val3)).yMax) - num3); list.RemoveAt(j); flag = true; break; } } } } while (flag && num < 4096); Rect result = val; foreach (Rect item in list) { Rect current = item; if (((Rect)(ref current)).width * ((Rect)(ref current)).height > ((Rect)(ref result)).width * ((Rect)(ref result)).height) { result = current; } } return result; } private static MeshRenderer MainRenderer(GameObject donor) { MeshRenderer result = null; int num = 0; MeshRenderer[] componentsInChildren = donor.GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val in componentsInChildren) { Material sharedMaterial = ((Renderer)val).sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null || (Object)(object)sharedMaterial.shader == (Object)null || !sharedMaterial.HasProperty("_MainTex") || (Object)(object)sharedMaterial.GetTexture("_MainTex") == (Object)null) { continue; } MeshFilter component = ((Component)val).GetComponent<MeshFilter>(); Mesh val2 = (((Object)(object)component != (Object)null) ? component.sharedMesh : null); if ((Object)(object)val2 == (Object)null) { continue; } int num2; try { if (!val2.isReadable) { continue; } num2 = val2.triangles.Length; goto IL_00a4; } catch { } continue; IL_00a4: if (num2 > num) { num = num2; result = val; } } return result; } private static bool IsMetal(string family) { return string.Equals(family, "iron", StringComparison.OrdinalIgnoreCase); } private static Rect MetalRegion(Texture sheet, string donor, Rect fallback) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_029e: 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_0058: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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_010f: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sheet == (Object)null) { return fallback; } int num = Mathf.Clamp(sheet.width, 8, 64); int num2 = Mathf.Clamp(sheet.height, 8, 64); RenderTexture val = null; RenderTexture active = RenderTexture.active; Texture2D val2 = null; Color[] pixels; try { val = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)2); Graphics.Blit(sheet, val); RenderTexture.active = val; val2 = new Texture2D(num, num2, (TextureFormat)5, false); val2.ReadPixels(new Rect(0f, 0f, (float)num, (float)num2), 0, 0); val2.Apply(); pixels = val2.GetPixels(); } catch (Exception ex) { KyndaPlugin.Log.LogWarning((object)("Could not read " + donor + "'s sheet to find its metal: " + ex.Message)); return fallback; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { RenderTexture.ReleaseTemporary(val); } if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)val2); } } if (pixels == null || pixels.Length < num * num2) { return fallback; } float[] array = new float[num]; float num3 = 0f; for (int i = 0; i < num; i++) { float num4 = 0f; for (int j = 0; j < num2; j++) { num4 += Saturation(pixels[j * num + i]); } array[i] = num4 / (float)num2; num3 += array[i]; } num3 /= (float)num; if (!Run(array, num3, out var from, out var to)) { return Grey(donor, fallback); } float[] array2 = new float[num2]; for (int k = 0; k < num2; k++) { float num5 = 0f; for (int l = from; l <= to; l++) { num5 += Saturation(pixels[k * num + l]); } array2[k] = num5 / (float)(to - from + 1); } if (!Run(array2, num3, out var from2, out var to2)) { from2 = 0; to2 = num2 - 1; } Rect result = default(Rect); ((Rect)(ref result))..ctor(((float)from + 1f) / (float)num, ((float)from2 + 1f) / (float)num2, Mathf.Max(1f, (float)(to - from) - 1f) / (float)num, Mathf.Max(1f, (float)(to2 - from2) - 1f) / (float)num2); KyndaPlugin.Log.LogInfo((object)$"{donor}'s metal is the {to - from + 1}x{to2 - from2 + 1} px block at {((Rect)(ref result)).x:0.000},{((Rect)(ref result)).y:0.000} - saturation {Mean(array, from, to):0.00} against {num3:0.00} across the sheet."); return result; } private static bool Run(float[] slices, float overall, out int from, out int to) { from = 0; to = -1; float num = Mathf.Min(overall * 0.5f, 0.18f); int num2 = 0; int num3 = -1; int num4 = -1; for (int i = 0; i < slices.Length; i++) { if (slices[i] <= num) { if (num4 < 0) { num4 = i; } if (i - num4 > num3 - num2) { num2 = num4; num3 = i; } } else { num4 = -1; } } if (num3 - num2 < 2) { return false; } from = num2; to = num3; return true; } private static float Saturation(Color c) { //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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(c.r, Mathf.Max(c.g, c.b)); float num2 = Mathf.Min(c.r, Mathf.Min(c.g, c.b)); if (!(num <= 0.0001f)) { return (num - num2) / num; } return 0f; } private static float Mean(float[] slices, int from, int to) { float num = 0f; for (int i = from; i <= to; i++) { num += slices[i]; } return num / (float)Mathf.Max(1, to - from + 1); } private static Rect Grey(string donor, Rect fallback) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) KyndaPlugin.Log.LogInfo((object)(donor + " has no unsaturated block - it is all one substance, so metal parts will wear the same surface as the rest of it.")); return fallback; } private static void Regions(Renderer renderer, out Rect side, out Rect cap) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0376: 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) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_0390: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Unknown result type (might be due to invalid IL or missing references) //IL_04e8: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_051b: Unknown result type (might be due to invalid IL or missing references) //IL_0520: 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_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_0501: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_0511: Unknown result type (might be due to invalid IL or missing references) //IL_0513: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, 1f, 1f); side = val; cap = val; 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; } Vector3[] vertices; Vector2[] uv; int[] triangles; try { if (!val3.isReadable) { return; } vertices = val3.vertices; uv = val3.uv; triangles = val3.triangles; } catch { return; } if (uv == null || uv.Length == 0 || triangles == null || triangles.Length < 3 || vertices == null || vertices.Length == 0) { return; } float[] array = new float[3]; List<Rect>[] array2 = new List<Rect>[3] { new List<Rect>(), new List<Rect>(), new List<Rect>() }; for (int i = 0; i + 2 < triangles.Length; i += 3) { int num = triangles[i]; int num2 = triangles[i + 1]; int num3 = triangles[i + 2]; if (num >= uv.Length || num2 >= uv.Length || num3 >= uv.Length || num >= vertices.Length || num2 >= vertices.Length || num3 >= vertices.Length) { continue; } Vector3 val4 = Vector3.Cross(vertices[num2] - vertices[num], vertices[num3] - vertices[num]); float magnitude = ((Vector3)(ref val4)).magnitude; if (!(magnitude <= 1E-09f)) { Vector3 val5 = val4 / magnitude; int num4 = ((!(Mathf.Abs(val5.x) >= Mathf.Abs(val5.y)) || !(Mathf.Abs(val5.x) >= Mathf.Abs(val5.z))) ? ((Mathf.Abs(val5.y) >= Mathf.Abs(val5.z)) ? 1 : 2) : 0); array[num4] += magnitude * 0.5f; float num5 = Mathf.Min(uv[num].x, Mathf.Min(uv[num2].x, uv[num3].x)); float num6 = Mathf.Max(uv[num].x, Mathf.Max(uv[num2].x, uv[num3].x)); float num7 = Mathf.Min(uv[num].y, Mathf.Min(uv[num2].y, uv[num3].y)); float num8 = Mathf.Max(uv[num].y, Mathf.Max(uv[num2].y, uv[num3].y)); float num9 = num6 - num5; float num10 = num8 - num7; if (!(num9 <= 0.005f) && !(num10 <= 0.005f) && !(num9 > 1f) && !(num10 > 1f)) { array2[num4].Add(new Rect(num5, num7, num9, num10)); } } } float num11 = array[0] + array[1] + array[2]; if (num11 <= 0f) { return; } int num12 = 0; for (int j = 1; j < 3; j++) { if (array[j] < array[num12]) { num12 = j; } } Rect[] array3 = (Rect[])(object)new Rect[3] { Cluster(array2[0]), Cluster(array2[1]), Cluster(array2[2]) }; Rect val6 = array3[0]; Rect[] array4 = array3; for (int k = 0; k < array4.Length; k++) { Rect val7 = array4[k]; if (((Rect)(ref val7)).width * ((Rect)(ref val7)).height > ((Rect)(ref val6)).width * ((Rect)(ref val6)).height) { val6 = val7; } } if (((Rect)(ref val6)).width > 0f) { side = val6; } KyndaPlugin.Log.LogInfo((object)$"{((Object)renderer).name}: surface {array[0] / num11 * 100f:0}/{array[1] / num11 * 100f:0}/{array[2] / num11 * 100f:0}% by axis, fields {((Rect)(ref array3[0])).width:0.000}x{((Rect)(ref array3[0])).height:0.000} / {((Rect)(ref array3[1])).width:0.000}x{((Rect)(ref array3[1])).height:0.000} / {((Rect)(ref array3[2])).width:0.000}x{((Rect)(ref array3[2])).height:0.000}."); float num13 = array[num12] / num11; Rect val8 = array3[num12]; if (num13 <= 0.3f && ((Rect)(ref val8)).width > 0f && val8 != side) { cap = val8; return; } cap = side; KyndaPlugin.Log.LogInfo((object)$"{((Object)renderer).name} has no separate end patch - its thinnest axis is {num13 * 100f:0}% of its surface. Sawn ends will use the side grain."); } public static void Remap(Mesh mesh, string[] groups, IDictionary<string, string> overrides) { //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_040a: Unknown result type (might be due to invalid IL or missing references) //IL_040f: 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); float num2 = Mathf.Max(1f, KyndaConfig.TexelsPerMetre.Value); bool[] array = new bool[uv.Length]; Vector2 val = default(Vector2); Vector2 val2 = default(Vector2); for (int i = 0; i < num; i++) { string text = Key(groups[i], overrides); if (!Atlas.TryGetValue(text, out var value) || !TexPx.TryGetValue(text, out var value2) || value2 <= 0) { continue; } int[] triangles = mesh.GetTriangles(i); if (triangles.Length == 0) { continue; } if (IsEnd(groups[i])) { int[] array2 = triangles; foreach (int num3 in array2) { if (num3 >= 0 && num3 < uv.Length && !array[num3]) { array[num3] = true; float num4 = Mathf.Clamp01(uv[num3].y); if (FlipV.Contains(text)) { num4 = 1f - num4; } uv[num3] = new Vector2(((Rect)(ref value)).x + Mathf.Clamp01(uv[num3].x) * ((Rect)(ref value)).width, ((Rect)(ref value)).y + num4 * ((Rect)(ref value)).height); } } KyndaPlugin.Log.LogInfo((object)$"'{text}' aimed at the end-grain patch at {((Rect)(ref value)).x:0.000},{((Rect)(ref value)).y:0.000} {((Rect)(ref value)).width:0.000}x{((Rect)(ref value)).height:0.000} ({Mathf.RoundToInt(((Rect)(ref value)).width * (float)value2)} texels across)."); continue; } List<List<int>> list = Islands(triangles, uv); float num5 = float.MaxValue; float num6 = 0f; foreach (List<int> item in list) { ((Vector2)(ref val))..ctor(float.MaxValue, float.MaxValue); ((Vector2)(ref val2))..ctor(float.MinValue, float.MinValue); foreach (int item2 in item) { val = Vector2.Min(val, uv[item2]); val2 = Vector2.Max(val2, uv[item2]); } Vector2 val3 = val2 - val; if (val3.x <= 0f || val3.y <= 0f) { continue; } float num7 = num2 / (float)value2; num7 = Mathf.Min(num7, ((Rect)(ref value)).width / val3.x); num7 = Mathf.Min(num7, ((Rect)(ref value)).height / val3.y); num5 = Mathf.Min(num5, num7 * (float)value2); num6 = Mathf.Max(num6, num7 * (float)value2); Vector2 val4 = (val + val2) * 0.5f; Vector2 val5 = new Vector2(((Rect)(ref value)).x + ((Rect)(ref value)).width * 0.5f, ((Rect)(ref value)).y + ((Rect)(ref value)).height * 0.5f) - val4 * num7; foreach (int item3 in item) { if (!array[item3]) { array[item3] = true; uv[item3] = new Vector2(Mathf.Clamp(uv[item3].x * num7 + val5.x, ((Rect)(ref value)).xMin, ((Rect)(ref value)).xMax), FlipV.Contains(text) ? (((Rect)(ref value)).yMax - (Mathf.Clamp(uv[item3].y * num7 + val5.y, ((Rect)(ref value)).yMin, ((Rect)(ref value)).yMax) - ((Rect)(ref value)).yMin)) : Mathf.Clamp(uv[item3].y * num7 + val5.y, ((Rect)(ref value)).yMin, ((Rect)(ref value)).yMax)); } } } if (!(num6 <= 0f)) { KyndaPlugin.Log.LogInfo((object)$"'{text}' laid out at {num5:0}-{num6:0} texels/m (wanted {num2:0}) across {list.Count} parts, in a {((Rect)(ref value)).width:0.000}x{((Rect)(ref value)).height:0.000} rect."); } } mesh.uv = uv; } } internal class SmelterCapacity : MonoBehaviour { private static readonly List<SmelterCapacity> All = new List<SmelterCapacity>(); private Smelter _smelter; private int _baseOre; private int _baseFuel; private bool Fuelled => _baseFuel > 0; public Vector3 ConnectionPoint { get { //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_002f: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Collider componentInChildren = ((Component)this).GetComponentInChildren<Collider>(); if ((Object)(object)componentInChildren != (Object)null) { Bounds bounds = componentInChildren.bounds; return ((Bounds)(ref bounds)).center; } return ((Component)this).transform.position + Vector3.up; } } private void Awake() { _smelter = ((Component)this).GetComponent<Smelter>(); if ((Object)(object)_smelter == (Object)null) { ((Behaviour)this).enabled = false; return; } _baseOre = _smelter.m_maxOre; _baseFuel = _smelter.m_maxFuel; All.Add(this); ((MonoBehaviour)this).InvokeRepeating("Recompute", 1f, 3f); } private void OnDestroy() { All.Remove(this); } private bool Serves(UpgradeDef def) { if (def == null || def.Stations == null) { return false; } string prefabName = Utils.GetPrefabName(((Component)this).gameObject); string[] array = (def.Stations.Value ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { if (string.Equals(array[i].Trim(), prefabName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private void Recompute() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_smelter == (Object)null)) { UpgradeDef upgradeDef = UpgradePrefabs.For(Fuelled); int num = ((KyndaConfig.Enabled.Value && Serves(upgradeDef)) ? Mathf.Min(UpgradeBin.CountNear(((Component)this).transform.position, Fuelled), Mathf.Max(0, KyndaConfig.MaxPerStation.Value)) : 0); int num2 = num * Mathf.Max(0, upgradeDef.OreCapacity.Value); int num3 = ((upgradeDef.FuelCapacity != null) ? (num * Mathf.Max(0, upgradeDef.FuelCapacity.Value)) : 0); if (_baseOre > 0) { _smelter.m_maxOre = _baseOre + num2; } if (_baseFuel > 0) { _smelter.m_maxFuel = _baseFuel + num3; } } } public static SmelterCapacity Nearest(Vector3 point, bool fuelled) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) float value = KyndaConfig.Range.Value; SmelterCapacity result = null; float num = float.MaxValue; foreach (SmelterCapacity item in All) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item._smelter == (Object)null) && item.Fuelled == fuelled && item.Serves(UpgradePrefabs.For(fuelled))) { float num2 = Vector3.Distance(((Component)item).transform.position, point); if (!(num2 > value) && !(num2 >= num)) { result = item; num = num2; } } } return result; } public static string NearestUsing(Vector3 point, bool fuelled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) SmelterCapacity smelterCapacity = Nearest(point, fuelled); if ((Object)(object)smelterCapacity == (Object)null) { return null; } Smelter smelter = smelterCapacity._smelter; List<string> list = new List<string>(); if (smelter.m_maxOre > 0) { list.Add("ore " + smelter.m_maxOre); } if (smelter.m_maxFuel > 0) { list.Add("fuel " + smelter.m_maxFuel); } return smelter.m_name + " (" + string.Join(", ", list.ToArray()) + ")"; } public static int AttachToPrefabs() { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return 0; } int num = 0; foreach (GameObject prefab in instance.m_prefabs) { if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab.GetComponent<Smelter>() == (Object)null) && !((Object)(object)prefab.GetComponent<SmelterCapacity>() != (Object)null)) { prefab.AddComponent<SmelterCapacity>(); num++; } } return num; } } internal static class SoftAssets { private static Dictionary<string, AssetID> _paths; private static bool _pathsFailed; private static readonly Dictionary<string, Material> _loaded = new Dictionary<string, Material>(StringComparer.OrdinalIgnoreCase); public static void MakeEverythingLoadable() { try { Runtime.MakeAllAssetsLoadable(); } catch (Exception ex) { KyndaPlugin.Log.LogWarning((object)("Could not enable the extended asset manifest: " + ex.Message + ". Location-only materials will fall back to summoning their carrier.")); } } public unsafe static Material LoadMaterial(string name) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be