using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("SmelterUpgrades")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.1.1.0")] [assembly: AssemblyInformationalVersion("1.1.1")] [assembly: AssemblyProduct("SmelterUpgrades")] [assembly: AssemblyTitle("SmelterUpgrades")] [assembly: AssemblyVersion("1.1.1.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 SmelterUpgrades { internal class HotkeyWatcher : MonoBehaviour { public const string ButtonName = "smx_cycle_lock"; private static ButtonConfig s_button; public static void RegisterButton() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown s_button = new ButtonConfig { Name = "smx_cycle_lock", Config = ModConfig.LockKey, HintToken = "$smx_hint_lock", ActiveInGUI = false, ActiveInCustomGUI = false }; InputManager.Instance.AddButton("zl.smelterupgrades", s_button); if (s_button.Name == "smx_cycle_lock") { Logger.LogWarning((object)"Lock key was not registered with Jotunn; the ore-lock hotkey will not respond."); s_button = null; } } private void Update() { if (s_button == null) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ZInput.instance == null || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || InventoryGui.IsVisible() || Menu.IsVisible() || Minimap.IsOpen() || !ZInput.GetButtonDown(s_button.Name)) { return; } GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); if (!((Object)(object)hoverObject == (Object)null)) { SmelterStation componentInParent = hoverObject.GetComponentInParent(); if (!((Object)(object)componentInParent == (Object)null)) { componentInParent.CycleLock(); } } } } internal class UpgradeDef { public string Key; public string PrefabName; public string CloneFrom; public string DisplayName; public string Description; public string TargetPrefab; public string CraftedAt; public string DefModelPrefab; public (string item, int amount)[] Requirements; public float DefSpeed; public int DefOreCap; public int DefFuelCap; public float DefFuelEff; public ConfigEntry Speed; public ConfigEntry OreCap; public ConfigEntry FuelCap; public ConfigEntry FuelEff; public ConfigEntry Range; public ConfigEntry ModelPrefab; } internal static class ModConfig { public static ConfigEntry UpgradeStations; public static ConfigEntry CoreFuelStations; public static ConfigEntry CoresEnabled; public static ConfigEntry CoreItem; public static ConfigEntry CoreFuelValue; public static ConfigEntry CoreSpeedMultiplier; public static ConfigEntry FeederEnabled; public static ConfigEntry FeederRange; public static ConfigEntry FeederInterval; public static ConfigEntry FeederFeedsFuel; public static ConfigEntry FeederFeedsOre; public static ConfigEntry DebugBaseStats; public static ConfigEntry ConfigVersion; private const int CurrentConfigVersion = 2; public static ConfigEntry UseVanillaShader; public static ConfigEntry FeederModelPrefab; public static ConfigEntry LockKey; private static readonly Dictionary ByKey = new Dictionary(); private static readonly UpgradeDef[] Defs = new UpgradeDef[4] { new UpgradeDef { Key = "kiln_vents", PrefabName = "smx_kiln_vents", CloneFrom = "forge_ext2", DisplayName = "Kiln Vents", Description = "Feeds air to a charcoal kiln, burning it down faster.", TargetPrefab = "charcoal_kiln", CraftedAt = "piece_workbench", Requirements = new(string, int)[2] { ("Bronze", 5), ("Copper", 10) }, DefModelPrefab = "forge_ext1", DefSpeed = 0.25f, DefOreCap = 15 }, new UpgradeDef { Key = "wood_rack", PrefabName = "smx_wood_rack", CloneFrom = "piece_workbench_ext1", DisplayName = "Wood Rack", Description = "Stacked wood within arm's reach of the kiln, so it can hold more.", TargetPrefab = "charcoal_kiln", CraftedAt = "piece_workbench", Requirements = new(string, int)[2] { ("FineWood", 15), ("Iron", 5) }, DefModelPrefab = "wood_stack", DefOreCap = 15 }, new UpgradeDef { Key = "bellows", PrefabName = "smx_bellows", CloneFrom = "forge_ext2", DisplayName = "Smelter Bellows", Description = "A hotter, cleaner burn: the smelter works faster on less coal.", TargetPrefab = "smelter", CraftedAt = "forge", Requirements = new(string, int)[3] { ("Iron", 10), ("TrollHide", 5), ("Chain", 2) }, DefModelPrefab = "forge_ext2", DefSpeed = 0.25f, DefFuelEff = 0.25f, DefOreCap = 6 }, new UpgradeDef { Key = "ore_hopper", PrefabName = "smx_ore_hopper", CloneFrom = "forge_ext1", DisplayName = "Ore Hopper", Description = "Holds a deeper stack of ore and a bigger bed of coal.", TargetPrefab = "smelter", CraftedAt = "forge", Requirements = new(string, int)[2] { ("Silver", 10), ("Obsidian", 10) }, DefModelPrefab = "piece_chest_barrel", DefOreCap = 10, DefFuelCap = 20 } }; public static IEnumerable All => ByKey.Values; public static void Init(ConfigFile cfg) { UpgradeStations = cfg.Bind("General", "UpgradeStations", "charcoal_kiln,smelter", "Prefab names of the stations that accept upgrade pieces. Comma separated."); CoreFuelStations = cfg.Bind("SurtlingCores", "Stations", "smelter,blastfurnace", "Prefab names of the stations that accept Surtling Cores as fuel. Comma separated. Stations with no fuel at all, such as the charcoal kiln, ignore this."); CoresEnabled = cfg.Bind("SurtlingCores", "Enabled", true, "Allow Surtling Cores to be burned as a high-grade fuel."); CoreItem = cfg.Bind("SurtlingCores", "ItemPrefab", "SurtlingCore", "Prefab name of the item used as super-fuel."); CoreFuelValue = cfg.Bind("SurtlingCores", "FuelValue", 4f, "How many units of ordinary fuel one core is worth."); CoreSpeedMultiplier = cfg.Bind("SurtlingCores", "SpeedMultiplier", 2f, "Speed multiplier while core fuel remains in the tank. Stacks on top of upgrade pieces. NOTE: fuel is a single pool, so once cores and coal are mixed the boost runs until the core fuel is spent rather than tracking individual cores."); FeederEnabled = cfg.Bind("FeederChest", "Enabled", true, "Let the Stoker's Chest top up nearby stations on its own."); FeederRange = cfg.Bind("FeederChest", "Range", 8f, "How far the chest reaches to find stations to feed."); FeederInterval = cfg.Bind("FeederChest", "IntervalSeconds", 3f, "Seconds between feeding passes. One fuel and one ore per station per pass."); FeederFeedsFuel = cfg.Bind("FeederChest", "FeedFuel", true, "Top up station fuel from the chest."); FeederFeedsOre = cfg.Bind("FeederChest", "FeedOre", true, "Top up the station input queue from the chest. Respects a locked ore type."); LockKey = cfg.Bind("General", "LockOreKey", (KeyCode)103, "Held on a smelter, steps its ore lock: unlocked -> each ore in turn -> unlocked. Also rebindable from the game's own controls menu."); FeederModelPrefab = cfg.Bind("FeederChest", "ModelPrefab", "piece_chest_blackmetal", "Vanilla prefab whose geometry the feeder chest wears."); UseVanillaShader = cfg.Bind("General", "UseVanillaShader", true, "Draw the mod's models with Valheim's own shader so they weather, wet and catch snow like the rest of the world. Turn off to use the plain shader shipped in the model bundle."); DebugBaseStats = cfg.Bind("General", "DebugLogBaseStats", false, "Log each station's untouched vanilla stats on spawn. Use this to tune the values below against the real numbers instead of guessing them."); UpgradeDef[] defs = Defs; foreach (UpgradeDef upgradeDef in defs) { upgradeDef.Speed = cfg.Bind(upgradeDef.Key, "SpeedBonus", upgradeDef.DefSpeed, "Added as a fraction of base speed. 0.25 = 25% faster. 0 disables."); upgradeDef.OreCap = cfg.Bind(upgradeDef.Key, "OreCapacityBonus", upgradeDef.DefOreCap, "Extra slots on the input queue."); upgradeDef.FuelCap = cfg.Bind(upgradeDef.Key, "FuelCapacityBonus", upgradeDef.DefFuelCap, "Extra fuel capacity. Ignored on stations with no fuel, such as the kiln."); upgradeDef.FuelEff = cfg.Bind(upgradeDef.Key, "FuelEfficiencyBonus", upgradeDef.DefFuelEff, "Fraction of fuel saved per product. NOTE: the game stores fuel-per-product as a whole number, so this rounds -- small values may have no effect at all. It can never drop below 1 fuel per product."); upgradeDef.Range = cfg.Bind(upgradeDef.Key, "Range", 5f, "How close to the station this piece must be built."); upgradeDef.ModelPrefab = cfg.Bind(upgradeDef.Key, "ModelPrefab", upgradeDef.DefModelPrefab, "Vanilla prefab whose geometry this piece wears. Using real game art is what makes these match the world. Any buildable prefab name works -- try wood_stack, coal_pile, piece_chest_barrel, forge_ext1..6, blackforge_ext1..4. Leave empty to keep the piece's own model."); ByKey[upgradeDef.Key] = upgradeDef; } Migrate(cfg); } private static void MigrateDefault(ConfigEntry entry, T previousDefault) { if (entry != null && object.Equals(entry.Value, previousDefault)) { entry.Value = (T)((ConfigEntryBase)entry).DefaultValue; } } private static void Migrate(ConfigFile cfg) { ConfigVersion = cfg.Bind("General", "ConfigVersion", 1, "Internal bookkeeping so balance updates reach existing configs. Do not edit."); if (ConfigVersion.Value < 2) { if (ConfigVersion.Value < 2) { MigrateDefault(Get("kiln_vents")?.OreCap, 0); MigrateDefault(Get("wood_rack")?.OreCap, 10); MigrateDefault(Get("bellows")?.OreCap, 0); MigrateDefault(Get("ore_hopper")?.OreCap, 5); MigrateDefault(Get("ore_hopper")?.FuelCap, 10); } Logger.LogInfo((object)($"Config migrated from version {ConfigVersion.Value} to {2}; " + "values you had customised were kept.")); ConfigVersion.Value = 2; } } public static UpgradeDef Get(string key) { if (key == null) { return null; } ByKey.TryGetValue(key, out var value); return value; } private static bool InList(ConfigEntry list, string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return false; } return (from s in list.Value.Split(new char[1] { ',' }) select s.Trim()).Any((string s) => s.Length > 0 && s == prefabName); } public static bool IsUpgradeStation(string prefabName) { return InList(UpgradeStations, prefabName); } public static bool IsCoreFuelStation(string prefabName) { return CoresEnabled.Value && InList(CoreFuelStations, prefabName); } public static bool IsManagedStation(string prefabName) { return IsUpgradeStation(prefabName) || IsCoreFuelStation(prefabName); } } internal static class ModLocalization { public static void Register() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "smx_level"; localization.AddTranslation(ref text, "Level"); text = "smx_speed"; localization.AddTranslation(ref text, "speed"); text = "smx_capacity"; localization.AddTranslation(ref text, "capacity"); text = "smx_fuel"; localization.AddTranslation(ref text, "fuel"); text = "smx_fuel_per_item"; localization.AddTranslation(ref text, "fuel per item"); text = "smx_core_boost"; localization.AddTranslation(ref text, "Core fire"); text = "smx_locked_to"; localization.AddTranslation(ref text, "Locked to"); text = "smx_unlocked"; localization.AddTranslation(ref text, "Unlocked - accepts any ore"); text = "smx_hint_lock"; localization.AddTranslation(ref text, "Lock ore type"); text = "smx_feeder_name"; localization.AddTranslation(ref text, "Stoker's Chest"); text = "smx_feeder_desc"; localization.AddTranslation(ref text, "Feeds fuel and ore to nearby smelters and kilns on its own."); text = "smx_feeder_range"; localization.AddTranslation(ref text, "Feeding"); text = "smx_feeder_idle"; localization.AddTranslation(ref text, "Idle"); } } [HarmonyPatch(typeof(Smelter), "Awake")] internal static class Smelter_Awake_Patch { private static void Postfix(Smelter __instance) { ZNetView val = ((Component)__instance).GetComponent() ?? ((Component)__instance).GetComponentInParent(); if (!((Object)(object)val == (Object)null) && val.GetZDO() != null) { string prefabName = Utils.GetPrefabName(((Component)val).gameObject); if (ModConfig.IsManagedStation(prefabName) && !((Object)(object)((Component)__instance).GetComponent() != (Object)null)) { ((Component)__instance).gameObject.AddComponent(); } } } } [HarmonyPatch(typeof(Smelter), "OnHoverAddOre")] internal static class Smelter_OnHoverAddOre_Patch { private static void Postfix(Smelter __instance, ref string __result) { SmelterStation component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null)) { component.PokeConnections(); __result += Localization.instance.Localize(component.GetUpgradeHoverText()); } } } [HarmonyPatch(typeof(Smelter), "OnAddOre")] internal static class Smelter_OnAddOre_Patch { private static bool Prefix(Smelter __instance, Humanoid user, ref ItemData item, ref bool __result) { SmelterStation component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsLocked) { return true; } string lockedOre = component.LockedOre; ItemConversion val = component.FindConversion(lockedOre); if (val == null) { return true; } string name = val.m_from.m_itemData.m_shared.m_name; if (item != null) { if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == lockedOre) { return true; } ((Character)user).Message((MessageType)2, Localization.instance.Localize("$smx_locked_to " + name), 0, (Sprite)null); __result = false; return false; } ItemData item2 = user.GetInventory().GetItem(name, -1, false); if (item2 == null) { ((Character)user).Message((MessageType)2, Localization.instance.Localize("$msg_donthaveany " + name), 0, (Sprite)null); __result = false; return false; } item = item2; return true; } } [HarmonyPatch(typeof(Smelter), "TryGetItems")] internal static class Smelter_TryGetItems_Patch { private static void Postfix(Smelter __instance, Switch switchRef, ref List items) { SmelterStation component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || !component.IsLocked || (Object)(object)switchRef != (Object)(object)__instance.m_addOreSwitch || items == null) { return; } string lockedShared = component.LockedSharedName; if (!string.IsNullOrEmpty(lockedShared)) { items.RemoveAll((string n) => n != lockedShared); } } } [HarmonyPatch(typeof(Smelter), "OnAddFuel")] internal static class Smelter_OnAddFuel_Patch { private static bool Prefix(Smelter __instance, Humanoid user, ItemData item, ref bool __result) { SmelterStation component = ((Component)__instance).GetComponent(); if ((Object)(object)component == (Object)null || !component.AllowsCores) { return true; } if (item == null || (Object)(object)item.m_dropPrefab == (Object)null) { return true; } if (((Object)item.m_dropPrefab).name != ModConfig.CoreItem.Value) { return true; } float value = ModConfig.CoreFuelValue.Value; if (value <= 0f) { return true; } if (component.CurrentFuel + value > (float)__instance.m_maxFuel + 0.001f) { ((Character)user).Message((MessageType)2, "$msg_itsfull", 0, (Sprite)null); __result = false; return false; } user.GetInventory().RemoveItem(item, 1); ((Character)user).Message((MessageType)2, Localization.instance.Localize("$msg_added " + item.m_shared.m_name), 0, (Sprite)null); component.AddCoreFuel(value); __result = true; return false; } } [HarmonyPatch(typeof(Smelter), "OnHoverAddFuel")] internal static class Smelter_OnHoverAddFuel_Patch { private static void Postfix(Smelter __instance, ref string __result) { SmelterStation component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null) && component.AllowsCores && component.BoostFuel > 0f) { __result += Localization.instance.Localize($"\n$smx_core_boost x{ModConfig.CoreSpeedMultiplier.Value:0.##} " + $"({Mathf.Ceil(component.BoostFuel)} $smx_fuel)"); } } } [HarmonyPatch(typeof(Smelter), "CanUseItems")] internal static class Smelter_CanUseItems_Patch { private static void Postfix(Smelter __instance, Player player, Switch switchRef, bool sendErrorMessage, ref bool __result) { SmelterStation component = ((Component)__instance).GetComponent(); if (!__result && (Object)(object)component != (Object)null && component.AllowsCores && (Object)(object)__instance.m_addWoodSwitch != (Object)null && (Object)(object)switchRef == (Object)(object)__instance.m_addWoodSwitch) { float value = ModConfig.CoreFuelValue.Value; if (component.CurrentFuel + value <= (float)__instance.m_maxFuel + 0.001f && HasCore(player)) { __result = true; return; } } if (!__result) { return; } SmelterStation component2 = ((Component)__instance).GetComponent(); if ((Object)(object)component2 == (Object)null || !component2.IsLocked || (Object)(object)switchRef != (Object)(object)__instance.m_addOreSwitch) { return; } string lockedSharedName = component2.LockedSharedName; if (!string.IsNullOrEmpty(lockedSharedName) && !((Humanoid)player).GetInventory().HaveItem(lockedSharedName, true)) { if (sendErrorMessage) { ((Character)player).Message((MessageType)2, Localization.instance.Localize("$msg_donthaveany " + lockedSharedName), 0, (Sprite)null); } __result = false; } } internal static bool HasCore(Player player) { string text = CoreItemSharedName(); return !string.IsNullOrEmpty(text) && ((Humanoid)player).GetInventory().HaveItem(text, true); } internal static string CoreItemSharedName() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } GameObject itemPrefab = instance.GetItemPrefab(ModConfig.CoreItem.Value); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent() : null); return ((Object)(object)val != (Object)null) ? val.m_itemData.m_shared.m_name : null; } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] internal static class Player_UpdatePlacementGhost_Patch { private static void Postfix(GameObject ___m_placementGhost) { if (!((Object)(object)___m_placementGhost == (Object)null)) { SmelterExtension component = ___m_placementGhost.GetComponent(); if (!((Object)(object)component == (Object)null)) { component.PokeConnection(); } } } } internal static class Pieces { private const string BundleName = "smelterupgrades"; private static AssetBundle s_bundle; private static bool s_bundleChecked; private static Transform s_visualHolder; private static readonly Dictionary s_retargeted = new Dictionary(); private static Shader s_pieceShader; public static void Register() { PrefabManager.OnVanillaPrefabsAvailable += AddPieces; } private static AssetBundle Bundle() { if (s_bundleChecked) { return s_bundle; } s_bundleChecked = true; if (!HasEmbeddedBundle()) { Logger.LogInfo((object)"No model bundle embedded; using vanilla models."); return null; } try { s_bundle = AssetUtils.LoadAssetBundleFromResources("smelterupgrades"); } catch (Exception ex) { Logger.LogError((object)("Model bundle failed to load: " + ex.Message)); s_bundle = null; } if ((Object)(object)s_bundle != (Object)null) { Logger.LogInfo((object)"Loaded custom model bundle."); } return s_bundle; } private static bool HasEmbeddedBundle() { int num = 0; string[] manifestResourceNames = typeof(Pieces).Assembly.GetManifestResourceNames(); foreach (string text in manifestResourceNames) { if (text.EndsWith("smelterupgrades")) { num++; } } if (num > 1) { Logger.LogError((object)(string.Format("{0} embedded resources end with '{1}'; the bundle lookup is ", num, "smelterupgrades") + "ambiguous and will fail. Only one may match.")); return false; } return num == 1; } private static GameObject MakePrefab(string prefabName, string cloneFrom, string modelPrefab, out bool fixReference) { AssetBundle val = Bundle(); if ((Object)(object)val != (Object)null && val.Contains(prefabName)) { GameObject val2 = val.LoadAsset(prefabName); if ((Object)(object)val2 != (Object)null) { fixReference = true; return PrefabManager.Instance.CreateClonedPrefab(prefabName, val2); } } fixReference = false; GameObject val3 = PrefabManager.Instance.CreateClonedPrefab(prefabName, cloneFrom); if ((Object)(object)val3 == (Object)null) { return null; } if ((Object)(object)val != (Object)null) { string text = prefabName + "_model"; if (val.Contains(text)) { GraftModel(val3, val.LoadAsset(text), retargetShader: true, alreadyInstantiated: false); return val3; } } if (!string.IsNullOrEmpty(modelPrefab) && modelPrefab != cloneFrom) { GameObject val4 = BorrowVisual(modelPrefab); if ((Object)(object)val4 != (Object)null) { GraftModel(val3, val4, retargetShader: false, alreadyInstantiated: true); return val3; } } return val3; } private static Transform VisualHolder() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)s_visualHolder != (Object)null) { return s_visualHolder; } GameObject val = new GameObject("smx_visual_holder"); val.SetActive(false); Object.DontDestroyOnLoad((Object)(object)val); s_visualHolder = val.transform; return s_visualHolder; } private static GameObject BorrowVisual(string modelPrefab) { GameObject prefab = PrefabManager.Instance.GetPrefab(modelPrefab); if ((Object)(object)prefab == (Object)null) { Logger.LogWarning((object)("Model prefab '" + modelPrefab + "' not found; keeping the donor's model.")); return null; } GameObject val = Object.Instantiate(prefab, VisualHolder()); LODGroup[] componentsInChildren = val.GetComponentsInChildren(true); foreach (LODGroup val2 in componentsInChildren) { LOD[] lODs = val2.GetLODs(); for (int j = 1; j < lODs.Length; j++) { Renderer[] renderers = lODs[j].renderers; foreach (Renderer val3 in renderers) { if ((Object)(object)val3 != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)val3).gameObject); } } } Object.DestroyImmediate((Object)(object)val2); } MonoBehaviour[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (MonoBehaviour val4 in componentsInChildren2) { if ((Object)(object)val4 != (Object)null) { Object.DestroyImmediate((Object)(object)val4); } } Collider[] componentsInChildren3 = val.GetComponentsInChildren(true); foreach (Collider val5 in componentsInChildren3) { if ((Object)(object)val5 != (Object)null) { Object.DestroyImmediate((Object)(object)val5); } } Rigidbody[] componentsInChildren4 = val.GetComponentsInChildren(true); foreach (Rigidbody val6 in componentsInChildren4) { if ((Object)(object)val6 != (Object)null) { Object.DestroyImmediate((Object)(object)val6); } } return val; } private static Shader PieceShader(GameObject donor) { if ((Object)(object)s_pieceShader != (Object)null) { return s_pieceShader; } s_pieceShader = Shader.Find("Custom/Piece"); if ((Object)(object)s_pieceShader != (Object)null) { return s_pieceShader; } Renderer[] componentsInChildren = donor.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if (!(val is MeshRenderer) && !(val is SkinnedMeshRenderer)) { continue; } Material sharedMaterial = val.sharedMaterial; if (!((Object)(object)sharedMaterial == (Object)null) && !((Object)(object)sharedMaterial.shader == (Object)null)) { string name = ((Object)sharedMaterial.shader).name; if (!name.Contains("Creature") && !name.Contains("Character") && !(name == "Standard")) { s_pieceShader = sharedMaterial.shader; return s_pieceShader; } } } Logger.LogWarning((object)"Could not resolve Valheim's piece shader; models keep the bundled one."); return null; } private static void GraftModel(GameObject prefab, GameObject model, bool retargetShader, bool alreadyInstantiated) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)model == (Object)null) { return; } Shader vanillaShader = PieceShader(prefab); LODGroup[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (LODGroup val in componentsInChildren) { Object.DestroyImmediate((Object)(object)val); } Renderer[] componentsInChildren2 = prefab.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren2) { if (val2 is MeshRenderer || val2 is SkinnedMeshRenderer) { val2.enabled = false; } } GameObject val3 = (alreadyInstantiated ? model : Object.Instantiate(model)); val3.transform.SetParent(prefab.transform, false); ((Object)val3).name = "smx_model"; val3.transform.localPosition = Vector3.zero; val3.transform.localRotation = Quaternion.identity; PrefabExtension.FixReferences(val3); if (retargetShader) { WarnOnUnresolvedMaterials(val3, ((Object)prefab).name); } if (retargetShader && ModConfig.UseVanillaShader.Value) { RetargetShader(val3, vanillaShader, ((Object)prefab).name); } } private static void RetargetShader(GameObject visual, Shader vanillaShader, string pieceName) { //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_0198: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vanillaShader == (Object)null) { Logger.LogWarning((object)("'" + pieceName + "': no vanilla shader to borrow; keeping the bundled one.")); return; } Renderer[] componentsInChildren = visual.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { Material[] sharedMaterials = val.sharedMaterials; Material[] array = (Material[])(object)new Material[sharedMaterials.Length]; for (int j = 0; j < sharedMaterials.Length; j++) { Material val2 = sharedMaterials[j]; if ((Object)(object)val2 == (Object)null) { array[j] = null; continue; } if ((Object)(object)val2.shader == (Object)(object)vanillaShader) { array[j] = val2; continue; } string key = ((Object)val2).name + "|" + ((Object)vanillaShader).name; if (!s_retargeted.TryGetValue(key, out var value)) { if ((Object)(object)val2.mainTexture != (Object)null && !CanCarryTexture(vanillaShader)) { Logger.LogWarning((object)("'" + pieceName + "': vanilla shader '" + ((Object)vanillaShader).name + "' has no _MainTex; keeping the bundled shader so the texture is not lost.")); s_retargeted[key] = val2; array[j] = val2; continue; } value = new Material(vanillaShader) { name = ((Object)val2).name + "_vanilla" }; if (value.HasProperty("_MainTex")) { value.mainTexture = val2.mainTexture; } if (value.HasProperty("_Color")) { value.color = val2.color; } s_retargeted[key] = value; Logger.LogInfo((object)("Retargeted '" + ((Object)val2).name + "' onto vanilla shader '" + ((Object)vanillaShader).name + "'.")); } array[j] = value; } val.sharedMaterials = array; } } private static bool CanCarryTexture(Shader shader) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown Material val = new Material(shader); bool result = val.HasProperty("_MainTex"); Object.DestroyImmediate((Object)(object)val); return result; } private static void RenderIcon(GameObject prefab) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown Piece component = prefab.GetComponent(); if (!((Object)(object)component == (Object)null)) { RenderRequest val = new RenderRequest(prefab) { Rotation = RenderManager.IsometricRotation, UseCache = true, Width = 128, Height = 128 }; Sprite val2 = RenderManager.Instance.Render(val); if ((Object)(object)val2 != (Object)null) { component.m_icon = val2; } else { Logger.LogWarning((object)("Icon render returned nothing for '" + ((Object)prefab).name + "'.")); } } } private static void WarnOnUnresolvedMaterials(GameObject visual, string pieceName) { Renderer[] componentsInChildren = visual.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { Material[] sharedMaterials = val.sharedMaterials; foreach (Material val2 in sharedMaterials) { if ((Object)(object)val2 == (Object)null) { Logger.LogWarning((object)("'" + pieceName + "': a renderer has no material.")); return; } if (((Object)val2).name.StartsWith("JVLmock_")) { Logger.LogWarning((object)("'" + pieceName + "': mock material '" + ((Object)val2).name + "' did not resolve. The vanilla material it names is probably not loaded; ship your own texture instead of mocking.")); return; } } } } private static void AddPieces() { PrefabManager.OnVanillaPrefabsAvailable -= AddPieces; foreach (UpgradeDef item in ModConfig.All) { try { AddPiece(item); } catch (Exception ex) { Logger.LogError((object)("Failed to register upgrade piece '" + item.Key + "': " + ex.Message)); } } try { AddFeederChest(); } catch (Exception ex2) { Logger.LogError((object)("Failed to register the feeder chest: " + ex2.Message)); } } private static void AddFeederChest() { //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected O, but got Unknown bool fixReference; GameObject val = MakePrefab("smx_feeder_chest", "piece_chest_wood", ModConfig.FeederModelPrefab.Value, out fixReference); if ((Object)(object)val == (Object)null) { Logger.LogError((object)"Could not build a prefab for the feeder chest."); return; } Container component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.m_name = "$smx_feeder_name"; } val.AddComponent(); PieceConfig val2 = new PieceConfig { Name = "$smx_feeder_name", Description = "$smx_feeder_desc", PieceTable = "Hammer", Category = "Crafting", CraftingStation = "forge" }; val2.AddRequirement(new RequirementConfig("BlackMetal", 10, 0, true)); val2.AddRequirement(new RequirementConfig("LinenThread", 10, 0, true)); val2.AddRequirement(new RequirementConfig("Iron", 5, 0, true)); PieceManager.Instance.AddPiece(new CustomPiece(val, fixReference, val2)); RenderIcon(val); } private static void AddPiece(UpgradeDef def) { //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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown bool fixReference; GameObject val = MakePrefab(def.PrefabName, def.CloneFrom, def.ModelPrefab.Value, out fixReference); if ((Object)(object)val == (Object)null) { Logger.LogError((object)("Could not build a prefab for '" + def.Key + "'.")); return; } StationExtension component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } SmelterExtension smelterExtension = val.AddComponent(); smelterExtension.UpgradeKey = def.Key; PieceConfig val2 = new PieceConfig { Name = def.DisplayName, Description = def.Description, PieceTable = "Hammer", Category = "Crafting", CraftingStation = def.CraftedAt }; (string, int)[] requirements = def.Requirements; for (int i = 0; i < requirements.Length; i++) { var (text, num) = requirements[i]; val2.AddRequirement(new RequirementConfig(text, num, 0, true)); } PieceManager.Instance.AddPiece(new CustomPiece(val, fixReference, val2)); RenderIcon(val); } } [BepInPlugin("zl.smelterupgrades", "Smelter Upgrades", "1.1.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "zl.smelterupgrades"; public const string PluginName = "Smelter Upgrades"; public const string PluginVersion = "1.1.1"; internal static Plugin Instance; private Harmony _harmony; private void Awake() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown Instance = this; ModConfig.Init(((BaseUnityPlugin)this).Config); ModLocalization.Register(); Pieces.Register(); HotkeyWatcher.RegisterButton(); QueueIconPanel.Register(); ((Component)this).gameObject.AddComponent(); _harmony = new Harmony("zl.smelterupgrades"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Smelter Upgrades 1.1.1 loaded"); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal class QueueIconPanel : MonoBehaviour { private class Slot { public GameObject Go; public RectTransform Rect; public Image Icon; public Text Count; } private const int MaxGroups = 12; private const float IconSize = 34f; private const float IconGap = 4f; private const float VerticalOffset = -120f; private static QueueIconPanel s_instance; private RectTransform m_content; private readonly List m_slots = new List(); private readonly List> m_queue = new List>(); private static readonly Dictionary s_iconCache = new Dictionary(); public static void Register() { GUIManager.OnCustomGUIAvailable += Create; } private static void Create() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)s_instance)) { GameObject val = new GameObject("smx_queue_panel", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(GUIManager.CustomGUIFront.transform, false); s_instance = val.AddComponent(); s_instance.Build(); } } private void Build() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) RectTransform component = ((Component)this).GetComponent(); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); component.anchorMax = val; component.anchorMin = val; component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = new Vector2(0f, -120f); component.sizeDelta = new Vector2(456f, 34f); GameObject val2 = new GameObject("content", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent((Transform)(object)component, false); m_content = val2.GetComponent(); m_content.anchorMin = Vector2.zero; m_content.anchorMax = Vector2.one; m_content.offsetMin = Vector2.zero; m_content.offsetMax = Vector2.zero; ((Component)m_content).gameObject.SetActive(false); } private Slot GetSlot(int index) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0074: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_010f: 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) //IL_0129: 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_0162: Unknown result type (might be due to invalid IL or missing references) Vector2 val2 = default(Vector2); while (m_slots.Count <= index) { GameObject val = new GameObject($"slot{m_slots.Count}", new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent((Transform)(object)m_content, false); RectTransform component = val.GetComponent(); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component.anchorMax = val2; component.anchorMin = val2; component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(34f, 34f); Image component2 = val.GetComponent(); component2.preserveAspect = true; ((Graphic)component2).raycastTarget = false; GameObject val3 = new GameObject("count", new Type[2] { typeof(RectTransform), typeof(Text) }); val3.transform.SetParent(val.transform, false); RectTransform component3 = val3.GetComponent(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = Vector2.zero; component3.offsetMax = Vector2.zero; Text component4 = val3.GetComponent(); component4.alignment = (TextAnchor)8; ((Graphic)component4).raycastTarget = false; GUIManager.Instance.ApplyTextStyle(component4, Color.white, 14, true); m_slots.Add(new Slot { Go = val, Rect = component, Icon = component2, Count = component4 }); } return m_slots[index]; } private static Sprite IconFor(string prefabName) { if (s_iconCache.TryGetValue(prefabName, out var value)) { return value; } Sprite val = null; ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return null; } GameObject itemPrefab = instance.GetItemPrefab(prefabName); ItemDrop val2 = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent() : null); if ((Object)(object)val2 != (Object)null && val2.m_itemData.m_shared.m_icons.Length != 0) { val = val2.m_itemData.m_shared.m_icons[0]; } s_iconCache[prefabName] = val; return val; } private void Update() { SmelterStation smelterStation = HoveredStation(); if ((Object)(object)smelterStation == (Object)null) { Hide(); return; } smelterStation.GetGroupedQueue(m_queue); if (m_queue.Count == 0) { Hide(); return; } ((Component)m_content).gameObject.SetActive(true); Layout(); } private void Hide() { if ((Object)(object)m_content != (Object)null && ((Component)m_content).gameObject.activeSelf) { ((Component)m_content).gameObject.SetActive(false); } } private void Layout() { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min(m_queue.Count, 12); float num2 = (float)num * 34f + (float)Mathf.Max(0, num - 1) * 4f; float num3 = (0f - num2) * 0.5f + 17f; for (int i = 0; i < num; i++) { Slot slot = GetSlot(i); KeyValuePair keyValuePair = m_queue[i]; Sprite val = IconFor(keyValuePair.Key); slot.Icon.sprite = val; ((Behaviour)slot.Icon).enabled = (Object)(object)val != (Object)null; slot.Count.text = ((keyValuePair.Value > 1) ? ("x" + keyValuePair.Value) : string.Empty); slot.Rect.anchoredPosition = new Vector2(num3, 0f); num3 += 38f; slot.Go.SetActive(true); } if (m_queue.Count > 12 && num > 0) { GetSlot(num - 1).Count.text = "+" + (m_queue.Count - 12 + 1); } for (int j = num; j < m_slots.Count; j++) { m_slots[j].Go.SetActive(false); } } private static SmelterStation HoveredStation() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return null; } if (InventoryGui.IsVisible() || Menu.IsVisible()) { return null; } GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); return ((Object)(object)hoverObject != (Object)null) ? hoverObject.GetComponentInParent() : null; } } public class SmelterExtension : MonoBehaviour, Hoverable { public string UpgradeKey; private static readonly List s_all = new List(); private static GameObject s_connectionPrefab; private static bool s_connectionPrefabSearched; private Piece m_piece; private GameObject m_connection; private string PieceName => ((Object)(object)m_piece != (Object)null) ? m_piece.m_name : ((Object)this).name; private void Awake() { m_piece = ((Component)this).GetComponent(); ZNetView component = ((Component)this).GetComponent(); if (!((Object)(object)component == (Object)null) && component.GetZDO() != null) { s_all.Add(this); } } private void OnDestroy() { StopConnectionEffect(); s_all.Remove(this); } public string GetHoverName() { return Localization.instance.Localize(PieceName); } public string GetHoverText() { PokeConnection(); return Localization.instance.Localize(PieceName); } public void PokeConnection(float timeout = 1f) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) UpgradeDef upgradeDef = ModConfig.Get(UpgradeKey); if (upgradeDef != null) { SmelterStation smelterStation = SmelterStation.FindClosest(upgradeDef.TargetPrefab, ((Component)this).transform.position, upgradeDef.Range.Value); if ((Object)(object)smelterStation != (Object)null) { StartConnectionEffect(smelterStation.StationPosition, timeout); } else { StopConnectionEffect(); } } } public static void FindFor(string targetPrefab, Vector3 stationPos, List into) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) foreach (SmelterExtension item in s_all) { if (!((Object)(object)item == (Object)null)) { UpgradeDef upgradeDef = ModConfig.Get(item.UpgradeKey); if (upgradeDef != null && !(upgradeDef.TargetPrefab != targetPrefab) && !(Vector3.Distance(((Component)item).transform.position, stationPos) >= upgradeDef.Range.Value) && !AlreadyHasType(into, item)) { into.Add(item); } } } } private static bool AlreadyHasType(List chosen, SmelterExtension candidate) { foreach (SmelterExtension item in chosen) { if (item.PieceName == candidate.PieceName) { return true; } } return false; } private static GameObject ConnectionPrefab() { if (s_connectionPrefabSearched) { return s_connectionPrefab; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return null; } s_connectionPrefabSearched = true; GameObject prefab = instance.GetPrefab("forge_ext1"); StationExtension val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent() : null); if ((Object)(object)val != (Object)null) { s_connectionPrefab = val.m_connectionPrefab; } if ((Object)(object)s_connectionPrefab == (Object)null) { Logger.LogWarning((object)"Could not borrow the station connection VFX; upgrade beams disabled."); } return s_connectionPrefab; } public void StartConnectionEffect(Vector3 targetPos, float timeout = 1f) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) GameObject val = ConnectionPrefab(); if ((Object)(object)val == (Object)null) { return; } Vector3 position = ((Component)this).transform.position; Vector3 val2 = targetPos - position; if (!(((Vector3)(ref val2)).sqrMagnitude < 0.0001f)) { if ((Object)(object)m_connection == (Object)null) { m_connection = Object.Instantiate(val, position, Quaternion.identity); } m_connection.transform.position = position; m_connection.transform.rotation = Quaternion.LookRotation(((Vector3)(ref val2)).normalized); m_connection.transform.localScale = new Vector3(1f, 1f, ((Vector3)(ref val2)).magnitude); ((MonoBehaviour)this).CancelInvoke("StopConnectionEffect"); ((MonoBehaviour)this).Invoke("StopConnectionEffect", timeout); } } public void StopConnectionEffect() { if (!((Object)(object)m_connection == (Object)null)) { Object.Destroy((Object)(object)m_connection); m_connection = null; } } } public class SmelterFeeder : MonoBehaviour { private Container m_container; private ZNetView m_nview; private static readonly List s_pieceBuffer = new List(); private static readonly HashSet s_seen = new HashSet(); private readonly List m_targets = new List(); private void Awake() { m_container = ((Component)this).GetComponent(); m_nview = ((Component)this).GetComponent(); if ((Object)(object)m_container == (Object)null || (Object)(object)m_nview == (Object)null || m_nview.GetZDO() == null) { ((Behaviour)this).enabled = false; } else { ((MonoBehaviour)this).InvokeRepeating("Tick", Random.Range(1f, 2f), ModConfig.FeederInterval.Value); } } private void Tick() { if (!ModConfig.FeederEnabled.Value || !m_nview.IsOwner()) { return; } Inventory inventory = m_container.GetInventory(); if (inventory == null || inventory.NrOfItems() == 0) { return; } CollectNearbySmelters(m_targets); foreach (Smelter target in m_targets) { FeedFuel(target, inventory); FeedOre(target, inventory); } } private void CollectNearbySmelters(List into) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) into.Clear(); s_pieceBuffer.Clear(); s_seen.Clear(); Piece.GetAllPiecesInRadius(((Component)this).transform.position, ModConfig.FeederRange.Value, s_pieceBuffer); foreach (Piece item in s_pieceBuffer) { if (!((Object)(object)item == (Object)null)) { Smelter componentInChildren = ((Component)item).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null && s_seen.Add(componentInChildren)) { into.Add(componentInChildren); } } } } private static ZNetView ViewOf(Smelter smelter) { return ((Component)smelter).GetComponent() ?? ((Component)smelter).GetComponentInParent(); } private void FeedFuel(Smelter smelter, Inventory inventory) { if (!ModConfig.FeederFeedsFuel.Value || smelter.m_maxFuel <= 0 || (Object)(object)smelter.m_fuelItem == (Object)null) { return; } ZNetView val = ViewOf(smelter); if ((Object)(object)val == (Object)null || !val.IsValid()) { return; } float num = val.GetZDO().GetFloat(ZDOVars.s_fuel, 0f); if (!(num + 1f > (float)smelter.m_maxFuel)) { string name = smelter.m_fuelItem.m_itemData.m_shared.m_name; if (inventory.HaveItem(name, true)) { inventory.RemoveItem(name, 1, -1, true); val.InvokeRPC("RPC_AddFuel", Array.Empty()); } } } private void FeedOre(Smelter smelter, Inventory inventory) { if (!ModConfig.FeederFeedsOre.Value || smelter.m_maxOre <= 0 || smelter.m_conversion == null) { return; } ZNetView val = ViewOf(smelter); if ((Object)(object)val == (Object)null || !val.IsValid()) { return; } int num = val.GetZDO().GetInt(ZDOVars.s_queued, 0); if (num >= smelter.m_maxOre) { return; } SmelterStation component = ((Component)smelter).GetComponent(); string text = (((Object)(object)component != (Object)null) ? component.LockedOre : string.Empty); foreach (ItemConversion item in smelter.m_conversion) { if (item == null || (Object)(object)item.m_from == (Object)null) { continue; } string name = ((Object)((Component)item.m_from).gameObject).name; if (string.IsNullOrEmpty(text) || !(name != text)) { string name2 = item.m_from.m_itemData.m_shared.m_name; if (inventory.HaveItem(name2, true)) { inventory.RemoveItem(name2, 1, -1, true); val.InvokeRPC("RPC_AddOre", new object[1] { name }); break; } } } } } public class SmelterStation : MonoBehaviour { private const float RecomputeInterval = 2f; private const string LockKey = "smx_lock"; private const string BoostKey = "smx_boostfuel"; private const string RpcSetLock = "smx_SetLock"; private const string RpcAddCoreFuel = "smx_AddCoreFuel"; private Smelter m_smelter; private ZNetView m_nview; private string m_prefabName; private BaseStats m_base; private static readonly List s_all = new List(); private readonly List m_attached = new List(); private StatDeltas m_deltas; private bool m_allowsUpgrades; private bool m_allowsCores; private float m_lastSeenFuel; private bool m_haveSeenFuel; public int Level => 1 + m_attached.Count; public Smelter Smelter => m_smelter; public Vector3 StationPosition => ((Object)(object)m_nview != (Object)null) ? ((Component)m_nview).transform.position : ((Component)this).transform.position; public string LockedOre { get { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return string.Empty; } return m_nview.GetZDO().GetString("smx_lock", string.Empty); } } public bool IsLocked => !string.IsNullOrEmpty(LockedOre); public string LockedSharedName { get { ItemConversion val = FindConversion(LockedOre); return (val != null) ? val.m_from.m_itemData.m_shared.m_name : string.Empty; } } public bool AllowsCores => m_allowsCores; public float CurrentFuel => ((Object)(object)m_nview != (Object)null && m_nview.IsValid()) ? m_nview.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) : 0f; public float BoostFuel => ((Object)(object)m_nview != (Object)null && m_nview.IsValid()) ? m_nview.GetZDO().GetFloat("smx_boostfuel", 0f) : 0f; public int QueueSize => ((Object)(object)m_nview != (Object)null && m_nview.IsValid()) ? m_nview.GetZDO().GetInt(ZDOVars.s_queued, 0) : 0; private void Awake() { m_smelter = ((Component)this).GetComponent(); m_nview = ((Component)this).GetComponent() ?? ((Component)this).GetComponentInParent(); if ((Object)(object)m_smelter == (Object)null || (Object)(object)m_nview == (Object)null || m_nview.GetZDO() == null) { ((Behaviour)this).enabled = false; return; } m_prefabName = Utils.GetPrefabName(((Component)m_nview).gameObject); m_base = new BaseStats { MaxOre = m_smelter.m_maxOre, MaxFuel = m_smelter.m_maxFuel, FuelPerProduct = m_smelter.m_fuelPerProduct, SecPerProduct = m_smelter.m_secPerProduct }; if (ModConfig.DebugBaseStats.Value) { Logger.LogInfo((object)($"[base stats] {m_prefabName}: maxOre={m_base.MaxOre} maxFuel={m_base.MaxFuel} " + $"fuelPerProduct={m_base.FuelPerProduct} secPerProduct={m_base.SecPerProduct}")); } m_allowsUpgrades = ModConfig.IsUpgradeStation(m_prefabName); m_allowsCores = ModConfig.IsCoreFuelStation(m_prefabName) && m_base.MaxFuel > 0; m_nview.Register("smx_SetLock", (Action)RPC_SetLock); m_nview.Register("smx_AddCoreFuel", (Action)RPC_AddCoreFuel); s_all.Add(this); ((MonoBehaviour)this).InvokeRepeating("Recompute", 0.1f, 2f); } private void OnDestroy() { s_all.Remove(this); } public static SmelterStation FindClosest(string targetPrefab, Vector3 point, float range) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) SmelterStation result = null; float num = float.MaxValue; foreach (SmelterStation item in s_all) { if (!((Object)(object)item == (Object)null) && !(item.m_prefabName != targetPrefab)) { float num2 = Vector3.Distance(item.StationPosition, point); if (!(num2 >= range) && !(num2 >= num)) { result = item; num = num2; } } } return result; } public void Recompute() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) m_attached.Clear(); if (m_allowsUpgrades) { SmelterExtension.FindFor(m_prefabName, StationPosition, m_attached); } m_deltas = default(StatDeltas); foreach (SmelterExtension item in m_attached) { UpgradeDef upgradeDef = ModConfig.Get(item.UpgradeKey); if (upgradeDef != null) { m_deltas.Speed += upgradeDef.Speed.Value; m_deltas.OreCap += upgradeDef.OreCap.Value; m_deltas.FuelCap += upgradeDef.FuelCap.Value; m_deltas.FuelEff += upgradeDef.FuelEff.Value; } } UpdateCoreBoost(); float extraSpeedMul = ((BoostFuel > 0f) ? ModConfig.CoreSpeedMultiplier.Value : 1f); BaseStats baseStats = StatMath.Apply(m_base, m_deltas, extraSpeedMul); m_smelter.m_secPerProduct = baseStats.SecPerProduct; m_smelter.m_maxOre = baseStats.MaxOre; m_smelter.m_maxFuel = baseStats.MaxFuel; m_smelter.m_fuelPerProduct = baseStats.FuelPerProduct; } public ItemConversion FindConversion(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return null; } foreach (ItemConversion item in m_smelter.m_conversion) { if ((Object)(object)item.m_from != (Object)null && ((Object)((Component)item.m_from).gameObject).name == prefabName) { return item; } } return null; } public void CycleLock() { List conversion = m_smelter.m_conversion; if (conversion == null || conversion.Count == 0) { return; } string lockedOre = LockedOre; int num = -1; for (int i = 0; i < conversion.Count; i++) { if ((Object)(object)conversion[i].m_from != (Object)null && ((Object)((Component)conversion[i].m_from).gameObject).name == lockedOre) { num = i; break; } } num++; string text = ((num >= conversion.Count) ? string.Empty : ((Object)((Component)conversion[num].m_from).gameObject).name); m_nview.InvokeRPC("smx_SetLock", new object[1] { text }); Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { if (string.IsNullOrEmpty(text)) { ((Character)localPlayer).Message((MessageType)2, "$smx_unlocked", 0, (Sprite)null); return; } string name = conversion[num].m_from.m_itemData.m_shared.m_name; ((Character)localPlayer).Message((MessageType)2, "$smx_locked_to " + name, 0, (Sprite)null); } } private void RPC_SetLock(long sender, string oreName) { if (m_nview.IsOwner()) { m_nview.GetZDO().Set("smx_lock", oreName ?? string.Empty); } } public void AddCoreFuel(float amount) { m_nview.InvokeRPC("smx_AddCoreFuel", new object[1] { amount }); } private void RPC_AddCoreFuel(long sender, float amount) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (m_nview.IsOwner() && !(amount <= 0f)) { ZDO zDO = m_nview.GetZDO(); zDO.Set(ZDOVars.s_fuel, zDO.GetFloat(ZDOVars.s_fuel, 0f) + amount); zDO.Set("smx_boostfuel", zDO.GetFloat("smx_boostfuel", 0f) + amount); m_smelter.m_fuelAddedEffects.Create(((Component)this).transform.position, ((Component)this).transform.rotation, ((Component)this).transform, 1f, -1); } } private void UpdateCoreBoost() { if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return; } float currentFuel = CurrentFuel; if (!m_haveSeenFuel) { m_lastSeenFuel = currentFuel; m_haveSeenFuel = true; return; } if (!m_nview.IsOwner()) { m_lastSeenFuel = currentFuel; return; } float num = m_nview.GetZDO().GetFloat("smx_boostfuel", 0f); float num2 = StatMath.ConsumeBoost(num, m_lastSeenFuel, currentFuel); if (num2 != num) { m_nview.GetZDO().Set("smx_boostfuel", num2); } m_lastSeenFuel = currentFuel; } public void GetGroupedQueue(List> into) { into.Clear(); if ((Object)(object)m_nview == (Object)null || !m_nview.IsValid()) { return; } ZDO zDO = m_nview.GetZDO(); int num = zDO.GetInt(ZDOVars.s_queued, 0); for (int i = 0; i < num; i++) { string text = zDO.GetString("item" + i, string.Empty); if (!string.IsNullOrEmpty(text)) { if (into.Count > 0 && into[into.Count - 1].Key == text) { into[into.Count - 1] = new KeyValuePair(text, into[into.Count - 1].Value + 1); } else { into.Add(new KeyValuePair(text, 1)); } } } } public void PokeConnections() { //IL_0002: 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_002c: Unknown result type (might be due to invalid IL or missing references) Vector3 stationPosition = StationPosition; foreach (SmelterExtension item in m_attached) { if ((Object)(object)item != (Object)null) { item.StartConnectionEffect(stationPosition); } } } public string GetUpgradeHoverText() { StringBuilder stringBuilder = new StringBuilder(); if (m_attached.Count > 0) { stringBuilder.Append($"\n$smx_level {Level}"); List list = new List(); if (m_deltas.Speed > 0f) { list.Add($"+{Mathf.RoundToInt(m_deltas.Speed * 100f)}% $smx_speed"); } if (m_deltas.OreCap != 0) { list.Add($"+{m_deltas.OreCap} $smx_capacity"); } if (m_base.MaxFuel > 0) { if (m_deltas.FuelCap != 0) { list.Add($"+{m_deltas.FuelCap} $smx_fuel"); } int num = m_base.FuelPerProduct - m_smelter.m_fuelPerProduct; if (num > 0) { list.Add($"-{num} $smx_fuel_per_item"); } } if (list.Count > 0) { stringBuilder.Append("\n" + string.Join(", ", list.ToArray()) + ""); } } if (IsLocked) { stringBuilder.Append("\n$smx_locked_to " + LockedSharedName + ""); } return stringBuilder.ToString(); } } public struct BaseStats { public int MaxOre; public int MaxFuel; public int FuelPerProduct; public float SecPerProduct; } public struct StatDeltas { public float Speed; public int OreCap; public int FuelCap; public float FuelEff; } public static class StatMath { private const float MinSpeedDivisor = 0.05f; public static BaseStats Apply(BaseStats b, StatDeltas d, float extraSpeedMul = 1f) { BaseStats result = b; float num = (1f + d.Speed) * ((extraSpeedMul <= 0f) ? 1f : extraSpeedMul); if (num < 0.05f) { num = 0.05f; } result.SecPerProduct = b.SecPerProduct / num; result.MaxOre = b.MaxOre + d.OreCap; if (b.MaxOre > 0 && result.MaxOre < 1) { result.MaxOre = 1; } if (b.MaxFuel > 0) { result.MaxFuel = b.MaxFuel + d.FuelCap; if (result.MaxFuel < 1) { result.MaxFuel = 1; } int val = (int)Math.Round((float)b.FuelPerProduct * (1f - d.FuelEff), MidpointRounding.AwayFromZero); result.FuelPerProduct = Math.Max(1, val); } return result; } public static float ConsumeBoost(float boost, float lastFuel, float currentFuel) { if (boost <= 0f) { return 0f; } float num = lastFuel - currentFuel; if (num < 0f) { num = 0f; } float num2 = boost - num; if (num2 < 0f) { num2 = 0f; } if (num2 > currentFuel) { num2 = currentFuel; } return num2; } } }