Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WaterproofYourWood v1.0.13
BepInEx/plugins/WaterproofingBrush/ValheimWaterproofBrush.dll
Decompiled 4 hours agousing 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); } } } }