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 RoofSwap v1.0.1
RoofSwap.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("RoofSwap")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RoofSwap")] [assembly: AssemblyTitle("RoofSwap")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RoofSwap { [BepInPlugin("pieter.valheim.roofswap", "Roof Swap", "1.0.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "pieter.valheim.roofswap"; public const string Name = "Roof Swap"; public const string Version = "1.0.1"; private const string ToolPrefab = "piece_roofswap"; private const string ThatchPrefix = "wood_roof"; private const string ShinglePrefix = "darkwood_roof"; private static ConfigEntry<KeyboardShortcut> _wholeRoofKey; private static ConfigEntry<int> _maxPieces; private static ConfigEntry<float> _chestRange; private static ManualLogSource _log; private static readonly HashSet<Container> Containers = new HashSet<Container>(); private static readonly MethodInfo CheckAccessMethod = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); private static readonly MethodInfo BuildStamina = AccessTools.Method(typeof(Player), "GetBuildStamina", (Type[])null, (Type[])null); private static readonly FieldRef<Player, bool> NoPlacementCost = AccessTools.FieldRefAccess<Player, bool>("m_noPlacementCost"); private void Awake() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown _log = ((BaseUnityPlugin)this).Logger; _wholeRoofKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Roof Swap", "Whole roof key", new KeyboardShortcut((KeyCode)304, Array.Empty<KeyCode>()), "Hold this while clicking with Roof Swap to swap every connected thatch roof piece at once."); _maxPieces = ((BaseUnityPlugin)this).Config.Bind<int>("Roof Swap", "Max pieces per whole-roof swap", 300, new ConfigDescription("Safety limit for one whole-roof swap.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 2000), Array.Empty<object>())); _chestRange = ((BaseUnityPlugin)this).Config.Bind<float>("Roof Swap", "Chest range (m)", 50f, new ConfigDescription("Nearby chests within this range pay for missing materials.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); PrefabManager.OnVanillaPrefabsAvailable += AddTool; Harmony.CreateAndPatchAll(typeof(Plugin), "pieter.valheim.roofswap"); } private void AddTool() { //IL_00ef: 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_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Expected O, but got Unknown //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown PrefabManager.OnVanillaPrefabsAvailable -= AddTool; try { GameObject prefab = PrefabManager.Instance.GetPrefab("Hammer"); GameObject val = ((prefab != null) ? prefab.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_buildPieces?.m_pieces?.FirstOrDefault((Func<GameObject, bool>)((GameObject p) => (Object)(object)p != (Object)null && (p.GetComponent<Piece>()?.m_repairPiece ?? false))); GameObject prefab2 = PrefabManager.Instance.GetPrefab("darkwood_roof"); Piece val2 = ((prefab2 != null) ? prefab2.GetComponent<Piece>() : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Roof Swap disabled, the hammer's repair tool or the shingle roof was not found."); return; } GameObject val3 = PrefabManager.Instance.CreateClonedPrefab("piece_roofswap", val); Piece component = val3.GetComponent<Piece>(); component.m_repairPiece = false; component.m_removePiece = false; PieceConfig val4 = new PieceConfig { Name = "Roof Swap", Description = "Click a thatch roof piece to replace it with shingles (same shape, same spot). " + $"Hold {_wholeRoofKey.Value} to swap the whole connected roof. Costs the shingle recipe; " + "the thatch drops its materials.", PieceTable = "Hammer", Category = "Building", Icon = val2.m_icon }; PieceManager.Instance.AddPiece(new CustomPiece(val3, false, val4)); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Roof Swap added to the hammer."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Adding Roof Swap failed: " + ex)); } } [HarmonyPatch(typeof(Container), "Awake")] [HarmonyPostfix] private static void ContainerAwake(Container __instance) { Containers.Add(__instance); } [HarmonyPatch(typeof(Player), "TryPlacePiece")] [HarmonyPrefix] [HarmonyPriority(800)] private static bool TryPlacePrefix(Player __instance, Piece piece, ref bool __result) { if ((Object)(object)piece == (Object)null || Utils.GetPrefabName(((Component)piece).gameObject) != "piece_roofswap") { return true; } __result = false; try { Swap(__instance, Traverse.Create((object)__instance).Method("GetRightItem", Array.Empty<object>()).GetValue<ItemData>()); } catch (Exception ex) { _log.LogError((object)("Roof swap failed: " + ex)); } return false; } private static void Swap(Player player, ItemData toolItem) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player != (Object)(object)Player.m_localPlayer) { return; } Piece hoveringPiece = player.GetHoveringPiece(); if ((Object)(object)hoveringPiece == (Object)null) { return; } if ((Object)(object)ShingleFor(hoveringPiece) == (Object)null) { ((Character)player).Message((MessageType)2, "Roof Swap only works on thatch roof pieces", 0, (Sprite)null, false); return; } KeyboardShortcut value = _wholeRoofKey.Value; List<Piece> list = (((KeyboardShortcut)(ref value)).IsPressed() ? ConnectedThatch(hoveringPiece) : new List<Piece> { hoveringPiece }); List<Piece> list2 = list.Where((Piece p) => CanReplace(player, p)).ToList(); if (list2.Count == 0) { return; } Piece val = ShingleFor(list2[0]); if (!player.HaveRequirements(val, (RequirementMode)1)) { ((Character)player).Message((MessageType)2, "You have not unlocked shingle roofs yet", 0, (Sprite)null, false); return; } if (!NoPlacementCost.Invoke(player) && (Object)(object)val.m_craftingStation != (Object)null && !Object.op_Implicit((Object)(object)CraftingStation.HaveBuildStationInRange(val.m_craftingStation.m_name, ((Component)player).transform.position)) && !ZoneSystem.instance.GetGlobalKey((GlobalKeys)27)) { ((Character)player).Message((MessageType)2, "$msg_missingstation", 0, (Sprite)null, false); return; } bool num = NoPlacementCost.Invoke(player) || ZoneSystem.instance.GetGlobalKey(val.FreeBuildKey()); Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (!num) { foreach (Piece item in list2) { Requirement[] resources = ShingleFor(item).m_resources; foreach (Requirement val2 in resources) { if ((Object)(object)val2?.m_resItem != (Object)null && val2.m_amount > 0) { string name = val2.m_resItem.m_itemData.m_shared.m_name; dictionary.TryGetValue(name, out var value2); dictionary[name] = value2 + val2.m_amount; } } } } List<string> list3 = (from c in dictionary where Available(player, c.Key) < c.Value select $"{Localization.instance.Localize(c.Key)} {Available(player, c.Key)}/{c.Value}").ToList(); if (list3.Count > 0) { ((Character)player).Message((MessageType)2, string.Format("Not enough for {0} piece(s): {1}", list2.Count, string.Join(", ", list3)), 0, (Sprite)null, false); return; } foreach (KeyValuePair<string, int> item2 in dictionary) { Pay(player, item2.Key, item2.Value); } bool flag = true; foreach (Piece item3 in list2) { Piece val3 = ShingleFor(item3); Vector3 position = ((Component)item3).transform.position; Quaternion rotation = ((Component)item3).transform.rotation; ((Component)item3).GetComponent<WearNTear>().Remove(false); player.PlacePiece(val3, position, rotation, flag, false); flag = false; } ((Character)player).UseStamina((float)BuildStamina.Invoke(player, null)); if (toolItem != null && toolItem.m_shared.m_useDurability) { toolItem.m_durability -= toolItem.m_shared.m_useDurabilityDrain * Game.m_durabilityRate; } int num3 = list.Count - list2.Count; ((Character)player).Message((MessageType)1, $"Swapped {list2.Count} roof piece(s) to shingles" + ((num3 > 0) ? $" ({num3} skipped: no access)" : ""), 0, (Sprite)null, false); } private static Piece ShingleFor(Piece piece) { if ((Object)(object)piece == (Object)null) { return null; } string prefabName = Utils.GetPrefabName(((Component)piece).gameObject); if (!prefabName.StartsWith("wood_roof", StringComparison.Ordinal)) { return null; } ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab("darkwood_roof" + prefabName.Substring("wood_roof".Length)) : null); if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent<Piece>(); } private static bool CanReplace(Player player, Piece piece) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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) if ((Object)(object)piece == (Object)null || !piece.m_canBeRemoved || !piece.CanBeRemoved()) { return false; } if ((Object)(object)((Component)piece).GetComponent<WearNTear>() == (Object)null || (Object)(object)((Component)piece).GetComponent<ZNetView>() == (Object)null) { return false; } Vector3 position = ((Component)piece).transform.position; if (Location.IsInsideNoBuildLocation(position)) { return false; } return PrivateArea.CheckAccess(position, 0f, true, false); } private static List<Piece> ConnectedThatch(Piece start) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) List<Piece> list = new List<Piece> { start }; HashSet<Piece> hashSet = new HashSet<Piece> { start }; List<Piece> list2 = new List<Piece>(); for (int i = 0; i < list.Count; i++) { if (list.Count >= _maxPieces.Value) { break; } list2.Clear(); Piece.GetAllPiecesInRadius(((Component)list[i]).transform.position, 3f, list2); foreach (Piece item in list2) { if (!((Object)(object)item == (Object)null) && !hashSet.Contains(item) && !((Object)(object)ShingleFor(item) == (Object)null)) { hashSet.Add(item); list.Add(item); if (list.Count >= _maxPieces.Value) { break; } } } } return list; } private static IEnumerable<Inventory> ChestInventories(Player player) { Vector3 origin = ((Component)player).transform.position; float maxSqr = _chestRange.Value * _chestRange.Value; Containers.RemoveWhere((Container c) => (Object)(object)c == (Object)null); foreach (Container item in Containers.OrderBy(delegate(Container c) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Vector3 val2 = ((Component)c).transform.position - origin; return ((Vector3)(ref val2)).sqrMagnitude; })) { Vector3 val = ((Component)item).transform.position - origin; if (((Vector3)(ref val)).sqrMagnitude > maxSqr) { break; } if (((Behaviour)item).isActiveAndEnabled && item.GetInventory() != null) { Piece componentInParent = ((Component)item).GetComponentInParent<Piece>(); if (!((Object)(object)componentInParent == (Object)null) && componentInParent.IsPlacedByPlayer() && !((Object)(object)item.m_wagon != (Object)null) && !((Object)(object)((Component)item).GetComponentInParent<Ship>() != (Object)null) && item.IsOwner() && !item.IsInUse() && (bool)CheckAccessMethod.Invoke(item, new object[1] { player.GetPlayerID() })) { yield return item.GetInventory(); } } } } private static int Available(Player player, string name) { return ((Humanoid)player).GetInventory().CountItems(name, -1, true) + ChestInventories(player).Sum((Inventory inv) => inv.CountItems(name, -1, true)); } private static void Pay(Player player, string name, int amount) { int num = Mathf.Min(amount, ((Humanoid)player).GetInventory().CountItems(name, -1, true)); if (num > 0) { ((Humanoid)player).GetInventory().RemoveItem(name, num, -1, true); } int num2 = amount - num; foreach (Inventory item in ChestInventories(player)) { if (num2 <= 0) { break; } int num3 = Mathf.Min(num2, item.CountItems(name, -1, true)); if (num3 > 0) { item.RemoveItem(name, num3, -1, true); num2 -= num3; } } } } }