using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BrennivinProtection")] [assembly: AssemblyFileVersion("0.1.3")] [assembly: AssemblyCompany("R4V9N1")] [assembly: AssemblyDescription("Created by R4V9N1")] [assembly: AssemblyProduct("BrennivinProtection")] [assembly: AssemblyCopyright("Created by R4V9N1")] [assembly: AssemblyMetadata("Creator", "Created by R4V9N1")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.1.3.0")] namespace BrennivinProtection; internal static class BrennivinEquipmentSheetCompat { private static bool _lookupComplete; private static MethodInfo _moveSheetItemsForDeath; internal static bool TryMoveSheetItemsToPlayerInventory(Player player) { if ((Object)(object)player == (Object)null) { return false; } EnsureLookup(); if (_moveSheetItemsForDeath == null) { return false; } try { _moveSheetItemsForDeath.Invoke(null, new object[1] { player }); BrennivinPlugin.LogInfo("Asked Equipment Sheet to move sheet items into the death inventory before Brennivin protection check."); return true; } catch (Exception ex) { BrennivinPlugin.LogWarning("Equipment Sheet compatibility call failed: " + ex.Message); return false; } } private static void EnsureLookup() { if (_lookupComplete) { return; } _lookupComplete = true; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType("EquipmentSheet.EquipmentSheetPlugin", throwOnError: false); if (!(type == null)) { _moveSheetItemsForDeath = type.GetMethod("MoveSheetItemsToPlayerInventoryForDeath", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); break; } } } } internal static class BrennivinBedTeleport { internal unsafe static bool TryTeleportToClaimedBed(Player player) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)Game.instance == (Object)null) { return false; } PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); if (playerProfile == null || !playerProfile.HaveCustomSpawnPoint()) { BrennivinPlugin.Message(player, "Brennivin needs a claimed bed."); return false; } Vector3 customSpawnPoint = playerProfile.GetCustomSpawnPoint(); if (customSpawnPoint == Vector3.zero) { BrennivinPlugin.Message(player, "Brennivin could not find your claimed bed."); return false; } if (((Character)player).IsTeleporting()) { BrennivinPlugin.Message(player, "Brennivin cannot be used while teleporting."); return false; } if (!((Character)player).TeleportTo(customSpawnPoint, ((Component)player).transform.rotation, true)) { BrennivinPlugin.Message(player, "Brennivin could not start the teleport."); return false; } BrennivinWakeupAnimation.Queue(player); BrennivinPlugin.Message(player, "Brennivin carries you home."); Vector3 val = customSpawnPoint; BrennivinPlugin.LogInfo("Started Brennivin bed teleport to " + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString() + "."); return true; } } internal sealed class BrennivinWakeupAnimation : MonoBehaviour { private static readonly FieldInfo AnimatorField = AccessTools.Field(typeof(Character), "m_animator"); private static readonly FieldInfo WakeupTimerField = AccessTools.Field(typeof(Player), "m_wakeupTimer"); private Player _player; private float _timeout; private float _settleDelay = 0.25f; internal static void Queue(Player player) { if (!((Object)(object)player == (Object)null)) { BrennivinWakeupAnimation brennivinWakeupAnimation = ((Component)player).GetComponent(); if ((Object)(object)brennivinWakeupAnimation == (Object)null) { brennivinWakeupAnimation = ((Component)player).gameObject.AddComponent(); } brennivinWakeupAnimation._player = player; brennivinWakeupAnimation._timeout = 18f; brennivinWakeupAnimation._settleDelay = 0.25f; } } private void Update() { if ((Object)(object)_player == (Object)null) { Object.Destroy((Object)(object)this); return; } _timeout -= Time.deltaTime; if (_timeout <= 0f) { Object.Destroy((Object)(object)this); } else if (!((Character)_player).IsTeleporting()) { _settleDelay -= Time.deltaTime; if (!(_settleDelay > 0f)) { PulseWakeup(_player); Object.Destroy((Object)(object)this); } } } private static void PulseWakeup(Player player) { try { ZNetView component = ((Component)player).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid() && component.IsOwner() && component.GetZDO() != null) { component.GetZDO().Set(ZDOVars.s_wakeup, true); } Animator val = (Animator)((AnimatorField == null) ? null : /*isinst with value type is only supported in some contexts*/); if ((Object)(object)val != (Object)null) { val.SetBool("wakeup", true); } if (WakeupTimerField != null) { WakeupTimerField.SetValue(player, 0f); } BrennivinPlugin.LogInfo("Pulsed Valheim wakeup animation after Brennivin teleport."); } catch (Exception ex) { BrennivinPlugin.LogWarning("Brennivin wakeup animation pulse failed: " + ex.Message); } } } [HarmonyPatch(typeof(Player), "CreateTombStone")] [HarmonyPriority(800)] [HarmonyBefore(new string[] { "r4v9n1.equipmentsheet" })] internal static class PlayerCreateTombStoneBrennivinPatch { private static bool Prefix(Player __instance) { if (!BrennivinPlugin.IsDeathProtectionEnabled() || (Object)(object)__instance == (Object)null) { return true; } Inventory inventory = ((Humanoid)__instance).GetInventory(); if (BrennivinPlugin.TryConsumeOne(inventory)) { BrennivinPlugin.Message(__instance, "Brennivin was spent. Your inventory stayed with you."); BrennivinPlugin.LogInfo("Brennivin death protection consumed one carried flask and skipped tombstone creation."); return false; } if (BrennivinEquipmentSheetCompat.TryMoveSheetItemsToPlayerInventory(__instance) && BrennivinPlugin.TryConsumeOne(inventory)) { BrennivinPlugin.Message(__instance, "Brennivin was spent. Your inventory stayed with you."); BrennivinPlugin.LogInfo("Brennivin death protection consumed one flask after Equipment Sheet items were moved into the player inventory."); return false; } return true; } } [HarmonyPatch(typeof(Player), "ConsumeItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(bool) })] internal static class PlayerConsumeItemBrennivinPatch { private static bool Prefix(Player __instance, Inventory inventory, ItemData item, bool checkWorldLevel, ref bool __result) { if (!BrennivinPlugin.IsBrennivin(item)) { return true; } __result = false; if ((Object)(object)__instance == (Object)null || inventory == null || !inventory.ContainsItem(item)) { return false; } if (!BrennivinPlugin.IsBedTeleportEnabled()) { BrennivinPlugin.Message(__instance, "Brennivin bed teleport is disabled."); return false; } if (!((Humanoid)__instance).CanConsumeItem(item, checkWorldLevel)) { return false; } if (!BrennivinBedTeleport.TryTeleportToClaimedBed(__instance)) { return false; } __result = inventory.RemoveOneItem(item); return false; } } [BepInPlugin("r4v9n1.brennivin", "BrennivinProtection", "0.1.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class BrennivinPlugin : BaseUnityPlugin { public const string PluginGuid = "r4v9n1.brennivin"; public const string PluginName = "BrennivinProtection"; public const string PluginVersion = "0.1.3"; public const string CreatorCredit = "Created by R4V9N1"; internal const string PrefabName = "R4V9N1_Brennivin"; internal const string BasePrefabName = "R4V9N1_BrennivinBase"; internal const string DisplayName = "Brennivín"; internal const string BaseDisplayName = "Mead base: Brennivín"; internal const string ItemDescription = "One sip of this can make you forget everything....\nWhile in inventory you will not loose your items on death, if you drink it....."; private static readonly string[] MeadBaseCandidates = new string[8] { "MeadHealthMinor", "MeadTasty", "MeadStaminaMinor", "MeadHealthMedium", "MeadEitrMinor", "MeadPoisonResist", "MeadFrostResist", "MeadHealthLingering" }; private static readonly string[] MeadBaseBaseCandidates = new string[8] { "MeadBaseHealthMinor", "MeadBaseTasty", "MeadBaseStaminaMinor", "MeadBaseHealthMedium", "MeadBaseEitrMinor", "MeadBasePoisonResist", "MeadBaseFrostResist", "BarleyWineBase" }; private static ConfigEntry _enabled; private static ConfigEntry _deathProtection; private static ConfigEntry _bedTeleport; private static ConfigEntry _logDiagnostics; private static ManualLogSource _log; private Harmony _harmony; private bool _itemRegistered; private void Awake() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown _log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, new ConfigDescription("Enable Brennivin item behavior. Server-controlled.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); _deathProtection = ((BaseUnityPlugin)this).Config.Bind("General", "DeathProtection", true, new ConfigDescription("If true, carrying Brennivin lets the player die normally but keep inventory and equipment, consuming one flask. Server-controlled.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); _bedTeleport = ((BaseUnityPlugin)this).Config.Bind("General", "BedTeleportOnDrink", true, new ConfigDescription("If true, drinking Brennivin teleports the player to their claimed bed. Server-controlled.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); _logDiagnostics = ((BaseUnityPlugin)this).Config.Bind("Diagnostics", "LogDiagnostics", false, "Log Brennivin death protection, item registration, and teleport decisions."); _harmony = new Harmony("r4v9n1.brennivin"); _harmony.PatchAll(); PrefabManager.OnVanillaPrefabsAvailable += RegisterBrennivin; ((BaseUnityPlugin)this).Logger.LogInfo((object)"BrennivinProtection 0.1.3 loaded."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Created by R4V9N1."); } private void OnDestroy() { PrefabManager.OnVanillaPrefabsAvailable -= RegisterBrennivin; if (_harmony != null) { _harmony.UnpatchSelf(); _harmony = null; } } private void RegisterBrennivin() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Expected O, but got Unknown //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Expected O, but got Unknown if (_itemRegistered || (ItemManager.Instance.GetItem("R4V9N1_Brennivin") != null && ItemManager.Instance.GetItem("R4V9N1_BrennivinBase") != null)) { _itemRegistered = true; return; } GameObject val = ResolveBaseMeadPrefab(); GameObject val2 = ResolveBaseMeadBasePrefab(); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Could not find vanilla mead flask/base prefabs to clone. Brennivin was not registered."); return; } ItemConfig val3 = new ItemConfig { Name = "Brennivín", Description = "One sip of this can make you forget everything....\nWhile in inventory you will not loose your items on death, if you drink it.....", StackSize = 10, Weight = 1f }; ItemConfig val4 = new ItemConfig { Name = "Mead base: Brennivín", Description = "Ferment this into Brennivín.", Amount = 1, CraftingStation = "MeadKetill", MinStationLevel = 1, StackSize = 10, Weight = 1f }; val4.AddRequirement("Cloudberry", 50, 0); val4.AddRequirement("Honey", 50, 0); val4.AddRequirement("Thistle", 50, 0); val4.AddRequirement("Dandelion", 50, 0); val4.AddRequirement("BoneFragments", 50, 0); CustomItem val5 = new CustomItem("R4V9N1_Brennivin", ((Object)val).name, val3); CustomItem val6 = new CustomItem("R4V9N1_BrennivinBase", ((Object)val2).name, val4); if (!IsValidCustomItem(val5) || !IsValidCustomItem(val6)) { ((BaseUnityPlugin)this).Logger.LogError((object)("Jotunn could not clone Brennivin flask/base from '" + ((Object)val).name + "' and '" + ((Object)val2).name + "'.")); return; } ConfigureFlask(val5); ConfigureMeadBase(val6); if (!ItemManager.Instance.AddItem(val5) || !ItemManager.Instance.AddItem(val6)) { ((BaseUnityPlugin)this).Logger.LogError((object)"Jotunn rejected Brennivin item registration."); return; } ItemManager.Instance.AddItemConversion(new CustomItemConversion((ConversionConfig)new FermenterConversionConfig { Station = "Fermenter", FromItem = "R4V9N1_BrennivinBase", ToItem = "R4V9N1_Brennivin", ProducedItems = 1 })); _itemRegistered = true; PrefabManager.OnVanillaPrefabsAvailable -= RegisterBrennivin; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered Mead base: Brennivin as a Mead Ketill craft and Brennivin as a one-flask fermenter output."); } private GameObject ResolveBaseMeadPrefab() { for (int i = 0; i < MeadBaseCandidates.Length; i++) { GameObject prefab = PrefabManager.Instance.GetPrefab(MeadBaseCandidates[i]); if (IsUsableConsumablePrefab(prefab)) { return prefab; } } return null; } private GameObject ResolveBaseMeadBasePrefab() { for (int i = 0; i < MeadBaseBaseCandidates.Length; i++) { GameObject prefab = PrefabManager.Instance.GetPrefab(MeadBaseBaseCandidates[i]); if (IsUsableItemPrefab(prefab)) { return prefab; } } return null; } private static bool IsUsableConsumablePrefab(GameObject prefab) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 ItemDrop usableItemDrop = GetUsableItemDrop(prefab); if ((Object)(object)usableItemDrop != (Object)null) { return (int)usableItemDrop.m_itemData.m_shared.m_itemType == 2; } return false; } private static bool IsUsableItemPrefab(GameObject prefab) { return (Object)(object)GetUsableItemDrop(prefab) != (Object)null; } private static ItemDrop GetUsableItemDrop(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return null; } ItemDrop component = prefab.GetComponent(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { return null; } return component; } private static bool IsValidCustomItem(CustomItem item) { if (item != null && (Object)(object)item.ItemPrefab != (Object)null && (Object)(object)item.ItemDrop != (Object)null && item.ItemDrop.m_itemData != null) { return item.ItemDrop.m_itemData.m_shared != null; } return false; } private static void ConfigureFlask(CustomItem item) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) ItemData itemData = item.ItemDrop.m_itemData; SharedData shared = itemData.m_shared; itemData.m_dropPrefab = item.ItemPrefab; shared.m_name = "Brennivín"; shared.m_description = "One sip of this can make you forget everything....\nWhile in inventory you will not loose your items on death, if you drink it....."; shared.m_itemType = (ItemType)2; shared.m_maxStackSize = 10; shared.m_weight = 1f; shared.m_teleportable = true; shared.m_isDrink = true; shared.m_food = 0f; shared.m_foodStamina = 0f; shared.m_foodEitr = 0f; shared.m_foodBurnTime = 0f; shared.m_foodRegen = 0f; shared.m_consumeStatusEffect = null; shared.m_equipDuration = 1f; TintPrefabCyan(item.ItemPrefab); } private static void ConfigureMeadBase(CustomItem item) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) ItemData itemData = item.ItemDrop.m_itemData; SharedData shared = itemData.m_shared; itemData.m_dropPrefab = item.ItemPrefab; shared.m_name = "Mead base: Brennivín"; shared.m_description = "Ferment this into Brennivín."; shared.m_itemType = (ItemType)1; shared.m_maxStackSize = 10; shared.m_weight = 1f; shared.m_teleportable = true; shared.m_isDrink = false; shared.m_food = 0f; shared.m_foodStamina = 0f; shared.m_foodEitr = 0f; shared.m_foodBurnTime = 0f; shared.m_foodRegen = 0f; shared.m_consumeStatusEffect = null; TintPrefabCyan(item.ItemPrefab); } private static void TintPrefabCyan(GameObject prefab) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)prefab == (Object)null) { return; } Renderer[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { Material[] sharedMaterials = val.sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { Material val2 = sharedMaterials[j]; if (!((Object)(object)val2 == (Object)null)) { Material val3 = new Material(val2); ((Object)val3).name = "Brennivin_Cyan_" + ((Object)val2).name; if (val3.HasProperty("_Color")) { val3.SetColor("_Color", new Color(0f, 0.9f, 1f, 1f)); } if (val3.HasProperty("_BaseColor")) { val3.SetColor("_BaseColor", new Color(0f, 0.9f, 1f, 1f)); } if (val3.HasProperty("_EmissionColor")) { val3.SetColor("_EmissionColor", new Color(0f, 0.35f, 0.45f, 1f)); } sharedMaterials[j] = val3; } } val.sharedMaterials = sharedMaterials; } } internal static bool IsEnabled() { if (_enabled != null) { return _enabled.Value; } return false; } internal static bool IsDeathProtectionEnabled() { if (IsEnabled() && _deathProtection != null) { return _deathProtection.Value; } return false; } internal static bool IsBedTeleportEnabled() { if (IsEnabled() && _bedTeleport != null) { return _bedTeleport.Value; } return false; } internal static bool IsBrennivin(ItemData item) { if (item == null) { return false; } if ((Object)(object)item.m_dropPrefab != (Object)null && string.Equals(GetPrefabName(((Object)item.m_dropPrefab).name), "R4V9N1_Brennivin", StringComparison.Ordinal)) { return true; } if (item.m_shared != null) { return string.Equals(item.m_shared.m_name, "Brennivín", StringComparison.Ordinal); } return false; } private static string GetPrefabName(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } int num = name.IndexOfAny(new char[2] { '(', ' ' }); if (num < 0) { return name; } return name.Substring(0, num); } internal static bool TryConsumeOne(Inventory inventory) { if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (IsBrennivin(val)) { return inventory.RemoveOneItem(val); } } return false; } internal static void Message(Player player, string text) { if ((Object)(object)player != (Object)null) { ((Character)player).Message((MessageType)2, text, 0, (Sprite)null); } } internal static void LogInfo(string text) { if (_logDiagnostics != null && _logDiagnostics.Value && _log != null) { _log.LogInfo((object)text); } } internal static void LogWarning(string text) { if (_log != null) { _log.LogWarning((object)text); } } }