using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HexBaseEnhancements.Features; using HexBaseEnhancements.Patches; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("HexNowYouRest")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("HexNowYouRest")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("2f19ccf3-f7b4-4290-8797-0a4a4d8f585b")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace HexBaseEnhancements { [BepInPlugin("com.hex.baseenhancements", "HexBaseEnhancements", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private const string PluginGuid = "com.hex.baseenhancements"; private const string PluginName = "HexBaseEnhancements"; private const string PluginVersion = "1.1.0"; private Harmony _harmonyInstance; private ConfigEntry _isRemoveWetDebuffEnabled; private ConfigEntry _isCustomRestedDelayEnabled; private ConfigEntry _restedDelayInSeconds; private ConfigEntry _isRapidHealthAndStaminaRegenEnabled; private ConfigEntry _isAutoRepairEnabled; private ConfigEntry _isDoorOpenFaster; private ConfigEntry _isDoorsAutoClose; private ConfigEntry _doorAutoCloseInSeconds; private ConfigEntry _isIncreasedComfortRadiusEnabled; private ConfigEntry _isAutoRepairSoundEnabled; private ConfigEntry _comfortRadiusInMeters; private ConfigEntry _isAutoFuelFireplacesEnabled; private ConfigEntry _fireplaceDetectionRadiusInMeters; internal static ManualLogSource Log; internal static Plugin Instance; internal static bool IsRemoveWetDebuffEnabled => Instance?._isRemoveWetDebuffEnabled?.Value == true; internal static bool IsCustomRestedDelayEnabled => Instance?._isCustomRestedDelayEnabled?.Value == true; internal static int RestedDelayInSeconds => Instance?._restedDelayInSeconds?.Value ?? 2; internal static bool IsRapidHealthAndStaminaRegenEnabled => Instance?._isRapidHealthAndStaminaRegenEnabled?.Value == true; internal static bool IsAutoRepairEnabled => Instance?._isAutoRepairEnabled?.Value == true; internal static bool DoorsOpensFaster => Instance?._isDoorOpenFaster?.Value == true; internal static bool DoorsAutoClose => Instance?._isDoorsAutoClose?.Value == true; internal static int DoorAutoCloseInSeconds => Instance?._doorAutoCloseInSeconds?.Value ?? 1; internal static bool IsIncreasedComfortRadiusEnabled => Instance?._isIncreasedComfortRadiusEnabled?.Value == true; internal static int ComfortRadiusInMeters => Instance?._comfortRadiusInMeters?.Value ?? 10; internal static bool IsAutoRepairSoundEnabled => Instance?._isAutoRepairSoundEnabled?.Value == true; internal static bool IsAutoFuelFireplacesEnabled => Instance?._isAutoFuelFireplacesEnabled?.Value == true; internal static int FireplaceDetectionRadiusInMeters => Instance?._fireplaceDetectionRadiusInMeters?.Value ?? 20; private void Awake() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; BindConfig(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmonyInstance = new Harmony("com.hex.baseenhancements"); _harmonyInstance.PatchAll(executingAssembly); Log.LogInfo((object)"HexBaseEnhancements v1.1.0 loaded."); } private void OnDestroy() { if (_isCustomRestedDelayEnabled != null) { _isCustomRestedDelayEnabled.SettingChanged -= OnCustomRestedDelayEnabledChanged; } if (_restedDelayInSeconds != null) { _restedDelayInSeconds.SettingChanged -= OnRestedDelaySecondsChanged; } Log.LogInfo((object)"HexBaseEnhancements v1.1.0 unloaded."); Harmony harmonyInstance = _harmonyInstance; if (harmonyInstance != null) { harmonyInstance.UnpatchSelf(); } _harmonyInstance = null; Instance = null; Log = null; } private void BindConfig() { //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown _isRemoveWetDebuffEnabled = ((BaseUnityPlugin)this).Config.Bind("Wet Debuff", "RemoveWetDebuff", true, "Removes the Wet status effect when the player is sheltered and near a fire."); _isCustomRestedDelayEnabled = ((BaseUnityPlugin)this).Config.Bind("Resting", "CustomRestedDelay", true, "Enables a custom delay before the player receives the Rested status effect."); _restedDelayInSeconds = ((BaseUnityPlugin)this).Config.Bind("Resting", "RestedDelayInSeconds", 2, "The number of seconds the player must rest before receiving the Rested status effect. Only used when CustomRestedDelay is enabled."); _isRapidHealthAndStaminaRegenEnabled = ((BaseUnityPlugin)this).Config.Bind("Health and Stamina Regen", "EnableRapidHealthAndStaminaRegen", true, "Rapidly restores health and stamina while the player is resting."); _isAutoRepairEnabled = ((BaseUnityPlugin)this).Config.Bind("Auto-Repair", "EnableAutoRepair", true, "Automatically repairs all repairable items in the player's inventory when the player is sheltered and near a fire."); _isDoorOpenFaster = ((BaseUnityPlugin)this).Config.Bind("Doors", "EnableDoorOpenFaster", true, "Makes doors open really fast."); _isDoorsAutoClose = ((BaseUnityPlugin)this).Config.Bind("Doors", "EnableDoorsAutoClose", true, "Automatically closes doors after a short delay when opened."); _doorAutoCloseInSeconds = ((BaseUnityPlugin)this).Config.Bind("Doors", "DoorAutoCloseInSeconds", 1, "The number of seconds after which doors will automatically close when opened. Only used when EnableDoorsAutoClose is enabled."); _isIncreasedComfortRadiusEnabled = ((BaseUnityPlugin)this).Config.Bind("Comfort", "EnableIncreasedComfortRadius", true, "Increases the radius used to detect nearby comfort-providing pieces."); _comfortRadiusInMeters = ((BaseUnityPlugin)this).Config.Bind("Comfort", "ComfortRadiusInMeters", 20, new ConfigDescription("The radius in meters used to detect nearby comfort-providing pieces. Only used when EnableIncreasedComfortRadius is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 50), Array.Empty())); _isAutoRepairSoundEnabled = ((BaseUnityPlugin)this).Config.Bind("Auto-Repair", "EnableAutoRepairSound", true, "Plays a sound when auto-repairing items. Only used when EnableAutoRepair is enabled."); _isAutoFuelFireplacesEnabled = ((BaseUnityPlugin)this).Config.Bind("Fireplaces", "EnableAutoFuelFireplaces", true, "Automatically adds fuel to nearby fireplaces when their fuel level is low."); _fireplaceDetectionRadiusInMeters = ((BaseUnityPlugin)this).Config.Bind("Fireplaces", "FireplaceDetectionRadiusInMeters", 20, new ConfigDescription("The radius in meters used to detect fireplaces for automatic fueling.", (AcceptableValueBase)(object)new AcceptableValueRange(5, 100), Array.Empty())); _isCustomRestedDelayEnabled.SettingChanged += OnCustomRestedDelayEnabledChanged; _restedDelayInSeconds.SettingChanged += OnRestedDelaySecondsChanged; } private void OnCustomRestedDelayEnabledChanged(object sender, EventArgs e) { PatchSECozySetup.ApplyRestedDelay(); } private void OnRestedDelaySecondsChanged(object sender, EventArgs e) { PatchSECozySetup.ApplyRestedDelay(); } } } namespace HexBaseEnhancements.Patches { [HarmonyPatch(typeof(Door), "SetState")] internal static class PatchDoorSetState { private const float VanillaAnimationSpeed = 1f; private const float FastOpenAnimationSpeed = 50f; [HarmonyPrefix] private static void Prefix(int state, Animator ___m_animator) { if (!((Object)(object)___m_animator == (Object)null)) { ___m_animator.speed = ((Plugin.DoorsOpensFaster && state != 0) ? 50f : 1f); } } [HarmonyPostfix] private static void Postfix(Door __instance, int state, ZNetView ___m_nview) { if (Plugin.DoorsAutoClose && !((Object)(object)__instance == (Object)null) && !((Object)(object)___m_nview == (Object)null) && state != 0 && ___m_nview.IsValid() && ___m_nview.IsOwner()) { ((MonoBehaviour)__instance).StartCoroutine(AutoCloseDoor(___m_nview)); } } private static IEnumerator AutoCloseDoor(ZNetView nview) { yield return (object)new WaitForSeconds((float)Plugin.DoorAutoCloseInSeconds); if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner()) { ZDO zDO = nview.GetZDO(); if (zDO != null && zDO.GetInt(ZDOVars.s_state, 0) != 0) { nview.InvokeRPC("UseDoor", new object[1] { false }); } } } } [HarmonyPatch(typeof(Fireplace), "Awake")] internal static class PatchFireplaceAwake { [HarmonyPostfix] private static void Postfix(Fireplace __instance) { AutoFuelFireplaces.Register(__instance); } } [HarmonyPatch(typeof(Player), "UpdateBaseValue")] internal static class PatchPlayerUpdateBaseValue { private static readonly FieldInfo BaseValueField = AccessTools.Field(typeof(Player), "m_baseValue"); private static readonly MethodInfo TryAutoFuelIfInBaseMethod = AccessTools.Method(typeof(PatchPlayerUpdateBaseValue), "TryAutoFuelIfInBase", (Type[])null, (Type[])null); [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown List list = instructions.ToList(); List list2 = (from x in list.Select((CodeInstruction instruction, int index2) => new { instruction = instruction, index = index2 }) where x.instruction.opcode == OpCodes.Stfld && object.Equals(x.instruction.operand, BaseValueField) select x.index).ToList(); if (list2.Count != 1) { return list; } int index = list2[0] + 1; list.InsertRange(index, (IEnumerable)(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)TryAutoFuelIfInBaseMethod) }); return list; } private static void TryAutoFuelIfInBase(Player player) { if (!((Object)(object)player == (Object)null) && !(BaseValueField == null) && (int)BaseValueField.GetValue(player) > 0) { AutoFuelFireplaces.TryAutoFuel(player); } } } [HarmonyPatch(typeof(Player), "UpdateCover")] internal static class PatchPlayerUpdateCover { private const float NearFireTimeout = 0.25f; private static readonly FieldInfo NearFireTimerField = AccessTools.Field(typeof(Player), "m_nearFireTimer"); private static readonly MethodInfo GetCoverForPointMethod = AccessTools.Method(typeof(Cover), "GetCoverForPoint", (Type[])null, (Type[])null); private static readonly MethodInfo ApplyBaseEnhancementsMethod = AccessTools.Method(typeof(PatchPlayerUpdateCover), "ApplyBaseEnhancements", (Type[])null, (Type[])null); [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown List list = instructions.ToList(); List list2 = (from x in list.Select((CodeInstruction instruction, int index2) => new { instruction = instruction, index = index2 }) where CodeInstructionExtensions.Calls(x.instruction, GetCoverForPointMethod) select x.index).ToList(); if (list2.Count != 1) { Plugin.Log.LogError((object)$"Player.UpdateCover transpiler expected 1 call to Cover.GetCoverForPoint, but found {list2.Count}. Patch not applied."); return list; } int index = list2[0] + 1; list.InsertRange(index, (IEnumerable)(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Call, (object)ApplyBaseEnhancementsMethod) }); return list; } private static void ApplyBaseEnhancements(Player player) { if (!((Object)(object)player == (Object)null) && !(NearFireTimerField == null) && player.InShelter() && !((float)NearFireTimerField.GetValue(player) >= 0.25f)) { RemoveWetDebuff(player); RestoreHealthAndStamina(player); RepairInventory(player); } } private static void RemoveWetDebuff(Player player) { if (Plugin.IsRemoveWetDebuffEnabled) { SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan != null && sEMan.HaveStatusEffect(SEMan.s_statusEffectWet)) { sEMan.RemoveStatusEffect(SEMan.s_statusEffectWet, false); } } } private static void RestoreHealthAndStamina(Player player) { if (Plugin.IsRapidHealthAndStaminaRegenEnabled) { ((Character)player).Heal(((Character)player).GetMaxHealth(), false); ((Character)player).AddStamina(((Character)player).GetMaxStamina()); } } private static void RepairInventory(Player player) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsAutoRepairEnabled) { return; } List allItems = ((Humanoid)player).GetInventory().GetAllItems(); bool flag = false; foreach (ItemData item in allItems) { if (item.m_shared.m_useDurability) { float maxDurability = item.GetMaxDurability(); if (!(item.m_durability >= maxDurability)) { item.m_durability = maxDurability; flag = true; } } } if (!flag) { return; } ((Character)player).Message((MessageType)1, "Inventory items repaired", 0, (Sprite)null); if (Plugin.IsAutoRepairSoundEnabled) { GameObject prefab = ZNetScene.instance.GetPrefab("sfx_gui_repairitem_workbench"); if ((Object)(object)prefab != (Object)null) { Object.Instantiate(prefab, ((Component)player).transform.position, Quaternion.identity); } } } } [HarmonyPatch(typeof(SE_Cozy), "Setup")] internal static class PatchSECozySetup { private const float VanillaRestedDelay = 10f; private static SE_Cozy _currentCozy; [HarmonyPostfix] private static void Postfix(SE_Cozy __instance) { if (!((Object)(object)__instance == (Object)null) && !(__instance.m_statusEffect != "Rested")) { _currentCozy = __instance; ApplyRestedDelay(); } } internal static void ApplyRestedDelay() { if (!((Object)(object)_currentCozy == (Object)null)) { _currentCozy.m_delay = (Plugin.IsCustomRestedDelayEnabled ? ((float)Plugin.RestedDelayInSeconds) : 10f); } } } [HarmonyPatch(typeof(SE_Rested), "GetNearbyComfortPieces")] internal static class PatchSERestedGetNearbyComfortPieces { private const float VanillaComfortRadius = 10f; private static readonly FieldInfo TempPiecesField = AccessTools.Field(typeof(SE_Rested), "s_tempPieces"); private static readonly MethodInfo GetAllComfortPiecesInRadiusMethod = AccessTools.Method(typeof(Piece), "GetAllComfortPiecesInRadius", (Type[])null, (Type[])null); private static readonly MethodInfo GetComfortRadiusMethod = AccessTools.Method(typeof(PatchSERestedGetNearbyComfortPieces), "GetComfortRadius", (Type[])null, (Type[])null); [HarmonyTranspiler] internal static IEnumerable Transpiler(IEnumerable instructions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null); val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[3] { new CodeMatch((OpCode?)OpCodes.Ldc_R4, (object)10f, (string)null), new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)TempPiecesField, (string)null), new CodeMatch((Func)((CodeInstruction instruction) => CodeInstructionExtensions.Calls(instruction, GetAllComfortPiecesInRadiusMethod)), (string)null) }); if (!val.IsValid) { Plugin.Log.LogError((object)"SE_Rested.GetNearbyComfortPieces transpiler could not locate the comfort radius call. Patch not applied."); return val.InstructionEnumeration(); } val.Instruction.opcode = OpCodes.Call; val.Instruction.operand = GetComfortRadiusMethod; return val.InstructionEnumeration(); } private static float GetComfortRadius() { if (!Plugin.IsIncreasedComfortRadiusEnabled) { return 10f; } return Plugin.ComfortRadiusInMeters; } } } namespace HexBaseEnhancements.Features { internal static class AutoFuelFireplaces { private const float AutoFuelThreshold = 1f; private const float MissingFuelMessageCooldown = 10f; private static float _nextMissingFuelMessageTime; private static int _autoFuelScanMask; private static readonly Collider[] ScanColliders = (Collider[])(object)new Collider[8192]; private static readonly HashSet Fireplaces = new HashSet(); private static readonly FieldInfo NViewField = AccessTools.Field(typeof(Fireplace), "m_nview"); internal static void TryAutoFuel(Player player) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsAutoFuelFireplacesEnabled || (Object)(object)player == (Object)null) { return; } int num = Physics.OverlapSphereNonAlloc(((Component)player).transform.position, (float)Plugin.FireplaceDetectionRadiusInMeters, ScanColliders, GetAutoFuelScanMask(), (QueryTriggerInteraction)2); HashSet hashSet = new HashSet(); for (int i = 0; i < num; i++) { Collider val = ScanColliders[i]; if (!((Object)(object)val == (Object)null)) { Container componentInParent = ((Component)val).GetComponentInParent(); if ((Object)(object)componentInParent != (Object)null) { hashSet.Add(componentInParent); } } } int fireplaceDetectionRadiusInMeters = Plugin.FireplaceDetectionRadiusInMeters; int num2 = fireplaceDetectionRadiusInMeters * fireplaceDetectionRadiusInMeters; Vector3 position = ((Component)player).transform.position; foreach (Fireplace fireplace in Fireplaces) { if (!((Object)(object)fireplace == (Object)null)) { Vector3 val2 = ((Component)fireplace).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > (float)num2)) { TryFuelFireplace(player, fireplace, hashSet); } } } } internal static void Register(Fireplace fireplace) { if (!((Object)(object)fireplace == (Object)null)) { Fireplaces.Add(fireplace); object? obj = NViewField?.GetValue(fireplace); object? obj2 = ((obj is ZNetView) ? obj : null); float? obj3; if (obj2 == null) { obj3 = null; } else { ZDO zDO = ((ZNetView)obj2).GetZDO(); obj3 = ((zDO != null) ? new float?(zDO.GetFloat(ZDOVars.s_fuel, -1f)) : ((float?)null)); } _ = obj3 ?? (-1f); } } private static void TryFuelFireplace(Player player, Fireplace fireplace, IEnumerable containers) { if ((Object)(object)fireplace == (Object)null || fireplace.m_infiniteFuel || !fireplace.m_canRefill || (Object)(object)fireplace.m_fuelItem == (Object)null) { return; } object? obj = NViewField?.GetValue(fireplace); ZNetView val = (ZNetView)((obj is ZNetView) ? obj : null); if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner()) { return; } ZDO zDO = val.GetZDO(); if (zDO == null) { return; } float num = zDO.GetFloat(ZDOVars.s_fuel, 0f); if (num > 1f) { return; } string name = fireplace.m_fuelItem.m_itemData.m_shared.m_name; int num2 = Mathf.CeilToInt(fireplace.m_maxFuel - num); if (num2 <= 0) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } int num3 = inventory.CountItems(name, -1, true); int num4 = CountFuelInContainers(containers, name); int num5 = num3 + num4; if (num5 <= 0) { ShowMissingFuelMessage(player, name); return; } int num6 = Mathf.Min(num2, num5); int num7 = 0; int num8 = Mathf.Min(num3, num6); if (num8 > 0) { int num9 = inventory.CountItems(name, -1, true); inventory.RemoveItem(name, num8, -1, true); int num10 = inventory.CountItems(name, -1, true); int num11 = num9 - num10; num7 += num11; num6 -= num11; } if (num6 > 0) { num7 += RemoveFuelFromContainers(containers, name, num6); } if (num7 > 0) { float fuel = Mathf.Min(fireplace.m_maxFuel, num + (float)num7); fireplace.SetFuel(fuel); } } private static int CountFuelInContainers(IEnumerable containers, string fuelName) { int num = 0; foreach (Container container in containers) { if (!((Object)(object)container == (Object)null)) { Inventory inventory = container.GetInventory(); if (inventory != null) { int num2 = inventory.CountItems(fuelName, -1, true); num += num2; } } } return num; } private static int RemoveFuelFromContainers(IEnumerable containers, string fuelName, int amount) { int num = amount; int num2 = 0; foreach (Container container in containers) { if (num <= 0) { break; } if ((Object)(object)container == (Object)null) { continue; } Inventory inventory = container.GetInventory(); if (inventory != null) { int num3 = inventory.CountItems(fuelName, -1, true); if (num3 > 0) { int num4 = Mathf.Min(num3, num); inventory.RemoveItem(fuelName, num4, -1, true); int num5 = inventory.CountItems(fuelName, -1, true); int num6 = num3 - num5; num2 += num6; num -= num6; } } } return num2; } private static void ShowMissingFuelMessage(Player player, string fuelName) { if (!(Time.time < _nextMissingFuelMessageTime)) { _nextMissingFuelMessageTime = Time.time + 10f; ((Character)player).Message((MessageType)1, "$msg_outof " + fuelName, 0, (Sprite)null); } } private static int GetAutoFuelScanMask() { if (_autoFuelScanMask == 0) { _autoFuelScanMask = LayerMask.GetMask(new string[2] { "piece", "piece_nonsolid" }); } return _autoFuelScanMask; } } }