Decompiled source of WaterproofYourWood v1.5.0

BepInEx/plugins/WaterproofingBrush/ValheimWaterproofBrush.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using BrushCameraCompatibility;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using ValheimBuildCamera;
using WaterproofBrushLogic;
using WaterproofCameraLogic;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.27.0")]
[assembly: AssemblyVersion("1.0.27.0")]
[BepInPlugin("valheim.waterproofbrush", "Waterproofing Brush", "1.0.27")]
public sealed class WaterproofBrushPlugin : BaseUnityPlugin
{
	private sealed class GameInventory : IBrushInventory
	{
		private sealed class Snapshot
		{
			internal List<ItemData> Items;

			internal int[] Stacks;
		}

		private readonly Player _player;

		private readonly Inventory _inventory;

		internal bool LastCreatedCheated;

		public int FreeSlots => _inventory.GetEmptySlots();

		internal GameInventory(Player player)
		{
			_player = player;
			_inventory = ((Humanoid)player).GetInventory();
		}

		internal static string ResourceName(string prefab)
		{
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab);
			if (!Object.op_Implicit((Object)(object)itemPrefab))
			{
				throw new InvalidOperationException("Missing resource prefab " + prefab);
			}
			return itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name;
		}

		public int Count(string resource)
		{
			return _inventory.CountItems(ResourceName(resource), -1, true);
		}

		public object Capture()
		{
			List<ItemData> list = new List<ItemData>(_inventory.GetAllItems());
			return new Snapshot
			{
				Items = list,
				Stacks = list.Select((ItemData item) => item.m_stack).ToArray()
			};
		}

		public void Restore(object state)
		{
			Snapshot snapshot = (Snapshot)state;
			List<ItemData> allItems = _inventory.GetAllItems();
			allItems.Clear();
			allItems.AddRange(snapshot.Items);
			for (int i = 0; i < allItems.Count; i++)
			{
				allItems[i].m_stack = snapshot.Stacks[i];
			}
			try
			{
				NotifyInventory.Invoke(_inventory, new object[2] { false, false });
			}
			catch (Exception ex)
			{
				Instance.ReportOnce("rollback.notify", ex);
			}
		}

		public bool AddBrush()
		{
			ItemData val = CreateBrushItem();
			LastCreatedCheated |= Instance._craftCompatibility.IsItemCheated(val);
			if (_inventory.AddItem(val))
			{
				return _inventory.ContainsItem(val);
			}
			return false;
		}

		internal ItemData CreateBrushItem()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = BrushPrefab.GetComponent<ItemDrop>().m_itemData.Clone();
			val.m_dropPrefab = BrushPrefab;
			val.m_stack = 1;
			val.m_quality = 1;
			val.m_variant = 0;
			val.m_worldLevel = (byte)Game.m_worldLevel;
			val.m_crafterID = _player.GetPlayerID();
			val.m_crafterName = _player.GetPlayerName();
			val.m_gridPos = new Vector2i(-1, -1);
			val.m_equipped = false;
			val.m_durability = 100f;
			CraftCompatibility craftCompatibility = Instance._craftCompatibility;
			if (craftCompatibility.HasItemFlag)
			{
				int? stationCheatedKey = craftCompatibility.StationCheatedKey;
				CraftingStation currentCraftingStation = _player.GetCurrentCraftingStation();
				object obj;
				if (currentCraftingStation == null)
				{
					obj = null;
				}
				else
				{
					ZNetView component = ((Component)currentCraftingStation).GetComponent<ZNetView>();
					obj = ((component != null) ? component.GetZDO() : null);
				}
				ZDO val2 = (ZDO)obj;
				bool stationCheated = stationCheatedKey.HasValue && val2 != null && val2.GetBool(stationCheatedKey.Value, false);
				craftCompatibility.SetItemMetadata(val, _player.NoCostCheat(), resourceCheated: false, stationCheated);
			}
			return val;
		}

		public void Spend(string resource, int amount)
		{
			_inventory.RemoveItem(ResourceName(resource), amount, -1, true);
		}
	}

	internal struct BrushTarget
	{
		internal Piece Piece;

		internal WearNTear Wear;

		internal Vector3 Point;
	}

	private sealed class GameCoating : ICoating
	{
		private readonly ZNetView _view;

		public bool CanWrite
		{
			get
			{
				if (Object.op_Implicit((Object)(object)_view) && _view.IsValid())
				{
					return _view.IsOwner();
				}
				return false;
			}
		}

		public bool Protected
		{
			get
			{
				if (Object.op_Implicit((Object)(object)_view) && _view.IsValid())
				{
					return _view.GetZDO().GetBool(-166505142, false);
				}
				return false;
			}
			set
			{
				if (!CanWrite)
				{
					throw new InvalidOperationException("Cannot save coating without piece ownership");
				}
				_view.GetZDO().Set(-166505142, value);
			}
		}

		internal GameCoating(ZNetView view)
		{
			_view = view;
		}
	}

	public const string PluginGuid = "valheim.waterproofbrush";

	public const string PluginName = "Waterproofing Brush";

	public const string PluginVersion = "1.0.27";

	internal const string BrushPrefabName = "WaterproofingBrush";

	internal const int LegacyBrushHash = -673997157;

	internal const int WaterproofHash = -166505142;

	internal static WaterproofBrushPlugin Instance;

	internal static GameObject BrushPrefab;

	internal static Recipe BrushRecipe;

	internal static bool Ready;

	private Harmony _harmony;

	private float _nextMaintenance;

	private float _nextUse;

	private int _lastCraftFrame = -1;

	private Recipe _lastSelected;

	private ObjectDB _selfTestDb;

	private bool _selfTestPassed;

	private ConfigEntry<KeyboardShortcut> _diagnosticKey;

	private ConfigEntry<bool> _useResin;

	private CraftCompatibility _craftCompatibility;

	private readonly HashSet<string> _reportedErrors = new HashSet<string>();

	internal static FieldInfo SelectedRecipeField;

	internal static PropertyInfo SelectedRecipeProperty;

	internal static PropertyInfo SelectedItemProperty;

	internal static MethodInfo NativeCraftComplete;

	internal static MethodInfo RefreshPanel;

	internal static MethodInfo NotifyInventory;

	private static MethodInfo AddKnownRecipe;

	private static FieldInfo DbByHash;

	private static FieldInfo DbByData;

	private static FieldInfo SceneByHash;

	private static FieldInfo RemoveRayMask;

	private static FieldInfo SnappingIcon;

	private ConfigEntry<KeyboardShortcut> _cameraKey;

	private ConfigEntry<KeyboardShortcut> _cameraResetKey;

	private ConfigEntry<bool> _cameraController;

	private ConfigEntry<bool> _cameraHideBody;

	private ConfigEntry<bool> _cameraExitDamage;

	private ConfigEntry<float> _cameraSpeed;

	private ConfigEntry<float> _cameraFast;

	private ConfigEntry<float> _cameraRadius;

	private ConfigEntry<float> _cameraReach;

	private ConfigEntry<float> _cameraPitch;

	private ConfigEntry<float> _cameraFov;

	private readonly CameraToggleInput _cameraToggle = new CameraToggleInput();

	private readonly Dictionary<Renderer, bool> _hiddenBody = new Dictionary<Renderer, bool>();

	private readonly List<Renderer> _bodyRenderers = new List<Renderer>(32);

	private readonly List<Renderer> _retiredBody = new List<Renderer>(8);

	private bool _paintCamera;

	private bool _cameraChord;

	private int _cameraFrame = -1;

	private float _cameraSuppressUntil;

	private float _nextBodyScan;

	private float _flightYaw;

	private float _flightPitch;

	private float _flightFov;

	private Player _cameraPlayer;

	private GameCamera _paintGameCamera;

	private Camera _paintLens;

	private Camera _paintSky;

	private Vector3 _bodyAnchor;

	private Vector3 _flightPosition;

	private Vector3 _previousCameraPosition;

	private Quaternion _flightRotation;

	private Quaternion _previousCameraRotation;

	private float _previousFov;

	private float _previousSkyFov;

	private float _previousNearClip;

	private BaseUnityPlugin _externalBuildCamera;

	private FieldInfo _externalCameraActive;

	private MethodInfo _externalCameraStop;

	private GameObject _prefabRoot;

	private ObjectDB _registeredDb;

	private ZNetScene _registeredScene;

	private bool _registering;

	private Sprite _icon;

	private readonly List<Material> _materials = new List<Material>();

	private readonly HashSet<WearNTear> _tinted = new HashSet<WearNTear>();

	private readonly HashSet<WearNTear> _highlighting = new HashSet<WearNTear>();

	private ChestResinCompatibility _chestResin;

	private bool _chestResinResolved;

	private ConfigEntry<float> _tintStrength;

	private ConfigEntry<float> _soundVolume;

	private BrushTarget _hoverTarget;

	private Hud _brushHud;

	private readonly AudioSource[] _brushSources = (AudioSource[])(object)new AudioSource[4];

	private AudioClip _brushSound;

	private AudioMixerGroup _brushMixer;

	private int _nextBrushSource;

	internal static bool Headless => Application.isBatchMode;

	internal bool WaterproofCameraActive
	{
		get
		{
			if (Ready)
			{
				return _paintCamera;
			}
			return false;
		}
	}

	internal bool CameraInputSuppressed
	{
		get
		{
			if (!_cameraChord)
			{
				return Time.unscaledTime < _cameraSuppressUntil;
			}
			return true;
		}
	}

	internal bool CameraOwnsControls
	{
		get
		{
			if (!WaterproofCameraActive)
			{
				return CameraInputSuppressed;
			}
			return true;
		}
	}

	internal float CameraWaterproofReach => CameraMath.Clamp(_cameraReach.Value, 50f, 5f, 50f);

	private float CameraRadius => CameraMath.Clamp(_cameraRadius.Value, 50f, 8f, 50f);

	private void Awake()
	{
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Expected O, but got Unknown
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Expected O, but got Unknown
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Expected O, but got Unknown
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Expected O, but got Unknown
		Instance = this;
		_useResin = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "UseResin", false, "Consume 1 Resin per newly waterproofed piece: player inventory first, then eligible chests within 200m when Easy Craft & Build from own chests is installed and enabled. False keeps coating free. Set on each brush user client; this setting is not server-synchronized.");
		try
		{
			BrushServerProtection.Install(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			BrushWeatherProtection.Install();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[weather.ready] Native server/vanilla-client coating protection installed; runtime=" + (Headless ? "dedicated server" : "client") + ". Server installation protects coated pieces for unmodded clients."));
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[weather.failed] Protection could not be installed; brush disabled. " + ex));
			return;
		}
		try
		{
			ResolveRegistrationApis();
			_harmony = new Harmony("valheim.waterproofbrush");
			if (Headless)
			{
				_harmony.Patch((MethodBase)Method(typeof(ObjectDB), "Awake"), (HarmonyMethod)null, new HarmonyMethod(typeof(BrushDbAwakePatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)Method(typeof(ObjectDB), "CopyOtherDB", typeof(ObjectDB)), (HarmonyMethod)null, new HarmonyMethod(typeof(BrushDbCopyPatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
				_harmony.Patch((MethodBase)Method(typeof(ZNetScene), "Awake"), (HarmonyMethod)null, new HarmonyMethod(typeof(BrushScenePatch), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				_diagnosticKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Diagnostics", "DumpStatus", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Write brush registration, selected recipe, UI state and patch owners to BepInEx/LogOutput.log.");
				BindFeedbackConfig();
				BindCameraConfig();
				StickInput.Initialize(typeof(ZInput), delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				});
				ResolveApis();
				Assembly assembly = typeof(PlayerProfile).Assembly;
				_craftCompatibility = new CraftCompatibility(typeof(ItemData), typeof(PlayerProfile), assembly.GetType("PlayerStatType"), assembly.GetType("ZDOVars"), ReportOnce);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[compat.craft] " + _craftCompatibility.Description));
				Harmony harmony = _harmony;
				Assembly assembly2 = typeof(WaterproofBrushPlugin).Assembly;
				ClearBuildingView.Initialize((BaseUnityPlugin)(object)this);
				harmony.PatchAll(assembly2);
			}
			VerifyPatches();
			Ready = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[startup.ok] Waterproofing Brush 1.0.27; game=" + Application.version + "; unity=" + Application.unityVersion + "; plugin=" + ((BaseUnityPlugin)this).Info.Location));
		}
		catch (Exception ex2)
		{
			Ready = false;
			if (_harmony != null)
			{
				_harmony.UnpatchAll("valheim.waterproofbrush");
			}
			((BaseUnityPlugin)this).Logger.LogError((object)("[startup.failed] Brush tools disabled; saved-coating weather protection remains active. " + ex2));
		}
	}

	private static void ResolveRegistrationApis()
	{
		DbByHash = Field(typeof(ObjectDB), "m_itemByHash");
		DbByData = Field(typeof(ObjectDB), "m_itemByData");
		SceneByHash = Field(typeof(ZNetScene), "m_namedPrefabs");
	}

	private static FieldInfo Field(Type type, string name)
	{
		return AccessTools.Field(type, name) ?? throw new MissingFieldException(type.FullName, name);
	}

	private static MethodInfo Method(Type type, string name, params Type[] arguments)
	{
		return AccessTools.Method(type, name, arguments, (Type[])null) ?? throw new MissingMethodException(type.FullName, name);
	}

	private static void ResolveApis()
	{
		SelectedRecipeField = Field(typeof(InventoryGui), "m_selectedRecipe");
		Type fieldType = SelectedRecipeField.FieldType;
		SelectedRecipeProperty = fieldType.GetProperty("Recipe") ?? throw new MissingMemberException(fieldType.FullName, "Recipe");
		SelectedItemProperty = fieldType.GetProperty("ItemData") ?? throw new MissingMemberException(fieldType.FullName, "ItemData");
		NativeCraftComplete = Method(typeof(InventoryGui), "DoCrafting", typeof(Player));
		RefreshPanel = Method(typeof(InventoryGui), "UpdateCraftingPanel", typeof(bool));
		NotifyInventory = Method(typeof(Inventory), "Changed", typeof(bool), typeof(bool));
		AddKnownRecipe = Method(typeof(Player), "AddKnownRecipe", typeof(Recipe));
		RemoveRayMask = Field(typeof(Player), "m_removeRayMask");
		SnappingIcon = Field(typeof(Hud), "m_snappingIcon");
	}

	private void VerifyPatches()
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Expected O, but got Unknown
		int num = 0;
		Type[] types = typeof(WaterproofBrushPlugin).Assembly.GetTypes();
		foreach (Type type in types)
		{
			object[] customAttributes = type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false);
			if (customAttributes.Length == 0)
			{
				continue;
			}
			HarmonyPatch val = (HarmonyPatch)customAttributes[0];
			if (!Headless || !(((HarmonyAttribute)val).info.declaringType != typeof(ObjectDB)) || !(((HarmonyAttribute)val).info.declaringType != typeof(ZNetScene)))
			{
				MethodInfo methodInfo = AccessTools.Method(((HarmonyAttribute)val).info.declaringType, ((HarmonyAttribute)val).info.methodName, (Type[])null, (Type[])null);
				Patches val2 = ((methodInfo == null) ? null : Harmony.GetPatchInfo((MethodBase)methodInfo));
				if (val2 == null || !val2.Owners.Contains("valheim.waterproofbrush"))
				{
					throw new InvalidOperationException("Patch not installed: " + type.Name);
				}
				num++;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[patch.ok] " + methodInfo.DeclaringType.Name + "." + methodInfo.Name));
			}
		}
		if (num == 0)
		{
			throw new InvalidOperationException("No declared patches were found.");
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[patches.verified] " + num));
	}

	private void Update()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		if (!Ready)
		{
			return;
		}
		if (!Headless)
		{
			TickBrushCamera();
			if (_diagnosticKey != null)
			{
				KeyboardShortcut value = _diagnosticKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					DumpStatus();
				}
			}
		}
		if (Time.unscaledTime < _nextMaintenance)
		{
			return;
		}
		_nextMaintenance = Time.unscaledTime + 0.5f;
		SafeRegister(ObjectDB.instance);
		if (Headless)
		{
			return;
		}
		try
		{
			InventoryGui instance = InventoryGui.instance;
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)localPlayer) && Object.op_Implicit((Object)(object)BrushRecipe) && (Object)(object)_selfTestDb != (Object)(object)ObjectDB.instance)
			{
				RunInventorySelfTest();
			}
			if (Object.op_Implicit((Object)(object)localPlayer) && Object.op_Implicit((Object)(object)BrushRecipe) && AtWorkbench(localPlayer) && !localPlayer.IsRecipeKnown("Waterproof Brush") && localPlayer.HaveRequirements(BrushRecipe, true, 1, 1))
			{
				AddKnownRecipe.Invoke(localPlayer, new object[1] { BrushRecipe });
			}
			if (!Object.op_Implicit((Object)(object)instance) || !InventoryGui.IsVisible())
			{
				return;
			}
			Recipe val = Selected(instance);
			if ((Object)(object)val != (Object)(object)_lastSelected)
			{
				_lastSelected = val;
				if (IsBrushRecipe(val))
				{
					DumpStatus();
				}
			}
		}
		catch (Exception ex)
		{
			ReportOnce("maintenance", ex);
		}
	}

	internal static bool IsBrush(ItemData item)
	{
		if (item != null && Object.op_Implicit((Object)(object)item.m_dropPrefab))
		{
			return ((Object)item.m_dropPrefab).name == "WaterproofingBrush";
		}
		return false;
	}

	internal static bool IsBrushRecipe(Recipe recipe)
	{
		if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item))
		{
			return ((Object)((Component)recipe.m_item).gameObject).name == "WaterproofingBrush";
		}
		return false;
	}

	internal static Recipe Selected(InventoryGui gui)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		return (Recipe)SelectedRecipeProperty.GetValue(SelectedRecipeField.GetValue(gui), null);
	}

	internal static bool AtWorkbench(Player player)
	{
		CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
		if (Object.op_Implicit((Object)(object)currentCraftingStation) && currentCraftingStation.m_name == "$piece_workbench")
		{
			return !currentCraftingStation.m_upgrader;
		}
		return false;
	}

	internal void CompleteCraft(InventoryGui gui, Player player, Recipe recipe, ItemData upgrade, bool multi, int batchAmount)
	{
		//IL_01a6: 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_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		if (!Ready || _lastCraftFrame == Time.frameCount)
		{
			return;
		}
		_lastCraftFrame = Time.frameCount;
		try
		{
			if (!Object.op_Implicit((Object)(object)player) || !IsBrushRecipe(recipe))
			{
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[craft.complete] recipe=" + ((Object)recipe).name));
			if ((Object)(object)_selfTestDb != (Object)(object)ObjectDB.instance)
			{
				RunInventorySelfTest();
			}
			if (!_selfTestPassed)
			{
				Tell(player, "Brush inventory self-test failed. Please send BepInEx/LogOutput.log.");
				return;
			}
			if (upgrade != null)
			{
				Tell(player, "The Waterproof Brush has no upgrades.");
				return;
			}
			if (!AtWorkbench(player))
			{
				Tell(player, "Use a workbench to craft the Waterproof Brush.");
				return;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			if (currentCraftingStation.GetLevel(true) < 1 || !currentCraftingStation.CheckUsable(player, true))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"[craft.blocked] Workbench is not usable.");
				return;
			}
			int amount = ((!multi) ? 1 : Math.Max(1, Math.Min(100, batchAmount)));
			bool free = player.NoCostCheat() || (Object.op_Implicit((Object)(object)ZoneSystem.instance) && ZoneSystem.instance.GetGlobalKey((GlobalKeys)25));
			GameInventory gameInventory = new GameInventory(player);
			int num = gameInventory.Count("Wood");
			Exception error;
			Outcome outcome = Transactions.Craft(gameInventory, amount, free, out error);
			if (outcome == Outcome.Success)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[craft.ok] brushes=" + amount + "; wood=" + num + "->" + gameInventory.Count("Wood")));
				try
				{
					currentCraftingStation.m_craftItemDoneEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
					_craftCompatibility.RecordCraft(Game.instance.GetPlayerProfile(), recipe.m_item.m_itemData.m_shared.m_name, amount, gameInventory.LastCreatedCheated);
				}
				catch (Exception ex)
				{
					ReportOnce("craft.feedback", ex);
				}
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[craft.blocked] result=" + outcome.ToString() + "; wood=" + num + "; slots=" + gameInventory.FreeSlots + "; amount=" + amount));
				if (error != null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)error);
				}
				Tell(player, (outcome == Outcome.FullInventory) ? ("You need " + amount + " empty inventory slot(s).") : "Craft failed; inventory restored. See BepInEx/LogOutput.log.");
			}
			try
			{
				RefreshPanel.Invoke(gui, new object[1] { false });
			}
			catch (Exception ex2)
			{
				ReportOnce("ui.refresh", ex2);
			}
		}
		catch (Exception ex3)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[craft.error] " + ex3));
			Tell(Player.m_localPlayer, "Brush crafting error. See BepInEx/LogOutput.log.");
		}
	}

	internal void ReportOnce(string context, Exception ex)
	{
		if (_reportedErrors.Add(context + ":" + ex.GetType().FullName + ":" + ex.Message))
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[" + context + ".error] " + ex));
		}
	}

	internal static void Tell(Player player, string message)
	{
		if (Object.op_Implicit((Object)(object)player))
		{
			((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false);
		}
	}

	private void RunInventorySelfTest()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Expected O, but got Unknown
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		_selfTestDb = ObjectDB.instance;
		_selfTestPassed = false;
		bool forceDisableInit = ZNetView.m_forceDisableInit;
		try
		{
			Inventory val = new Inventory("WaterproofBrush self-test", (Sprite)null, 2, 1);
			ItemData val2 = new GameInventory(Player.m_localPlayer).CreateBrushItem();
			if (!val.AddItem(val2) || !val.ContainsItem(val2))
			{
				throw new InvalidOperationException("Native AddItem rejected the test brush");
			}
			ZPackage val3 = new ZPackage();
			val.Save(val3);
			Inventory val4 = new Inventory("WaterproofBrush restore self-test", (Sprite)null, 2, 1);
			val4.Load(new ZPackage(val3.GetArray()));
			List<ItemData> allItems = val4.GetAllItems();
			if (allItems.Count != 1 || !IsBrush(allItems[0]) || allItems[0].m_stack != 1 || (Object)(object)allItems[0].m_shared.m_buildPieces != (Object)null || !Object.op_Implicit((Object)(object)allItems[0].GetIcon()) || allItems[0].HavePrimaryAttack() || allItems[0].HaveSecondaryAttack())
			{
				throw new InvalidOperationException("Native save/load did not restore a valid brush tool");
			}
			_selfTestPassed = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[selftest.ok] Craft item factory + metadata + native Inventory.AddItem + Save + Load passed in throwaway inventories; player inventory untouched.");
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[selftest.failed] " + ex));
		}
		finally
		{
			ZNetView.m_forceDisableInit = forceDisableInit;
		}
	}

	private void DumpStatus()
	{
		//IL_028e: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d6: Expected O, but got Unknown
		try
		{
			InventoryGui gui = InventoryGui.instance;
			Recipe val = (Object.op_Implicit((Object)(object)gui) ? Selected(gui) : null);
			Player localPlayer = Player.m_localPlayer;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[status] version=1.0.27; ready=" + Ready + "; prefab=" + ((Object)(object)BrushPrefab != (Object)null) + "; recipe=" + ((Object)(object)BrushRecipe != (Object)null) + "; selected=" + (Object.op_Implicit((Object)(object)val) ? ((Object)val).name : "none") + "; selectedIsBrush=" + IsBrushRecipe(val) + "; buttonActive=" + (Object.op_Implicit((Object)(object)gui) && Object.op_Implicit((Object)(object)gui.m_craftButton) && ((Component)gui.m_craftButton).gameObject.activeInHierarchy) + "; buttonInteractable=" + (Object.op_Implicit((Object)(object)gui) && Object.op_Implicit((Object)(object)gui.m_craftButton) && ((Selectable)gui.m_craftButton).interactable) + "; nativeCraftProgress=true; inventorySelfTest=" + _selfTestPassed + "; workbench=" + (Object.op_Implicit((Object)(object)localPlayer) && AtWorkbench(localPlayer)) + "; wood=" + (Object.op_Implicit((Object)(object)localPlayer) ? new GameInventory(localPlayer).Count("Wood") : (-1))));
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)NativeCraftComplete);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[status.patches] DoCrafting owners=" + ((patchInfo == null) ? "NONE" : string.Join(",", patchInfo.Owners.ToArray()))));
			if (Object.op_Implicit((Object)(object)gui) && Object.op_Implicit((Object)(object)gui.m_craftButton) && InventoryGui.IsVisible() && Object.op_Implicit((Object)(object)EventSystem.current))
			{
				Transform transform = ((Component)gui.m_craftButton).transform;
				RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				Canvas componentInParent = ((Component)gui.m_craftButton).GetComponentInParent<Canvas>();
				Camera val3 = ((Object.op_Implicit((Object)(object)componentInParent) && (int)componentInParent.renderMode != 0) ? componentInParent.worldCamera : null);
				PointerEventData val4 = new PointerEventData(EventSystem.current);
				Rect rect = val2.rect;
				val4.position = RectTransformUtility.WorldToScreenPoint(val3, ((Transform)val2).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center)));
				PointerEventData val5 = val4;
				List<RaycastResult> list = new List<RaycastResult>();
				EventSystem.current.RaycastAll(val5, list);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[status.raycast] Craft button centre hits=" + string.Join(" | ", (from hit in list.Take(5)
					select ((Object)((RaycastResult)(ref hit)).gameObject).name + " (routesToCraft=" + ((Object)(object)((RaycastResult)(ref hit)).gameObject.GetComponentInParent<Button>() == (Object)(object)gui.m_craftButton) + ")").ToArray())));
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[status.error] " + ex));
		}
	}

	private void OnDestroy()
	{
		ClearBuildingView.Shutdown();
		if (!Headless)
		{
			StopBrushCamera("Plugin destroyed");
		}
		Ready = false;
		if (!Headless)
		{
			ReleaseFeedback();
		}
		if (_harmony != null)
		{
			_harmony.UnpatchAll("valheim.waterproofbrush");
		}
		BrushWeatherProtection.Uninstall();
		BrushServerProtection.Uninstall();
		ReleaseAssets();
		Instance = null;
	}

	private void BindCameraConfig()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		_cameraKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Camera controls", "ToggleKey", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Enter/leave waterproofing free camera; only while Waterproof Brush is equipped.");
		_cameraResetKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Camera controls", "ResetViewKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Reset the active waterproofing camera.");
		_cameraController = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera controls", "ControllerStickToggle", true, "Press both stick buttons together to toggle. Release both before toggling again.");
		_cameraHideBody = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera view", "HideLocalPlayer", true, "Hide the local character on this client during waterproofing camera. Your body remains in the world.");
		_cameraExitDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera safety", "ExitOnDamage", true, "Return to the normal camera when your body takes damage.");
		_cameraSpeed = CameraFloat("MoveSpeed", 6f, 1f, 15f, "Flight speed in metres per second.");
		_cameraFast = CameraFloat("FastMultiplier", 2f, 1f, 3f, "Hold Run to fly faster.");
		_cameraRadius = CameraFloat("CameraRadius", 50f, 8f, 50f, "Free-camera radius in metres from your body.");
		_cameraReach = CameraFloat("WaterproofReach", 50f, 5f, 50f, "Waterproofing reach in metres from your body, only in free camera.");
		_cameraPitch = CameraFloat("StartingPitch", 50f, 20f, 75f, "Initial downward viewing angle.");
		_cameraFov = CameraFloat("FieldOfView", 45f, 25f, 75f, "Initial FOV. Page Up/Down zoom outside menus.");
	}

	private ConfigEntry<float> CameraFloat(string key, float value, float min, float max, string description)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		return ((BaseUnityPlugin)this).Config.Bind<float>("Waterproofing camera", key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
	}

	internal bool EnsureBrushSession()
	{
		if (Application.isBatchMode)
		{
			return false;
		}
		Player localPlayer = Player.m_localPlayer;
		bool flag = ((Behaviour)this).isActiveAndEnabled && BrushSessionRules.CanStay(BrushSelected(localPlayer), Object.op_Implicit((Object)(object)localPlayer) && !((Character)localPlayer).IsDead(), Object.op_Implicit((Object)(object)localPlayer) && (((Character)localPlayer).IsTeleporting() || ((Character)localPlayer).InCutscene() || ((Character)localPlayer).IsAttached() || localPlayer.GetDoodadController() != null));
		if (flag && _paintCamera)
		{
			flag = (Object)(object)localPlayer == (Object)(object)_cameraPlayer && Object.op_Implicit((Object)(object)_paintGameCamera) && (Object)(object)_paintGameCamera == (Object)(object)GameCamera.instance && Object.op_Implicit((Object)(object)_paintLens);
		}
		if (flag)
		{
			return true;
		}
		StopBrushCamera("Brush unequipped or player/camera changed");
		ClearHover();
		_cameraToggle.Reset(waitForRelease: true);
		_cameraChord = false;
		_cameraSuppressUntil = 0f;
		return false;
	}

	internal void TickBrushCamera()
	{
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: 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)
		TickClearBuildingView();
		if (!Ready || Application.isBatchMode || _cameraFrame == Time.frameCount)
		{
			return;
		}
		_cameraFrame = Time.frameCount;
		try
		{
			if (!EnsureBrushSession())
			{
				return;
			}
			ReleaseExternalBuildCamera();
			if (ZInput.instance == null || !Application.isFocused)
			{
				return;
			}
			bool flag = ZInput.GetButton("JoyLStick");
			bool flag2 = ZInput.GetButton("JoyRStick");
			Gamepad current = Gamepad.current;
			if (current != null)
			{
				flag |= current.leftStickButton.isPressed;
				flag2 |= current.rightStickButton.isPressed;
			}
			bool flag3 = CameraModal();
			bool flag4 = _cameraController.Value && !flag3;
			_cameraChord = flag4 && flag && flag2;
			CameraToggleInput cameraToggle = _cameraToggle;
			int frameCount = Time.frameCount;
			KeyboardShortcut value = _cameraKey.Value;
			if (cameraToggle.Step(frameCount, ((KeyboardShortcut)(ref value)).IsDown(), !flag3, flag, flag2, flag4, Time.unscaledDeltaTime))
			{
				if (_paintCamera)
				{
					StopBrushCamera("Toggled off");
				}
				else
				{
					StartBrushCamera(Player.m_localPlayer);
				}
			}
			else
			{
				if (!_paintCamera)
				{
					return;
				}
				if (!flag3 && ZInput.GetKeyDown((KeyCode)27, true))
				{
					StopBrushCamera("Escape");
					return;
				}
				KeepWaterproofBodyStill();
				if (!flag3)
				{
					value = _cameraResetKey.Value;
					if (((KeyboardShortcut)(ref value)).IsDown())
					{
						ResetWaterproofCamera();
					}
				}
				if (!flag3 && !CameraInputSuppressed)
				{
					MoveWaterproofCamera();
				}
				ApplyWaterproofCameraPose();
			}
		}
		catch (Exception ex)
		{
			StopBrushCamera("Camera error");
			ReportOnce("waterproof.camera", ex);
		}
	}

	internal static bool CanUseTool(Player player)
	{
		if (Object.op_Implicit((Object)(object)player) && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InCutscene() && !InventoryGui.IsVisible() && !Hud.InRadial() && !Menu.IsVisible() && !Console.IsVisible() && !StoreGui.IsVisible() && !TextInput.IsVisible() && !Minimap.IsOpen() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()))
		{
			return !GameCamera.InFreeFly();
		}
		return false;
	}

	private static bool CameraModal()
	{
		try
		{
			return !CanUseTool(Player.m_localPlayer) || UnifiedPopup.IsVisible() || (Object.op_Implicit((Object)(object)TextViewer.instance) && TextViewer.instance.IsVisible()) || PlayerCustomizaton.IsBarberGuiVisible() || (Object.op_Implicit((Object)(object)ZNet.instance) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && Game.IsPaused());
		}
		catch (NullReferenceException)
		{
			return true;
		}
	}

	private void StartBrushCamera(Player player)
	{
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		if (!BrushSelected(player) || CameraModal())
		{
			return;
		}
		if (!((Character)player).IsOnGround() || ((Character)player).IsSwimming())
		{
			Tell(player, "Stand on solid ground to enter the waterproofing camera.");
			return;
		}
		_paintGameCamera = GameCamera.instance;
		_paintLens = (Object.op_Implicit((Object)(object)_paintGameCamera) ? ((Component)_paintGameCamera).GetComponent<Camera>() : null);
		if (Object.op_Implicit((Object)(object)_paintLens) && !GameCamera.InFreeFly())
		{
			_paintSky = _paintGameCamera.m_skyCamera;
			_cameraPlayer = player;
			_bodyAnchor = ((Component)player).transform.position;
			_previousCameraPosition = ((Component)_paintGameCamera).transform.position;
			_previousCameraRotation = ((Component)_paintGameCamera).transform.rotation;
			_previousFov = _paintLens.fieldOfView;
			_previousNearClip = _paintLens.nearClipPlane;
			_previousSkyFov = (Object.op_Implicit((Object)(object)_paintSky) ? _paintSky.fieldOfView : _previousFov);
			_paintCamera = true;
			player.m_autoRun = false;
			((Character)player).StopMovement();
			player.SetControls(Vector3.zero, false, false, false, false, false, false, false, false, false, false, false);
			((Character)player).m_onDamaged = (Action<float, Character>)Delegate.Combine(((Character)player).m_onDamaged, new Action<float, Character>(WaterproofCameraDamaged));
			ResetWaterproofCamera();
			HideWaterproofBody();
			_cameraSuppressUntil = Time.unscaledTime + 0.2f;
			ApplyWaterproofCameraPose();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[waterproof.camera] ON; radius=" + CameraRadius + "; waterproof reach=" + CameraWaterproofReach));
		}
	}

	private void ResetWaterproofCamera()
	{
		//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_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		Quaternion rotation = ((Character)_cameraPlayer).m_eye.rotation;
		_flightYaw = ((Quaternion)(ref rotation)).eulerAngles.y;
		_flightPitch = CameraMath.Clamp(_cameraPitch.Value, 50f, 20f, 75f);
		_flightFov = CameraMath.Clamp(_cameraFov.Value, 45f, 25f, 75f);
		_flightRotation = Quaternion.Euler(_flightPitch, _flightYaw, 0f);
		Vector3 val = _bodyAnchor + Quaternion.Euler(0f, _flightYaw, 0f) * Vector3.forward * 5f + Vector3.up;
		_flightPosition = val - _flightRotation * Vector3.forward * 12f;
		ClampWaterproofCamera();
	}

	private void MoveWaterproofCamera()
	{
		//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)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: 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_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//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_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
		float num = CameraMath.Clamp(Time.unscaledDeltaTime, 0f, 0f, 0.05f);
		Vector2 val = Vector2.zero;
		if (ZInput.IsMouseActive())
		{
			val = ZInput.GetMouseDelta() * (ZInput.IsGamepadMouseActive() ? PlayerController.m_switchMouseSens : PlayerController.m_mouseSens);
			if (PlayerController.m_invertMouse)
			{
				val.y *= -1f;
			}
		}
		else if (ZInput.IsGamepadActive())
		{
			val = new Vector2(StickInput.RightStickX(), 0f - StickInput.RightStickY()) * 110f * num * PlayerController.m_gamepadSens;
			if (PlayerController.m_invertCameraX)
			{
				val.x *= -1f;
			}
			if (PlayerController.m_invertCameraY)
			{
				val.y *= -1f;
			}
		}
		_flightYaw = Mathf.Repeat(_flightYaw + val.x, 360f);
		_flightPitch = Mathf.Clamp(_flightPitch - val.y, -80f, 85f);
		if (ZInput.GetKey((KeyCode)280, true))
		{
			_flightFov -= 25f * num;
		}
		if (ZInput.GetKey((KeyCode)281, true))
		{
			_flightFov += 25f * num;
		}
		_flightFov = Mathf.Clamp(_flightFov, 25f, 75f);
		_flightRotation = Quaternion.Euler(_flightPitch, _flightYaw, 0f);
		float x = (float)((ZInput.GetButton("Right") ? 1 : 0) - (ZInput.GetButton("Left") ? 1 : 0)) + StickInput.LeftStickX();
		float z = (float)((ZInput.GetButton("Forward") ? 1 : 0) - (ZInput.GetButton("Backward") ? 1 : 0)) - StickInput.LeftStickY();
		float y = ((ZInput.GetButton("Jump") || ZInput.GetButton("JoyJump")) ? 1 : 0) - ((ZInput.GetButton("Crouch") || ZInput.GetButton("JoyCrouch")) ? 1 : 0);
		CameraMath.NormalizeInput(ref x, ref y, ref z);
		MovementVector movementVector = CameraMath.ViewRelativeMovement(_flightYaw, _flightPitch, x, y, z);
		CameraMath.NormalizeInput(ref movementVector.X, ref movementVector.Y, ref movementVector.Z);
		float num2 = CameraMath.Clamp(_cameraSpeed.Value, 6f, 1f, 15f);
		if (ZInput.GetButton("Run") || ZInput.GetButton("JoyRun"))
		{
			num2 *= CameraMath.Clamp(_cameraFast.Value, 2f, 1f, 3f);
		}
		_flightPosition += new Vector3(movementVector.X, movementVector.Y, movementVector.Z) * num2 * num;
		ClampWaterproofCamera();
	}

	private void ClampWaterproofCamera()
	{
		//IL_0001: 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_000b: 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_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = _bodyAnchor + Vector3.up;
		Vector3 val2 = _flightPosition - val;
		CameraMath.ClampToSphere(ref val2.x, ref val2.y, ref val2.z, CameraRadius);
		_flightPosition = val + val2;
	}

	private void KeepWaterproofBodyStill()
	{
		//IL_0019: 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)
		//IL_0024: 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_0044: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)_cameraPlayer))
		{
			Vector3 val = ((Component)_cameraPlayer).transform.position - _bodyAnchor;
			if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
			{
				((Component)_cameraPlayer).transform.position = _bodyAnchor;
				Physics.SyncTransforms();
			}
			((Character)_cameraPlayer).StopMovement();
		}
	}

	internal void ApplyWaterproofCameraPose()
	{
		//IL_0030: 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_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: 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_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: 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 (!_paintCamera || !Object.op_Implicit((Object)(object)_paintLens) || !Object.op_Implicit((Object)(object)_paintGameCamera))
		{
			return;
		}
		Transform transform = ((Component)_paintGameCamera).transform;
		Vector3 position = transform.position;
		if (((Vector3)(ref position)).Equals(_flightPosition))
		{
			Quaternion rotation = transform.rotation;
			if (((Quaternion)(ref rotation)).Equals(_flightRotation))
			{
				goto IL_006d;
			}
		}
		transform.SetPositionAndRotation(_flightPosition, _flightRotation);
		goto IL_006d;
		IL_006d:
		if (_paintLens.fieldOfView != _flightFov)
		{
			_paintLens.fieldOfView = _flightFov;
		}
		float num = Mathf.Min(_previousNearClip, 0.1f);
		if (_paintLens.nearClipPlane != num)
		{
			_paintLens.nearClipPlane = num;
		}
		if (Object.op_Implicit((Object)(object)_paintSky) && _paintSky.fieldOfView != _flightFov)
		{
			_paintSky.fieldOfView = _flightFov;
		}
	}

	private void HideWaterproofBody()
	{
		if (!_cameraHideBody.Value)
		{
			RestoreWaterproofBody();
			return;
		}
		GameObject val = (Object.op_Implicit((Object)(object)_cameraPlayer) ? ((Character)_cameraPlayer).GetVisual() : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return;
		}
		_bodyRenderers.Clear();
		val.GetComponentsInChildren<Renderer>(true, _bodyRenderers);
		foreach (Renderer bodyRenderer in _bodyRenderers)
		{
			if (Object.op_Implicit((Object)(object)bodyRenderer))
			{
				if (!_hiddenBody.ContainsKey(bodyRenderer))
				{
					_hiddenBody.Add(bodyRenderer, bodyRenderer.forceRenderingOff);
				}
				bodyRenderer.forceRenderingOff = true;
			}
		}
		_retiredBody.Clear();
		foreach (KeyValuePair<Renderer, bool> item in _hiddenBody)
		{
			if (!Object.op_Implicit((Object)(object)item.Key))
			{
				_retiredBody.Add(item.Key);
			}
			else if ((Object)(object)((Component)item.Key).transform != (Object)(object)val.transform && !((Component)item.Key).transform.IsChildOf(val.transform))
			{
				item.Key.forceRenderingOff = item.Value;
				_retiredBody.Add(item.Key);
			}
		}
		foreach (Renderer item2 in _retiredBody)
		{
			_hiddenBody.Remove(item2);
		}
		_retiredBody.Clear();
		_bodyRenderers.Clear();
	}

	private void RestoreWaterproofBody()
	{
		foreach (KeyValuePair<Renderer, bool> item in _hiddenBody)
		{
			try
			{
				if (Object.op_Implicit((Object)(object)item.Key))
				{
					item.Key.forceRenderingOff = item.Value;
				}
			}
			catch (Exception ex)
			{
				ReportOnce("camera.renderer.restore", ex);
			}
		}
		_hiddenBody.Clear();
		_bodyRenderers.Clear();
		_retiredBody.Clear();
	}

	internal void StopBrushCamera(string reason)
	{
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: 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_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		ClearBuildingView.Stop();
		_cameraToggle.CancelPending();
		if (!_paintCamera)
		{
			return;
		}
		_paintCamera = false;
		try
		{
			if (Object.op_Implicit((Object)(object)_cameraPlayer))
			{
				Player cameraPlayer = _cameraPlayer;
				((Character)cameraPlayer).m_onDamaged = (Action<float, Character>)Delegate.Remove(((Character)cameraPlayer).m_onDamaged, new Action<float, Character>(WaterproofCameraDamaged));
			}
		}
		catch (Exception ex)
		{
			ReportOnce("camera.damage.restore", ex);
		}
		RestoreWaterproofBody();
		try
		{
			if (Object.op_Implicit((Object)(object)_paintLens))
			{
				_paintLens.fieldOfView = _previousFov;
				_paintLens.nearClipPlane = _previousNearClip;
			}
			if (Object.op_Implicit((Object)(object)_paintSky))
			{
				_paintSky.fieldOfView = _previousSkyFov;
			}
			if (Object.op_Implicit((Object)(object)_cameraPlayer) && (Object)(object)_cameraPlayer == (Object)(object)Player.m_localPlayer && !((Character)_cameraPlayer).IsTeleporting() && !((Character)_cameraPlayer).InCutscene() && !((Character)_cameraPlayer).IsAttached() && _cameraPlayer.GetDoodadController() == null)
			{
				Vector3 val = ((Component)_cameraPlayer).transform.position - _bodyAnchor;
				if (((Vector3)(ref val)).sqrMagnitude < 1f && Object.op_Implicit((Object)(object)_paintGameCamera) && (Object)(object)_paintGameCamera == (Object)(object)GameCamera.instance && !GameCamera.InFreeFly())
				{
					((Component)_paintGameCamera).transform.SetPositionAndRotation(_previousCameraPosition, _previousCameraRotation);
				}
			}
		}
		catch (Exception ex2)
		{
			ReportOnce("camera.pose.restore", ex2);
		}
		_cameraPlayer = null;
		_paintGameCamera = null;
		_paintLens = null;
		_paintSky = null;
		_cameraSuppressUntil = Time.unscaledTime + 0.2f;
		ClearHover();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[waterproof.camera] OFF: " + reason));
	}

	private void WaterproofCameraDamaged(float damage, Character attacker)
	{
		if (_paintCamera && _cameraExitDamage.Value && damage > 0f)
		{
			StopBrushCamera("Player damaged");
		}
	}

	private void LateUpdate()
	{
		if (!Ready || Application.isBatchMode)
		{
			return;
		}
		try
		{
			if (EnsureBrushSession() && _paintCamera)
			{
				KeepWaterproofBodyStill();
				ApplyWaterproofCameraPose();
				if (Time.unscaledTime >= _nextBodyScan)
				{
					HideWaterproofBody();
					_nextBodyScan = Time.unscaledTime + 0.25f;
				}
			}
		}
		catch (Exception ex)
		{
			StopBrushCamera("Camera error");
			ReportOnce("waterproof.camera.late", ex);
		}
	}

	private void OnDisable()
	{
		StopBrushCamera("Plugin disabled");
		_cameraToggle.Reset(waitForRelease: true);
		_cameraChord = false;
		_cameraSuppressUntil = 0f;
	}

	private void OnApplicationFocus(bool focused)
	{
		ClearBuildingView.FocusChanged(focused);
		if (!focused)
		{
			StopBrushCamera("Focus lost");
			_cameraToggle.Reset(waitForRelease: true);
		}
	}

	private void ReleaseExternalBuildCamera()
	{
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Expected O, but got Unknown
		if (!Object.op_Implicit((Object)(object)_externalBuildCamera))
		{
			if (!Chainloader.PluginInfos.TryGetValue("valheim.buildcamera", out var value) || !Object.op_Implicit((Object)(object)value.Instance))
			{
				return;
			}
			_externalBuildCamera = value.Instance;
			Type type = ((object)_externalBuildCamera).GetType();
			_externalCameraActive = AccessTools.Field(type, "_active");
			_externalCameraStop = AccessTools.Method(type, "StopCamera", new Type[2]
			{
				typeof(string),
				typeof(bool)
			}, (Type[])null);
			MethodInfo methodInfo = AccessTools.Method(type, "StartCamera", new Type[1] { typeof(Player) }, (Type[])null);
			if (methodInfo != null && _harmony != null)
			{
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(WaterproofBrushPlugin), "AllowStandaloneBuildCameraStart", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}
		if (_externalCameraActive != null && (bool)_externalCameraActive.GetValue(_externalBuildCamera))
		{
			if (_externalCameraStop == null)
			{
				throw new InvalidOperationException("Exit the standalone build camera before using the waterproof brush.");
			}
			_externalCameraStop.Invoke(_externalBuildCamera, new object[2] { "Waterproof Brush selected", false });
		}
	}

	private static bool AllowStandaloneBuildCameraStart()
	{
		return !BrushSelected(Player.m_localPlayer);
	}

	internal static void SafeRegister(ObjectDB db)
	{
		if (!Ready || !Object.op_Implicit((Object)(object)Instance) || !Object.op_Implicit((Object)(object)db))
		{
			return;
		}
		try
		{
			Instance.Register(db);
		}
		catch (Exception ex)
		{
			Instance.ReportOnce("registration", ex);
		}
	}

	private void Register(ObjectDB db)
	{
		if (_registering || ((Object)(object)_registeredDb == (Object)(object)db && (Object)(object)_registeredScene == (Object)(object)ZNetScene.instance && Object.op_Implicit((Object)(object)BrushPrefab) && Object.op_Implicit((Object)(object)BrushRecipe) && (Object)(object)db.GetItemPrefab("WaterproofingBrush") == (Object)(object)BrushPrefab && db.m_recipes.Contains(BrushRecipe)))
		{
			return;
		}
		_registering = true;
		try
		{
			GameObject itemPrefab = db.GetItemPrefab("Hammer");
			if (!Object.op_Implicit((Object)(object)itemPrefab))
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)BrushPrefab))
			{
				CreatePrefab(itemPrefab);
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode("WaterproofingBrush");
			Dictionary<int, GameObject> obj = (Dictionary<int, GameObject>)DbByHash.GetValue(db);
			if (obj.TryGetValue(stableHashCode, out var value) && Object.op_Implicit((Object)(object)value) && (Object)(object)value != (Object)(object)BrushPrefab)
			{
				throw new InvalidOperationException("Brush hash already belongs to " + ((Object)value).name + ". Remove duplicate mod copies.");
			}
			if (!db.m_items.Contains(BrushPrefab))
			{
				db.m_items.Add(BrushPrefab);
			}
			obj[stableHashCode] = BrushPrefab;
			obj[-673997157] = BrushPrefab;
			ItemDrop component = BrushPrefab.GetComponent<ItemDrop>();
			((Dictionary<SharedData, GameObject>)DbByData.GetValue(db))[component.m_itemData.m_shared] = BrushPrefab;
			ZNetScene instance = ZNetScene.instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				Dictionary<int, GameObject> obj2 = (Dictionary<int, GameObject>)SceneByHash.GetValue(instance);
				if (obj2.TryGetValue(stableHashCode, out value) && Object.op_Implicit((Object)(object)value) && (Object)(object)value != (Object)(object)BrushPrefab)
				{
					throw new InvalidOperationException("Duplicate brush network prefab");
				}
				if (!instance.m_prefabs.Contains(BrushPrefab))
				{
					instance.m_prefabs.Add(BrushPrefab);
				}
				obj2[stableHashCode] = BrushPrefab;
				obj2[-673997157] = BrushPrefab;
			}
			CraftingStation val = FindWorkbench(db, itemPrefab, instance);
			if (Object.op_Implicit((Object)(object)val))
			{
				if (!Object.op_Implicit((Object)(object)BrushRecipe))
				{
					BrushRecipe = ScriptableObject.CreateInstance<Recipe>();
					((Object)BrushRecipe).name = "Recipe_WaterproofingBrush";
				}
				BrushRecipe.m_item = component;
				BrushRecipe.m_amount = 1;
				BrushRecipe.m_enabled = true;
				BrushRecipe.m_noCraftOnlyUpgrade = false;
				BrushRecipe.m_minStationLevel = 1;
				BrushRecipe.m_craftingStation = val;
				BrushRecipe.m_repairStation = val;
				BrushRecipe.m_requireOnlyOneIngredient = false;
				BrushRecipe.m_resources = (Requirement[])(object)new Requirement[0];
				db.m_recipes.RemoveAll((Recipe r) => Object.op_Implicit((Object)(object)r) && (Object)(object)r != (Object)(object)BrushRecipe && IsBrushRecipe(r));
				if (!db.m_recipes.Contains(BrushRecipe))
				{
					db.m_recipes.Add(BrushRecipe);
				}
				_registeredDb = db;
				_registeredScene = instance;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[register.ok] prefab=WaterproofingBrush; hash=" + stableHashCode + "; activeSelf=" + BrushPrefab.activeSelf + "; activeInHierarchy=" + BrushPrefab.activeInHierarchy + "; station=" + val.m_name + "; cost=free; network=" + ((Object)(object)instance != (Object)null)));
			}
		}
		finally
		{
			_registering = false;
		}
	}

	private static CraftingStation FindWorkbench(ObjectDB db, GameObject hammer, ZNetScene scene)
	{
		GameObject val = (Object.op_Implicit((Object)(object)scene) ? scene.GetPrefab("piece_workbench") : null);
		if (Object.op_Implicit((Object)(object)val))
		{
			return val.GetComponent<CraftingStation>();
		}
		PieceTable buildPieces = hammer.GetComponent<ItemDrop>().m_itemData.m_shared.m_buildPieces;
		if (Object.op_Implicit((Object)(object)buildPieces))
		{
			foreach (GameObject piece in buildPieces.m_pieces)
			{
				if (Object.op_Implicit((Object)(object)piece) && ((Object)piece).name == "piece_workbench")
				{
					return piece.GetComponent<CraftingStation>();
				}
			}
		}
		foreach (Recipe recipe in db.m_recipes)
		{
			if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_craftingStation) && recipe.m_craftingStation.m_name == "$piece_workbench")
			{
				return recipe.m_craftingStation;
			}
		}
		return null;
	}

	private void CreatePrefab(GameObject hammer)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Expected O, but got Unknown
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Expected O, but got Unknown
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Expected O, but got Unknown
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_0292: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02da: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_030f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0323: Unknown result type (might be due to invalid IL or missing references)
		//IL_037c: Unknown result type (might be due to invalid IL or missing references)
		//IL_039c: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)_prefabRoot))
		{
			_prefabRoot = new GameObject("WaterproofBrush_PrefabRoot");
			_prefabRoot.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_prefabRoot);
		}
		GameObject val = Object.Instantiate<GameObject>(hammer, _prefabRoot.transform, false);
		try
		{
			((Object)val).name = "WaterproofingBrush";
			val.SetActive(true);
			ItemDrop component = val.GetComponent<ItemDrop>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				throw new InvalidOperationException("Hammer has no ItemDrop");
			}
			component.m_itemData = new ItemData();
			ItemData itemData = component.m_itemData;
			SharedData val2 = new SharedData();
			val2.m_name = "Waterproof Brush";
			val2.m_description = "Seal wooden building pieces against rain. Equip, aim at wood and left-click. Resin use is configurable. Sealed wood has a subtle warm finish.";
			val2.m_itemType = (ItemType)19;
			val2.m_animationState = (AnimationState)1;
			val2.m_maxStackSize = 1;
			val2.m_maxQuality = 1;
			val2.m_weight = 0.5f;
			val2.m_useDurability = false;
			val2.m_maxDurability = 100f;
			val2.m_equipDuration = 0.25f;
			val2.m_buildPieces = null;
			val2.m_dlc = "";
			val2.m_attackForce = 0f;
			val2.m_variants = 1;
			val2.m_attack = new Attack();
			val2.m_secondaryAttack = new Attack();
			val2.m_icons = (Sprite[])(Application.isBatchMode ? ((Array)hammer.GetComponent<ItemDrop>().m_itemData.m_shared.m_icons) : ((Array)new Sprite[1] { LoadIcon() }));
			itemData.m_shared = val2;
			component.m_itemData.m_dropPrefab = val;
			component.m_itemData.m_quality = 1;
			component.m_itemData.m_stack = 1;
			component.m_itemData.m_durability = 100f;
			if (Application.isBatchMode)
			{
				BrushPrefab = val;
				return;
			}
			Transform val3 = val.transform.Find("attach");
			if (!Object.op_Implicit((Object)(object)val3))
			{
				throw new InvalidOperationException("Hammer attach transform missing");
			}
			Material val4 = (from r in ((Component)val3).GetComponentsInChildren<Renderer>(true)
				select r.sharedMaterial).FirstOrDefault((Func<Material, bool>)((Material m) => Object.op_Implicit((Object)(object)m) && Object.op_Implicit((Object)(object)m.shader)));
			if (!Object.op_Implicit((Object)(object)val4))
			{
				throw new InvalidOperationException("No supported material on Hammer");
			}
			Renderer[] componentsInChildren = ((Component)val3).GetComponentsInChildren<Renderer>(true);
			for (int num = 0; num < componentsInChildren.Length; num++)
			{
				componentsInChildren[num].enabled = false;
			}
			Material material = ColoredMaterial(val4, new Color(0.6f, 0.4f, 0.2f), "BrushWood");
			Material material2 = ColoredMaterial(val4, new Color(0.4f, 0.26f, 0.13f), "BrushBinding");
			Material material3 = ColoredMaterial(val4, new Color(0.27f, 0.15f, 0.055f), "BrushBristles");
			MakePart(val3, "Handle", new Vector3(0f, 0.1f, 0f), new Vector3(0.035f, 0.17f, 0.035f), material);
			MakePart(val3, "Binding", new Vector3(0f, 0.285f, 0f), new Vector3(0.067f, 0.04f, 0.067f), material2);
			for (int num2 = 0; num2 < 7; num2++)
			{
				float num3 = (float)num2 * (float)Math.PI * 2f / 7f;
				MakePart(val3, "Bristle" + num2, new Vector3(Mathf.Cos(num3) * 0.023f, 0.355f, Mathf.Sin(num3) * 0.023f), new Vector3(0.019f, 0.055f + (float)(num2 % 3) * 0.003f, 0.019f), material3);
			}
			BrushPrefab = val;
		}
		catch
		{
			Object.Destroy((Object)(object)val);
			throw;
		}
	}

	private Material ColoredMaterial(Material source, Color color, string name)
	{
		//IL_0001: 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_000e: Expected O, but got Unknown
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		Material val = new Material(source)
		{
			name = name
		};
		if (val.HasProperty("_MainTex"))
		{
			val.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture);
		}
		if (val.HasProperty("_Color"))
		{
			val.SetColor("_Color", color);
		}
		if (val.HasProperty("_BaseColor"))
		{
			val.SetColor("_BaseColor", color);
		}
		_materials.Add(val);
		return val;
	}

	private static void MakePart(Transform parent, string name, Vector3 position, Vector3 scale, Material material)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		GameObject obj = GameObject.CreatePrimitive((PrimitiveType)2);
		((Object)obj).name = name;
		obj.transform.SetParent(parent, false);
		obj.transform.localPosition = position;
		obj.transform.localScale = scale;
		Collider component = obj.GetComponent<Collider>();
		component.enabled = false;
		Object.Destroy((Object)(object)component);
		obj.GetComponent<Renderer>().sharedMaterial = material;
		obj.layer = ((Component)parent).gameObject.layer;
	}

	private Sprite LoadIcon()
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Expected O, but got Unknown
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)_icon))
		{
			return _icon;
		}
		using Stream stream = typeof(WaterproofBrushPlugin).Assembly.GetManifestResourceStream("ValheimWaterproofBrush.WaterproofBrushIcon.png");
		if (stream == null)
		{
			throw new InvalidOperationException("Embedded brush icon missing");
		}
		using MemoryStream memoryStream = new MemoryStream();
		stream.CopyTo(memoryStream);
		Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
		if (!ImageConversion.LoadImage(val, memoryStream.ToArray(), true))
		{
			Object.Destroy((Object)(object)val);
			throw new InvalidOperationException("Invalid brush PNG");
		}
		((Object)val).name = "WaterproofBrushIcon";
		_icon = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 128f);
		return _icon;
	}

	internal void SyncVisual(WearNTear wear)
	{
		SyncVisual(wear, IsProtected(wear));
	}

	private void SyncVisual(WearNTear wear, bool coated)
	{
		if (!Object.op_Implicit((Object)(object)wear) || Application.isBatchMode || !Object.op_Implicit((Object)(object)MaterialMan.instance))
		{
			return;
		}
		try
		{
			if (!coated)
			{
				if (_tinted.Remove(wear) && !_highlighting.Contains(wear))
				{
					MaterialMan.instance.ResetValue(((Component)wear).gameObject, ShaderProps._Color);
				}
			}
			else if (_tinted.Add(wear) && !_highlighting.Contains(wear))
			{
				ApplyCoatingTint(wear);
			}
		}
		catch (Exception ex)
		{
			ReportOnce("visual", ex);
		}
	}

	private void ApplyCoatingTint(WearNTear wear)
	{
		//IL_002c: 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_0055: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)wear) && Object.op_Implicit((Object)(object)MaterialMan.instance) && !Application.isBatchMode)
		{
			MaterialMan.instance.SetValue<Color>(((Component)wear).gameObject, ShaderProps._Color, Color.Lerp(Color.white, new Color(0.7f, 0.42f, 0.08f, 1f), _tintStrength.Value), false);
		}
	}

	internal void HighlightStarted(WearNTear wear)
	{
		if (IsProtected(wear))
		{
			_highlighting.Add(wear);
		}
	}

	internal void HighlightEnded(WearNTear wear)
	{
		_highlighting.Remove(wear);
		if (IsProtected(wear))
		{
			_tinted.Add(wear);
			ApplyCoatingTint(wear);
		}
	}

	internal void Forget(WearNTear wear)
	{
		_tinted.Remove(wear);
		_highlighting.Remove(wear);
	}

	private void ReleaseAssets()
	{
		foreach (WearNTear item in _tinted)
		{
			if (Object.op_Implicit((Object)(object)item) && Object.op_Implicit((Object)(object)MaterialMan.instance) && !_highlighting.Contains(item))
			{
				MaterialMan.instance.ResetValue(((Component)item).gameObject, ShaderProps._Color);
			}
		}
		_tinted.Clear();
		_highlighting.Clear();
		foreach (Material material in _materials)
		{
			if (Object.op_Implicit((Object)(object)material))
			{
				Object.Destroy((Object)(object)material);
			}
		}
		if (Object.op_Implicit((Object)(object)_icon))
		{
			Object.Destroy((Object)(object)_icon.texture);
			Object.Destroy((Object)(object)_icon);
		}
		if (Object.op_Implicit((Object)(object)_prefabRoot))
		{
			Object.Destroy((Object)(object)_prefabRoot);
		}
		if (Object.op_Implicit((Object)(object)BrushRecipe))
		{
			Object.Destroy((Object)(object)BrushRecipe);
		}
		BrushRecipe = null;
		BrushPrefab = null;
	}

	internal unsafe void UseBrush(Player player)
	{
		//IL_00c0: 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_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		if (!Ready || !EnsureBrushSession() || CameraInputSuppressed || CameraModal() || Time.unscaledTime < _nextUse)
		{
			return;
		}
		_nextUse = Time.unscaledTime + 0.25f;
		if ((Object)(object)player != (Object)(object)Player.m_localPlayer || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || InventoryGui.IsVisible() || Hud.InRadial())
		{
			return;
		}
		try
		{
			if (!TryGetBrushTarget(player, out var target))
			{
				Tell(player, "Aim at a wooden building piece within " + (WaterproofCameraActive ? CameraWaterproofReach : 5f) + " metres.");
				return;
			}
			WearNTear wear = target.Wear;
			Piece piece = target.Piece;
			if (!PrivateArea.CheckAccess(((Component)piece).transform.position, 0f, true, false))
			{
				Tell(player, "A ward prevents you from coating this piece.");
				return;
			}
			ZNetView component = ((Component)wear).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				Tell(player, "This piece is not ready to save yet. Try again.");
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO.GetBool(-166505142, false))
			{
				SyncVisual(wear);
				Tell(player, "This piece is already waterproof.");
				return;
			}
			component.ClaimOwnership();
			Exception error;
			Outcome outcome = CoatWithResin(player, new GameCoating(component), out error);
			if (outcome == Outcome.Success)
			{
				BrushServerProtection.CoatingChanged(zDO);
				ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
				string[] obj = new string[6]
				{
					"[coat.ok] piece=",
					((Object)((Component)piece).gameObject).name,
					"; zdo=",
					null,
					null,
					null
				};
				ZDOID uid = zDO.m_uid;
				obj[3] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
				obj[4] = "; useResin=";
				obj[5] = _useResin.Value.ToString();
				logger.LogInfo((object)string.Concat(obj));
				SyncVisual(wear);
				PlayBrushSound(target.Point);
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[coat.blocked] " + outcome));
				if (error != null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)error);
				}
				Tell(player, outcome switch
				{
					Outcome.MissingResource => "You need 1 Resin in your inventory or an accessible Easy Craft chest.", 
					Outcome.ResourceBusy => "Another chest operation is busy. Try again in a moment.", 
					Outcome.Synchronizing => "Waiting for chest access. Try the brush again in a moment.", 
					Outcome.AlreadyProtected => "Already waterproof.", 
					Outcome.NotOwner => "Could not take ownership of that piece. Try again.", 
					_ => "Coating did not complete. See BepInEx/LogOutput.log.", 
				});
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[coat.error] " + ex));
			Tell(player, "Brush error. See BepInEx/LogOutput.log.");
		}
	}

	internal static bool TryGetBrushTarget(Player player, out BrushTarget target)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: 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_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		target = default(BrushTarget);
		GameCamera instance = GameCamera.instance;
		if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)instance))
		{
			return false;
		}
		float num = ((Object.op_Implicit((Object)(object)Instance) && Instance.WaterproofCameraActive) ? Instance.CameraWaterproofReach : Math.Min(5f, player.m_maxPlaceDistance));
		Vector3 eyePoint = ((Character)player).GetEyePoint();
		float num2 = CameraMath.BuildRayDistance(Vector3.Distance(((Component)instance).transform.position, eyePoint), num, 0f);
		RaycastHit val = default(RaycastHit);
		if (!Physics.Raycast(((Component)instance).transform.position, ((Component)instance).transform.forward, ref val, num2, (int)RemoveRayMask.GetValue(player), (QueryTriggerInteraction)1))
		{
			return false;
		}
		if (Vector3.Distance(eyePoint, ((RaycastHit)(ref val)).point) > num)
		{
			return false;
		}
		Piece componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Piece>();
		WearNTear val2 = (Object.op_Implicit((Object)(object)componentInParent) ? ((Component)componentInParent).GetComponent<WearNTear>() : null);
		if (!Object.op_Implicit((Object)(object)componentInParent) || !Object.op_Implicit((Object)(object)val2) || !IsWood(val2))
		{
			return false;
		}
		ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>();
		if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
		{
			return false;
		}
		target = new BrushTarget
		{
			Piece = componentInParent,
			Wear = val2,
			Point = ((RaycastHit)(ref val)).point
		};
		return true;
	}

	internal static bool IsWood(WearNTear wear)
	{
		//IL_0001: 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_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Invalid comparison between Unknown and I4
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Invalid comparison between Unknown and I4
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Invalid comparison between Unknown and I4
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Invalid comparison between Unknown and I4
		MaterialType materialType = wear.m_materialType;
		if ((int)materialType <= 3)
		{
			if ((int)materialType == 0 || (int)materialType == 3)
			{
				goto IL_001c;
			}
		}
		else if ((int)materialType == 6 || (int)materialType == 8)
		{
			goto IL_001c;
		}
		return false;
		IL_001c:
		return true;
	}

	internal static bool IsProtected(WearNTear wear)
	{
		return BrushWeatherProtection.IsCoated(wear);
	}

	private ChestResinCompatibility ChestResin()
	{
		if (!_chestResinResolved)
		{
			_chestResinResolved = true;
			if (Chainloader.PluginInfos.TryGetValue("com.modmaking.easycraftfromownchests", out var value) && Object.op_Implicit((Object)(object)value.Instance))
			{
				try
				{
					_chestResin = new ChestResinCompatibility(((object)value.Instance).GetType(), typeof(Player), typeof(Recipe), typeof(Inventory));
					((BaseUnityPlugin)this).Logger.LogInfo((object)"[resin.chests] Using Easy Craft & Build's chest permissions and resource transactions.");
				}
				catch (Exception ex)
				{
					ReportOnce("resin.chest.compatibility", ex);
				}
			}
		}
		return _chestResin;
	}

	private Outcome CoatWithResin(Player player, ICoating coating, out Exception error)
	{
		if (!_useResin.Value)
		{
			return Transactions.Coat(coating, out error);
		}
		GameInventory gameInventory = new GameInventory(player);
		if (gameInventory.Count("Resin") > 0)
		{
			return Transactions.Coat(coating, gameInventory, useResin: true, out error);
		}
		ChestResinCompatibility chestResinCompatibility = ChestResin();
		if (chestResinCompatibility != null && chestResinCompatibility.Available)
		{
			return chestResinCompatibility.Coat(player, ((Humanoid)player).GetInventory(), GameInventory.ResourceName("Resin"), coating, out error);
		}
		return Transactions.Coat(coating, gameInventory, useResin: true, out error);
	}

	private void BindFeedbackConfig()
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Expected O, but got Unknown
		_tintStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Appearance", "CoatingTintStrength", 0.18f, new ConfigDescription("Subtle warm finish on waterproofed wood. Requires reloading the world after changing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
		_soundVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "BrushVolume", 0.75f, new ConfigDescription("Volume of the resin rubbing sound; also follows the game's sound-effects volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
	}

	internal static bool BrushSelected(Player player)
	{
		if (Ready && Object.op_Implicit((Object)(object)player) && (Object)(object)player == (Object)(object)Player.m_localPlayer)
		{
			return IsBrush(((Humanoid)player).RightItem);
		}
		return false;
	}

	private static bool CanShowBrush(Player player)
	{
		if (BrushSelected(player) && CanUseTool(player))
		{
			return !CameraModal();
		}
		return false;
	}

	internal void UpdateBrushHud(Hud hud, Player player)
	{
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			_brushHud = hud;
			bool flag = CanShowBrush(player);
			hud.m_buildHud.SetActive(flag);
			if (!flag)
			{
				ClearHover();
				return;
			}
			if (Object.op_Implicit((Object)(object)hud.m_buildUi) && ((Component)hud.m_buildUi).gameObject.activeSelf)
			{
				hud.m_buildUi.Close();
			}
			if (Object.op_Implicit((Object)(object)hud.m_closePieceSelectionButton))
			{
				((Component)hud.m_closePieceSelectionButton).gameObject.SetActive(false);
			}
			object? value = SnappingIcon.GetValue(hud);
			Image val = (Image)((value is Image) ? value : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				((Behaviour)val).enabled = false;
			}
			hud.m_buildSelection.text = "Waterproof";
			((Behaviour)hud.m_buildIcon).enabled = true;
			hud.m_buildIcon.sprite = _icon;
			if (TryGetBrushTarget(player, out var target))
			{
				_hoverTarget = target;
				target.Wear.Highlight();
				hud.m_pieceDescription.text = (IsProtected(target.Wear) ? "<color=#66FF99><b>Already waterproofed</b></color>" : "Waterproof");
			}
			else
			{
				ClearHover();
				hud.m_pieceDescription.text = "Aim at wood";
			}
			TMP_Text pieceDescription = hud.m_pieceDescription;
			pieceDescription.text = pieceDescription.text + "\n" + ((object)_cameraKey.Value/*cast due to .constrained prefix*/).ToString() + ": camera · both sticks: toggle" + (WaterproofCameraActive ? (" · " + CameraWaterproofReach + "m reach") : "");
			GameObject[] requirementItems = hud.m_requirementItems;
			for (int i = 0; i < requirementItems.Length; i++)
			{
				requirementItems[i].SetActive(false);
			}
		}
		catch (Exception ex)
		{
			ReportOnce("brush.hud", ex);
		}
	}

	internal void UpdateBrushCrosshair(Hud hud, Player player)
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		if (CanShowBrush(player))
		{
			WearNTear wear = _hoverTarget.Wear;
			if (Object.op_Implicit((Object)(object)wear) && Object.op_Implicit((Object)(object)_hoverTarget.Piece))
			{
				bool flag = IsProtected(wear);
				((TMP_Text)hud.m_hoverName).text = Localization.instance.Localize(_hoverTarget.Piece.m_name) + (flag ? "\n<color=#66FF99><b>Waterproofed</b></color>" : (_useResin.Value ? "\n<color=yellow>Waterproof (1 Resin)</color>" : "\n<color=yellow>Waterproof</color>"));
				((Graphic)hud.m_crosshair).color = (Color)(flag ? new Color(0.4f, 1f, 0.6f) : Color.yellow);
				((Component)hud.m_pieceHealthRoot).gameObject.SetActive(true);
				hud.m_pieceHealthBar.SetValue(wear.GetHealthPercentage());
			}
		}
	}

	internal void ClearHover()
	{
		_hoverTarget = default(BrushTarget);
	}

	internal void PlayBrushSound(Vector3 point)
	{
		//IL_01e3: 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_017b: Unknown result type (might be due to invalid IL or missing references)
		if (Application.isBatchMode || _soundVolume.Value <= 0f)
		{
			return;
		}
		try
		{
			if (!Object.op_Implicit((Object)(object)_brushSound))
			{
				using Stream stream = typeof(WaterproofBrushPlugin).Assembly.GetManifestResourceStream("ValheimWaterproofBrush.BrushRub.wav");
				PcmWave pcmWave = PcmWave.Read(stream);
				_brushSound = AudioClip.Create("WaterproofBrush_ResinRub", pcmWave.Samples.Length / pcmWave.Channels, pcmWave.Channels, pcmWave.SampleRate, false);
				if (!_brushSound.SetData(pcmWave.Samples, 0))
				{
					Object.Destroy((Object)(object)_brushSound);
					_brushSound = null;
					throw new InvalidDataException("AudioClip.SetData failed");
				}
			}
			if (!Object.op_Implicit((Object)(object)_brushMixer) && Object.op_Implicit((Object)(object)AudioMan.instance))
			{
				AudioMixer masterMixer = AudioMan.instance.m_masterMixer;
				if (Object.op_Implicit((Object)(object)masterMixer))
				{
					_brushMixer = ((IEnumerable<AudioMixerGroup>)masterMixer.FindMatchingGroups("")).FirstOrDefault((Func<AudioMixerGroup, bool>)((AudioMixerGroup group) => string.Equals(((Object)group).name, "SFX", StringComparison.OrdinalIgnoreCase)));
				}
				if (!Object.op_Implicit((Object)(object)_brushMixer))
				{
					_brushMixer = AudioMan.instance.m_guiMixer;
				}
			}
			int num = _nextBrushSource++ % _brushSources.Length;
			AudioSource val = _brushSources[num];
			if (!Object.op_Implicit((Object)(object)val))
			{
				GameObject val2 = new GameObject("WaterproofBrush_RubAudio" + num);
				val2.transform.SetParent(((Component)this).transform, false);
				val = val2.AddComponent<AudioSource>();
				val.playOnAwake = false;
				val.loop = false;
				val.spatialBlend = 1f;
				val.minDistance = 4f;
				val.maxDistance = 18f;
				val.rolloffMode = (AudioRolloffMode)1;
				val.dopplerLevel = 0f;
				_brushSources[num] = val;
			}
			((Component)val).transform.position = point;
			val.outputAudioMixerGroup = _brushMixer;
			val.pitch = Random.Range(0.96f, 1.04f);
			val.PlayOneShot(_brushSound, _soundVolume.Value * (Object.op_Implicit((Object)(object)_brushMixer) ? 1f : AudioMan.GetSFXVolume()));
		}
		catch (Exception ex)
		{
			ReportOnce("brush.audio", ex);
		}
	}

	private void ReleaseFeedback()
	{
		ClearHover();
		if (Object.op_Implicit((Object)(object)_brushHud) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && IsBrush(((Humanoid)Player.m_localPlayer).RightItem))
		{
			_brushHud.m_buildHud.SetActive(false);
		}
		AudioSource[] brushSources = _brushSources;
		foreach (AudioSource val in brushSources)
		{
			if (Object.op_Implicit((Object)(object)val))
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
		}
		if (Object.op_Implicit((Object)(object)_brushSound))
		{
			Object.Destroy((Object)(object)_brushSound);
		}
	}

	private void TickClearBuildingView()
	{
		if (WaterproofCameraActive)
		{
			ClearBuildingView.Tick(active: true, CameraModal());
		}
	}

	private void DrawClearBuildingView()
	{
		if (WaterproofCameraActive)
		{
			ClearBuildingView.Draw(active: true, CameraModal());
		}
	}

	private void OnGUI()
	{
		DrawClearBuildingView();
	}
}
[HarmonyPatch(typeof(ObjectDB), "Awake")]
internal static class BrushDbAwakePatch
{
	private static void Postfix(ObjectDB __instance)
	{
		WaterproofBrushPlugin.SafeRegister(__instance);
	}
}
[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
internal static class BrushDbCopyPatch
{
	private static void Postfix(ObjectDB __instance)
	{
		WaterproofBrushPlugin.SafeRegister(__instance);
	}
}
[HarmonyPatch(typeof(ZNetScene), "Awake")]
internal static class BrushScenePatch
{
	private static void Postfix()
	{
		WaterproofBrushPlugin.SafeRegister(ObjectDB.instance);
	}
}
[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
internal static class BrushCraftPatch
{
	private static bool Prefix(InventoryGui __instance, Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem, bool ___m_multiCrafting, int ___m_multiCraftAmount)
	{
		if (!WaterproofBrushPlugin.Ready || !WaterproofBrushPlugin.IsBrushRecipe(___m_craftRecipe))
		{
			return true;
		}
		WaterproofBrushPlugin.Instance.CompleteCraft(__instance, player, ___m_craftRecipe, ___m_craftUpgradeItem, ___m_multiCrafting, ___m_multiCraftAmount);
		return false;
	}
}
[HarmonyPatch(typeof(Player), "SetControls")]
internal static class BrushControlsPatch
{
	private static void Prefix(Player __instance, ref Vector3 movedir, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool autoRun, ref bool dodge)
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		if (!WaterproofBrushPlugin.Ready || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
		{
			return;
		}
		WaterproofBrushPlugin instance = WaterproofBrushPlugin.Instance;
		instance.EnsureBrushSession();
		if (WaterproofBrushPlugin.IsBrush(((Humanoid)__instance).RightItem))
		{
			if (instance.CameraOwnsControls)
			{
				movedir = Vector3.zero;
				jump = (crouch = (run = (autoRun = (dodge = false))));
				__instance.m_autoRun = false;
				((Character)__instance).StopMovement();
			}
			if (attack)
			{
				instance.UseBrush(__instance);
			}
			attack = (attackHold = (secondaryAttack = (secondaryAttackHold = (block = (blockHold = false)))));
		}
	}
}
[HarmonyPatch(typeof(Player), "PlayerAttackInput")]
internal static class BrushNoAttackPatch
{
	private static bool Prefix(Player __instance)
	{
		if (WaterproofBrushPlugin.Ready)
		{
			return !WaterproofBrushPlugin.IsBrush(((Humanoid)__instance).RightItem);
		}
		return true;
	}
}
[HarmonyPatch(typeof(WearNTear), "UpdateWear")]
internal static class BrushWeatherVisualPatch
{
	private static void Postfix(WearNTear __instance)
	{
		if (WaterproofBrushPlugin.Ready && Object.op_Implicit((Object)(object)WaterproofBrushPlugin.Instance))
		{
			WaterproofBrushPlugin.Instance.SyncVisual(__instance);
		}
	}
}
[HarmonyPatch(typeof(WearNTear), "Start")]
internal static class BrushPieceStartPatch
{
	private static void Postfix(WearNTear __instance)
	{
		if (WaterproofBrushPlugin.Ready)
		{
			WaterproofBrushPlugin.Instance.SyncVisual(__instance);
		}
	}
}
[HarmonyPatch(typeof(WearNTear), "OnDestroy")]
internal static class BrushPieceDestroyPatch
{
	private static void Prefix(WearNTear __instance)
	{
		if (Object.op_Implicit((Object)(object)WaterproofBrushPlugin.Instance))
		{
			WaterproofBrushPlugin.Instance.Forget(__instance);
		}
	}
}
[HarmonyPatch(typeof(VisEquipment), "AttachBackItem")]
internal static class BrushHolsterPatch
{
	private static void Postfix(VisEquipment __instance, int hash, GameObject __result)
	{
		//IL_002c: 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_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)__result) && (hash == StringExtensionMethods.GetStableHashCode("WaterproofingBrush") || hash == -673997157))
		{
			Transform transform = ((Component)__instance).transform;
			__result.transform.rotation = Quaternion.LookRotation(transform.forward, -transform.up);
		}
	}
}
[HarmonyPatch(typeof(Player), "Update")]
internal static class WaterproofCameraEarlyInputPatch
{
	private static void Prefix(Player __instance)
	{
		if (WaterproofBrushPlugin.Ready && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
		{
			WaterproofBrushPlugin.Instance.TickBrushCamera();
		}
	}
}
[HarmonyPatch(typeof(GameCamera), "LateUpdate")]
internal static class WaterproofCameraFinalPosePatch
{
	[HarmonyAfter(new string[] { "valheim.buildcamera" })]
	private static void Postfix()
	{
		WaterproofBrushPlugin instance = WaterproofBrushPlugin.Instance;
		if (Object.op_Implicit((Object)(object)instance) && instance.EnsureBrushSession() && instance.WaterproofCameraActive)
		{
			instance.ApplyWaterproofCameraPose();
		}
	}
}
[HarmonyPatch(typeof(Player), "SetMouseLook")]
internal static class WaterproofCameraBodyLookPatch
{
	private static bool Prefix(Player __instance)
	{
		if (WaterproofBrushPlugin.Ready && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
		{
			return !WaterproofBrushPlugin.Instance.CameraOwnsControls;
		}
		return true;
	}
}
[HarmonyPatch(typeof(Player), "StartGuardianPower")]
internal static class WaterproofCameraGuardianPatch
{
	private static bool Prefix(Player __instance)
	{
		if (WaterproofBrushPlugin.Ready && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
		{
			return !WaterproofBrushPlugin.Instance.CameraOwnsControls;
		}
		return true;
	}
}
internal static class BrushWeatherProtection
{
	internal const string HarmonyId = "valheim.waterproofbrush.weather";

	private static Harmony _harmony;

	internal static bool Installed { get; private set; }

	internal static void Install()
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Expected O, but got Unknown
		MethodInfo method = typeof(WearNTear).GetMethod("UpdateWear", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(float) }, null);
		if (method == null)
		{
			throw new MissingMethodException("WearNTear.UpdateWear(float)");
		}
		_harmony = new Harmony("valheim.waterproofbrush.weather");
		try
		{
			_harmony.Patch((MethodBase)method, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BrushWeatherProtection), "Transpiler", (Type[])null), (HarmonyMethod)null);
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)method);
			if (patchInfo == null || !patchInfo.Transpilers.Any((Patch p) => p.owner == "valheim.waterproofbrush.weather"))
			{
				throw new InvalidOperationException("Saved-coating weather patch was not installed");
			}
			Installed = true;
		}
		catch
		{
			_harmony.UnpatchAll("valheim.waterproofbrush.weather");
			_harmony = null;
			Installed = false;
			throw;
		}
	}

	internal static void Uninstall()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchAll("valheim.waterproofbrush.weather");
		}
		_harmony = null;
		Installed = false;
	}

	internal static bool IsCoated(WearNTear wear)
	{
		if (!Object.op_Implicit((Object)(object)wear) || !WaterproofBrushPlugin.IsWood(wear))
		{
			return false;
		}
		ZNetView component = ((Component)wear).GetComponent<ZNetView>();
		if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
		{
			return false;
		}
		ZDO zDO = component.GetZDO();
		if (zDO != null)
		{
			return zDO.GetBool(-166505142, false);
		}
		return false;
	}

	internal static bool AllowWeatherWear(WearNTear wear)
	{
		if (wear.m_noRoofWear)
		{
			return !IsCoated(wear);
		}
		return false;
	}

	internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> source)
	{
		List<CodeInstruction> list = source.Select((Func<CodeInstruction, CodeInstruction>)((CodeInstruction i) => new CodeInstruction(i))).ToList();
		FieldInfo field = typeof(WearNTear).GetField("m_noRoofWear");
		MethodInfo method = typeof(BrushWeatherProtection).GetMethod("AllowWeatherWear", BindingFlags.Static | BindingFlags.NonPublic);
		CodeInstruction[] array = list.Where((CodeInstruction i) => i.opcode == OpCodes.Ldfld && object.Equals(i.operand, field)).ToArray();
		if (field == null || method == null || array.Length != 2)
		{
			throw new InvalidOperationException("Unsupported WearNTear weather layout: expected 2 rain-wear checks, found " + array.Length);
		}
		CodeInstruction[] array2 = array;
		foreach (CodeInstruction obj in array2)
		{
			obj.opcode = OpCodes.Call;
			obj.operand = method;
		}
		return list;
	}
}
internal static class BrushServerProtection
{
	internal const string HarmonyId = "valheim.waterproofbrush.serverweather";

	internal const string FieldsKey = "HasFields";

	internal const string WearFieldsKey = "HasFieldsWearNTear";

	internal const string RainFieldKey = "WearNTear.m_noRoofWear";

	private static Harmony _harmony;

	private static ZDOMan _world;

	private static readonly Queue<ZDOID> Pending = new Queue<ZDOID>();

	private static readonly HashSet<ZDOID> Queued = new HashSet<ZDOID>();

	private static readonly HashSet<ZDOID> Retiring = new HashSet<ZDOID>();

	private static readonly HashSet<ZDOID> ReportedFailures = new HashSet<ZDOID>();

	private static bool _recreating;

	private static Action<string> _log;

	private static bool IsServer
	{
		get
		{
			if (ZNet.instance != null)
			{
				return ZNet.instance.IsServer();
			}
			return false;
		}
	}

	internal static void Install(Action<string> log)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Expected O, but got Unknown
		_log = log;
		_harmony = new Harmony("valheim.waterproofbrush.serverweather");
		try
		{
			if (typeof(ZNetView).GetMethod("LoadFields", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) == null)
			{
				throw new MissingMethodException("ZNetView.LoadFields");
			}
			Patch(typeof(ZDO), "Deserialize", "BeforeReceive", "AfterReceive");
			Patch(typeof(ZDOMan), "Update", "BeforeWorldUpdate", null);
			Patch(typeof(ZDOMan), "ResetBeforeLoad", null, "Reset");
			Patch(typeof(ZDOMan), "ShutDown", null, "Reset");
			Patch(typeof(ZDOMan), "HandleDestroyedZDO", null, "AfterDestroyed");
		}
		catch
		{
			Uninstall();
			throw;
		}
	}

	private static void Patch(Type type, string name, string prefix, string postfix)
	{
		//IL_0035: 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)
		MethodInfo method = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (method == null)
		{
			throw new MissingMethodException(type.Name, name);
		}
		_harmony.Patch((MethodBase)method, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(BrushServerProtection), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(BrushServerProtection), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		Patches patchInfo = Harmony.GetPatchInfo((MethodBase)method);
		if (patchInfo == null || !patchInfo.Owners.Contains("valheim.waterproofbrush.serverweather"))
		{
			throw new InvalidOperationException("Server weather patch missing: " + name);
		}
	}

	internal static void Uninstall()
	{
		if (_harmony != null)
		{
			_harmony.UnpatchAll("valheim.waterproofbrush.serverweather");
		}
		_harmony = null;
		Reset();
	}

	internal static void Reset()
	{
		_world = null;
		Pending.Clear();
		Queued.Clear();
		Retiring.Clear();
		ReportedFailures.Clear();
		_recreating = false;
	}

	internal static bool ApplyNative(ZDO data)
	{
		if (data == null || !data.GetBool(-166505142, false))
		{
			return false;
		}
		if (data.GetBool("HasFields", false) && data.GetBool("HasFieldsWearNTear", false) && !data.GetBool("WearNTear.m_noRoofWear", true))
		{
			return false;
		}
		data.Set("HasFields", true);
		data.Set("HasFieldsWearNTear", true);
		data.Set("WearNTear.m_noRoofWear", false);
		return true;
	}

	internal static void CoatingChanged(ZDO data)
	{
		//IL_0010: 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_003d: Unknown result type (might be due to invalid IL or missing references)
		if (IsServer && data != null && !Retiring.Contains(data.m_uid) && ApplyNative(data) && Queued.Add(data.m_uid))
		{
			Pending.Enqueue(data.m_uid);
		}
	}

	internal static void BeforeReceive(ZDO __instance, out bool __state)
	{
		__state = IsServer && !_recreating && __instance.GetBool(-166505142, false);
	}

	internal static void AfterReceive(ZDO __instance, bool __state)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		if (IsServer && !_recreating && !Retiring.Contains(__instance.m_uid))
		{
			if (__state && !__instance.GetBool(-166505142, false))
			{
				__instance.Set(-166505142, true);
			}
			CoatingChanged(__instance);
		}
	}

	internal unsafe static void BeforeWorldUpdate(ZDOMan __instance, Dictionary<ZDOID, ZDO> ___m_objectsByID, List<ZDOID> ___m_destroySendList)
	{
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: 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_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: 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)
		//IL_00c7: 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)
		if (!IsServer)
		{
			return;
		}
		if (_world != __instance)
		{
			int num = 0;
			foreach (ZDO value in ___m_objectsByID.Values)
			{
				if (ApplyNative(value))
				{
					num++;
				}
			}
			_world = __instance;
			if (_log != null)
			{
				_log("[serverweather.world] Ready for vanilla clients; upgraded saved coatings=" + num);
			}
		}
		int num2 = Math.Min(32, Pending.Count);
		for (int i = 0; i < num2; i++)
		{
			ZDOID val = Pending.Dequeue();
			Queued.Remove(val);
			ZDO zDO = __instance.GetZDO(val);
			if (zDO == null || !zDO.IsValid() || Retiring.Contains(val) || ___m_destroySendList.Contains(val) || !zDO.GetBool(-166505142, false))
			{
				continue;
			}
			try
			{
				Refresh(__instance, zDO);
			}
			catch (Exception ex)
			{
				if (Queued.Add(val))
				{
					Pending.Enqueue(val);
				}
				if (ReportedFailures.Add(val) && _log != null)
				{
					Action<string> log = _log;
					ZDOID val2 = val;
					log("[serverweather.refresh.failed] Will retry " + ((object)(*(ZDOID*)(&val2))/*cast due to .constrained prefix*/).ToString() + ": " + ex);
				}
			}
		}
	}

	private unsafe static void Refresh(ZDOMan world, ZDO original)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		ZPackage val