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 Loxury v1.0.1
Loxury.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Loxury.Core; using Loxury.Features; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("Loxury")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: AssemblyProduct("Loxury")] [assembly: AssemblyTitle("Loxury")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] [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 Loxury { [BepInPlugin("loxury.valheim", "Loxury", "1.0.1")] public class Plugin : BaseUnityPlugin { public const string Guid = "loxury.valheim"; public const string Name = "Loxury"; public const string Version = "1.0.1"; internal static ManualLogSource Log; internal static Plugin Instance; private Harmony _harmony; private FileSystemWatcher _watcher; private volatile bool _fileChanged; private float _reloadAt = -1f; private float _ignoreUntil; private void Awake() { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; PlayerTweaks.Init(((BaseUnityPlugin)this).Config); Workstations.Init(((BaseUnityPlugin)this).Config); Building.Init(((BaseUnityPlugin)this).Config); Fires.Init(((BaseUnityPlugin)this).Config); Portals.Init(((BaseUnityPlugin)this).Config); Loot.Init(((BaseUnityPlugin)this).Config); Items.Init(((BaseUnityPlugin)this).Config); SkillGain.Init(((BaseUnityPlugin)this).Config); Production.Init(((BaseUnityPlugin)this).Config); Sailing.Init(((BaseUnityPlugin)this).Config); Tames.Init(((BaseUnityPlugin)this).Config); Death.Init(((BaseUnityPlugin)this).Config); Food.Init(((BaseUnityPlugin)this).Config); Powers.Init(((BaseUnityPlugin)this).Config); NearbyChests.Init(((BaseUnityPlugin)this).Config); Trash.Init(((BaseUnityPlugin)this).Config); Farming.Init(((BaseUnityPlugin)this).Config); Travel.Init(((BaseUnityPlugin)this).Config); Sleep.Init(((BaseUnityPlugin)this).Config); FreeBuild.Init(((BaseUnityPlugin)this).Config); FillMax.Init(((BaseUnityPlugin)this).Config); MultiUserChest.Init(((BaseUnityPlugin)this).Config); SharedMap.Init(((BaseUnityPlugin)this).Config); _harmony = new Harmony("loxury.valheim"); PatchEachClass(); WatchConfigFile(); } private void WatchConfigFile() { try { string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath; _watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath)) { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite), IncludeSubdirectories = false }; _watcher.Changed += delegate { _fileChanged = true; }; _watcher.Created += delegate { _fileChanged = true; }; _watcher.Renamed += delegate { _fileChanged = true; }; _watcher.EnableRaisingEvents = true; } catch (Exception ex) { Log.LogWarning((object)("Config file watching unavailable, changes apply after a restart: " + ex.Message)); } } private void Update() { if (_fileChanged) { _fileChanged = false; if (Time.unscaledTime >= _ignoreUntil) { _reloadAt = Time.unscaledTime + 0.5f; } } if (!(_reloadAt < 0f) && !(Time.unscaledTime < _reloadAt)) { _reloadAt = -1f; try { ((BaseUnityPlugin)this).Config.Reload(); Log.LogInfo((object)"Config file changed, settings reloaded."); } catch (Exception ex) { Log.LogWarning((object)("Could not reload config: " + ex.Message)); } _ignoreUntil = Time.unscaledTime + 1f; } } private void PatchEachClass() { int num = 0; int num2 = 0; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly); foreach (Type type in typesFromAssembly) { if (type.IsDefined(typeof(HarmonyPatch), inherit: false)) { try { _harmony.CreateClassProcessor(type).Patch(); num++; } catch (Exception ex) { num2++; Log.LogError((object)("Patch " + type.FullName + " failed, that feature is inactive: " + ex.GetBaseException().Message)); } } } Log.LogInfo((object)string.Format("{0} {1} loaded: {2} patches applied, {3} failed.", "Loxury", "1.0.1", num, num2)); } private void OnDestroy() { _watcher?.Dispose(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace Loxury.Features { public static class Building { [HarmonyPatch(typeof(WearNTear), "GetMaterialProperties")] private static class StabilityPatch { private static void Postfix(ref float horizontalLoss, ref float verticalLoss) { if (StabilityEnabled.Value) { horizontalLoss *= StabilityLoss.Value; verticalLoss *= StabilityLoss.Value; } } } [HarmonyPatch(typeof(WearNTear), "UpdateWear")] private static class RainPatch { private static void Prefix(WearNTear __instance, out bool __state) { __state = __instance.m_noRoofWear; if (NoRainDamage.Value) { __instance.m_noRoofWear = false; } } private static void Finalizer(WearNTear __instance, bool __state) { __instance.m_noRoofWear = __state; } } private const string Section = "03 - Building"; public static Setting<bool> NoRainDamage; public static Setting<bool> StabilityEnabled; public static Setting<float> StabilityLoss; public static void Init(ConfigFile cfg) { NoRainDamage = ConfigSync.Bind(cfg, "03 - Building", "No Rain Damage", defaultValue: true, "Structures without a roof no longer take damage from rain."); StabilityEnabled = ConfigSync.Bind(cfg, "03 - Building", "Build Stability Enabled", defaultValue: true, "Change how fast structural support drops off."); StabilityLoss = ConfigSync.Bind(cfg, "03 - Building", "Stability Loss Multiplier", 0.5f, "How much support is lost per piece away from the ground, both upwards and sideways. 1 = vanilla, 0.5 = you can build about twice as high/far, 0.1 = about ten times.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f)); } } public static class Death { private sealed class SavedEffect { public int Hash; public float Remaining; } [HarmonyPatch(typeof(Player), "OnDeath")] private static class OnDeathPatch { private static void Prefix(Player __instance, out List<Food> __state) { __state = null; if (!((Character)__instance).m_nview.IsOwner()) { return; } if (On(KeepFood)) { __state = new List<Food>(__instance.m_foods); } PendingEffects.Clear(); if (!On(KeepPotions)) { return; } HashSet<int> hashSet = PotionEffects(); foreach (StatusEffect statusEffect in ((Character)__instance).m_seman.GetStatusEffects()) { if (hashSet.Contains(statusEffect.NameHash()) && !(statusEffect.m_ttl <= 0f)) { PendingEffects.Add(new SavedEffect { Hash = statusEffect.NameHash(), Remaining = statusEffect.GetRemaningTime() }); } } } private static void Postfix(Player __instance, List<Food> __state) { if (__state != null) { __instance.m_foods.Clear(); __instance.m_foods.AddRange(__state); } } } [HarmonyPatch(typeof(Player), "OnSpawned")] private static class RestorePotions { private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || PendingEffects.Count == 0) { return; } foreach (SavedEffect pendingEffect in PendingEffects) { StatusEffect val = ((Character)__instance).m_seman.AddStatusEffect(pendingEffect.Hash, true, 0, 0f, (short)(-1)); if ((Object)(object)val != (Object)null && val.m_ttl > 0f) { val.m_time = Mathf.Max(0f, val.m_ttl - pendingEffect.Remaining); } } PendingEffects.Clear(); } } [HarmonyPatch(typeof(Skills), "OnDeath")] private static class SkillPatch { private static bool Prefix() { return !On(NoSkillDrain); } } [HarmonyPatch(typeof(Player), "CreateTombStone")] private static class TombstonePatch { private static bool Prefix(Player __instance) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (On(KeepAllItems)) { return false; } if (!On(KeepEquipped)) { return true; } Inventory inventory = ((Humanoid)__instance).m_inventory; ZoneSystem instance = ZoneSystem.instance; if (inventory.NrOfItems() == 0 || instance.GetGlobalKey((GlobalKeys)23)) { return false; } if (instance.GetGlobalKey((GlobalKeys)20) || instance.GetGlobalKey((GlobalKeys)21)) { inventory.RemoveUnequipped(); } bool flag = false; foreach (ItemData allItem in inventory.GetAllItems()) { if (!allItem.m_equipped && !allItem.m_shared.m_questItem) { flag = true; break; } } if (!flag) { return false; } GameObject obj = Object.Instantiate<GameObject>(__instance.m_tombstone, ((Character)__instance).GetCenterPoint(), ((Component)__instance).transform.rotation); obj.GetComponent<Container>().GetInventory().MoveInventoryToGrave(inventory); PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); obj.GetComponent<TombStone>().Setup(playerProfile.GetName(), playerProfile.GetPlayerID()); return false; } } private const string Section = "12 - Death"; public static Setting<bool> Enabled; public static Setting<bool> KeepFood; public static Setting<bool> KeepPotions; public static Setting<bool> NoSkillDrain; public static Setting<bool> KeepEquipped; public static Setting<bool> KeepAllItems; private static readonly List<SavedEffect> PendingEffects = new List<SavedEffect>(); private static HashSet<int> _potionEffects; public static void Init(ConfigFile cfg) { Enabled = ConfigSync.Bind(cfg, "12 - Death", "Death Tweaks Enabled", defaultValue: true, "Master switch for the options below."); KeepFood = ConfigSync.Bind(cfg, "12 - Death", "Keep Food", defaultValue: true, "Your eaten food (and its remaining time) survives death."); KeepPotions = ConfigSync.Bind(cfg, "12 - Death", "Keep Potions", defaultValue: true, "Active mead and potion effects survive death with their remaining time. Debuffs like Wet or Poison do not."); NoSkillDrain = ConfigSync.Bind(cfg, "12 - Death", "No Skill Loss", defaultValue: true, "Dying does not lower your skills."); KeepEquipped = ConfigSync.Bind(cfg, "12 - Death", "Keep Equipped Items", defaultValue: true, "Worn armor and equipped weapons stay on you; everything else goes into the tombstone."); KeepAllItems = ConfigSync.Bind(cfg, "12 - Death", "Keep All Items", defaultValue: false, "Keep your whole inventory. No tombstone is created. Overrides Keep Equipped Items."); } private static bool On(Setting<bool> s) { if (Enabled.Value) { return s.Value; } return false; } private static HashSet<int> PotionEffects() { if (_potionEffects != null || (Object)(object)ObjectDB.instance == (Object)null) { return _potionEffects ?? new HashSet<int>(); } _potionEffects = new HashSet<int>(); foreach (GameObject item in ObjectDB.instance.m_items) { StatusEffect val = ((!Object.op_Implicit((Object)(object)item)) ? null : item.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_consumeStatusEffect); if ((Object)(object)val != (Object)null) { _potionEffects.Add(val.NameHash()); } } return _potionEffects; } } public static class Farming { [HarmonyPatch(typeof(Plant), "GetGrowTime")] private static class GrowTimePatch { private static void Postfix(ref float __result) { if (GrowEnabled.Value) { __result *= GrowTime.Value; } } } [HarmonyPatch(typeof(Plant), "GetHoverText")] private static class GrowTimer { private static void Postfix(Plant __instance, ref string __result) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (ShowGrowTimer.Value && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && (int)__instance.GetStatus() == 0) { double num = (double)__instance.GetGrowTime() - __instance.TimeSincePlanted(); __result += ((num > 0.0) ? ("\n<color=#9fd48a>Fully grown in " + FormatTime(num) + "</color>") : "\n<color=#9fd48a>Ready to grow</color>"); } } private static string FormatTime(double seconds) { int num = Mathf.CeilToInt((float)seconds); if (num >= 3600) { return $"{num / 3600}h {num % 3600 / 60}m"; } if (num >= 60) { return $"{num / 60}m {num % 60}s"; } return $"{num}s"; } } [HarmonyPatch(typeof(Plant), "Awake")] private static class TrackPlant { private static void Postfix(Plant __instance) { if (IsReal((Component)(object)__instance)) { Anchors.Add((Component)(object)__instance); } } } [HarmonyPatch(typeof(Pickable), "Awake")] private static class TrackPickable { private static void Postfix(Pickable __instance) { if (IsReal((Component)(object)__instance)) { Anchors.Add((Component)(object)__instance); } } } [HarmonyPatch(typeof(Player), "UpdatePlacementGhost")] private static class GridSnap { private static void Postfix(Player __instance) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Invalid comparison between Unknown and I4 //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Invalid comparison between Unknown and I4 //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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_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_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0176: 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_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018b: 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_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Invalid comparison between Unknown and I4 //IL_0244: Unknown result type (might be due to invalid IL or missing references) GameObject placementGhost = __instance.m_placementGhost; if (!GridPlanting.Value || (Object)(object)placementGhost == (Object)null || !placementGhost.activeSelf) { return; } KeyboardShortcut value = GridToggleKey.Value; if (((KeyboardShortcut)(ref value)).IsPressed()) { return; } Plant component = placementGhost.GetComponent<Plant>(); Piece component2 = placementGhost.GetComponent<Piece>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { return; } PlacementStatus placementStatus = __instance.m_placementStatus; if ((int)placementStatus != 0 && (int)placementStatus != 9 && (int)placementStatus != 5) { return; } Vector3 position = placementGhost.transform.position; float ghostFootprint = Footprint((Component)(object)component); Anchors.RemoveWhere((Component a) => (Object)(object)a == (Object)null); Component val = null; float num = 0f; float num2 = float.MaxValue; foreach (Component anchor in Anchors) { if (Near(anchor, position)) { float num3 = Spacing(component, ghostFootprint, anchor); float num4 = FlatDistance(anchor.transform.position, position); if (num4 < num3 * 3f && num4 < num2) { num2 = num4; val = anchor; num = num3; } } } if ((Object)(object)val == (Object)null) { return; } Vector3 position2 = val.transform.position; Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(position2.x + Mathf.Round((position.x - position2.x) / num) * num, position.y, position2.z + Mathf.Round((position.z - position2.z) / num) * num); val2.y = ZoneSystem.instance.GetGroundHeight(val2); placementGhost.transform.position = val2; PlacementStatus val3 = (PlacementStatus)0; if (component2.m_cultivatedGroundOnly) { Heightmap val4 = Heightmap.FindHeightmap(val2); if ((Object)(object)val4 == (Object)null || !val4.IsCultivated(val2)) { val3 = (PlacementStatus)9; } } foreach (Component anchor2 in Anchors) { if (Near(anchor2, val2) && FlatDistance(anchor2.transform.position, val2) < Spacing(component, ghostFootprint, anchor2) - GridMargin.Value - 0.01f) { val3 = (PlacementStatus)5; break; } } __instance.m_placementStatus = val3; __instance.SetPlacementGhostValid((int)val3 == 0); } } private sealed class ExtraPlant { public string Key; public string Label; public string Item; public string Grown; public ExtraPlant(string key, string label, string item, string grown) { Key = key; Label = label; Item = item; Grown = grown; } } [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class Register { private static void Prefix(ZNetScene __instance) { if (!ExtraPlants.Entry.Value) { return; } try { CreateSaplings(__instance.m_prefabs); } catch (Exception arg) { Plugin.Log.LogError((object)$"Extra plantables failed: {arg}"); } } private static void Postfix() { AddToCultivator(); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class AddOnObjectDb { private static void Postfix() { AddToCultivator(); } } [HarmonyPatch(typeof(Plant), "Grow")] private static class MarkPlanted { private static void Postfix(Plant __instance, GameObject __result) { if (!((Object)(object)__result == (Object)null) && ((Object)__instance).name.StartsWith("Hearthbound_sapling_")) { ZNetView component = __result.GetComponent<ZNetView>(); ZDO obj = ((component != null) ? component.GetZDO() : null); if (obj != null) { obj.Set(PlantedFlag, true); } } } } [HarmonyPatch(typeof(Pickable), "RPC_Pick")] private static class HarvestMultiplier { private static void Prefix(Pickable __instance, out int __state) { __state = -1; ZDO val = (((Object)(object)__instance.m_nview != (Object)null) ? __instance.m_nview.GetZDO() : null); if (val != null && val.GetBool(PlantedFlag, false)) { __state = __instance.m_amount; __instance.m_amount *= ExtraHarvest.Value; } } private static void Finalizer(Pickable __instance, int __state) { if (__state >= 0) { __instance.m_amount = __state; } } } private const string Section = "17 - Farming"; private const string SaplingPrefix = "Hearthbound_sapling_"; private static readonly int PlantedFlag = StringExtensionMethods.GetStableHashCode("Hearthbound_planted"); public static Setting<bool> GrowEnabled; public static Setting<float> GrowTime; public static Setting<bool> ShowGrowTimer; public static Setting<bool> GridPlanting; public static Setting<float> GridMargin; public static ConfigEntry<KeyboardShortcut> GridToggleKey; public static Setting<bool> ExtraPlants; public static Setting<int> ExtraPlantCost; public static Setting<int> ExtraHarvest; private static readonly HashSet<Component> Anchors = new HashSet<Component>(); private static readonly Dictionary<string, float> ExtentCache = new Dictionary<string, float>(); private static readonly ExtraPlant[] Extras = new ExtraPlant[8] { new ExtraPlant("raspberry", "Raspberry bush", "Raspberry", "RaspberryBush"), new ExtraPlant("blueberry", "Blueberry bush", "Blueberries", "BlueberryBush"), new ExtraPlant("cloudberry", "Cloudberry bush", "Cloudberry", "CloudberryBush"), new ExtraPlant("mushroom", "Mushroom", "Mushroom", "Pickable_Mushroom"), new ExtraPlant("mushroom_yellow", "Yellow mushroom", "MushroomYellow", "Pickable_Mushroom_yellow"), new ExtraPlant("mushroom_blue", "Blue mushroom", "MushroomBlue", "Pickable_Mushroom_blue"), new ExtraPlant("thistle", "Thistle", "Thistle", "Pickable_Thistle"), new ExtraPlant("dandelion", "Dandelion", "Dandelion", "Pickable_Dandelion") }; private const Biome AllowedBiomes = (Biome)543; private static readonly List<GameObject> CreatedSaplings = new List<GameObject>(); private static GameObject _holder; public static void Init(ConfigFile cfg) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown GrowEnabled = ConfigSync.Bind(cfg, "17 - Farming", "Grow Time Enabled", defaultValue: true, "Change how long planted crops and trees take to grow."); GrowTime = ConfigSync.Bind(cfg, "17 - Farming", "Grow Time Multiplier", 0.5f, "Grow time. 1 = vanilla, 0.5 = twice as fast, 2 = twice as slow.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 10f)); ShowGrowTimer = ConfigSync.Bind(cfg, "17 - Farming", "Show Grow Timer", defaultValue: true, "Hovering a growing plant shows how long until it is fully grown."); GridPlanting = ConfigSync.Bind(cfg, "17 - Farming", "Grid Planting", defaultValue: true, "When planting near other plants, snap to a grid with exactly the spacing each crop needs to stay healthy, including its full-grown size."); GridMargin = ConfigSync.Bind(cfg, "17 - Farming", "Grid Extra Spacing", 0.05f, "Extra distance added to the minimum spacing, in meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f)); GridToggleKey = cfg.Bind<KeyboardShortcut>("17 - Farming", "Grid Hold To Disable", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), new ConfigDescription("Hold this key to place plants freely without the grid. Personal setting.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes() })); ExtraPlants = ConfigSync.Bind(cfg, "17 - Farming", "Extra Plantables", defaultValue: true, "The cultivator can plant raspberries, blueberries, cloudberries, mushrooms, thistle and dandelions. Takes effect after restarting the game."); ExtraPlantCost = ConfigSync.Bind(cfg, "17 - Farming", "Extra Plantables Cost", 1, "How many of the item one planting costs. Takes effect after restarting the game.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20)); ExtraHarvest = ConfigSync.Bind(cfg, "17 - Farming", "Extra Plantables Harvest Multiplier", 3, "Harvests from planted berries, mushrooms and flowers give this many times the normal amount. Wild ones are unaffected.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20)); } private static bool IsReal(Component c) { ZNetView component = c.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null) { return component.GetZDO() != null; } return false; } private static float Extent(GameObject go) { string prefabName = Utils.GetPrefabName(go); if (ExtentCache.TryGetValue(prefabName, out var value)) { return value; } float num = MeasureExtent(go); ExtentCache[prefabName] = num; return num; } private static float MeasureExtent(GameObject go) { //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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00ed: Unknown result type (might be due to invalid IL or missing references) float num = 0.1f; Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { if (val.isTrigger) { continue; } Vector3 lossyScale = ((Component)val).transform.lossyScale; float num2 = Mathf.Max(Mathf.Abs(lossyScale.x), Mathf.Abs(lossyScale.z)); Vector3 val2 = go.transform.InverseTransformPoint(((Component)val).transform.position); Vector2 val3 = new Vector2(val2.x, val2.z); float magnitude = ((Vector2)(ref val3)).magnitude; SphereCollider val4 = (SphereCollider)(object)((val is SphereCollider) ? val : null); float num3; if (val4 == null) { CapsuleCollider val5 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null); if (val5 == null) { BoxCollider val6 = (BoxCollider)(object)((val is BoxCollider) ? val : null); num3 = ((val6 == null) ? (0.3f * num2) : (Mathf.Max(val6.size.x * Mathf.Abs(lossyScale.x), val6.size.z * Mathf.Abs(lossyScale.z)) * 0.5f)); } else { num3 = val5.radius * num2; } } else { num3 = val4.radius * num2; } num = Mathf.Max(num, num3 + magnitude); } return num; } private static float Footprint(Component c) { float num = Extent(c.gameObject); Plant val = (Plant)(object)((c is Plant) ? c : null); if (val != null) { GameObject[] grownPrefabs = val.m_grownPrefabs; foreach (GameObject val2 in grownPrefabs) { if ((Object)(object)val2 != (Object)null) { num = Mathf.Max(num, Extent(val2)); } } } return num; } private static float GrowRadius(Component c) { return ((Plant)(((c is Plant) ? c : null)?)).m_growRadius ?? 0f; } private static float Spacing(Plant ghost, float ghostFootprint, Component other) { return Mathf.Max(ghost.m_growRadius + Footprint(other), GrowRadius(other) + ghostFootprint) + GridMargin.Value; } private static bool Near(Component a, Vector3 p) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Abs(a.transform.position.x - p.x) < 8f) { return Mathf.Abs(a.transform.position.z - p.z) < 8f; } return false; } private static float FlatDistance(Vector3 a, Vector3 b) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) Vector2 val = new Vector2(a.x - b.x, a.z - b.z); return ((Vector2)(ref val)).magnitude; } private static void CreateSaplings(List<GameObject> prefabs) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Expected O, but got Unknown //IL_021e: Unknown result type (might be due to invalid IL or missing references) if (CreatedSaplings.Count == 0) { GameObject val = Find("sapling_carrot"); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Extra plantables: sapling_carrot not found."); return; } if ((Object)(object)_holder == (Object)null) { _holder = new GameObject("Loxury_Prefabs"); _holder.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_holder); } ExtraPlant[] extras = Extras; foreach (ExtraPlant extraPlant in extras) { GameObject val2 = Find(extraPlant.Grown); GameObject obj = Find(extraPlant.Item); ItemDrop val3 = ((obj != null) ? obj.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { Plugin.Log.LogWarning((object)("Extra plantables: skipping " + extraPlant.Label + " (missing " + (((Object)(object)val2 == (Object)null) ? extraPlant.Grown : extraPlant.Item) + ").")); continue; } GameObject val4 = Object.Instantiate<GameObject>(val, _holder.transform); ((Object)val4).name = "Hearthbound_sapling_" + extraPlant.Key; Piece component = val4.GetComponent<Piece>(); component.m_name = extraPlant.Label; component.m_description = "Plant " + extraPlant.Label.ToLower() + ". Grows into a harvestable " + extraPlant.Label.ToLower() + "."; component.m_icon = val3.m_itemData.GetIcon(); component.m_resources = (Requirement[])(object)new Requirement[1] { new Requirement { m_resItem = val3, m_amount = ExtraPlantCost.Entry.Value, m_recover = true } }; Plant component2 = val4.GetComponent<Plant>(); component2.m_name = extraPlant.Label; component2.m_grownPrefabs = (GameObject[])(object)new GameObject[1] { val2 }; component2.m_biome = (Biome)543; component2.m_minScale = (component2.m_maxScale = 1f); component2.m_destroyIfCantGrow = false; component2.m_growRadius = Mathf.Max(component2.m_growRadius, Extent(val2) + 0.25f); ReplaceVisuals(component2, val2); CreatedSaplings.Add(val4); Plugin.Log.LogInfo((object)("Extra plantable ready: " + extraPlant.Label)); } } foreach (GameObject createdSapling in CreatedSaplings) { if (!prefabs.Contains(createdSapling)) { prefabs.Add(createdSapling); } } GameObject Find(string name) { return prefabs.Find((GameObject p) => (Object)(object)p != (Object)null && ((Object)p).name == name); } } private static void ReplaceVisuals(Plant plant, GameObject grown) { GameObject[] array = (GameObject[])(object)new GameObject[4] { plant.m_healthy, plant.m_unhealthy, plant.m_healthyGrown, plant.m_unhealthyGrown }; foreach (GameObject val in array) { if ((Object)(object)val != (Object)null) { Object.DestroyImmediate((Object)(object)val); } } Pickable component = grown.GetComponent<Pickable>(); float num = (((Object)(object)component != (Object)null && component.m_respawnTimeMinutes > 0f) ? 0.8f : 0.6f); plant.m_healthy = MakeVisual(grown, ((Component)plant).transform, num, "healthy"); plant.m_unhealthy = MakeVisual(grown, ((Component)plant).transform, num * 0.85f, "unhealthy"); plant.m_healthyGrown = null; plant.m_unhealthyGrown = null; } private static GameObject MakeVisual(GameObject grown, Transform parent, float scale, string name) { //IL_0015: 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_0035: 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) GameObject val = Object.Instantiate<GameObject>(grown, parent); ((Object)val).name = name; val.transform.localPosition = Vector3.zero; val.transform.localRotation = Quaternion.identity; val.transform.localScale = Vector3.one * scale; Pickable component = grown.GetComponent<Pickable>(); if ((Object)(object)component != (Object)null && (Object)(object)component.m_hideWhenPicked != (Object)null && (Object)(object)component.m_hideWhenPicked != (Object)(object)grown) { string text = PathFrom(grown.transform, component.m_hideWhenPicked.transform); Transform val2 = ((text == null) ? null : val.transform.Find(text)); if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); if (val.GetComponentsInChildren<Renderer>(false).Length == 0) { ((Component)val2).gameObject.SetActive(true); } } } Strip(val); return val; } private static string PathFrom(Transform root, Transform target) { List<string> list = new List<string>(); Transform val = target; while ((Object)(object)val != (Object)null) { if ((Object)(object)val == (Object)(object)root) { list.Reverse(); return string.Join("/", list); } list.Add(((Object)val).name); val = val.parent; } return null; } private static void Strip(GameObject go) { for (int i = 0; i < 4; i++) { MonoBehaviour[] componentsInChildren = go.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInChildren) { if (!(val is ZNetView)) { Object.DestroyImmediate((Object)(object)val); } } } Component[] componentsInChildren2 = go.GetComponentsInChildren<Component>(true); foreach (Component val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !(val2 is Transform) && !(val2 is MeshFilter) && !(val2 is Renderer) && !(val2 is LODGroup)) { Object.DestroyImmediate((Object)(object)val2); } } } private static void AddToCultivator() { if (CreatedSaplings.Count == 0 || (Object)(object)ObjectDB.instance == (Object)null) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Cultivator"); PieceTable val = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null)?.m_itemData.m_shared.m_buildPieces; if ((Object)(object)val == (Object)null) { return; } foreach (GameObject createdSapling in CreatedSaplings) { if (!val.m_pieces.Contains(createdSapling)) { val.m_pieces.Add(createdSapling); } } } } public static class FillMax { [HarmonyPatch(typeof(Smelter), "OnAddOre")] private static class SmelterOre { private static void Prefix(Smelter __instance, out int __state) { __state = __instance.GetQueueSize(); } private static void Postfix(Smelter __instance, Switch sw, Humanoid user, bool __result, int __state) { if (__result && ShouldRepeat(user)) { Repeat(__instance.m_maxOre - __state - 1, () => __instance.OnAddOre(sw, user, (ItemData)null)); } } } [HarmonyPatch(typeof(Smelter), "OnAddFuel")] private static class SmelterFuel { private static void Prefix(Smelter __instance, out int __state) { __state = Mathf.CeilToInt(__instance.GetFuel()); } private static void Postfix(Smelter __instance, Switch sw, Humanoid user, bool __result, int __state) { if (__result && ShouldRepeat(user)) { Repeat(__instance.m_maxFuel - __state - 1, () => __instance.OnAddFuel(sw, user, (ItemData)null)); } } } [HarmonyPatch(typeof(Fireplace), "Interact")] private static class FireFuel { private static void Prefix(Fireplace __instance, out int __state) { __state = (((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()) ? Mathf.CeilToInt(__instance.m_nview.GetZDO().GetFloat(ZDOVars.s_fuel, 0f)) : int.MaxValue); } private static void Postfix(Fireplace __instance, Humanoid user, bool hold, bool alt, bool __result, int __state) { if (!(!__result || hold) && alt && __state != int.MaxValue && ShouldRepeat(user)) { Repeat(Mathf.CeilToInt(__instance.m_maxFuel) - __state - 1, () => __instance.Interact(user, false, true)); } } } [HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")] private static class OvenFuel { private static void Prefix(CookingStation __instance, out int __state) { __state = Mathf.CeilToInt(__instance.GetFuel()); } private static void Postfix(CookingStation __instance, Switch sw, Humanoid user, bool __result, int __state) { if (__result && ShouldRepeat(user)) { Repeat(__instance.m_maxFuel - __state - 1, () => __instance.OnAddFuelSwitch(sw, user, (ItemData)null)); } } } [HarmonyPatch(typeof(ShieldGenerator), "OnAddFuel")] private static class ShieldFuel { private static void Prefix(ShieldGenerator __instance, out int __state) { __state = Mathf.CeilToInt(__instance.GetFuel()); } private static void Postfix(ShieldGenerator __instance, Switch sw, Humanoid user, bool __result, int __state) { if (__result && ShouldRepeat(user)) { Repeat(__instance.m_maxFuel - __state - 1, () => __instance.OnAddFuel(sw, user, (ItemData)null)); } } } [HarmonyPatch(typeof(Turret), "UseItem")] private static class TurretAmmo { private static void Prefix(Turret __instance, ItemData item, out int __state) { __state = ((item == null) ? __instance.GetAmmo() : (-1)); } private static void Postfix(Turret __instance, Humanoid user, bool __result, int __state) { if (__result && __state >= 0 && __instance.m_maxAmmo > 0 && ShouldRepeat(user)) { Repeat(__instance.m_maxAmmo - __state - 1, () => __instance.UseItem(user, (ItemData)null)); } } } private const string Section = "21 - Fill Stations"; public static Setting<bool> Enabled; private static bool _repeating; public static void Init(ConfigFile cfg) { Enabled = ConfigSync.Bind(cfg, "21 - Fill Stations", "Shift E Fills To Max", defaultValue: true, "Hold Shift while pressing E on a smelter, kiln, furnace, refinery, windmill, spinning wheel, fire, oven, shield generator or ballista to add as much as fits."); } private static bool ShiftHeld() { if (!ZInput.GetButton("AltPlace") && !Input.GetKey((KeyCode)304)) { return Input.GetKey((KeyCode)303); } return true; } private static bool ShouldRepeat(Humanoid user) { if (Enabled.Value && !_repeating && (Object)(object)user != (Object)null && (Object)(object)user == (Object)(object)Player.m_localPlayer) { return ShiftHeld(); } return false; } private static void Repeat(int times, Func<bool> addOne) { _repeating = true; try { for (int i = 0; i < times; i++) { if (!addOne()) { break; } } } finally { _repeating = false; } } } public static class Fires { [HarmonyPatch(typeof(Fireplace), "UpdateFireplace")] private static class NoFuelDrain { private static void Prefix(Fireplace __instance) { if (InfiniteFuel.Value && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && __instance.m_nview.IsOwner()) { __instance.GetTimeSinceLastUpdate(); } } } private const string Section = "04 - Fires"; public static Setting<bool> InfiniteFuel; public static void Init(ConfigFile cfg) { InfiniteFuel = ConfigSync.Bind(cfg, "04 - Fires", "Infinite Fires And Lights", defaultValue: true, "Campfires, hearths, torches, braziers, sconces and the hot tub keep burning once lit. They still need fuel once to light. Smelters, kilns, furnaces and ovens are not affected."); } } public static class Food { [HarmonyPatch(typeof(Player), "CanEat")] private static class CanEatLimit { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> i) { return ReplaceFoodLimit(i); } } [HarmonyPatch(typeof(Player), "EatFood")] private static class EatFoodLimit { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> i) { return ReplaceFoodLimit(i); } } [HarmonyPatch(typeof(Food), "CanEatAgain")] private static class ReEat { private static void Postfix(Food __instance, ref bool __result) { if (Enabled.Value) { __result = __instance.m_time < __instance.m_item.m_shared.m_foodBurnTime * ReEatAt.Value + 0.001f; } } } [HarmonyPatch(typeof(Player), "UpdateFood")] private static class DurationPatch { private static void Prefix(Player __instance, float dt, bool forceUpdate) { if (!(!Enabled.Value || forceUpdate)) { float num = dt * Game.m_foodRate; if (InfiniteDuration.Value) { __instance.m_foodUpdateTimer -= num; } else { __instance.m_foodUpdateTimer -= num * (1f - 1f / DurationMultiplier.Value); } } } private static void Postfix(Player __instance) { if (!Enabled.Value || !NoDegradation.Value || __instance.m_foods.Count == 0) { return; } bool flag = false; foreach (Food food in __instance.m_foods) { SharedData shared = food.m_item.m_shared; if (food.m_health < shared.m_food || food.m_stamina < shared.m_foodStamina || food.m_eitr < shared.m_foodEitr) { food.m_health = shared.m_food; food.m_stamina = shared.m_foodStamina; food.m_eitr = shared.m_foodEitr; flag = true; } } if (flag) { float num = default(float); float num2 = default(float); float num3 = default(float); __instance.GetTotalFoodValue(ref num, ref num2, ref num3); __instance.SetMaxHealth(num, false); __instance.SetMaxStamina(num2, false); __instance.SetMaxEitr(num3, false); } } } [HarmonyPatch(typeof(Hud), "UpdateFood")] private static class HudSlots { private static void Prefix(Hud __instance) { int num = Mathf.Max(3, CurrentMax()); if (__instance.m_foodIcons.Length >= num) { return; } try { AddSlots(__instance, num); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not add food HUD slots: " + ex.Message)); } } private static void AddSlots(Hud hud, int wanted) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0079: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) List<Image> list = new List<Image>(hud.m_foodBars); List<Image> list2 = new List<Image>(hud.m_foodIcons); List<TMP_Text> list3 = new List<TMP_Text>(hud.m_foodTime); int count = list2.Count; Vector2 val = ((Graphic)list2[count - 1]).rectTransform.anchoredPosition - ((Graphic)list2[count - 2]).rectTransform.anchoredPosition; Vector2 val2 = ((Graphic)list[count - 1]).rectTransform.anchoredPosition - ((Graphic)list[count - 2]).rectTransform.anchoredPosition; bool flag = list3[count - 1].transform.IsChildOf(((Component)list2[count - 1]).transform); Vector2 val3 = list3[count - 1].rectTransform.anchoredPosition - list3[count - 2].rectTransform.anchoredPosition; while (list2.Count < wanted) { int index = list2.Count - 1; Image val4 = Object.Instantiate<Image>(list2[index], ((Component)list2[index]).transform.parent); ((Graphic)val4).rectTransform.anchoredPosition = ((Graphic)list2[index]).rectTransform.anchoredPosition + val; list2.Add(val4); Image val5 = Object.Instantiate<Image>(list[index], ((Component)list[index]).transform.parent); ((Graphic)val5).rectTransform.anchoredPosition = ((Graphic)list[index]).rectTransform.anchoredPosition + val2; list.Add(val5); if (flag) { list3.Add(((Component)val4).GetComponentInChildren<TMP_Text>(true)); continue; } TMP_Text val6 = Object.Instantiate<TMP_Text>(list3[index], list3[index].transform.parent); val6.rectTransform.anchoredPosition = list3[index].rectTransform.anchoredPosition + val3; list3.Add(val6); } hud.m_foodBars = list.ToArray(); hud.m_foodIcons = list2.ToArray(); hud.m_foodTime = list3.ToArray(); } } private const string Section = "13 - Food"; public static Setting<bool> Enabled; public static Setting<int> MaxFoods; public static Setting<bool> NoDegradation; public static Setting<bool> InfiniteDuration; public static Setting<float> DurationMultiplier; public static Setting<float> ReEatAt; public static void Init(ConfigFile cfg) { Enabled = ConfigSync.Bind(cfg, "13 - Food", "Food Tweaks Enabled", defaultValue: true, "Master switch for the options below."); MaxFoods = ConfigSync.Bind(cfg, "13 - Food", "Max Foods", 4, "How many different foods you can have active at once. Vanilla is 3.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 6)); NoDegradation = ConfigSync.Bind(cfg, "13 - Food", "No Food Degradation", defaultValue: true, "Food gives its full health, stamina and eitr until it runs out, instead of fading over time."); InfiniteDuration = ConfigSync.Bind(cfg, "13 - Food", "Infinite Food Duration", defaultValue: false, "Food never runs out. Overrides the duration multiplier."); DurationMultiplier = ConfigSync.Bind(cfg, "13 - Food", "Food Duration Multiplier", 1.5f, "How long food lasts. 1 = vanilla, 2 = twice as long.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f)); ReEatAt = ConfigSync.Bind(cfg, "13 - Food", "Can Eat Again At", 0.5f, "Fraction of the food's time left at which you can eat the same food again. Vanilla is 0.5 (halfway). 1 = any time.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f)); } public static int CurrentMax() { if (!Enabled.Value) { return 3; } return MaxFoods.Value; } private static IEnumerable<CodeInstruction> ReplaceFoodLimit(IEnumerable<CodeInstruction> instructions) { MethodInfo getCount = AccessTools.PropertyGetter(typeof(List<Food>), "Count"); MethodInfo max = AccessTools.Method(typeof(Food), "CurrentMax", (Type[])null, (Type[])null); CodeInstruction val = null; int replaced = 0; foreach (CodeInstruction ins in instructions) { if (val != null && CodeInstructionExtensions.Calls(val, getCount) && ins.opcode == OpCodes.Ldc_I4_3) { yield return CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Call, (object)max), ins); replaced++; } else { yield return ins; } val = ins; } if (replaced == 0) { Plugin.Log.LogWarning((object)"Max Foods: food limit not found in game code, the setting has no effect."); } } } public enum FreeBuildMode { Off, StructuresOnly, StructuresAndFurniture, Everything } public static class FreeBuild { [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] private static class Requirements { private static void Postfix(Player __instance, Piece piece, RequirementMode mode, ref bool __result) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (__result || (int)mode == 1 || !IsFree(piece)) { return; } if (Object.op_Implicit((Object)(object)piece.m_craftingStation)) { if ((int)mode == 2) { if (!__instance.m_knownStations.ContainsKey(piece.m_craftingStation.m_name)) { return; } } else if (!Object.op_Implicit((Object)(object)CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)__instance).transform.position)) && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)27)) { return; } } if (piece.m_dlc.Length <= 0 || DLCMan.instance.IsDLCInstalled(piece.m_dlc)) { __result = true; } } } [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static class PlacementScope { private static void Finalizer() { _placingFree = false; } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static class Place { private static void Prefix(Player __instance, Piece piece) { _placingFree = IsFree(piece) && !__instance.NoCostCheat(); } } [HarmonyPatch(typeof(Piece), "SetCreator")] private static class Mark { private static void Postfix(Piece __instance) { if (_placingFree) { ZDO obj = (((Object)(object)__instance.m_nview != (Object)null) ? __instance.m_nview.GetZDO() : null); if (obj != null) { obj.Set(FreeFlag, true); } } } } [HarmonyPatch(typeof(Player), "ConsumeResources")] private static class NoCost { private static bool Prefix() { if (!_placingFree) { return true; } _placingFree = false; return false; } } [HarmonyPatch(typeof(Piece), "DropResources")] private static class NoDrops { private static bool Prefix(Piece __instance) { ZNetView val = (((Object)(object)__instance.m_nview != (Object)null) ? __instance.m_nview : ((Component)__instance).GetComponent<ZNetView>()); ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null); if (val2 != null) { return !val2.GetBool(FreeFlag, false); } return true; } } private const string Section = "20 - Free Building"; private static readonly int FreeFlag = StringExtensionMethods.GetStableHashCode("Hearthbound_free"); public static Setting<FreeBuildMode> Mode; private static bool _placingFree; public static void Init(ConfigFile cfg) { Mode = ConfigSync.Bind(cfg, "20 - Free Building", "Free Building", FreeBuildMode.Off, "Which pieces cost nothing to build. StructuresOnly = walls, floors, roofs, beams, stairs (workbench and stonecutter building tabs). StructuresAndFurniture = plus the furniture tab. Everything = all pieces. Pieces built for free never drop materials when removed or destroyed. Plants, feast tables and items placed as pieces are never free, since they could be turned into items."); } public static bool IsFree(Piece piece) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected I4, but got Unknown FreeBuildMode value = Mode.Value; if (value == FreeBuildMode.Off || (Object)(object)piece == (Object)null) { return false; } if (Object.op_Implicit((Object)(object)((Component)piece).GetComponent<Plant>()) || Object.op_Implicit((Object)(object)((Component)piece).GetComponent<Pickable>()) || Object.op_Implicit((Object)(object)((Component)piece).GetComponent<Feast>()) || Object.op_Implicit((Object)(object)((Component)piece).GetComponent<ItemDrop>())) { return false; } PieceCategory category = piece.m_category; switch (category - 2) { case 0: case 1: case 3: return true; case 2: return value >= FreeBuildMode.StructuresAndFurniture; default: return value == FreeBuildMode.Everything; } } } public static class Items { [HarmonyPatch(typeof(ObjectDB), "Awake")] private static class OnAwake { private static void Postfix() { Apply(); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] private static class OnCopy { private static void Postfix() { Apply(); } } private const string Section = "07 - Items"; public static Setting<bool> StackEnabled; public static Setting<float> StackMultiplier; public static Setting<float> ArrowCost; private static readonly Dictionary<string, int> OriginalStacks = new Dictionary<string, int>(); private static readonly Dictionary<string, int[]> OriginalArrowCosts = new Dictionary<string, int[]>(); public static void Init(ConfigFile cfg) { StackEnabled = ConfigSync.Bind(cfg, "07 - Items", "Stack Size Enabled", defaultValue: true, "Multiply how many of an item fit in one slot."); StackMultiplier = ConfigSync.Bind(cfg, "07 - Items", "Stack Size Multiplier", 2f, "Stack size multiplier. Only items that already stack are affected (weapons and armor stay at 1).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f)); ArrowCost = ConfigSync.Bind(cfg, "07 - Items", "Arrow Crafting Cost Multiplier", 1f, "Multiplies the materials needed to craft arrows (arrows only, not bolts or other ammo). 0.5 = half the materials. Each material costs at least 1.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f)); ArrowCost.Changed += Apply; StackEnabled.Changed += Apply; StackMultiplier.Changed += Apply; ConfigSync.Synced += Apply; } public static void Apply() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return; } ApplyArrowCosts(instance); foreach (GameObject item in instance.m_items) { ItemDrop val = (Object.op_Implicit((Object)(object)item) ? item.GetComponent<ItemDrop>() : null); if (!((Object)(object)val == (Object)null)) { SharedData shared = val.m_itemData.m_shared; if (!OriginalStacks.TryGetValue(((Object)item).name, out var value)) { value = shared.m_maxStackSize; OriginalStacks[((Object)item).name] = value; } if (value > 1) { shared.m_maxStackSize = (StackEnabled.Value ? Mathf.Max(1, Mathf.RoundToInt((float)value * StackMultiplier.Value)) : value); } } } } private static void ApplyArrowCosts(ObjectDB db) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Invalid comparison between Unknown and I4 HashSet<string> hashSet = new HashSet<string>(); foreach (GameObject item in db.m_items) { SharedData val = ((!Object.op_Implicit((Object)(object)item)) ? null : item.GetComponent<ItemDrop>()?.m_itemData.m_shared); if (val != null && (int)val.m_skillType == 8 && !string.IsNullOrEmpty(val.m_ammoType)) { hashSet.Add(val.m_ammoType); } } if (hashSet.Count == 0) { hashSet.Add("$ammo_arrows"); } foreach (Recipe recipe in db.m_recipes) { SharedData val2 = (((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null) ? recipe.m_item.m_itemData.m_shared : null); if (val2 == null || (int)val2.m_itemType != 9 || !hashSet.Contains(val2.m_ammoType)) { continue; } if (!OriginalArrowCosts.TryGetValue(((Object)recipe).name, out var value)) { value = new int[recipe.m_resources.Length]; for (int i = 0; i < value.Length; i++) { value[i] = recipe.m_resources[i].m_amount; } OriginalArrowCosts[((Object)recipe).name] = value; } for (int j = 0; j < recipe.m_resources.Length && j < value.Length; j++) { int num = value[j]; recipe.m_resources[j].m_amount = ((num <= 0) ? num : Mathf.Max(1, Mathf.RoundToInt((float)num * ArrowCost.Value))); } } } } public static class Loot { [HarmonyPatch(typeof(Ragdoll), "Awake")] private static class CorpseTimer { private static void Prefix(Ragdoll __instance) { if (QuickLoot.Value) { if (Shortened.Count > 64) { Prune(); } Shortened[__instance] = __instance.m_ttl; __instance.m_ttl = CorpseTime.Value; } } } [HarmonyPatch(typeof(Humanoid), "OnRagdollCreated")] private static class KeepPlayerCorpse { private static void Postfix(Humanoid __instance, Ragdoll ragdoll) { if (!((Object)(object)ragdoll == (Object)null) && Shortened.TryGetValue(ragdoll, out var value)) { Shortened.Remove(ragdoll); if (__instance is Player) { ragdoll.m_ttl = value; ((MonoBehaviour)ragdoll).CancelInvoke("DestroyNow"); ((MonoBehaviour)ragdoll).InvokeRepeating("DestroyNow", value, 1f); } } } } private const string Section = "06 - Loot"; public static Setting<bool> QuickLoot; public static Setting<float> CorpseTime; private static readonly Dictionary<Ragdoll, float> Shortened = new Dictionary<Ragdoll, float>(); public static void Init(ConfigFile cfg) { QuickLoot = ConfigSync.Bind(cfg, "06 - Loot", "Quick Loot Enabled", defaultValue: true, "Change how long creature corpses stay before turning into loot. Player corpses are not affected."); CorpseTime = ConfigSync.Bind(cfg, "06 - Loot", "Corpse Time", 0.5f, "Seconds until a corpse vanishes and drops its loot. Vanilla is a few seconds, depending on the creature.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f)); } private static void Prune() { List<Ragdoll> list = new List<Ragdoll>(); foreach (Ragdoll key in Shortened.Keys) { if ((Object)(object)key == (Object)null) { list.Add(key); } } foreach (Ragdoll item in list) { Shortened.Remove(item); } } } public static class MultiUserChest { [HarmonyPatch(typeof(Container), "RPC_RequestOpen")] private static class AllowSecondUser { private static bool Prefix(Container __instance, long uid, long playerID) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (!Enabled.Value || !__instance.m_nview.IsOwner() || uid == ZNet.GetUID()) { return true; } if ((!__instance.IsInUse() && (!((Object)(object)__instance.m_wagon != (Object)null) || !__instance.m_wagon.InUse())) || !__instance.CheckAccess(playerID)) { return true; } ZDOMan.instance.ForceSendZDO(uid, __instance.m_nview.GetZDO().m_uid); __instance.m_nview.InvokeRPC(uid, "RPC_OpenResponse", new object[1] { true }); return false; } } [HarmonyPatch(typeof(Container), "Awake")] private static class Register { private static void Postfix(Container __instance) { ZNetView nview = __instance.m_nview; if (!((Object)(object)nview == (Object)null) && nview.GetZDO() != null) { nview.Register<ZPackage>("Loxury_MUC", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { OnRequest(__instance, sender, pkg); }); nview.Register<ZPackage>("Loxury_MUC_Result", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { OnResult(__instance, pkg); }); } } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static class ViewerClicks { private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item, Vector2i pos, Modifier mod) { //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Invalid comparison between Unknown and I4 //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Invalid comparison between Unknown and I4 //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0117: 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_012a: 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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) Container current = Current; if (!IsViewer(current)) { return true; } Inventory inventory = current.GetInventory(); Inventory inventory2 = ((Humanoid)Player.m_localPlayer).GetInventory(); Inventory inventory3 = grid.GetInventory(); if (Object.op_Implicit((Object)(object)__instance.m_dragGo)) { ItemData dragItem = __instance.m_dragItem; Inventory dragInventory = __instance.m_dragInventory; int dragAmount = __instance.m_dragAmount; if (dragInventory != inventory && inventory3 != inventory) { return true; } if (dragItem == null || !dragInventory.ContainsItem(dragItem)) { __instance.SetupDragItem((ItemData)null, (Inventory)null, 1); return false; } if (dragInventory == inventory && inventory3 == inventory2) { ItemData itemAt = inventory2.GetItemAt(pos.x, pos.y); bool flag = itemAt == null || itemAt.IsSameType(dragItem); SendTake(current, dragItem, dragAmount, flag ? pos.x : (-1), flag ? pos.y : (-1)); } else if (dragInventory == inventory2 && inventory3 == inventory) { PutFromPlayer(current, dragItem, dragAmount, pos.x, pos.y); } else if (dragInventory == inventory && inventory3 == inventory) { SendMove(current, dragItem, dragAmount, pos.x, pos.y); } __instance.m_moveItemEffects.Create(((Component)__instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); __instance.SetupDragItem((ItemData)null, (Inventory)null, 1); return false; } if (item == null) { return true; } if ((int)mod == 2 && !item.m_shared.m_questItem) { if (inventory3 == inventory) { SendTake(current, item, item.m_stack, -1, -1); } else { PutFromPlayer(current, item, item.m_stack, -1, -1); } __instance.m_moveItemEffects.Create(((Component)__instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); return false; } if ((int)mod == 3 && inventory3 == inventory) { ((Character)Player.m_localPlayer).Message((MessageType)2, "Take the item out of the chest first", 0, (Sprite)null, false); return false; } return true; } } [HarmonyPatch(typeof(InventoryGui), "OnDropOutside")] private static class ViewerDropOutside { private static bool Prefix(InventoryGui __instance) { if (!Object.op_Implicit((Object)(object)__instance.m_dragGo) || !IsViewerInventory(__instance.m_dragInventory, out var _)) { return true; } ((Character)Player.m_localPlayer).Message((MessageType)2, "Take the item out of the chest first", 0, (Sprite)null, false); __instance.SetupDragItem((ItemData)null, (Inventory)null, 1); return false; } } [HarmonyPatch(typeof(Humanoid), "UseItem")] private static class ViewerUse { private static bool Prefix(Humanoid __instance, Inventory inventory) { if (!IsViewerInventory(inventory, out var _) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } ((Character)__instance).Message((MessageType)2, "Take the item out of the chest first", 0, (Sprite)null, false); return false; } } [HarmonyPatch(typeof(Humanoid), "DropItem")] private static class ViewerDrop { private static bool Prefix(Inventory inventory, ref bool __result) { if (!IsViewerInventory(inventory, out var _)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Container), "TakeAll")] private static class ViewerTakeAll { private static bool Prefix(Container __instance, ref bool __result) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if (!IsViewer(__instance)) { return true; } ZPackage val = new ZPackage(); val.Write(4); Send(__instance, val); __result = true; return false; } } [HarmonyPatch(typeof(Container), "StackAll")] private static class ViewerStackAll { private static bool Prefix(Container __instance) { if (!IsViewer(__instance)) { return true; } StackAllAsViewer(__instance, skipHotbar: false); return false; } } [HarmonyPatch(typeof(InventoryGui), "Update")] private static class FastRefresh { private static void Postfix() { Container current = Current; if ((Object)(object)current == (Object)null || (Object)(object)current.m_nview == (Object)null || !current.m_nview.IsValid()) { return; } if (Enabled.Value && current.m_nview.IsOwner() && !current.IsInUse()) { current.SetInUse(true); } if (IsViewer(current)) { _refresh -= Time.deltaTime; if (!(_refresh > 0f)) { _refresh = 0.15f; current.CheckForChanges(); } } } } [HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[] { typeof(Inventory), typeof(ItemData), typeof(int), typeof(int), typeof(int) })] private static class StaleMoveGuard { private static bool Prefix(Inventory fromInventory, ItemData item, ref bool __result) { if (fromInventory == null || item == null || fromInventory.ContainsItem(item)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Inventory), "MoveItemToThis", new Type[] { typeof(Inventory), typeof(ItemData) })] private static class StaleMoveGuardShift { private static bool Prefix(Inventory fromInventory, ItemData item) { if (fromInventory != null && item != null) { return fromInventory.ContainsItem(item); } return true; } } private const string Section = "22 - Multi-User Chests"; private const string RpcRequest = "Loxury_MUC"; private const string RpcResult = "Loxury_MUC_Result"; private const int OpTake = 1; private const int OpPut = 2; private const int OpMove = 3; private const int OpTakeAll = 4; private const int ResItems = 1; private const int ResReturned = 2; public static Setting<bool> Enabled; private static float _refresh; private static Container Current { get { if (!((Object)(object)InventoryGui.instance != (Object)null)) { return null; } return InventoryGui.instance.m_currentContainer; } } public static void Init(ConfigFile cfg) { Enabled = ConfigSync.Bind(cfg, "22 - Multi-User Chests", "Multi-User Chests", defaultValue: true, "Several players can have the same chest open at the same time. All players need Loxury."); } public static bool IsViewer(Container c) { if ((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.IsValid() && !c.m_nview.IsOwner()) { return (Object)(object)Current == (Object)(object)c; } return false; } private static bool IsViewerInventory(Inventory inv, out Container c) { c = Current; if (inv != null && IsViewer(c)) { return c.GetInventory() == inv; } return false; } private static ZPackage PackItems(List<ItemData> items) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown Inventory val = new Inventory("hearthbound_transfer", (Sprite)null, Mathf.Max(1, items.Count), 1); for (int i = 0; i < items.Count; i++) { items[i].m_gridPos = new Vector2i(i, 0); val.m_inventory.Add(items[i]); } ZPackage val2 = new ZPackage(); val.Save(val2); return val2; } private static List<ItemData> UnpackItems(ZPackage pkg) { //IL_0009: 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) Inventory val = new Inventory("hearthbound_transfer", (Sprite)null, 64, 1); val.Load(pkg); return new List<ItemData>(val.GetAllItems()); } private static void Send(Container c, ZPackage pkg) { c.m_nview.InvokeRPC("Loxury_MUC", new object[1] { pkg }); } private static void Reply(Container c, long to, int kind, List<ItemData> items, int x = -1, int y = -1) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown if (items.Count != 0) { ZPackage val = new ZPackage(); val.Write(kind); val.Write(x); val.Write(y); val.Write(PackItems(items)); c.m_nview.InvokeRPC(to, "Loxury_MUC_Result", new object[1] { val }); } } private static void OnRequest(Container c, long sender, ZPackage pkg) { //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) int num = pkg.ReadInt(); if (!c.m_nview.IsOwner()) { if (num == 2) { pkg.ReadInt(); pkg.ReadInt(); Reply(c, sender, 2, UnpackItems(pkg.ReadPackage())); } return; } c.Load(); Inventory inventory = c.GetInventory(); switch (num) { case 1: { int num2 = pkg.ReadInt(); int num3 = pkg.ReadInt(); int num4 = pkg.ReadInt(); string text = pkg.ReadString(); int x = pkg.ReadInt(); int y = pkg.ReadInt(); ItemData itemAt = inventory.GetItemAt(num2, num3); if (itemAt == null || itemAt.m_shared.m_name != text || num4 <= 0) { return; } Reply(c, sender, 1, new List<ItemData> { TakeFrom(inventory, itemAt, num4) }, x, y); break; } case 2: { int x2 = pkg.ReadInt(); int y2 = pkg.ReadInt(); List<ItemData> list2 = new List<ItemData>(); foreach (ItemData item in UnpackItems(pkg.ReadPackage())) { if (!PutInto(inventory, item, x2, y2)) { list2.Add(item); } } Reply(c, sender, 2, list2); break; } case 3: { int num5 = pkg.ReadInt(); int num6 = pkg.ReadInt(); int num7 = pkg.ReadInt(); int num8 = pkg.ReadInt(); int num9 = pkg.ReadInt(); string text2 = pkg.ReadString(); ItemData itemAt2 = inventory.GetItemAt(num5, num6); if (itemAt2 == null || itemAt2.m_shared.m_name != text2) { return; } ItemData itemAt3 = inventory.GetItemAt(num7, num8); if (itemAt3 != null && itemAt3 != itemAt2 && !itemAt3.IsSameType(itemAt2) && num9 >= itemAt2.m_stack) { itemAt3.m_gridPos = itemAt2.m_gridPos; itemAt2.m_gridPos = new Vector2i(num7, num8); } else if (itemAt3 != itemAt2) { inventory.MoveItemToThis(inventory, itemAt2, num9, num7, num8); } break; } case 4: { List<ItemData> list = new List<ItemData>(); foreach (ItemData item2 in new List<ItemData>(inventory.GetAllItems())) { list.Add(TakeFrom(inventory, item2, item2.m_stack)); } Reply(c, sender, 1, list); break; } } inventory.Changed(false, false); } private static ItemData TakeFrom(Inventory inv, ItemData item, int amount) { amount = Mathf.Min(amount, item.m_stack); ItemData obj = item.Clone(); obj.m_stack = amount; if (amount >= item.m_stack) { inv.RemoveItem(item); return obj; } inv.RemoveItem(item, amount); return obj; } private static bool PutInto(Inventory inv, ItemData item, int x, int y) { if (x >= 0 && y >= 0 && x < inv.GetWidth() && y < inv.GetHeight()) { inv.AddItem(item, item.m_stack, x, y, false); if (item.m_stack <= 0) { return true; } } return inv.AddItem(item); } private static void OnResult(Container c, ZPackage pkg) { //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } int num = pkg.ReadInt(); int x = pkg.ReadInt(); int y = pkg.ReadInt(); List<ItemData> list = UnpackItems(pkg.ReadPackage()); Inventory inventory = ((Humanoid)localPlayer).GetInventory(); List<ItemData> list2 = new List<ItemData>(); foreach (ItemData item in list) { if (!PutInto(inventory, item, x, y)) { list2.Add(item); } } if (num == 2) { ((Character)localPlayer).Message((MessageType)2, "Chest is full", 0, (Sprite)null, false); } if (list2.Count == 0) { return; } if (num == 1 && (Object)(object)c != (Object)null && c.m_nview.IsValid()) { ((Character)localPlayer).Message((MessageType)2, "$inventory_full", 0, (Sprite)null, false); SendPut(c, list2, -1, -1); return; } foreach (ItemData item2 in list2) { ItemDrop.DropItem(item2, item2.m_stack, ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward + Vector3.up, Quaternion.identity); } } private static void SendTake(Container c, ItemData item, int amount, int tx, int ty) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(1); val.Write(item.m_gridPos.x); val.Write(item.m_gridPos.y); val.Write(amount); val.Write(item.m_shared.m_name); val.Write(tx); val.Write(ty); Send(c, val); } private static void SendPut(Container c, List<ItemData> items, int x, int y) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(2); val.Write(x); val.Write(y); val.Write(PackItems(items)); Send(c, val); } public static void PutFromPlayer(Container c, ItemData item, int amount, int x, int y) { Player localPlayer = Player.m_localPlayer; Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory.ContainsItem(item) && !item.m_shared.m_questItem) { amount = Mathf.Clamp(amount, 1, item.m_stack); if (((Humanoid)localPlayer).IsItemEquiped(item)) { ((Humanoid)localPlayer).UnequipItem(item, false); } ((Humanoid)localPlayer).RemoveEquipAction(item); ItemData val; if (amount >= item.m_stack) { inventory.RemoveItem(item); val = item; } else { val = item.Clone(); val.m_stack = amount; inventory.RemoveItem(item, amount); } SendPut(c, new List<ItemData> { val }, x, y); } } private static void SendMove(Container c, ItemData item, int amount, int tx, int ty) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(3); val.Write(item.m_gridPos.x); val.Write(item.m_gridPos.y); val.Write(tx); val.Write(ty); val.Write(amount); val.Write(item.m_shared.m_name); Send(c, val); } public static int StackAllAsViewer(Container c, bool skipHotbar) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return 0; } int num = 0; Inventory inventory = c.GetInventory(); foreach (ItemData item in new List<ItemData>(((Humanoid)localPlayer).GetInventory().GetAllItems())) { if ((!skipHotbar || item.m_gridPos.y != 0) && !((Humanoid)localPlayer).IsItemEquiped(item) && !item.m_shared.m_questItem && inventory.ContainsItemByName(item.m_shared.m_name)) { num += item.m_stack; PutFromPlayer(c, item, item.m_stack, -1, -1); } } return num; } } public static class NearbyChests { [HarmonyPatch(typeof(Container), "Awake")] private static class Track { private static void Postfix(Container __instance) { All.Add(__instance); } } [HarmonyPatch(typeof(Container), "OnDestroyed")] private static class Untrack { private static void Postfix(Container __instance) { All.Remove(__instance); } } [HarmonyPatch(typeof(Player), "HaveRequirementItems")] private static class ScopeRecipe { private static void Prefix() { Enter(); } private static void Finalizer() { Exit(); } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] private static class ScopePiece { private static void Prefix() { Enter(); } private static void Finalizer() { Exit(); } } [HarmonyPatch(typeof(Player), "GetFirstRequiredItem")] private static class ScopeFirst { private static void Prefix() { Enter(); } private static void Finalizer() { Exit(); } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] private static class ScopeDisplay { private static void Prefix() { Enter(); } private static void Finalizer() { Exit(); } } [HarmonyPatch(typeof(Player), "ConsumeResources")] private static class ScopeConsume { private static void Prefix() { _consumeScope++; Enter(); } private static void Finalizer() { if (_consumeScope > 0) { _consumeScope--; } Exit(); } } [HarmonyPatch(typeof(Inventory), "CountItems")] private static class CountPatch { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (_countScope == 0 || _inner || _scopeChests == null || name == null || !IsPlayerInventory(__instance)) { return; } _inner = true; try { foreach (Container scopeChest in _scopeChests) { if ((Object)(object)scopeChest != (Object)null) { __result += scopeChest.GetInventory().CountItems(name, quality, matchWorldLevel); } } } finally { _inner = false; } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] private static class RemovePatch { private static void Prefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased) { if (_consumeScope == 0 || _inner || _scopeChests == null || !IsPlayerInventory(__instance)) { return; } _inner = true; try { int num = __instance.CountItems(name, itemQuality, worldLevelBased); int num2 = amount - num; if (num2 <= 0) { return; } foreach (Container scopeChest in _scopeChests) { if (num2 <= 0) { break; } if ((Object)(object)scopeChest == (Object)null || !scopeChest.m_nview.IsValid() || IsOpenedBySomeoneElse(scopeChest)) { continue; } Inventory inventory = scopeChest.GetInventory(); int num3 = inventory.CountItems(name, itemQuality, worldLevelBased); if (num3 > 0) { if (!scopeChest.m_nview.IsOwner()) { scopeChest.m_nview.ClaimOwnership(); } int num4 = Mathf.Min(num3, num2); inventory.RemoveItem(name, num4, itemQuality, worldLevelBased); num2 -= num4; } } amount = num; } finally { _inner = false; } } } [HarmonyPatch(typeof(Smelter), "OnAddFuel")] private static class SmelterFuel { private static void Prefix(Smelter __instance, Humanoid user, ItemData item) { if (item == null && __instance.GetFuel() <= (float)(__instance.m_maxFuel - 1)) { EnsureOne(user, __instance.m_fuelItem); } } } [HarmonyPatch(typeof(Smelter), "OnAddOre")] private static class SmelterOre { private static void Prefix(Smelter __instance, Humanoid user, ItemData item) { if (item != null || !IsLocal(user) || __instance.GetQueueSize() >= __instance.m_maxOre || __instance.FindCookableItem(user.GetInventory()) != null) { return; } foreach (ItemConversion item2 in __instance.m_conversion) { if ((Object)(object)item2.m_from != (Object)null && PullToPlayer(item2.m_from.m_itemData.m_shared.m_name) > 0) { break; } } } } [HarmonyPatch(typeof(Fireplace), "Interact")] private static class FireplaceFuel { private static void Prefix(Fireplace __instance, Humanoid user, bool hold, bool alt) { if (!hold && __instance.m_canRefill && !__instance.m_infiniteFuel && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && (!__instance.m_canTurnOff || alt || !(__instance.m_nview.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > 0f)) && !(__instance.m_nview.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) > __instance.m_maxFuel - 1f)) { EnsureOne(user, __instance.m_fuelItem); } } } [HarmonyPatch(typeof(CookingStation), "OnAddFuelSwitch")] private static class OvenFuel { private static void Prefix(CookingStation __instance, Humanoid user, ItemData item) { if (item == null && __instance.GetFuel() <= (float)(__instance.m_maxFuel - 1)) { EnsureOne(user, __instance.m_fuelItem); } } } [HarmonyPatch(typeof(CookingStation), "OnInteract")] private static class CookFromChest { private static void Prefix(CookingStation __instance, Humanoid user) { if (!IsLocal(user) || __instance.GetFreeSlot() < 0 || __instance.HaveDoneItem() || __instance.FindCookableItem(user.GetInventory()) != null) { return; } foreach (ItemConversion item in __instance.m_conversion) { if ((Object)(object)item.m_from != (Object)null && PullToPlayer(item.m_from.m_itemData.m_shared.m_name) > 0) { break; } } } } [HarmonyPatch(typeof(Player), "Update")] private static class QuickStackKeyPatch { private static void Postfix(Player __instance) { //IL_001f: 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) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !QuickStackEnabled.Value) { return; } KeyboardShortcut value = QuickStackKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Chat instance = Chat.instance; if ((instance == null || !instance.HasFocus()) && !Console.IsVisible() && !Menu.IsVisible() && !TextInput.IsVisible() && !Minimap.IsOpen()) { QuickStack(); } } } } [HarmonyPatch(typeof(Container), "RPC_StackResponse")] private static class StackResponse { private static bool Prefix(Container __instance, bool granted) { if (!PendingStack.Remove(__instance)) { return true; } if (!granted || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } _stackedTotal += StackInto(__instance.GetInventory(), ((Humanoid)Player.m_localPlayer).GetInventory()); return false; } } private const string Section = "15 - Nearby Chests"; public static Setting<bool> CraftFromChests; public static Setting<float> CraftRange; public static Setting<bool> QuickStackEnabled; public static Setting<float> QuickStackRange; public static Setting<bool> QuickStackSkipHotbar; public static ConfigEntry<KeyboardShortcut> QuickStackKey; private static readonly HashSet<Container> All = new HashSet<Container>(); private static int _countScope; private static int _consumeScope; private static bool _inner; private static List<Container> _scopeChests; private static List<Container> _cached; private static float _cachedAt = -1f; private static readonly HashSet<Container> PendingStack = new HashSet<Container>(); private static int _stackedTotal; private static bool _summaryScheduled; public static void Init(ConfigFile cfg) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown CraftFromChests = ConfigSync.Bind(cfg, "15 - Nearby Chests", "Craft From Nearby Chests", defaultValue: true, "Crafting, upgrading and building can use materials stored in nearby chests."); CraftRange = ConfigSync.Bind(cfg, "15 - Nearby Chests", "Craft Range", 20f, "Radius in meters around you in which chests count.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f)); QuickStackEnabled = ConfigSync.Bind(cfg, "15 - Nearby Chests", "Quick Stack Enabled", defaultValue: true, "Press the quick-stack key to put items into nearby chests that already contain the same item."); QuickStackRange = ConfigSync.Bind(cfg, "15 - Nearby Chests", "Quick Stack Range", 10f, "Radius in meters for quick-stack.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f)); QuickStackSkipHotbar = ConfigSync.Bind(cfg, "15 - Nearby Chests", "Quick Stack Skips Hotbar", defaultValue: true, "Items in your hotbar (top row) are never quick-stacked."); QuickStackKey = cfg.Bind<KeyboardShortcut>("15 - Nearby Chests", "Quick Stack Key", new KeyboardShortcut((KeyCode)103, Array.Empty<KeyCode>()), new ConfigDescription("Key for quick-stack. Personal setting, not controlled by the server.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes() })); } private static List<Container> Usable(float range, bool includeInUse = false) { //IL_002d: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) List<Container> list = new List<Container>(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return list; } long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); Vector3 position = ((Component)localPlayer).transform.position; All.RemoveWhere((Container c) => (Object)(object)c == (Object)null); foreach (Container item in All) { ZNetView nview = item.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && !((Object)(object)((Component)item).GetComponent<Piece>() == (Object)null) && !((Object)(object)((Component)item).GetComponent<TombStone>() != (Object)null) && !(Vector3.Distance(position, ((Component)item).transform.position) > range) && item.CheckAccess(playerID) && (!item.m_checkGuardStone || PrivateArea.CheckAccess(((Component)item).transform.position, 0f, false, false)) && (includeInUse || !IsOpenedBySomeoneElse(item))) { list.Add(item); } } return list; } private static bool IsOpenedBySomeoneElse(Container c) { if ((Object)(object)InventoryGui.instance != (Object)null && (Object)(object)InventoryGui.instance.m_currentContainer == (Object)(object)c) { return !c.m_nview.IsOwner(); } if (c.m_nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1) { return true; } if ((Object)(object)c.m_wagon != (Object)null) { return c.m_wagon.InUse(); } return false; } private static void Enter() { if (_countScope++ != 0) { return; } if (!CraftFromChests.Value) { _scopeChests = null; return; } if (_cached == null || Time.time - _cachedAt > 0.25f || _consumeScope > 0) { _cached = Usable(CraftRange.Value); _cachedAt = Time.time; } _scopeChests = _cached; } private static void Exit() { if (_countScope > 0 && --_countScope == 0) { _scopeChests = null; } } private static bool IsPlayerInventory(Inventory inv) { if ((Object)(object)Player.m_localPlayer != (Object)null) { return inv == ((Humanoid)Player.m_localPlayer).m_inventory; } return false; } public static int PullToPlayer(string itemName, int amount = 1) { Player localPlayer = Player.m_localPlayer; if (!CraftFromChests.Value || (Object)(object)localPlayer == (Object)null || amount <= 0) { return 0; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); int num = 0; foreach (Container item2 in Usable(CraftRange.Value)) { if (num >= amount) { break; } Inventory inventory2 = item2.GetInventory(); ItemData item = inventory2.GetItem(itemName, -1, false); if (item != null) { int num2 = Mathf.Min(item.m_stack, amount - num); ItemData val = item.Clone(); val.m_stack = num2; if (!inventory.CanAddItem(val, -1)) { ((Character)localPlayer).Message((MessageType)2, "$inventory_full", 0, (Sprite)null, false); break; } if (!item2.m_nview.IsOwner()) { item2.m_nview.ClaimOwnership(); } inventory2.RemoveItem(item, num2); inventory.AddItem(val); num += num2; } } return num; } private static bool IsLocal(Humanoid user) { if ((Object)(object)user != (Object)null) { return (Object)(object)user == (Object)(object)Player.m_localPlayer; } return false; } private static void EnsureOne(Humanoid user, ItemDrop fuelItem) { if (IsLocal(user) && !((Object)(object)fuelItem == (Object)null)) { string name = fuelItem.m_itemData.m_shared.m_name; if (!user.GetInventory().HaveItem(name, true)) { PullToPlayer(name); } } } public static void QuickStack() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || !QuickStackEnabled.Value) { return; } List<Container> list = Usable(QuickStackRange.Value, MultiUserChest.Enabled.Value); if (list.Count == 0) { ((Character)localPlayer).Message((MessageType)2, "No chests nearby", 0, (Sprite)null, false); return; } _stackedTotal = 0; foreach (Container item in list) { if (IsOpenedBySomeoneElse(item)) { _stackedTotal += MultiUserChest.StackAllAsViewer(item, QuickStackSkipHotbar.Value); continue; } PendingStack.Add(item); item.StackAll(); } if (!_summaryScheduled) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(ShowSummary()); } } private static IEnumerator ShowSummary() { _summaryScheduled = true; yield return (object)new WaitForSeconds(0.6f); _summaryScheduled = false; PendingStack.Clear(); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, (_stackedTotal > 0) ? $"Quick-stacked {_stackedTotal} items" : "Nothing to quick-stack", 0, (Sprite)null, false); } if (_stackedTotal > 0 && (Object)(object)Player.m_localPlayer != (Object)null) { InventoryGui.instance.m_moveItemEffects.Create(((Component)Player.m_localPlayer).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } private static int StackInto(Inventory chest, Inventory from) { int num = chest.CountItems((string)null, -1, true); foreach (ItemData item in new List<ItemData>(from.GetAllItems())) { if ((!QuickStackSkipHotbar.Value || item.m_gridPos.y != 0) && chest.ContainsItemByName(item.m_shared.m_name) && !((Humanoid)Player.m_localPlayer).IsItemEquiped(item) && chest.AddItem(item)) { from.RemoveItem(item); } } chest.Changed(false, false); from.Changed(false, false); return chest.CountItems((string)null, -1, true) - num; } } public static class PlayerTweaks { [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] private static class CarryWeight { private static void Postfix(ref float __result) { if (CarryEnabled.Value) { __result += CarryBonus.Value; } } } [HarmonyPatch(typeof(SEMan), "ModifyRunStaminaDrain")] private static class SprintStaminaPatch { private static void Postfix(SEMan __instance, ref float drain) { if (StaminaEnabled.Value && __instance.m_character is Player) { drain *= SprintStamina.Value; } } } [HarmonyPatch(typeof(SEMan), "ModifyJumpStaminaUsage")] private static class JumpStaminaPatch { private static void Postfix(SEMan __instance, ref float staminaUse) { if (StaminaEnabled.Value && __instance.m_character is Player) { staminaUse *= JumpStamina.Value; } } } [HarmonyPatch(typeof(SEMan), "ModifyFallDamage")] private static class FallDamagePatch { private static void Postfix(SEMan __instance, ref float damage) { if (StaminaEnabled.Value && __instance.m_character is Player) { damage *= FallDamage.Value; } } } [HarmonyPatch(typeof(Player), "UpdateFood")] private static class SmoothRegenPatch { private static void Prefix(Player __instance, bool forceUpdate) { if (SmoothRegen.Value && !forceUpdate) { __instance.m_foodRegenTimer = 0f; } } private static void Postfix(Player __instance, float dt, bool forceUpdate) { if (!SmoothRegen.Value || forceUpdate || ((Character)__instance).IsDead() || ((Character)__instance).GetHealth() >= ((Character)__instance).GetMaxHealth()) { return; } float num = 0f; foreach (Food food in __instance.m_foods) { num += food.m_item.m_shared.m_foodRegen; } if (!(num <= 0f)) { float num2 = 1f; ((Character)__instance).m_seman.ModifyHealthRegen(ref num2); ((Character)__instance).Heal(num * num2 * dt / 10f, false); } } } [HarmonyPatch(typeof(Player), "QueueEquipAction")] private static class InstantEquipPatch { private static bool Prefix(Player __instance, ItemData item) { if (!InstantEquip.Value || item == null || __instance.IsEquipActionQueued(item)) { return true; } ((Humanoid)__instance).EquipItem(item, true); return false; } } [HarmonyPatch(typeof(Player), "QueueUnequipAction")] private static class InstantUnequipPatch { private static bool Prefix(Player __instance, ItemData item) { if (!InstantEquip.Value || item == null || __instance.IsEquipActionQueued(item)) { return true; } ((Humanoid)__instance).UnequipItem(item, true); return false; } } [HarmonyPatch(typeof(Humanoid), "UpdateEquipment")] private static class SwimUpdateEquipment { private static void Prefix() { _inEquipmentUpdate = EquipInWater.Value; } private static void Finalizer() { _inEquipmentUpdate = false; } } [HarmonyPatch(typeof(Humanoid), "HideHandItems")] private static class SwimKeepHands {