Decompiled source of WaterproofYourWood v1.0.13

BepInEx/plugins/WaterproofingBrush/ValheimWaterproofBrush.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using WaterproofBrushLogic;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.12.0")]
[assembly: AssemblyVersion("1.0.12.0")]
[BepInPlugin("com.lesly.valheim.waterproofbrush", "Waterproofing Brush", "1.0.12")]
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();
		}

		private 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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 compatibility = Instance._craftCompatibility;
			if (compatibility.HasItemFlag)
			{
				string woodName = ResourceName("Wood");
				bool resourceCheated = _inventory.GetAllItems().Any((ItemData resource) => resource.m_shared.m_name == woodName && resource.m_worldLevel >= Game.m_worldLevel && compatibility.IsItemCheated(resource));
				int? stationCheatedKey = compatibility.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);
				compatibility.SetItemMetadata(val, _player.NoCostCheat(), resourceCheated, 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(WaterproofHash, false);
				}
				return false;
			}
			set
			{
				if (!CanWrite)
				{
					throw new InvalidOperationException("Cannot save coating without piece ownership");
				}
				_view.GetZDO().Set(WaterproofHash, value);
			}
		}

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

	public const string PluginGuid = "com.lesly.valheim.waterproofbrush";

	public const string PluginName = "Waterproofing Brush";

	public const string PluginVersion = "1.0.12";

	internal const string BrushPrefabName = "Lesly_WaterproofingBrush";

	internal const string WaterproofKey = "Lesly_WaterproofBrush_Protected";

	internal static readonly int WaterproofHash = StringExtensionMethods.GetStableHashCode("Lesly_WaterproofBrush_Protected");

	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 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 RainTimer;

	private static FieldInfo RainWet;

	private static FieldInfo SnappingIcon;

	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 ConfigEntry<float> _tintStrength;

	private ConfigEntry<float> _soundVolume;

	private BrushTarget _hoverTarget;

	private Hud _brushHud;

	private Requirement _resinRequirement;

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

	private AudioClip _brushSound;

	private AudioMixerGroup _brushMixer;

	private int _nextBrushSource;

	private void Awake()
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Expected O, but got Unknown
		Instance = this;
		_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();
		try
		{
			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 = new Harmony("com.lesly.valheim.waterproofbrush");
			_harmony.PatchAll(typeof(WaterproofBrushPlugin).Assembly);
			VerifyPatches();
			Ready = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[startup.ok] Waterproofing Brush 1.0.12; game=" + Application.version + "; unity=" + Application.unityVersion + "; plugin=" + ((BaseUnityPlugin)this).Info.Location));
		}
		catch (Exception ex)
		{
			Ready = false;
			if (_harmony != null)
			{
				_harmony.UnpatchAll("com.lesly.valheim.waterproofbrush");
			}
			((BaseUnityPlugin)this).Logger.LogError((object)("[startup.failed] Brush disabled; critical API/patch missing. " + ex));
		}
	}

	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));
		DbByHash = Field(typeof(ObjectDB), "m_itemByHash");
		DbByData = Field(typeof(ObjectDB), "m_itemByData");
		SceneByHash = Field(typeof(ZNetScene), "m_namedPrefabs");
		RemoveRayMask = Field(typeof(Player), "m_removeRayMask");
		RainTimer = Field(typeof(WearNTear), "m_rainTimer");
		RainWet = Field(typeof(WearNTear), "m_rainWet");
		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)
			{
				HarmonyPatch val = (HarmonyPatch)customAttributes[0];
				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("com.lesly.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 != 13)
		{
			throw new InvalidOperationException("Incomplete patch set: " + num);
		}
	}

	private void Update()
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		if (!Ready)
		{
			return;
		}
		KeyboardShortcut value = _diagnosticKey.Value;
		if (((KeyboardShortcut)(ref value)).IsDown())
		{
			DumpStatus();
		}
		if (Time.unscaledTime < _nextMaintenance)
		{
			return;
		}
		_nextMaintenance = Time.unscaledTime + 0.5f;
		SafeRegister(ObjectDB.instance);
		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 == "Lesly_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 == "Lesly_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 num = ((!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 num2 = gameInventory.Count("Wood");
			Exception error;
			Outcome outcome = Transactions.Craft(gameInventory, num, free, out error);
			if (outcome == Outcome.Success)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[craft.ok] brushes=" + num + "; wood=" + num2 + "->" + 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, num, gameInventory.LastCreatedCheated);
				}
				catch (Exception ex)
				{
					ReportOnce("craft.feedback", ex);
				}
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[craft.blocked] result=" + outcome.ToString() + "; wood=" + num2 + "; slots=" + gameInventory.FreeSlots + "; amount=" + num));
				if (error != null)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)error);
				}
				Tell(player, outcome switch
				{
					Outcome.FullInventory => "You need " + num + " empty inventory slot(s).", 
					Outcome.MissingResource => "You need " + num * 2 + " Wood.", 
					_ => "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.12; 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()
	{
		Ready = false;
		ReleaseFeedback();
		if (_harmony != null)
		{
			_harmony.UnpatchAll("com.lesly.valheim.waterproofbrush");
		}
		ReleaseAssets();
		Instance = null;
	}

	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)
	{
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0299: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a0: 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_02af: Expected O, but got Unknown
		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("Lesly_WaterproofingBrush") == (Object)(object)BrushPrefab && db.m_recipes.Contains(BrushRecipe)))
		{
			return;
		}
		_registering = true;
		try
		{
			GameObject itemPrefab = db.GetItemPrefab("Hammer");
			GameObject itemPrefab2 = db.GetItemPrefab("Wood");
			if (!Object.op_Implicit((Object)(object)itemPrefab) || !Object.op_Implicit((Object)(object)itemPrefab2))
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)BrushPrefab))
			{
				CreatePrefab(itemPrefab);
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode("Lesly_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;
			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;
			}
			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[1]
				{
					new Requirement
					{
						m_resItem = itemPrefab2.GetComponent<ItemDrop>(),
						m_amount = 2,
						m_amountPerLevel = 0,
						m_recover = true
					}
				};
				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=Lesly_WaterproofingBrush; hash=" + stableHashCode + "; activeSelf=" + BrushPrefab.activeSelf + "; activeInHierarchy=" + BrushPrefab.activeInHierarchy + "; station=" + val.m_name + "; cost=2 Wood; 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 = "Lesly_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; uses 1 Resin. 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("Lesly.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_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		if (!Ready || 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 5 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(WaterproofHash, false))
			{
				SyncVisual(wear);
				Tell(player, "This piece is already waterproof. No resin used.");
				return;
			}
			GameInventory gameInventory = new GameInventory(player);
			if (gameInventory.Count("Resin") < 1)
			{
				Tell(player, "You need 1 Resin in your inventory.");
				return;
			}
			component.ClaimOwnership();
			Exception error;
			Outcome outcome = Transactions.Coat(gameInventory, new GameCoating(component), out error);
			if (outcome == Outcome.Success)
			{
				ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
				string[] obj = new string[5]
				{
					"[coat.ok] piece=",
					((Object)((Component)piece).gameObject).name,
					"; zdo=",
					null,
					null
				};
				ZDOID uid = zDO.m_uid;
				obj[3] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
				obj[4] = "; resin=1";
				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.AlreadyProtected => "Already waterproof. No resin used.", 
					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_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: 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)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: 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;
		}
		RaycastHit val = default(RaycastHit);
		if (!Physics.Raycast(((Component)instance).transform.position, ((Component)instance).transform.forward, ref val, 50f, (int)RemoveRayMask.GetValue(player), (QueryTriggerInteraction)1))
		{
			return false;
		}
		if (Vector3.Distance(((Character)player).GetEyePoint(), ((RaycastHit)(ref val)).point) > Math.Min(5f, player.m_maxPlaceDistance))
		{
			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)
	{
		if (!Ready || !Object.op_Implicit((Object)(object)wear) || !IsWood(wear))
		{
			return false;
		}
		ZNetView component = ((Component)wear).GetComponent<ZNetView>();
		if (Object.op_Implicit((Object)(object)component) && component.IsValid())
		{
			return component.GetZDO().GetBool(WaterproofHash, false);
		}
		return false;
	}

	internal static void BeforeWear(WearNTear wear, out bool? originalRainWear)
	{
		originalRainWear = null;
		if (!Ready)
		{
			return;
		}
		bool flag = IsProtected(wear);
		if (flag)
		{
			originalRainWear = wear.m_noRoofWear;
			wear.m_noRoofWear = false;
			RainTimer.SetValue(wear, 0f);
			RainWet.SetValue(wear, false);
			if (Object.op_Implicit((Object)(object)wear.m_wet))
			{
				wear.m_wet.SetActive(false);
			}
		}
		Instance.SyncVisual(wear, flag);
	}

	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) && !((Character)player).IsDead() && !((Character)player).IsTeleporting() && !((Character)player).InCutscene() && !InventoryGui.IsVisible())
		{
			return !Hud.InRadial();
		}
		return false;
	}

	internal void UpdateBrushHud(Hud hud, Player player)
	{
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_0181: Expected O, but got Unknown
		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>" : "Apply resin to seal this piece");
			}
			else
			{
				ClearHover();
				hud.m_pieceDescription.text = "Aim at wood • 1 Resin per piece";
			}
			GameObject val2 = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab("Resin") : null);
			if (Object.op_Implicit((Object)(object)val2) && (_resinRequirement == null || (Object)(object)_resinRequirement.m_resItem != (Object)(object)val2.GetComponent<ItemDrop>()))
			{
				_resinRequirement = new Requirement
				{
					m_resItem = val2.GetComponent<ItemDrop>(),
					m_amount = 1,
					m_amountPerLevel = 0
				};
			}
			for (int i = 0; i < hud.m_requirementItems.Length; i++)
			{
				GameObject val3 = hud.m_requirementItems[i];
				bool flag2 = i == 0 && _resinRequirement != null && (!Object.op_Implicit((Object)(object)_hoverTarget.Wear) || !IsProtected(_hoverTarget.Wear));
				val3.SetActive(flag2);
				if (flag2)
				{
					InventoryGui.SetupRequirement(val3.transform, _resinRequirement, player, false, 0, 1);
				}
			}
		}
		catch (Exception ex)
		{
			ReportOnce("brush.hud", ex);
		}
	}

	internal void UpdateBrushCrosshair(Hud hud, Player player)
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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>" : "\n<color=yellow>Waterproof · 1 Resin</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("Lesly.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);
		}
	}
}
[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 bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold)
	{
		if (WaterproofBrushPlugin.Ready && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && WaterproofBrushPlugin.IsBrush(((Humanoid)__instance).RightItem))
		{
			if (attack)
			{
				WaterproofBrushPlugin.Instance.UseBrush(__instance);
			}
			attack = (attackHold = (secondaryAttack = (secondaryAttackHold = 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 BrushRainPatch
{
	private static void Prefix(WearNTear __instance, out bool? __state)
	{
		WaterproofBrushPlugin.BeforeWear(__instance, out __state);
	}

	private static void Finalizer(WearNTear __instance, bool? __state)
	{
		if (__state.HasValue && Object.op_Implicit((Object)(object)__instance))
		{
			__instance.m_noRoofWear = __state.Value;
		}
	}
}
[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(Hud), "UpdateBuild")]
internal static class BrushHudPatch
{
	private static bool Prefix(Hud __instance, Player player)
	{
		if (!WaterproofBrushPlugin.BrushSelected(player))
		{
			if (Object.op_Implicit((Object)(object)WaterproofBrushPlugin.Instance))
			{
				WaterproofBrushPlugin.Instance.ClearHover();
			}
			return true;
		}
		WaterproofBrushPlugin.Instance.UpdateBrushHud(__instance, player);
		return false;
	}
}
[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
internal static class BrushCrosshairPatch
{
	private static void Postfix(Hud __instance, Player player)
	{
		if (WaterproofBrushPlugin.BrushSelected(player))
		{
			WaterproofBrushPlugin.Instance.UpdateBrushCrosshair(__instance, player);
		}
	}
}
[HarmonyPatch(typeof(WearNTear), "Highlight")]
internal static class BrushHighlightPatch
{
	private static void Postfix(WearNTear __instance)
	{
		if (WaterproofBrushPlugin.Ready)
		{
			WaterproofBrushPlugin.Instance.HighlightStarted(__instance);
		}
	}
}
[HarmonyPatch(typeof(WearNTear), "ResetHighlight")]
internal static class BrushHighlightResetPatch
{
	private static void Postfix(WearNTear __instance)
	{
		if (WaterproofBrushPlugin.Ready)
		{
			WaterproofBrushPlugin.Instance.HighlightEnded(__instance);
		}
	}
}
namespace WaterproofBrushLogic;

internal interface IBrushInventory
{
	int FreeSlots { get; }

	int Count(string resource);

	object Capture();

	void Restore(object snapshot);

	bool AddBrush();

	void Spend(string resource, int amount);
}
internal interface ICoating
{
	bool CanWrite { get; }

	bool Protected { get; set; }
}
internal enum Outcome
{
	Success,
	MissingResource,
	FullInventory,
	AlreadyProtected,
	NotOwner,
	Failed
}
internal static class Transactions
{
	internal static Outcome Craft(IBrushInventory inventory, int amount, bool free, out Exception error)
	{
		error = null;
		if (amount < 1 || amount > 100)
		{
			throw new ArgumentOutOfRangeException("amount");
		}
		int num = ((!free) ? checked(amount * 2) : 0);
		int num2 = inventory.Count("Wood");
		if (num2 < num)
		{
			return Outcome.MissingResource;
		}
		if (inventory.FreeSlots < amount)
		{
			return Outcome.FullInventory;
		}
		object snapshot = inventory.Capture();
		try
		{
			for (int i = 0; i < amount; i++)
			{
				if (!inventory.AddBrush())
				{
					throw new InvalidOperationException("Inventory rejected brush " + (i + 1));
				}
			}
			if (num != 0)
			{
				inventory.Spend("Wood", num);
			}
			if (inventory.Count("Wood") != num2 - num)
			{
				throw new InvalidOperationException("Wood debit did not match the recipe cost");
			}
			return Outcome.Success;
		}
		catch (Exception ex)
		{
			try
			{
				inventory.Restore(snapshot);
			}
			catch (Exception ex2)
			{
				throw new AggregateException("Craft rollback failed", ex, ex2);
			}
			error = ex;
			return Outcome.Failed;
		}
	}

	internal static Outcome Coat(IBrushInventory inventory, ICoating coating, out Exception error)
	{
		error = null;
		if (coating.Protected)
		{
			return Outcome.AlreadyProtected;
		}
		if (!coating.CanWrite)
		{
			return Outcome.NotOwner;
		}
		int num = inventory.Count("Resin");
		if (num < 1)
		{
			return Outcome.MissingResource;
		}
		object snapshot = inventory.Capture();
		bool flag = false;
		try
		{
			inventory.Spend("Resin", 1);
			if (inventory.Count("Resin") != num - 1)
			{
				throw new InvalidOperationException("Resin debit was not exactly one");
			}
			if (!coating.CanWrite)
			{
				throw new InvalidOperationException("Piece ownership changed");
			}
			flag = true;
			coating.Protected = true;
			if (!coating.Protected)
			{
				throw new InvalidOperationException("The piece did not retain its protected flag");
			}
			return Outcome.Success;
		}
		catch (Exception ex)
		{
			try
			{
				if (flag)
				{
					coating.Protected = false;
					if (coating.Protected)
					{
						throw new InvalidOperationException("Could not clear coating during rollback");
					}
				}
				inventory.Restore(snapshot);
			}
			catch (Exception ex2)
			{
				throw new AggregateException("Coating rollback failed", ex, ex2);
			}
			error = ex;
			return Outcome.Failed;
		}
	}
}
internal sealed class PcmWave
{
	internal int Channels;

	internal int SampleRate;

	internal float[] Samples;

	internal static PcmWave Read(Stream stream)
	{
		if (stream == null || !stream.CanSeek)
		{
			throw new InvalidDataException("Missing embedded brush WAV");
		}
		using BinaryReader binaryReader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true);
		if (Tag(binaryReader) != "RIFF")
		{
			throw new InvalidDataException("Not RIFF audio");
		}
		uint num = binaryReader.ReadUInt32();
		if ((long)num + 8L > stream.Length || Tag(binaryReader) != "WAVE")
		{
			throw new InvalidDataException("Invalid WAV header");
		}
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		byte[] array = null;
		while (stream.Position + 8 <= (long)num + 8L)
		{
			string text = Tag(binaryReader);
			uint num6 = binaryReader.ReadUInt32();
			long num7 = stream.Position + num6 + (num6 & 1);
			if (num7 > (long)num + 8L || num6 > 16777216)
			{
				throw new InvalidDataException("Truncated WAV chunk");
			}
			if (text == "fmt ")
			{
				if (num6 < 16)
				{
					throw new InvalidDataException("Truncated WAV format");
				}
				num5 = binaryReader.ReadUInt16();
				num2 = binaryReader.ReadUInt16();
				num3 = binaryReader.ReadInt32();
				binaryReader.ReadInt32();
				binaryReader.ReadUInt16();
				num4 = binaryReader.ReadUInt16();
			}
			else if (text == "data")
			{
				array = binaryReader.ReadBytes((int)num6);
			}
			stream.Position = num7;
		}
		if (num5 != 1 || num4 != 16 || num2 < 1 || num2 > 2 || num3 < 8000 || num3 > 96000 || array == null || array.Length == 0 || array.Length % (num2 * 2) != 0)
		{
			throw new InvalidDataException("Brush WAV must be complete PCM16 mono/stereo audio");
		}
		float[] array2 = new float[array.Length / 2];
		for (int i = 0; i < array2.Length; i++)
		{
			array2[i] = (float)(short)(array[i * 2] | (array[i * 2 + 1] << 8)) / 32768f;
		}
		return new PcmWave
		{
			Channels = num2,
			SampleRate = num3,
			Samples = array2
		};
	}

	private static string Tag(BinaryReader reader)
	{
		return Encoding.ASCII.GetString(reader.ReadBytes(4));
	}
}
internal sealed class CraftCompatibility
{
	private const BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

	private const BindingFlags StaticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	private readonly FieldInfo _itemFlag;

	private readonly FieldInfo _bypassChecks;

	private readonly FieldInfo _stationKey;

	private readonly Type _statType;

	private readonly MethodInfo _incrementStat;

	private readonly MethodInfo _incrementItemCraft;

	private readonly Action<string, Exception> _report;

	internal bool HasItemFlag => _itemFlag != null;

	internal string Description => "itemFlag=" + (_itemFlag != null) + "; bypassField=" + (_bypassChecks != null) + "; stationFlag=" + (_stationKey != null) + "; craftStats=" + (_incrementStat != null);

	internal int? StationCheatedKey
	{
		get
		{
			if (!(_stationKey == null))
			{
				return (int)_stationKey.GetValue(null);
			}
			return null;
		}
	}

	internal CraftCompatibility(Type itemType, Type profileType, Type statType, Type zdoVarsType, Action<string, Exception> report)
	{
		_report = report;
		_itemFlag = FindField(itemType, "m_cheated", typeof(bool), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		_bypassChecks = FindField(profileType, "s_bypassCheatChecks", typeof(bool), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		_stationKey = FindField(zdoVarsType, "s_cheated", typeof(int), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		_statType = ((statType != null && statType.IsEnum) ? statType : null);
		if (_statType != null)
		{
			_incrementStat = FindMethod(profileType, "IncrementStat", _statType, typeof(float), typeof(bool)) ?? FindMethod(profileType, "IncrementStat", _statType, typeof(float));
		}
		_incrementItemCraft = FindMethod(profileType, "IncrementStatItemCraft", typeof(string), typeof(int), typeof(bool)) ?? FindMethod(profileType, "IncrementStatItemCraft", typeof(string), typeof(int));
	}

	private static FieldInfo FindField(Type type, string name, Type fieldType, BindingFlags flags)
	{
		FieldInfo fieldInfo = ((type == null) ? null : type.GetField(name, flags));
		if (!(fieldInfo != null) || !(fieldInfo.FieldType == fieldType))
		{
			return null;
		}
		return fieldInfo;
	}

	private static MethodInfo FindMethod(Type type, string name, params Type[] args)
	{
		if (!(type == null))
		{
			return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
		}
		return null;
	}

	internal bool IsItemCheated(object item)
	{
		if (item != null && _itemFlag != null)
		{
			return (bool)_itemFlag.GetValue(item);
		}
		return false;
	}

	internal bool SetItemMetadata(object item, bool noCost, bool resourceCheated, bool stationCheated)
	{
		if (_itemFlag == null)
		{
			return false;
		}
		bool flag = _bypassChecks != null && (bool)_bypassChecks.GetValue(null);
		bool flag2 = IsItemCheated(item) || (!flag && (noCost || resourceCheated || stationCheated));
		_itemFlag.SetValue(item, flag2);
		return flag2;
	}

	internal void RecordCraft(object profile, string itemName, int amount, bool cheated)
	{
		if (profile != null)
		{
			Increment(profile, "CraftsOrUpgrades", 1f, cheated);
			Increment(profile, "Crafts", amount, cheated);
			if (_incrementItemCraft != null)
			{
				InvokeStat(_incrementItemCraft, profile, (_incrementItemCraft.GetParameters().Length != 3) ? new object[2] { itemName, amount } : new object[3] { itemName, amount, cheated });
			}
		}
	}

	private void Increment(object profile, string name, float amount, bool cheated)
	{
		if (!(_incrementStat == null) && Enum.IsDefined(_statType, name))
		{
			object obj = Enum.Parse(_statType, name);
			InvokeStat(_incrementStat, profile, (_incrementStat.GetParameters().Length != 3) ? new object[2] { obj, amount } : new object[3] { obj, amount, cheated });
		}
	}

	private void InvokeStat(MethodInfo method, object profile, object[] args)
	{
		try
		{
			method.Invoke(profile, args);
		}
		catch (Exception ex)
		{
			if (_report != null)
			{
				_report("craft.stats", (ex is TargetInvocationException && ex.InnerException != null) ? ex.InnerException : ex);
			}
		}
	}
}