Decompiled source of RoofSwap v1.0.1

RoofSwap.dll

Decompiled a day ago
using 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;
				}
			}
		}
	}
}