From 0c58db1f62440bdfb39015f6cae0cb53d15f0637 Mon Sep 17 00:00:00 2001 From: LabodiDavid Date: Sun, 13 Sep 2026 21:39:38 +0200 Subject: [PATCH] Added repair fires feature - v1.2.2 --- AutoRefillFires.csproj | 2 +- Configuration/ModConfig.cs | 18 ++++-- README.md | 23 +++++++- Refill/FireplaceRefiller.cs | 50 +--------------- Refill/FireplaceTypeFilter.cs | 55 ++++++++++++++++++ Refill/HotTubRefiller.cs | 7 ++- Refill/RefillManager.cs | 9 ++- Repair/RepairManager.cs | 104 ++++++++++++++++++++++++++++++++++ Thunderstore/CHANGELOG.md | 15 +++++ Thunderstore/README.md | 23 +++++++- Thunderstore/manifest.json | 2 +- 11 files changed, 247 insertions(+), 61 deletions(-) create mode 100644 Refill/FireplaceTypeFilter.cs create mode 100644 Repair/RepairManager.cs diff --git a/AutoRefillFires.csproj b/AutoRefillFires.csproj index 5973c22..9241502 100644 --- a/AutoRefillFires.csproj +++ b/AutoRefillFires.csproj @@ -6,7 +6,7 @@ AutoRefillFires latest - 1.2.1 + 1.2.2 D:\SteamLibrary\steamapps\common\Valheim Default diff --git a/Configuration/ModConfig.cs b/Configuration/ModConfig.cs index 41bf103..3624fb8 100644 --- a/Configuration/ModConfig.cs +++ b/Configuration/ModConfig.cs @@ -1,4 +1,4 @@ -using BepInEx.Configuration; +using BepInEx.Configuration; using UnityEngine; namespace AutoRefillFires @@ -28,13 +28,18 @@ namespace AutoRefillFires public ConfigEntry FuelSourcePriority { get; } public ConfigEntry OnlyRefillOwnPieces { get; } + + public ConfigEntry AutoRepair { get; } + public ConfigEntry RepairBelowPercent { get; } + public ConfigEntry OnlyRepairOwnPieces { get; } + public ConfigEntry ToggleHotkey { get; } public ConfigEntry LogLevel { get; } public ModConfig(ConfigFile config) { - Radius = config.Bind("General", "Radius", 20f, "Refill fireplaces within this radius around the player."); - CheckInterval = config.Bind("General", "CheckInterval", 3f, "How often fireplaces are checked, in seconds."); + Radius = config.Bind("General", "Radius", 20f, "Refill and repair supported objects within this radius around the player."); + CheckInterval = config.Bind("General", "CheckInterval", 3f, "How often supported objects are checked, in seconds."); RefillBelowPercent = config.Bind("General", "RefillBelowPercent", 0.75f, "Refill when fuel drops below this percentage."); RefillToMax = config.Bind("General", "RefillToMax", true, "If true, refill fireplaces to maximum fuel. If false, add only RefillAmount fuel."); RefillAmount = config.Bind("General", "RefillAmount", 5, "Amount of fuel to add when RefillToMax is false."); @@ -51,10 +56,15 @@ namespace AutoRefillFires FillHotTubs = config.Bind("Fireplace Types", "FillHotTubs", true, "Automatically refill hot tubs."); FillOtherFireplaces = config.Bind("Fireplace Types", "FillOtherFireplaces", true, "Automatically refill unknown or modded Fireplace-based objects."); - ToggleHotkey = config.Bind("General", "ToggleHotkey", new KeyboardShortcut(KeyCode.F7), "Hotkey used to enable or disable automatic refilling."); + ToggleHotkey = config.Bind("General", "ToggleHotkey", new KeyboardShortcut(KeyCode.F7), "Hotkey used to enable or disable automatic refilling and repair."); KeepFuelReserve = config.Bind("Fuel", "KeepFuelReserve", 0, "Minimum amount of fuel to keep in the source inventory. 10 means the last 10 Wood/Resin will not be used."); FuelSourcePriority = config.Bind("Fuel", "FuelSourcePriority", AutoRefillFires.FuelSourcePriority.PlayerFirst, "Select whether player inventory or nearby containers should be used first."); OnlyRefillOwnPieces = config.Bind("General", "OnlyRefillOwnPieces", false, "If enabled, only refill fireplaces and torches built by the local player."); + + AutoRepair = config.Bind("Repair", "AutoRepair", false, "Automatically repair supported objects whose corresponding Fill* option is enabled."); + RepairBelowPercent = config.Bind("Repair", "RepairBelowPercent", 0.75f, "Repair supported objects when their health drops below this percentage. Example: 0.75 = 75%."); + OnlyRepairOwnPieces = config.Bind("Repair", "OnlyRepairOwnPieces", false, "If enabled, only automatically repair supported objects built by the local player."); + LogLevel = config.Bind("Logging", "LogLevel", ModLogLevel.Info, "Logging verbosity. Available values: None, Error, Warning, Info, Debug."); } } diff --git a/README.md b/README.md index b105c34..af7d6fc 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,11 @@ A lightweight BepInEx mod for Valheim that automatically refills nearby fireplaces and torches. -## What's New in 1.2.1 +## What's New in 1.2.2 ##### [Full CHANGELOG](https://github.com/LabodiDavid/AutoRefillFires/blob/main/Thunderstore/CHANGELOG.md) -- Support for Valheim 1.0 Deep North update +- Added optional automatic repair for all supported refillable object types. +- Repair follows the existing `Fill*` type toggles. +- Added configurable repair health threshold and own-piece restriction. ## Features @@ -14,6 +16,7 @@ A lightweight BepInEx mod for Valheim that automatically refills nearby fireplac - Configurable refill radius and threshold - Refill to maximum or by a fixed amount - Individual enable/disable options for fireplace types +- Optional automatic repair for supported refillable objects; repair follows the existing `Fill*` type toggles ## Configuration @@ -31,6 +34,22 @@ Pressing the hotkey toggles Auto Refill Fires on or off. When disabled, the configuration file is automatically reloaded, allowing most settings to be changed without restarting Valheim. +### Auto Repair Fires + +Automatic repair is disabled by default. Enable it with: + +`AutoRepair = true` in the config file. + +By default supported objects are repaired when their health falls below 75%: + +`RepairBelowPercent = 0.75` + +Repair follows the existing `Fill*` type toggles. For example, if `FillHearths = true`, Hearths can also be repaired; if `FillHearths = false`, they are ignored by both refill and repair. The same rule applies to campfires, standing/wall torches, braziers, bonfires, Hot Tubs and compatible `OtherFireplaces`. + +Set `OnlyRepairOwnPieces = true` to restrict automatic repair to pieces built by the local player. + +The repair logic reuses the existing fireplace and Hot Tub scans, so enabling repair does not add another scene-wide search. Resin candles remain ignored because they are consumable, non-refillable light sources. + ### Fuel Reserve Example: diff --git a/Refill/FireplaceRefiller.cs b/Refill/FireplaceRefiller.cs index 005f1f9..bb8c82f 100644 --- a/Refill/FireplaceRefiller.cs +++ b/Refill/FireplaceRefiller.cs @@ -1,4 +1,4 @@ -using UnityEngine; +using UnityEngine; namespace AutoRefillFires { @@ -130,55 +130,11 @@ namespace AutoRefillFires private bool ShouldRefillFireplace(Fireplace fireplace) { - string objectName = fireplace.gameObject.name.Replace("(Clone)", "").ToLowerInvariant(); - - bool result; - string matchedRule; - - if (objectName.Contains("candle")) - { - result = false; - matchedRule = "IgnoredCandle"; - } - if (objectName.Contains("fire_pit") || objectName.Contains("firepit")) - { - result = _config.FillCampfires.Value; - matchedRule = "FillCampfires"; - } - else if (objectName.Contains("hearth")) - { - result = _config.FillHearths.Value; - matchedRule = "FillHearths"; - } - else if (objectName.Contains("groundtorch")) - { - result = _config.FillStandingTorches.Value; - matchedRule = "FillStandingTorches"; - } - else if (objectName.Contains("walltorch")) - { - result = _config.FillWallTorches.Value; - matchedRule = "FillWallTorches"; - } - else if (objectName.Contains("brazier")) - { - result = _config.FillBraziers.Value; - matchedRule = "FillBraziers"; - } - else if (objectName.Contains("bonfire")) - { - result = _config.FillBonfires.Value; - matchedRule = "FillBonfires"; - } - else - { - result = _config.FillOtherFireplaces.Value; - matchedRule = "FillOtherFireplaces"; - } + bool result = FireplaceTypeFilter.IsEnabled(_config, fireplace, out string matchedRule); _log.Debug( $"ShouldRefillFireplace: name='{fireplace.gameObject.name}', " + - $"normalized='{objectName}', matchedRule={matchedRule}, result={result}" + $"matchedRule={matchedRule}, result={result}" ); return result; diff --git a/Refill/FireplaceTypeFilter.cs b/Refill/FireplaceTypeFilter.cs new file mode 100644 index 0000000..9395e27 --- /dev/null +++ b/Refill/FireplaceTypeFilter.cs @@ -0,0 +1,55 @@ +namespace AutoRefillFires +{ + internal static class FireplaceTypeFilter + { + public static bool IsEnabled(ModConfig config, Fireplace fireplace, out string matchedRule) + { + string objectName = fireplace.gameObject.name.Replace("(Clone)", "").ToLowerInvariant(); + + if (objectName.Contains("candle")) + { + matchedRule = "IgnoredCandle"; + return false; + } + + if (objectName.Contains("fire_pit") || objectName.Contains("firepit")) + { + matchedRule = "FillCampfires"; + return config.FillCampfires.Value; + } + + if (objectName.Contains("hearth")) + { + matchedRule = "FillHearths"; + return config.FillHearths.Value; + } + + if (objectName.Contains("groundtorch")) + { + matchedRule = "FillStandingTorches"; + return config.FillStandingTorches.Value; + } + + if (objectName.Contains("walltorch")) + { + matchedRule = "FillWallTorches"; + return config.FillWallTorches.Value; + } + + if (objectName.Contains("brazier")) + { + matchedRule = "FillBraziers"; + return config.FillBraziers.Value; + } + + if (objectName.Contains("bonfire")) + { + matchedRule = "FillBonfires"; + return config.FillBonfires.Value; + } + + matchedRule = "FillOtherFireplaces"; + return config.FillOtherFireplaces.Value; + } + } +} diff --git a/Refill/HotTubRefiller.cs b/Refill/HotTubRefiller.cs index 8e7bdd3..54ef7be 100644 --- a/Refill/HotTubRefiller.cs +++ b/Refill/HotTubRefiller.cs @@ -1,4 +1,4 @@ -using UnityEngine; +using UnityEngine; namespace AutoRefillFires { @@ -6,12 +6,14 @@ namespace AutoRefillFires { private readonly ModConfig _config; private readonly FuelManager _fuelManager; + private readonly RepairManager _repairManager; private readonly ModLogger _log; - public HotTubRefiller(ModConfig config, FuelManager fuelManager, ModLogger log) + public HotTubRefiller(ModConfig config, FuelManager fuelManager, RepairManager repairManager, ModLogger log) { _config = config; _fuelManager = fuelManager; + _repairManager = repairManager; _log = log; } @@ -41,6 +43,7 @@ namespace AutoRefillFires $"distance={distance:0.0}m" ); + _repairManager.TryRepair(player, smelter, "hot tub"); TryRefillHotTub(player, smelter, containers); } diff --git a/Refill/RefillManager.cs b/Refill/RefillManager.cs index 2b4aaf8..fc1f7fa 100644 --- a/Refill/RefillManager.cs +++ b/Refill/RefillManager.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using UnityEngine; namespace AutoRefillFires @@ -8,6 +8,7 @@ namespace AutoRefillFires private readonly ModConfig _config; private readonly FireplaceRefiller _fireplaceRefiller; private readonly HotTubRefiller _hotTubRefiller; + private readonly RepairManager _repairManager; private readonly ModLogger _log; public RefillManager(ModConfig config, ModLogger log) @@ -17,7 +18,8 @@ namespace AutoRefillFires FuelManager fuelManager = new FuelManager(_config, _log); _fireplaceRefiller = new FireplaceRefiller(_config, fuelManager, _log); - _hotTubRefiller = new HotTubRefiller(_config, fuelManager, _log); + _repairManager = new RepairManager(_config, _log); + _hotTubRefiller = new HotTubRefiller(_config, fuelManager, _repairManager, _log); } public void Run(Player player) @@ -100,6 +102,9 @@ namespace AutoRefillFires $"distance={candidate.Distance:0.0}m" ); + if (FireplaceTypeFilter.IsEnabled(_config, candidate.Fireplace, out string matchedRule)) + _repairManager.TryRepair(player, candidate.Fireplace, matchedRule); + _fireplaceRefiller.TryRefill(player, candidate.Fireplace, containers); } diff --git a/Repair/RepairManager.cs b/Repair/RepairManager.cs new file mode 100644 index 0000000..baab081 --- /dev/null +++ b/Repair/RepairManager.cs @@ -0,0 +1,104 @@ +using UnityEngine; + +namespace AutoRefillFires +{ + internal class RepairManager + { + private readonly ModConfig _config; + private readonly ModLogger _log; + + public RepairManager(ModConfig config, ModLogger log) + { + _config = config; + _log = log; + } + + public void TryRepair(Player player, Component target, string label) + { + if (!_config.AutoRepair.Value || target == null) + return; + + if (_config.OnlyRepairOwnPieces.Value && !IsOwnPiece(player, target)) + { + _log.Debug($"Skipping repair for '{target.gameObject.name}' - not owned by local player."); + return; + } + + WearNTear wearNTear = target.GetComponent(); + + if (wearNTear == null) + wearNTear = target.GetComponentInParent(); + + if (wearNTear == null) + { + _log.Debug($"Skipping repair for '{target.gameObject.name}' - no WearNTear found."); + return; + } + + ZNetView nview = wearNTear.GetComponent(); + + if (nview == null) + nview = wearNTear.GetComponentInParent(); + + if (nview == null || !nview.IsValid()) + { + _log.Debug($"Skipping repair for '{target.gameObject.name}' - invalid ZNetView."); + return; + } + + ZDO zdo = nview.GetZDO(); + + if (zdo == null) + { + _log.Debug($"Skipping repair for '{target.gameObject.name}' - ZDO is null."); + return; + } + + float maxHealth = wearNTear.m_health; + + if (maxHealth <= 0f) + { + _log.Debug($"Skipping repair for '{target.gameObject.name}' - max health <= 0 ({maxHealth})."); + return; + } + + float currentHealth = zdo.GetFloat(ZDOVars.s_health, maxHealth); + float healthPercent = currentHealth / maxHealth; + float threshold = Mathf.Clamp01(_config.RepairBelowPercent.Value); + + _log.Debug( + $"Repair state: type={label}, name='{target.gameObject.name}', " + + $"health={currentHealth:0.0}/{maxHealth:0.0} ({healthPercent * 100f:0.0}%), " + + $"threshold={threshold * 100f:0.0}%" + ); + + if (healthPercent >= threshold) + return; + + if (wearNTear.Repair()) + { + _log.Info( + $"Repaired {label}: {target.gameObject.name}, " + + $"{currentHealth:0.0}/{maxHealth:0.0} ({healthPercent * 100f:0.0}% -> 100%)" + ); + } + else + { + _log.Debug($"Repair request for '{target.gameObject.name}' returned false."); + } + } + + private static bool IsOwnPiece(Player player, Component target) + { + Piece piece = target.GetComponent(); + + if (piece == null) + piece = target.GetComponentInParent(); + + if (piece == null) + return true; + + return piece.GetCreator() == player.GetPlayerID(); + } + } +} diff --git a/Thunderstore/CHANGELOG.md b/Thunderstore/CHANGELOG.md index 107acf2..6251556 100644 --- a/Thunderstore/CHANGELOG.md +++ b/Thunderstore/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to Auto Refill Fires will be documented in this file. +## [1.2.2] - 2026-09-13 + +### Added + +- Added optional automatic repair for all supported refillable object types. +- Added `AutoRepair` master configuration option. +- Repair follows the existing `FillCampfires`, `FillHearths`, `FillStandingTorches`, `FillWallTorches`, `FillBraziers`, `FillBonfires`, `FillHotTubs` and `FillOtherFireplaces` toggles. +- Added `RepairBelowPercent` configuration option to control the health threshold that triggers repair. +- Added `OnlyRepairOwnPieces` configuration option. +- Repair reuses the existing fireplace and Hot Tub scans and does not add another scene-wide object search. + +### Fixed + +- Fixed Resin candles being able to fall through to `FillOtherFireplaces` despite the candle ignore rule. + ## [1.2.1] - 2026-09-09 ### Added diff --git a/Thunderstore/README.md b/Thunderstore/README.md index b105c34..ac21fb4 100644 --- a/Thunderstore/README.md +++ b/Thunderstore/README.md @@ -3,9 +3,11 @@ A lightweight BepInEx mod for Valheim that automatically refills nearby fireplaces and torches. -## What's New in 1.2.1 +## What's New in 1.2.2 ##### [Full CHANGELOG](https://github.com/LabodiDavid/AutoRefillFires/blob/main/Thunderstore/CHANGELOG.md) -- Support for Valheim 1.0 Deep North update +- Added optional automatic repair for all supported refillable object types. +- Repair follows the existing `Fill*` type toggles, including Hot Tubs. +- Added configurable repair health threshold and own-piece restriction. ## Features @@ -14,6 +16,7 @@ A lightweight BepInEx mod for Valheim that automatically refills nearby fireplac - Configurable refill radius and threshold - Refill to maximum or by a fixed amount - Individual enable/disable options for fireplace types +- Optional automatic repair for supported refillable objects; repair follows the existing `Fill*` type toggles ## Configuration @@ -31,6 +34,22 @@ Pressing the hotkey toggles Auto Refill Fires on or off. When disabled, the configuration file is automatically reloaded, allowing most settings to be changed without restarting Valheim. +### Auto Repair Fires + +Automatic repair is disabled by default. Enable it with: + +`AutoRepair = true` in the config file. + +By default supported objects are repaired when their health falls below 75%: + +`RepairBelowPercent = 0.75` + +Repair follows the existing `Fill*` type toggles. For example, if `FillHearths = true`, Hearths can also be repaired; if `FillHearths = false`, they are ignored by both refill and repair. The same rule applies to campfires, standing/wall torches, braziers, bonfires, Hot Tubs and compatible `OtherFireplaces`. + +Set `OnlyRepairOwnPieces = true` to restrict automatic repair to pieces built by the local player. + +The repair logic reuses the existing fireplace and Hot Tub scans, so enabling repair does not add another scene-wide search. Resin candles remain ignored because they are consumable, non-refillable light sources. + ### Fuel Reserve Example: diff --git a/Thunderstore/manifest.json b/Thunderstore/manifest.json index 172d770..ffb2bd2 100644 --- a/Thunderstore/manifest.json +++ b/Thunderstore/manifest.json @@ -2,7 +2,7 @@ "name": "AutoRefillFires", "version_number": "__VERSION__", "website_url": "https://github.com/LabodiDavid/AutoRefillFires", - "description": "Automatically refills nearby campfires, hearths and torches using fuel from the player inventory or optionally nearby containers.", + "description": "Automatically refills nearby fires from player or container fuel, with optional automatic repair for supported refillable objects.", "dependencies": [ "denikson-BepInExPack_Valheim-5.4.2350" ]